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