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