1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 32 /* All Rights Reserved */ 33 34 /* 35 * Copyright (c) 2018, Joyent, Inc. 36 * Copyright 2024 Oxide Computer Company 37 */ 38 39 /* 40 * University Copyright- Copyright (c) 1982, 1986, 1988 41 * The Regents of the University of California 42 * All Rights Reserved 43 * 44 * University Acknowledgment- Portions of this document are derived from 45 * software developed by the University of California, Berkeley, and its 46 * contributors. 47 */ 48 49 /* 50 * Combined mv/cp/ln command: 51 * mv file1 file2 52 * mv dir1 dir2 53 * mv file1 ... filen dir1 54 */ 55 #include <sys/time.h> 56 #include <signal.h> 57 #include <locale.h> 58 #include <stdarg.h> 59 #include <sys/acl.h> 60 #include <libcmdutils.h> 61 #include <aclutils.h> 62 #include <assert.h> 63 #include "getresponse.h" 64 65 #define FTYPE(A) (A.st_mode) 66 #define FMODE(A) (A.st_mode) 67 #define UID(A) (A.st_uid) 68 #define GID(A) (A.st_gid) 69 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino) 70 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR) 71 #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR) 72 #define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK) 73 #define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG) 74 #define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \ 75 (A.st_mode & S_IFMT) == S_IFBLK || \ 76 (A.st_mode & S_IFMT) == S_IFIFO) 77 #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK) 78 79 #define DELIM '/' 80 #define EQ(x, y) (strcmp(x, y) == 0) 81 #define FALSE 0 82 #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 83 #define TRUE 1 84 85 typedef enum { 86 /* 87 * Indicates that after checking the file we should proceed with the 88 * action. 89 */ 90 CHK_CONT, 91 /* 92 * Indicates that after checking the file we encountered an error that 93 * should be percolated up. 94 */ 95 CHK_ERROR, 96 /* 97 * Indicate that the user opted to skip this file. No action should be 98 * taken and this should be treated as successful. 99 */ 100 CHK_SKIP 101 } chkfiles_t; 102 103 static chkfiles_t chkfiles(const char *, char **); 104 static const char *dname(const char *); 105 static int lnkfil(const char *, char *); 106 static int cpymve(const char *, char *); 107 static int rcopy(const char *, char *); 108 static int chk_different(const char *, const char *); 109 static int chg_time(const char *, struct stat); 110 static int chg_mode(const char *, uid_t, gid_t, mode_t); 111 static int copydir(const char *, char *); 112 static int copyspecial(const char *); 113 static int getrealpath(const char *, char *); 114 static void usage(void); 115 static void Perror(const char *); 116 static void Perror2(const char *, const char *); 117 static int use_stdin(void); 118 static int copyattributes(const char *, const char *); 119 static int copy_sysattr(const char *, const char *); 120 static tree_node_t *create_tnode(dev_t, ino_t); 121 122 static struct stat s1, s2, s3, s4; 123 static int cpy = FALSE; 124 static int mve = FALSE; 125 static int lnk = FALSE; 126 static char *cmd; 127 static int fflg = 0; 128 static int pflg = 0; 129 static int Rflg = 0; /* recursive copy */ 130 static int rflg = 0; /* recursive copy */ 131 static int sflg = 0; 132 static int Hflg = 0; /* follow cmd line arg symlink to dir */ 133 static int Lflg = 0; /* follow symlinks */ 134 static int Pflg = 0; /* do not follow symlinks */ 135 static int atflg = 0; 136 static int attrsilent = 0; 137 static int targetexists = 0; 138 static int cmdarg; /* command line argument */ 139 static avl_tree_t *stree = NULL; /* source file inode search tree */ 140 static acl_t *s1acl; 141 static int saflg = 0; /* 'cp' extended system attr. */ 142 static int srcfd = -1; 143 static int targfd = -1; 144 static int sourcedirfd = -1; 145 static int targetdirfd = -1; 146 static DIR *srcdirp = NULL; 147 static int srcattrfd = -1; 148 static int targattrfd = -1; 149 static struct stat attrdir; 150 151 /* 152 * cp, mv, and ln all have behaviors around what happens when a file already 153 * exists at the target. These behaviors depend on a combination of the program, 154 * the options specified, and the permissions of the target file. 155 * 156 * 1) Explicitly remove any target file 157 * 2) Always ask the user 158 * 3) Take no action and treat as successful 159 * 4) Take no action and treat as a failure 160 * 5) Replace the target file if permissions align, otherwise fail 161 * 6) Replace the target file if permissions align, otherwise prompt if stdin 162 * is a tty 163 * 164 * The default action varies based on the program. cp defaults to (5), mv to 165 * (6), and ln to (4). There are three flags that depending on the program 166 * will change the behavior that is taken: -f, -i, and -n. Of these only -i has 167 * the same meaning across all three programs. In this context, we treat these 168 * flags as: 169 * 170 * -f: take action (1) 171 * -i: take action (2) 172 * -n: take action (3) 173 * 174 * The following table shows which programs honor which of these flags: 175 * 176 * CP LN MV 177 * -f N Y Y 178 * -i Y Y Y 179 * -n Y Y N 180 * 181 * Any case where you see a 'N' above means the program has a different meaning 182 * for the flag. These four actions are summarized in the following enumeration. 183 * 184 * The last wrinkle with these is how they are processed on the command line. In 185 * general, we treat these as the last one wins. That is, if you specified -i -n 186 * then we would use the -n behavior. The only wrinkle for this is with mv. The 187 * non-POSIX form of mv any -f trump all -i options, but the xpg4 behavior was 188 * the last one wins. In this case -n takes the last one wins behavior with mv 189 * to try to make it as similar to everything else as we can. 190 */ 191 typedef enum { 192 /* 193 * Take the program's default behavior. 194 */ 195 TA_DEFAULT, 196 /* 197 * The user has explicitly said we should remove the file. 198 */ 199 TA_OVERWRITE, 200 /* 201 * The user has said we should always prompt about the file. 202 */ 203 TA_PROMPT, 204 /* 205 * The user has said we should always leave it be. 206 */ 207 TA_SKIP 208 } target_action_t; 209 210 target_action_t targact = TA_DEFAULT; 211 212 /* Extended system attributes support */ 213 214 static int open_source(const char *); 215 static int open_target_srctarg_attrdirs(const char *, const char *); 216 static int open_attrdirp(const char *); 217 static int traverse_attrfile(struct dirent *, const char *, const char *, int); 218 static void rewind_attrdir(DIR *); 219 static void close_all(void); 220 221 222 int 223 main(int argc, char *argv[]) 224 { 225 int c, i, r, errflg = 0; 226 char target[PATH_MAX]; 227 int (*move)(const char *, char *); 228 229 /* 230 * Determine command invoked (mv, cp, or ln) 231 */ 232 233 if (cmd = strrchr(argv[0], '/')) 234 ++cmd; 235 else 236 cmd = argv[0]; 237 238 /* 239 * Set flags based on command. 240 */ 241 242 (void) setlocale(LC_ALL, ""); 243 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 244 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 245 #endif 246 (void) textdomain(TEXT_DOMAIN); 247 if (init_yes() < 0) { 248 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 249 strerror(errno)); 250 exit(3); 251 } 252 253 if (EQ(cmd, "mv")) 254 mve = TRUE; 255 else if (EQ(cmd, "ln")) 256 lnk = TRUE; 257 else if (EQ(cmd, "cp")) 258 cpy = TRUE; 259 else { 260 (void) fprintf(stderr, 261 gettext("Invalid command name (%s); expecting " 262 "mv, cp, or ln.\n"), cmd); 263 exit(1); 264 } 265 266 /* 267 * Check for options: 268 * cp [ -r|-R [-H|-L|-P]] [-afinp@/] file1 [file2 ...] target 269 * cp [-afinprR@/] file1 [file2 ...] target 270 * ln [-fi] [-n] [-s] file1 [file2 ...] target 271 * ln [-fi] [-n] [-s] file1 [file2 ...] 272 * mv [-f|i] [-n] file1 [file2 ...] target 273 * mv [-f|i] [-n] dir1 target 274 */ 275 276 if (cpy) { 277 while ((c = getopt(argc, argv, "afHinLpPrR@/")) != EOF) 278 switch (c) { 279 case 'f': 280 fflg++; 281 break; 282 case 'i': 283 targact = TA_PROMPT; 284 break; 285 case 'n': 286 targact = TA_SKIP; 287 break; 288 case 'p': 289 pflg++; 290 #ifdef XPG4 291 attrsilent = 1; 292 atflg = 0; 293 saflg = 0; 294 #else 295 if (atflg == 0) 296 attrsilent = 1; 297 #endif 298 break; 299 case 'H': 300 /* 301 * If more than one of -H, -L, or -P are 302 * specified, only the last option specified 303 * determines the behavior. 304 */ 305 Lflg = Pflg = 0; 306 Hflg++; 307 break; 308 case 'L': 309 Hflg = Pflg = 0; 310 Lflg++; 311 break; 312 case 'P': 313 Lflg = Hflg = 0; 314 Pflg++; 315 break; 316 case 'R': 317 /* 318 * The default behavior of cp -R|-r 319 * when specified without -H|-L|-P 320 * is -L. 321 */ 322 Rflg++; 323 /*FALLTHROUGH*/ 324 case 'r': 325 rflg++; 326 break; 327 case 'a': 328 Lflg = Hflg = 0; 329 pflg++; 330 Pflg++; 331 Rflg++; 332 rflg++; 333 break; 334 case '@': 335 atflg++; 336 attrsilent = 0; 337 #ifdef XPG4 338 pflg = 0; 339 #endif 340 break; 341 case '/': 342 saflg++; 343 attrsilent = 0; 344 #ifdef XPG4 345 pflg = 0; 346 #endif 347 break; 348 default: 349 errflg++; 350 } 351 352 /* -R or -r must be specified with -H, -L, or -P */ 353 if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) { 354 errflg++; 355 } 356 357 } else if (mve) { 358 while ((c = getopt(argc, argv, "fins")) != EOF) 359 switch (c) { 360 case 'f': 361 targact = TA_OVERWRITE; 362 break; 363 case 'i': 364 #ifdef XPG4 365 targact = TA_PROMPT; 366 #else 367 if (targact != TA_OVERWRITE) { 368 targact = TA_PROMPT; 369 } 370 #endif 371 break; 372 case 'n': 373 targact = TA_SKIP; 374 break; 375 default: 376 errflg++; 377 } 378 } else { /* ln */ 379 while ((c = getopt(argc, argv, "fins")) != EOF) 380 switch (c) { 381 case 'f': 382 targact = TA_OVERWRITE; 383 break; 384 case 'i': 385 targact = TA_PROMPT; 386 break; 387 case 'n': 388 /* silently ignored; this is the default */ 389 break; 390 case 's': 391 sflg++; 392 break; 393 default: 394 errflg++; 395 } 396 } 397 398 /* 399 * For BSD compatibility allow - to delimit the end of 400 * options for mv. 401 */ 402 if (mve && optind < argc && (strcmp(argv[optind], "-") == 0)) 403 optind++; 404 405 /* 406 * Check for sufficient arguments 407 * or a usage error. 408 */ 409 410 argc -= optind; 411 argv = &argv[optind]; 412 413 if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) { 414 (void) fprintf(stderr, 415 gettext("%s: Insufficient arguments (%d)\n"), 416 cmd, argc); 417 usage(); 418 } 419 420 if (errflg != 0) 421 usage(); 422 423 /* 424 * If there is more than a source and target, 425 * the last argument (the target) must be a directory 426 * which really exists. 427 */ 428 429 if (argc > 2) { 430 if (stat(argv[argc-1], &s2) < 0) { 431 (void) fprintf(stderr, 432 gettext("%s: %s not found\n"), 433 cmd, argv[argc-1]); 434 exit(2); 435 } 436 437 if (!ISDIR(s2)) { 438 (void) fprintf(stderr, 439 gettext("%s: Target %s must be a directory\n"), 440 cmd, argv[argc-1]); 441 usage(); 442 } 443 } 444 445 if (strlen(argv[argc-1]) >= PATH_MAX) { 446 (void) fprintf(stderr, 447 gettext("%s: Target %s file name length exceeds PATH_MAX" 448 " %d\n"), cmd, argv[argc-1], PATH_MAX); 449 exit(78); 450 } 451 452 if (argc == 1) { 453 if (!lnk) 454 usage(); 455 (void) strcpy(target, "."); 456 } else { 457 (void) strcpy(target, argv[--argc]); 458 } 459 460 /* 461 * Perform a multiple argument mv|cp|ln by 462 * multiple invocations of cpymve() or lnkfil(). 463 */ 464 if (lnk) 465 move = lnkfil; 466 else 467 move = cpymve; 468 469 r = 0; 470 for (i = 0; i < argc; i++) { 471 stree = NULL; 472 cmdarg = 1; 473 r += move(argv[i], target); 474 } 475 476 /* 477 * Show errors by nonzero exit code. 478 */ 479 return (r ? 2 : 0); 480 } 481 482 static int 483 lnkfil(const char *source, char *target) 484 { 485 char *buf = NULL; 486 487 if (sflg) { 488 489 /* 490 * If target is a directory make complete 491 * name of the new symbolic link within that 492 * directory. 493 */ 494 495 if ((stat(target, &s2) >= 0) && ISDIR(s2)) { 496 size_t len; 497 498 len = strlen(target) + strlen(dname(source)) + 4; 499 if ((buf = (char *)malloc(len)) == NULL) { 500 (void) fprintf(stderr, 501 gettext("%s: Insufficient memory " 502 "to %s %s\n"), cmd, cmd, source); 503 exit(3); 504 } 505 (void) snprintf(buf, len, "%s/%s", 506 target, dname(source)); 507 target = buf; 508 } 509 510 /* 511 * Check to see if the file exists already. 512 * In this case we use lstat() instead of stat(): 513 * unlink(2) and symlink(2) will operate on the file 514 * itself, not its reference, if the file is a symlink. 515 */ 516 517 if ((lstat(target, &s2) == 0)) { 518 /* 519 * Check what our current overwrite behavior is i.e. the 520 * -f or -n options. If prompting is set (-i), ask the 521 * user. If overwrite is set (-n) then we attempt to 522 * remove it. In both cases if the target is a directory 523 * then we refuse to remove this. Once this is done, the 524 * program will proceed with creating the symlink. 525 */ 526 switch (targact) { 527 case TA_OVERWRITE: 528 case TA_PROMPT: 529 if (ISDIR(s2)) { 530 (void) fprintf(stderr, 531 gettext("%s: cannot create link " 532 "over directory %s\n"), cmd, 533 target); 534 return (1); 535 } 536 537 /* 538 * See the longer discussion in chkfiles about 539 * the use of use_stdin() while prompting. 540 */ 541 if (targact == TA_PROMPT && use_stdin()) { 542 (void) fprintf(stderr, 543 gettext("%s: overwrite %s " 544 "(%s/%s)? "), cmd, target, yesstr, 545 nostr); 546 if (yes() == 0) { 547 return (0); 548 } 549 } 550 551 if (unlink(target) < 0) { 552 (void) fprintf(stderr, 553 gettext("%s: cannot unlink %s: "), 554 cmd, target); 555 perror(""); 556 return (1); 557 } 558 case TA_DEFAULT: 559 break; 560 case TA_SKIP: 561 /* 562 * This shouldn't be selectable. 563 */ 564 abort(); 565 } 566 } 567 568 569 /* 570 * Create a symbolic link to the source. 571 */ 572 573 if (symlink(source, target) < 0) { 574 (void) fprintf(stderr, 575 gettext("%s: cannot create %s: "), 576 cmd, target); 577 perror(""); 578 if (buf != NULL) 579 free(buf); 580 return (1); 581 } 582 if (buf != NULL) 583 free(buf); 584 return (0); 585 } 586 587 switch (chkfiles(source, &target)) { 588 case CHK_ERROR: 589 return (1); 590 case CHK_SKIP: 591 return (0); 592 case CHK_CONT: 593 break; 594 } 595 596 /* 597 * Make sure source file is not a directory, 598 * we cannot link directories... 599 */ 600 601 if (ISDIR(s1)) { 602 (void) fprintf(stderr, 603 gettext("%s: %s is a directory\n"), cmd, source); 604 return (1); 605 } 606 607 /* 608 * hard link, call link() and return. 609 */ 610 611 if (link(source, target) < 0) { 612 if (errno == EXDEV) 613 (void) fprintf(stderr, 614 gettext("%s: %s is on a different file system\n"), 615 cmd, target); 616 else { 617 (void) fprintf(stderr, 618 gettext("%s: cannot create link %s: "), 619 cmd, target); 620 perror(""); 621 } 622 if (buf != NULL) 623 free(buf); 624 return (1); 625 } else { 626 if (buf != NULL) 627 free(buf); 628 return (0); 629 } 630 } 631 632 static int 633 cpymve(const char *source, char *target) 634 { 635 int n; 636 int fi, fo; 637 int ret = 0; 638 int attret = 0; 639 int sattret = 0; 640 int errno_save; 641 int error = 0; 642 643 switch (chkfiles(source, &target)) { 644 case CHK_ERROR: 645 return (1); 646 case CHK_SKIP: 647 return (0); 648 case CHK_CONT: 649 break; 650 } 651 652 /* 653 * If it's a recursive copy and source 654 * is a directory, then call rcopy (from copydir). 655 */ 656 if (cpy) { 657 if (ISDIR(s1)) { 658 int rc; 659 avl_index_t where = 0; 660 tree_node_t *tnode; 661 tree_node_t *tptr; 662 dev_t save_dev = s1.st_dev; 663 ino_t save_ino = s1.st_ino; 664 665 /* 666 * We will be recursing into the directory so 667 * save the inode information to a search tree 668 * to avoid getting into an endless loop. 669 */ 670 if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) { 671 if (rc == 0) { 672 /* 673 * We've already visited this directory. 674 * Don't remove the search tree entry 675 * to make sure we don't get into an 676 * endless loop if revisited from a 677 * different part of the hierarchy. 678 */ 679 (void) fprintf(stderr, gettext( 680 "%s: cycle detected: %s\n"), 681 cmd, source); 682 } else { 683 Perror(source); 684 } 685 return (1); 686 } 687 688 cmdarg = 0; 689 rc = copydir(source, target); 690 691 /* 692 * Create a tnode to get an index to the matching 693 * node (same dev and inode) in the search tree, 694 * then use the index to remove the matching node 695 * so it we do not wrongly detect a cycle when 696 * revisiting this directory from another part of 697 * the hierarchy. 698 */ 699 if ((tnode = create_tnode(save_dev, 700 save_ino)) == NULL) { 701 Perror(source); 702 return (1); 703 } 704 if ((tptr = avl_find(stree, tnode, &where)) != NULL) { 705 avl_remove(stree, tptr); 706 } 707 free(tptr); 708 free(tnode); 709 return (rc); 710 711 } else if (ISDEV(s1) && Rflg) { 712 return (copyspecial(target)); 713 } else { 714 goto copy; 715 } 716 } 717 718 if (mve) { 719 if (rename(source, target) >= 0) 720 return (0); 721 if (errno != EXDEV) { 722 if (errno == ENOTDIR && ISDIR(s1)) { 723 (void) fprintf(stderr, 724 gettext("%s: %s is a directory\n"), 725 cmd, source); 726 return (1); 727 } 728 (void) fprintf(stderr, 729 gettext("%s: cannot rename %s to %s: "), 730 cmd, source, target); 731 perror(""); 732 return (1); 733 } 734 735 /* 736 * cannot move a non-directory (source) onto an existing 737 * directory (target) 738 * 739 */ 740 if (targetexists && ISDIR(s2) && (!ISDIR(s1))) { 741 (void) fprintf(stderr, 742 gettext("%s: cannot mv a non directory %s " 743 "over existing directory" 744 " %s \n"), cmd, source, target); 745 return (1); 746 } 747 if (ISDIR(s1)) { 748 #ifdef XPG4 749 if (targetexists && ISDIR(s2)) { 750 /* existing target dir must be empty */ 751 if (rmdir(target) < 0) { 752 errno_save = errno; 753 (void) fprintf(stderr, 754 gettext("%s: cannot rmdir %s: "), 755 cmd, target); 756 errno = errno_save; 757 perror(""); 758 return (1); 759 } 760 } 761 #endif 762 if ((n = copydir(source, target)) == 0) 763 (void) rmdir(source); 764 return (n); 765 } 766 767 /* doors cannot be moved across filesystems */ 768 if (ISDOOR(s1)) { 769 (void) fprintf(stderr, 770 gettext("%s: %s: cannot move door " 771 "across file systems\n"), cmd, source); 772 return (1); 773 } 774 775 /* sockets cannot be moved across filesystems */ 776 if (ISSOCK(s1)) { 777 (void) fprintf(stderr, 778 gettext("%s: %s: cannot move socket " 779 "across file systems\n"), cmd, source); 780 return (1); 781 } 782 783 /* 784 * File cannot be renamed, try to recreate the symbolic 785 * link or special device, or copy the file wholesale 786 * between file systems. 787 */ 788 if (ISLNK(s1)) { 789 register int m; 790 register mode_t md; 791 char symln[PATH_MAX + 1]; 792 793 if (targetexists && unlink(target) < 0) { 794 (void) fprintf(stderr, 795 gettext("%s: cannot unlink %s: "), 796 cmd, target); 797 perror(""); 798 return (1); 799 } 800 801 if ((m = readlink(source, symln, 802 sizeof (symln) - 1)) < 0) { 803 Perror(source); 804 return (1); 805 } 806 symln[m] = '\0'; 807 808 md = umask(~(s1.st_mode & MODEBITS)); 809 if (symlink(symln, target) < 0) { 810 Perror(target); 811 return (1); 812 } 813 (void) umask(md); 814 m = lchown(target, UID(s1), GID(s1)); 815 #ifdef XPG4 816 if (m < 0) { 817 (void) fprintf(stderr, gettext("%s: cannot" 818 " change owner and group of" 819 " %s: "), cmd, target); 820 perror(""); 821 } 822 #endif 823 goto cleanup; 824 } 825 if (ISDEV(s1)) { 826 827 if (targetexists && unlink(target) < 0) { 828 (void) fprintf(stderr, 829 gettext("%s: cannot unlink %s: "), 830 cmd, target); 831 perror(""); 832 return (1); 833 } 834 835 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 836 Perror(target); 837 return (1); 838 } 839 840 (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1)); 841 (void) chg_time(target, s1); 842 goto cleanup; 843 } 844 845 if (ISREG(s1)) { 846 if (ISDIR(s2)) { 847 if (targetexists && rmdir(target) < 0) { 848 (void) fprintf(stderr, 849 gettext("%s: cannot rmdir %s: "), 850 cmd, target); 851 perror(""); 852 return (1); 853 } 854 } else { 855 if (targetexists && unlink(target) < 0) { 856 (void) fprintf(stderr, 857 gettext("%s: cannot unlink %s: "), 858 cmd, target); 859 perror(""); 860 return (1); 861 } 862 } 863 864 865 copy: 866 /* 867 * If the source file is a symlink, and either 868 * -P or -H flag (only if -H is specified and the 869 * source file is not a command line argument) 870 * were specified, then action is taken on the symlink 871 * itself, not the file referenced by the symlink. 872 * Note: this is executed for 'cp' only. 873 */ 874 if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) { 875 int m; 876 mode_t md; 877 char symln[PATH_MAX + 1]; 878 879 m = readlink(source, symln, sizeof (symln) - 1); 880 881 if (m < 0) { 882 Perror(source); 883 return (1); 884 } 885 symln[m] = '\0'; 886 887 /* 888 * Copy the sym link to the target. 889 * Note: If the target exists, write a 890 * diagnostic message, do nothing more 891 * with the source file, and return to 892 * process any remaining files. 893 */ 894 md = umask(~(s1.st_mode & MODEBITS)); 895 if (symlink(symln, target) < 0) { 896 Perror(target); 897 return (1); 898 } 899 (void) umask(md); 900 m = lchown(target, UID(s1), GID(s1)); 901 902 if (m < 0) { 903 (void) fprintf(stderr, gettext( 904 "cp: cannot change owner and " 905 "group of %s:"), target); 906 perror(""); 907 } 908 } else { 909 /* 910 * Copy the file. If it happens to be a 911 * symlink, copy the file referenced 912 * by the symlink. 913 */ 914 fi = open(source, O_RDONLY); 915 if (fi < 0) { 916 (void) fprintf(stderr, 917 gettext("%s: cannot open %s: "), 918 cmd, source); 919 perror(""); 920 return (1); 921 } 922 923 fo = creat(target, s1.st_mode & MODEBITS); 924 if (fo < 0) { 925 /* 926 * If -f and creat() failed, unlink 927 * and try again. 928 */ 929 if (fflg) { 930 (void) unlink(target); 931 fo = creat(target, 932 s1.st_mode & MODEBITS); 933 } 934 } 935 if (fo < 0) { 936 (void) fprintf(stderr, 937 gettext("%s: cannot create %s: "), 938 cmd, target); 939 perror(""); 940 (void) close(fi); 941 return (1); 942 } else { 943 /* stat the new file, its used below */ 944 (void) stat(target, &s2); 945 } 946 947 /* 948 * Set target's permissions to the source 949 * before any copying so that any partially 950 * copied file will have the source's 951 * permissions (at most) or umask permissions 952 * whichever is the most restrictive. 953 * 954 * ACL for regular files 955 */ 956 957 if (pflg || mve) { 958 (void) chmod(target, FMODE(s1)); 959 if (s1acl != NULL) { 960 if ((acl_set(target, 961 s1acl)) < 0) { 962 error++; 963 (void) fprintf(stderr, 964 gettext("%s: " 965 "Failed to set " 966 "acl entries " 967 "on %s\n"), cmd, 968 target); 969 acl_free(s1acl); 970 s1acl = NULL; 971 /* 972 * else: silent and 973 * continue 974 */ 975 } 976 } 977 } 978 979 if (fstat(fi, &s1) < 0) { 980 (void) fprintf(stderr, 981 gettext("%s: cannot access %s\n"), 982 cmd, source); 983 return (1); 984 } 985 if (IDENTICAL(s1, s2)) { 986 (void) fprintf(stderr, 987 gettext( 988 "%s: %s and %s are identical\n"), 989 cmd, source, target); 990 return (1); 991 } 992 993 if (writefile(fi, fo, source, target, NULL, 994 NULL, &s1, &s2) != 0) { 995 return (1); 996 } 997 998 (void) close(fi); 999 if (close(fo) < 0) { 1000 Perror2(target, "write"); 1001 return (1); 1002 } 1003 } 1004 /* Copy regular extended attributes */ 1005 if (pflg || atflg || mve || saflg) { 1006 attret = copyattributes(source, target); 1007 if (attret != 0 && !attrsilent) { 1008 (void) fprintf(stderr, gettext( 1009 "%s: Failed to preserve" 1010 " extended attributes of file" 1011 " %s\n"), cmd, source); 1012 } 1013 /* Copy extended system attributes */ 1014 if (pflg || mve || saflg) 1015 sattret = copy_sysattr(source, target); 1016 if (mve && attret != 0) { 1017 (void) unlink(target); 1018 return (1); 1019 } 1020 if (attrsilent) { 1021 attret = 0; 1022 } 1023 } 1024 1025 /* 1026 * XPG4: the write system call will clear setgid 1027 * and setuid bits, so set them again. 1028 */ 1029 if (pflg || mve) { 1030 if ((ret = chg_mode(target, UID(s1), GID(s1), 1031 FMODE(s1))) > 0) 1032 return (1); 1033 /* 1034 * Reapply ACL, since chmod may have 1035 * altered ACL 1036 */ 1037 if (s1acl != NULL) { 1038 if ((acl_set(target, s1acl)) < 0) { 1039 error++; 1040 (void) fprintf(stderr, 1041 gettext("%s: Failed to " 1042 "set acl entries " 1043 "on %s\n"), cmd, target); 1044 /* 1045 * else: silent and 1046 * continue 1047 */ 1048 } 1049 } 1050 if ((ret = chg_time(target, s1)) > 0) 1051 return (1); 1052 } 1053 if (cpy) { 1054 if (error != 0 || attret != 0 || sattret != 0) 1055 return (1); 1056 return (0); 1057 } 1058 goto cleanup; 1059 } 1060 (void) fprintf(stderr, 1061 gettext("%s: %s: unknown file type 0x%x\n"), cmd, 1062 source, (s1.st_mode & S_IFMT)); 1063 return (1); 1064 1065 cleanup: 1066 if (unlink(source) < 0) { 1067 (void) unlink(target); 1068 (void) fprintf(stderr, 1069 gettext("%s: cannot unlink %s: "), 1070 cmd, source); 1071 perror(""); 1072 return (1); 1073 } 1074 if (error != 0 || attret != 0 || sattret != 0) 1075 return (1); 1076 return (ret); 1077 } 1078 /*NOTREACHED*/ 1079 return (ret); 1080 } 1081 1082 /* 1083 * create_tnode() 1084 * 1085 * Create a node for use with the search tree which contains the 1086 * inode information (device id and inode number). 1087 * 1088 * Input 1089 * dev - device id 1090 * ino - inode number 1091 * 1092 * Output 1093 * tnode - NULL on error, otherwise returns a tnode structure 1094 * which contains the input device id and inode number. 1095 */ 1096 static tree_node_t * 1097 create_tnode(dev_t dev, ino_t ino) 1098 { 1099 tree_node_t *tnode; 1100 1101 if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) { 1102 tnode->node_dev = dev; 1103 tnode->node_ino = ino; 1104 } 1105 1106 return (tnode); 1107 } 1108 1109 static chkfiles_t 1110 chkfiles(const char *source, char **to) 1111 { 1112 char *buf = (char *)NULL; 1113 int (*statf)() = (cpy && 1114 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat; 1115 char *target = *to; 1116 int error; 1117 1118 /* 1119 * Make sure source file exists. 1120 */ 1121 if ((*statf)(source, &s1) < 0) { 1122 /* 1123 * Keep the old error message except when someone tries to 1124 * mv/cp/ln a symbolic link that has a trailing slash and 1125 * points to a file. 1126 */ 1127 if (errno == ENOTDIR) 1128 (void) fprintf(stderr, "%s: %s: %s\n", cmd, source, 1129 strerror(errno)); 1130 else 1131 (void) fprintf(stderr, 1132 gettext("%s: cannot access %s\n"), cmd, source); 1133 return (CHK_ERROR); 1134 } 1135 1136 /* 1137 * Get ACL info: don't bother with ln or cp/mv'ing symlinks 1138 */ 1139 if (!lnk && !ISLNK(s1)) { 1140 if (s1acl != NULL) { 1141 acl_free(s1acl); 1142 s1acl = NULL; 1143 } 1144 if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) { 1145 (void) fprintf(stderr, 1146 "%s: failed to get acl entries: %s\n", source, 1147 acl_strerror(error)); 1148 return (CHK_ERROR); 1149 } 1150 /* else: just permission bits */ 1151 } 1152 1153 /* 1154 * If stat fails, then the target doesn't exist, 1155 * we will create a new target with default file type of regular. 1156 */ 1157 1158 FTYPE(s2) = S_IFREG; 1159 targetexists = 0; 1160 if ((*statf)(target, &s2) >= 0) { 1161 if (ISLNK(s2)) 1162 (void) stat(target, &s2); 1163 /* 1164 * If target is a directory, 1165 * make complete name of new file 1166 * within that directory. 1167 */ 1168 if (ISDIR(s2)) { 1169 size_t len; 1170 1171 len = strlen(target) + strlen(dname(source)) + 4; 1172 if ((buf = (char *)malloc(len)) == NULL) { 1173 (void) fprintf(stderr, 1174 gettext("%s: Insufficient memory to " 1175 "%s %s\n "), cmd, cmd, source); 1176 exit(3); 1177 } 1178 (void) snprintf(buf, len, "%s/%s", 1179 target, dname(source)); 1180 *to = target = buf; 1181 } 1182 1183 if ((*statf)(target, &s2) >= 0) { 1184 boolean_t prompt = B_FALSE; 1185 boolean_t overwrite = B_FALSE; 1186 boolean_t override = B_FALSE; 1187 1188 targetexists++; 1189 if (cpy || mve) { 1190 /* 1191 * For cp and mv, it is an error if the 1192 * source and target are the same file. 1193 * Check for the same inode and file 1194 * system, but don't check for the same 1195 * absolute pathname because it is an 1196 * error when the source and target are 1197 * hard links to the same file. 1198 */ 1199 if (IDENTICAL(s1, s2)) { 1200 (void) fprintf(stderr, 1201 gettext( 1202 "%s: %s and %s are identical\n"), 1203 cmd, source, target); 1204 if (buf != NULL) 1205 free(buf); 1206 return (CHK_ERROR); 1207 } 1208 } 1209 if (lnk) { 1210 /* 1211 * For ln, it is an error if the source and 1212 * target are identical files (same inode, 1213 * same file system, and filenames resolve 1214 * to same absolute pathname). 1215 */ 1216 if (!chk_different(source, target)) { 1217 if (buf != NULL) 1218 free(buf); 1219 return (CHK_ERROR); 1220 } 1221 } 1222 1223 /* 1224 * Determine if we need to prompt for overwriting the 1225 * file or for overriding its permissions. There is a 1226 * lot of thorny history here. 1227 * 1228 * The action we take depends on targact, which is the 1229 * user's selection or the command's default behavior. 1230 * If the user has explicitly opted into prompting, 1231 * explicitly asked invoked a force option, or said to 1232 * ignore things when there is a file here, then our 1233 * options are straightforward. When we're with the 1234 * program defaults, things get a bit more nuanced and 1235 * vary by program: 1236 * 1237 * ln: by default always fail with an error if target 1238 * exists, regardless of what kind of entity it is. 1239 * 1240 * cp: overwriting is allowed by default, overriding is 1241 * not. To override the -f flag will be specified which 1242 * will force removal. 1243 * 1244 * mv: overwriting is allowed by default, overriding 1245 * requires prompting if on stdin, otherwise it 1246 * proceeds. Note, "on stdin" varies based on XPG4 or 1247 * not. 1248 * 1249 * The history here is messy. Logically speaking the way 1250 * that overwriting and overriding was checked in the 1251 * past was the following rough logic: 1252 * 1253 * 1) Overwriting is considered if -i is set, -f (mv 1254 * only) wasn't set, and use_stdin() was true (always 1255 * true for XPG4, otherwise only if it was a tty). 1256 * 1257 * 2) Overriding is considered if it was mv, the file 1258 * was not write accessible, -f wasn't specified and the 1259 * target wasn't a symbolic link. 1260 * 1261 * 3) If both overwrite and override were set, it would 1262 * always prompt. If just override was set, it would 1263 * always prompt. However, if only overwrite was set, it 1264 * would only prompt if the target was a regular file! 1265 * 1266 * Based on this, you can see that cp/mv -i didn't 1267 * actually prompt for any number of cases as it didn't 1268 * consider if -i had been specified, which is 1269 * definitely against the spirit of -i (and POSIX). If 1270 * -i is specified we will **always** consider the 1271 * prompt based on use_stdin() because of history. If we 1272 * are looking at defaults, then we will honor the 1273 * historical conditions that were used to check for 1274 * overwriting and overriding. 1275 */ 1276 switch (targact) { 1277 case TA_SKIP: 1278 if (buf != NULL) 1279 free(buf); 1280 return (CHK_SKIP); 1281 case TA_OVERWRITE: 1282 break; 1283 case TA_PROMPT: 1284 if (use_stdin()) { 1285 prompt = B_TRUE; 1286 overwrite = B_TRUE; 1287 if (mve && access(target, W_OK) < 0 && 1288 !ISLNK(s2)) { 1289 override = B_TRUE; 1290 } 1291 } 1292 break; 1293 case TA_DEFAULT: 1294 if (lnk) { 1295 (void) fprintf(stderr, 1296 gettext("%s: %s: File exists\n"), 1297 cmd, target); 1298 if (buf != NULL) 1299 free(buf); 1300 return (CHK_ERROR); 1301 } 1302 1303 /* 1304 * Now we have to figure out what we're going to 1305 * do here. Determine if this meets the 1306 * traditional prompting guidelines. 1307 */ 1308 if (mve && access(target, W_OK) < 0 && 1309 use_stdin() && !ISLNK(s2)) { 1310 prompt = B_TRUE; 1311 override = B_TRUE; 1312 } 1313 break; 1314 } 1315 1316 /* 1317 * We've been asked to prompt. Determine the appropriate 1318 * message for the command and the type of action that 1319 * is going on. 1320 */ 1321 if (prompt) { 1322 assert(overwrite || override); 1323 if (overwrite && override) { 1324 (void) fprintf(stderr, gettext("%s: " 1325 "overwrite %s and override " 1326 "protection %o (%s/%s)? "), cmd, 1327 target, FMODE(s2) & MODEBITS, 1328 yesstr, nostr); 1329 } else if (overwrite) { 1330 (void) fprintf(stderr, gettext("%s: " 1331 "overwrite %s (%s/%s)? "), cmd, 1332 target, yesstr, nostr); 1333 } else if (override) { 1334 (void) fprintf(stderr, gettext("%s: " 1335 "%s: override protection %o " 1336 "(%s/%s)? "), cmd, target, 1337 FMODE(s2) & MODEBITS, yesstr, 1338 nostr); 1339 } 1340 if (yes() == 0) { 1341 if (buf != NULL) 1342 free(buf); 1343 return (CHK_SKIP); 1344 } 1345 } 1346 1347 if (lnk && unlink(target) < 0) { 1348 (void) fprintf(stderr, 1349 gettext("%s: cannot unlink %s: "), 1350 cmd, target); 1351 perror(""); 1352 return (CHK_ERROR); 1353 } 1354 } 1355 } 1356 return (CHK_CONT); 1357 } 1358 1359 /* 1360 * check whether source and target are different 1361 * return 1 when they are different 1362 * return 0 when they are identical, or when unable to resolve a pathname 1363 */ 1364 static int 1365 chk_different(const char *source, const char *target) 1366 { 1367 char rtarget[PATH_MAX], rsource[PATH_MAX]; 1368 1369 if (IDENTICAL(s1, s2)) { 1370 /* 1371 * IDENTICAL will be true for hard links, therefore 1372 * check whether the filenames are different 1373 */ 1374 if ((getrealpath(source, rsource) == 0) || 1375 (getrealpath(target, rtarget) == 0)) { 1376 return (0); 1377 } 1378 if (strncmp(rsource, rtarget, PATH_MAX) == 0) { 1379 (void) fprintf(stderr, gettext( 1380 "%s: %s and %s are identical\n"), 1381 cmd, source, target); 1382 return (0); 1383 } 1384 } 1385 return (1); 1386 } 1387 1388 /* 1389 * get real path (resolved absolute pathname) 1390 * return 1 on success, 0 on failure 1391 */ 1392 static int 1393 getrealpath(const char *path, char *rpath) 1394 { 1395 if (realpath(path, rpath) == NULL) { 1396 int errno_save = errno; 1397 (void) fprintf(stderr, gettext( 1398 "%s: cannot resolve path %s: "), cmd, path); 1399 errno = errno_save; 1400 perror(""); 1401 return (0); 1402 } 1403 return (1); 1404 } 1405 1406 static int 1407 rcopy(const char *from, char *to) 1408 { 1409 DIR *fold = opendir(from); 1410 struct dirent *dp; 1411 struct stat statb, s1save; 1412 int errs = 0; 1413 char fromname[PATH_MAX]; 1414 1415 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) { 1416 Perror(from); 1417 return (1); 1418 } 1419 if (pflg || mve) { 1420 /* 1421 * Save s1 (stat information for source dir) so that 1422 * mod and access times can be reserved during "cp -p" 1423 * or mv, since s1 gets overwritten. 1424 */ 1425 s1save = s1; 1426 } 1427 for (;;) { 1428 dp = readdir(fold); 1429 if (dp == 0) { 1430 (void) closedir(fold); 1431 if (pflg || mve) 1432 return (chg_time(to, s1save) + errs); 1433 return (errs); 1434 } 1435 if (dp->d_ino == 0) 1436 continue; 1437 if ((strcmp(dp->d_name, ".") == 0) || 1438 (strcmp(dp->d_name, "..") == 0)) 1439 continue; 1440 if (strlen(from)+1+strlen(dp->d_name) >= 1441 sizeof (fromname) - 1) { 1442 (void) fprintf(stderr, 1443 gettext("%s : %s/%s: Name too long\n"), 1444 cmd, from, dp->d_name); 1445 errs++; 1446 continue; 1447 } 1448 (void) snprintf(fromname, sizeof (fromname), 1449 "%s/%s", from, dp->d_name); 1450 errs += cpymve(fromname, to); 1451 } 1452 } 1453 1454 static const char * 1455 dname(const char *name) 1456 { 1457 const char *p; 1458 1459 /* 1460 * Return just the file name given the complete path. 1461 * Like basename(1). 1462 */ 1463 1464 p = name; 1465 1466 /* 1467 * While there are characters left, 1468 * set name to start after last 1469 * delimiter. 1470 */ 1471 1472 while (*p) 1473 if (*p++ == DELIM && *p) 1474 name = p; 1475 return (name); 1476 } 1477 1478 static void 1479 usage(void) 1480 { 1481 /* 1482 * Display usage message. 1483 */ 1484 1485 if (mve) { 1486 (void) fprintf(stderr, gettext( 1487 "Usage: mv [-fin] f1 f2\n" 1488 " mv [-fin] f1 ... fn d1\n" 1489 " mv [-fin] d1 d2\n")); 1490 } else if (lnk) { 1491 #ifdef XPG4 1492 (void) fprintf(stderr, gettext( 1493 "Usage: ln [-fi] [-s] f1 [f2]\n" 1494 " ln [-fi] [-s] f1 ... fn d1\n" 1495 " ln [-fi] -s d1 d2\n")); 1496 #else 1497 (void) fprintf(stderr, gettext( 1498 "Usage: ln [-fi] [-n] [-s] f1 [f2]\n" 1499 " ln [-fi] [-n] [-s] f1 ... fn d1\n" 1500 " ln [-fi] [-n] -s d1 d2\n")); 1501 #endif 1502 } else if (cpy) { 1503 (void) fprintf(stderr, gettext( 1504 "Usage: cp [-afinp@/] f1 f2\n" 1505 " cp [-afinp@/] f1 ... fn d1\n" 1506 " cp [-r|-R [-H|-L|-P]] [-afinp@/] " 1507 "d1 ... dn-1 dn\n")); 1508 } 1509 exit(2); 1510 } 1511 1512 /* 1513 * chg_time() 1514 * 1515 * Try to preserve modification and access time. 1516 * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version, 1517 * don't report a utimensat() failure. 1518 * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p) 1519 * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero 1520 * exit status only if pflg is set. 1521 * utimensat(2) is being used to achieve granularity in nanoseconds 1522 * (if supported by the underlying file system) while setting file times. 1523 */ 1524 static int 1525 chg_time(const char *to, struct stat ss) 1526 { 1527 struct timespec times[2]; 1528 #ifdef XPG4 1529 int rc; 1530 #endif 1531 1532 times[0] = ss.st_atim; 1533 times[1] = ss.st_mtim; 1534 1535 #ifdef XPG4 1536 rc = utimensat(AT_FDCWD, to, times, 1537 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0); 1538 if ((pflg || mve) && rc != 0) { 1539 (void) fprintf(stderr, 1540 gettext("%s: cannot set times for %s: "), cmd, to); 1541 perror(""); 1542 if (pflg) 1543 return (1); 1544 } 1545 #else 1546 (void) utimensat(AT_FDCWD, to, times, 1547 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0); 1548 #endif 1549 1550 return (0); 1551 1552 } 1553 1554 /* 1555 * chg_mode() 1556 * 1557 * This function is called upon "cp -p" or mv across filesystems. 1558 * 1559 * Try to preserve the owner and group id. If chown() fails, 1560 * only print a diagnostic message if doing a mv in the XPG4 version; 1561 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear 1562 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a 1563 * non-zero exit status because this is a security violation. 1564 * Try to preserve permissions. 1565 * If this is the XPG4 version and chmod() fails, print a diagnostic message 1566 * and arrange for a non-zero exit status. 1567 * If this is the Solaris version and chmod() fails, do not print a 1568 * diagnostic message or exit with a non-zero value. 1569 */ 1570 static int 1571 chg_mode(const char *target, uid_t uid, gid_t gid, mode_t mode) 1572 { 1573 int clearflg = 0; /* controls message printed upon chown() error */ 1574 struct stat st; 1575 1576 /* Don't change mode if target is symlink */ 1577 if (lstat(target, &st) == 0 && ISLNK(st)) 1578 return (0); 1579 1580 if (chown(target, uid, gid) != 0) { 1581 #ifdef XPG4 1582 if (mve) { 1583 (void) fprintf(stderr, gettext("%s: cannot change" 1584 " owner and group of %s: "), cmd, target); 1585 perror(""); 1586 } 1587 #endif 1588 if (mode & (S_ISUID | S_ISGID)) { 1589 /* try to clear S_ISUID and S_ISGID */ 1590 mode &= ~S_ISUID & ~S_ISGID; 1591 ++clearflg; 1592 } 1593 } 1594 if (chmod(target, mode) != 0) { 1595 if (clearflg) { 1596 (void) fprintf(stderr, gettext( 1597 "%s: cannot clear S_ISUID and S_ISGID bits in" 1598 " %s: "), cmd, target); 1599 perror(""); 1600 /* cp -p should get non-zero exit; mv should not */ 1601 if (pflg) 1602 return (1); 1603 } 1604 #ifdef XPG4 1605 else { 1606 (void) fprintf(stderr, gettext( 1607 "%s: cannot set permissions for %s: "), cmd, target); 1608 perror(""); 1609 /* cp -p should get non-zero exit; mv should not */ 1610 if (pflg) 1611 return (1); 1612 } 1613 #endif 1614 } 1615 return (0); 1616 1617 } 1618 1619 static void 1620 Perror(const char *s) 1621 { 1622 char buf[PATH_MAX + 10]; 1623 1624 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s); 1625 perror(buf); 1626 } 1627 1628 static void 1629 Perror2(const char *s1, const char *s2) 1630 { 1631 char buf[PATH_MAX + 20]; 1632 1633 (void) snprintf(buf, sizeof (buf), "%s: %s: %s", 1634 cmd, gettext(s1), gettext(s2)); 1635 perror(buf); 1636 } 1637 1638 /* 1639 * used for cp -R and for mv across file systems 1640 */ 1641 static int 1642 copydir(const char *source, char *target) 1643 { 1644 int ret, attret = 0; 1645 int sattret = 0; 1646 int pret = 0; /* need separate flag if -p is specified */ 1647 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */ 1648 struct stat s1save; 1649 acl_t *s1acl_save; 1650 int error = 0; 1651 1652 s1acl_save = NULL; 1653 1654 if (cpy && !rflg) { 1655 (void) fprintf(stderr, 1656 gettext("%s: %s: is a directory\n"), cmd, source); 1657 return (1); 1658 } 1659 1660 if (stat(target, &s2) < 0) { 1661 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) { 1662 (void) fprintf(stderr, "%s: ", cmd); 1663 perror(target); 1664 return (1); 1665 } 1666 if (stat(target, &s2) == 0) { 1667 fixmode = s2.st_mode; 1668 } else { 1669 fixmode = s1.st_mode; 1670 } 1671 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU)); 1672 } else if (!(ISDIR(s2))) { 1673 (void) fprintf(stderr, 1674 gettext("%s: %s: not a directory.\n"), cmd, target); 1675 return (1); 1676 } 1677 if (pflg || mve) { 1678 /* 1679 * Save s1 (stat information for source dir) and acl info, 1680 * if any, so that ownership, modes, times, and acl's can 1681 * be reserved during "cp -p" or mv. 1682 * s1 gets overwritten when doing the recursive copy. 1683 */ 1684 s1save = s1; 1685 if (s1acl != NULL) { 1686 s1acl_save = acl_dup(s1acl); 1687 if (s1acl_save == NULL) { 1688 (void) fprintf(stderr, gettext("%s: " 1689 "Insufficient memory to save acl" 1690 " entry\n"), cmd); 1691 if (pflg) 1692 return (1); 1693 1694 } 1695 #ifdef XPG4 1696 else { 1697 (void) fprintf(stderr, gettext("%s: " 1698 "Insufficient memory to save acl" 1699 " entry\n"), cmd); 1700 if (pflg) 1701 return (1); 1702 } 1703 #endif 1704 } 1705 } 1706 1707 ret = rcopy(source, target); 1708 1709 /* 1710 * Once we created a directory, go ahead and set 1711 * its attributes, e.g. acls and time. The info 1712 * may get overwritten if we continue traversing 1713 * down the tree. 1714 * 1715 * ACL for directory 1716 */ 1717 if (pflg || mve) { 1718 if ((pret = chg_mode(target, UID(s1save), GID(s1save), 1719 FMODE(s1save))) == 0) 1720 pret = chg_time(target, s1save); 1721 ret += pret; 1722 if (s1acl_save != NULL) { 1723 if (acl_set(target, s1acl_save) < 0) { 1724 error++; 1725 #ifdef XPG4 1726 if (pflg || mve) { 1727 #else 1728 if (pflg) { 1729 #endif 1730 (void) fprintf(stderr, gettext( 1731 "%s: failed to set acl entries " 1732 "on %s\n"), cmd, target); 1733 if (pflg) { 1734 acl_free(s1acl_save); 1735 s1acl_save = NULL; 1736 ret++; 1737 } 1738 } 1739 /* else: silent and continue */ 1740 } 1741 acl_free(s1acl_save); 1742 s1acl_save = NULL; 1743 } 1744 } else if (fixmode != (mode_t)0) 1745 (void) chmod(target, fixmode & MODEBITS); 1746 1747 if (pflg || atflg || mve || saflg) { 1748 attret = copyattributes(source, target); 1749 if (!attrsilent && attret != 0) { 1750 (void) fprintf(stderr, gettext("%s: Failed to preserve" 1751 " extended attributes of directory" 1752 " %s\n"), cmd, source); 1753 } else { 1754 /* 1755 * Otherwise ignore failure. 1756 */ 1757 attret = 0; 1758 } 1759 /* Copy extended system attributes */ 1760 if (pflg || mve || saflg) { 1761 sattret = copy_sysattr(source, target); 1762 if (sattret != 0) { 1763 (void) fprintf(stderr, gettext( 1764 "%s: Failed to preserve " 1765 "extended system attributes " 1766 "of directory %s\n"), cmd, source); 1767 } 1768 } 1769 } 1770 if (attret != 0 || sattret != 0 || error != 0) 1771 return (1); 1772 return (ret); 1773 } 1774 1775 static int 1776 copyspecial(const char *target) 1777 { 1778 int ret = 0; 1779 1780 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) { 1781 (void) fprintf(stderr, gettext( 1782 "cp: cannot create special file %s: "), target); 1783 perror(""); 1784 return (1); 1785 } 1786 1787 if (pflg) { 1788 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0) 1789 ret = chg_time(target, s1); 1790 } 1791 1792 return (ret); 1793 } 1794 1795 static int 1796 use_stdin(void) 1797 { 1798 #ifdef XPG4 1799 return (1); 1800 #else 1801 return (isatty(fileno(stdin))); 1802 #endif 1803 } 1804 1805 /* Copy non-system extended attributes */ 1806 1807 static int 1808 copyattributes(const char *source, const char *target) 1809 { 1810 struct dirent *dp; 1811 int error = 0; 1812 int aclerror; 1813 mode_t mode; 1814 int clearflg = 0; 1815 acl_t *xacl = NULL; 1816 acl_t *attrdiracl = NULL; 1817 struct timespec times[2]; 1818 1819 1820 if (pathconf(source, _PC_XATTR_EXISTS) != 1) 1821 return (0); 1822 1823 if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 1824 if (!attrsilent) { 1825 (void) fprintf(stderr, 1826 gettext( 1827 "%s: cannot preserve extended attributes, " 1828 "operation not supported on file" 1829 " %s\n"), cmd, target); 1830 } 1831 return (1); 1832 } 1833 if (open_source(source) != 0) 1834 return (1); 1835 if (open_target_srctarg_attrdirs(source, target) != 0) 1836 return (1); 1837 if (open_attrdirp(source) != 0) 1838 return (1); 1839 1840 if (pflg || mve) { 1841 if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 1842 if (!attrsilent) { 1843 (void) fprintf(stderr, 1844 gettext("%s: failed to set file mode" 1845 " correctly on attribute directory of" 1846 " file %s: "), cmd, target); 1847 perror(""); 1848 ++error; 1849 } 1850 } 1851 1852 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 1853 if (!attrsilent) { 1854 (void) fprintf(stderr, 1855 gettext("%s: failed to set file" 1856 " ownership correctly on attribute" 1857 " directory of file %s: "), cmd, target); 1858 perror(""); 1859 ++error; 1860 } 1861 } 1862 /* 1863 * Now that we are the owner we can update st_ctime by calling 1864 * utimensat. 1865 */ 1866 times[0] = attrdir.st_atim; 1867 times[1] = attrdir.st_mtim; 1868 if (utimensat(targetdirfd, ".", times, 0) < 0) { 1869 if (!attrsilent) { 1870 (void) fprintf(stderr, 1871 gettext("%s: cannot set attribute times" 1872 " for %s: "), cmd, target); 1873 perror(""); 1874 ++error; 1875 } 1876 } 1877 1878 /* 1879 * Now set owner and group of attribute directory, implies 1880 * changing the ACL of the hidden attribute directory first. 1881 */ 1882 if ((aclerror = facl_get(sourcedirfd, 1883 ACL_NO_TRIVIAL, &attrdiracl)) != 0) { 1884 if (!attrsilent) { 1885 (void) fprintf(stderr, gettext( 1886 "%s: failed to get acl entries of" 1887 " attribute directory for" 1888 " %s : %s\n"), cmd, 1889 source, acl_strerror(aclerror)); 1890 ++error; 1891 } 1892 } 1893 1894 if (attrdiracl) { 1895 if (facl_set(targetdirfd, attrdiracl) != 0) { 1896 if (!attrsilent) { 1897 (void) fprintf(stderr, gettext( 1898 "%s: failed to set acl entries" 1899 " on attribute directory " 1900 "for %s\n"), cmd, target); 1901 ++error; 1902 } 1903 acl_free(attrdiracl); 1904 attrdiracl = NULL; 1905 } 1906 } 1907 } 1908 1909 while ((dp = readdir(srcdirp)) != NULL) { 1910 int ret; 1911 1912 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1) 1913 continue; 1914 else if (ret > 0) { 1915 ++error; 1916 goto out; 1917 } 1918 1919 if (pflg || mve) { 1920 if ((aclerror = facl_get(srcattrfd, 1921 ACL_NO_TRIVIAL, &xacl)) != 0) { 1922 if (!attrsilent) { 1923 (void) fprintf(stderr, gettext( 1924 "%s: failed to get acl entries of" 1925 " attribute %s for" 1926 " %s: %s"), cmd, dp->d_name, 1927 source, acl_strerror(aclerror)); 1928 ++error; 1929 } 1930 } 1931 } 1932 1933 /* 1934 * preserve ACL 1935 */ 1936 if ((pflg || mve) && xacl != NULL) { 1937 if ((facl_set(targattrfd, xacl)) < 0) { 1938 if (!attrsilent) { 1939 (void) fprintf(stderr, gettext( 1940 "%s: failed to set acl entries on" 1941 " attribute %s for" 1942 "%s\n"), cmd, dp->d_name, target); 1943 ++error; 1944 } 1945 acl_free(xacl); 1946 xacl = NULL; 1947 } 1948 } 1949 1950 if (writefile(srcattrfd, targattrfd, source, target, 1951 dp->d_name, dp->d_name, &s3, &s4) != 0) { 1952 if (!attrsilent) { 1953 ++error; 1954 } 1955 goto next; 1956 } 1957 1958 if (pflg || mve) { 1959 mode = FMODE(s3); 1960 1961 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 1962 if (!attrsilent) { 1963 (void) fprintf(stderr, 1964 gettext("%s: cannot change" 1965 " owner and group of" 1966 " attribute %s for" " file" 1967 " %s: "), cmd, dp->d_name, target); 1968 perror(""); 1969 ++error; 1970 } 1971 if (mode & (S_ISUID | S_ISGID)) { 1972 /* try to clear S_ISUID and S_ISGID */ 1973 mode &= ~S_ISUID & ~S_ISGID; 1974 ++clearflg; 1975 } 1976 } 1977 times[0] = s3.st_atim; 1978 times[1] = s3.st_mtim; 1979 if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) { 1980 if (!attrsilent) { 1981 (void) fprintf(stderr, 1982 gettext("%s: cannot set attribute" 1983 " times for %s: "), cmd, target); 1984 perror(""); 1985 ++error; 1986 } 1987 } 1988 if (fchmod(targattrfd, mode) != 0) { 1989 if (clearflg) { 1990 (void) fprintf(stderr, gettext( 1991 "%s: cannot clear S_ISUID and " 1992 "S_ISGID bits in attribute %s" 1993 " for file" 1994 " %s: "), cmd, dp->d_name, target); 1995 } else { 1996 if (!attrsilent) { 1997 (void) fprintf(stderr, 1998 gettext( 1999 "%s: cannot set permissions of attribute" 2000 " %s for %s: "), cmd, dp->d_name, target); 2001 perror(""); 2002 ++error; 2003 } 2004 } 2005 } 2006 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) { 2007 if (!attrsilent) { 2008 (void) fprintf(stderr, gettext( 2009 "%s: failed to set acl entries on" 2010 " attribute %s for" 2011 "%s\n"), cmd, dp->d_name, target); 2012 ++error; 2013 } 2014 acl_free(xacl); 2015 xacl = NULL; 2016 } 2017 } 2018 next: 2019 if (xacl != NULL) { 2020 acl_free(xacl); 2021 xacl = NULL; 2022 } 2023 if (srcattrfd != -1) 2024 (void) close(srcattrfd); 2025 if (targattrfd != -1) 2026 (void) close(targattrfd); 2027 srcattrfd = targattrfd = -1; 2028 } 2029 out: 2030 if (xacl != NULL) { 2031 acl_free(xacl); 2032 xacl = NULL; 2033 } 2034 if (attrdiracl != NULL) { 2035 acl_free(attrdiracl); 2036 attrdiracl = NULL; 2037 } 2038 2039 if (!saflg && !pflg && !mve) 2040 close_all(); 2041 return (error == 0 ? 0 : 1); 2042 } 2043 2044 /* Copy extended system attributes from source to target */ 2045 2046 static int 2047 copy_sysattr(const char *source, const char *target) 2048 { 2049 struct dirent *dp; 2050 nvlist_t *response; 2051 int error = 0; 2052 int target_sa_support = 0; 2053 2054 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1) 2055 return (0); 2056 2057 if (open_source(source) != 0) 2058 return (1); 2059 2060 /* 2061 * Gets non default extended system attributes from the 2062 * source file to copy to the target. The target has 2063 * the defaults set when its created and thus no need 2064 * to copy the defaults. 2065 */ 2066 response = sysattr_list(cmd, srcfd, source); 2067 2068 if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) { 2069 if (response != NULL) { 2070 (void) fprintf(stderr, 2071 gettext( 2072 "%s: cannot preserve extended system " 2073 "attribute, operation not supported on file" 2074 " %s\n"), cmd, target); 2075 error++; 2076 goto out; 2077 } 2078 } else { 2079 target_sa_support = 1; 2080 } 2081 2082 if (target_sa_support) { 2083 if (srcdirp == NULL) { 2084 if (open_target_srctarg_attrdirs(source, 2085 target) != 0) { 2086 error++; 2087 goto out; 2088 } 2089 if (open_attrdirp(source) != 0) { 2090 error++; 2091 goto out; 2092 } 2093 } else { 2094 rewind_attrdir(srcdirp); 2095 } 2096 while ((dp = readdir(srcdirp)) != NULL) { 2097 nvlist_t *res; 2098 int ret; 2099 2100 if ((ret = traverse_attrfile(dp, source, target, 2101 0)) == -1) 2102 continue; 2103 else if (ret > 0) { 2104 ++error; 2105 goto out; 2106 } 2107 /* 2108 * Gets non default extended system attributes from the 2109 * attribute file to copy to the target. The target has 2110 * the defaults set when its created and thus no need 2111 * to copy the defaults. 2112 */ 2113 res = sysattr_list(cmd, srcattrfd, dp->d_name); 2114 if (res == NULL) 2115 goto next; 2116 2117 /* 2118 * Copy non default extended system attributes of named 2119 * attribute file. 2120 */ 2121 if (fsetattr(targattrfd, 2122 XATTR_VIEW_READWRITE, res) != 0) { 2123 ++error; 2124 (void) fprintf(stderr, gettext("%s: " 2125 "Failed to copy extended system " 2126 "attributes from attribute file " 2127 "%s of %s to %s\n"), cmd, 2128 dp->d_name, source, target); 2129 } 2130 2131 next: 2132 if (srcattrfd != -1) 2133 (void) close(srcattrfd); 2134 if (targattrfd != -1) 2135 (void) close(targattrfd); 2136 srcattrfd = targattrfd = -1; 2137 nvlist_free(res); 2138 } 2139 } 2140 /* Copy source file non default extended system attributes to target */ 2141 if (target_sa_support && (response != NULL) && 2142 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) { 2143 ++error; 2144 (void) fprintf(stderr, gettext("%s: Failed to " 2145 "copy extended system attributes from " 2146 "%s to %s\n"), cmd, source, target); 2147 } 2148 out: 2149 nvlist_free(response); 2150 close_all(); 2151 return (error == 0 ? 0 : 1); 2152 } 2153 2154 /* Open the source file */ 2155 2156 static int 2157 open_source(const char *src) 2158 { 2159 int error = 0; 2160 2161 srcfd = -1; 2162 if ((srcfd = open(src, O_RDONLY)) == -1) { 2163 if (pflg && attrsilent) { 2164 error++; 2165 goto out; 2166 } 2167 if (!attrsilent) { 2168 (void) fprintf(stderr, 2169 gettext("%s: cannot open file" 2170 " %s: "), cmd, src); 2171 perror(""); 2172 } 2173 ++error; 2174 } 2175 out: 2176 if (error) 2177 close_all(); 2178 return (error == 0 ? 0 : 1); 2179 } 2180 2181 /* Open source attribute dir, target and target attribute dir. */ 2182 2183 static int 2184 open_target_srctarg_attrdirs(const char *src, const char *targ) 2185 { 2186 int error = 0; 2187 2188 targfd = sourcedirfd = targetdirfd = -1; 2189 2190 if ((targfd = open(targ, O_RDONLY)) == -1) { 2191 if (pflg && attrsilent) { 2192 error++; 2193 goto out; 2194 } 2195 if (!attrsilent) { 2196 (void) fprintf(stderr, 2197 gettext("%s: cannot open file" 2198 " %s: "), cmd, targ); 2199 perror(""); 2200 } 2201 ++error; 2202 goto out; 2203 } 2204 2205 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 2206 if (pflg && attrsilent) { 2207 error++; 2208 goto out; 2209 } 2210 if (!attrsilent) { 2211 (void) fprintf(stderr, 2212 gettext("%s: cannot open attribute" 2213 " directory for %s: "), cmd, src); 2214 perror(""); 2215 } 2216 ++error; 2217 goto out; 2218 } 2219 2220 if (fstat(sourcedirfd, &attrdir) == -1) { 2221 if (pflg && attrsilent) { 2222 error++; 2223 goto out; 2224 } 2225 2226 if (!attrsilent) { 2227 (void) fprintf(stderr, 2228 gettext("%s: could not retrieve stat" 2229 " information for attribute directory" 2230 "of file %s: "), cmd, src); 2231 perror(""); 2232 } 2233 ++error; 2234 goto out; 2235 } 2236 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 2237 if (pflg && attrsilent) { 2238 error++; 2239 goto out; 2240 } 2241 if (!attrsilent) { 2242 (void) fprintf(stderr, 2243 gettext("%s: cannot open attribute" 2244 " directory for %s: "), cmd, targ); 2245 perror(""); 2246 } 2247 ++error; 2248 } 2249 out: 2250 if (error) 2251 close_all(); 2252 return (error == 0 ? 0 : 1); 2253 } 2254 2255 static int 2256 open_attrdirp(const char *source) 2257 { 2258 int tmpfd = -1; 2259 int error = 0; 2260 2261 /* 2262 * dup sourcedirfd for use by fdopendir(). 2263 * fdopendir will take ownership of given fd and will close 2264 * it when closedir() is called. 2265 */ 2266 2267 if ((tmpfd = dup(sourcedirfd)) == -1) { 2268 if (pflg && attrsilent) { 2269 error++; 2270 goto out; 2271 } 2272 if (!attrsilent) { 2273 (void) fprintf(stderr, 2274 gettext( 2275 "%s: unable to dup attribute directory" 2276 " file descriptor for %s: "), cmd, source); 2277 perror(""); 2278 ++error; 2279 } 2280 goto out; 2281 } 2282 if ((srcdirp = fdopendir(tmpfd)) == NULL) { 2283 if (pflg && attrsilent) { 2284 error++; 2285 goto out; 2286 } 2287 if (!attrsilent) { 2288 (void) fprintf(stderr, 2289 gettext("%s: failed to open attribute" 2290 " directory for %s: "), cmd, source); 2291 perror(""); 2292 ++error; 2293 } 2294 } 2295 out: 2296 if (error) 2297 close_all(); 2298 return (error == 0 ? 0 : 1); 2299 } 2300 2301 /* Skips through ., .., and system attribute 'view' files */ 2302 static int 2303 traverse_attrfile(struct dirent *dp, const char *source, const char *target, 2304 int first) 2305 { 2306 int error = 0; 2307 2308 srcattrfd = targattrfd = -1; 2309 2310 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 2311 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 2312 dp->d_name[2] == '\0') || 2313 (sysattr_type(dp->d_name) == _RO_SATTR) || 2314 (sysattr_type(dp->d_name) == _RW_SATTR)) 2315 return (-1); 2316 2317 if ((srcattrfd = openat(sourcedirfd, dp->d_name, 2318 O_RDONLY)) == -1) { 2319 if (!attrsilent) { 2320 (void) fprintf(stderr, 2321 gettext("%s: cannot open attribute %s on" 2322 " file %s: "), cmd, dp->d_name, source); 2323 perror(""); 2324 ++error; 2325 goto out; 2326 } 2327 } 2328 2329 if (fstat(srcattrfd, &s3) < 0) { 2330 if (!attrsilent) { 2331 (void) fprintf(stderr, 2332 gettext("%s: could not stat attribute" 2333 " %s on file" 2334 " %s: "), cmd, dp->d_name, source); 2335 perror(""); 2336 ++error; 2337 } 2338 goto out; 2339 } 2340 2341 if (first) { 2342 (void) unlinkat(targetdirfd, dp->d_name, 0); 2343 if ((targattrfd = openat(targetdirfd, dp->d_name, 2344 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) { 2345 if (!attrsilent) { 2346 (void) fprintf(stderr, 2347 gettext("%s: could not create attribute" 2348 " %s on file %s: "), cmd, dp->d_name, 2349 target); 2350 perror(""); 2351 ++error; 2352 } 2353 goto out; 2354 } 2355 } else { 2356 if ((targattrfd = openat(targetdirfd, dp->d_name, 2357 O_RDONLY)) == -1) { 2358 if (!attrsilent) { 2359 (void) fprintf(stderr, 2360 gettext("%s: could not open attribute" 2361 " %s on file %s: "), cmd, dp->d_name, 2362 target); 2363 perror(""); 2364 ++error; 2365 } 2366 goto out; 2367 } 2368 } 2369 2370 2371 if (fstat(targattrfd, &s4) < 0) { 2372 if (!attrsilent) { 2373 (void) fprintf(stderr, 2374 gettext("%s: could not stat attribute" 2375 " %s on file" 2376 " %s: "), cmd, dp->d_name, target); 2377 perror(""); 2378 ++error; 2379 } 2380 } 2381 2382 out: 2383 if (error) { 2384 if (srcattrfd != -1) 2385 (void) close(srcattrfd); 2386 if (targattrfd != -1) 2387 (void) close(targattrfd); 2388 srcattrfd = targattrfd = -1; 2389 } 2390 return (error == 0 ? 0 :1); 2391 } 2392 2393 static void 2394 rewind_attrdir(DIR * sdp) 2395 { 2396 int pwdfd; 2397 2398 pwdfd = open(".", O_RDONLY); 2399 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) { 2400 rewinddir(sdp); 2401 (void) fchdir(pwdfd); 2402 (void) close(pwdfd); 2403 } else { 2404 if (!attrsilent) { 2405 (void) fprintf(stderr, gettext("%s: " 2406 "failed to rewind attribute dir\n"), 2407 cmd); 2408 } 2409 } 2410 } 2411 2412 static void 2413 close_all(void) 2414 { 2415 if (srcattrfd != -1) 2416 (void) close(srcattrfd); 2417 if (targattrfd != -1) 2418 (void) close(targattrfd); 2419 if (sourcedirfd != -1) 2420 (void) close(sourcedirfd); 2421 if (targetdirfd != -1) 2422 (void) close(targetdirfd); 2423 if (srcdirp != NULL) { 2424 (void) closedir(srcdirp); 2425 srcdirp = NULL; 2426 } 2427 if (srcfd != -1) 2428 (void) close(srcfd); 2429 if (targfd != -1) 2430 (void) close(targfd); 2431 } 2432