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