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