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