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 2005 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 int 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 return (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 return (ret); 906 } 907 908 static int 909 writefile(int fi, int fo, char *source, char *target, 910 struct stat *s1p, struct stat *s2p) 911 { 912 int mapsize, munmapsize; 913 caddr_t cp; 914 off_t filesize = s1p->st_size; 915 off_t offset; 916 int nbytes; 917 int remains; 918 int n; 919 920 if (ISREG(*s1p) && s1p->st_size > SMALLFILESIZE) { 921 /* 922 * Determine size of initial mapping. This will determine the 923 * size of the address space chunk we work with. This initial 924 * mapping size will be used to perform munmap() in the future. 925 */ 926 mapsize = MAXMAPSIZE; 927 if (s1p->st_size < mapsize) mapsize = s1p->st_size; 928 munmapsize = mapsize; 929 930 /* 931 * Mmap time! 932 */ 933 if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ, 934 MAP_SHARED, fi, (off_t)0)) == MAP_FAILED) 935 mapsize = 0; /* can't mmap today */ 936 } else 937 mapsize = 0; 938 939 if (mapsize != 0) { 940 offset = 0; 941 942 for (;;) { 943 nbytes = write(fo, cp, mapsize); 944 /* 945 * if we write less than the mmaped size it's due to a 946 * media error on the input file or out of space on 947 * the output file. So, try again, and look for errno. 948 */ 949 if ((nbytes >= 0) && (nbytes != (int)mapsize)) { 950 remains = mapsize - nbytes; 951 while (remains > 0) { 952 nbytes = write(fo, 953 cp + mapsize - remains, remains); 954 if (nbytes < 0) { 955 if (errno == ENOSPC) 956 Perror(target); 957 else 958 Perror(source); 959 (void) close(fi); 960 (void) close(fo); 961 (void) munmap(cp, munmapsize); 962 if (ISREG(*s2p)) 963 (void) unlink(target); 964 return (1); 965 } 966 remains -= nbytes; 967 if (remains == 0) 968 nbytes = mapsize; 969 } 970 } 971 /* 972 * although the write manual page doesn't specify this 973 * as a possible errno, it is set when the nfs read 974 * via the mmap'ed file is accessed, so report the 975 * problem as a source access problem, not a target file 976 * problem 977 */ 978 if (nbytes < 0) { 979 if (errno == EACCES) 980 Perror(source); 981 else 982 Perror(target); 983 (void) close(fi); 984 (void) close(fo); 985 (void) munmap(cp, munmapsize); 986 if (ISREG(*s2p)) 987 (void) unlink(target); 988 return (1); 989 } 990 filesize -= nbytes; 991 if (filesize == 0) 992 break; 993 offset += nbytes; 994 if (filesize < mapsize) 995 mapsize = filesize; 996 if (mmap(cp, mapsize, PROT_READ, MAP_SHARED | MAP_FIXED, 997 fi, offset) == MAP_FAILED) { 998 Perror(source); 999 (void) close(fi); 1000 (void) close(fo); 1001 (void) munmap(cp, munmapsize); 1002 if (ISREG(*s2p)) 1003 (void) unlink(target); 1004 return (1); 1005 } 1006 } 1007 (void) munmap(cp, munmapsize); 1008 } else { 1009 char buf[SMALLFILESIZE]; 1010 for (;;) { 1011 n = read(fi, buf, sizeof (buf)); 1012 if (n == 0) { 1013 return (0); 1014 } else if (n < 0) { 1015 Perror2(source, "read"); 1016 (void) close(fi); 1017 (void) close(fo); 1018 if (ISREG(*s2p)) 1019 (void) unlink(target); 1020 return (1); 1021 } else if (write(fo, buf, n) != n) { 1022 Perror2(target, "write"); 1023 (void) close(fi); 1024 (void) close(fo); 1025 if (ISREG(*s2p)) 1026 (void) unlink(target); 1027 return (1); 1028 } 1029 } 1030 } 1031 return (0); 1032 } 1033 1034 /* 1035 * create_tnode() 1036 * 1037 * Create a node for use with the search tree which contains the 1038 * inode information (device id and inode number). 1039 * 1040 * Input 1041 * dev - device id 1042 * ino - inode number 1043 * 1044 * Output 1045 * tnode - NULL on error, otherwise returns a tnode structure 1046 * which contains the input device id and inode number. 1047 */ 1048 static tree_node_t * 1049 create_tnode(dev_t dev, ino_t ino) 1050 { 1051 tree_node_t *tnode; 1052 1053 if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) { 1054 tnode->node_dev = dev; 1055 tnode->node_ino = ino; 1056 } 1057 1058 return (tnode); 1059 } 1060 1061 static int 1062 chkfiles(char *source, char **to) 1063 { 1064 char *buf = (char *)NULL; 1065 int (*statf)() = (cpy && 1066 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat; 1067 char *target = *to; 1068 1069 /* 1070 * Make sure source file exists. 1071 */ 1072 if ((*statf)(source, &s1) < 0) { 1073 /* 1074 * Keep the old error message except when someone tries to 1075 * mv/cp/ln a symbolic link that has a trailing slash and 1076 * points to a file. 1077 */ 1078 if (errno == ENOTDIR) 1079 (void) fprintf(stderr, "%s: %s: %s\n", cmd, source, 1080 strerror(errno)); 1081 else 1082 (void) fprintf(stderr, 1083 gettext("%s: cannot access %s\n"), cmd, source); 1084 return (1); 1085 } 1086 1087 /* 1088 * Get ACL info: don't bother with ln or mv'ing symlinks 1089 */ 1090 if ((!lnk) && !(mve && ISLNK(s1))) { 1091 if (s1aclp != NULL) { 1092 free(s1aclp); 1093 s1aclp = NULL; 1094 } 1095 if ((s1aclcnt = acl(source, GETACLCNT, 0, NULL)) < 0) { 1096 (void) fprintf(stderr, 1097 "%s: failed to get acl entries\n", source); 1098 return (1); 1099 } 1100 if (s1aclcnt > MIN_ACL_ENTRIES) { 1101 if ((s1aclp = (aclent_t *)malloc( 1102 sizeof (aclent_t) * s1aclcnt)) == NULL) { 1103 (void) fprintf(stderr, "Insufficient memory\n"); 1104 return (1); 1105 } 1106 if ((acl(source, GETACL, s1aclcnt, s1aclp)) < 0) { 1107 (void) fprintf(stderr, 1108 "%s: failed to get acl entries\n", source); 1109 return (1); 1110 } 1111 } 1112 /* else: just permission bits */ 1113 } 1114 1115 /* 1116 * If stat fails, then the target doesn't exist, 1117 * we will create a new target with default file type of regular. 1118 */ 1119 1120 FTYPE(s2) = S_IFREG; 1121 targetexists = 0; 1122 if ((*statf)(target, &s2) >= 0) { 1123 if (ISLNK(s2)) 1124 (void) stat(target, &s2); 1125 /* 1126 * If target is a directory, 1127 * make complete name of new file 1128 * within that directory. 1129 */ 1130 if (ISDIR(s2)) { 1131 size_t len; 1132 1133 len = strlen(target) + strlen(dname(source)) + 4; 1134 if ((buf = (char *)malloc(len)) == NULL) { 1135 (void) fprintf(stderr, 1136 gettext("%s: Insufficient memory to " 1137 "%s %s\n "), cmd, cmd, source); 1138 exit(3); 1139 } 1140 (void) snprintf(buf, len, "%s/%s", 1141 target, dname(source)); 1142 *to = target = buf; 1143 } 1144 1145 if ((*statf)(target, &s2) >= 0) { 1146 int overwrite = FALSE; 1147 int override = FALSE; 1148 1149 targetexists++; 1150 if (cpy || mve) { 1151 /* 1152 * For cp and mv, it is an error if the 1153 * source and target are the same file. 1154 * Check for the same inode and file 1155 * system, but don't check for the same 1156 * absolute pathname because it is an 1157 * error when the source and target are 1158 * hard links to the same file. 1159 */ 1160 if (IDENTICAL(s1, s2)) { 1161 (void) fprintf(stderr, 1162 gettext( 1163 "%s: %s and %s are identical\n"), 1164 cmd, source, target); 1165 if (buf != NULL) 1166 free(buf); 1167 return (1); 1168 } 1169 } 1170 if (lnk) { 1171 /* 1172 * For ln, it is an error if the source and 1173 * target are identical files (same inode, 1174 * same file system, and filenames resolve 1175 * to same absolute pathname). 1176 */ 1177 if (!chk_different(source, target)) { 1178 if (buf != NULL) 1179 free(buf); 1180 return (1); 1181 } 1182 } 1183 if (lnk && !silent) { 1184 (void) fprintf(stderr, 1185 gettext("%s: %s: File exists\n"), 1186 cmd, target); 1187 if (buf != NULL) 1188 free(buf); 1189 return (1); 1190 } 1191 1192 /* 1193 * overwrite: 1194 * If the user does not have access to 1195 * the target, ask ----if it is not 1196 * silent and user invoked command 1197 * interactively. 1198 * 1199 * override: 1200 * If not silent, and stdin is a terminal, and 1201 * there's no write access, and the file isn't a 1202 * symbolic link, ask for permission. 1203 * 1204 * XPG4: both overwrite and override: 1205 * ask only one question. 1206 * 1207 * TRANSLATION_NOTE - The following messages will 1208 * contain the first character of the strings for 1209 * "yes" and "no" defined in the file 1210 * "nl_langinfo.po". After substitution, the 1211 * message will appear as follows: 1212 * <cmd>: overwrite <filename> (y/n)? 1213 * where <cmd> is the name of the command 1214 * (cp, mv) and <filename> is the destination file 1215 */ 1216 1217 1218 overwrite = iflg && !silent && use_stdin(); 1219 override = !cpy && (access(target, 2) < 0) && 1220 !silent && use_stdin() && !ISLNK(s2); 1221 1222 if (overwrite && override) 1223 (void) fprintf(stderr, 1224 gettext("%s: overwrite %s and override " 1225 "protection %o (%s/%s)? "), cmd, target, 1226 FMODE(s2) & MODEBITS, yeschr, nochr); 1227 else if (overwrite && ISREG(s2)) 1228 (void) fprintf(stderr, 1229 gettext("%s: overwrite %s (%s/%s)? "), 1230 cmd, target, yeschr, nochr); 1231 else if (override) 1232 (void) fprintf(stderr, 1233 gettext("%s: %s: override protection " 1234 /*CSTYLED*/ 1235 "%o (%s/%s)? "), 1236 /*CSTYLED*/ 1237 cmd, target, FMODE(s2) & MODEBITS, 1238 yeschr, nochr); 1239 if (overwrite || override) { 1240 if (ISREG(s2)) { 1241 if (getresp()) { 1242 if (buf != NULL) 1243 free(buf); 1244 return (2); 1245 } 1246 } 1247 } 1248 if (lnk && unlink(target) < 0) { 1249 (void) fprintf(stderr, 1250 gettext("%s: cannot unlink %s: "), 1251 cmd, target); 1252 perror(""); 1253 return (1); 1254 } 1255 } 1256 } 1257 return (0); 1258 } 1259 1260 /* 1261 * check whether source and target are different 1262 * return 1 when they are different 1263 * return 0 when they are identical, or when unable to resolve a pathname 1264 */ 1265 static int 1266 chk_different(char *source, char *target) 1267 { 1268 char rtarget[PATH_MAX], rsource[PATH_MAX]; 1269 1270 if (IDENTICAL(s1, s2)) { 1271 /* 1272 * IDENTICAL will be true for hard links, therefore 1273 * check whether the filenames are different 1274 */ 1275 if ((getrealpath(source, rsource) == 0) || 1276 (getrealpath(target, rtarget) == 0)) { 1277 return (0); 1278 } 1279 if (strncmp(rsource, rtarget, PATH_MAX) == 0) { 1280 (void) fprintf(stderr, gettext( 1281 "%s: %s and %s are identical\n"), 1282 cmd, source, target); 1283 return (0); 1284 } 1285 } 1286 return (1); 1287 } 1288 1289 /* 1290 * get real path (resolved absolute pathname) 1291 * return 1 on success, 0 on failure 1292 */ 1293 static int 1294 getrealpath(char *path, char *rpath) 1295 { 1296 if (realpath(path, rpath) == NULL) { 1297 int errno_save = errno; 1298 (void) fprintf(stderr, gettext( 1299 "%s: can't resolve path %s: "), cmd, path); 1300 errno = errno_save; 1301 perror(""); 1302 return (0); 1303 } 1304 return (1); 1305 } 1306 1307 static int 1308 rcopy(char *from, char *to) 1309 { 1310 DIR *fold = opendir(from); 1311 struct dirent *dp; 1312 struct stat statb, s1save; 1313 int errs = 0; 1314 char fromname[PATH_MAX]; 1315 1316 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) { 1317 Perror(from); 1318 return (1); 1319 } 1320 if (pflg || mve) { 1321 /* 1322 * Save s1 (stat information for source dir) so that 1323 * mod and access times can be reserved during "cp -p" 1324 * or mv, since s1 gets overwritten. 1325 */ 1326 s1save = s1; 1327 } 1328 for (;;) { 1329 dp = readdir(fold); 1330 if (dp == 0) { 1331 (void) closedir(fold); 1332 if (pflg || mve) 1333 return (chg_time(to, s1save) + errs); 1334 return (errs); 1335 } 1336 if (dp->d_ino == 0) 1337 continue; 1338 if ((strcmp(dp->d_name, ".") == 0) || 1339 (strcmp(dp->d_name, "..") == 0)) 1340 continue; 1341 if (strlen(from)+1+strlen(dp->d_name) >= 1342 sizeof (fromname) - 1) { 1343 (void) fprintf(stderr, 1344 gettext("%s : %s/%s: Name too long\n"), 1345 cmd, from, dp->d_name); 1346 errs++; 1347 continue; 1348 } 1349 (void) snprintf(fromname, sizeof (fromname), 1350 "%s/%s", from, dp->d_name); 1351 errs += cpymve(fromname, to); 1352 } 1353 } 1354 1355 static char * 1356 dname(char *name) 1357 { 1358 register char *p; 1359 1360 /* 1361 * Return just the file name given the complete path. 1362 * Like basename(1). 1363 */ 1364 1365 p = name; 1366 1367 /* 1368 * While there are characters left, 1369 * set name to start after last 1370 * delimiter. 1371 */ 1372 1373 while (*p) 1374 if (*p++ == DELIM && *p) 1375 name = p; 1376 return (name); 1377 } 1378 1379 static int 1380 getresp(void) 1381 { 1382 register int c, i; 1383 char ans_buf[SCHAR_MAX + 1]; 1384 1385 /* 1386 * Get response from user. Based on 1387 * first character, make decision. 1388 * Discard rest of line. 1389 */ 1390 for (i = 0; ; i++) { 1391 c = getchar(); 1392 if (c == '\n' || c == 0 || c == EOF) { 1393 ans_buf[i] = 0; 1394 break; 1395 } 1396 if (i < SCHAR_MAX) 1397 ans_buf[i] = c; 1398 } 1399 if (i >= SCHAR_MAX) { 1400 i = SCHAR_MAX; 1401 ans_buf[SCHAR_MAX] = 0; 1402 } 1403 if ((i == 0) | (strncmp(yeschr, ans_buf, i))) 1404 return (1); 1405 return (0); 1406 } 1407 1408 static void 1409 usage(void) 1410 { 1411 /* 1412 * Display usage message. 1413 */ 1414 1415 if (mve) { 1416 (void) fprintf(stderr, gettext( 1417 "Usage: mv [-f] [-i] f1 f2\n" 1418 " mv [-f] [-i] f1 ... fn d1\n" 1419 " mv [-f] [-i] d1 d2\n")); 1420 } else if (lnk) { 1421 (void) fprintf(stderr, gettext( 1422 #ifdef XPG4 1423 "Usage: ln [-f] [-s] f1 [f2]\n" 1424 " ln [-f] [-s] f1 ... fn d1\n" 1425 " ln [-f] -s d1 d2\n")); 1426 #else 1427 "Usage: ln [-f] [-n] [-s] f1 [f2]\n" 1428 " ln [-f] [-n] [-s] f1 ... fn d1\n" 1429 " ln [-f] [-n] -s d1 d2\n")); 1430 #endif 1431 } else if (cpy) { 1432 (void) fprintf(stderr, gettext( 1433 "Usage: cp [-f] [-i] [-p] [-@] f1 f2\n" 1434 " cp [-f] [-i] [-p] [-@] f1 ... fn d1\n" 1435 " cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] " 1436 "d1 ... dn-1 dn\n")); 1437 } 1438 exit(2); 1439 } 1440 1441 /* 1442 * chg_time() 1443 * 1444 * Try to preserve modification and access time. 1445 * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version, 1446 * don't report a utime() failure. 1447 * If this is the XPG4 version and utime fails, if 1) pflg is set (cp -p) 1448 * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero 1449 * exit status only if pflg is set. 1450 * utimes(2) is being used to achieve granularity in 1451 * microseconds while setting file times. 1452 */ 1453 static int 1454 chg_time(char *to, struct stat ss) 1455 { 1456 struct timeval times[2]; 1457 int rc; 1458 1459 timestruc_to_timeval(&ss.st_atim, times); 1460 timestruc_to_timeval(&ss.st_mtim, times + 1); 1461 1462 rc = utimes(to, times); 1463 #ifdef XPG4 1464 if ((pflg || mve) && rc != 0) { 1465 (void) fprintf(stderr, 1466 gettext("%s: cannot set times for %s: "), cmd, to); 1467 perror(""); 1468 if (pflg) 1469 return (1); 1470 } 1471 #endif 1472 1473 return (0); 1474 1475 } 1476 1477 /* 1478 * chg_mode() 1479 * 1480 * This function is called upon "cp -p" or mv across filesystems. 1481 * 1482 * Try to preserve the owner and group id. If chown() fails, 1483 * only print a diagnostic message if doing a mv in the XPG4 version; 1484 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear 1485 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a 1486 * non-zero exit status because this is a security violation. 1487 * Try to preserve permissions. 1488 * If this is the XPG4 version and chmod() fails, print a diagnostic message 1489 * and arrange for a non-zero exit status. 1490 * If this is the Solaris version and chmod() fails, do not print a 1491 * diagnostic message or exit with a non-zero value. 1492 */ 1493 static int 1494 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode) 1495 { 1496 int clearflg = 0; /* controls message printed upon chown() error */ 1497 1498 if (chown(target, uid, gid) != 0) { 1499 #ifdef XPG4 1500 if (mve) { 1501 (void) fprintf(stderr, gettext("%s: cannot change" 1502 " owner and group of %s: "), cmd, target); 1503 perror(""); 1504 } 1505 #endif 1506 if (mode & (S_ISUID | S_ISGID)) { 1507 /* try to clear S_ISUID and S_ISGID */ 1508 mode &= ~S_ISUID & ~S_ISGID; 1509 ++clearflg; 1510 } 1511 } 1512 if (chmod(target, mode) != 0) { 1513 if (clearflg) { 1514 (void) fprintf(stderr, gettext( 1515 "%s: cannot clear S_ISUID and S_ISGID bits in" 1516 " %s: "), cmd, target); 1517 perror(""); 1518 /* cp -p should get non-zero exit; mv should not */ 1519 if (pflg) 1520 return (1); 1521 } 1522 #ifdef XPG4 1523 else { 1524 (void) fprintf(stderr, gettext( 1525 "%s: cannot set permissions for %s: "), cmd, target); 1526 perror(""); 1527 /* cp -p should get non-zero exit; mv should not */ 1528 if (pflg) 1529 return (1); 1530 } 1531 #endif 1532 } 1533 return (0); 1534 1535 } 1536 1537 static void 1538 Perror(char *s) 1539 { 1540 char buf[PATH_MAX + 10]; 1541 1542 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s); 1543 perror(buf); 1544 } 1545 1546 static void 1547 Perror2(char *s1, char *s2) 1548 { 1549 char buf[PATH_MAX + 20]; 1550 1551 (void) snprintf(buf, sizeof (buf), "%s: %s: %s", 1552 cmd, gettext(s1), gettext(s2)); 1553 perror(buf); 1554 } 1555 1556 /* 1557 * used for cp -R and for mv across file systems 1558 */ 1559 static int 1560 copydir(char *source, char *target) 1561 { 1562 int ret, attret = 0; 1563 int pret = 0; /* need separate flag if -p is specified */ 1564 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */ 1565 struct stat s1save; 1566 int s1aclcnt_save; 1567 aclent_t *s1aclp_save = NULL; 1568 1569 if (cpy && !rflg) { 1570 (void) fprintf(stderr, 1571 gettext("%s: %s: is a directory\n"), cmd, source); 1572 return (1); 1573 } 1574 1575 if (stat(target, &s2) < 0) { 1576 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) { 1577 (void) fprintf(stderr, "%s: ", cmd); 1578 perror(target); 1579 return (1); 1580 } 1581 if (stat(target, &s2) == 0) { 1582 fixmode = s2.st_mode; 1583 } else { 1584 fixmode = s1.st_mode; 1585 } 1586 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU)); 1587 } else if (!(ISDIR(s2))) { 1588 (void) fprintf(stderr, 1589 gettext("%s: %s: not a directory.\n"), cmd, target); 1590 return (1); 1591 } 1592 if (pflg || mve) { 1593 /* 1594 * Save s1 (stat information for source dir) and acl info, 1595 * if any, so that ownership, modes, times, and acl's can 1596 * be reserved during "cp -p" or mv. 1597 * s1 gets overwritten when doing the recursive copy. 1598 */ 1599 s1save = s1; 1600 if (s1aclp != NULL) { 1601 if ((s1aclp_save = (aclent_t *)malloc(sizeof (aclent_t) 1602 * s1aclcnt)) != NULL) { 1603 (void) memcpy(s1aclp_save, s1aclp, 1604 sizeof (aclent_t) * s1aclcnt); 1605 s1aclcnt_save = s1aclcnt; 1606 } 1607 #ifdef XPG4 1608 else { 1609 (void) fprintf(stderr, gettext("%s: " 1610 "Insufficient memory to save acl" 1611 " entry\n"), cmd); 1612 if (pflg) 1613 return (1); 1614 } 1615 #endif 1616 } 1617 } 1618 1619 ret = rcopy(source, target); 1620 1621 /* 1622 * Once we created a directory, go ahead and set 1623 * its attributes, e.g. acls and time. The info 1624 * may get overwritten if we continue traversing 1625 * down the tree. 1626 * 1627 * ACL for directory 1628 */ 1629 if (pflg || mve) { 1630 if (s1aclp_save != NULL) { 1631 if ((acl(target, SETACL, s1aclcnt_save, s1aclp_save)) 1632 < 0) { 1633 #ifdef XPG4 1634 if (pflg || mve) { 1635 #else 1636 if (pflg) { 1637 #endif 1638 (void) fprintf(stderr, gettext( 1639 "%s: failed to set acl entries " 1640 "on %s\n"), cmd, target); 1641 if (pflg) { 1642 free(s1aclp_save); 1643 ret++; 1644 } 1645 } 1646 /* else: silent and continue */ 1647 } 1648 free(s1aclp_save); 1649 } 1650 if ((pret = chg_mode(target, UID(s1save), GID(s1save), 1651 FMODE(s1save))) == 0) 1652 pret = chg_time(target, s1save); 1653 ret += pret; 1654 } else if (fixmode != (mode_t)0) 1655 (void) chmod(target, fixmode & MODEBITS); 1656 1657 if (pflg || atflg || mve) { 1658 attret = copyattributes(source, target); 1659 if (!attrsilent && attret != 0) { 1660 (void) fprintf(stderr, gettext("%s: Failed to preserve" 1661 " extended attributes of directory" 1662 " %s\n"), cmd, source); 1663 } else { 1664 /* 1665 * Otherwise ignore failure. 1666 */ 1667 attret = 0; 1668 } 1669 } 1670 if (attret != 0) 1671 return (attret); 1672 return (ret); 1673 } 1674 1675 static int 1676 copyspecial(char *target) 1677 { 1678 int ret = 0; 1679 1680 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) { 1681 (void) fprintf(stderr, gettext( 1682 "cp: cannot create special file %s: "), target); 1683 perror(""); 1684 return (1); 1685 } 1686 1687 if (pflg) { 1688 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0) 1689 ret = chg_time(target, s1); 1690 } 1691 1692 return (ret); 1693 } 1694 1695 static int 1696 use_stdin(void) 1697 { 1698 #ifdef XPG4 1699 return (1); 1700 #else 1701 return (isatty(fileno(stdin))); 1702 #endif 1703 } 1704 1705 static int 1706 copyattributes(char *source, char *target) 1707 { 1708 int ret; 1709 int sourcedirfd, targetdirfd; 1710 int srcfd, targfd; 1711 int tmpfd; 1712 DIR *srcdirp; 1713 int srcattrfd, targattrfd; 1714 struct dirent *dp; 1715 char *attrstr; 1716 char *srcbuf, *targbuf; 1717 size_t src_size, targ_size; 1718 int error = 0; 1719 mode_t mode; 1720 int clearflg = 0; 1721 int aclcnt; 1722 int attrdiraclcnt; 1723 aclent_t *aclp = NULL; 1724 aclent_t *attrdiraclp = NULL; 1725 struct stat attrdir, s3, s4; 1726 struct timeval times[2]; 1727 mode_t targmode; 1728 1729 srcdirp = NULL; 1730 srcfd = targfd = tmpfd = -1; 1731 sourcedirfd = targetdirfd = srcattrfd = targattrfd = -1; 1732 srcbuf = targbuf = NULL; 1733 1734 if (pathconf(source, _PC_XATTR_EXISTS) != 1) 1735 return (0); 1736 1737 if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 1738 if (!attrsilent) { 1739 (void) fprintf(stderr, 1740 gettext( 1741 "%s: cannot preserve extended attributes, " 1742 "operation not supported on file" 1743 " %s\n"), cmd, target); 1744 } 1745 return (1); 1746 } 1747 1748 1749 if ((srcfd = open(source, O_RDONLY)) == -1) { 1750 if (pflg && attrsilent) { 1751 error = 0; 1752 goto out; 1753 } 1754 if (!attrsilent) { 1755 (void) fprintf(stderr, 1756 gettext("%s: cannot open file" 1757 " %s: "), cmd, source); 1758 perror(""); 1759 } 1760 ++error; 1761 goto out; 1762 } 1763 if ((targfd = open(target, O_RDONLY)) == -1) { 1764 1765 if (pflg && attrsilent) { 1766 error = 0; 1767 goto out; 1768 } 1769 if (!attrsilent) { 1770 (void) fprintf(stderr, 1771 gettext("%s: cannot open file" 1772 " %s: "), cmd, source); 1773 perror(""); 1774 } 1775 ++error; 1776 goto out; 1777 } 1778 1779 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 1780 if (pflg && attrsilent) { 1781 error = 0; 1782 goto out; 1783 } 1784 if (!attrsilent) { 1785 (void) fprintf(stderr, 1786 gettext("%s: cannot open attribute" 1787 " directory for %s: "), cmd, source); 1788 perror(""); 1789 ++error; 1790 } 1791 goto out; 1792 } 1793 1794 if (fstat(sourcedirfd, &attrdir) == -1) { 1795 if (pflg && attrsilent) { 1796 error = 0; 1797 goto out; 1798 } 1799 1800 if (!attrsilent) { 1801 (void) fprintf(stderr, 1802 gettext("%s: could not retrieve stat" 1803 " information for attribute directory" 1804 "of file %s: "), cmd, source); 1805 perror(""); 1806 ++error; 1807 } 1808 goto out; 1809 } 1810 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 1811 /* 1812 * We couldn't create the attribute directory 1813 * 1814 * Lets see if we can add write support to the mode 1815 * and create the directory and then put the mode back 1816 * to way it should be. 1817 */ 1818 1819 targmode = FMODE(s1) | S_IWUSR; 1820 if (fchmod(targfd, targmode) == 0) { 1821 targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR); 1822 /* 1823 * Put mode back to what it was 1824 */ 1825 targmode = FMODE(s1) & MODEBITS; 1826 if (fchmod(targfd, targmode) == -1) { 1827 if (pflg && attrsilent) { 1828 error = 0; 1829 goto out; 1830 } 1831 if (!attrsilent) { 1832 (void) fprintf(stderr, 1833 gettext("%s: failed to set" 1834 " mode correctly on file" 1835 " %s: "), cmd, target); 1836 perror(""); 1837 ++error; 1838 goto out; 1839 } 1840 } 1841 } else { 1842 if (pflg && attrsilent) { 1843 error = 0; 1844 goto out; 1845 } 1846 if (!attrsilent) { 1847 (void) fprintf(stderr, 1848 gettext("%s: cannot open attribute" 1849 " directory for %s: "), cmd, target); 1850 perror(""); 1851 ++error; 1852 } 1853 goto out; 1854 } 1855 } 1856 1857 if (targetdirfd == -1) { 1858 if (pflg && attrsilent) { 1859 error = 0; 1860 goto out; 1861 } 1862 if (!attrsilent) { 1863 (void) fprintf(stderr, 1864 gettext("%s: cannot open attribute directory" 1865 " for %s: "), cmd, target); 1866 perror(""); 1867 ++error; 1868 } 1869 goto out; 1870 } 1871 1872 /* 1873 * Set mode of attribute directory same as on source, 1874 * if pflg set or this is a move. 1875 */ 1876 1877 if (pflg || mve) { 1878 if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 1879 if (!attrsilent) { 1880 (void) fprintf(stderr, 1881 gettext("%s: failed to set file mode" 1882 " correctly on attribute directory of" 1883 " file %s: "), cmd, target); 1884 perror(""); 1885 ++error; 1886 } 1887 } 1888 1889 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 1890 if (!attrsilent) { 1891 (void) fprintf(stderr, 1892 gettext("%s: failed to set file" 1893 " ownership correctly on attribute" 1894 " directory of file %s: "), cmd, target); 1895 perror(""); 1896 ++error; 1897 } 1898 } 1899 /* 1900 * Now that we are the owner we can update st_ctime by calling 1901 * futimesat. 1902 */ 1903 times[0].tv_sec = attrdir.st_atime; 1904 times[0].tv_usec = 0; 1905 times[1].tv_sec = attrdir.st_mtime; 1906 times[1].tv_usec = 0; 1907 if (futimesat(targetdirfd, ".", times) < 0) { 1908 if (!attrsilent) { 1909 (void) fprintf(stderr, 1910 gettext("%s: cannot set attribute times" 1911 " for %s: "), cmd, target); 1912 perror(""); 1913 ++error; 1914 } 1915 } 1916 1917 /* 1918 * Now set owner and group of attribute directory, implies 1919 * changing the ACL of the hidden attribute directory first. 1920 */ 1921 if ((attrdiraclcnt = facl(sourcedirfd, 1922 GETACLCNT, 0, NULL)) < 0) { 1923 if (!attrsilent) { 1924 (void) fprintf(stderr, gettext( 1925 "%s: failed to get acl entries of" 1926 " attribute directory for" 1927 " %s\n"), cmd, source); 1928 ++error; 1929 } 1930 } 1931 if (attrdiraclcnt > MIN_ACL_ENTRIES) { 1932 if ((attrdiraclp = (aclent_t *)malloc( 1933 sizeof (aclent_t) * attrdiraclcnt)) == NULL) { 1934 if (!attrsilent) { 1935 (void) fprintf(stderr, gettext( 1936 "insufficient memory" 1937 " for acl\n")); 1938 ++error; 1939 } 1940 } else { 1941 if ((ret = facl(sourcedirfd, GETACL, 1942 attrdiraclcnt, attrdiraclp)) == -1) { 1943 if (!attrsilent) { 1944 (void) fprintf(stderr, 1945 gettext( 1946 "%s: failed to get acl" 1947 " entries of attribute" 1948 " directory for" 1949 " %s\n"), cmd, target); 1950 free(attrdiraclp); 1951 attrdiraclp = NULL; 1952 attrdiraclcnt = 0; 1953 ++error; 1954 } 1955 1956 } 1957 if (ret != -1 && (facl(targetdirfd, SETACL, 1958 attrdiraclcnt, 1959 attrdiraclp) != 0)) { 1960 if (!attrsilent) { 1961 (void) fprintf(stderr, gettext( 1962 "%s: failed to set acl entries" 1963 " on attribute directory " 1964 "for %s\n"), cmd, target); 1965 ++error; 1966 } 1967 free(attrdiraclp); 1968 attrdiraclp = NULL; 1969 attrdiraclcnt = 0; 1970 } 1971 } 1972 1973 } 1974 } 1975 1976 /* 1977 * dup sourcedirfd for use by fdopendir(). 1978 * fdopendir will take ownership of given fd and will close 1979 * it when closedir() is called. 1980 */ 1981 1982 if ((tmpfd = dup(sourcedirfd)) == -1) { 1983 if (pflg && attrsilent) { 1984 error = 0; 1985 goto out; 1986 } 1987 if (!attrsilent) { 1988 (void) fprintf(stderr, 1989 gettext( 1990 "%s: unable to dup attribute directory" 1991 " file descriptor for %s: "), cmd, source); 1992 perror(""); 1993 ++error; 1994 } 1995 goto out; 1996 } 1997 if ((srcdirp = fdopendir(tmpfd)) == NULL) { 1998 if (pflg && attrsilent) { 1999 error = 0; 2000 goto out; 2001 } 2002 if (!attrsilent) { 2003 (void) fprintf(stderr, 2004 gettext("%s: failed to open attribute" 2005 " directory for %s: "), cmd, source); 2006 perror(""); 2007 ++error; 2008 } 2009 goto out; 2010 } 2011 2012 while (dp = readdir(srcdirp)) { 2013 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 2014 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 2015 dp->d_name[2] == '\0')) 2016 continue; 2017 2018 if ((srcattrfd = openat(sourcedirfd, dp->d_name, 2019 O_RDONLY)) == -1) { 2020 if (!attrsilent) { 2021 (void) fprintf(stderr, 2022 gettext("%s: cannot open attribute %s on" 2023 " file %s: "), cmd, dp->d_name, source); 2024 perror(""); 2025 ++error; 2026 goto next; 2027 } 2028 } 2029 2030 if (fstat(srcattrfd, &s3) < 0) { 2031 if (!attrsilent) { 2032 (void) fprintf(stderr, 2033 gettext("%s: could not stat attribute" 2034 " %s on file" 2035 " %s: "), cmd, dp->d_name, source); 2036 perror(""); 2037 ++error; 2038 } 2039 goto next; 2040 } 2041 2042 if (pflg || mve) { 2043 if ((aclcnt = facl(srcattrfd, 2044 GETACLCNT, 0, NULL)) < 0) { 2045 if (!attrsilent) { 2046 (void) fprintf(stderr, gettext( 2047 "%s: failed to get acl entries of" 2048 " attribute %s for" 2049 " %s: "), cmd, dp->d_name, source); 2050 perror(""); 2051 ++error; 2052 } 2053 } 2054 if (aclcnt > MIN_ACL_ENTRIES) { 2055 if ((aclp = (aclent_t *)malloc( 2056 sizeof (aclent_t) * aclcnt)) == 2057 NULL) { 2058 if (!attrsilent) { 2059 (void) fprintf(stderr, gettext( 2060 "insufficient memory" 2061 " for acl: ")); 2062 perror(""); 2063 ++error; 2064 } 2065 } else { 2066 2067 if ((facl(srcattrfd, GETACL, 2068 aclcnt, aclp)) < 0) { 2069 if (!attrsilent) { 2070 (void) fprintf(stderr, 2071 gettext( 2072 "%s: failed to get" 2073 " acl entries of" 2074 " attribute %s for" 2075 /*CSTYLED*/ 2076 " %s: "), cmd, 2077 dp->d_name, target); 2078 free(aclp); 2079 aclp = NULL; 2080 perror(""); 2081 ++error; 2082 } 2083 2084 } 2085 2086 } 2087 } 2088 2089 } 2090 2091 (void) unlinkat(targetdirfd, dp->d_name, 0); 2092 if ((targattrfd = openat(targetdirfd, dp->d_name, 2093 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) { 2094 if (!attrsilent) { 2095 (void) fprintf(stderr, 2096 gettext("%s: could not create attribute" 2097 " %s on file" 2098 " %s: "), cmd, dp->d_name, target); 2099 perror(""); 2100 ++error; 2101 } 2102 goto next; 2103 } 2104 2105 /* 2106 * preserve ACL 2107 */ 2108 if ((pflg || mve) && aclp != NULL) { 2109 if ((facl(targattrfd, SETACL, aclcnt, aclp)) < 0) { 2110 if (!attrsilent) { 2111 (void) fprintf(stderr, gettext( 2112 "%s: failed to set acl entries on" 2113 " attribute %s for" 2114 "%s\n"), cmd, dp->d_name, target); 2115 ++error; 2116 } 2117 free(aclp); 2118 aclp = NULL; 2119 aclcnt = 0; 2120 } 2121 } 2122 2123 if (fstat(targattrfd, &s4) < 0) { 2124 if (!attrsilent) { 2125 (void) fprintf(stderr, 2126 gettext("%s: could not stat attribute" 2127 " %s on file" 2128 " %s: "), cmd, dp->d_name, source); 2129 perror(""); 2130 ++error; 2131 } 2132 goto next; 2133 } 2134 2135 /* 2136 * setup path string to be passed to writefile 2137 * 2138 * We need to include attribute in the string so that 2139 * a useful error message can be printed in the case of a failure. 2140 */ 2141 attrstr = gettext(" attribute "); 2142 src_size = strlen(source) + 2143 strlen(dp->d_name) + strlen(attrstr) + 1; 2144 srcbuf = malloc(src_size); 2145 2146 if (srcbuf == NULL) { 2147 if (!attrsilent) { 2148 (void) fprintf(stderr, 2149 gettext("%s: could not allocate memory" 2150 " for path buffer: "), cmd); 2151 perror(""); 2152 ++error; 2153 } 2154 goto next; 2155 } 2156 targ_size = strlen(target) + 2157 strlen(dp->d_name) + strlen(attrstr) + 1; 2158 targbuf = malloc(targ_size); 2159 if (targbuf == NULL) { 2160 if (!attrsilent) { 2161 (void) fprintf(stderr, 2162 gettext("%s: could not allocate memory" 2163 " for path buffer: "), cmd); 2164 perror(""); 2165 ++error; 2166 } 2167 goto next; 2168 } 2169 2170 (void) snprintf(srcbuf, src_size, "%s%s%s", 2171 source, attrstr, dp->d_name); 2172 (void) snprintf(targbuf, targ_size, "%s%s%s", 2173 target, attrstr, dp->d_name); 2174 2175 if (writefile(srcattrfd, targattrfd, 2176 srcbuf, targbuf, &s3, &s4) != 0) { 2177 if (!attrsilent) { 2178 ++error; 2179 } 2180 goto next; 2181 } 2182 2183 if (pflg || mve) { 2184 mode = FMODE(s3); 2185 2186 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 2187 if (!attrsilent) { 2188 (void) fprintf(stderr, 2189 gettext("%s: cannot change" 2190 " owner and group of" 2191 " attribute %s for" " file" 2192 " %s: "), cmd, dp->d_name, target); 2193 perror(""); 2194 ++error; 2195 } 2196 if (mode & (S_ISUID | S_ISGID)) { 2197 /* try to clear S_ISUID and S_ISGID */ 2198 mode &= ~S_ISUID & ~S_ISGID; 2199 ++clearflg; 2200 } 2201 } 2202 /* tv_usec were cleared above */ 2203 times[0].tv_sec = s3.st_atime; 2204 times[1].tv_sec = s3.st_mtime; 2205 if (futimesat(targetdirfd, dp->d_name, times) < 0) { 2206 if (!attrsilent) { 2207 (void) fprintf(stderr, 2208 gettext("%s: cannot set attribute" 2209 " times for %s: "), cmd, target); 2210 perror(""); 2211 ++error; 2212 } 2213 } 2214 if (fchmod(targattrfd, mode) != 0) { 2215 if (clearflg) { 2216 (void) fprintf(stderr, gettext( 2217 "%s: cannot clear S_ISUID and " 2218 "S_ISGID bits in attribute %s" 2219 " for file" 2220 " %s: "), cmd, dp->d_name, target); 2221 } else { 2222 if (!attrsilent) { 2223 (void) fprintf(stderr, 2224 gettext( 2225 "%s: cannot set permissions of attribute" 2226 " %s for %s: "), cmd, dp->d_name, target); 2227 perror(""); 2228 ++error; 2229 } 2230 } 2231 } 2232 } 2233 next: 2234 if (aclp != NULL) { 2235 free(aclp); 2236 aclp = NULL; 2237 } 2238 aclcnt = 0; 2239 if (srcbuf != NULL) 2240 free(srcbuf); 2241 if (targbuf != NULL) 2242 free(targbuf); 2243 if (srcattrfd != -1) 2244 (void) close(srcattrfd); 2245 if (targattrfd != -1) 2246 (void) close(targattrfd); 2247 srcattrfd = targattrfd = -1; 2248 srcbuf = targbuf = NULL; 2249 } 2250 out: 2251 if (aclp != NULL) 2252 free(aclp); 2253 if (attrdiraclp != NULL) 2254 free(attrdiraclp); 2255 if (srcbuf) 2256 free(srcbuf); 2257 if (targbuf) 2258 free(targbuf); 2259 if (sourcedirfd != -1) 2260 (void) close(sourcedirfd); 2261 if (targetdirfd != -1) 2262 (void) close(targetdirfd); 2263 if (srcdirp != NULL) 2264 (void) closedir(srcdirp); 2265 if (srcfd != -1) 2266 (void) close(srcfd); 2267 if (targfd != -1) 2268 (void) close(targfd); 2269 return (error == 0 ? 0 : 1); 2270 } 2271 2272 /* 2273 * nanoseconds are rounded off to microseconds by flooring. 2274 */ 2275 static void 2276 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 2277 { 2278 tv->tv_sec = ts->tv_sec; 2279 tv->tv_usec = ts->tv_nsec / 1000; 2280 } 2281