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