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