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