1 /* 2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 #include <sys/stat.h> 40 #include <sys/types.h> 41 #include <sys/time.h> 42 #include <ctype.h> 43 #include <sys/socket.h> 44 #include <sys/acl.h> 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 #include <errno.h> 48 #include <stdio.h> 49 #include <string.h> 50 #include <time.h> 51 #include <cstack.h> 52 #include "ndmp.h" 53 #include "ndmpd.h" 54 #include <bitmap.h> 55 #include <traverse.h> 56 57 58 /* 59 * Maximum length of the string-representation of u_longlong_t type. 60 */ 61 #define QUAD_DECIMAL_LEN 20 62 63 64 /* Is Y=yes or T=true */ 65 #define IS_YORT(c) (strchr("YT", toupper(c))) 66 67 /* Is F=file format (vs D=node-dir format) */ 68 #define IS_F(c) (toupper(c) == 'F') 69 70 /* 71 * If path is defined. 72 */ 73 #define ISDEFINED(cp) ((cp) && *(cp)) 74 #define SHOULD_LBRBK(bpp) (!((bpp)->bp_opr & TLM_OP_CHOOSE_ARCHIVE)) 75 76 /* 77 * Component boundary means end of path or on a '/'. At this 78 * point both paths should be on component boundary. 79 */ 80 #define COMPBNDRY(p) (!*(p) || (*p) == '/') 81 82 typedef struct bk_param_v3 { 83 ndmpd_session_t *bp_session; 84 ndmp_lbr_params_t *bp_nlp; 85 tlm_job_stats_t *bp_js; 86 tlm_cmd_t *bp_lcmd; 87 tlm_commands_t *bp_cmds; 88 tlm_acls_t *bp_tlmacl; 89 int bp_opr; 90 char *bp_tmp; 91 char *bp_chkpnm; 92 char **bp_excls; 93 char *bp_unchkpnm; 94 } bk_param_v3_t; 95 96 97 /* 98 * Multiple destination restore mode 99 */ 100 #define MULTIPLE_DEST_DIRS 128 101 102 int multiple_dest_restore = 0; 103 104 /* 105 * Plug-in module ops 106 */ 107 ndmp_plugin_t *ndmp_pl; 108 109 /* 110 * NDMP exclusion list 111 */ 112 char **ndmp_excl_list = NULL; 113 114 /* 115 * split_env 116 * 117 * Splits the string into list of sections separated by the 118 * sep character. 119 * 120 * Parameters: 121 * envp (input) - the environment variable that should be broken 122 * sep (input) - the separator character 123 * 124 * Returns: 125 * Array of character pointers: On success. The array is allocated 126 * as well as all its entries. They all should be freed by the 127 * caller. 128 * NULL: on error 129 */ 130 static char ** 131 split_env(char *envp, char sep) 132 { 133 char *bp, *cp, *ep; 134 char *save; 135 char **cpp; 136 int n; 137 138 if (!envp) 139 return (NULL); 140 141 while (isspace(*envp)) 142 envp++; 143 144 if (!*envp) 145 return (NULL); 146 147 bp = save = strdup(envp); 148 if (!bp) 149 return (NULL); 150 151 /* 152 * Since the env variable is not empty, it contains at least one 153 * component 154 */ 155 n = 1; 156 while ((cp = strchr(bp, sep))) { 157 if (cp > save && *(cp-1) != '\\') 158 n++; 159 160 bp = cp + 1; 161 } 162 163 n++; /* for the terminating NULL pointer */ 164 cpp = ndmp_malloc(sizeof (char *) * n); 165 if (!cpp) { 166 free(save); 167 return (NULL); 168 } 169 170 (void) memset(cpp, 0, n * sizeof (char *)); 171 n = 0; 172 cp = bp = ep = save; 173 while (*cp) 174 if (*cp == sep) { 175 *ep = '\0'; 176 if (strlen(bp) > 0) { 177 cpp[n] = strdup(bp); 178 if (!cpp[n++]) { 179 tlm_release_list(cpp); 180 cpp = NULL; 181 break; 182 } 183 } 184 ep = bp = ++cp; 185 } else if (*cp == '\\') { 186 ++cp; 187 if (*cp == 'n') { /* "\n" */ 188 *ep++ = '\n'; 189 cp++; 190 } else if (*cp == 't') { /* "\t" */ 191 *ep++ = '\t'; 192 cp++; 193 } else 194 *ep++ = *cp++; 195 } else 196 *ep++ = *cp++; 197 198 *ep = '\0'; 199 if (cpp) { 200 if (strlen(bp) > 0) { 201 cpp[n] = strdup(bp); 202 if (!cpp[n++]) { 203 tlm_release_list(cpp); 204 cpp = NULL; 205 } else 206 cpp[n] = NULL; 207 } 208 209 if (n == 0 && cpp != NULL) { 210 tlm_release_list(cpp); 211 cpp = NULL; 212 } 213 } 214 215 free(save); 216 return (cpp); 217 } 218 219 220 /* 221 * prl 222 * 223 * Print the array of character pointers passed to it. This is 224 * used for debugging purpose. 225 * 226 * Parameters: 227 * lpp (input) - pointer to the array of strings 228 * 229 * Returns: 230 * void 231 */ 232 static void 233 prl(char **lpp) 234 { 235 if (!lpp) { 236 NDMP_LOG(LOG_DEBUG, "empty"); 237 return; 238 } 239 240 while (*lpp) 241 NDMP_LOG(LOG_DEBUG, "\"%s\"", *lpp++); 242 } 243 244 245 /* 246 * inlist 247 * 248 * Looks through all the strings of the array to see if the ent 249 * matches any of the strings. The strings are patterns. 250 * 251 * Parameters: 252 * lpp (input) - pointer to the array of strings 253 * ent (input) - the entry to be matched 254 * 255 * Returns: 256 * TRUE: if there is a match 257 * FALSE: invalid argument or no match 258 */ 259 static boolean_t 260 inlist(char **lpp, char *ent) 261 { 262 if (!lpp || !ent) { 263 NDMP_LOG(LOG_DEBUG, "empty list"); 264 return (FALSE); 265 } 266 267 while (*lpp) { 268 /* 269 * Fixing the sync_sort NDMPV3 problem, it sends the inclusion 270 * like "./" which we should skip the "./" 271 */ 272 char *pattern = *lpp; 273 if (strncmp(pattern, "./", 2) == 0) 274 pattern += 2; 275 276 NDMP_LOG(LOG_DEBUG, "pattern %s, ent %s", pattern, ent); 277 278 if (match(pattern, ent)) { 279 NDMP_LOG(LOG_DEBUG, "match(%s,%s)", pattern, ent); 280 return (TRUE); 281 } 282 lpp++; 283 } 284 285 NDMP_LOG(LOG_DEBUG, "no match"); 286 return (FALSE); 287 } 288 289 290 /* 291 * inexl 292 * 293 * Checks if the entry is in the list. This is used for exclusion 294 * list. If the exclusion list is empty, FALSE should be returned 295 * showing that nothing should be excluded by default. 296 * 297 * Parameters: 298 * lpp (input) - pointer to the array of strings 299 * ent (input) - the entry to be matched 300 * 301 * Returns: 302 * TRUE: if there is a match 303 * FALSE: invalid argument or no match 304 * 305 */ 306 static boolean_t 307 inexl(char **lpp, char *ent) 308 { 309 if (!lpp || !ent) 310 return (FALSE); 311 312 return (inlist(lpp, ent)); 313 } 314 315 316 /* 317 * ininc 318 * 319 * Checks if the entry is in the list. This is used for inclusion 320 * list. If the inclusion list is empty, TRUE should be returned 321 * showing that everything should be included by default. 322 * 323 * Parameters: 324 * lpp (input) - pointer to the array of strings 325 * ent (input) - the entry to be matched 326 * 327 * Returns: 328 * TRUE: if there is a match or the list is empty 329 * FALSE: no match 330 */ 331 static boolean_t 332 ininc(char **lpp, char *ent) 333 { 334 if (!lpp || !ent || !*ent) 335 return (TRUE); 336 337 return (inlist(lpp, ent)); 338 } 339 340 341 /* 342 * setupsels 343 * 344 * Set up the selection list for Local B/R functions. A new array of 345 * "char *" is created and the pointers point to the original paths of 346 * the Nlist. 347 * 348 * Parameters: 349 * session (input) - pointer to the session 350 * params (input) - pointer to the parameters structure 351 * nlp (input) - pointer to the nlp structure 352 * index(input) - If not zero is the DAR entry position 353 * 354 * Returns: 355 * list pointer: on success 356 * NULL: on error 357 */ 358 /*ARGSUSED*/ 359 char ** 360 setupsels(ndmpd_session_t *session, ndmpd_module_params_t *params, 361 ndmp_lbr_params_t *nlp, int index) 362 { 363 char **lpp, **save; 364 int i, n; 365 int len; 366 int start, end; 367 mem_ndmp_name_v3_t *ep; 368 369 n = session->ns_data.dd_nlist_len; 370 371 save = lpp = ndmp_malloc(sizeof (char *) * (n + 1)); 372 if (!lpp) { 373 MOD_LOGV3(params, NDMP_LOG_ERROR, "Insufficient memory.\n"); 374 return (NULL); 375 } 376 377 if (index) { /* DAR, just one entry */ 378 /* 379 * We have to setup a list of strings that will not match any 380 * file. One DAR entry will be added in the right position later 381 * in this function. 382 * When the match is called from tar_getdir the 383 * location of the selection that matches the entry is 384 * important 385 */ 386 for (i = 0; i < n; ++i) 387 *(lpp+i) = " "; 388 n = 1; 389 start = index-1; 390 end = start+1; 391 lpp += start; /* Next selection entry will be in lpp[start] */ 392 } else { 393 start = 0; 394 end = n; 395 } 396 397 for (i = start; i < end; i++) { 398 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i); 399 if (!ep) 400 continue; 401 402 /* 403 * Check for clients that send original path as "."(like 404 * CA products). In this situation opath is something like 405 * "/v1/." and we should change it to "/v1/" 406 */ 407 len = strlen(ep->nm3_opath); 408 if (len > 1 && ep->nm3_opath[len-2] == '/' && 409 ep->nm3_opath[len-1] == '.') { 410 ep->nm3_opath[len-1] = '\0'; 411 NDMP_LOG(LOG_DEBUG, 412 "nm3_opath changed from %s. to %s", 413 ep->nm3_opath, ep->nm3_opath); 414 } 415 *lpp++ = ep->nm3_opath; 416 } 417 418 /* list termination indicator is a null pointer */ 419 *lpp = NULL; 420 421 return (save); 422 } 423 424 425 /* 426 * mkrsp 427 * 428 * Make Restore Path. 429 * It gets a path, a selection (with which the path has matched) a new 430 * name and makes a new name for the path. 431 * All the components of the path and the selection are skipped as long 432 * as they are the same. If either of the path or selection are not on 433 * a component boundary, the match was reported falsefully and no new name 434 * is generated(Except the situation in which both path and selection 435 * end with trailing '/' and selection is the prefix of the path). 436 * Otherwise, the remaining of the path is appended to the 437 * new name. The result is saved in the buffer passed. 438 * 439 * Parameters: 440 * bp (output) - pointer to the result buffer 441 * pp (input) - pointer to the path 442 * sp (input) - pointer to the selection 443 * np (input) - pointer to the new name 444 * 445 * Returns: 446 * pointer to the bp: on success 447 * NULL: otherwise 448 */ 449 char * 450 mkrsp(char *bp, char *pp, char *sp, char *np) 451 { 452 if (!bp || !pp) 453 return (NULL); 454 455 456 pp += strspn(pp, "/"); 457 if (sp) { 458 sp += strspn(sp, "/"); 459 460 /* skip as much as match */ 461 while (*sp && *pp && *sp == *pp) { 462 sp++; 463 pp++; 464 } 465 466 if (!COMPBNDRY(pp) || !COMPBNDRY(sp)) 467 /* An exception to the boundary rule */ 468 /* (!(!*sp && (*(pp - 1)) == '/')) */ 469 if (*sp || (*(pp - 1)) != '/') 470 return (NULL); 471 472 /* if pp shorter than sp, it should not be restored */ 473 if (!*pp && *sp) { 474 sp += strspn(sp, "/"); 475 if (strlen(sp) > 0) 476 return (NULL); 477 } 478 } 479 480 if (np) 481 np += strspn(np, "/"); 482 else 483 np = ""; 484 485 if (!tlm_cat_path(bp, np, pp)) { 486 NDMP_LOG(LOG_ERR, "Restore path too long %s/%s.", np, pp); 487 return (NULL); 488 } 489 490 return (bp); 491 } 492 493 494 /* 495 * mknewname 496 * 497 * This is used as callback for creating the restore path. This function 498 * can handle both single destination and multiple restore paths. 499 * 500 * Make up the restore destination path for a particular file/directory, path, 501 * based on nm3_opath and nm3_dpath. path should have matched nm3_opath 502 * in some way. 503 */ 504 char * 505 mknewname(struct rs_name_maker *rnp, char *buf, int idx, char *path) 506 { 507 char *rv; 508 ndmp_lbr_params_t *nlp; 509 mem_ndmp_name_v3_t *ep; 510 511 rv = NULL; 512 if (!buf) { 513 NDMP_LOG(LOG_DEBUG, "buf is NULL"); 514 } else if (!path) { 515 NDMP_LOG(LOG_DEBUG, "path is NULL"); 516 } else if ((nlp = rnp->rn_nlp) == 0) { 517 NDMP_LOG(LOG_DEBUG, "rnp->rn_nlp is NULL"); 518 } else if (!nlp->nlp_params) { 519 NDMP_LOG(LOG_DEBUG, "nlp->nlp_params is NULL"); 520 } else 521 if (!ndmp_full_restore_path) { 522 if (idx < 0 || idx >= (int)nlp->nlp_nfiles) { 523 NDMP_LOG(LOG_DEBUG, 524 "Invalid idx %d range (0, %d)", 525 idx, nlp->nlp_nfiles); 526 } else if (!(ep = (mem_ndmp_name_v3_t *)MOD_GETNAME( 527 nlp->nlp_params, idx))) { 528 NDMP_LOG(LOG_DEBUG, 529 "nlist entry %d is NULL", idx); 530 } else { 531 rv = mkrsp(buf, path, ep->nm3_opath, 532 ep->nm3_dpath); 533 534 NDMP_LOG(LOG_DEBUG, 535 "idx %d org \"%s\" dst \"%s\"", 536 idx, ep->nm3_opath, ep->nm3_dpath); 537 if (rv) { 538 NDMP_LOG(LOG_DEBUG, 539 "path \"%s\": \"%s\"", path, rv); 540 } else { 541 NDMP_LOG(LOG_DEBUG, 542 "path \"%s\": NULL", path); 543 } 544 } 545 } else { 546 if (!tlm_cat_path(buf, nlp->nlp_restore_path, path)) { 547 NDMP_LOG(LOG_ERR, "Path too long %s/%s.", 548 nlp->nlp_restore_path, path); 549 rv = NULL; 550 } else { 551 rv = buf; 552 NDMP_LOG(LOG_DEBUG, 553 "path \"%s\": \"%s\"", path, rv); 554 } 555 } 556 557 return (rv); 558 } 559 560 561 /* 562 * chopslash 563 * 564 * Remove the slash from the end of the given path 565 */ 566 static void 567 chopslash(char *cp) 568 { 569 int ln; 570 571 if (!cp || !*cp) 572 return; 573 574 ln = strlen(cp); 575 cp += ln - 1; /* end of the string */ 576 while (ln > 0 && *cp == '/') { 577 *cp-- = '\0'; 578 ln--; 579 } 580 } 581 582 583 /* 584 * joinpath 585 * 586 * Join two given paths 587 */ 588 static char * 589 joinpath(char *bp, char *pp, char *np) 590 { 591 if (pp && *pp) { 592 if (np && *np) 593 (void) tlm_cat_path(bp, pp, np); 594 else 595 (void) strlcpy(bp, pp, TLM_MAX_PATH_NAME); 596 } else { 597 if (np && *np) 598 (void) strlcpy(bp, np, TLM_MAX_PATH_NAME); 599 else 600 bp = NULL; 601 } 602 603 return (bp); 604 } 605 606 607 /* 608 * voliswr 609 * 610 * Is the volume writable? 611 */ 612 static int 613 voliswr(char *path) 614 { 615 int rv; 616 617 if (!path) 618 return (0); 619 620 rv = !fs_is_rdonly(path) && !fs_is_chkpntvol(path); 621 NDMP_LOG(LOG_DEBUG, "%d path \"%s\"", rv, path); 622 return (rv); 623 624 } 625 626 627 /* 628 * get_bk_path_v3 629 * 630 * Get the backup path from the NDMP environment variables. 631 * 632 * Parameters: 633 * params (input) - pointer to the parameters structure. 634 * 635 * Returns: 636 * The backup path: if anything is specified 637 * NULL: Otherwise 638 */ 639 char * 640 get_bk_path_v3(ndmpd_module_params_t *params) 641 { 642 char *bkpath; 643 644 bkpath = MOD_GETENV(params, "PREFIX"); 645 if (!bkpath) 646 bkpath = MOD_GETENV(params, "FILESYSTEM"); 647 648 649 if (!bkpath) { 650 MOD_LOGV3(params, NDMP_LOG_ERROR, 651 "Backup path not defined.\n"); 652 } else { 653 NDMP_LOG(LOG_DEBUG, "bkpath: \"%s\"", bkpath); 654 } 655 656 return (bkpath); 657 } 658 659 660 /* 661 * is_valid_backup_dir_v3 662 * 663 * Checks the validity of the backup path. Backup path should 664 * have the following characteristics to be valid: 665 * 1) It should be an absolute path. 666 * 2) It should be a directory. 667 * 3) It should not be checkpoint root directory 668 * 4) If the file system is read-only, the backup path 669 * should be a checkpointed path. Checkpoint cannot 670 * be created on a read-only file system. 671 * 672 * Parameters: 673 * params (input) - pointer to the parameters structure. 674 * bkpath (input) - the backup path 675 * 676 * Returns: 677 * TRUE: if everything's OK 678 * FALSE: otherwise. 679 */ 680 static boolean_t 681 is_valid_backup_dir_v3(ndmpd_module_params_t *params, char *bkpath) 682 { 683 char *msg; 684 struct stat64 st; 685 686 if (*bkpath != '/') { 687 MOD_LOGV3(params, NDMP_LOG_ERROR, 688 "Relative backup path not allowed \"%s\".\n", bkpath); 689 return (FALSE); 690 } 691 if (stat64(bkpath, &st) < 0) { 692 msg = strerror(errno); 693 MOD_LOGV3(params, NDMP_LOG_ERROR, "\"%s\" %s.\n", 694 bkpath, msg); 695 return (FALSE); 696 } 697 if (!S_ISDIR(st.st_mode)) { 698 /* only directories can be specified as the backup path */ 699 MOD_LOGV3(params, NDMP_LOG_ERROR, 700 "\"%s\" is not a directory.\n", bkpath); 701 return (FALSE); 702 } 703 if (ndmp_is_chkpnt_root(bkpath)) { 704 /* it is the chkpnt root directory */ 705 MOD_LOGV3(params, NDMP_LOG_ERROR, 706 "\"%s\" is a checkpoint root directory.\n", bkpath); 707 return (FALSE); 708 } 709 if (fs_is_rdonly(bkpath) && !fs_is_chkpntvol(bkpath) && 710 fs_is_chkpnt_enabled(bkpath)) { 711 /* it is not a chkpnted path */ 712 MOD_LOGV3(params, NDMP_LOG_ERROR, 713 "\"%s\" is not a checkpointed path.\n", bkpath); 714 return (FALSE); 715 } 716 717 return (TRUE); 718 } 719 720 721 /* 722 * log_date_token_v3 723 * 724 * Log the token sequence number and also the date of the 725 * last backup for token-based backup in the system log 726 * and also send them as normal log to the client. 727 * 728 * Parameters: 729 * params (input) - pointer to the parameters structure 730 * nlp (input) - pointer to the nlp structure 731 * 732 * Returns: 733 * void 734 */ 735 static void 736 log_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 737 { 738 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Token sequence counter: %d.\n", 739 nlp->nlp_tokseq); 740 741 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Date of the last backup: %s.\n", 742 cctime(&nlp->nlp_tokdate)); 743 744 if (nlp->nlp_dmpnm) { 745 MOD_LOGV3(params, NDMP_LOG_NORMAL, 746 "Backup date log file name: \"%s\".\n", nlp->nlp_dmpnm); 747 } 748 } 749 750 751 /* 752 * log_lbr_bk_v3 753 * 754 * Log the backup level and data of the backup for LBR-type 755 * backup in the system log and also send them as normal log 756 * to the client. 757 * 758 * Parameters: 759 * params (input) - pointer to the parameters structure 760 * nlp (input) - pointer to the nlp structure 761 * 762 * Returns: 763 * void 764 */ 765 static void 766 log_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 767 { 768 MOD_LOGV3(params, NDMP_LOG_NORMAL, 769 "Date of this level '%c': %s.\n", nlp->nlp_clevel, 770 cctime(&nlp->nlp_cdate)); 771 772 if (nlp->nlp_dmpnm) { 773 MOD_LOGV3(params, NDMP_LOG_NORMAL, 774 "Backup date log file name: \"%s\".\n", nlp->nlp_dmpnm); 775 } 776 } 777 778 779 /* 780 * log_level_v3 781 * 782 * Log the backup level and date of the last and the current 783 * backup for level-type backup in the system log and also 784 * send them as normal log to the client. 785 * 786 * Parameters: 787 * params (input) - pointer to the parameters structure 788 * nlp (input) - pointer to the nlp structure 789 * 790 * Returns: 791 * void 792 */ 793 static void 794 log_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 795 { 796 MOD_LOGV3(params, NDMP_LOG_NORMAL, 797 "Date of the last level '%u': %s.\n", nlp->nlp_llevel, 798 cctime(&nlp->nlp_ldate)); 799 800 MOD_LOGV3(params, NDMP_LOG_NORMAL, 801 "Date of this level '%u': %s.\n", nlp->nlp_clevel, 802 cctime(&nlp->nlp_cdate)); 803 804 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Update: %s.\n", 805 NDMP_TORF(NLP_ISSET(nlp, NLPF_UPDATE))); 806 } 807 808 809 /* 810 * log_bk_params_v3 811 * 812 * Dispatcher function which calls the appropriate function 813 * for logging the backup date and level in the system log 814 * and also send them as normal log message to the client. 815 * 816 * Parameters: 817 * session (input) - pointer to the session 818 * params (input) - pointer to the parameters structure 819 * nlp (input) - pointer to the nlp structure 820 * 821 * Returns: 822 * void 823 */ 824 static void 825 log_bk_params_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 826 ndmp_lbr_params_t *nlp) 827 { 828 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Backing up \"%s\".\n", 829 nlp->nlp_backup_path); 830 831 if (session->ns_mover.md_data_addr.addr_type == NDMP_ADDR_LOCAL) 832 MOD_LOGV3(params, NDMP_LOG_NORMAL, 833 "Tape record size: %d.\n", 834 session->ns_mover.md_record_size); 835 836 MOD_LOGV3(params, NDMP_LOG_NORMAL, "File history: %c.\n", 837 NDMP_YORN(NLP_ISSET(nlp, NLPF_FH))); 838 839 if (NLP_ISSET(nlp, NLPF_TOKENBK)) 840 log_date_token_v3(params, nlp); 841 else if (NLP_ISSET(nlp, NLPF_LBRBK)) 842 log_lbr_bk_v3(params, nlp); 843 else if (NLP_ISSET(nlp, NLPF_LEVELBK)) 844 log_level_v3(params, nlp); 845 else { 846 MOD_LOGV3(params, NDMP_LOG_ERROR, 847 "Internal error: backup level not defined for \"%s\".\n", 848 nlp->nlp_backup_path); 849 } 850 } 851 852 853 /* 854 * get_update_env_v3 855 * 856 * Is the UPDATE environment variable specified? If it is 857 * the corresponding flag is set in the flags field of the 858 * nlp structure, otherwise the flag is cleared. 859 * 860 * Parameters: 861 * params (input) - pointer to the parameters structure 862 * nlp (input) - pointer to the nlp structure 863 * 864 * Returns: 865 * void 866 */ 867 static void 868 get_update_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 869 { 870 char *envp; 871 872 envp = MOD_GETENV(params, "UPDATE"); 873 if (!envp) { 874 NLP_SET(nlp, NLPF_UPDATE); 875 NDMP_LOG(LOG_DEBUG, 876 "env(UPDATE) not defined, default to TRUE"); 877 } else { 878 NDMP_LOG(LOG_DEBUG, "env(UPDATE): \"%s\"", envp); 879 if (IS_YORT(*envp)) 880 NLP_SET(nlp, NLPF_UPDATE); 881 else 882 NLP_UNSET(nlp, NLPF_UPDATE); 883 } 884 } 885 886 887 /* 888 * get_hist_env_v3 889 * 890 * Is backup history requested? If it is, the corresponding 891 * flag is set in the flags field of the nlp structure, otherwise 892 * the flag is cleared. 893 * 894 * Parameters: 895 * params (input) - pointer to the parameters structure 896 * nlp (input) - pointer to the nlp structure 897 * 898 * Returns: 899 * void 900 */ 901 static void 902 get_hist_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 903 { 904 char *envp; 905 906 envp = MOD_GETENV(params, "HIST"); 907 if (!envp) { 908 NDMP_LOG(LOG_DEBUG, "env(HIST) not defined"); 909 NLP_UNSET(nlp, NLPF_FH); 910 } else { 911 NDMP_LOG(LOG_DEBUG, "env(HIST): \"%s\"", envp); 912 if (IS_YORT(*envp) || IS_F(*envp)) 913 NLP_SET(nlp, NLPF_FH); 914 else 915 NLP_UNSET(nlp, NLPF_FH); 916 917 /* Force file format if specified */ 918 if (IS_F(*envp)) { 919 params->mp_file_history_path_func = 920 ndmpd_api_file_history_file_v3; 921 params->mp_file_history_dir_func = 0; 922 params->mp_file_history_node_func = 0; 923 } 924 } 925 } 926 927 928 /* 929 * get_exc_env_v3 930 * 931 * Gets the EXCLUDE environment variable and breaks it 932 * into strings. The separator of the EXCLUDE environment 933 * variable is the ',' character. 934 * 935 * Parameters: 936 * params (input) - pointer to the parameters structure 937 * nlp (input) - pointer to the nlp structure 938 * 939 * Returns: 940 * void 941 */ 942 static void 943 get_exc_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 944 { 945 char *envp; 946 947 envp = MOD_GETENV(params, "EXCLUDE"); 948 if (!envp) { 949 NDMP_LOG(LOG_DEBUG, "env(EXCLUDE) not defined"); 950 nlp->nlp_exl = NULL; 951 } else { 952 NDMP_LOG(LOG_DEBUG, "env(EXCLUDE): \"%s\"", envp); 953 nlp->nlp_exl = split_env(envp, ','); 954 prl(nlp->nlp_exl); 955 } 956 } 957 958 959 /* 960 * get_inc_env_v3 961 * 962 * Gets the FILES environment variable that shows which files 963 * should be backed up, and breaks it into strings. The 964 * separator of the FILES environment variable is the space 965 * character. 966 * 967 * Parameters: 968 * params (input) - pointer to the parameters structure 969 * nlp (input) - pointer to the nlp structure 970 * 971 * Returns: 972 * void 973 */ 974 static void 975 get_inc_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 976 { 977 char *envp; 978 979 envp = MOD_GETENV(params, "FILES"); 980 if (!envp) { 981 NDMP_LOG(LOG_DEBUG, "env(FILES) not defined"); 982 nlp->nlp_inc = NULL; 983 } else { 984 NDMP_LOG(LOG_DEBUG, "env(FILES): \"%s\"", envp); 985 nlp->nlp_inc = split_env(envp, ' '); 986 prl(nlp->nlp_inc); 987 } 988 } 989 990 991 /* 992 * get_direct_env_v3 993 * 994 * Gets the DIRECT environment variable that shows if the fh_info should 995 * be sent to the client or not. 996 * 997 * Parameters: 998 * params (input) - pointer to the parameters structure 999 * nlp (input) - pointer to the nlp structure 1000 * 1001 * Returns: 1002 * void 1003 */ 1004 static void 1005 get_direct_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1006 { 1007 char *envp; 1008 1009 /* 1010 * We should send the fh_info to the DMA, unless it is specified 1011 * in the request that we should not send fh_info. 1012 * At the moment we do not support DAR on directories, so if the user 1013 * needs to restore a directory they should disable the DAR. 1014 */ 1015 if (params->mp_operation == NDMP_DATA_OP_RECOVER && !ndmp_dar_support) { 1016 NDMP_LOG(LOG_DEBUG, "Direct Access Restore Disabled"); 1017 NLP_UNSET(nlp, NLPF_DIRECT); 1018 MOD_LOGV3(params, NDMP_LOG_NORMAL, 1019 "DAR is disabled. Running Restore without DAR"); 1020 return; 1021 } 1022 1023 /* 1024 * Regardless of whether DIRECT is defined at backup time we send 1025 * back the fh_info, for some clients do not use get_backup_attrs. 1026 * If operation is restore we have to unset the DIRECT, for 1027 * some clients do not set the MOVER window. 1028 */ 1029 if (params->mp_operation == NDMP_DATA_OP_BACKUP) { 1030 NDMP_LOG(LOG_DEBUG, "backup default env(DIRECT): YES"); 1031 NLP_SET(nlp, NLPF_DIRECT); 1032 } else { 1033 1034 envp = MOD_GETENV(params, "DIRECT"); 1035 if (!envp) { 1036 NDMP_LOG(LOG_DEBUG, "env(DIRECT) not defined"); 1037 NLP_UNSET(nlp, NLPF_DIRECT); 1038 } else { 1039 NDMP_LOG(LOG_DEBUG, "env(DIRECT): \"%s\"", envp); 1040 if (IS_YORT(*envp)) { 1041 NLP_SET(nlp, NLPF_DIRECT); 1042 NDMP_LOG(LOG_DEBUG, 1043 "Direct Access Restore Enabled"); 1044 } else { 1045 NLP_UNSET(nlp, NLPF_DIRECT); 1046 NDMP_LOG(LOG_DEBUG, 1047 "Direct Access Restore Disabled"); 1048 } 1049 } 1050 } 1051 1052 if (NLP_ISSET(nlp, NLPF_DIRECT)) { 1053 if (params->mp_operation == NDMP_DATA_OP_BACKUP) 1054 MOD_LOGV3(params, NDMP_LOG_NORMAL, 1055 "Direct Access Restore information is supported"); 1056 else 1057 MOD_LOGV3(params, NDMP_LOG_NORMAL, 1058 "Running Restore with Direct Access Restore"); 1059 } else { 1060 if (params->mp_operation == NDMP_DATA_OP_BACKUP) 1061 MOD_LOGV3(params, NDMP_LOG_NORMAL, 1062 "Direct Access Restore is not supported"); 1063 else 1064 MOD_LOGV3(params, NDMP_LOG_NORMAL, 1065 "Running Restore without Direct Access Restore"); 1066 } 1067 } 1068 1069 1070 /* 1071 * get_date_token_v3 1072 * 1073 * Parse the token passed as the argument. Evaluate it and 1074 * issue any warning or error if needed. Save the date and 1075 * token sequence in the nlp structure fields. The sequence 1076 * number in the token should be less than hard-limit. If 1077 * it's between soft and hard limit, a warning is issued. 1078 * There is a configurable limit which should be less than 1079 * the soft-limit saved in ndmp_max_tok_seq variable. 1080 * 1081 * The NLPF_TOKENBK flag is set in the nlp flags field to 1082 * show that the backup type is token-based. 1083 * 1084 * Parameters: 1085 * params (input) - pointer to the parameters structure 1086 * nlp (input) - pointer to the nlp structure 1087 * basedate (input) - the value of the BASE_DATE environment 1088 * variable. 1089 * 1090 * Returns: 1091 * NDMP_NO_ERR: on success 1092 * != NDMP_NO_ERR: Otherwise 1093 * 1094 */ 1095 static ndmp_error 1096 get_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp, 1097 char *basedate) 1098 { 1099 char *endp; 1100 uint_t seq; 1101 ndmp_error rv; 1102 time_t tstamp; 1103 u_longlong_t tok; 1104 1105 if (!params || !nlp || !basedate || !*basedate) 1106 return (NDMP_ILLEGAL_ARGS_ERR); 1107 1108 if (MOD_GETENV(params, "LEVEL")) { 1109 MOD_LOGV3(params, NDMP_LOG_WARNING, 1110 "Both BASE_DATE and LEVEL environment variables " 1111 "defined.\n"); 1112 MOD_LOGCONTV3(params, NDMP_LOG_WARNING, 1113 "BASE_DATE is being used for this backup.\n"); 1114 } 1115 1116 tok = strtoll(basedate, &endp, 10); 1117 if (endp == basedate) { 1118 MOD_LOGV3(params, NDMP_LOG_ERROR, 1119 "Invalid BASE_DATE environment variable: \"%s\".\n", 1120 basedate); 1121 return (NDMP_ILLEGAL_ARGS_ERR); 1122 } 1123 1124 tstamp = tok & 0xffffffff; 1125 seq = (tok >> 32) & 0xffffffff; 1126 NDMP_LOG(LOG_DEBUG, "basedate \"%s\" %lld seq %u tstamp %u", 1127 basedate, tok, seq, tstamp); 1128 1129 if ((int)seq > ndmp_get_max_tok_seq()) { 1130 rv = NDMP_ILLEGAL_ARGS_ERR; 1131 MOD_LOGV3(params, NDMP_LOG_ERROR, 1132 "The sequence counter of the token exceeds the " 1133 "maximum permitted value.\n"); 1134 MOD_LOGCONTV3(params, NDMP_LOG_ERROR, 1135 "Token sequence: %u, maxiumum value: %u.\n", 1136 seq, ndmp_get_max_tok_seq()); 1137 } else if (seq >= NDMP_TOKSEQ_HLIMIT) { 1138 rv = NDMP_ILLEGAL_ARGS_ERR; 1139 MOD_LOGV3(params, NDMP_LOG_ERROR, 1140 "The sequence counter the of token exceeds the " 1141 "hard-limit.\n"); 1142 MOD_LOGCONTV3(params, NDMP_LOG_ERROR, 1143 "Token sequence: %u, hard-limit: %u.\n", 1144 seq, NDMP_TOKSEQ_HLIMIT); 1145 } else { 1146 rv = NDMP_NO_ERR; 1147 /* 1148 * Issue a warning if the seq is equal to the maximum 1149 * permitted seq number or equal to the soft-limit. 1150 */ 1151 if (seq == NDMP_TOKSEQ_SLIMIT) { 1152 MOD_LOGV3(params, NDMP_LOG_WARNING, 1153 "The sequence counter of the token has reached " 1154 "the soft-limit.\n"); 1155 MOD_LOGCONTV3(params, NDMP_LOG_WARNING, 1156 "Token sequence: %u, soft-limit: %u.\n", 1157 seq, NDMP_TOKSEQ_SLIMIT); 1158 } else if ((int)seq == ndmp_get_max_tok_seq()) { 1159 MOD_LOGV3(params, NDMP_LOG_WARNING, 1160 "The sequence counter of the token has reached " 1161 "the maximum permitted value.\n"); 1162 MOD_LOGCONTV3(params, NDMP_LOG_WARNING, 1163 "Token sequence: %u, maxiumum value: %u.\n", 1164 seq, ndmp_get_max_tok_seq()); 1165 } 1166 1167 /* 1168 * The current seq is equal to the seq field of the 1169 * token. It will be increased after successful backup 1170 * before setting the DUMP_DATE environment variable. 1171 */ 1172 nlp->nlp_dmpnm = MOD_GETENV(params, "DMP_NAME"); 1173 NLP_SET(nlp, NLPF_TOKENBK); 1174 NLP_UNSET(nlp, NLPF_LEVELBK); 1175 NLP_UNSET(nlp, NLPF_LBRBK); 1176 nlp->nlp_tokseq = seq; 1177 nlp->nlp_tokdate = tstamp; 1178 /* 1179 * The value of nlp_cdate will be set to the checkpoint 1180 * creation time after it is created. 1181 */ 1182 } 1183 1184 return (rv); 1185 } 1186 1187 1188 /* 1189 * get_lbr_bk_v3 1190 * 1191 * Sets the level fields of the nlp structures for 1192 * LBR-type backup. The NLPF_LBRBK flag of the 1193 * nlp flags is also set to show the backup type. 1194 * 1195 * Parameters: 1196 * params (input) - pointer to the parameters structure 1197 * nlp (input) - pointer to the nlp structure 1198 * type (input) - the backup level: 'F', 'A', 'I', 'D' or 1199 * their lower-case values. 1200 * 1201 * Returns: 1202 * NDMP_NO_ERR: on success 1203 * != NDMP_NO_ERR: Otherwise 1204 */ 1205 static ndmp_error 1206 get_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp, char *type) 1207 { 1208 if (!params || !nlp || !type || !*type) 1209 return (NDMP_ILLEGAL_ARGS_ERR); 1210 1211 NLP_SET(nlp, NLPF_LBRBK); 1212 NLP_UNSET(nlp, NLPF_TOKENBK); 1213 NLP_UNSET(nlp, NLPF_LEVELBK); 1214 nlp->nlp_dmpnm = MOD_GETENV(params, "DMP_NAME"); 1215 nlp->nlp_llevel = toupper(*type); 1216 nlp->nlp_ldate = (time_t)0; 1217 nlp->nlp_clevel = nlp->nlp_llevel; 1218 (void) time(&nlp->nlp_cdate); 1219 1220 return (NDMP_NO_ERR); 1221 } 1222 1223 1224 /* 1225 * get_backup_level_v3 1226 * 1227 * Gets the backup level from the environment variables. If 1228 * BASE_DATE is specified, it will be used, otherwise LEVEL 1229 * will be used. If neither is specified, LEVEL = '0' is 1230 * assumed. 1231 * 1232 * Parameters: 1233 * params (input) - pointer to the parameters structure 1234 * nlp (input) - pointer to the nlp structure 1235 * 1236 * Returns: 1237 * NDMP_NO_ERR: on success 1238 * != NDMP_NO_ERR: Otherwise 1239 */ 1240 static ndmp_error 1241 get_backup_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1242 { 1243 char *envp; 1244 ndmp_error rv; 1245 1246 /* 1247 * If the BASE_DATE env variable is specified use it, otherwise 1248 * look to see if LEVEL is specified. If LEVEL is not 1249 * specified either, backup level '0' must be made. Level backup 1250 * does not clear the archive bit. 1251 * 1252 * If LEVEL environment varaible is specified, values for 1253 * 'F', 'D', 'I' and 'A' (for 'Full', 'Differential', 1254 * 'Incremental', and 'Archive' is checked first. Then 1255 * level '0' to '9' will be checked. 1256 * 1257 * LEVEL environment variable can hold only one character. 1258 * If its length is longer than 1, an error is returned. 1259 */ 1260 envp = MOD_GETENV(params, "BASE_DATE"); 1261 if (envp) 1262 return (get_date_token_v3(params, nlp, envp)); 1263 1264 1265 envp = MOD_GETENV(params, "LEVEL"); 1266 if (!envp) { 1267 NDMP_LOG(LOG_DEBUG, "env(LEVEL) not defined, default to 0"); 1268 NLP_SET(nlp, NLPF_LEVELBK); 1269 NLP_UNSET(nlp, NLPF_LBRBK); 1270 NLP_UNSET(nlp, NLPF_TOKENBK); 1271 nlp->nlp_llevel = 0; 1272 nlp->nlp_ldate = 0; 1273 nlp->nlp_clevel = 0; 1274 /* 1275 * The value of nlp_cdate will be set to the checkpoint 1276 * creation time after it is created. 1277 */ 1278 return (NDMP_NO_ERR); 1279 } 1280 1281 if (*(envp+1) != '\0') { 1282 MOD_LOGV3(params, NDMP_LOG_ERROR, 1283 "Invalid backup level \"%s\".\n", envp); 1284 return (NDMP_ILLEGAL_ARGS_ERR); 1285 } 1286 1287 if (IS_LBR_BKTYPE(*envp)) 1288 return (get_lbr_bk_v3(params, nlp, envp)); 1289 1290 if (!isdigit(*envp)) { 1291 MOD_LOGV3(params, NDMP_LOG_ERROR, 1292 "Invalid backup level \"%s\".\n", envp); 1293 return (NDMP_ILLEGAL_ARGS_ERR); 1294 } 1295 1296 NLP_SET(nlp, NLPF_LEVELBK); 1297 NLP_UNSET(nlp, NLPF_LBRBK); 1298 NLP_UNSET(nlp, NLPF_TOKENBK); 1299 nlp->nlp_llevel = *envp - '0'; 1300 nlp->nlp_ldate = 0; 1301 nlp->nlp_clevel = nlp->nlp_llevel; 1302 /* 1303 * The value of nlp_cdate will be set to the checkpoint 1304 * creation time after it is created. 1305 */ 1306 if (ndmpd_get_dumptime(nlp->nlp_backup_path, &nlp->nlp_llevel, 1307 &nlp->nlp_ldate) < 0) { 1308 MOD_LOGV3(params, NDMP_LOG_ERROR, 1309 "Getting dumpdates for %s level '%c'.\n", 1310 nlp->nlp_backup_path, *envp); 1311 return (NDMP_NO_MEM_ERR); 1312 } else { 1313 get_update_env_v3(params, nlp); 1314 rv = NDMP_NO_ERR; 1315 } 1316 1317 return (rv); 1318 } 1319 1320 1321 /* 1322 * save_date_token_v3 1323 * 1324 * Make the value of DUMP_DATE env variable and append the values 1325 * of the current backup in the file specified with the DMP_NAME 1326 * env variable if any file is specified. The file will be 1327 * relative name in the backup directory path. 1328 * 1329 * Parameters: 1330 * params (input) - pointer to the parameters structure 1331 * nlp (input) - pointer to the nlp structure 1332 * 1333 * Returns: 1334 * void 1335 */ 1336 static void 1337 save_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1338 { 1339 char val[QUAD_DECIMAL_LEN]; 1340 u_longlong_t tok; 1341 1342 if (!params || !nlp) 1343 return; 1344 1345 nlp->nlp_tokseq++; 1346 tok = ((u_longlong_t)nlp->nlp_tokseq << 32) | nlp->nlp_cdate; 1347 (void) snprintf(val, sizeof (val), "%llu", tok); 1348 1349 NDMP_LOG(LOG_DEBUG, "tok: %lld %s", tok, val); 1350 1351 if (MOD_SETENV(params, "DUMP_DATE", val) != 0) { 1352 MOD_LOGV3(params, NDMP_LOG_ERROR, 1353 "Could not set DUMP_DATE to %s", val); 1354 } else if (!nlp->nlp_dmpnm) { 1355 NDMP_LOG(LOG_DEBUG, "No log file defined"); 1356 } else if (ndmpd_append_dumptime(nlp->nlp_dmpnm, nlp->nlp_backup_path, 1357 nlp->nlp_tokseq, nlp->nlp_tokdate) < 0) { 1358 MOD_LOGV3(params, NDMP_LOG_ERROR, 1359 "Saving backup date for \"%s\" in \"%s\".\n", 1360 nlp->nlp_backup_path, nlp->nlp_dmpnm); 1361 } 1362 } 1363 1364 1365 /* 1366 * save_lbr_bk_v3 1367 * 1368 * Append the backup type and date in the DMP_NAME file for 1369 * LBR-type backup if any file is specified. 1370 * 1371 * Parameters: 1372 * params (input) - pointer to the parameters structure 1373 * nlp (input) - pointer to the nlp structure 1374 * 1375 * Returns: 1376 * void 1377 */ 1378 static void 1379 save_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1380 { 1381 if (!params || !nlp) 1382 return; 1383 1384 if (!nlp->nlp_dmpnm) { 1385 NDMP_LOG(LOG_DEBUG, "No log file defined"); 1386 } else if (ndmpd_append_dumptime(nlp->nlp_dmpnm, nlp->nlp_backup_path, 1387 nlp->nlp_clevel, nlp->nlp_cdate) < 0) { 1388 MOD_LOGV3(params, NDMP_LOG_ERROR, 1389 "Saving backup date for \"%s\" in \"%s\".\n", 1390 nlp->nlp_backup_path, nlp->nlp_dmpnm); 1391 } 1392 } 1393 1394 1395 /* 1396 * save_level_v3 1397 * 1398 * Save the date and level of the current backup in the dumpdates 1399 * file. 1400 * 1401 * Parameters: 1402 * params (input) - pointer to the parameters structure 1403 * nlp (input) - pointer to the nlp structure 1404 * 1405 * Returns: 1406 * void 1407 */ 1408 static void 1409 save_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1410 { 1411 if (!params || !nlp) 1412 return; 1413 1414 if (!NLP_SHOULD_UPDATE(nlp)) { 1415 NDMP_LOG(LOG_DEBUG, "update not requested"); 1416 } else if (ndmpd_put_dumptime(nlp->nlp_backup_path, nlp->nlp_clevel, 1417 nlp->nlp_cdate) < 0) { 1418 MOD_LOGV3(params, NDMP_LOG_ERROR, "Logging backup date.\n"); 1419 } 1420 } 1421 1422 1423 /* 1424 * save_backup_date_v3 1425 * 1426 * A dispatcher function to call the corresponding save function 1427 * based on the backup type. 1428 * 1429 * Parameters: 1430 * params (input) - pointer to the parameters structure 1431 * nlp (input) - pointer to the nlp structure 1432 * 1433 * Returns: 1434 * void 1435 */ 1436 static void 1437 save_backup_date_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 1438 { 1439 if (!params || !nlp) 1440 return; 1441 1442 if (NLP_ISSET(nlp, NLPF_TOKENBK)) 1443 save_date_token_v3(params, nlp); 1444 else if (NLP_ISSET(nlp, NLPF_LBRBK)) 1445 save_lbr_bk_v3(params, nlp); 1446 else if (NLP_ISSET(nlp, NLPF_LEVELBK)) 1447 save_level_v3(params, nlp); 1448 else { 1449 MOD_LOGV3(params, NDMP_LOG_ERROR, 1450 "Internal error: lost backup level type for \"%s\".\n", 1451 nlp->nlp_backup_path); 1452 } 1453 } 1454 1455 1456 /* 1457 * backup_alloc_structs_v3 1458 * 1459 * Create the structures for V3 backup. This includes: 1460 * Job stats 1461 * Reader writer IPC 1462 * File history callback structure 1463 * 1464 * Parameters: 1465 * session (input) - pointer to the session 1466 * jname (input) - name assigned to the current backup for 1467 * job stats strucure 1468 * 1469 * Returns: 1470 * 0: on success 1471 * -1: otherwise 1472 */ 1473 static int 1474 backup_alloc_structs_v3(ndmpd_session_t *session, char *jname) 1475 { 1476 int n; 1477 long xfer_size; 1478 ndmp_lbr_params_t *nlp; 1479 tlm_commands_t *cmds; 1480 1481 nlp = ndmp_get_nlp(session); 1482 if (!nlp) { 1483 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 1484 return (-1); 1485 } 1486 1487 nlp->nlp_jstat = tlm_new_job_stats(jname); 1488 if (!nlp->nlp_jstat) { 1489 NDMP_LOG(LOG_DEBUG, "Creating job stats"); 1490 return (-1); 1491 } 1492 1493 cmds = &nlp->nlp_cmds; 1494 (void) memset(cmds, 0, sizeof (*cmds)); 1495 1496 xfer_size = ndmp_buffer_get_size(session); 1497 if (xfer_size < 512*KILOBYTE) { 1498 /* 1499 * Read multiple of mover_record_size near to 512K. This 1500 * will prevent the data being copied in the mover buffer 1501 * when we write the data. 1502 */ 1503 n = 512 * KILOBYTE / xfer_size; 1504 if (n <= 0) 1505 n = 1; 1506 xfer_size *= n; 1507 NDMP_LOG(LOG_DEBUG, "Adjusted read size: %d", 1508 xfer_size); 1509 } 1510 1511 cmds->tcs_command = tlm_create_reader_writer_ipc(TRUE, xfer_size); 1512 if (!cmds->tcs_command) { 1513 tlm_un_ref_job_stats(jname); 1514 return (-1); 1515 } 1516 1517 nlp->nlp_logcallbacks = lbrlog_callbacks_init(session, 1518 ndmpd_fhpath_v3_cb, ndmpd_fhdir_v3_cb, ndmpd_fhnode_v3_cb); 1519 if (!nlp->nlp_logcallbacks) { 1520 tlm_release_reader_writer_ipc(cmds->tcs_command); 1521 tlm_un_ref_job_stats(jname); 1522 return (-1); 1523 } 1524 nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks); 1525 nlp->nlp_restored = NULL; 1526 1527 return (0); 1528 } 1529 1530 1531 /* 1532 * restore_alloc_structs_v3 1533 * 1534 * Create the structures for V3 Restore. This includes: 1535 * Job stats 1536 * Reader writer IPC 1537 * File recovery callback structure 1538 * 1539 * Parameters: 1540 * session (input) - pointer to the session 1541 * jname (input) - name assigned to the current backup for 1542 * job stats strucure 1543 * 1544 * Returns: 1545 * 0: on success 1546 * -1: otherwise 1547 */ 1548 int 1549 restore_alloc_structs_v3(ndmpd_session_t *session, char *jname) 1550 { 1551 long xfer_size; 1552 ndmp_lbr_params_t *nlp; 1553 tlm_commands_t *cmds; 1554 1555 nlp = ndmp_get_nlp(session); 1556 if (!nlp) { 1557 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 1558 return (-1); 1559 } 1560 1561 /* this is used in ndmpd_path_restored_v3() */ 1562 nlp->nlp_lastidx = -1; 1563 1564 nlp->nlp_jstat = tlm_new_job_stats(jname); 1565 if (!nlp->nlp_jstat) { 1566 NDMP_LOG(LOG_DEBUG, "Creating job stats"); 1567 return (-1); 1568 } 1569 1570 cmds = &nlp->nlp_cmds; 1571 (void) memset(cmds, 0, sizeof (*cmds)); 1572 1573 xfer_size = ndmp_buffer_get_size(session); 1574 cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size); 1575 if (!cmds->tcs_command) { 1576 tlm_un_ref_job_stats(jname); 1577 return (-1); 1578 } 1579 1580 nlp->nlp_logcallbacks = lbrlog_callbacks_init(session, 1581 ndmpd_path_restored_v3, NULL, NULL); 1582 if (!nlp->nlp_logcallbacks) { 1583 tlm_release_reader_writer_ipc(cmds->tcs_command); 1584 tlm_un_ref_job_stats(jname); 1585 return (-1); 1586 } 1587 nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks); 1588 1589 nlp->nlp_rsbm = bm_alloc(nlp->nlp_nfiles, 0); 1590 if (nlp->nlp_rsbm < 0) { 1591 NDMP_LOG(LOG_ERR, "Out of memory."); 1592 lbrlog_callbacks_done(nlp->nlp_logcallbacks); 1593 tlm_release_reader_writer_ipc(cmds->tcs_command); 1594 tlm_un_ref_job_stats(jname); 1595 return (-1); 1596 } 1597 1598 return (0); 1599 } 1600 1601 1602 /* 1603 * free_structs_v3 1604 * 1605 * Release the resources allocated by backup_alloc_structs_v3 1606 * function. 1607 * 1608 * Parameters: 1609 * session (input) - pointer to the session 1610 * jname (input) - name assigned to the current backup for 1611 * job stats strucure 1612 * 1613 * Returns: 1614 * void 1615 */ 1616 /*ARGSUSED*/ 1617 static void 1618 free_structs_v3(ndmpd_session_t *session, char *jname) 1619 { 1620 ndmp_lbr_params_t *nlp; 1621 tlm_commands_t *cmds; 1622 1623 nlp = ndmp_get_nlp(session); 1624 if (!nlp) { 1625 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 1626 return; 1627 } 1628 cmds = &nlp->nlp_cmds; 1629 if (!cmds) { 1630 NDMP_LOG(LOG_DEBUG, "cmds == NULL"); 1631 return; 1632 } 1633 1634 if (nlp->nlp_logcallbacks) { 1635 lbrlog_callbacks_done(nlp->nlp_logcallbacks); 1636 nlp->nlp_logcallbacks = NULL; 1637 } else 1638 NDMP_LOG(LOG_DEBUG, "FH CALLBACKS == NULL"); 1639 1640 if (cmds->tcs_command) { 1641 if (cmds->tcs_command->tc_buffers != NULL) 1642 tlm_release_reader_writer_ipc(cmds->tcs_command); 1643 else 1644 NDMP_LOG(LOG_DEBUG, "BUFFERS == NULL"); 1645 cmds->tcs_command = NULL; 1646 } else 1647 NDMP_LOG(LOG_DEBUG, "COMMAND == NULL"); 1648 1649 if (nlp->nlp_bkmap >= 0) { 1650 (void) dbm_free(nlp->nlp_bkmap); 1651 nlp->nlp_bkmap = -1; 1652 } 1653 1654 if (session->ns_data.dd_operation == NDMP_DATA_OP_RECOVER) { 1655 if (nlp->nlp_rsbm < 0) { 1656 NDMP_LOG(LOG_DEBUG, "nlp_rsbm < 0 %d", nlp->nlp_rsbm); 1657 } else { 1658 (void) bm_free(nlp->nlp_rsbm); 1659 nlp->nlp_rsbm = -1; 1660 } 1661 } 1662 } 1663 1664 1665 /* 1666 * backup_dirv3 1667 * 1668 * Backup a directory and update the bytes processed field of the 1669 * data server. 1670 * 1671 * Parameters: 1672 * bpp (input) - pointer to the backup parameters structure 1673 * pnp (input) - pointer to the path node 1674 * enp (input) - pointer to the entry node 1675 * 1676 * Returns: 1677 * 0: on success 1678 * != 0: otherwise 1679 */ 1680 static int 1681 backup_dirv3(bk_param_v3_t *bpp, fst_node_t *pnp, 1682 fst_node_t *enp) 1683 { 1684 longlong_t apos, bpos; 1685 acl_t *aclp = NULL; 1686 char *acltp; 1687 struct stat64 st; 1688 char fullpath[TLM_MAX_PATH_NAME]; 1689 char *p; 1690 1691 if (!bpp || !pnp || !enp) { 1692 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 1693 return (-1); 1694 } 1695 1696 NDMP_LOG(LOG_DEBUG, "d(%s)", bpp->bp_tmp); 1697 1698 if (lstat64(bpp->bp_tmp, &st) != 0) 1699 return (0); 1700 1701 if (acl_get(bpp->bp_tmp, ACL_NO_TRIVIAL, &aclp) != 0) { 1702 NDMP_LOG(LOG_DEBUG, "acl_get error errno=%d", errno); 1703 return (-1); 1704 } 1705 if (aclp && (acltp = acl_totext(aclp, 1706 ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) { 1707 (void) strlcpy(bpp->bp_tlmacl->acl_info.attr_info, 1708 acltp, TLM_MAX_ACL_TXT); 1709 acl_free(aclp); 1710 free(acltp); 1711 } else { 1712 *bpp->bp_tlmacl->acl_info.attr_info = '\0'; 1713 } 1714 1715 bpos = tlm_get_data_offset(bpp->bp_lcmd); 1716 1717 p = bpp->bp_tmp + strlen(bpp->bp_chkpnm); 1718 if (*p == '/') 1719 (void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s%s", 1720 bpp->bp_unchkpnm, p); 1721 else 1722 (void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s/%s", 1723 bpp->bp_unchkpnm, p); 1724 1725 if (tm_tar_ops.tm_putdir != NULL) 1726 (void) (tm_tar_ops.tm_putdir)(fullpath, bpp->bp_tlmacl, 1727 bpp->bp_lcmd, bpp->bp_js); 1728 1729 apos = tlm_get_data_offset(bpp->bp_lcmd); 1730 bpp->bp_session->ns_data.dd_module.dm_stats.ms_bytes_processed += 1731 apos - bpos; 1732 1733 return (0); 1734 } 1735 1736 1737 /* 1738 * backup_filev3 1739 * 1740 * Backup a file and update the bytes processed field of the 1741 * data server. 1742 * 1743 * Parameters: 1744 * bpp (input) - pointer to the backup parameters structure 1745 * pnp (input) - pointer to the path node 1746 * enp (input) - pointer to the entry node 1747 * 1748 * Returns: 1749 * 0: on success 1750 * != 0: otherwise 1751 */ 1752 static int 1753 backup_filev3(bk_param_v3_t *bpp, fst_node_t *pnp, 1754 fst_node_t *enp) 1755 { 1756 char *ent; 1757 longlong_t rv; 1758 longlong_t apos, bpos; 1759 acl_t *aclp = NULL; 1760 char *acltp; 1761 struct stat64 st; 1762 char fullpath[TLM_MAX_PATH_NAME]; 1763 char *p; 1764 1765 if (!bpp || !pnp || !enp) { 1766 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 1767 return (-1); 1768 } 1769 1770 NDMP_LOG(LOG_DEBUG, "f(%s)", bpp->bp_tmp); 1771 1772 if (lstat64(bpp->bp_tmp, &st) != 0) 1773 return (0); 1774 1775 if (!S_ISLNK(bpp->bp_tlmacl->acl_attr.st_mode)) { 1776 if (acl_get(bpp->bp_tmp, ACL_NO_TRIVIAL, &aclp) != 0) { 1777 NDMP_LOG(LOG_DEBUG, "acl_get error"); 1778 return (-1); 1779 } 1780 1781 if (aclp && 1782 (acltp = acl_totext(aclp, 1783 ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) { 1784 (void) strlcpy(bpp->bp_tlmacl->acl_info.attr_info, 1785 acltp, TLM_MAX_ACL_TXT); 1786 acl_free(aclp); 1787 free(acltp); 1788 } else { 1789 *bpp->bp_tlmacl->acl_info.attr_info = '\0'; 1790 } 1791 } 1792 1793 bpos = tlm_get_data_offset(bpp->bp_lcmd); 1794 ent = enp->tn_path ? enp->tn_path : ""; 1795 1796 p = pnp->tn_path + strlen(bpp->bp_chkpnm); 1797 if (*p == '/') 1798 (void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s%s", 1799 bpp->bp_unchkpnm, p); 1800 else 1801 (void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s/%s", 1802 bpp->bp_unchkpnm, p); 1803 1804 if (tm_tar_ops.tm_putfile != NULL) 1805 rv = (tm_tar_ops.tm_putfile)(fullpath, ent, pnp->tn_path, 1806 bpp->bp_tlmacl, bpp->bp_cmds, bpp->bp_lcmd, bpp->bp_js, 1807 bpp->bp_session->hardlink_q); 1808 1809 apos = tlm_get_data_offset(bpp->bp_lcmd); 1810 bpp->bp_session->ns_data.dd_module.dm_stats.ms_bytes_processed += 1811 apos - bpos; 1812 1813 return (rv < 0 ? rv : 0); 1814 } 1815 1816 1817 /* 1818 * check_bk_args 1819 * 1820 * Check the argument of the bpp. This is shared function between 1821 * timebk_v3 and lbrbk_v3 functions. The checks include: 1822 * - The bpp itself. 1823 * - If the session pointer of the bpp is valid. 1824 * - If the session connection to the DMA is closed. 1825 * - If the nlp pointer of the bpp is valid. 1826 * - If the backup is aborted. 1827 * 1828 * Parameters: 1829 * bpp (input) - pointer to the backup parameters structure 1830 * 1831 * Returns: 1832 * 0: if everything's OK 1833 * != 0: otherwise 1834 */ 1835 static int 1836 check_bk_args(bk_param_v3_t *bpp) 1837 { 1838 int rv; 1839 1840 if (!bpp) { 1841 rv = -1; 1842 NDMP_LOG(LOG_DEBUG, "Lost bpp"); 1843 } else if (!bpp->bp_session) { 1844 rv = -1; 1845 NDMP_LOG(LOG_DEBUG, "Session is NULL"); 1846 } else if (bpp->bp_session->ns_eof) { 1847 rv = -1; 1848 NDMP_LOG(LOG_INFO, 1849 "Connection client is closed for backup \"%s\"", 1850 bpp->bp_nlp->nlp_backup_path); 1851 } else if (!bpp->bp_nlp) { 1852 NDMP_LOG(LOG_DEBUG, "Lost nlp"); 1853 return (-1); 1854 } else if (bpp->bp_session->ns_data.dd_abort) { 1855 rv = -1; 1856 NDMP_LOG(LOG_INFO, "Backup aborted \"%s\"", 1857 bpp->bp_nlp->nlp_backup_path); 1858 } else 1859 rv = 0; 1860 1861 return (rv); 1862 } 1863 1864 1865 /* 1866 * shouldskip 1867 * 1868 * Determines if the current entry should be skipped or it 1869 * should be backed up. 1870 * 1871 * Parameters: 1872 * bpp (input) - pointer to the backup parameters structure 1873 * pnp (input) - pointer to the path node 1874 * enp (input) - pointer to the entry node 1875 * errp (output) - pointer to the error value that should be 1876 * returned by the caller 1877 * 1878 * Returns: 1879 * TRUE: if the entry should not be backed up 1880 * FALSE: otherwise 1881 */ 1882 static boolean_t 1883 shouldskip(bk_param_v3_t *bpp, fst_node_t *pnp, 1884 fst_node_t *enp, int *errp) 1885 { 1886 char *ent; 1887 boolean_t rv; 1888 struct stat64 *estp; 1889 1890 if (!bpp || !pnp || !enp || !errp) { 1891 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 1892 return (TRUE); 1893 } 1894 1895 if (!enp->tn_path) { 1896 ent = ""; 1897 estp = pnp->tn_st; 1898 } else { 1899 ent = enp->tn_path; 1900 estp = enp->tn_st; 1901 } 1902 1903 /* 1904 * When excluding or skipping entries, FST_SKIP should be 1905 * returned, otherwise, 0 should be returned to 1906 * get other entries in the directory of this entry. 1907 */ 1908 if (!dbm_getone(bpp->bp_nlp->nlp_bkmap, (u_longlong_t)estp->st_ino)) { 1909 rv = TRUE; 1910 *errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0; 1911 NDMP_LOG(LOG_DEBUG, "Skipping %d %s/%s", 1912 *errp, pnp->tn_path, ent); 1913 } else if (tlm_is_excluded(pnp->tn_path, ent, bpp->bp_excls)) { 1914 rv = TRUE; 1915 *errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0; 1916 NDMP_LOG(LOG_DEBUG, "excl %d \"%s/%s\"", 1917 *errp, pnp->tn_path, ent); 1918 } else if (inexl(bpp->bp_nlp->nlp_exl, ent)) { 1919 rv = TRUE; 1920 *errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0; 1921 NDMP_LOG(LOG_DEBUG, "out %d \"%s/%s\"", 1922 *errp, pnp->tn_path, ent); 1923 } else if (!S_ISDIR(estp->st_mode) && 1924 !ininc(bpp->bp_nlp->nlp_inc, ent)) { 1925 rv = TRUE; 1926 *errp = 0; 1927 NDMP_LOG(LOG_DEBUG, "!in \"%s/%s\"", pnp->tn_path, ent); 1928 } else 1929 rv = FALSE; 1930 1931 return (rv); 1932 } 1933 1934 1935 /* 1936 * ischngd 1937 * 1938 * Check if the object specified should be backed up or not. 1939 * If stp belongs to a directory and if it is marked in the 1940 * bitmap vector, it shows that either the directory itself is 1941 * modified or there is something below it that will be backed 1942 * up. 1943 * 1944 * By setting ndmp_force_bk_dirs global variable to a non-zero 1945 * value, directories are backed up anyways. 1946 * 1947 * Backing up the directories unconditionally helps 1948 * restoring the metadata of directories as well, when one 1949 * of the objects below them are being restored. 1950 * 1951 * For non-directory objects, if the modification or change 1952 * time of the object is after the date specified by the 1953 * bk_selector_t, the the object must be backed up. 1954 */ 1955 static boolean_t 1956 ischngd(struct stat64 *stp, time_t t, ndmp_lbr_params_t *nlp) 1957 { 1958 boolean_t rv; 1959 1960 if (!stp) { 1961 rv = FALSE; 1962 NDMP_LOG(LOG_DEBUG, "stp is NULL"); 1963 } else if (!nlp) { 1964 rv = FALSE; 1965 NDMP_LOG(LOG_DEBUG, "nlp is NULL"); 1966 } else if (t == 0) { 1967 /* 1968 * if we are doing base backup then we do not need to 1969 * check the time, for we should backup everything. 1970 */ 1971 rv = TRUE; 1972 NDMP_LOG(LOG_DEBUG, "Base Backup"); 1973 } else if (S_ISDIR(stp->st_mode) && ndmp_force_bk_dirs) { 1974 rv = TRUE; 1975 NDMP_LOG(LOG_DEBUG, "d(%lu)", (uint_t)stp->st_ino); 1976 } else if (S_ISDIR(stp->st_mode) && 1977 dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino) && 1978 ((NLP_ISDUMP(nlp) && ndmp_dump_path_node) || 1979 (NLP_ISTAR(nlp) && ndmp_tar_path_node))) { 1980 /* 1981 * If the object is a directory and it leads to a modified 1982 * object (that should be backed up) and for that type of 1983 * backup the path nodes should be backed up, then return 1984 * TRUE. 1985 * 1986 * This is required by some DMAs like Backup Express, which 1987 * needs to receive ADD_NODE (for dump) or ADD_PATH (for tar) 1988 * for the intermediate directories of a modified object. 1989 * Other DMAs, like net_backup and net_worker, do not have such 1990 * requirement. This requirement makes sense for dump format 1991 * but for 'tar' format, it does not. In provision to the 1992 * NDMP-v4 spec, for 'tar' format the intermediate directories 1993 * need not to be reported. 1994 */ 1995 rv = TRUE; 1996 NDMP_LOG(LOG_DEBUG, "p(%lu)", (u_longlong_t)stp->st_ino); 1997 } else if (stp->st_mtime > t) { 1998 rv = TRUE; 1999 NDMP_LOG(LOG_DEBUG, "m(%lu): %lu > %lu", 2000 (uint_t)stp->st_ino, (uint_t)stp->st_mtime, (uint_t)t); 2001 } else if (stp->st_ctime > t) { 2002 if (NLP_IGNCTIME(nlp)) { 2003 rv = FALSE; 2004 NDMP_LOG(LOG_DEBUG, "ign c(%lu): %lu > %lu", 2005 (uint_t)stp->st_ino, (uint_t)stp->st_ctime, 2006 (uint_t)t); 2007 } else { 2008 rv = TRUE; 2009 NDMP_LOG(LOG_DEBUG, "c(%lu): %lu > %lu", 2010 (uint_t)stp->st_ino, (uint_t)stp->st_ctime, 2011 (uint_t)t); 2012 } 2013 } else { 2014 rv = FALSE; 2015 NDMP_LOG(LOG_DEBUG, "mc(%lu): (%lu,%lu) < %lu", 2016 (uint_t)stp->st_ino, (uint_t)stp->st_mtime, 2017 (uint_t)stp->st_ctime, (uint_t)t); 2018 } 2019 2020 return (rv); 2021 } 2022 2023 2024 /* 2025 * iscreated 2026 * 2027 * This function is used to check last mtime (currently inside the ACL 2028 * structure) instead of ctime for checking if the file is to be backed up 2029 * or not. See option "inc.lmtime" for more details 2030 */ 2031 /*ARGSUSED*/ 2032 int iscreated(ndmp_lbr_params_t *nlp, char *name, tlm_acls_t *tacl, 2033 time_t t) 2034 { 2035 int ret; 2036 acl_t *aclp = NULL; 2037 char *acltp; 2038 2039 NDMP_LOG(LOG_DEBUG, "flags %x", nlp->nlp_flags); 2040 if (NLP_INCLMTIME(nlp) == FALSE) 2041 return (0); 2042 2043 ret = acl_get(name, ACL_NO_TRIVIAL, &aclp); 2044 if (ret != 0) { 2045 NDMP_LOG(LOG_DEBUG, 2046 "Error getting the acl information: err %d", ret); 2047 return (0); 2048 } 2049 if (aclp && (acltp = acl_totext(aclp, 2050 ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) { 2051 (void) strlcpy(tacl->acl_info.attr_info, acltp, 2052 TLM_MAX_ACL_TXT); 2053 acl_free(aclp); 2054 free(acltp); 2055 } 2056 2057 /* Need to add support for last mtime */ 2058 2059 return (0); 2060 } 2061 2062 /* 2063 * size_cb 2064 * 2065 * The callback function for calculating the size of 2066 * the backup path. This is used to get an estimate 2067 * of the progress of backup during NDMP backup 2068 */ 2069 static int 2070 size_cb(void *arg, fst_node_t *pnp, fst_node_t *enp) 2071 { 2072 struct stat64 *stp; 2073 2074 stp = enp->tn_path ? enp->tn_st : pnp->tn_st; 2075 *((u_longlong_t *)arg) += stp->st_size; 2076 2077 return (0); 2078 } 2079 2080 /* 2081 * timebk_v3 2082 * 2083 * The callback function for backing up objects based on 2084 * their time stamp. This is shared between token-based 2085 * and level-based backup, which look at the time stamps 2086 * of the objects to determine if they should be backed 2087 * up. 2088 * 2089 * Parameters: 2090 * arg (input) - pointer to the backup parameters structure 2091 * pnp (input) - pointer to the path node 2092 * enp (input) - pointer to the entry node 2093 * 2094 * Returns: 2095 * 0: if backup should continue 2096 * -1: if the backup should be stopped 2097 * FST_SKIP: if backing up the current directory is enough 2098 */ 2099 static int 2100 timebk_v3(void *arg, fst_node_t *pnp, fst_node_t *enp) 2101 { 2102 char *ent; 2103 int rv; 2104 time_t t; 2105 bk_param_v3_t *bpp; 2106 struct stat64 *stp; 2107 fs_fhandle_t *fhp; 2108 2109 bpp = (bk_param_v3_t *)arg; 2110 2111 rv = check_bk_args(bpp); 2112 if (rv != 0) 2113 return (rv); 2114 2115 stp = enp->tn_path ? enp->tn_st : pnp->tn_st; 2116 if (shouldskip(bpp, pnp, enp, &rv)) 2117 return (rv); 2118 2119 if (enp->tn_path) { 2120 ent = enp->tn_path; 2121 stp = enp->tn_st; 2122 fhp = enp->tn_fh; 2123 } else { 2124 ent = ""; 2125 stp = pnp->tn_st; 2126 fhp = pnp->tn_fh; 2127 } 2128 2129 2130 if (!tlm_cat_path(bpp->bp_tmp, pnp->tn_path, ent)) { 2131 NDMP_LOG(LOG_ERR, "Path too long %s/%s.", pnp->tn_path, ent); 2132 return (FST_SKIP); 2133 } 2134 if (NLP_ISSET(bpp->bp_nlp, NLPF_TOKENBK)) 2135 t = bpp->bp_nlp->nlp_tokdate; 2136 else if (NLP_ISSET(bpp->bp_nlp, NLPF_LEVELBK)) { 2137 t = bpp->bp_nlp->nlp_ldate; 2138 } else { 2139 NDMP_LOG(LOG_DEBUG, "Unknown backup type on \"%s/%s\"", 2140 pnp->tn_path, ent); 2141 return (-1); 2142 } 2143 2144 if (S_ISDIR(stp->st_mode)) { 2145 bpp->bp_tlmacl->acl_dir_fh = *fhp; 2146 (void) ndmpd_fhdir_v3_cb(bpp->bp_nlp->nlp_logcallbacks, 2147 bpp->bp_tmp, stp); 2148 2149 if (ischngd(stp, t, bpp->bp_nlp)) { 2150 (void) memcpy(&bpp->bp_tlmacl->acl_attr, stp, 2151 sizeof (struct stat64)); 2152 rv = backup_dirv3(bpp, pnp, enp); 2153 } 2154 } else { 2155 if (ischngd(stp, t, bpp->bp_nlp) || 2156 iscreated(bpp->bp_nlp, bpp->bp_tmp, bpp->bp_tlmacl, t)) { 2157 rv = 0; 2158 (void) memcpy(&bpp->bp_tlmacl->acl_attr, stp, 2159 sizeof (struct stat64)); 2160 bpp->bp_tlmacl->acl_fil_fh = *fhp; 2161 (void) backup_filev3(bpp, pnp, enp); 2162 } 2163 } 2164 2165 return (rv); 2166 } 2167 2168 2169 /* 2170 * lbrbk_v3 2171 * 2172 * The callback function for backing up objects based on 2173 * their archive directory bit. This is used in LBR-type 2174 * backup. In which the objects are backed up if their 2175 * archive bit is set. 2176 * 2177 * Parameters: 2178 * arg (input) - pointer to the backup parameters structure 2179 * pnp (input) - pointer to the path node 2180 * enp (input) - pointer to the entry node 2181 * 2182 * Returns: 2183 * 0: if backup should continue 2184 * -1: if the backup should be stopped 2185 * FST_SKIP: if backing up the current directory is enough 2186 */ 2187 static int 2188 lbrbk_v3(void *arg, fst_node_t *pnp, fst_node_t *enp) 2189 { 2190 char *ent; 2191 int rv; 2192 bk_param_v3_t *bpp; 2193 struct stat64 *stp; 2194 fs_fhandle_t *fhp; 2195 2196 bpp = (bk_param_v3_t *)arg; 2197 rv = check_bk_args(bpp); 2198 if (rv != 0) 2199 return (rv); 2200 2201 stp = enp->tn_path ? enp->tn_st : pnp->tn_st; 2202 if (shouldskip(bpp, pnp, enp, &rv)) 2203 return (rv); 2204 2205 if (enp->tn_path) { 2206 ent = enp->tn_path; 2207 stp = enp->tn_st; 2208 fhp = enp->tn_fh; 2209 } else { 2210 ent = ""; 2211 stp = pnp->tn_st; 2212 fhp = pnp->tn_fh; 2213 } 2214 2215 if (!tlm_cat_path(bpp->bp_tmp, pnp->tn_path, ent)) { 2216 NDMP_LOG(LOG_ERR, "Path too long %s/%s.", pnp->tn_path, ent); 2217 return (FST_SKIP); 2218 } 2219 if (!NLP_ISSET(bpp->bp_nlp, NLPF_LBRBK)) { 2220 NDMP_LOG(LOG_DEBUG, "!NLPF_LBRBK"); 2221 return (-1); 2222 } 2223 2224 if (S_ISDIR(stp->st_mode)) { 2225 bpp->bp_tlmacl->acl_dir_fh = *fhp; 2226 (void) ndmpd_fhdir_v3_cb(bpp->bp_nlp->nlp_logcallbacks, 2227 bpp->bp_tmp, stp); 2228 2229 if (SHOULD_LBRBK(bpp)) { 2230 bpp->bp_tlmacl->acl_attr = *stp; 2231 rv = backup_dirv3(bpp, pnp, enp); 2232 } 2233 } else if (SHOULD_LBRBK(bpp)) { 2234 rv = 0; 2235 bpp->bp_tlmacl->acl_attr = *stp; 2236 bpp->bp_tlmacl->acl_fil_fh = *fhp; 2237 (void) backup_filev3(bpp, pnp, enp); 2238 } 2239 2240 return (rv); 2241 } 2242 2243 2244 /* 2245 * backup_reader_v3 2246 * 2247 * The reader thread for the backup. It sets up the callback 2248 * parameters and traverses the backup hierarchy in level-order 2249 * way. 2250 * 2251 * Parameters: 2252 * jname (input) - name assigned to the current backup for 2253 * job stats strucure 2254 * nlp (input) - pointer to the nlp structure 2255 * cmds (input) - pointer to the tlm_commands_t structure 2256 * 2257 * Returns: 2258 * 0: on success 2259 * != 0: otherwise 2260 */ 2261 static int 2262 backup_reader_v3(backup_reader_arg_t *argp) 2263 { 2264 int rv; 2265 tlm_cmd_t *lcmd; 2266 tlm_acls_t tlm_acls; 2267 longlong_t bpos, n; 2268 bk_param_v3_t bp; 2269 fs_traverse_t ft; 2270 char *jname; 2271 ndmp_lbr_params_t *nlp; 2272 tlm_commands_t *cmds; 2273 2274 if (!argp) 2275 return (-1); 2276 2277 jname = argp->br_jname; 2278 nlp = argp->br_nlp; 2279 cmds = argp->br_cmds; 2280 2281 rv = 0; 2282 lcmd = cmds->tcs_command; 2283 lcmd->tc_ref++; 2284 cmds->tcs_reader_count++; 2285 2286 (void) memset(&tlm_acls, 0, sizeof (tlm_acls)); 2287 2288 /* NDMP parameters */ 2289 bp.bp_session = nlp->nlp_session; 2290 bp.bp_nlp = nlp; 2291 2292 /* LBR-related parameters */ 2293 bp.bp_js = tlm_ref_job_stats(jname); 2294 bp.bp_cmds = cmds; 2295 bp.bp_lcmd = lcmd; 2296 bp.bp_tlmacl = &tlm_acls; 2297 bp.bp_opr = 0; 2298 2299 /* release the parent thread, after referencing the job stats */ 2300 (void) pthread_barrier_wait(&argp->br_barrier); 2301 2302 bp.bp_tmp = ndmp_malloc(sizeof (char) * TLM_MAX_PATH_NAME); 2303 if (!bp.bp_tmp) 2304 return (-1); 2305 2306 /* 2307 * Make the checkpointed paths for traversing the 2308 * backup hierarchy, if we make the checkpoint. 2309 */ 2310 bp.bp_unchkpnm = nlp->nlp_backup_path; 2311 if (!NLP_ISCHKPNTED(nlp)) { 2312 tlm_acls.acl_checkpointed = TRUE; 2313 bp.bp_chkpnm = ndmp_malloc(sizeof (char) * TLM_MAX_PATH_NAME); 2314 if (!bp.bp_chkpnm) { 2315 NDMP_FREE(bp.bp_tmp); 2316 return (-1); 2317 } 2318 (void) tlm_build_snapshot_name(nlp->nlp_backup_path, 2319 bp.bp_chkpnm, nlp->nlp_jstat->js_job_name); 2320 } else { 2321 tlm_acls.acl_checkpointed = FALSE; 2322 bp.bp_chkpnm = nlp->nlp_backup_path; 2323 } 2324 bp.bp_excls = ndmpd_make_exc_list(); 2325 2326 /* set traversing arguments */ 2327 ft.ft_path = nlp->nlp_backup_path; 2328 ft.ft_lpath = bp.bp_chkpnm; 2329 2330 NDMP_LOG(LOG_DEBUG, "path %s lpath %s", ft.ft_path, ft.ft_lpath); 2331 if (NLP_ISSET(nlp, NLPF_TOKENBK) || NLP_ISSET(nlp, NLPF_LEVELBK)) { 2332 ft.ft_callbk = timebk_v3; 2333 tlm_acls.acl_clear_archive = FALSE; 2334 } else if (NLP_ISSET(nlp, NLPF_LBRBK)) { 2335 ft.ft_callbk = lbrbk_v3; 2336 tlm_acls.acl_clear_archive = FALSE; 2337 2338 NDMP_LOG(LOG_DEBUG, "bp_opr %x clr_arc %c", 2339 bp.bp_opr, NDMP_YORN(tlm_acls.acl_clear_archive)); 2340 } else { 2341 rv = -1; 2342 MOD_LOGV3(nlp->nlp_params, NDMP_LOG_ERROR, 2343 "Unknow backup type.\n"); 2344 } 2345 ft.ft_arg = &bp; 2346 ft.ft_logfp = (ft_log_t)ndmp_log; 2347 ft.ft_flags = FST_VERBOSE; /* Solaris */ 2348 2349 /* take into account the header written to the stream so far */ 2350 n = tlm_get_data_offset(lcmd); 2351 nlp->nlp_session->ns_data.dd_module.dm_stats.ms_bytes_processed = n; 2352 2353 if (rv == 0) { 2354 /* start traversing the hierarchy and actual backup */ 2355 rv = traverse_level(&ft); 2356 if (rv == 0) { 2357 /* write the trailer and update the bytes processed */ 2358 bpos = tlm_get_data_offset(lcmd); 2359 (void) write_tar_eof(lcmd); 2360 n = tlm_get_data_offset(lcmd) - bpos; 2361 nlp->nlp_session-> 2362 ns_data.dd_module.dm_stats.ms_bytes_processed += n; 2363 } 2364 } 2365 2366 if (!NLP_ISCHKPNTED(nlp)) 2367 NDMP_FREE(bp.bp_chkpnm); 2368 NDMP_FREE(bp.bp_tmp); 2369 NDMP_FREE(bp.bp_excls); 2370 2371 cmds->tcs_reader_count--; 2372 lcmd->tc_writer = TLM_STOP; 2373 tlm_release_reader_writer_ipc(lcmd); 2374 tlm_un_ref_job_stats(jname); 2375 return (rv); 2376 2377 } 2378 2379 2380 /* 2381 * tar_backup_v3 2382 * 2383 * Traverse the backup hierarchy if needed and make the bitmap. 2384 * Then launch reader and writer threads to do the actual backup. 2385 * 2386 * Parameters: 2387 * session (input) - pointer to the session 2388 * params (input) - pointer to the parameters structure 2389 * nlp (input) - pointer to the nlp structure 2390 * jname (input) - job name 2391 * 2392 * Returns: 2393 * 0: on success 2394 * != 0: otherwise 2395 */ 2396 static int 2397 tar_backup_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 2398 ndmp_lbr_params_t *nlp, char *jname) 2399 { 2400 tlm_commands_t *cmds; 2401 backup_reader_arg_t arg; 2402 pthread_t rdtp; 2403 char info[256]; 2404 int result; 2405 ndmp_context_t nctx; 2406 int err; 2407 2408 if (ndmp_get_bk_dir_ino(nlp)) 2409 return (-1); 2410 2411 result = err = 0; 2412 2413 /* exit as if there was an internal error */ 2414 if (session->ns_eof) 2415 return (-1); 2416 2417 if (!session->ns_data.dd_abort) { 2418 if (backup_alloc_structs_v3(session, jname) < 0) { 2419 nlp->nlp_bkmap = -1; 2420 return (-1); 2421 } 2422 2423 if (ndmpd_mark_inodes_v3(session, nlp) != 0) { 2424 if (nlp->nlp_bkmap != -1) { 2425 (void) dbm_free(nlp->nlp_bkmap); 2426 nlp->nlp_bkmap = -1; 2427 } 2428 free_structs_v3(session, jname); 2429 return (-1); 2430 } 2431 2432 nlp->nlp_jstat->js_start_ltime = time(NULL); 2433 nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime; 2434 nlp->nlp_jstat->js_chkpnt_time = nlp->nlp_cdate; 2435 2436 cmds = &nlp->nlp_cmds; 2437 cmds->tcs_reader = cmds->tcs_writer = TLM_BACKUP_RUN; 2438 cmds->tcs_command->tc_reader = TLM_BACKUP_RUN; 2439 cmds->tcs_command->tc_writer = TLM_BACKUP_RUN; 2440 2441 if (ndmp_write_utf8magic(cmds->tcs_command) < 0) { 2442 free_structs_v3(session, jname); 2443 return (-1); 2444 } 2445 2446 NDMP_LOG(LOG_DEBUG, 2447 "Backing up \"%s\" started.", nlp->nlp_backup_path); 2448 2449 /* Plug-in module */ 2450 if (ndmp_pl != NULL && 2451 ndmp_pl->np_pre_backup != NULL) { 2452 (void) memset(&nctx, 0, sizeof (ndmp_context_t)); 2453 nctx.nc_plversion = ndmp_pl->np_plversion; 2454 nctx.nc_plname = ndmpd_get_prop(NDMP_PLUGIN_PATH); 2455 nctx.nc_cmds = cmds; 2456 nctx.nc_params = params; 2457 if ((err = ndmp_pl->np_pre_backup(ndmp_pl, &nctx, 2458 nlp->nlp_backup_path)) != 0) { 2459 NDMP_LOG(LOG_ERR, "Pre-backup plug-in: %m"); 2460 goto backup_out; 2461 } 2462 } 2463 2464 (void) memset(&arg, 0, sizeof (backup_reader_arg_t)); 2465 arg.br_jname = jname; 2466 arg.br_nlp = nlp; 2467 arg.br_cmds = cmds; 2468 2469 (void) pthread_barrier_init(&arg.br_barrier, 0, 2); 2470 2471 err = pthread_create(&rdtp, NULL, (funct_t)backup_reader_v3, 2472 (void *)&arg); 2473 if (err == 0) { 2474 (void) pthread_barrier_wait(&arg.br_barrier); 2475 (void) pthread_barrier_destroy(&arg.br_barrier); 2476 } else { 2477 (void) pthread_barrier_destroy(&arg.br_barrier); 2478 free_structs_v3(session, jname); 2479 NDMP_LOG(LOG_DEBUG, "Launch backup_reader_v3: %m"); 2480 return (-1); 2481 } 2482 2483 if ((err = ndmp_tar_writer(session, params, cmds)) != 0) 2484 result = EIO; 2485 2486 nlp->nlp_jstat->js_stop_time = time(NULL); 2487 2488 (void) snprintf(info, sizeof (info), 2489 "Runtime [%s] %llu bytes (%llu): %d seconds\n", 2490 nlp->nlp_backup_path, 2491 session->ns_data.dd_module.dm_stats.ms_bytes_processed, 2492 session->ns_data.dd_module.dm_stats.ms_bytes_processed, 2493 nlp->nlp_jstat->js_stop_time - 2494 nlp->nlp_jstat->js_start_ltime); 2495 MOD_LOGV3(params, NDMP_LOG_NORMAL, info); 2496 2497 ndmp_wait_for_reader(cmds); 2498 (void) pthread_join(rdtp, NULL); 2499 2500 /* exit as if there was an internal error */ 2501 if (session->ns_eof) { 2502 result = EPIPE; 2503 err = -1; 2504 } 2505 if (!session->ns_data.dd_abort) { 2506 ndmpd_audit_backup(session->ns_connection, 2507 nlp->nlp_backup_path, 2508 session->ns_data.dd_data_addr.addr_type, 2509 session->ns_tape.td_adapter_name, result); 2510 NDMP_LOG(LOG_DEBUG, "Backing up \"%s\" Finished.", 2511 nlp->nlp_backup_path); 2512 } 2513 } 2514 2515 if (session->ns_data.dd_abort) { 2516 ndmpd_audit_backup(session->ns_connection, 2517 nlp->nlp_backup_path, 2518 session->ns_data.dd_data_addr.addr_type, 2519 session->ns_tape.td_adapter_name, EINTR); 2520 NDMP_LOG(LOG_DEBUG, 2521 "Backing up \"%s\" aborted.", nlp->nlp_backup_path); 2522 err = -1; 2523 } else { 2524 2525 backup_out: 2526 /* Plug-in module */ 2527 if (ndmp_pl != NULL && 2528 ndmp_pl->np_post_backup != NULL && 2529 ndmp_pl->np_post_backup(ndmp_pl, &nctx, err) == -1) { 2530 NDMP_LOG(LOG_DEBUG, "Post-backup plug-in: %m"); 2531 return (-1); 2532 } 2533 } 2534 2535 free_structs_v3(session, jname); 2536 return (err); 2537 } 2538 2539 /* 2540 * get_backup_size 2541 * 2542 * Find the estimate of backup size. This is used to get an estimate 2543 * of the progress of backup during NDMP backup. 2544 */ 2545 void 2546 get_backup_size(ndmp_bkup_size_arg_t *sarg) 2547 { 2548 fs_traverse_t ft; 2549 u_longlong_t bk_size; 2550 char spath[PATH_MAX]; 2551 int rv; 2552 2553 bk_size = 0; 2554 if (fs_is_chkpntvol(sarg->bs_path)) { 2555 ft.ft_path = sarg->bs_path; 2556 } else { 2557 (void) tlm_build_snapshot_name(sarg->bs_path, 2558 spath, sarg->bs_jname); 2559 ft.ft_path = spath; 2560 } 2561 2562 ft.ft_lpath = ft.ft_path; 2563 ft.ft_callbk = size_cb; 2564 ft.ft_arg = &bk_size; 2565 ft.ft_logfp = (ft_log_t)ndmp_log; 2566 ft.ft_flags = FST_VERBOSE; 2567 2568 if ((rv = traverse_level(&ft)) != 0) { 2569 NDMP_LOG(LOG_DEBUG, "bksize err=%d", rv); 2570 bk_size = 0; 2571 } else { 2572 NDMP_LOG(LOG_DEBUG, "bksize %lld, %lldKB, %lldMB\n", 2573 bk_size, bk_size / 1024, bk_size /(1024 * 1024)); 2574 } 2575 sarg->bs_session->ns_data.dd_data_size = bk_size; 2576 } 2577 2578 /* 2579 * get_rs_path_v3 2580 * 2581 * Find the restore path 2582 */ 2583 ndmp_error 2584 get_rs_path_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 2585 { 2586 char *dp; 2587 ndmp_error rv; 2588 mem_ndmp_name_v3_t *ep; 2589 int i, nm_cnt; 2590 char *nm_dpath_list[MULTIPLE_DEST_DIRS]; 2591 static char mdest_buf[256]; 2592 2593 *mdest_buf = 0; 2594 *nm_dpath_list = ""; 2595 for (i = 0, nm_cnt = 0; i < (int)nlp->nlp_nfiles; i++) { 2596 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i); 2597 if (!ep) { 2598 NDMP_LOG(LOG_DEBUG, "Can't get Nlist[%d]", i); 2599 return (NDMP_ILLEGAL_ARGS_ERR); 2600 } 2601 if (strcmp(nm_dpath_list[nm_cnt], ep->nm3_dpath) != 0 && 2602 nm_cnt < MULTIPLE_DEST_DIRS - 1) 2603 nm_dpath_list[++nm_cnt] = ep->nm3_dpath; 2604 } 2605 2606 multiple_dest_restore = (nm_cnt > 1); 2607 nlp->nlp_restore_path = mdest_buf; 2608 2609 for (i = 1; i < nm_cnt + 1; i++) { 2610 if (ISDEFINED(nm_dpath_list[i])) 2611 dp = nm_dpath_list[i]; 2612 else 2613 /* the default destination path is backup directory */ 2614 dp = nlp->nlp_backup_path; 2615 2616 /* check the destination directory exists and is writable */ 2617 if (!fs_volexist(dp)) { 2618 rv = NDMP_ILLEGAL_ARGS_ERR; 2619 MOD_LOGV3(params, NDMP_LOG_ERROR, 2620 "Invalid destination path volume \"%s\".\n", dp); 2621 } else if (!voliswr(dp)) { 2622 rv = NDMP_ILLEGAL_ARGS_ERR; 2623 MOD_LOGV3(params, NDMP_LOG_ERROR, 2624 "The destination path volume" 2625 " is not writable \"%s\".\n", dp); 2626 } else { 2627 rv = NDMP_NO_ERR; 2628 (void) strlcat(nlp->nlp_restore_path, dp, 2629 sizeof (mdest_buf)); 2630 NDMP_LOG(LOG_DEBUG, "rspath: \"%s\"", dp); 2631 } 2632 2633 /* 2634 * Exit if there is an error or it is not a multiple 2635 * destination restore mode 2636 */ 2637 if (rv != NDMP_NO_ERR || !multiple_dest_restore) 2638 break; 2639 2640 if (i < nm_cnt) 2641 (void) strlcat(nlp->nlp_restore_path, ", ", 2642 sizeof (mdest_buf)); 2643 } 2644 2645 return (rv); 2646 } 2647 2648 2649 /* 2650 * fix_nlist_v3 2651 * 2652 * Check if the recovery list is valid and fix it if there are some 2653 * unspecified entries in it. It checks for original, destination 2654 * and new path for all NDMP names provided inside the list. 2655 * 2656 * V3: dpath is the destination directory. If newnm is not NULL, the 2657 * destination path is dpath/newnm. Otherwise the destination path is 2658 * dpath/opath_last_node, where opath_last_node is the last node in opath. 2659 * 2660 * V4: If newnm is not NULL, dpath is the destination directory, and 2661 * dpath/newnm is the destination path. If newnm is NULL, dpath is 2662 * the destination path (opath is not involved in forming destination path). 2663 */ 2664 ndmp_error 2665 fix_nlist_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 2666 ndmp_lbr_params_t *nlp) 2667 { 2668 char *cp, *buf, *bp; 2669 int i, n; 2670 int iswrbk; 2671 int bvexists; 2672 ndmp_error rv; 2673 mem_ndmp_name_v3_t *ep; 2674 char *dp; 2675 char *nm; 2676 int existsvol; 2677 int isrwdst; 2678 2679 buf = ndmp_malloc(TLM_MAX_PATH_NAME); 2680 if (!buf) { 2681 MOD_LOGV3(params, NDMP_LOG_ERROR, "Insufficient memory.\n"); 2682 return (NDMP_NO_MEM_ERR); 2683 } 2684 2685 bvexists = fs_volexist(nlp->nlp_backup_path); 2686 iswrbk = voliswr(nlp->nlp_backup_path); 2687 2688 rv = NDMP_NO_ERR; 2689 n = session->ns_data.dd_nlist_len; 2690 for (i = 0; i < n; i++) { 2691 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i); 2692 if (!ep) 2693 continue; 2694 2695 /* chop off the trailing slashes */ 2696 chopslash(ep->nm3_opath); 2697 2698 chopslash(ep->nm3_dpath); 2699 chopslash(ep->nm3_newnm); 2700 2701 /* existing and non-empty destination path */ 2702 if (ISDEFINED(ep->nm3_dpath)) { 2703 dp = ep->nm3_dpath; 2704 existsvol = fs_volexist(dp); 2705 isrwdst = voliswr(dp); 2706 } else { 2707 /* the default destination path is backup directory */ 2708 dp = nlp->nlp_backup_path; 2709 existsvol = bvexists; 2710 isrwdst = iswrbk; 2711 } 2712 2713 /* check the destination directory exists and is writable */ 2714 if (!existsvol) { 2715 rv = NDMP_ILLEGAL_ARGS_ERR; 2716 MOD_LOGV3(params, NDMP_LOG_ERROR, 2717 "Invalid destination path volume " 2718 "\"%s\".\n", dp); 2719 break; 2720 } 2721 if (!isrwdst) { 2722 rv = NDMP_ILLEGAL_ARGS_ERR; 2723 MOD_LOGV3(params, NDMP_LOG_ERROR, 2724 "The destination path volume is not " 2725 "writable \"%s\".\n", dp); 2726 break; 2727 } 2728 2729 /* 2730 * If new name is not specified, the default new name is 2731 * the last component of the original path, if any 2732 * (except in V4). 2733 */ 2734 if (session->ns_protocol_version == NDMPV4) { 2735 nm = ep->nm3_newnm; 2736 } else { 2737 if (ISDEFINED(ep->nm3_newnm)) { 2738 nm = ep->nm3_newnm; 2739 } else { 2740 /* 2741 * Find the last component of nm3_opath. 2742 * nm3_opath has no trailing '/'. 2743 */ 2744 char *p = strrchr(ep->nm3_opath, '/'); 2745 nm = p? p : ep->nm3_opath; 2746 } 2747 } 2748 2749 bp = joinpath(buf, dp, nm); 2750 if (!bp) { 2751 /* 2752 * Note: What should be done with this entry? 2753 * We leave it untouched for now, hence no path in 2754 * the backup image matches with this entry and will 2755 * be reported as not found. 2756 */ 2757 MOD_LOGV3(params, NDMP_LOG_ERROR, 2758 "Destination path too long(%s/%s)", dp, nm); 2759 continue; 2760 } 2761 cp = strdup(bp); 2762 if (!cp) { 2763 MOD_LOGV3(params, NDMP_LOG_ERROR, 2764 "Insufficient memory.\n"); 2765 rv = NDMP_NO_MEM_ERR; 2766 break; 2767 } 2768 free(ep->nm3_dpath); 2769 ep->nm3_dpath = cp; 2770 NDMP_FREE(ep->nm3_newnm); 2771 2772 bp = joinpath(buf, nlp->nlp_backup_path, ep->nm3_opath); 2773 if (!bp) { 2774 /* 2775 * Note: The same problem of above with long path. 2776 */ 2777 MOD_LOGV3(params, NDMP_LOG_ERROR, 2778 "Path too long(%s/%s)", 2779 nlp->nlp_backup_path, ep->nm3_opath); 2780 continue; 2781 } 2782 cp = strdup(bp); 2783 if (!cp) { 2784 MOD_LOGV3(params, NDMP_LOG_ERROR, 2785 "Insufficient memory.\n"); 2786 rv = NDMP_NO_MEM_ERR; 2787 break; 2788 } 2789 NDMP_FREE(ep->nm3_opath); 2790 ep->nm3_opath = cp; 2791 2792 NDMP_LOG(LOG_DEBUG, "orig[%d]: \"%s\"", i, ep->nm3_opath); 2793 if (ep->nm3_dpath) { 2794 NDMP_LOG(LOG_DEBUG, 2795 "dest[%d]: \"%s\"", i, ep->nm3_dpath); 2796 } else { 2797 NDMP_LOG(LOG_DEBUG, "dest[%d]: \"%s\"", i, "NULL"); 2798 } 2799 } 2800 2801 free(buf); 2802 2803 return (rv); 2804 } 2805 2806 2807 /* 2808 * allvalidfh 2809 * 2810 * Run a sanity check on the file history info. The file history 2811 * info is the offset of the record starting the entry on the tape 2812 * and is used in DAR (direct access restore mode). 2813 */ 2814 static boolean_t 2815 allvalidfh(ndmpd_session_t *session, ndmpd_module_params_t *params) 2816 { 2817 int i, n; 2818 boolean_t rv; 2819 mem_ndmp_name_v3_t *ep; 2820 2821 rv = TRUE; 2822 n = session->ns_data.dd_nlist_len; 2823 for (i = 0; i < n; i++) { 2824 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i); 2825 if (!ep) 2826 continue; 2827 /* 2828 * The fh_info's sent from the client are multiples 2829 * of RECORDSIZE which is 512 bytes. 2830 * 2831 * All our fh_info's are at the RECORDSIZE boundary. If there 2832 * is any fh_info that is less than RECORDSIZE (this covers 0 2833 * and -1 values too), then the result is that DAR cannot be 2834 * done. 2835 */ 2836 if (ep->nm3_fh_info < RECORDSIZE || 2837 ep->nm3_fh_info % RECORDSIZE != 0) { 2838 rv = FALSE; 2839 break; 2840 } 2841 } 2842 2843 return (rv); 2844 } 2845 2846 2847 /* 2848 * log_rs_params_v3 2849 * 2850 * Log a copy of all values of the restore parameters 2851 */ 2852 void 2853 log_rs_params_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 2854 ndmp_lbr_params_t *nlp) 2855 { 2856 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Restoring to \"%s\".\n", 2857 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL"); 2858 2859 if (session->ns_data.dd_data_addr.addr_type == NDMP_ADDR_LOCAL) { 2860 MOD_LOGV3(params, NDMP_LOG_NORMAL, "Tape server: local.\n"); 2861 MOD_LOGV3(params, NDMP_LOG_NORMAL, 2862 "Tape record size: %d.\n", 2863 session->ns_mover.md_record_size); 2864 } else if (session->ns_data.dd_data_addr.addr_type == NDMP_ADDR_TCP) 2865 MOD_LOGV3(params, NDMP_LOG_NORMAL, 2866 "Tape server: remote at %s:%d.\n", 2867 inet_ntoa(IN_ADDR(session->ns_data.dd_data_addr.tcp_ip_v3)), 2868 session->ns_data.dd_data_addr.tcp_port_v3); 2869 else 2870 MOD_LOGV3(params, NDMP_LOG_ERROR, 2871 "Unknown tape server address type.\n"); 2872 2873 if (NLP_ISSET(nlp, NLPF_DIRECT)) 2874 MOD_LOGV3(params, NDMP_LOG_NORMAL, 2875 "Direct Access Restore.\n"); 2876 } 2877 2878 2879 /* 2880 * send_unrecovered_list_v3 2881 * 2882 * Create the list of files that were in restore list but 2883 * not recovered due to some errors. 2884 */ 2885 int 2886 send_unrecovered_list_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp) 2887 { 2888 int i, rv; 2889 int err; 2890 2891 if (!params) { 2892 NDMP_LOG(LOG_DEBUG, "params == NULL"); 2893 return (-1); 2894 } 2895 if (!nlp) { 2896 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 2897 return (-1); 2898 } 2899 2900 if (nlp->nlp_lastidx != -1) { 2901 if (!bm_getone(nlp->nlp_rsbm, (u_longlong_t)nlp->nlp_lastidx)) 2902 err = ENOENT; 2903 else 2904 err = 0; 2905 (void) ndmp_send_recovery_stat_v3(params, nlp, 2906 nlp->nlp_lastidx, err); 2907 nlp->nlp_lastidx = -1; 2908 } 2909 2910 rv = 0; 2911 for (i = 0; i < (int)nlp->nlp_nfiles; i++) { 2912 if (!bm_getone(nlp->nlp_rsbm, (u_longlong_t)i)) { 2913 rv = ndmp_send_recovery_stat_v3(params, nlp, i, ENOENT); 2914 if (rv < 0) 2915 break; 2916 } 2917 } 2918 2919 return (rv); 2920 } 2921 2922 2923 2924 /* 2925 * restore_dar_alloc_structs_v3 2926 * 2927 * Allocates the necessary structures for running DAR restore. 2928 * It just creates the reader writer IPC. 2929 * This function is called for each entry in the restore entry list. 2930 * 2931 * Parameters: 2932 * session (input) - pointer to the session 2933 * jname (input) - Job name 2934 * 2935 * Returns: 2936 * 0: on success 2937 * -1: on error 2938 */ 2939 int 2940 restore_dar_alloc_structs_v3(ndmpd_session_t *session, char *jname) 2941 { 2942 long xfer_size; 2943 ndmp_lbr_params_t *nlp; 2944 tlm_commands_t *cmds; 2945 2946 nlp = ndmp_get_nlp(session); 2947 if (!nlp) { 2948 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 2949 return (-1); 2950 } 2951 2952 cmds = &nlp->nlp_cmds; 2953 (void) memset(cmds, 0, sizeof (*cmds)); 2954 2955 xfer_size = ndmp_buffer_get_size(session); 2956 cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size); 2957 if (!cmds->tcs_command) { 2958 tlm_un_ref_job_stats(jname); 2959 return (-1); 2960 } 2961 2962 return (0); 2963 } 2964 2965 2966 /* 2967 * free_dar_structs_v3 2968 * 2969 * To free the structures were created by restore_dar_alloc_structs_v3. 2970 * This funnction is called for each entry in restore entry list. 2971 * 2972 * Parameters: 2973 * session (input) - pointer to the session 2974 * jname (input) - job name 2975 * 2976 * Returns: 2977 * NONE 2978 */ 2979 /*ARGSUSED*/ 2980 static void 2981 free_dar_structs_v3(ndmpd_session_t *session, char *jname) 2982 { 2983 ndmp_lbr_params_t *nlp; 2984 tlm_commands_t *cmds; 2985 2986 nlp = ndmp_get_nlp(session); 2987 if (!nlp) { 2988 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 2989 return; 2990 } 2991 cmds = &nlp->nlp_cmds; 2992 if (!cmds) { 2993 NDMP_LOG(LOG_DEBUG, "cmds == NULL"); 2994 return; 2995 } 2996 2997 if (cmds->tcs_command) { 2998 if (cmds->tcs_command->tc_buffers != NULL) 2999 tlm_release_reader_writer_ipc(cmds->tcs_command); 3000 else 3001 NDMP_LOG(LOG_DEBUG, "BUFFERS == NULL"); 3002 cmds->tcs_command = NULL; 3003 } else 3004 NDMP_LOG(LOG_DEBUG, "COMMAND == NULL"); 3005 } 3006 3007 3008 /* 3009 * ndmp_dar_tar_init_v3 3010 * 3011 * Constructor for the DAR restore. Creates job name, allocates structures 3012 * needed for keeping the statistics, and reports the start of restore action. 3013 * It is called once for each DAR restore request. 3014 * 3015 * Parameters: 3016 * session (input) - pointer to the session 3017 * nlp (input) - pointer to the nlp structure 3018 * 3019 * Returns: 3020 * char pointer: on success 3021 * NULL: on error 3022 */ 3023 static char *ndmpd_dar_tar_init_v3(ndmpd_session_t *session, 3024 ndmp_lbr_params_t *nlp) 3025 { 3026 char *jname; 3027 3028 jname = ndmp_malloc(TLM_MAX_BACKUP_JOB_NAME); 3029 3030 if (!jname) 3031 return (NULL); 3032 3033 (void) ndmp_new_job_name(jname); 3034 3035 if (!nlp) { 3036 free(jname); 3037 NDMP_LOG(LOG_DEBUG, "nlp == NULL"); 3038 return (NULL); 3039 } 3040 3041 nlp->nlp_jstat = tlm_new_job_stats(jname); 3042 if (!nlp->nlp_jstat) { 3043 free(jname); 3044 NDMP_LOG(LOG_DEBUG, "Creating job stats"); 3045 return (NULL); 3046 } 3047 3048 nlp->nlp_jstat->js_start_ltime = time(NULL); 3049 nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime; 3050 3051 nlp->nlp_logcallbacks = lbrlog_callbacks_init(session, 3052 ndmpd_path_restored_v3, NULL, NULL); 3053 if (!nlp->nlp_logcallbacks) { 3054 tlm_un_ref_job_stats(jname); 3055 free(jname); 3056 return (NULL); 3057 } 3058 nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks); 3059 3060 nlp->nlp_rsbm = bm_alloc(nlp->nlp_nfiles, 0); 3061 if (nlp->nlp_rsbm < 0) { 3062 NDMP_LOG(LOG_ERR, "Out of memory."); 3063 lbrlog_callbacks_done(nlp->nlp_logcallbacks); 3064 tlm_un_ref_job_stats(jname); 3065 free(jname); 3066 return (NULL); 3067 } 3068 3069 /* this is used in ndmpd_path_restored_v3() */ 3070 nlp->nlp_lastidx = -1; 3071 3072 NDMP_LOG(LOG_DEBUG, "Restoring from %s tape(s).", 3073 ndmp_data_get_mover_mode(session)); 3074 3075 return (jname); 3076 } 3077 3078 /* 3079 * ndmpd_dar_tar_end_v3 3080 * 3081 * Deconstructor for the DAR restore. This function is called once per 3082 * DAR request. It deallocates memories allocated by ndmpd_dar_tar_init_v3. 3083 * 3084 * Parameters: 3085 * session (input) - pointer to the session 3086 * params (input) - pointer to the parameters structure 3087 * nlp (input) - pointer to the nlp structure 3088 * jname(input) - job name 3089 * 3090 * Returns: 3091 * 0: on success 3092 * -1: on error 3093 */ 3094 static int ndmpd_dar_tar_end_v3(ndmpd_session_t *session, 3095 ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp, char *jname) 3096 { 3097 int err = 0; 3098 3099 3100 NDMP_LOG(LOG_DEBUG, "lastidx %d", nlp->nlp_lastidx); 3101 3102 /* nothing restored. */ 3103 (void) send_unrecovered_list_v3(params, nlp); 3104 3105 if (nlp->nlp_jstat) { 3106 nlp->nlp_bytes_total = 3107 (u_longlong_t)nlp->nlp_jstat->js_bytes_total; 3108 tlm_un_ref_job_stats(jname); 3109 nlp->nlp_jstat = NULL; 3110 } else { 3111 NDMP_LOG(LOG_DEBUG, "JSTAT == NULL"); 3112 } 3113 3114 if (nlp->nlp_logcallbacks) { 3115 lbrlog_callbacks_done(nlp->nlp_logcallbacks); 3116 nlp->nlp_logcallbacks = NULL; 3117 } else { 3118 NDMP_LOG(LOG_DEBUG, "FH CALLBACKS == NULL"); 3119 } 3120 3121 if (session->ns_data.dd_abort) { 3122 NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" aborted.", 3123 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL"); 3124 err = EINTR; 3125 } else { 3126 NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" finished. (%d)", 3127 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : 3128 "NULL", err); 3129 } 3130 3131 if (session->ns_data.dd_operation == NDMP_DATA_OP_RECOVER) { 3132 if (nlp->nlp_rsbm < 0) { 3133 NDMP_LOG(LOG_DEBUG, "nlp_rsbm < 0 %d", nlp->nlp_rsbm); 3134 } else { 3135 (void) bm_free(nlp->nlp_rsbm); 3136 nlp->nlp_rsbm = -1; 3137 } 3138 } 3139 3140 free(jname); 3141 3142 return (err); 3143 } 3144 3145 3146 /* 3147 * ndmpd_dar_tar_v3 3148 * 3149 * This function is called for each entry in DAR entry list. The window 3150 * is already located and we should be in the right position to read 3151 * the data from the tape. 3152 * For each entry we setup selection list; so that, if the file name from 3153 * tape is not as the name client asked for, error be returned. 3154 * 3155 * Parameters: 3156 * session (input) - pointer to the session 3157 * params (input) - pointer to the parameters structure 3158 * nlp (input) - pointer to the nlp structure 3159 * jname (input) - job name 3160 * dar_index(input) - Index of this entry in the restore list 3161 * 3162 * Returns: 3163 * 0: on success 3164 * -1: on error 3165 */ 3166 static int 3167 ndmpd_dar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 3168 ndmp_lbr_params_t *nlp, char *jname, int dar_index) 3169 { 3170 char *excl; 3171 char **sels; 3172 int flags; 3173 int err; 3174 tlm_commands_t *cmds; 3175 struct rs_name_maker rn; 3176 int data_addr_type = session->ns_data.dd_data_addr.addr_type; 3177 ndmp_tar_reader_arg_t arg; 3178 pthread_t rdtp; 3179 ndmp_context_t nctx; 3180 mem_ndmp_name_v3_t *ep; 3181 3182 err = 0; 3183 3184 /* 3185 * We have to allocate and deallocate buffers every time we 3186 * run the restore, for we need to flush the buffers. 3187 */ 3188 if (restore_dar_alloc_structs_v3(session, jname) < 0) 3189 return (-1); 3190 3191 sels = setupsels(session, params, nlp, dar_index); 3192 if (!sels) { 3193 free_dar_structs_v3(session, jname); 3194 return (-1); 3195 } 3196 excl = NULL; 3197 flags = RSFLG_OVR_ALWAYS; 3198 rn.rn_nlp = nlp; 3199 rn.rn_fp = mknewname; 3200 3201 if (!session->ns_data.dd_abort) { 3202 cmds = &nlp->nlp_cmds; 3203 cmds->tcs_reader = cmds->tcs_writer = TLM_RESTORE_RUN; 3204 cmds->tcs_command->tc_reader = TLM_RESTORE_RUN; 3205 cmds->tcs_command->tc_writer = TLM_RESTORE_RUN; 3206 3207 arg.tr_session = session; 3208 arg.tr_mod_params = params; 3209 arg.tr_cmds = cmds; 3210 3211 err = pthread_create(&rdtp, NULL, (funct_t)ndmp_tar_reader, 3212 (void *)&arg); 3213 if (err == 0) { 3214 tlm_cmd_wait(cmds->tcs_command, TLM_TAR_READER); 3215 } else { 3216 NDMP_LOG(LOG_DEBUG, "launch ndmp_tar_reader: %m"); 3217 return (-1); 3218 } 3219 3220 cmds->tcs_command->tc_ref++; 3221 cmds->tcs_writer_count++; 3222 3223 /* Plug-in module */ 3224 if (ndmp_pl != NULL && 3225 ndmp_pl->np_pre_restore != NULL) { 3226 nctx.nc_cmds = cmds; 3227 nctx.nc_params = params; 3228 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, 3229 dar_index - 1); 3230 3231 if ((err = ndmp_pl->np_pre_restore(ndmp_pl, &nctx, 3232 ep->nm3_opath, ep->nm3_dpath)) 3233 != 0) { 3234 NDMP_LOG(LOG_ERR, "Pre-restore plug-in: %m"); 3235 cmds->tcs_command->tc_reader = TLM_STOP; 3236 ndmp_stop_local_reader(session, cmds); 3237 ndmp_wait_for_reader(cmds); 3238 (void) pthread_join(rdtp, NULL); 3239 ndmp_stop_remote_reader(session); 3240 goto restore_out; 3241 } 3242 } 3243 3244 if (tm_tar_ops.tm_getdir != NULL) { 3245 err = (tm_tar_ops.tm_getdir)(cmds, cmds->tcs_command, 3246 nlp->nlp_jstat, &rn, 1, 1, sels, &excl, flags, 3247 dar_index, nlp->nlp_backup_path, 3248 session->hardlink_q); 3249 } 3250 3251 cmds->tcs_writer_count--; 3252 cmds->tcs_command->tc_ref--; 3253 cmds->tcs_command->tc_reader = TLM_STOP; 3254 3255 3256 /* 3257 * If it is a two-way restore then we stop the reader. 3258 */ 3259 NDMP_LOG(LOG_DEBUG, "stop local reader."); 3260 ndmp_stop_local_reader(session, cmds); 3261 3262 ndmp_wait_for_reader(cmds); 3263 (void) pthread_join(rdtp, NULL); 3264 3265 /* 3266 * If this is the last DAR entry and it is a three-way 3267 * restore then we should close the connection. 3268 */ 3269 if ((data_addr_type == NDMP_ADDR_TCP) && 3270 (dar_index == (int)session->ns_data.dd_nlist_len)) { 3271 NDMP_LOG(LOG_DEBUG, "stop remote reader."); 3272 ndmp_stop_remote_reader(session); 3273 } 3274 3275 /* exit as if there was an internal error */ 3276 if (session->ns_eof) 3277 err = -1; 3278 restore_out: 3279 /* Plug-in module */ 3280 if (ndmp_pl != NULL && 3281 ndmp_pl->np_post_restore != NULL && 3282 ndmp_pl->np_post_restore(ndmp_pl, &nctx, err) == -1) { 3283 NDMP_LOG(LOG_DEBUG, "Post-restore plug-in: %m"); 3284 err = -1; 3285 } 3286 } 3287 3288 NDMP_FREE(sels); 3289 3290 free_dar_structs_v3(session, jname); 3291 3292 return (err); 3293 } 3294 3295 /* 3296 * ndmpd_dar_locate_windwos_v3 3297 * 3298 * Locating the right window in which the requested file is backed up. 3299 * We should go through windows to find the exact location, for the 3300 * file can be located in for example 10th window after the current window. 3301 * 3302 * Parameters: 3303 * session (input) - pointer to the session 3304 * params (input) - pointer to the parameters structure 3305 * fh_info (input) - index from the beginning of the backup stream 3306 * len (input) - Length of the mover window 3307 * 3308 * Returns: 3309 * 0: on success 3310 * -1: on error 3311 */ 3312 static int 3313 ndmpd_dar_locate_window_v3(ndmpd_session_t *session, 3314 ndmpd_module_params_t *params, u_longlong_t fh_info, u_longlong_t len) 3315 { 3316 int ret = 0; 3317 3318 3319 for (; ; ) { 3320 ret = (*params->mp_seek_func)(session, fh_info, len); 3321 3322 NDMP_LOG(LOG_DEBUG, "ret %d", ret); 3323 if (ret == 0) /* Seek was done successfully */ 3324 break; 3325 else if (ret < 0) { 3326 NDMP_LOG(LOG_DEBUG, "Seek error"); 3327 break; 3328 } 3329 3330 /* 3331 * DMA moved to a new window. 3332 * If we are reading the remainig of the file from 3333 * new window, seek is handled by ndmpd_local_read_v3. 3334 * Here we should continue the seek inside the new 3335 * window. 3336 */ 3337 continue; 3338 } 3339 return (ret); 3340 } 3341 3342 /* 3343 * ndmpd_rs_dar_tar_v3 3344 * 3345 * Main DAR function. It calls the constructor, then for each entry it 3346 * calls the locate_window_v3 to find the exact position of the file. Then 3347 * it restores the file. 3348 * When all restore requests are done it calls the deconstructor to clean 3349 * everything up. 3350 * 3351 * Parameters: 3352 * session (input) - pointer to the session 3353 * params (input) - pointer to the parameters structure 3354 * nlp (input) - pointer to the nlp structure 3355 * 3356 * Returns: 3357 * 0: on success 3358 * -1: on error 3359 */ 3360 static int 3361 ndmpd_rs_dar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 3362 ndmp_lbr_params_t *nlp) 3363 { 3364 mem_ndmp_name_v3_t *ep; 3365 u_longlong_t len; 3366 char *jname; 3367 int n = session->ns_data.dd_nlist_len; 3368 int i, ret = 0; 3369 int result = 0; 3370 3371 jname = ndmpd_dar_tar_init_v3(session, nlp); 3372 3373 if (!jname) 3374 return (-1); 3375 3376 /* 3377 * We set the length = sizeof (tlm_tar_hdr_t) 3378 * This is important for three-way DAR restore, for we should 3379 * read the header first (If we ask for more data then we have 3380 * to read and discard the remaining data in the socket) 3381 */ 3382 len = tlm_tarhdr_size(); 3383 3384 for (i = 0; i < n; ++i) { 3385 ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i); 3386 if (!ep) { 3387 NDMP_LOG(LOG_DEBUG, "ep NULL, i %d", i); 3388 continue; 3389 } 3390 NDMP_LOG(LOG_DEBUG, 3391 "restoring opath %s, dpath %s, fh_info %lld", 3392 ep->nm3_opath ? ep->nm3_opath : "NULL", 3393 ep->nm3_dpath ? ep->nm3_dpath : "NULL", 3394 ep->nm3_fh_info); 3395 3396 /* 3397 * We should seek till finding the window in which file 3398 * is located. 3399 */ 3400 ret = ndmpd_dar_locate_window_v3(session, params, 3401 ep->nm3_fh_info, len); 3402 3403 if (ret < 0) /* If seek fails, restore should be aborted */ 3404 break; 3405 /* 3406 * We are inside the target window. 3407 * for each restore we will use one entry as selection list 3408 */ 3409 if ((ret = ndmpd_dar_tar_v3(session, params, nlp, jname, i+1)) 3410 != 0) 3411 result = EIO; 3412 ndmpd_audit_restore(session->ns_connection, 3413 ep->nm3_opath ? ep->nm3_opath : "NULL", 3414 session->ns_data.dd_data_addr.addr_type, 3415 session->ns_tape.td_adapter_name, result); 3416 } 3417 3418 NDMP_LOG(LOG_DEBUG, "End of restore list"); 3419 3420 (void) ndmpd_dar_tar_end_v3(session, params, nlp, jname); 3421 3422 return (ret); 3423 } 3424 3425 /* 3426 * ndmp_plugin_pre_restore 3427 * 3428 * Wrapper for pre-restore callback with multiple path 3429 */ 3430 static int 3431 ndmp_plugin_pre_restore(ndmp_context_t *ctxp, ndmpd_module_params_t *params, 3432 int ncount) 3433 { 3434 mem_ndmp_name_v3_t *ep; 3435 int err; 3436 int i; 3437 3438 for (i = 0; i < ncount; i++) { 3439 if (!(ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i))) 3440 continue; 3441 if ((err = ndmp_pl->np_pre_restore(ndmp_pl, ctxp, 3442 ep->nm3_opath, ep->nm3_dpath)) != 0) 3443 return (err); 3444 } 3445 3446 return (0); 3447 } 3448 3449 /* 3450 * get_absolute_path 3451 * 3452 * Get resolved path name which does not involve ".", ".." or extra 3453 * "/" or symbolic links. 3454 * 3455 * e.g. 3456 * 3457 * /backup/path/ -> /backup/path 3458 * /backup/path/. -> /backup/path 3459 * /backup/path/../path/ -> /backup/path 3460 * /link-to-backup-path -> /backup/path 3461 * 3462 * Returns: 3463 * Pointer to the new path (allocated) 3464 * NULL if the path doesnt exist 3465 */ 3466 static char * 3467 get_absolute_path(const char *bkpath) 3468 { 3469 char *pbuf; 3470 char *rv; 3471 3472 if (!(pbuf = ndmp_malloc(TLM_MAX_PATH_NAME))) 3473 return (NULL); 3474 3475 if ((rv = realpath(bkpath, pbuf)) == NULL) { 3476 NDMP_LOG(LOG_DEBUG, "Invalid path [%s] err=%d", 3477 bkpath, errno); 3478 } 3479 return (rv); 3480 } 3481 3482 3483 /* 3484 * Expands the format string and logs the resulting message to the 3485 * remote DMA 3486 */ 3487 void 3488 ndmp_log_dma(ndmp_context_t *nctx, ndmp_log_dma_type_t lt, const char *fmt, ...) 3489 { 3490 va_list ap; 3491 char buf[256]; 3492 ndmpd_module_params_t *params; 3493 3494 if (nctx == NULL || 3495 (params = (ndmpd_module_params_t *)nctx->nc_params) == NULL) 3496 return; 3497 3498 va_start(ap, fmt); 3499 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 3500 va_end(ap); 3501 3502 MOD_LOGV3(params, (ndmp_log_type)lt, "%s", buf); 3503 } 3504 3505 3506 /* 3507 * ndmpd_rs_sar_tar_v3 3508 * 3509 * Main non-DAR restore function. It will try to restore all the entries 3510 * that have been backed up. 3511 * 3512 * Parameters: 3513 * session (input) - pointer to the session 3514 * params (input) - pointer to the parameters structure 3515 * nlp (input) - pointer to the nlp structure 3516 * 3517 * Returns: 3518 * 0: on success 3519 * -1: on error 3520 */ 3521 static int 3522 ndmpd_rs_sar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params, 3523 ndmp_lbr_params_t *nlp) 3524 { 3525 char jname[TLM_MAX_BACKUP_JOB_NAME]; 3526 char *excl; 3527 char **sels; 3528 int flags; 3529 int err; 3530 tlm_commands_t *cmds; 3531 struct rs_name_maker rn; 3532 ndmp_tar_reader_arg_t arg; 3533 pthread_t rdtp; 3534 int result; 3535 ndmp_context_t nctx; 3536 3537 result = err = 0; 3538 (void) ndmp_new_job_name(jname); 3539 if (restore_alloc_structs_v3(session, jname) < 0) 3540 return (-1); 3541 3542 sels = setupsels(session, params, nlp, 0); 3543 if (!sels) { 3544 free_structs_v3(session, jname); 3545 return (-1); 3546 } 3547 excl = NULL; 3548 flags = RSFLG_OVR_ALWAYS; 3549 rn.rn_nlp = nlp; 3550 rn.rn_fp = mknewname; 3551 3552 nlp->nlp_jstat->js_start_ltime = time(NULL); 3553 nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime; 3554 3555 if (!session->ns_data.dd_abort && !session->ns_data.dd_abort) { 3556 cmds = &nlp->nlp_cmds; 3557 cmds->tcs_reader = cmds->tcs_writer = TLM_RESTORE_RUN; 3558 cmds->tcs_command->tc_reader = TLM_RESTORE_RUN; 3559 cmds->tcs_command->tc_writer = TLM_RESTORE_RUN; 3560 3561 NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" started.", 3562 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL"); 3563 3564 arg.tr_session = session; 3565 arg.tr_mod_params = params; 3566 arg.tr_cmds = cmds; 3567 err = pthread_create(&rdtp, NULL, (funct_t)ndmp_tar_reader, 3568 (void *)&arg); 3569 if (err == 0) { 3570 tlm_cmd_wait(cmds->tcs_command, TLM_TAR_READER); 3571 } else { 3572 NDMP_LOG(LOG_DEBUG, "Launch ndmp_tar_reader: %m"); 3573 free_structs_v3(session, jname); 3574 return (-1); 3575 } 3576 3577 if (!ndmp_check_utf8magic(cmds->tcs_command)) { 3578 NDMP_LOG(LOG_DEBUG, "UTF8Magic not found!"); 3579 } else { 3580 NDMP_LOG(LOG_DEBUG, "UTF8Magic found"); 3581 } 3582 3583 /* Plug-in module */ 3584 if (ndmp_pl != NULL && 3585 ndmp_pl->np_pre_restore != NULL) { 3586 nctx.nc_cmds = cmds; 3587 nctx.nc_params = params; 3588 if ((err = ndmp_plugin_pre_restore(&nctx, params, 3589 nlp->nlp_nfiles)) 3590 != 0) { 3591 NDMP_LOG(LOG_ERR, "Pre-restore plug-in: %m"); 3592 cmds->tcs_command->tc_reader = TLM_STOP; 3593 ndmp_stop_local_reader(session, cmds); 3594 ndmp_wait_for_reader(cmds); 3595 (void) pthread_join(rdtp, NULL); 3596 ndmp_stop_remote_reader(session); 3597 goto restore_out; 3598 } 3599 } 3600 3601 cmds->tcs_command->tc_ref++; 3602 cmds->tcs_writer_count++; 3603 3604 if (tm_tar_ops.tm_getdir != NULL) 3605 err = (tm_tar_ops.tm_getdir)(cmds, cmds->tcs_command, 3606 nlp->nlp_jstat, &rn, 1, 1, sels, &excl, flags, 0, 3607 nlp->nlp_backup_path, session->hardlink_q); 3608 3609 cmds->tcs_writer_count--; 3610 cmds->tcs_command->tc_ref--; 3611 cmds->tcs_command->tc_reader = TLM_STOP; 3612 nlp->nlp_jstat->js_stop_time = time(NULL); 3613 3614 /* Send the list of un-recovered files/dirs to the client. */ 3615 (void) send_unrecovered_list_v3(params, nlp); 3616 3617 ndmp_stop_local_reader(session, cmds); 3618 ndmp_wait_for_reader(cmds); 3619 (void) pthread_join(rdtp, NULL); 3620 3621 ndmp_stop_remote_reader(session); 3622 3623 /* exit as if there was an internal error */ 3624 if (session->ns_eof) 3625 err = -1; 3626 if (err == -1) 3627 result = EIO; 3628 } 3629 3630 (void) send_unrecovered_list_v3(params, nlp); /* nothing restored. */ 3631 if (session->ns_data.dd_abort) { 3632 NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" aborted.", 3633 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL"); 3634 result = EINTR; 3635 ndmpd_audit_restore(session->ns_connection, 3636 nlp->nlp_restore_path, 3637 session->ns_data.dd_data_addr.addr_type, 3638 session->ns_tape.td_adapter_name, result); 3639 err = -1; 3640 } else { 3641 NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" finished. (%d)", 3642 (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL", 3643 err); 3644 ndmpd_audit_restore(session->ns_connection, 3645 nlp->nlp_restore_path, 3646 session->ns_data.dd_data_addr.addr_type, 3647 session->ns_tape.td_adapter_name, result); 3648 3649 restore_out: 3650 /* Plug-in module */ 3651 if (ndmp_pl != NULL && 3652 ndmp_pl->np_post_restore != NULL && 3653 ndmp_pl->np_post_restore(ndmp_pl, &nctx, err) == -1) { 3654 NDMP_LOG(LOG_DEBUG, "Post-restore plug-in: %m"); 3655 err = -1; 3656 } 3657 } 3658 3659 NDMP_FREE(sels); 3660 free_structs_v3(session, jname); 3661 3662 return (err); 3663 } 3664 3665 3666 /* 3667 * ndmp_backup_get_params_v3 3668 * 3669 * Get the backup parameters from the NDMP env variables 3670 * and log them in the system log and as normal messages 3671 * to the DMA. 3672 * 3673 * Parameters: 3674 * session (input) - pointer to the session 3675 * params (input) - pointer to the parameters structure 3676 * 3677 * Returns: 3678 * NDMP_NO_ERR: on success 3679 * != NDMP_NO_ERR: otherwise 3680 */ 3681 ndmp_error 3682 ndmp_backup_get_params_v3(ndmpd_session_t *session, 3683 ndmpd_module_params_t *params) 3684 { 3685 ndmp_error rv; 3686 ndmp_lbr_params_t *nlp; 3687 3688 if (!session || !params) 3689 return (NDMP_ILLEGAL_ARGS_ERR); 3690 3691 rv = NDMP_NO_ERR; 3692 nlp = ndmp_get_nlp(session); 3693 if (!nlp) { 3694 MOD_LOGV3(params, NDMP_LOG_ERROR, 3695 "Internal error: NULL nlp.\n"); 3696 rv = NDMP_ILLEGAL_ARGS_ERR; 3697 } else { 3698 if (!(nlp->nlp_backup_path = get_bk_path_v3(params))) 3699 rv = NDMP_FILE_NOT_FOUND_ERR; 3700 else if (!is_valid_backup_dir_v3(params, nlp->nlp_backup_path)) 3701 rv = NDMP_ILLEGAL_ARGS_ERR; 3702 } 3703 3704 nlp->nlp_backup_path = get_absolute_path(nlp->nlp_backup_path); 3705 if (!nlp->nlp_backup_path) 3706 rv = NDMP_FILE_NOT_FOUND_ERR; 3707 3708 if (rv != NDMP_NO_ERR) 3709 return (rv); 3710 3711 if (fs_is_chkpntvol(nlp->nlp_backup_path) || 3712 fs_is_rdonly(nlp->nlp_backup_path) || 3713 !fs_is_chkpnt_enabled(nlp->nlp_backup_path)) 3714 NLP_SET(nlp, NLPF_CHKPNTED_PATH); 3715 else 3716 NLP_UNSET(nlp, NLPF_CHKPNTED_PATH); 3717 3718 /* Should the st_ctime be ignored when backing up? */ 3719 if (ndmp_ignore_ctime) { 3720 NDMP_LOG(LOG_DEBUG, "ignoring st_ctime"); 3721 NLP_SET(nlp, NLPF_IGNCTIME); 3722 } else { 3723 NLP_UNSET(nlp, NLPF_IGNCTIME); 3724 } 3725 3726 if (ndmp_include_lmtime == TRUE) { 3727 NDMP_LOG(LOG_DEBUG, "including st_lmtime"); 3728 NLP_SET(nlp, NLPF_INCLMTIME); 3729 } else { 3730 NLP_UNSET(nlp, NLPF_INCLMTIME); 3731 } 3732 3733 NDMP_LOG(LOG_DEBUG, "flags %x", nlp->nlp_flags); 3734 3735 get_hist_env_v3(params, nlp); 3736 get_exc_env_v3(params, nlp); 3737 get_inc_env_v3(params, nlp); 3738 get_direct_env_v3(params, nlp); 3739 rv = get_backup_level_v3(params, nlp); 3740 3741 return (rv); 3742 } 3743 3744 3745 /* 3746 * ndmpd_tar_backup_starter_v3 3747 * 3748 * Create the checkpoint for the backup and do the backup, 3749 * then remove the backup checkpoint if we created it. 3750 * Save the backup time information based on the backup 3751 * type and stop the data server. 3752 * 3753 * Parameters: 3754 * params (input) - pointer to the parameters structure 3755 * 3756 * Returns: 3757 * 0: on success 3758 * != 0: otherwise 3759 */ 3760 int 3761 ndmpd_tar_backup_starter_v3(ndmpd_module_params_t *params) 3762 { 3763 int err; 3764 ndmpd_session_t *session; 3765 ndmp_lbr_params_t *nlp; 3766 char jname[TLM_MAX_BACKUP_JOB_NAME]; 3767 ndmp_bkup_size_arg_t sarg; 3768 pthread_t tid; 3769 3770 session = (ndmpd_session_t *)(params->mp_daemon_cookie); 3771 *(params->mp_module_cookie) = nlp = ndmp_get_nlp(session); 3772 ndmp_session_ref(session); 3773 (void) ndmp_new_job_name(jname); 3774 3775 err = 0; 3776 if (!NLP_ISCHKPNTED(nlp) && 3777 ndmp_start_check_point(nlp->nlp_backup_path, jname) < 0) { 3778 MOD_LOGV3(params, NDMP_LOG_ERROR, 3779 "Creating checkpoint on \"%s\".\n", 3780 nlp->nlp_backup_path); 3781 err = -1; 3782 } 3783 3784 NDMP_LOG(LOG_DEBUG, "err %d, chkpnted %c", 3785 err, NDMP_YORN(NLP_ISCHKPNTED(nlp))); 3786 3787 if (err == 0) { 3788 sarg.bs_session = session; 3789 sarg.bs_jname = jname; 3790 sarg.bs_path = nlp->nlp_backup_path; 3791 3792 /* Get an estimate of the data size */ 3793 if (pthread_create(&tid, NULL, (funct_t)get_backup_size, 3794 (void *)&sarg) == 0) 3795 (void) pthread_detach(tid); 3796 3797 err = ndmp_get_cur_bk_time(nlp, &nlp->nlp_cdate, jname); 3798 if (err != 0) { 3799 NDMP_LOG(LOG_DEBUG, "err %d", err); 3800 } else { 3801 log_bk_params_v3(session, params, nlp); 3802 err = tar_backup_v3(session, params, nlp, jname); 3803 } 3804 } 3805 3806 if (!NLP_ISCHKPNTED(nlp)) 3807 (void) ndmp_release_check_point(nlp->nlp_backup_path, jname); 3808 3809 NDMP_LOG(LOG_DEBUG, "err %d, update %c", 3810 err, NDMP_YORN(NLP_SHOULD_UPDATE(nlp))); 3811 3812 if (err == 0) 3813 save_backup_date_v3(params, nlp); 3814 3815 MOD_DONE(params, err); 3816 3817 /* nlp_params is allocated in start_backup_v3() */ 3818 NDMP_FREE(nlp->nlp_params); 3819 NDMP_FREE(nlp->nlp_backup_path); 3820 3821 NS_DEC(nbk); 3822 ndmp_session_unref(session); 3823 return (err); 3824 3825 } 3826 3827 3828 /* 3829 * ndmpd_tar_backup_abort_v3 3830 * 3831 * Abort the backup operation and stop the reader thread. 3832 * 3833 * Parameters: 3834 * module_cookie (input) - pointer to the nlp structure 3835 * 3836 * Returns: 3837 * 0: always 3838 */ 3839 int 3840 ndmpd_tar_backup_abort_v3(void *module_cookie) 3841 { 3842 ndmp_lbr_params_t *nlp; 3843 3844 nlp = (ndmp_lbr_params_t *)module_cookie; 3845 if (nlp && nlp->nlp_session) { 3846 if (nlp->nlp_session->ns_data.dd_data_addr.addr_type == 3847 NDMP_ADDR_TCP && 3848 nlp->nlp_session->ns_data.dd_sock != -1) { 3849 (void) close(nlp->nlp_session->ns_data.dd_sock); 3850 nlp->nlp_session->ns_data.dd_sock = -1; 3851 } 3852 ndmp_stop_reader_thread(nlp->nlp_session); 3853 } 3854 3855 return (0); 3856 } 3857 3858 3859 /* 3860 * ndmp_restore_get_params_v3 3861 * 3862 * Get the parameters specified for recovery such as restore path, type 3863 * of restore (DAR, non-DAR) etc 3864 * 3865 * Parameters: 3866 * session (input) - pointer to the session 3867 * params (input) - pointer to the parameters structure 3868 * 3869 * Returns: 3870 * NDMP_NO_ERR: on success 3871 * != NDMP_NO_ERR: otherwise 3872 */ 3873 ndmp_error 3874 ndmp_restore_get_params_v3(ndmpd_session_t *session, 3875 ndmpd_module_params_t *params) 3876 { 3877 ndmp_error rv; 3878 ndmp_lbr_params_t *nlp; 3879 3880 if (!(nlp = ndmp_get_nlp(session))) { 3881 NDMP_LOG(LOG_DEBUG, "nlp is NULL"); 3882 rv = NDMP_ILLEGAL_ARGS_ERR; 3883 } else if (!(nlp->nlp_backup_path = get_bk_path_v3(params))) 3884 rv = NDMP_ILLEGAL_ARGS_ERR; 3885 else if ((nlp->nlp_nfiles = session->ns_data.dd_nlist_len) == 0) { 3886 NDMP_LOG(LOG_DEBUG, "nfiles: %d", nlp->nlp_nfiles); 3887 rv = NDMP_ILLEGAL_ARGS_ERR; 3888 } else if (get_rs_path_v3(params, nlp) != NDMP_NO_ERR) { 3889 rv = NDMP_ILLEGAL_ARGS_ERR; 3890 } else if ((rv = fix_nlist_v3(session, params, nlp)) != NDMP_NO_ERR) { 3891 NDMP_LOG(LOG_DEBUG, "fix_nlist_v3: %d", rv); 3892 } else { 3893 rv = NDMP_NO_ERR; 3894 get_direct_env_v3(params, nlp); 3895 if (NLP_ISSET(nlp, NLPF_DIRECT)) { 3896 if (NLP_ISSET(nlp, NLPF_RECURSIVE)) { 3897 /* Currently we dont support DAR on directory */ 3898 NDMP_LOG(LOG_DEBUG, 3899 "Can't have RECURSIVE and DIRECT together"); 3900 rv = NDMP_ILLEGAL_ARGS_ERR; 3901 return (rv); 3902 } 3903 3904 /* 3905 * DAR can be done if all the fh_info's are valid. 3906 */ 3907 if (allvalidfh(session, params)) { 3908 ndmp_sort_nlist_v3(session); 3909 } else { 3910 MOD_LOGV3(params, NDMP_LOG_WARNING, 3911 "Cannot do direct access recovery. " 3912 "Some 'fh_info'es are not valid.\n"); 3913 NLP_UNSET(nlp, NLPF_DIRECT); 3914 } 3915 } 3916 3917 log_rs_params_v3(session, params, nlp); 3918 } 3919 3920 return (rv); 3921 } 3922 3923 3924 /* 3925 * ndmpd_tar_restore_starter_v3 3926 * 3927 * The main restore starter function. It will start a DAR or 3928 * non-DAR recovery based on the parameters. (V3 and V4 only) 3929 * 3930 * Parameters: 3931 * params (input) - pointer to the parameters structure 3932 * 3933 * Returns: 3934 * NDMP_NO_ERR: on success 3935 * != NDMP_NO_ERR: otherwise 3936 */ 3937 int 3938 ndmpd_tar_restore_starter_v3(ndmpd_module_params_t *params) 3939 { 3940 int err; 3941 ndmpd_session_t *session; 3942 ndmp_lbr_params_t *nlp; 3943 3944 3945 session = (ndmpd_session_t *)(params->mp_daemon_cookie); 3946 *(params->mp_module_cookie) = nlp = ndmp_get_nlp(session); 3947 ndmp_session_ref(session); 3948 3949 if (NLP_ISSET(nlp, NLPF_DIRECT)) 3950 err = ndmpd_rs_dar_tar_v3(session, params, nlp); 3951 else 3952 err = ndmpd_rs_sar_tar_v3(session, params, nlp); 3953 3954 MOD_DONE(params, err); 3955 3956 NS_DEC(nrs); 3957 /* nlp_params is allocated in start_recover() */ 3958 NDMP_FREE(nlp->nlp_params); 3959 ndmp_session_unref(session); 3960 return (err); 3961 3962 } 3963 3964 3965 /* 3966 * ndmp_tar_restore_abort_v3 3967 * 3968 * Restore abort function (V3 and V4 only) 3969 * 3970 * Parameters: 3971 * module_cookie (input) - pointer to nlp 3972 * 3973 * Returns: 3974 * 0 3975 */ 3976 int 3977 ndmpd_tar_restore_abort_v3(void *module_cookie) 3978 { 3979 ndmp_lbr_params_t *nlp; 3980 3981 nlp = (ndmp_lbr_params_t *)module_cookie; 3982 if (nlp != NULL && nlp->nlp_session != NULL) { 3983 if (nlp->nlp_session->ns_data.dd_mover.addr_type == 3984 NDMP_ADDR_TCP && 3985 nlp->nlp_session->ns_data.dd_sock != -1) { 3986 (void) close(nlp->nlp_session->ns_data.dd_sock); 3987 nlp->nlp_session->ns_data.dd_sock = -1; 3988 } 3989 nlp_event_nw(nlp->nlp_session); 3990 ndmp_stop_writer_thread(nlp->nlp_session); 3991 } 3992 3993 3994 return (0); 3995 3996 } 3997