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) 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 char *srcbuf, *targbuf; 1605 int error = 0; 1606 int aclerror; 1607 mode_t mode; 1608 int clearflg = 0; 1609 acl_t *xacl = NULL; 1610 acl_t *attrdiracl = NULL; 1611 struct timeval times[2]; 1612 1613 srcbuf = targbuf = NULL; 1614 1615 1616 if (pathconf(source, _PC_XATTR_EXISTS) != 1) 1617 return (0); 1618 1619 if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 1620 if (!attrsilent) { 1621 (void) fprintf(stderr, 1622 gettext( 1623 "%s: cannot preserve extended attributes, " 1624 "operation not supported on file" 1625 " %s\n"), cmd, target); 1626 } 1627 return (1); 1628 } 1629 if (open_source(source) != 0) 1630 return (1); 1631 if (open_target_srctarg_attrdirs(source, target) != 0) 1632 return (1); 1633 if (open_attrdirp(source) != 0) 1634 return (1); 1635 1636 if (pflg || mve) { 1637 if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 1638 if (!attrsilent) { 1639 (void) fprintf(stderr, 1640 gettext("%s: failed to set file mode" 1641 " correctly on attribute directory of" 1642 " file %s: "), cmd, target); 1643 perror(""); 1644 ++error; 1645 } 1646 } 1647 1648 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 1649 if (!attrsilent) { 1650 (void) fprintf(stderr, 1651 gettext("%s: failed to set file" 1652 " ownership correctly on attribute" 1653 " directory of file %s: "), cmd, target); 1654 perror(""); 1655 ++error; 1656 } 1657 } 1658 /* 1659 * Now that we are the owner we can update st_ctime by calling 1660 * futimesat. 1661 */ 1662 times[0].tv_sec = attrdir.st_atime; 1663 times[0].tv_usec = 0; 1664 times[1].tv_sec = attrdir.st_mtime; 1665 times[1].tv_usec = 0; 1666 if (futimesat(targetdirfd, ".", times) < 0) { 1667 if (!attrsilent) { 1668 (void) fprintf(stderr, 1669 gettext("%s: cannot set attribute times" 1670 " for %s: "), cmd, target); 1671 perror(""); 1672 ++error; 1673 } 1674 } 1675 1676 /* 1677 * Now set owner and group of attribute directory, implies 1678 * changing the ACL of the hidden attribute directory first. 1679 */ 1680 if ((aclerror = facl_get(sourcedirfd, 1681 ACL_NO_TRIVIAL, &attrdiracl)) != 0) { 1682 if (!attrsilent) { 1683 (void) fprintf(stderr, gettext( 1684 "%s: failed to get acl entries of" 1685 " attribute directory for" 1686 " %s : %s\n"), cmd, 1687 source, acl_strerror(aclerror)); 1688 ++error; 1689 } 1690 } 1691 1692 if (attrdiracl) { 1693 if (facl_set(targetdirfd, attrdiracl) != 0) { 1694 if (!attrsilent) { 1695 (void) fprintf(stderr, gettext( 1696 "%s: failed to set acl entries" 1697 " on attribute directory " 1698 "for %s\n"), cmd, target); 1699 ++error; 1700 } 1701 acl_free(attrdiracl); 1702 attrdiracl = NULL; 1703 } 1704 } 1705 } 1706 1707 while ((dp = readdir(srcdirp)) != NULL) { 1708 int ret; 1709 1710 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1) 1711 continue; 1712 else if (ret > 0) { 1713 ++error; 1714 goto out; 1715 } 1716 1717 if (pflg || mve) { 1718 if ((aclerror = facl_get(srcattrfd, 1719 ACL_NO_TRIVIAL, &xacl)) != 0) { 1720 if (!attrsilent) { 1721 (void) fprintf(stderr, gettext( 1722 "%s: failed to get acl entries of" 1723 " attribute %s for" 1724 " %s: %s"), cmd, dp->d_name, 1725 source, acl_strerror(aclerror)); 1726 ++error; 1727 } 1728 } 1729 } 1730 1731 /* 1732 * preserve ACL 1733 */ 1734 if ((pflg || mve) && xacl != NULL) { 1735 if ((facl_set(targattrfd, xacl)) < 0) { 1736 if (!attrsilent) { 1737 (void) fprintf(stderr, gettext( 1738 "%s: failed to set acl entries on" 1739 " attribute %s for" 1740 "%s\n"), cmd, dp->d_name, target); 1741 ++error; 1742 } 1743 acl_free(xacl); 1744 xacl = NULL; 1745 } 1746 } 1747 1748 if (writefile(srcattrfd, targattrfd, source, target, 1749 dp->d_name, dp->d_name, &s3, &s4) != 0) { 1750 if (!attrsilent) { 1751 ++error; 1752 } 1753 goto next; 1754 } 1755 1756 if (pflg || mve) { 1757 mode = FMODE(s3); 1758 1759 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 1760 if (!attrsilent) { 1761 (void) fprintf(stderr, 1762 gettext("%s: cannot change" 1763 " owner and group of" 1764 " attribute %s for" " file" 1765 " %s: "), cmd, dp->d_name, target); 1766 perror(""); 1767 ++error; 1768 } 1769 if (mode & (S_ISUID | S_ISGID)) { 1770 /* try to clear S_ISUID and S_ISGID */ 1771 mode &= ~S_ISUID & ~S_ISGID; 1772 ++clearflg; 1773 } 1774 } 1775 /* tv_usec were cleared above */ 1776 times[0].tv_sec = s3.st_atime; 1777 times[1].tv_sec = s3.st_mtime; 1778 if (futimesat(targetdirfd, dp->d_name, times) < 0) { 1779 if (!attrsilent) { 1780 (void) fprintf(stderr, 1781 gettext("%s: cannot set attribute" 1782 " times for %s: "), cmd, target); 1783 perror(""); 1784 ++error; 1785 } 1786 } 1787 if (fchmod(targattrfd, mode) != 0) { 1788 if (clearflg) { 1789 (void) fprintf(stderr, gettext( 1790 "%s: cannot clear S_ISUID and " 1791 "S_ISGID bits in attribute %s" 1792 " for file" 1793 " %s: "), cmd, dp->d_name, target); 1794 } else { 1795 if (!attrsilent) { 1796 (void) fprintf(stderr, 1797 gettext( 1798 "%s: cannot set permissions of attribute" 1799 " %s for %s: "), cmd, dp->d_name, target); 1800 perror(""); 1801 ++error; 1802 } 1803 } 1804 } 1805 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) { 1806 if (!attrsilent) { 1807 (void) fprintf(stderr, gettext( 1808 "%s: failed to set acl entries on" 1809 " attribute %s for" 1810 "%s\n"), cmd, dp->d_name, target); 1811 ++error; 1812 } 1813 acl_free(xacl); 1814 xacl = NULL; 1815 } 1816 } 1817 next: 1818 if (xacl != NULL) { 1819 acl_free(xacl); 1820 xacl = NULL; 1821 } 1822 if (srcbuf != NULL) 1823 free(srcbuf); 1824 if (targbuf != NULL) 1825 free(targbuf); 1826 if (srcattrfd != -1) 1827 (void) close(srcattrfd); 1828 if (targattrfd != -1) 1829 (void) close(targattrfd); 1830 srcattrfd = targattrfd = -1; 1831 srcbuf = targbuf = NULL; 1832 } 1833 out: 1834 if (xacl != NULL) { 1835 acl_free(xacl); 1836 xacl = NULL; 1837 } 1838 if (attrdiracl != NULL) { 1839 acl_free(attrdiracl); 1840 attrdiracl = NULL; 1841 } 1842 if (srcbuf) 1843 free(srcbuf); 1844 if (targbuf) 1845 free(targbuf); 1846 1847 if (!saflg && !pflg && !mve) 1848 close_all(); 1849 return (error == 0 ? 0 : 1); 1850 } 1851 1852 /* Copy extended system attributes from source to target */ 1853 1854 static int 1855 copy_sysattr(char *source, char *target) 1856 { 1857 struct dirent *dp; 1858 nvlist_t *response; 1859 int error = 0; 1860 int chk_support = 0; 1861 int sa_support = 1; 1862 1863 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1) 1864 return (0); 1865 1866 if (open_source(source) != 0) 1867 return (1); 1868 1869 /* 1870 * Gets non default extended system attributes from the 1871 * source file to copy to the target. The target has 1872 * the defaults set when its created and thus no need 1873 * to copy the defaults. 1874 */ 1875 response = sysattr_list(cmd, srcfd, source); 1876 1877 if (response != NULL && 1878 sysattr_support(target, _PC_SATTR_ENABLED) != 1) { 1879 (void) fprintf(stderr, 1880 gettext( 1881 "%s: cannot preserve extended system " 1882 "attribute, operation not supported on file" 1883 " %s\n"), cmd, target); 1884 error++; 1885 goto out; 1886 } 1887 1888 if (srcdirp == NULL) { 1889 if (open_target_srctarg_attrdirs(source, target) != 0) { 1890 error++; 1891 goto out; 1892 } 1893 if (open_attrdirp(source) != 0) { 1894 error++; 1895 goto out; 1896 } 1897 } else { 1898 rewind_attrdir(srcdirp); 1899 } 1900 while (sa_support && (dp = readdir(srcdirp)) != NULL) { 1901 nvlist_t *res; 1902 int ret; 1903 1904 if ((ret = traverse_attrfile(dp, source, target, 0)) == -1) 1905 continue; 1906 else if (ret > 0) { 1907 ++error; 1908 goto out; 1909 } 1910 /* 1911 * Gets non default extended system attributes from the 1912 * attribute file to copy to the target. The target has 1913 * the defaults set when its created and thus no need 1914 * to copy the defaults. 1915 */ 1916 if (dp->d_name != NULL) { 1917 res = sysattr_list(cmd, srcattrfd, dp->d_name); 1918 if (res == NULL) 1919 goto next; 1920 1921 if (!chk_support && 1922 sysattr_support(target, _PC_SATTR_ENABLED) != 1) { 1923 (void) fprintf(stderr, 1924 gettext( 1925 "%s: cannot preserve extended " 1926 "system attribute, operation not " 1927 " %s\n"), cmd, target); 1928 error++; 1929 sa_support = 0; 1930 } else { 1931 chk_support = 1; 1932 } 1933 1934 /* 1935 * Copy non default extended system attributes of named 1936 * attribute file. 1937 */ 1938 if (sa_support && fsetattr(targattrfd, 1939 XATTR_VIEW_READWRITE, res) != 0) { 1940 ++error; 1941 (void) fprintf(stderr, gettext("%s: " 1942 "Failed to copy extended system " 1943 "attributes from attribute file " 1944 "%s of %s to %s\n"), cmd, 1945 dp->d_name, source, target); 1946 } 1947 } 1948 next: 1949 if (srcattrfd != -1) 1950 (void) close(srcattrfd); 1951 if (targattrfd != -1) 1952 (void) close(targattrfd); 1953 srcattrfd = targattrfd = -1; 1954 if (res != NULL) 1955 nvlist_free(res); 1956 } 1957 1958 /* Copy source file non default extended system attributes to target */ 1959 if ((response != NULL) && 1960 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) { 1961 ++error; 1962 (void) fprintf(stderr, gettext("%s: Failed to " 1963 "copy extended system attributes from " 1964 "%s to %s\n"), cmd, source, target); 1965 } 1966 out: 1967 if (response != NULL) 1968 nvlist_free(response); 1969 close_all(); 1970 return (error == 0 ? 0 : 1); 1971 } 1972 1973 /* 1974 * nanoseconds are rounded off to microseconds by flooring. 1975 */ 1976 static void 1977 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 1978 { 1979 tv->tv_sec = ts->tv_sec; 1980 tv->tv_usec = ts->tv_nsec / 1000; 1981 } 1982 1983 /* Open the source file */ 1984 1985 int 1986 open_source(char *src) 1987 { 1988 int error = 0; 1989 1990 srcfd = -1; 1991 if ((srcfd = open(src, O_RDONLY)) == -1) { 1992 if (pflg && attrsilent) { 1993 error++; 1994 goto out; 1995 } 1996 if (!attrsilent) { 1997 (void) fprintf(stderr, 1998 gettext("%s: cannot open file" 1999 " %s: "), cmd, src); 2000 perror(""); 2001 } 2002 ++error; 2003 } 2004 out: 2005 if (error) 2006 close_all(); 2007 return (error == 0 ? 0 : 1); 2008 } 2009 2010 /* Open source attribute dir, target and target attribute dir. */ 2011 2012 int 2013 open_target_srctarg_attrdirs(char *src, char *targ) 2014 { 2015 int error = 0; 2016 2017 targfd = sourcedirfd = targetdirfd = -1; 2018 2019 if ((targfd = open(targ, O_RDONLY)) == -1) { 2020 if (pflg && attrsilent) { 2021 error++; 2022 goto out; 2023 } 2024 if (!attrsilent) { 2025 (void) fprintf(stderr, 2026 gettext("%s: cannot open file" 2027 " %s: "), cmd, targ); 2028 perror(""); 2029 } 2030 ++error; 2031 goto out; 2032 } 2033 2034 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 2035 if (pflg && attrsilent) { 2036 error++; 2037 goto out; 2038 } 2039 if (!attrsilent) { 2040 (void) fprintf(stderr, 2041 gettext("%s: cannot open attribute" 2042 " directory for %s: "), cmd, src); 2043 perror(""); 2044 } 2045 ++error; 2046 goto out; 2047 } 2048 2049 if (fstat(sourcedirfd, &attrdir) == -1) { 2050 if (pflg && attrsilent) { 2051 error++; 2052 goto out; 2053 } 2054 2055 if (!attrsilent) { 2056 (void) fprintf(stderr, 2057 gettext("%s: could not retrieve stat" 2058 " information for attribute directory" 2059 "of file %s: "), cmd, src); 2060 perror(""); 2061 } 2062 ++error; 2063 goto out; 2064 } 2065 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 2066 if (pflg && attrsilent) { 2067 error++; 2068 goto out; 2069 } 2070 if (!attrsilent) { 2071 (void) fprintf(stderr, 2072 gettext("%s: cannot open attribute" 2073 " directory for %s: "), cmd, targ); 2074 perror(""); 2075 } 2076 ++error; 2077 } 2078 out: 2079 if (error) 2080 close_all(); 2081 return (error == 0 ? 0 : 1); 2082 } 2083 2084 int 2085 open_attrdirp(char *source) 2086 { 2087 int tmpfd = -1; 2088 int error = 0; 2089 2090 /* 2091 * dup sourcedirfd for use by fdopendir(). 2092 * fdopendir will take ownership of given fd and will close 2093 * it when closedir() is called. 2094 */ 2095 2096 if ((tmpfd = dup(sourcedirfd)) == -1) { 2097 if (pflg && attrsilent) { 2098 error++; 2099 goto out; 2100 } 2101 if (!attrsilent) { 2102 (void) fprintf(stderr, 2103 gettext( 2104 "%s: unable to dup attribute directory" 2105 " file descriptor for %s: "), cmd, source); 2106 perror(""); 2107 ++error; 2108 } 2109 goto out; 2110 } 2111 if ((srcdirp = fdopendir(tmpfd)) == NULL) { 2112 if (pflg && attrsilent) { 2113 error++; 2114 goto out; 2115 } 2116 if (!attrsilent) { 2117 (void) fprintf(stderr, 2118 gettext("%s: failed to open attribute" 2119 " directory for %s: "), cmd, source); 2120 perror(""); 2121 ++error; 2122 } 2123 } 2124 out: 2125 if (error) 2126 close_all(); 2127 return (error == 0 ? 0 : 1); 2128 } 2129 2130 /* Skips through ., .., and system attribute 'view' files */ 2131 int 2132 traverse_attrfile(struct dirent *dp, char *source, char *target, int first) 2133 { 2134 int error = 0; 2135 2136 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 2137 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 2138 dp->d_name[2] == '\0') || 2139 (sysattr_type(dp->d_name) == _RO_SATTR) || 2140 (sysattr_type(dp->d_name) == _RW_SATTR)) 2141 return (-1); 2142 2143 if ((srcattrfd = openat(sourcedirfd, dp->d_name, 2144 O_RDONLY)) == -1) { 2145 if (!attrsilent) { 2146 (void) fprintf(stderr, 2147 gettext("%s: cannot open attribute %s on" 2148 " file %s: "), cmd, dp->d_name, source); 2149 perror(""); 2150 ++error; 2151 goto out; 2152 } 2153 } 2154 2155 if (fstat(srcattrfd, &s3) < 0) { 2156 if (!attrsilent) { 2157 (void) fprintf(stderr, 2158 gettext("%s: could not stat attribute" 2159 " %s on file" 2160 " %s: "), cmd, dp->d_name, source); 2161 perror(""); 2162 ++error; 2163 } 2164 goto out; 2165 } 2166 2167 if (first) { 2168 (void) unlinkat(targetdirfd, dp->d_name, 0); 2169 targattrfd = openat(targetdirfd, dp->d_name, 2170 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS); 2171 } else { 2172 targattrfd = openat(targetdirfd, dp->d_name, O_RDWR); 2173 } 2174 2175 if (targattrfd == -1) { 2176 if (!attrsilent) { 2177 (void) fprintf(stderr, 2178 gettext("%s: could not create attribute" 2179 " %s on file %s: "), cmd, dp->d_name, 2180 target); 2181 perror(""); 2182 ++error; 2183 } 2184 goto out; 2185 } 2186 2187 if (fstat(targattrfd, &s4) < 0) { 2188 if (!attrsilent) { 2189 (void) fprintf(stderr, 2190 gettext("%s: could not stat attribute" 2191 " %s on file" 2192 " %s: "), cmd, dp->d_name, target); 2193 perror(""); 2194 ++error; 2195 } 2196 } 2197 2198 out: 2199 if (error) { 2200 if (srcattrfd != -1) 2201 (void) close(srcattrfd); 2202 if (targattrfd != -1) 2203 (void) close(targattrfd); 2204 } 2205 return (error == 0 ? 0 :1); 2206 } 2207 2208 void 2209 rewind_attrdir(DIR * sdp) 2210 { 2211 int pwdfd; 2212 2213 pwdfd = open(".", O_RDONLY); 2214 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) { 2215 rewinddir(sdp); 2216 (void) fchdir(pwdfd); 2217 (void) close(pwdfd); 2218 } else { 2219 if (!attrsilent) { 2220 (void) fprintf(stderr, gettext("%s: " 2221 "failed to rewind attribute dir\n"), 2222 cmd); 2223 } 2224 } 2225 } 2226 2227 void 2228 close_all() 2229 { 2230 if (srcattrfd != -1) 2231 (void) close(srcattrfd); 2232 if (targattrfd != -1) 2233 (void) close(targattrfd); 2234 if (sourcedirfd != -1) 2235 (void) close(sourcedirfd); 2236 if (targetdirfd != -1) 2237 (void) close(targetdirfd); 2238 if (srcdirp != NULL) { 2239 (void) closedir(srcdirp); 2240 srcdirp = NULL; 2241 } 2242 if (srcfd != -1) 2243 (void) close(srcfd); 2244 if (targfd != -1) 2245 (void) close(targfd); 2246 } 2247