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 int rc; 1358 1359 times[0] = ss.st_atim; 1360 times[1] = ss.st_mtim; 1361 1362 rc = utimensat(AT_FDCWD, to, times, 1363 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0); 1364 #ifdef XPG4 1365 if ((pflg || mve) && rc != 0) { 1366 (void) fprintf(stderr, 1367 gettext("%s: cannot set times for %s: "), cmd, to); 1368 perror(""); 1369 if (pflg) 1370 return (1); 1371 } 1372 #endif 1373 1374 return (0); 1375 1376 } 1377 1378 /* 1379 * chg_mode() 1380 * 1381 * This function is called upon "cp -p" or mv across filesystems. 1382 * 1383 * Try to preserve the owner and group id. If chown() fails, 1384 * only print a diagnostic message if doing a mv in the XPG4 version; 1385 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear 1386 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a 1387 * non-zero exit status because this is a security violation. 1388 * Try to preserve permissions. 1389 * If this is the XPG4 version and chmod() fails, print a diagnostic message 1390 * and arrange for a non-zero exit status. 1391 * If this is the Solaris version and chmod() fails, do not print a 1392 * diagnostic message or exit with a non-zero value. 1393 */ 1394 static int 1395 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode) 1396 { 1397 int clearflg = 0; /* controls message printed upon chown() error */ 1398 struct stat st; 1399 1400 /* Don't change mode if target is symlink */ 1401 if (lstat(target, &st) == 0 && ISLNK(st)) 1402 return (0); 1403 1404 if (chown(target, uid, gid) != 0) { 1405 #ifdef XPG4 1406 if (mve) { 1407 (void) fprintf(stderr, gettext("%s: cannot change" 1408 " owner and group of %s: "), cmd, target); 1409 perror(""); 1410 } 1411 #endif 1412 if (mode & (S_ISUID | S_ISGID)) { 1413 /* try to clear S_ISUID and S_ISGID */ 1414 mode &= ~S_ISUID & ~S_ISGID; 1415 ++clearflg; 1416 } 1417 } 1418 if (chmod(target, mode) != 0) { 1419 if (clearflg) { 1420 (void) fprintf(stderr, gettext( 1421 "%s: cannot clear S_ISUID and S_ISGID bits in" 1422 " %s: "), cmd, target); 1423 perror(""); 1424 /* cp -p should get non-zero exit; mv should not */ 1425 if (pflg) 1426 return (1); 1427 } 1428 #ifdef XPG4 1429 else { 1430 (void) fprintf(stderr, gettext( 1431 "%s: cannot set permissions for %s: "), cmd, target); 1432 perror(""); 1433 /* cp -p should get non-zero exit; mv should not */ 1434 if (pflg) 1435 return (1); 1436 } 1437 #endif 1438 } 1439 return (0); 1440 1441 } 1442 1443 static void 1444 Perror(char *s) 1445 { 1446 char buf[PATH_MAX + 10]; 1447 1448 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s); 1449 perror(buf); 1450 } 1451 1452 static void 1453 Perror2(char *s1, char *s2) 1454 { 1455 char buf[PATH_MAX + 20]; 1456 1457 (void) snprintf(buf, sizeof (buf), "%s: %s: %s", 1458 cmd, gettext(s1), gettext(s2)); 1459 perror(buf); 1460 } 1461 1462 /* 1463 * used for cp -R and for mv across file systems 1464 */ 1465 static int 1466 copydir(char *source, char *target) 1467 { 1468 int ret, attret = 0; 1469 int sattret = 0; 1470 int pret = 0; /* need separate flag if -p is specified */ 1471 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */ 1472 struct stat s1save; 1473 acl_t *s1acl_save; 1474 int error = 0; 1475 1476 s1acl_save = NULL; 1477 1478 if (cpy && !rflg) { 1479 (void) fprintf(stderr, 1480 gettext("%s: %s: is a directory\n"), cmd, source); 1481 return (1); 1482 } 1483 1484 if (stat(target, &s2) < 0) { 1485 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) { 1486 (void) fprintf(stderr, "%s: ", cmd); 1487 perror(target); 1488 return (1); 1489 } 1490 if (stat(target, &s2) == 0) { 1491 fixmode = s2.st_mode; 1492 } else { 1493 fixmode = s1.st_mode; 1494 } 1495 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU)); 1496 } else if (!(ISDIR(s2))) { 1497 (void) fprintf(stderr, 1498 gettext("%s: %s: not a directory.\n"), cmd, target); 1499 return (1); 1500 } 1501 if (pflg || mve) { 1502 /* 1503 * Save s1 (stat information for source dir) and acl info, 1504 * if any, so that ownership, modes, times, and acl's can 1505 * be reserved during "cp -p" or mv. 1506 * s1 gets overwritten when doing the recursive copy. 1507 */ 1508 s1save = s1; 1509 if (s1acl != NULL) { 1510 s1acl_save = acl_dup(s1acl); 1511 if (s1acl_save == NULL) { 1512 (void) fprintf(stderr, gettext("%s: " 1513 "Insufficient memory to save acl" 1514 " entry\n"), cmd); 1515 if (pflg) 1516 return (1); 1517 1518 } 1519 #ifdef XPG4 1520 else { 1521 (void) fprintf(stderr, gettext("%s: " 1522 "Insufficient memory to save acl" 1523 " entry\n"), cmd); 1524 if (pflg) 1525 return (1); 1526 } 1527 #endif 1528 } 1529 } 1530 1531 ret = rcopy(source, target); 1532 1533 /* 1534 * Once we created a directory, go ahead and set 1535 * its attributes, e.g. acls and time. The info 1536 * may get overwritten if we continue traversing 1537 * down the tree. 1538 * 1539 * ACL for directory 1540 */ 1541 if (pflg || mve) { 1542 if ((pret = chg_mode(target, UID(s1save), GID(s1save), 1543 FMODE(s1save))) == 0) 1544 pret = chg_time(target, s1save); 1545 ret += pret; 1546 if (s1acl_save != NULL) { 1547 if (acl_set(target, s1acl_save) < 0) { 1548 error++; 1549 #ifdef XPG4 1550 if (pflg || mve) { 1551 #else 1552 if (pflg) { 1553 #endif 1554 (void) fprintf(stderr, gettext( 1555 "%s: failed to set acl entries " 1556 "on %s\n"), cmd, target); 1557 if (pflg) { 1558 acl_free(s1acl_save); 1559 s1acl_save = NULL; 1560 ret++; 1561 } 1562 } 1563 /* else: silent and continue */ 1564 } 1565 acl_free(s1acl_save); 1566 s1acl_save = NULL; 1567 } 1568 } else if (fixmode != (mode_t)0) 1569 (void) chmod(target, fixmode & MODEBITS); 1570 1571 if (pflg || atflg || mve || saflg) { 1572 attret = copyattributes(source, target); 1573 if (!attrsilent && attret != 0) { 1574 (void) fprintf(stderr, gettext("%s: Failed to preserve" 1575 " extended attributes of directory" 1576 " %s\n"), cmd, source); 1577 } else { 1578 /* 1579 * Otherwise ignore failure. 1580 */ 1581 attret = 0; 1582 } 1583 /* Copy extended system attributes */ 1584 if (pflg || mve || saflg) { 1585 sattret = copy_sysattr(source, target); 1586 if (sattret != 0) { 1587 (void) fprintf(stderr, gettext( 1588 "%s: Failed to preserve " 1589 "extended system attributes " 1590 "of directory %s\n"), cmd, source); 1591 } 1592 } 1593 } 1594 if (attret != 0 || sattret != 0 || error != 0) 1595 return (1); 1596 return (ret); 1597 } 1598 1599 static int 1600 copyspecial(char *target) 1601 { 1602 int ret = 0; 1603 1604 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) { 1605 (void) fprintf(stderr, gettext( 1606 "cp: cannot create special file %s: "), target); 1607 perror(""); 1608 return (1); 1609 } 1610 1611 if (pflg) { 1612 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0) 1613 ret = chg_time(target, s1); 1614 } 1615 1616 return (ret); 1617 } 1618 1619 static int 1620 use_stdin(void) 1621 { 1622 #ifdef XPG4 1623 return (1); 1624 #else 1625 return (isatty(fileno(stdin))); 1626 #endif 1627 } 1628 1629 /* Copy non-system extended attributes */ 1630 1631 static int 1632 copyattributes(char *source, char *target) 1633 { 1634 struct dirent *dp; 1635 int error = 0; 1636 int aclerror; 1637 mode_t mode; 1638 int clearflg = 0; 1639 acl_t *xacl = NULL; 1640 acl_t *attrdiracl = NULL; 1641 struct timespec times[2]; 1642 1643 1644 if (pathconf(source, _PC_XATTR_EXISTS) != 1) 1645 return (0); 1646 1647 if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 1648 if (!attrsilent) { 1649 (void) fprintf(stderr, 1650 gettext( 1651 "%s: cannot preserve extended attributes, " 1652 "operation not supported on file" 1653 " %s\n"), cmd, target); 1654 } 1655 return (1); 1656 } 1657 if (open_source(source) != 0) 1658 return (1); 1659 if (open_target_srctarg_attrdirs(source, target) != 0) 1660 return (1); 1661 if (open_attrdirp(source) != 0) 1662 return (1); 1663 1664 if (pflg || mve) { 1665 if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 1666 if (!attrsilent) { 1667 (void) fprintf(stderr, 1668 gettext("%s: failed to set file mode" 1669 " correctly on attribute directory of" 1670 " file %s: "), cmd, target); 1671 perror(""); 1672 ++error; 1673 } 1674 } 1675 1676 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 1677 if (!attrsilent) { 1678 (void) fprintf(stderr, 1679 gettext("%s: failed to set file" 1680 " ownership correctly on attribute" 1681 " directory of file %s: "), cmd, target); 1682 perror(""); 1683 ++error; 1684 } 1685 } 1686 /* 1687 * Now that we are the owner we can update st_ctime by calling 1688 * utimensat. 1689 */ 1690 times[0] = attrdir.st_atim; 1691 times[1] = attrdir.st_mtim; 1692 if (utimensat(targetdirfd, ".", times, 0) < 0) { 1693 if (!attrsilent) { 1694 (void) fprintf(stderr, 1695 gettext("%s: cannot set attribute times" 1696 " for %s: "), cmd, target); 1697 perror(""); 1698 ++error; 1699 } 1700 } 1701 1702 /* 1703 * Now set owner and group of attribute directory, implies 1704 * changing the ACL of the hidden attribute directory first. 1705 */ 1706 if ((aclerror = facl_get(sourcedirfd, 1707 ACL_NO_TRIVIAL, &attrdiracl)) != 0) { 1708 if (!attrsilent) { 1709 (void) fprintf(stderr, gettext( 1710 "%s: failed to get acl entries of" 1711 " attribute directory for" 1712 " %s : %s\n"), cmd, 1713 source, acl_strerror(aclerror)); 1714 ++error; 1715 } 1716 } 1717 1718 if (attrdiracl) { 1719 if (facl_set(targetdirfd, attrdiracl) != 0) { 1720 if (!attrsilent) { 1721 (void) fprintf(stderr, gettext( 1722 "%s: failed to set acl entries" 1723 " on attribute directory " 1724 "for %s\n"), cmd, target); 1725 ++error; 1726 } 1727 acl_free(attrdiracl); 1728 attrdiracl = NULL; 1729 } 1730 } 1731 } 1732 1733 while ((dp = readdir(srcdirp)) != NULL) { 1734 int ret; 1735 1736 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1) 1737 continue; 1738 else if (ret > 0) { 1739 ++error; 1740 goto out; 1741 } 1742 1743 if (pflg || mve) { 1744 if ((aclerror = facl_get(srcattrfd, 1745 ACL_NO_TRIVIAL, &xacl)) != 0) { 1746 if (!attrsilent) { 1747 (void) fprintf(stderr, gettext( 1748 "%s: failed to get acl entries of" 1749 " attribute %s for" 1750 " %s: %s"), cmd, dp->d_name, 1751 source, acl_strerror(aclerror)); 1752 ++error; 1753 } 1754 } 1755 } 1756 1757 /* 1758 * preserve ACL 1759 */ 1760 if ((pflg || mve) && xacl != NULL) { 1761 if ((facl_set(targattrfd, xacl)) < 0) { 1762 if (!attrsilent) { 1763 (void) fprintf(stderr, gettext( 1764 "%s: failed to set acl entries on" 1765 " attribute %s for" 1766 "%s\n"), cmd, dp->d_name, target); 1767 ++error; 1768 } 1769 acl_free(xacl); 1770 xacl = NULL; 1771 } 1772 } 1773 1774 if (writefile(srcattrfd, targattrfd, source, target, 1775 dp->d_name, dp->d_name, &s3, &s4) != 0) { 1776 if (!attrsilent) { 1777 ++error; 1778 } 1779 goto next; 1780 } 1781 1782 if (pflg || mve) { 1783 mode = FMODE(s3); 1784 1785 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 1786 if (!attrsilent) { 1787 (void) fprintf(stderr, 1788 gettext("%s: cannot change" 1789 " owner and group of" 1790 " attribute %s for" " file" 1791 " %s: "), cmd, dp->d_name, target); 1792 perror(""); 1793 ++error; 1794 } 1795 if (mode & (S_ISUID | S_ISGID)) { 1796 /* try to clear S_ISUID and S_ISGID */ 1797 mode &= ~S_ISUID & ~S_ISGID; 1798 ++clearflg; 1799 } 1800 } 1801 times[0] = s3.st_atim; 1802 times[1] = s3.st_mtim; 1803 if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) { 1804 if (!attrsilent) { 1805 (void) fprintf(stderr, 1806 gettext("%s: cannot set attribute" 1807 " times for %s: "), cmd, target); 1808 perror(""); 1809 ++error; 1810 } 1811 } 1812 if (fchmod(targattrfd, mode) != 0) { 1813 if (clearflg) { 1814 (void) fprintf(stderr, gettext( 1815 "%s: cannot clear S_ISUID and " 1816 "S_ISGID bits in attribute %s" 1817 " for file" 1818 " %s: "), cmd, dp->d_name, target); 1819 } else { 1820 if (!attrsilent) { 1821 (void) fprintf(stderr, 1822 gettext( 1823 "%s: cannot set permissions of attribute" 1824 " %s for %s: "), cmd, dp->d_name, target); 1825 perror(""); 1826 ++error; 1827 } 1828 } 1829 } 1830 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) { 1831 if (!attrsilent) { 1832 (void) fprintf(stderr, gettext( 1833 "%s: failed to set acl entries on" 1834 " attribute %s for" 1835 "%s\n"), cmd, dp->d_name, target); 1836 ++error; 1837 } 1838 acl_free(xacl); 1839 xacl = NULL; 1840 } 1841 } 1842 next: 1843 if (xacl != NULL) { 1844 acl_free(xacl); 1845 xacl = NULL; 1846 } 1847 if (srcattrfd != -1) 1848 (void) close(srcattrfd); 1849 if (targattrfd != -1) 1850 (void) close(targattrfd); 1851 srcattrfd = targattrfd = -1; 1852 } 1853 out: 1854 if (xacl != NULL) { 1855 acl_free(xacl); 1856 xacl = NULL; 1857 } 1858 if (attrdiracl != NULL) { 1859 acl_free(attrdiracl); 1860 attrdiracl = NULL; 1861 } 1862 1863 if (!saflg && !pflg && !mve) 1864 close_all(); 1865 return (error == 0 ? 0 : 1); 1866 } 1867 1868 /* Copy extended system attributes from source to target */ 1869 1870 static int 1871 copy_sysattr(char *source, char *target) 1872 { 1873 struct dirent *dp; 1874 nvlist_t *response; 1875 int error = 0; 1876 int target_sa_support = 0; 1877 1878 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1) 1879 return (0); 1880 1881 if (open_source(source) != 0) 1882 return (1); 1883 1884 /* 1885 * Gets non default extended system attributes from the 1886 * source file to copy to the target. The target has 1887 * the defaults set when its created and thus no need 1888 * to copy the defaults. 1889 */ 1890 response = sysattr_list(cmd, srcfd, source); 1891 1892 if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) { 1893 if (response != NULL) { 1894 (void) fprintf(stderr, 1895 gettext( 1896 "%s: cannot preserve extended system " 1897 "attribute, operation not supported on file" 1898 " %s\n"), cmd, target); 1899 error++; 1900 goto out; 1901 } 1902 } else { 1903 target_sa_support = 1; 1904 } 1905 1906 if (target_sa_support) { 1907 if (srcdirp == NULL) { 1908 if (open_target_srctarg_attrdirs(source, 1909 target) != 0) { 1910 error++; 1911 goto out; 1912 } 1913 if (open_attrdirp(source) != 0) { 1914 error++; 1915 goto out; 1916 } 1917 } else { 1918 rewind_attrdir(srcdirp); 1919 } 1920 while ((dp = readdir(srcdirp)) != NULL) { 1921 nvlist_t *res; 1922 int ret; 1923 1924 if ((ret = traverse_attrfile(dp, source, target, 1925 0)) == -1) 1926 continue; 1927 else if (ret > 0) { 1928 ++error; 1929 goto out; 1930 } 1931 /* 1932 * Gets non default extended system attributes from the 1933 * attribute file to copy to the target. The target has 1934 * the defaults set when its created and thus no need 1935 * to copy the defaults. 1936 */ 1937 if (dp->d_name != NULL) { 1938 res = sysattr_list(cmd, srcattrfd, dp->d_name); 1939 if (res == NULL) 1940 goto next; 1941 1942 /* 1943 * Copy non default extended system attributes of named 1944 * attribute file. 1945 */ 1946 if (fsetattr(targattrfd, 1947 XATTR_VIEW_READWRITE, res) != 0) { 1948 ++error; 1949 (void) fprintf(stderr, gettext("%s: " 1950 "Failed to copy extended system " 1951 "attributes from attribute file " 1952 "%s of %s to %s\n"), cmd, 1953 dp->d_name, source, target); 1954 } 1955 } 1956 next: 1957 if (srcattrfd != -1) 1958 (void) close(srcattrfd); 1959 if (targattrfd != -1) 1960 (void) close(targattrfd); 1961 srcattrfd = targattrfd = -1; 1962 nvlist_free(res); 1963 } 1964 } 1965 /* Copy source file non default extended system attributes to target */ 1966 if (target_sa_support && (response != NULL) && 1967 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) { 1968 ++error; 1969 (void) fprintf(stderr, gettext("%s: Failed to " 1970 "copy extended system attributes from " 1971 "%s to %s\n"), cmd, source, target); 1972 } 1973 out: 1974 nvlist_free(response); 1975 close_all(); 1976 return (error == 0 ? 0 : 1); 1977 } 1978 1979 /* Open the source file */ 1980 1981 int 1982 open_source(char *src) 1983 { 1984 int error = 0; 1985 1986 srcfd = -1; 1987 if ((srcfd = open(src, O_RDONLY)) == -1) { 1988 if (pflg && attrsilent) { 1989 error++; 1990 goto out; 1991 } 1992 if (!attrsilent) { 1993 (void) fprintf(stderr, 1994 gettext("%s: cannot open file" 1995 " %s: "), cmd, src); 1996 perror(""); 1997 } 1998 ++error; 1999 } 2000 out: 2001 if (error) 2002 close_all(); 2003 return (error == 0 ? 0 : 1); 2004 } 2005 2006 /* Open source attribute dir, target and target attribute dir. */ 2007 2008 int 2009 open_target_srctarg_attrdirs(char *src, char *targ) 2010 { 2011 int error = 0; 2012 2013 targfd = sourcedirfd = targetdirfd = -1; 2014 2015 if ((targfd = open(targ, O_RDONLY)) == -1) { 2016 if (pflg && attrsilent) { 2017 error++; 2018 goto out; 2019 } 2020 if (!attrsilent) { 2021 (void) fprintf(stderr, 2022 gettext("%s: cannot open file" 2023 " %s: "), cmd, targ); 2024 perror(""); 2025 } 2026 ++error; 2027 goto out; 2028 } 2029 2030 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 2031 if (pflg && attrsilent) { 2032 error++; 2033 goto out; 2034 } 2035 if (!attrsilent) { 2036 (void) fprintf(stderr, 2037 gettext("%s: cannot open attribute" 2038 " directory for %s: "), cmd, src); 2039 perror(""); 2040 } 2041 ++error; 2042 goto out; 2043 } 2044 2045 if (fstat(sourcedirfd, &attrdir) == -1) { 2046 if (pflg && attrsilent) { 2047 error++; 2048 goto out; 2049 } 2050 2051 if (!attrsilent) { 2052 (void) fprintf(stderr, 2053 gettext("%s: could not retrieve stat" 2054 " information for attribute directory" 2055 "of file %s: "), cmd, src); 2056 perror(""); 2057 } 2058 ++error; 2059 goto out; 2060 } 2061 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 2062 if (pflg && attrsilent) { 2063 error++; 2064 goto out; 2065 } 2066 if (!attrsilent) { 2067 (void) fprintf(stderr, 2068 gettext("%s: cannot open attribute" 2069 " directory for %s: "), cmd, targ); 2070 perror(""); 2071 } 2072 ++error; 2073 } 2074 out: 2075 if (error) 2076 close_all(); 2077 return (error == 0 ? 0 : 1); 2078 } 2079 2080 int 2081 open_attrdirp(char *source) 2082 { 2083 int tmpfd = -1; 2084 int error = 0; 2085 2086 /* 2087 * dup sourcedirfd for use by fdopendir(). 2088 * fdopendir will take ownership of given fd and will close 2089 * it when closedir() is called. 2090 */ 2091 2092 if ((tmpfd = dup(sourcedirfd)) == -1) { 2093 if (pflg && attrsilent) { 2094 error++; 2095 goto out; 2096 } 2097 if (!attrsilent) { 2098 (void) fprintf(stderr, 2099 gettext( 2100 "%s: unable to dup attribute directory" 2101 " file descriptor for %s: "), cmd, source); 2102 perror(""); 2103 ++error; 2104 } 2105 goto out; 2106 } 2107 if ((srcdirp = fdopendir(tmpfd)) == NULL) { 2108 if (pflg && attrsilent) { 2109 error++; 2110 goto out; 2111 } 2112 if (!attrsilent) { 2113 (void) fprintf(stderr, 2114 gettext("%s: failed to open attribute" 2115 " directory for %s: "), cmd, source); 2116 perror(""); 2117 ++error; 2118 } 2119 } 2120 out: 2121 if (error) 2122 close_all(); 2123 return (error == 0 ? 0 : 1); 2124 } 2125 2126 /* Skips through ., .., and system attribute 'view' files */ 2127 int 2128 traverse_attrfile(struct dirent *dp, char *source, char *target, int first) 2129 { 2130 int error = 0; 2131 2132 srcattrfd = targattrfd = -1; 2133 2134 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 2135 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 2136 dp->d_name[2] == '\0') || 2137 (sysattr_type(dp->d_name) == _RO_SATTR) || 2138 (sysattr_type(dp->d_name) == _RW_SATTR)) 2139 return (-1); 2140 2141 if ((srcattrfd = openat(sourcedirfd, dp->d_name, 2142 O_RDONLY)) == -1) { 2143 if (!attrsilent) { 2144 (void) fprintf(stderr, 2145 gettext("%s: cannot open attribute %s on" 2146 " file %s: "), cmd, dp->d_name, source); 2147 perror(""); 2148 ++error; 2149 goto out; 2150 } 2151 } 2152 2153 if (fstat(srcattrfd, &s3) < 0) { 2154 if (!attrsilent) { 2155 (void) fprintf(stderr, 2156 gettext("%s: could not stat attribute" 2157 " %s on file" 2158 " %s: "), cmd, dp->d_name, source); 2159 perror(""); 2160 ++error; 2161 } 2162 goto out; 2163 } 2164 2165 if (first) { 2166 (void) unlinkat(targetdirfd, dp->d_name, 0); 2167 if ((targattrfd = openat(targetdirfd, dp->d_name, 2168 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) { 2169 if (!attrsilent) { 2170 (void) fprintf(stderr, 2171 gettext("%s: could not create attribute" 2172 " %s on file %s: "), cmd, dp->d_name, 2173 target); 2174 perror(""); 2175 ++error; 2176 } 2177 goto out; 2178 } 2179 } else { 2180 if ((targattrfd = openat(targetdirfd, dp->d_name, 2181 O_RDONLY)) == -1) { 2182 if (!attrsilent) { 2183 (void) fprintf(stderr, 2184 gettext("%s: could not open attribute" 2185 " %s on file %s: "), cmd, dp->d_name, 2186 target); 2187 perror(""); 2188 ++error; 2189 } 2190 goto out; 2191 } 2192 } 2193 2194 2195 if (fstat(targattrfd, &s4) < 0) { 2196 if (!attrsilent) { 2197 (void) fprintf(stderr, 2198 gettext("%s: could not stat attribute" 2199 " %s on file" 2200 " %s: "), cmd, dp->d_name, target); 2201 perror(""); 2202 ++error; 2203 } 2204 } 2205 2206 out: 2207 if (error) { 2208 if (srcattrfd != -1) 2209 (void) close(srcattrfd); 2210 if (targattrfd != -1) 2211 (void) close(targattrfd); 2212 srcattrfd = targattrfd = -1; 2213 } 2214 return (error == 0 ? 0 :1); 2215 } 2216 2217 void 2218 rewind_attrdir(DIR * sdp) 2219 { 2220 int pwdfd; 2221 2222 pwdfd = open(".", O_RDONLY); 2223 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) { 2224 rewinddir(sdp); 2225 (void) fchdir(pwdfd); 2226 (void) close(pwdfd); 2227 } else { 2228 if (!attrsilent) { 2229 (void) fprintf(stderr, gettext("%s: " 2230 "failed to rewind attribute dir\n"), 2231 cmd); 2232 } 2233 } 2234 } 2235 2236 void 2237 close_all() 2238 { 2239 if (srcattrfd != -1) 2240 (void) close(srcattrfd); 2241 if (targattrfd != -1) 2242 (void) close(targattrfd); 2243 if (sourcedirfd != -1) 2244 (void) close(sourcedirfd); 2245 if (targetdirfd != -1) 2246 (void) close(targetdirfd); 2247 if (srcdirp != NULL) { 2248 (void) closedir(srcdirp); 2249 srcdirp = NULL; 2250 } 2251 if (srcfd != -1) 2252 (void) close(srcfd); 2253 if (targfd != -1) 2254 (void) close(targfd); 2255 } 2256