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