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