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