1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 /* 42 * Combined mv/cp/ln command: 43 * mv file1 file2 44 * mv dir1 dir2 45 * mv file1 ... filen dir1 46 */ 47 #include <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 if (getresp()) { 1237 if (buf != NULL) 1238 free(buf); 1239 return (2); 1240 } 1241 } else if (overwrite && ISREG(s2)) { 1242 (void) fprintf(stderr, 1243 gettext("%s: overwrite %s (%s/%s)? "), 1244 cmd, target, yeschr, nochr); 1245 if (getresp()) { 1246 if (buf != NULL) 1247 free(buf); 1248 return (2); 1249 } 1250 } else if (override) { 1251 (void) fprintf(stderr, 1252 gettext("%s: %s: override protection " 1253 /*CSTYLED*/ 1254 "%o (%s/%s)? "), 1255 /*CSTYLED*/ 1256 cmd, target, FMODE(s2) & MODEBITS, 1257 yeschr, nochr); 1258 if (getresp()) { 1259 if (buf != NULL) 1260 free(buf); 1261 return (2); 1262 } 1263 } 1264 1265 if (lnk && unlink(target) < 0) { 1266 (void) fprintf(stderr, 1267 gettext("%s: cannot unlink %s: "), 1268 cmd, target); 1269 perror(""); 1270 return (1); 1271 } 1272 } 1273 } 1274 return (0); 1275 } 1276 1277 /* 1278 * check whether source and target are different 1279 * return 1 when they are different 1280 * return 0 when they are identical, or when unable to resolve a pathname 1281 */ 1282 static int 1283 chk_different(char *source, char *target) 1284 { 1285 char rtarget[PATH_MAX], rsource[PATH_MAX]; 1286 1287 if (IDENTICAL(s1, s2)) { 1288 /* 1289 * IDENTICAL will be true for hard links, therefore 1290 * check whether the filenames are different 1291 */ 1292 if ((getrealpath(source, rsource) == 0) || 1293 (getrealpath(target, rtarget) == 0)) { 1294 return (0); 1295 } 1296 if (strncmp(rsource, rtarget, PATH_MAX) == 0) { 1297 (void) fprintf(stderr, gettext( 1298 "%s: %s and %s are identical\n"), 1299 cmd, source, target); 1300 return (0); 1301 } 1302 } 1303 return (1); 1304 } 1305 1306 /* 1307 * get real path (resolved absolute pathname) 1308 * return 1 on success, 0 on failure 1309 */ 1310 static int 1311 getrealpath(char *path, char *rpath) 1312 { 1313 if (realpath(path, rpath) == NULL) { 1314 int errno_save = errno; 1315 (void) fprintf(stderr, gettext( 1316 "%s: can't resolve path %s: "), cmd, path); 1317 errno = errno_save; 1318 perror(""); 1319 return (0); 1320 } 1321 return (1); 1322 } 1323 1324 static int 1325 rcopy(char *from, char *to) 1326 { 1327 DIR *fold = opendir(from); 1328 struct dirent *dp; 1329 struct stat statb, s1save; 1330 int errs = 0; 1331 char fromname[PATH_MAX]; 1332 1333 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) { 1334 Perror(from); 1335 return (1); 1336 } 1337 if (pflg || mve) { 1338 /* 1339 * Save s1 (stat information for source dir) so that 1340 * mod and access times can be reserved during "cp -p" 1341 * or mv, since s1 gets overwritten. 1342 */ 1343 s1save = s1; 1344 } 1345 for (;;) { 1346 dp = readdir(fold); 1347 if (dp == 0) { 1348 (void) closedir(fold); 1349 if (pflg || mve) 1350 return (chg_time(to, s1save) + errs); 1351 return (errs); 1352 } 1353 if (dp->d_ino == 0) 1354 continue; 1355 if ((strcmp(dp->d_name, ".") == 0) || 1356 (strcmp(dp->d_name, "..") == 0)) 1357 continue; 1358 if (strlen(from)+1+strlen(dp->d_name) >= 1359 sizeof (fromname) - 1) { 1360 (void) fprintf(stderr, 1361 gettext("%s : %s/%s: Name too long\n"), 1362 cmd, from, dp->d_name); 1363 errs++; 1364 continue; 1365 } 1366 (void) snprintf(fromname, sizeof (fromname), 1367 "%s/%s", from, dp->d_name); 1368 errs += cpymve(fromname, to); 1369 } 1370 } 1371 1372 static char * 1373 dname(char *name) 1374 { 1375 register char *p; 1376 1377 /* 1378 * Return just the file name given the complete path. 1379 * Like basename(1). 1380 */ 1381 1382 p = name; 1383 1384 /* 1385 * While there are characters left, 1386 * set name to start after last 1387 * delimiter. 1388 */ 1389 1390 while (*p) 1391 if (*p++ == DELIM && *p) 1392 name = p; 1393 return (name); 1394 } 1395 1396 static int 1397 getresp(void) 1398 { 1399 register int c, i; 1400 char ans_buf[SCHAR_MAX + 1]; 1401 1402 /* 1403 * Get response from user. Based on 1404 * first character, make decision. 1405 * Discard rest of line. 1406 */ 1407 for (i = 0; ; i++) { 1408 c = getchar(); 1409 if (c == '\n' || c == 0 || c == EOF) { 1410 ans_buf[i] = 0; 1411 break; 1412 } 1413 if (i < SCHAR_MAX) 1414 ans_buf[i] = c; 1415 } 1416 if (i >= SCHAR_MAX) { 1417 i = SCHAR_MAX; 1418 ans_buf[SCHAR_MAX] = 0; 1419 } 1420 if ((i == 0) | (strncmp(yeschr, ans_buf, i))) 1421 return (1); 1422 return (0); 1423 } 1424 1425 static void 1426 usage(void) 1427 { 1428 /* 1429 * Display usage message. 1430 */ 1431 1432 if (mve) { 1433 (void) fprintf(stderr, gettext( 1434 "Usage: mv [-f] [-i] f1 f2\n" 1435 " mv [-f] [-i] f1 ... fn d1\n" 1436 " mv [-f] [-i] d1 d2\n")); 1437 } else if (lnk) { 1438 (void) fprintf(stderr, gettext( 1439 #ifdef XPG4 1440 "Usage: ln [-f] [-s] f1 [f2]\n" 1441 " ln [-f] [-s] f1 ... fn d1\n" 1442 " ln [-f] -s d1 d2\n")); 1443 #else 1444 "Usage: ln [-f] [-n] [-s] f1 [f2]\n" 1445 " ln [-f] [-n] [-s] f1 ... fn d1\n" 1446 " ln [-f] [-n] -s d1 d2\n")); 1447 #endif 1448 } else if (cpy) { 1449 (void) fprintf(stderr, gettext( 1450 "Usage: cp [-f] [-i] [-p] [-@] f1 f2\n" 1451 " cp [-f] [-i] [-p] [-@] f1 ... fn d1\n" 1452 " cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] " 1453 "d1 ... dn-1 dn\n")); 1454 } 1455 exit(2); 1456 } 1457 1458 /* 1459 * chg_time() 1460 * 1461 * Try to preserve modification and access time. 1462 * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version, 1463 * don't report a utime() failure. 1464 * If this is the XPG4 version and utime fails, if 1) pflg is set (cp -p) 1465 * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero 1466 * exit status only if pflg is set. 1467 * utimes(2) is being used to achieve granularity in 1468 * microseconds while setting file times. 1469 */ 1470 static int 1471 chg_time(char *to, struct stat ss) 1472 { 1473 struct timeval times[2]; 1474 int rc; 1475 1476 timestruc_to_timeval(&ss.st_atim, times); 1477 timestruc_to_timeval(&ss.st_mtim, times + 1); 1478 1479 rc = utimes(to, times); 1480 #ifdef XPG4 1481 if ((pflg || mve) && rc != 0) { 1482 (void) fprintf(stderr, 1483 gettext("%s: cannot set times for %s: "), cmd, to); 1484 perror(""); 1485 if (pflg) 1486 return (1); 1487 } 1488 #endif 1489 1490 return (0); 1491 1492 } 1493 1494 /* 1495 * chg_mode() 1496 * 1497 * This function is called upon "cp -p" or mv across filesystems. 1498 * 1499 * Try to preserve the owner and group id. If chown() fails, 1500 * only print a diagnostic message if doing a mv in the XPG4 version; 1501 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear 1502 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a 1503 * non-zero exit status because this is a security violation. 1504 * Try to preserve permissions. 1505 * If this is the XPG4 version and chmod() fails, print a diagnostic message 1506 * and arrange for a non-zero exit status. 1507 * If this is the Solaris version and chmod() fails, do not print a 1508 * diagnostic message or exit with a non-zero value. 1509 */ 1510 static int 1511 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode) 1512 { 1513 int clearflg = 0; /* controls message printed upon chown() error */ 1514 1515 if (chown(target, uid, gid) != 0) { 1516 #ifdef XPG4 1517 if (mve) { 1518 (void) fprintf(stderr, gettext("%s: cannot change" 1519 " owner and group of %s: "), cmd, target); 1520 perror(""); 1521 } 1522 #endif 1523 if (mode & (S_ISUID | S_ISGID)) { 1524 /* try to clear S_ISUID and S_ISGID */ 1525 mode &= ~S_ISUID & ~S_ISGID; 1526 ++clearflg; 1527 } 1528 } 1529 if (chmod(target, mode) != 0) { 1530 if (clearflg) { 1531 (void) fprintf(stderr, gettext( 1532 "%s: cannot clear S_ISUID and S_ISGID bits in" 1533 " %s: "), cmd, target); 1534 perror(""); 1535 /* cp -p should get non-zero exit; mv should not */ 1536 if (pflg) 1537 return (1); 1538 } 1539 #ifdef XPG4 1540 else { 1541 (void) fprintf(stderr, gettext( 1542 "%s: cannot set permissions for %s: "), cmd, target); 1543 perror(""); 1544 /* cp -p should get non-zero exit; mv should not */ 1545 if (pflg) 1546 return (1); 1547 } 1548 #endif 1549 } 1550 return (0); 1551 1552 } 1553 1554 static void 1555 Perror(char *s) 1556 { 1557 char buf[PATH_MAX + 10]; 1558 1559 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s); 1560 perror(buf); 1561 } 1562 1563 static void 1564 Perror2(char *s1, char *s2) 1565 { 1566 char buf[PATH_MAX + 20]; 1567 1568 (void) snprintf(buf, sizeof (buf), "%s: %s: %s", 1569 cmd, gettext(s1), gettext(s2)); 1570 perror(buf); 1571 } 1572 1573 /* 1574 * used for cp -R and for mv across file systems 1575 */ 1576 static int 1577 copydir(char *source, char *target) 1578 { 1579 int ret, attret = 0; 1580 int pret = 0; /* need separate flag if -p is specified */ 1581 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */ 1582 struct stat s1save; 1583 acl_t *s1acl_save; 1584 1585 s1acl_save = NULL; 1586 1587 if (cpy && !rflg) { 1588 (void) fprintf(stderr, 1589 gettext("%s: %s: is a directory\n"), cmd, source); 1590 return (1); 1591 } 1592 1593 if (stat(target, &s2) < 0) { 1594 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) { 1595 (void) fprintf(stderr, "%s: ", cmd); 1596 perror(target); 1597 return (1); 1598 } 1599 if (stat(target, &s2) == 0) { 1600 fixmode = s2.st_mode; 1601 } else { 1602 fixmode = s1.st_mode; 1603 } 1604 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU)); 1605 } else if (!(ISDIR(s2))) { 1606 (void) fprintf(stderr, 1607 gettext("%s: %s: not a directory.\n"), cmd, target); 1608 return (1); 1609 } 1610 if (pflg || mve) { 1611 /* 1612 * Save s1 (stat information for source dir) and acl info, 1613 * if any, so that ownership, modes, times, and acl's can 1614 * be reserved during "cp -p" or mv. 1615 * s1 gets overwritten when doing the recursive copy. 1616 */ 1617 s1save = s1; 1618 if (s1acl != NULL) { 1619 s1acl_save = acl_dup(s1acl); 1620 #ifdef XPG4 1621 if (s1acl_save == NULL) { 1622 (void) fprintf(stderr, gettext("%s: " 1623 "Insufficient memory to save acl" 1624 " entry\n"), cmd); 1625 if (pflg) 1626 return (1); 1627 1628 } 1629 #endif 1630 } 1631 } 1632 1633 ret = rcopy(source, target); 1634 1635 /* 1636 * Once we created a directory, go ahead and set 1637 * its attributes, e.g. acls and time. The info 1638 * may get overwritten if we continue traversing 1639 * down the tree. 1640 * 1641 * ACL for directory 1642 */ 1643 if (pflg || mve) { 1644 if ((pret = chg_mode(target, UID(s1save), GID(s1save), 1645 FMODE(s1save))) == 0) 1646 pret = chg_time(target, s1save); 1647 ret += pret; 1648 if (s1acl_save != NULL) { 1649 if (acl_set(target, s1acl_save) < 0) { 1650 #ifdef XPG4 1651 if (pflg || mve) { 1652 #else 1653 if (pflg) { 1654 #endif 1655 (void) fprintf(stderr, gettext( 1656 "%s: failed to set acl entries " 1657 "on %s\n"), cmd, target); 1658 if (pflg) { 1659 acl_free(s1acl_save); 1660 s1acl_save = NULL; 1661 ret++; 1662 } 1663 } 1664 /* else: silent and continue */ 1665 } 1666 acl_free(s1acl_save); 1667 s1acl_save = NULL; 1668 } 1669 } else if (fixmode != (mode_t)0) 1670 (void) chmod(target, fixmode & MODEBITS); 1671 1672 if (pflg || atflg || mve) { 1673 attret = copyattributes(source, target); 1674 if (!attrsilent && attret != 0) { 1675 (void) fprintf(stderr, gettext("%s: Failed to preserve" 1676 " extended attributes of directory" 1677 " %s\n"), cmd, source); 1678 } else { 1679 /* 1680 * Otherwise ignore failure. 1681 */ 1682 attret = 0; 1683 } 1684 } 1685 if (attret != 0) 1686 return (attret); 1687 return (ret); 1688 } 1689 1690 static int 1691 copyspecial(char *target) 1692 { 1693 int ret = 0; 1694 1695 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) { 1696 (void) fprintf(stderr, gettext( 1697 "cp: cannot create special file %s: "), target); 1698 perror(""); 1699 return (1); 1700 } 1701 1702 if (pflg) { 1703 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0) 1704 ret = chg_time(target, s1); 1705 } 1706 1707 return (ret); 1708 } 1709 1710 static int 1711 use_stdin(void) 1712 { 1713 #ifdef XPG4 1714 return (1); 1715 #else 1716 return (isatty(fileno(stdin))); 1717 #endif 1718 } 1719 1720 static int 1721 copyattributes(char *source, char *target) 1722 { 1723 int sourcedirfd, targetdirfd; 1724 int srcfd, targfd; 1725 int tmpfd; 1726 DIR *srcdirp; 1727 int srcattrfd, targattrfd; 1728 struct dirent *dp; 1729 char *attrstr; 1730 char *srcbuf, *targbuf; 1731 size_t src_size, targ_size; 1732 int error = 0; 1733 int aclerror; 1734 mode_t mode; 1735 int clearflg = 0; 1736 acl_t *xacl = NULL; 1737 acl_t *attrdiracl = NULL; 1738 struct stat attrdir, s3, s4; 1739 struct timeval times[2]; 1740 mode_t targmode; 1741 1742 srcdirp = NULL; 1743 srcfd = targfd = tmpfd = -1; 1744 sourcedirfd = targetdirfd = srcattrfd = targattrfd = -1; 1745 srcbuf = targbuf = NULL; 1746 1747 if (pathconf(source, _PC_XATTR_EXISTS) != 1) 1748 return (0); 1749 1750 if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 1751 if (!attrsilent) { 1752 (void) fprintf(stderr, 1753 gettext( 1754 "%s: cannot preserve extended attributes, " 1755 "operation not supported on file" 1756 " %s\n"), cmd, target); 1757 } 1758 return (1); 1759 } 1760 1761 1762 if ((srcfd = open(source, O_RDONLY)) == -1) { 1763 if (pflg && attrsilent) { 1764 error = 0; 1765 goto out; 1766 } 1767 if (!attrsilent) { 1768 (void) fprintf(stderr, 1769 gettext("%s: cannot open file" 1770 " %s: "), cmd, source); 1771 perror(""); 1772 } 1773 ++error; 1774 goto out; 1775 } 1776 if ((targfd = open(target, O_RDONLY)) == -1) { 1777 1778 if (pflg && attrsilent) { 1779 error = 0; 1780 goto out; 1781 } 1782 if (!attrsilent) { 1783 (void) fprintf(stderr, 1784 gettext("%s: cannot open file" 1785 " %s: "), cmd, source); 1786 perror(""); 1787 } 1788 ++error; 1789 goto out; 1790 } 1791 1792 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 1793 if (pflg && attrsilent) { 1794 error = 0; 1795 goto out; 1796 } 1797 if (!attrsilent) { 1798 (void) fprintf(stderr, 1799 gettext("%s: cannot open attribute" 1800 " directory for %s: "), cmd, source); 1801 perror(""); 1802 ++error; 1803 } 1804 goto out; 1805 } 1806 1807 if (fstat(sourcedirfd, &attrdir) == -1) { 1808 if (pflg && attrsilent) { 1809 error = 0; 1810 goto out; 1811 } 1812 1813 if (!attrsilent) { 1814 (void) fprintf(stderr, 1815 gettext("%s: could not retrieve stat" 1816 " information for attribute directory" 1817 "of file %s: "), cmd, source); 1818 perror(""); 1819 ++error; 1820 } 1821 goto out; 1822 } 1823 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 1824 /* 1825 * We couldn't create the attribute directory 1826 * 1827 * Lets see if we can add write support to the mode 1828 * and create the directory and then put the mode back 1829 * to way it should be. 1830 */ 1831 1832 targmode = FMODE(s1) | S_IWUSR; 1833 if (fchmod(targfd, targmode) == 0) { 1834 targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR); 1835 /* 1836 * Put mode back to what it was 1837 */ 1838 targmode = FMODE(s1) & MODEBITS; 1839 if (fchmod(targfd, targmode) == -1) { 1840 if (pflg && attrsilent) { 1841 error = 0; 1842 goto out; 1843 } 1844 if (!attrsilent) { 1845 (void) fprintf(stderr, 1846 gettext("%s: failed to set" 1847 " mode correctly on file" 1848 " %s: "), cmd, target); 1849 perror(""); 1850 ++error; 1851 goto out; 1852 } 1853 } 1854 } else { 1855 if (pflg && attrsilent) { 1856 error = 0; 1857 goto out; 1858 } 1859 if (!attrsilent) { 1860 (void) fprintf(stderr, 1861 gettext("%s: cannot open attribute" 1862 " directory for %s: "), cmd, target); 1863 perror(""); 1864 ++error; 1865 } 1866 goto out; 1867 } 1868 } 1869 1870 if (targetdirfd == -1) { 1871 if (pflg && attrsilent) { 1872 error = 0; 1873 goto out; 1874 } 1875 if (!attrsilent) { 1876 (void) fprintf(stderr, 1877 gettext("%s: cannot open attribute directory" 1878 " for %s: "), cmd, target); 1879 perror(""); 1880 ++error; 1881 } 1882 goto out; 1883 } 1884 1885 /* 1886 * Set mode of attribute directory same as on source, 1887 * if pflg set or this is a move. 1888 */ 1889 1890 if (pflg || mve) { 1891 if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 1892 if (!attrsilent) { 1893 (void) fprintf(stderr, 1894 gettext("%s: failed to set file mode" 1895 " correctly on attribute directory of" 1896 " file %s: "), cmd, target); 1897 perror(""); 1898 ++error; 1899 } 1900 } 1901 1902 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 1903 if (!attrsilent) { 1904 (void) fprintf(stderr, 1905 gettext("%s: failed to set file" 1906 " ownership correctly on attribute" 1907 " directory of file %s: "), cmd, target); 1908 perror(""); 1909 ++error; 1910 } 1911 } 1912 /* 1913 * Now that we are the owner we can update st_ctime by calling 1914 * futimesat. 1915 */ 1916 times[0].tv_sec = attrdir.st_atime; 1917 times[0].tv_usec = 0; 1918 times[1].tv_sec = attrdir.st_mtime; 1919 times[1].tv_usec = 0; 1920 if (futimesat(targetdirfd, ".", times) < 0) { 1921 if (!attrsilent) { 1922 (void) fprintf(stderr, 1923 gettext("%s: cannot set attribute times" 1924 " for %s: "), cmd, target); 1925 perror(""); 1926 ++error; 1927 } 1928 } 1929 1930 /* 1931 * Now set owner and group of attribute directory, implies 1932 * changing the ACL of the hidden attribute directory first. 1933 */ 1934 if ((aclerror = facl_get(sourcedirfd, 1935 ACL_NO_TRIVIAL, &attrdiracl)) != 0) { 1936 if (!attrsilent) { 1937 (void) fprintf(stderr, gettext( 1938 "%s: failed to get acl entries of" 1939 " attribute directory for" 1940 " %s : %s\n"), cmd, 1941 source, acl_strerror(aclerror)); 1942 ++error; 1943 } 1944 } 1945 1946 if (attrdiracl) { 1947 if (facl_set(targetdirfd, attrdiracl) != 0) { 1948 if (!attrsilent) { 1949 (void) fprintf(stderr, gettext( 1950 "%s: failed to set acl entries" 1951 " on attribute directory " 1952 "for %s\n"), cmd, target); 1953 ++error; 1954 } 1955 acl_free(attrdiracl); 1956 attrdiracl = NULL; 1957 } 1958 } 1959 } 1960 1961 /* 1962 * dup sourcedirfd for use by fdopendir(). 1963 * fdopendir will take ownership of given fd and will close 1964 * it when closedir() is called. 1965 */ 1966 1967 if ((tmpfd = dup(sourcedirfd)) == -1) { 1968 if (pflg && attrsilent) { 1969 error = 0; 1970 goto out; 1971 } 1972 if (!attrsilent) { 1973 (void) fprintf(stderr, 1974 gettext( 1975 "%s: unable to dup attribute directory" 1976 " file descriptor for %s: "), cmd, source); 1977 perror(""); 1978 ++error; 1979 } 1980 goto out; 1981 } 1982 if ((srcdirp = fdopendir(tmpfd)) == NULL) { 1983 if (pflg && attrsilent) { 1984 error = 0; 1985 goto out; 1986 } 1987 if (!attrsilent) { 1988 (void) fprintf(stderr, 1989 gettext("%s: failed to open attribute" 1990 " directory for %s: "), cmd, source); 1991 perror(""); 1992 ++error; 1993 } 1994 goto out; 1995 } 1996 1997 while (dp = readdir(srcdirp)) { 1998 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 1999 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 2000 dp->d_name[2] == '\0')) 2001 continue; 2002 2003 if ((srcattrfd = openat(sourcedirfd, dp->d_name, 2004 O_RDONLY)) == -1) { 2005 if (!attrsilent) { 2006 (void) fprintf(stderr, 2007 gettext("%s: cannot open attribute %s on" 2008 " file %s: "), cmd, dp->d_name, source); 2009 perror(""); 2010 ++error; 2011 goto next; 2012 } 2013 } 2014 2015 if (fstat(srcattrfd, &s3) < 0) { 2016 if (!attrsilent) { 2017 (void) fprintf(stderr, 2018 gettext("%s: could not stat attribute" 2019 " %s on file" 2020 " %s: "), cmd, dp->d_name, source); 2021 perror(""); 2022 ++error; 2023 } 2024 goto next; 2025 } 2026 2027 if (pflg || mve) { 2028 if ((aclerror = facl_get(srcattrfd, 2029 ACL_NO_TRIVIAL, &xacl)) != 0) { 2030 if (!attrsilent) { 2031 (void) fprintf(stderr, gettext( 2032 "%s: failed to get acl entries of" 2033 " attribute %s for" 2034 " %s: %s"), cmd, dp->d_name, 2035 source, acl_strerror(aclerror)); 2036 ++error; 2037 } 2038 } 2039 } 2040 2041 (void) unlinkat(targetdirfd, dp->d_name, 0); 2042 if ((targattrfd = openat(targetdirfd, dp->d_name, 2043 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) { 2044 if (!attrsilent) { 2045 (void) fprintf(stderr, 2046 gettext("%s: could not create attribute" 2047 " %s on file" 2048 " %s: "), cmd, dp->d_name, target); 2049 perror(""); 2050 ++error; 2051 } 2052 goto next; 2053 } 2054 2055 /* 2056 * preserve ACL 2057 */ 2058 if ((pflg || mve) && xacl != NULL) { 2059 if ((facl_set(targattrfd, xacl)) < 0) { 2060 if (!attrsilent) { 2061 (void) fprintf(stderr, gettext( 2062 "%s: failed to set acl entries on" 2063 " attribute %s for" 2064 "%s\n"), cmd, dp->d_name, target); 2065 ++error; 2066 } 2067 acl_free(xacl); 2068 xacl = NULL; 2069 } 2070 } 2071 2072 if (fstat(targattrfd, &s4) < 0) { 2073 if (!attrsilent) { 2074 (void) fprintf(stderr, 2075 gettext("%s: could not stat attribute" 2076 " %s on file" 2077 " %s: "), cmd, dp->d_name, source); 2078 perror(""); 2079 ++error; 2080 } 2081 goto next; 2082 } 2083 2084 /* 2085 * setup path string to be passed to writefile 2086 * 2087 * We need to include attribute in the string so that 2088 * a useful error message can be printed in the case of a failure. 2089 */ 2090 attrstr = gettext(" attribute "); 2091 src_size = strlen(source) + 2092 strlen(dp->d_name) + strlen(attrstr) + 1; 2093 srcbuf = malloc(src_size); 2094 2095 if (srcbuf == NULL) { 2096 if (!attrsilent) { 2097 (void) fprintf(stderr, 2098 gettext("%s: could not allocate memory" 2099 " for path buffer: "), cmd); 2100 perror(""); 2101 ++error; 2102 } 2103 goto next; 2104 } 2105 targ_size = strlen(target) + 2106 strlen(dp->d_name) + strlen(attrstr) + 1; 2107 targbuf = malloc(targ_size); 2108 if (targbuf == NULL) { 2109 if (!attrsilent) { 2110 (void) fprintf(stderr, 2111 gettext("%s: could not allocate memory" 2112 " for path buffer: "), cmd); 2113 perror(""); 2114 ++error; 2115 } 2116 goto next; 2117 } 2118 2119 (void) snprintf(srcbuf, src_size, "%s%s%s", 2120 source, attrstr, dp->d_name); 2121 (void) snprintf(targbuf, targ_size, "%s%s%s", 2122 target, attrstr, dp->d_name); 2123 2124 if (writefile(srcattrfd, targattrfd, 2125 srcbuf, targbuf, &s3, &s4) != 0) { 2126 if (!attrsilent) { 2127 ++error; 2128 } 2129 goto next; 2130 } 2131 2132 if (pflg || mve) { 2133 mode = FMODE(s3); 2134 2135 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 2136 if (!attrsilent) { 2137 (void) fprintf(stderr, 2138 gettext("%s: cannot change" 2139 " owner and group of" 2140 " attribute %s for" " file" 2141 " %s: "), cmd, dp->d_name, target); 2142 perror(""); 2143 ++error; 2144 } 2145 if (mode & (S_ISUID | S_ISGID)) { 2146 /* try to clear S_ISUID and S_ISGID */ 2147 mode &= ~S_ISUID & ~S_ISGID; 2148 ++clearflg; 2149 } 2150 } 2151 /* tv_usec were cleared above */ 2152 times[0].tv_sec = s3.st_atime; 2153 times[1].tv_sec = s3.st_mtime; 2154 if (futimesat(targetdirfd, dp->d_name, times) < 0) { 2155 if (!attrsilent) { 2156 (void) fprintf(stderr, 2157 gettext("%s: cannot set attribute" 2158 " times for %s: "), cmd, target); 2159 perror(""); 2160 ++error; 2161 } 2162 } 2163 if (fchmod(targattrfd, mode) != 0) { 2164 if (clearflg) { 2165 (void) fprintf(stderr, gettext( 2166 "%s: cannot clear S_ISUID and " 2167 "S_ISGID bits in attribute %s" 2168 " for file" 2169 " %s: "), cmd, dp->d_name, target); 2170 } else { 2171 if (!attrsilent) { 2172 (void) fprintf(stderr, 2173 gettext( 2174 "%s: cannot set permissions of attribute" 2175 " %s for %s: "), cmd, dp->d_name, target); 2176 perror(""); 2177 ++error; 2178 } 2179 } 2180 } 2181 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) { 2182 if (!attrsilent) { 2183 (void) fprintf(stderr, gettext( 2184 "%s: failed to set acl entries on" 2185 " attribute %s for" 2186 "%s\n"), cmd, dp->d_name, target); 2187 ++error; 2188 } 2189 acl_free(xacl); 2190 xacl = NULL; 2191 } 2192 } 2193 next: 2194 if (xacl != NULL) { 2195 acl_free(xacl); 2196 xacl = NULL; 2197 } 2198 if (srcbuf != NULL) 2199 free(srcbuf); 2200 if (targbuf != NULL) 2201 free(targbuf); 2202 if (srcattrfd != -1) 2203 (void) close(srcattrfd); 2204 if (targattrfd != -1) 2205 (void) close(targattrfd); 2206 srcattrfd = targattrfd = -1; 2207 srcbuf = targbuf = NULL; 2208 } 2209 out: 2210 if (xacl != NULL) { 2211 acl_free(xacl); 2212 xacl = NULL; 2213 } 2214 if (attrdiracl != NULL) { 2215 acl_free(attrdiracl); 2216 attrdiracl = NULL; 2217 } 2218 if (srcbuf) 2219 free(srcbuf); 2220 if (targbuf) 2221 free(targbuf); 2222 if (sourcedirfd != -1) 2223 (void) close(sourcedirfd); 2224 if (targetdirfd != -1) 2225 (void) close(targetdirfd); 2226 if (srcdirp != NULL) 2227 (void) closedir(srcdirp); 2228 if (srcfd != -1) 2229 (void) close(srcfd); 2230 if (targfd != -1) 2231 (void) close(targfd); 2232 return (error == 0 ? 0 : 1); 2233 } 2234 2235 /* 2236 * nanoseconds are rounded off to microseconds by flooring. 2237 */ 2238 static void 2239 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 2240 { 2241 tv->tv_sec = ts->tv_sec; 2242 tv->tv_usec = ts->tv_nsec / 1000; 2243 } 2244