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