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