10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51465Smarks * Common Development and Distribution License (the "License"). 61465Smarks * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 229324SRenaud.Manus@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 270Sstevel@tonic-gate /* All Rights Reserved */ 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 310Sstevel@tonic-gate * The Regents of the University of California 320Sstevel@tonic-gate * All Rights Reserved 330Sstevel@tonic-gate * 340Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 350Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 360Sstevel@tonic-gate * contributors. 370Sstevel@tonic-gate */ 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 400Sstevel@tonic-gate * Combined mv/cp/ln command: 410Sstevel@tonic-gate * mv file1 file2 420Sstevel@tonic-gate * mv dir1 dir2 430Sstevel@tonic-gate * mv file1 ... filen dir1 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate #include <sys/time.h> 460Sstevel@tonic-gate #include <signal.h> 470Sstevel@tonic-gate #include <locale.h> 480Sstevel@tonic-gate #include <stdarg.h> 490Sstevel@tonic-gate #include <sys/acl.h> 500Sstevel@tonic-gate #include <libcmdutils.h> 51789Sahrens #include <aclutils.h> 524774Sas145665 #include "getresponse.h" 530Sstevel@tonic-gate 540Sstevel@tonic-gate #define FTYPE(A) (A.st_mode) 550Sstevel@tonic-gate #define FMODE(A) (A.st_mode) 560Sstevel@tonic-gate #define UID(A) (A.st_uid) 570Sstevel@tonic-gate #define GID(A) (A.st_gid) 580Sstevel@tonic-gate #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino) 590Sstevel@tonic-gate #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK) 600Sstevel@tonic-gate #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR) 610Sstevel@tonic-gate #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR) 620Sstevel@tonic-gate #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR) 630Sstevel@tonic-gate #define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO) 640Sstevel@tonic-gate #define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK) 650Sstevel@tonic-gate #define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG) 660Sstevel@tonic-gate #define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \ 670Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFBLK || \ 680Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFIFO) 69*10294Sdannywebster@googlemail.com #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK) 700Sstevel@tonic-gate 710Sstevel@tonic-gate #define BLKSIZE 4096 720Sstevel@tonic-gate #define PATHSIZE 1024 730Sstevel@tonic-gate #define DOT "." 740Sstevel@tonic-gate #define DOTDOT ".." 750Sstevel@tonic-gate #define DELIM '/' 760Sstevel@tonic-gate #define EQ(x, y) (strcmp(x, y) == 0) 770Sstevel@tonic-gate #define FALSE 0 780Sstevel@tonic-gate #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 790Sstevel@tonic-gate #define TRUE 1 800Sstevel@tonic-gate #define MAXMAPSIZE (1024*1024*8) /* map at most 8MB */ 810Sstevel@tonic-gate #define SMALLFILESIZE (32*1024) /* don't use mmap on little files */ 820Sstevel@tonic-gate 830Sstevel@tonic-gate static char *dname(char *); 840Sstevel@tonic-gate static int lnkfil(char *, char *); 850Sstevel@tonic-gate static int cpymve(char *, char *); 860Sstevel@tonic-gate static int chkfiles(char *, char **); 870Sstevel@tonic-gate static int rcopy(char *, char *); 880Sstevel@tonic-gate static int chk_different(char *, char *); 890Sstevel@tonic-gate static int chg_time(char *, struct stat); 900Sstevel@tonic-gate static int chg_mode(char *, uid_t, gid_t, mode_t); 910Sstevel@tonic-gate static int copydir(char *, char *); 920Sstevel@tonic-gate static int copyspecial(char *); 930Sstevel@tonic-gate static int getrealpath(char *, char *); 940Sstevel@tonic-gate static void usage(void); 950Sstevel@tonic-gate static void Perror(char *); 960Sstevel@tonic-gate static void Perror2(char *, char *); 970Sstevel@tonic-gate static int use_stdin(void); 980Sstevel@tonic-gate static int copyattributes(char *, char *); 995331Samw static int copy_sysattr(char *, char *); 1000Sstevel@tonic-gate static void timestruc_to_timeval(timestruc_t *, struct timeval *); 1010Sstevel@tonic-gate static tree_node_t *create_tnode(dev_t, ino_t); 1020Sstevel@tonic-gate 1035331Samw static struct stat s1, s2, s3, s4; 1040Sstevel@tonic-gate static int cpy = FALSE; 1050Sstevel@tonic-gate static int mve = FALSE; 1060Sstevel@tonic-gate static int lnk = FALSE; 1070Sstevel@tonic-gate static char *cmd; 1080Sstevel@tonic-gate static int silent = 0; 1090Sstevel@tonic-gate static int fflg = 0; 1100Sstevel@tonic-gate static int iflg = 0; 1110Sstevel@tonic-gate static int pflg = 0; 1120Sstevel@tonic-gate static int Rflg = 0; /* recursive copy */ 1130Sstevel@tonic-gate static int rflg = 0; /* recursive copy */ 1140Sstevel@tonic-gate static int sflg = 0; 1150Sstevel@tonic-gate static int Hflg = 0; /* follow cmd line arg symlink to dir */ 1160Sstevel@tonic-gate static int Lflg = 0; /* follow symlinks */ 1170Sstevel@tonic-gate static int Pflg = 0; /* do not follow symlinks */ 1180Sstevel@tonic-gate static int atflg = 0; 1190Sstevel@tonic-gate static int attrsilent = 0; 1200Sstevel@tonic-gate static int targetexists = 0; 1210Sstevel@tonic-gate static int cmdarg; /* command line argument */ 1220Sstevel@tonic-gate static avl_tree_t *stree = NULL; /* source file inode search tree */ 123789Sahrens static acl_t *s1acl; 1245331Samw static int saflg = 0; /* 'cp' extended system attr. */ 1256091Sbasabi static int srcfd = -1; 1266091Sbasabi static int targfd = -1; 1276091Sbasabi static int sourcedirfd = -1; 1286091Sbasabi static int targetdirfd = -1; 1296091Sbasabi static DIR *srcdirp = NULL; 1306091Sbasabi static int srcattrfd = -1; 1316091Sbasabi static int targattrfd = -1; 1325331Samw static struct stat attrdir; 1335331Samw 1345331Samw /* Extended system attributes support */ 1355331Samw 1365331Samw static int open_source(char *); 1375331Samw static int open_target_srctarg_attrdirs(char *, char *); 1385331Samw static int open_attrdirp(char *); 1395331Samw static int traverse_attrfile(struct dirent *, char *, char *, int); 1405331Samw static void rewind_attrdir(DIR *); 1415331Samw static void close_all(); 1425331Samw 1430Sstevel@tonic-gate 144212Scf46844 int 1450Sstevel@tonic-gate main(int argc, char *argv[]) 1460Sstevel@tonic-gate { 1470Sstevel@tonic-gate int c, i, r, errflg = 0; 1480Sstevel@tonic-gate char target[PATH_MAX]; 1490Sstevel@tonic-gate int (*move)(char *, char *); 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * Determine command invoked (mv, cp, or ln) 1530Sstevel@tonic-gate */ 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate if (cmd = strrchr(argv[0], '/')) 1560Sstevel@tonic-gate ++cmd; 1570Sstevel@tonic-gate else 1580Sstevel@tonic-gate cmd = argv[0]; 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate /* 1610Sstevel@tonic-gate * Set flags based on command. 1620Sstevel@tonic-gate */ 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1650Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1660Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1670Sstevel@tonic-gate #endif 1680Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1694774Sas145665 if (init_yes() < 0) { 1704774Sas145665 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 1714774Sas145665 strerror(errno)); 1724774Sas145665 exit(3); 1734774Sas145665 } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate if (EQ(cmd, "mv")) 1760Sstevel@tonic-gate mve = TRUE; 1770Sstevel@tonic-gate else if (EQ(cmd, "ln")) 1780Sstevel@tonic-gate lnk = TRUE; 1790Sstevel@tonic-gate else if (EQ(cmd, "cp")) 1800Sstevel@tonic-gate cpy = TRUE; 1810Sstevel@tonic-gate else { 1820Sstevel@tonic-gate (void) fprintf(stderr, 1830Sstevel@tonic-gate gettext("Invalid command name (%s); expecting " 1840Sstevel@tonic-gate "mv, cp, or ln.\n"), cmd); 1850Sstevel@tonic-gate exit(1); 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* 1890Sstevel@tonic-gate * Check for options: 1905331Samw * cp -r|-R [-H|-L|-P] [-fip@/] file1 [file2 ...] target 1915331Samw * cp [-fiprR@/] file1 [file2 ...] target 1920Sstevel@tonic-gate * ln [-f] [-n] [-s] file1 [file2 ...] target 1930Sstevel@tonic-gate * ln [-f] [-n] [-s] file1 [file2 ...] 1940Sstevel@tonic-gate * mv [-f|i] file1 [file2 ...] target 1950Sstevel@tonic-gate * mv [-f|i] dir1 target 1960Sstevel@tonic-gate */ 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate if (cpy) { 1995331Samw while ((c = getopt(argc, argv, "fHiLpPrR@/")) != EOF) 2000Sstevel@tonic-gate switch (c) { 2010Sstevel@tonic-gate case 'f': 2020Sstevel@tonic-gate fflg++; 2030Sstevel@tonic-gate break; 2040Sstevel@tonic-gate case 'i': 2050Sstevel@tonic-gate iflg++; 2060Sstevel@tonic-gate break; 2070Sstevel@tonic-gate case 'p': 2080Sstevel@tonic-gate pflg++; 2090Sstevel@tonic-gate #ifdef XPG4 2100Sstevel@tonic-gate attrsilent = 1; 2110Sstevel@tonic-gate atflg = 0; 2125331Samw saflg = 0; 2130Sstevel@tonic-gate #else 2145374Sbasabi if (atflg == 0) 2150Sstevel@tonic-gate attrsilent = 1; 2160Sstevel@tonic-gate #endif 2170Sstevel@tonic-gate break; 2180Sstevel@tonic-gate case 'H': 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * If more than one of -H, -L, or -P are 2210Sstevel@tonic-gate * specified, only the last option specified 2220Sstevel@tonic-gate * determines the behavior. 2230Sstevel@tonic-gate */ 2240Sstevel@tonic-gate Lflg = Pflg = 0; 2250Sstevel@tonic-gate Hflg++; 2260Sstevel@tonic-gate break; 2270Sstevel@tonic-gate case 'L': 2280Sstevel@tonic-gate Hflg = Pflg = 0; 2290Sstevel@tonic-gate Lflg++; 2300Sstevel@tonic-gate break; 2310Sstevel@tonic-gate case 'P': 2320Sstevel@tonic-gate Lflg = Hflg = 0; 2330Sstevel@tonic-gate Pflg++; 2340Sstevel@tonic-gate break; 2350Sstevel@tonic-gate case 'R': 2360Sstevel@tonic-gate /* 2370Sstevel@tonic-gate * The default behavior of cp -R|-r 2380Sstevel@tonic-gate * when specified without -H|-L|-P 2390Sstevel@tonic-gate * is -L. 2400Sstevel@tonic-gate */ 2410Sstevel@tonic-gate Rflg++; 2420Sstevel@tonic-gate /*FALLTHROUGH*/ 2430Sstevel@tonic-gate case 'r': 2440Sstevel@tonic-gate rflg++; 2450Sstevel@tonic-gate break; 2460Sstevel@tonic-gate case '@': 2470Sstevel@tonic-gate atflg++; 2480Sstevel@tonic-gate attrsilent = 0; 2490Sstevel@tonic-gate #ifdef XPG4 2500Sstevel@tonic-gate pflg = 0; 2510Sstevel@tonic-gate #endif 2520Sstevel@tonic-gate break; 2535331Samw case '/': 2545331Samw saflg++; 2555331Samw attrsilent = 0; 2565331Samw #ifdef XPG4 2575331Samw pflg = 0; 2585331Samw #endif 2595331Samw break; 2600Sstevel@tonic-gate default: 2610Sstevel@tonic-gate errflg++; 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate /* -R or -r must be specified with -H, -L, or -P */ 2650Sstevel@tonic-gate if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) { 2660Sstevel@tonic-gate errflg++; 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate } else if (mve) { 2700Sstevel@tonic-gate while ((c = getopt(argc, argv, "fis")) != EOF) 2710Sstevel@tonic-gate switch (c) { 2720Sstevel@tonic-gate case 'f': 2730Sstevel@tonic-gate silent++; 2740Sstevel@tonic-gate #ifdef XPG4 2750Sstevel@tonic-gate iflg = 0; 2760Sstevel@tonic-gate #endif 2770Sstevel@tonic-gate break; 2780Sstevel@tonic-gate case 'i': 2790Sstevel@tonic-gate iflg++; 2800Sstevel@tonic-gate #ifdef XPG4 2810Sstevel@tonic-gate silent = 0; 2820Sstevel@tonic-gate #endif 2830Sstevel@tonic-gate break; 2840Sstevel@tonic-gate default: 2850Sstevel@tonic-gate errflg++; 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate } else { /* ln */ 2880Sstevel@tonic-gate while ((c = getopt(argc, argv, "fns")) != EOF) 2890Sstevel@tonic-gate switch (c) { 2900Sstevel@tonic-gate case 'f': 2910Sstevel@tonic-gate silent++; 2920Sstevel@tonic-gate break; 2930Sstevel@tonic-gate case 'n': 2940Sstevel@tonic-gate /* silently ignored; this is the default */ 2950Sstevel@tonic-gate break; 2960Sstevel@tonic-gate case 's': 2970Sstevel@tonic-gate sflg++; 2980Sstevel@tonic-gate break; 2990Sstevel@tonic-gate default: 3000Sstevel@tonic-gate errflg++; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate } 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate /* 3050Sstevel@tonic-gate * For BSD compatibility allow - to delimit the end of 3060Sstevel@tonic-gate * options for mv. 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate if (mve && optind < argc && (strcmp(argv[optind], "-") == 0)) 3090Sstevel@tonic-gate optind++; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate /* 3120Sstevel@tonic-gate * Check for sufficient arguments 3130Sstevel@tonic-gate * or a usage error. 3140Sstevel@tonic-gate */ 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate argc -= optind; 3170Sstevel@tonic-gate argv = &argv[optind]; 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) { 3200Sstevel@tonic-gate (void) fprintf(stderr, 3210Sstevel@tonic-gate gettext("%s: Insufficient arguments (%d)\n"), 3220Sstevel@tonic-gate cmd, argc); 3230Sstevel@tonic-gate usage(); 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate if (errflg != 0) 3270Sstevel@tonic-gate usage(); 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate /* 3300Sstevel@tonic-gate * If there is more than a source and target, 3310Sstevel@tonic-gate * the last argument (the target) must be a directory 3320Sstevel@tonic-gate * which really exists. 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate if (argc > 2) { 3360Sstevel@tonic-gate if (stat(argv[argc-1], &s2) < 0) { 3370Sstevel@tonic-gate (void) fprintf(stderr, 3380Sstevel@tonic-gate gettext("%s: %s not found\n"), 3390Sstevel@tonic-gate cmd, argv[argc-1]); 3400Sstevel@tonic-gate exit(2); 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate if (!ISDIR(s2)) { 3440Sstevel@tonic-gate (void) fprintf(stderr, 3450Sstevel@tonic-gate gettext("%s: Target %s must be a directory\n"), 3460Sstevel@tonic-gate cmd, argv[argc-1]); 3470Sstevel@tonic-gate usage(); 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate if (strlen(argv[argc-1]) >= PATH_MAX) { 3520Sstevel@tonic-gate (void) fprintf(stderr, 3530Sstevel@tonic-gate gettext("%s: Target %s file name length exceeds PATH_MAX" 3540Sstevel@tonic-gate " %d\n"), cmd, argv[argc-1], PATH_MAX); 3550Sstevel@tonic-gate exit(78); 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate if (argc == 1) { 3590Sstevel@tonic-gate if (!lnk) 3600Sstevel@tonic-gate usage(); 3610Sstevel@tonic-gate (void) strcpy(target, "."); 3620Sstevel@tonic-gate } else { 3630Sstevel@tonic-gate (void) strcpy(target, argv[--argc]); 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate /* 3670Sstevel@tonic-gate * Perform a multiple argument mv|cp|ln by 3680Sstevel@tonic-gate * multiple invocations of cpymve() or lnkfil(). 3690Sstevel@tonic-gate */ 3700Sstevel@tonic-gate if (lnk) 3710Sstevel@tonic-gate move = lnkfil; 3720Sstevel@tonic-gate else 3730Sstevel@tonic-gate move = cpymve; 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate r = 0; 3760Sstevel@tonic-gate for (i = 0; i < argc; i++) { 3770Sstevel@tonic-gate stree = NULL; 3780Sstevel@tonic-gate cmdarg = 1; 3790Sstevel@tonic-gate r += move(argv[i], target); 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate /* 3830Sstevel@tonic-gate * Show errors by nonzero exit code. 3840Sstevel@tonic-gate */ 3850Sstevel@tonic-gate 386212Scf46844 return (r?2:0); 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate static int 3900Sstevel@tonic-gate lnkfil(char *source, char *target) 3910Sstevel@tonic-gate { 3920Sstevel@tonic-gate char *buf = NULL; 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate if (sflg) { 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate /* 3970Sstevel@tonic-gate * If target is a directory make complete 3980Sstevel@tonic-gate * name of the new symbolic link within that 3990Sstevel@tonic-gate * directory. 4000Sstevel@tonic-gate */ 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate if ((stat(target, &s2) >= 0) && ISDIR(s2)) { 4030Sstevel@tonic-gate size_t len; 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4; 4060Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) { 4070Sstevel@tonic-gate (void) fprintf(stderr, 4080Sstevel@tonic-gate gettext("%s: Insufficient memory " 4090Sstevel@tonic-gate "to %s %s\n"), cmd, cmd, source); 4100Sstevel@tonic-gate exit(3); 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s", 4130Sstevel@tonic-gate target, dname(source)); 4140Sstevel@tonic-gate target = buf; 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate /* 4186555Schin * Check to see if the file exists already. 4196555Schin * In this case we use lstat() instead of stat(): 4206555Schin * unlink(2) and symlink(2) will operate on the file 4216555Schin * itself, not its reference, if the file is a symlink. 4220Sstevel@tonic-gate */ 4230Sstevel@tonic-gate 4246555Schin if ((lstat(target, &s2) == 0)) { 4250Sstevel@tonic-gate /* 4260Sstevel@tonic-gate * Check if the silent flag is set ie. the -f option 4270Sstevel@tonic-gate * is used. If so, use unlink to remove the current 4280Sstevel@tonic-gate * target to replace with the new target, specified 4290Sstevel@tonic-gate * on the command line. Proceed with symlink. 4300Sstevel@tonic-gate */ 4310Sstevel@tonic-gate if (silent) { 4326555Schin /* 4336555Schin * Don't allow silent (-f) removal of an existing 4346555Schin * directory; could leave unreferenced directory 4356555Schin * entries. 4366555Schin */ 4376555Schin if (ISDIR(s2)) { 4386555Schin (void) fprintf(stderr, 4396555Schin gettext("%s: cannot create link " 4406555Schin "over directory %s\n"), cmd, 4416555Schin target); 4426555Schin return (1); 4436555Schin } 4440Sstevel@tonic-gate if (unlink(target) < 0) { 4450Sstevel@tonic-gate (void) fprintf(stderr, 4460Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 4470Sstevel@tonic-gate cmd, target); 4480Sstevel@tonic-gate perror(""); 4490Sstevel@tonic-gate return (1); 4500Sstevel@tonic-gate } 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate } 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* 4560Sstevel@tonic-gate * Create a symbolic link to the source. 4570Sstevel@tonic-gate */ 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate if (symlink(source, target) < 0) { 4600Sstevel@tonic-gate (void) fprintf(stderr, 4610Sstevel@tonic-gate gettext("%s: cannot create %s: "), 4620Sstevel@tonic-gate cmd, target); 4630Sstevel@tonic-gate perror(""); 4640Sstevel@tonic-gate if (buf != NULL) 4650Sstevel@tonic-gate free(buf); 4660Sstevel@tonic-gate return (1); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate if (buf != NULL) 4690Sstevel@tonic-gate free(buf); 4700Sstevel@tonic-gate return (0); 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate switch (chkfiles(source, &target)) { 4740Sstevel@tonic-gate case 1: return (1); 4750Sstevel@tonic-gate case 2: return (0); 4760Sstevel@tonic-gate /* default - fall through */ 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * Make sure source file is not a directory, 481*10294Sdannywebster@googlemail.com * we cannot link directories... 4820Sstevel@tonic-gate */ 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate if (ISDIR(s1)) { 4850Sstevel@tonic-gate (void) fprintf(stderr, 4860Sstevel@tonic-gate gettext("%s: %s is a directory\n"), cmd, source); 4870Sstevel@tonic-gate return (1); 4880Sstevel@tonic-gate } 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate /* 4910Sstevel@tonic-gate * hard link, call link() and return. 4920Sstevel@tonic-gate */ 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate if (link(source, target) < 0) { 4950Sstevel@tonic-gate if (errno == EXDEV) 4960Sstevel@tonic-gate (void) fprintf(stderr, 4970Sstevel@tonic-gate gettext("%s: %s is on a different file system\n"), 4980Sstevel@tonic-gate cmd, target); 4990Sstevel@tonic-gate else { 5000Sstevel@tonic-gate (void) fprintf(stderr, 5010Sstevel@tonic-gate gettext("%s: cannot create link %s: "), 5020Sstevel@tonic-gate cmd, target); 5030Sstevel@tonic-gate perror(""); 5040Sstevel@tonic-gate } 5050Sstevel@tonic-gate if (buf != NULL) 5060Sstevel@tonic-gate free(buf); 5070Sstevel@tonic-gate return (1); 5080Sstevel@tonic-gate } else { 5090Sstevel@tonic-gate if (buf != NULL) 5100Sstevel@tonic-gate free(buf); 5110Sstevel@tonic-gate return (0); 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate } 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate static int 5160Sstevel@tonic-gate cpymve(char *source, char *target) 5170Sstevel@tonic-gate { 5180Sstevel@tonic-gate int n; 5190Sstevel@tonic-gate int fi, fo; 5200Sstevel@tonic-gate int ret = 0; 5210Sstevel@tonic-gate int attret = 0; 5225331Samw int sattret = 0; 5230Sstevel@tonic-gate int errno_save; 5245374Sbasabi int error = 0; 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate switch (chkfiles(source, &target)) { 5270Sstevel@tonic-gate case 1: return (1); 5280Sstevel@tonic-gate case 2: return (0); 5290Sstevel@tonic-gate /* default - fall through */ 5300Sstevel@tonic-gate } 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate /* 5330Sstevel@tonic-gate * If it's a recursive copy and source 5340Sstevel@tonic-gate * is a directory, then call rcopy (from copydir). 5350Sstevel@tonic-gate */ 5360Sstevel@tonic-gate if (cpy) { 5370Sstevel@tonic-gate if (ISDIR(s1)) { 5380Sstevel@tonic-gate int rc; 5390Sstevel@tonic-gate avl_index_t where = 0; 5400Sstevel@tonic-gate tree_node_t *tnode; 5410Sstevel@tonic-gate tree_node_t *tptr; 5420Sstevel@tonic-gate dev_t save_dev = s1.st_dev; 5430Sstevel@tonic-gate ino_t save_ino = s1.st_ino; 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate /* 5460Sstevel@tonic-gate * We will be recursing into the directory so 5470Sstevel@tonic-gate * save the inode information to a search tree 5480Sstevel@tonic-gate * to avoid getting into an endless loop. 5490Sstevel@tonic-gate */ 5500Sstevel@tonic-gate if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) { 5510Sstevel@tonic-gate if (rc == 0) { 5520Sstevel@tonic-gate /* 5530Sstevel@tonic-gate * We've already visited this directory. 5540Sstevel@tonic-gate * Don't remove the search tree entry 5550Sstevel@tonic-gate * to make sure we don't get into an 5560Sstevel@tonic-gate * endless loop if revisited from a 5570Sstevel@tonic-gate * different part of the hierarchy. 5580Sstevel@tonic-gate */ 5590Sstevel@tonic-gate (void) fprintf(stderr, gettext( 5600Sstevel@tonic-gate "%s: cycle detected: %s\n"), 5610Sstevel@tonic-gate cmd, source); 5620Sstevel@tonic-gate } else { 5630Sstevel@tonic-gate Perror(source); 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate return (1); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate cmdarg = 0; 5690Sstevel@tonic-gate rc = copydir(source, target); 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate /* 5720Sstevel@tonic-gate * Create a tnode to get an index to the matching 5730Sstevel@tonic-gate * node (same dev and inode) in the search tree, 5740Sstevel@tonic-gate * then use the index to remove the matching node 5750Sstevel@tonic-gate * so it we do not wrongly detect a cycle when 5760Sstevel@tonic-gate * revisiting this directory from another part of 5770Sstevel@tonic-gate * the hierarchy. 5780Sstevel@tonic-gate */ 5790Sstevel@tonic-gate if ((tnode = create_tnode(save_dev, 5800Sstevel@tonic-gate save_ino)) == NULL) { 5810Sstevel@tonic-gate Perror(source); 5820Sstevel@tonic-gate return (1); 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate if ((tptr = avl_find(stree, tnode, &where)) != NULL) { 5850Sstevel@tonic-gate avl_remove(stree, tptr); 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate free(tptr); 5880Sstevel@tonic-gate free(tnode); 5890Sstevel@tonic-gate return (rc); 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate } else if (ISDEV(s1) && Rflg) { 5920Sstevel@tonic-gate return (copyspecial(target)); 5930Sstevel@tonic-gate } else { 5940Sstevel@tonic-gate goto copy; 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate } 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate if (mve) { 5990Sstevel@tonic-gate if (rename(source, target) >= 0) 6000Sstevel@tonic-gate return (0); 6010Sstevel@tonic-gate if (errno != EXDEV) { 6020Sstevel@tonic-gate if (errno == ENOTDIR && ISDIR(s1)) { 6030Sstevel@tonic-gate (void) fprintf(stderr, 6040Sstevel@tonic-gate gettext("%s: %s is a directory\n"), 6050Sstevel@tonic-gate cmd, source); 6060Sstevel@tonic-gate return (1); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate (void) fprintf(stderr, 6090Sstevel@tonic-gate gettext("%s: cannot rename %s to %s: "), 6100Sstevel@tonic-gate cmd, source, target); 6110Sstevel@tonic-gate perror(""); 6120Sstevel@tonic-gate return (1); 6130Sstevel@tonic-gate } 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate /* 6160Sstevel@tonic-gate * cannot move a non-directory (source) onto an existing 6170Sstevel@tonic-gate * directory (target) 6180Sstevel@tonic-gate * 6190Sstevel@tonic-gate */ 6200Sstevel@tonic-gate if (targetexists && ISDIR(s2) && (!ISDIR(s1))) { 6210Sstevel@tonic-gate (void) fprintf(stderr, 6220Sstevel@tonic-gate gettext("%s: cannot mv a non directory %s " 6230Sstevel@tonic-gate "over existing directory" 6240Sstevel@tonic-gate " %s \n"), cmd, source, target); 6250Sstevel@tonic-gate return (1); 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate if (ISDIR(s1)) { 6280Sstevel@tonic-gate #ifdef XPG4 6290Sstevel@tonic-gate if (targetexists && ISDIR(s2)) { 6300Sstevel@tonic-gate /* existing target dir must be empty */ 6310Sstevel@tonic-gate if (rmdir(target) < 0) { 6320Sstevel@tonic-gate errno_save = errno; 6330Sstevel@tonic-gate (void) fprintf(stderr, 6340Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "), 6350Sstevel@tonic-gate cmd, target); 6360Sstevel@tonic-gate errno = errno_save; 6370Sstevel@tonic-gate perror(""); 6380Sstevel@tonic-gate return (1); 6390Sstevel@tonic-gate } 6400Sstevel@tonic-gate } 6410Sstevel@tonic-gate #endif 6420Sstevel@tonic-gate if ((n = copydir(source, target)) == 0) 6430Sstevel@tonic-gate (void) rmdir(source); 6440Sstevel@tonic-gate return (n); 6450Sstevel@tonic-gate } 6460Sstevel@tonic-gate 647*10294Sdannywebster@googlemail.com /* doors cannot be moved across filesystems */ 6480Sstevel@tonic-gate if (ISDOOR(s1)) { 6490Sstevel@tonic-gate (void) fprintf(stderr, 650*10294Sdannywebster@googlemail.com gettext("%s: %s: cannot move door " 6510Sstevel@tonic-gate "across file systems\n"), cmd, source); 6520Sstevel@tonic-gate return (1); 6530Sstevel@tonic-gate } 654*10294Sdannywebster@googlemail.com 655*10294Sdannywebster@googlemail.com /* sockets cannot be moved across filesystems */ 656*10294Sdannywebster@googlemail.com if (ISSOCK(s1)) { 657*10294Sdannywebster@googlemail.com (void) fprintf(stderr, 658*10294Sdannywebster@googlemail.com gettext("%s: %s: cannot move socket " 659*10294Sdannywebster@googlemail.com "across file systems\n"), cmd, source); 660*10294Sdannywebster@googlemail.com return (1); 661*10294Sdannywebster@googlemail.com } 662*10294Sdannywebster@googlemail.com 6630Sstevel@tonic-gate /* 664*10294Sdannywebster@googlemail.com * File cannot be renamed, try to recreate the symbolic 6650Sstevel@tonic-gate * link or special device, or copy the file wholesale 6660Sstevel@tonic-gate * between file systems. 6670Sstevel@tonic-gate */ 6680Sstevel@tonic-gate if (ISLNK(s1)) { 6690Sstevel@tonic-gate register int m; 6700Sstevel@tonic-gate register mode_t md; 6710Sstevel@tonic-gate char symln[PATH_MAX + 1]; 6720Sstevel@tonic-gate 6730Sstevel@tonic-gate if (targetexists && unlink(target) < 0) { 6740Sstevel@tonic-gate (void) fprintf(stderr, 6750Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 6760Sstevel@tonic-gate cmd, target); 6770Sstevel@tonic-gate perror(""); 6780Sstevel@tonic-gate return (1); 6790Sstevel@tonic-gate } 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate if ((m = readlink(source, symln, 6820Sstevel@tonic-gate sizeof (symln) - 1)) < 0) { 6830Sstevel@tonic-gate Perror(source); 6840Sstevel@tonic-gate return (1); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate symln[m] = '\0'; 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS)); 6890Sstevel@tonic-gate if (symlink(symln, target) < 0) { 6900Sstevel@tonic-gate Perror(target); 6910Sstevel@tonic-gate return (1); 6920Sstevel@tonic-gate } 6930Sstevel@tonic-gate (void) umask(md); 6940Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1)); 6950Sstevel@tonic-gate #ifdef XPG4 6960Sstevel@tonic-gate if (m < 0) { 6970Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot" 6980Sstevel@tonic-gate " change owner and group of" 6990Sstevel@tonic-gate " %s: "), cmd, target); 7000Sstevel@tonic-gate perror(""); 7010Sstevel@tonic-gate } 7020Sstevel@tonic-gate #endif 7030Sstevel@tonic-gate goto cleanup; 7040Sstevel@tonic-gate } 7050Sstevel@tonic-gate if (ISDEV(s1)) { 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate if (targetexists && unlink(target) < 0) { 7080Sstevel@tonic-gate (void) fprintf(stderr, 7090Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 7100Sstevel@tonic-gate cmd, target); 7110Sstevel@tonic-gate perror(""); 7120Sstevel@tonic-gate return (1); 7130Sstevel@tonic-gate } 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 7160Sstevel@tonic-gate Perror(target); 7170Sstevel@tonic-gate return (1); 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1)); 7210Sstevel@tonic-gate (void) chg_time(target, s1); 7220Sstevel@tonic-gate goto cleanup; 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate if (ISREG(s1)) { 7260Sstevel@tonic-gate if (ISDIR(s2)) { 7270Sstevel@tonic-gate if (targetexists && rmdir(target) < 0) { 7280Sstevel@tonic-gate (void) fprintf(stderr, 7290Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "), 7300Sstevel@tonic-gate cmd, target); 7310Sstevel@tonic-gate perror(""); 7320Sstevel@tonic-gate return (1); 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate } else { 7350Sstevel@tonic-gate if (targetexists && unlink(target) < 0) { 7360Sstevel@tonic-gate (void) fprintf(stderr, 7370Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 7380Sstevel@tonic-gate cmd, target); 7390Sstevel@tonic-gate perror(""); 7400Sstevel@tonic-gate return (1); 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate } 7430Sstevel@tonic-gate 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate copy: 7460Sstevel@tonic-gate /* 7470Sstevel@tonic-gate * If the source file is a symlink, and either 7480Sstevel@tonic-gate * -P or -H flag (only if -H is specified and the 7490Sstevel@tonic-gate * source file is not a command line argument) 7500Sstevel@tonic-gate * were specified, then action is taken on the symlink 7510Sstevel@tonic-gate * itself, not the file referenced by the symlink. 7520Sstevel@tonic-gate * Note: this is executed for 'cp' only. 7530Sstevel@tonic-gate */ 7540Sstevel@tonic-gate if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) { 7550Sstevel@tonic-gate int m; 7560Sstevel@tonic-gate mode_t md; 7570Sstevel@tonic-gate char symln[PATH_MAX + 1]; 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate m = readlink(source, symln, sizeof (symln) - 1); 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate if (m < 0) { 7620Sstevel@tonic-gate Perror(source); 7630Sstevel@tonic-gate return (1); 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate symln[m] = '\0'; 7660Sstevel@tonic-gate 7670Sstevel@tonic-gate /* 7680Sstevel@tonic-gate * Copy the sym link to the target. 7690Sstevel@tonic-gate * Note: If the target exists, write a 7700Sstevel@tonic-gate * diagnostic message, do nothing more 7710Sstevel@tonic-gate * with the source file, and return to 7720Sstevel@tonic-gate * process any remaining files. 7730Sstevel@tonic-gate */ 7740Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS)); 7750Sstevel@tonic-gate if (symlink(symln, target) < 0) { 7760Sstevel@tonic-gate Perror(target); 7770Sstevel@tonic-gate return (1); 7780Sstevel@tonic-gate } 7790Sstevel@tonic-gate (void) umask(md); 7800Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1)); 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate if (m < 0) { 7830Sstevel@tonic-gate (void) fprintf(stderr, gettext( 7840Sstevel@tonic-gate "cp: cannot change owner and " 7850Sstevel@tonic-gate "group of %s:"), target); 7860Sstevel@tonic-gate perror(""); 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate } else { 7890Sstevel@tonic-gate /* 7900Sstevel@tonic-gate * Copy the file. If it happens to be a 7910Sstevel@tonic-gate * symlink, copy the file referenced 7920Sstevel@tonic-gate * by the symlink. 7930Sstevel@tonic-gate */ 7940Sstevel@tonic-gate fi = open(source, O_RDONLY); 7950Sstevel@tonic-gate if (fi < 0) { 7960Sstevel@tonic-gate (void) fprintf(stderr, 7970Sstevel@tonic-gate gettext("%s: cannot open %s: "), 7980Sstevel@tonic-gate cmd, source); 7990Sstevel@tonic-gate perror(""); 8000Sstevel@tonic-gate return (1); 8010Sstevel@tonic-gate } 8020Sstevel@tonic-gate 8030Sstevel@tonic-gate fo = creat(target, s1.st_mode & MODEBITS); 8040Sstevel@tonic-gate if (fo < 0) { 8050Sstevel@tonic-gate /* 8060Sstevel@tonic-gate * If -f and creat() failed, unlink 8070Sstevel@tonic-gate * and try again. 8080Sstevel@tonic-gate */ 8090Sstevel@tonic-gate if (fflg) { 8100Sstevel@tonic-gate (void) unlink(target); 8110Sstevel@tonic-gate fo = creat(target, 8120Sstevel@tonic-gate s1.st_mode & MODEBITS); 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate } 8150Sstevel@tonic-gate if (fo < 0) { 8160Sstevel@tonic-gate (void) fprintf(stderr, 8170Sstevel@tonic-gate gettext("%s: cannot create %s: "), 8180Sstevel@tonic-gate cmd, target); 8190Sstevel@tonic-gate perror(""); 8200Sstevel@tonic-gate (void) close(fi); 8210Sstevel@tonic-gate return (1); 8220Sstevel@tonic-gate } else { 8230Sstevel@tonic-gate /* stat the new file, its used below */ 8240Sstevel@tonic-gate (void) stat(target, &s2); 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate /* 8280Sstevel@tonic-gate * Set target's permissions to the source 8290Sstevel@tonic-gate * before any copying so that any partially 8300Sstevel@tonic-gate * copied file will have the source's 8310Sstevel@tonic-gate * permissions (at most) or umask permissions 8320Sstevel@tonic-gate * whichever is the most restrictive. 8330Sstevel@tonic-gate * 8340Sstevel@tonic-gate * ACL for regular files 8350Sstevel@tonic-gate */ 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate if (pflg || mve) { 8380Sstevel@tonic-gate (void) chmod(target, FMODE(s1)); 839789Sahrens if (s1acl != NULL) { 840789Sahrens if ((acl_set(target, 841789Sahrens s1acl)) < 0) { 8425374Sbasabi error++; 8435374Sbasabi (void) fprintf(stderr, 8445374Sbasabi gettext("%s: " 8455374Sbasabi "Failed to set " 8465374Sbasabi "acl entries " 8475374Sbasabi "on %s\n"), cmd, 8485374Sbasabi target); 8491465Smarks acl_free(s1acl); 8501465Smarks s1acl = NULL; 8510Sstevel@tonic-gate /* 8520Sstevel@tonic-gate * else: silent and 8530Sstevel@tonic-gate * continue 8540Sstevel@tonic-gate */ 8550Sstevel@tonic-gate } 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate if (fstat(fi, &s1) < 0) { 8600Sstevel@tonic-gate (void) fprintf(stderr, 8610Sstevel@tonic-gate gettext("%s: cannot access %s\n"), 8620Sstevel@tonic-gate cmd, source); 8630Sstevel@tonic-gate return (1); 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate if (IDENTICAL(s1, s2)) { 8660Sstevel@tonic-gate (void) fprintf(stderr, 8670Sstevel@tonic-gate gettext( 8680Sstevel@tonic-gate "%s: %s and %s are identical\n"), 8690Sstevel@tonic-gate cmd, source, target); 8700Sstevel@tonic-gate return (1); 8710Sstevel@tonic-gate } 8720Sstevel@tonic-gate 8735331Samw if (writefile(fi, fo, source, target, NULL, 8745331Samw NULL, &s1, &s2) != 0) { 8750Sstevel@tonic-gate return (1); 8760Sstevel@tonic-gate } 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate (void) close(fi); 8790Sstevel@tonic-gate if (close(fo) < 0) { 8800Sstevel@tonic-gate Perror2(target, "write"); 8810Sstevel@tonic-gate return (1); 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate } 8845331Samw /* Copy regular extended attributes */ 8855331Samw if (pflg || atflg || mve || saflg) { 8860Sstevel@tonic-gate attret = copyattributes(source, target); 8870Sstevel@tonic-gate if (attret != 0 && !attrsilent) { 8880Sstevel@tonic-gate (void) fprintf(stderr, gettext( 8894774Sas145665 "%s: Failed to preserve" 8904774Sas145665 " extended attributes of file" 8914774Sas145665 " %s\n"), cmd, source); 8920Sstevel@tonic-gate } 8935331Samw /* Copy extended system attributes */ 8945374Sbasabi if (pflg || mve || saflg) 8955331Samw sattret = copy_sysattr(source, target); 8960Sstevel@tonic-gate if (mve && attret != 0) { 8970Sstevel@tonic-gate (void) unlink(target); 8980Sstevel@tonic-gate return (1); 8990Sstevel@tonic-gate } 9005331Samw if (attrsilent) { 9010Sstevel@tonic-gate attret = 0; 9025331Samw } 9030Sstevel@tonic-gate } 9040Sstevel@tonic-gate 9050Sstevel@tonic-gate /* 9060Sstevel@tonic-gate * XPG4: the write system call will clear setgid 9070Sstevel@tonic-gate * and setuid bits, so set them again. 9080Sstevel@tonic-gate */ 9090Sstevel@tonic-gate if (pflg || mve) { 9100Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1), 9110Sstevel@tonic-gate FMODE(s1))) > 0) 9120Sstevel@tonic-gate return (1); 9131231Smarks /* 9141231Smarks * Reapply ACL, since chmod may have 9151231Smarks * altered ACL 9161231Smarks */ 9171231Smarks if (s1acl != NULL) { 9181231Smarks if ((acl_set(target, s1acl)) < 0) { 9195374Sbasabi error++; 9205374Sbasabi (void) fprintf(stderr, 9215374Sbasabi gettext("%s: Failed to " 9225374Sbasabi "set acl entries " 9235374Sbasabi "on %s\n"), cmd, target); 9241231Smarks /* 9251231Smarks * else: silent and 9261231Smarks * continue 9271231Smarks */ 9281231Smarks } 9291231Smarks } 9300Sstevel@tonic-gate if ((ret = chg_time(target, s1)) > 0) 9310Sstevel@tonic-gate return (1); 9320Sstevel@tonic-gate } 9330Sstevel@tonic-gate if (cpy) { 9345374Sbasabi if (error != 0 || attret != 0 || sattret != 0) 9350Sstevel@tonic-gate return (1); 9360Sstevel@tonic-gate return (0); 9370Sstevel@tonic-gate } 9380Sstevel@tonic-gate goto cleanup; 9390Sstevel@tonic-gate } 9400Sstevel@tonic-gate (void) fprintf(stderr, 9410Sstevel@tonic-gate gettext("%s: %s: unknown file type 0x%x\n"), cmd, 9424774Sas145665 source, (s1.st_mode & S_IFMT)); 9430Sstevel@tonic-gate return (1); 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate cleanup: 9460Sstevel@tonic-gate if (unlink(source) < 0) { 9470Sstevel@tonic-gate (void) unlink(target); 9480Sstevel@tonic-gate (void) fprintf(stderr, 9490Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 9500Sstevel@tonic-gate cmd, source); 9510Sstevel@tonic-gate perror(""); 9520Sstevel@tonic-gate return (1); 9530Sstevel@tonic-gate } 9545374Sbasabi if (error != 0 || attret != 0 || sattret != 0) 9555374Sbasabi return (1); 9560Sstevel@tonic-gate return (ret); 9570Sstevel@tonic-gate } 9580Sstevel@tonic-gate /*NOTREACHED*/ 959212Scf46844 return (ret); 9600Sstevel@tonic-gate } 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate /* 9630Sstevel@tonic-gate * create_tnode() 9640Sstevel@tonic-gate * 9650Sstevel@tonic-gate * Create a node for use with the search tree which contains the 9660Sstevel@tonic-gate * inode information (device id and inode number). 9670Sstevel@tonic-gate * 9680Sstevel@tonic-gate * Input 9690Sstevel@tonic-gate * dev - device id 9700Sstevel@tonic-gate * ino - inode number 9710Sstevel@tonic-gate * 9720Sstevel@tonic-gate * Output 9730Sstevel@tonic-gate * tnode - NULL on error, otherwise returns a tnode structure 9740Sstevel@tonic-gate * which contains the input device id and inode number. 9750Sstevel@tonic-gate */ 9760Sstevel@tonic-gate static tree_node_t * 9770Sstevel@tonic-gate create_tnode(dev_t dev, ino_t ino) 9780Sstevel@tonic-gate { 9790Sstevel@tonic-gate tree_node_t *tnode; 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) { 9820Sstevel@tonic-gate tnode->node_dev = dev; 9830Sstevel@tonic-gate tnode->node_ino = ino; 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate return (tnode); 9870Sstevel@tonic-gate } 9880Sstevel@tonic-gate 9890Sstevel@tonic-gate static int 9900Sstevel@tonic-gate chkfiles(char *source, char **to) 9910Sstevel@tonic-gate { 9920Sstevel@tonic-gate char *buf = (char *)NULL; 9930Sstevel@tonic-gate int (*statf)() = (cpy && 9944774Sas145665 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat; 9950Sstevel@tonic-gate char *target = *to; 996789Sahrens int error; 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate /* 9990Sstevel@tonic-gate * Make sure source file exists. 10000Sstevel@tonic-gate */ 10010Sstevel@tonic-gate if ((*statf)(source, &s1) < 0) { 10020Sstevel@tonic-gate /* 10030Sstevel@tonic-gate * Keep the old error message except when someone tries to 10040Sstevel@tonic-gate * mv/cp/ln a symbolic link that has a trailing slash and 10050Sstevel@tonic-gate * points to a file. 10060Sstevel@tonic-gate */ 10070Sstevel@tonic-gate if (errno == ENOTDIR) 10080Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", cmd, source, 10090Sstevel@tonic-gate strerror(errno)); 10100Sstevel@tonic-gate else 10110Sstevel@tonic-gate (void) fprintf(stderr, 10120Sstevel@tonic-gate gettext("%s: cannot access %s\n"), cmd, source); 10130Sstevel@tonic-gate return (1); 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate /* 10179324SRenaud.Manus@Sun.COM * Get ACL info: don't bother with ln or cp/mv'ing symlinks 10180Sstevel@tonic-gate */ 10199324SRenaud.Manus@Sun.COM if (!lnk && !ISLNK(s1)) { 1020789Sahrens if (s1acl != NULL) { 1021789Sahrens acl_free(s1acl); 1022789Sahrens s1acl = NULL; 10230Sstevel@tonic-gate } 1024789Sahrens if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) { 1025789Sahrens (void) fprintf(stderr, 1026789Sahrens "%s: failed to get acl entries: %s\n", source, 1027789Sahrens acl_strerror(error)); 1028789Sahrens return (1); 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate /* else: just permission bits */ 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate /* 10340Sstevel@tonic-gate * If stat fails, then the target doesn't exist, 10350Sstevel@tonic-gate * we will create a new target with default file type of regular. 10360Sstevel@tonic-gate */ 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate FTYPE(s2) = S_IFREG; 10390Sstevel@tonic-gate targetexists = 0; 10400Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) { 10410Sstevel@tonic-gate if (ISLNK(s2)) 10420Sstevel@tonic-gate (void) stat(target, &s2); 10430Sstevel@tonic-gate /* 10440Sstevel@tonic-gate * If target is a directory, 10450Sstevel@tonic-gate * make complete name of new file 10460Sstevel@tonic-gate * within that directory. 10470Sstevel@tonic-gate */ 10480Sstevel@tonic-gate if (ISDIR(s2)) { 10490Sstevel@tonic-gate size_t len; 10500Sstevel@tonic-gate 10510Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4; 10520Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) { 10530Sstevel@tonic-gate (void) fprintf(stderr, 10540Sstevel@tonic-gate gettext("%s: Insufficient memory to " 10554774Sas145665 "%s %s\n "), cmd, cmd, source); 10560Sstevel@tonic-gate exit(3); 10570Sstevel@tonic-gate } 10580Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s", 10590Sstevel@tonic-gate target, dname(source)); 10600Sstevel@tonic-gate *to = target = buf; 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) { 10640Sstevel@tonic-gate int overwrite = FALSE; 10650Sstevel@tonic-gate int override = FALSE; 10660Sstevel@tonic-gate 10670Sstevel@tonic-gate targetexists++; 10680Sstevel@tonic-gate if (cpy || mve) { 10690Sstevel@tonic-gate /* 10700Sstevel@tonic-gate * For cp and mv, it is an error if the 10710Sstevel@tonic-gate * source and target are the same file. 10720Sstevel@tonic-gate * Check for the same inode and file 10730Sstevel@tonic-gate * system, but don't check for the same 10740Sstevel@tonic-gate * absolute pathname because it is an 10750Sstevel@tonic-gate * error when the source and target are 10760Sstevel@tonic-gate * hard links to the same file. 10770Sstevel@tonic-gate */ 10780Sstevel@tonic-gate if (IDENTICAL(s1, s2)) { 10790Sstevel@tonic-gate (void) fprintf(stderr, 10800Sstevel@tonic-gate gettext( 10810Sstevel@tonic-gate "%s: %s and %s are identical\n"), 10820Sstevel@tonic-gate cmd, source, target); 10830Sstevel@tonic-gate if (buf != NULL) 10840Sstevel@tonic-gate free(buf); 10850Sstevel@tonic-gate return (1); 10860Sstevel@tonic-gate } 10870Sstevel@tonic-gate } 10880Sstevel@tonic-gate if (lnk) { 10890Sstevel@tonic-gate /* 10900Sstevel@tonic-gate * For ln, it is an error if the source and 10910Sstevel@tonic-gate * target are identical files (same inode, 10920Sstevel@tonic-gate * same file system, and filenames resolve 10930Sstevel@tonic-gate * to same absolute pathname). 10940Sstevel@tonic-gate */ 10950Sstevel@tonic-gate if (!chk_different(source, target)) { 10960Sstevel@tonic-gate if (buf != NULL) 10970Sstevel@tonic-gate free(buf); 10980Sstevel@tonic-gate return (1); 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate } 11010Sstevel@tonic-gate if (lnk && !silent) { 11020Sstevel@tonic-gate (void) fprintf(stderr, 11030Sstevel@tonic-gate gettext("%s: %s: File exists\n"), 11040Sstevel@tonic-gate cmd, target); 11050Sstevel@tonic-gate if (buf != NULL) 11060Sstevel@tonic-gate free(buf); 11070Sstevel@tonic-gate return (1); 11080Sstevel@tonic-gate } 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate /* 11110Sstevel@tonic-gate * overwrite: 11120Sstevel@tonic-gate * If the user does not have access to 11130Sstevel@tonic-gate * the target, ask ----if it is not 11140Sstevel@tonic-gate * silent and user invoked command 11150Sstevel@tonic-gate * interactively. 11160Sstevel@tonic-gate * 11170Sstevel@tonic-gate * override: 11180Sstevel@tonic-gate * If not silent, and stdin is a terminal, and 11190Sstevel@tonic-gate * there's no write access, and the file isn't a 11200Sstevel@tonic-gate * symbolic link, ask for permission. 11210Sstevel@tonic-gate * 11220Sstevel@tonic-gate * XPG4: both overwrite and override: 11230Sstevel@tonic-gate * ask only one question. 11240Sstevel@tonic-gate * 11250Sstevel@tonic-gate * TRANSLATION_NOTE - The following messages will 11260Sstevel@tonic-gate * contain the first character of the strings for 11270Sstevel@tonic-gate * "yes" and "no" defined in the file 11280Sstevel@tonic-gate * "nl_langinfo.po". After substitution, the 11290Sstevel@tonic-gate * message will appear as follows: 11300Sstevel@tonic-gate * <cmd>: overwrite <filename> (y/n)? 11310Sstevel@tonic-gate * where <cmd> is the name of the command 11320Sstevel@tonic-gate * (cp, mv) and <filename> is the destination file 11330Sstevel@tonic-gate */ 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate 11360Sstevel@tonic-gate overwrite = iflg && !silent && use_stdin(); 11370Sstevel@tonic-gate override = !cpy && (access(target, 2) < 0) && 11380Sstevel@tonic-gate !silent && use_stdin() && !ISLNK(s2); 11390Sstevel@tonic-gate 11403819Sas145665 if (overwrite && override) { 11410Sstevel@tonic-gate (void) fprintf(stderr, 11420Sstevel@tonic-gate gettext("%s: overwrite %s and override " 11430Sstevel@tonic-gate "protection %o (%s/%s)? "), cmd, target, 11444774Sas145665 FMODE(s2) & MODEBITS, yesstr, nostr); 11454774Sas145665 if (yes() == 0) { 11463819Sas145665 if (buf != NULL) 11473819Sas145665 free(buf); 11483819Sas145665 return (2); 11493819Sas145665 } 11503819Sas145665 } else if (overwrite && ISREG(s2)) { 11510Sstevel@tonic-gate (void) fprintf(stderr, 11520Sstevel@tonic-gate gettext("%s: overwrite %s (%s/%s)? "), 11534774Sas145665 cmd, target, yesstr, nostr); 11544774Sas145665 if (yes() == 0) { 11553819Sas145665 if (buf != NULL) 11563819Sas145665 free(buf); 11573819Sas145665 return (2); 11583819Sas145665 } 11593819Sas145665 } else if (override) { 11600Sstevel@tonic-gate (void) fprintf(stderr, 11610Sstevel@tonic-gate gettext("%s: %s: override protection " 11620Sstevel@tonic-gate /*CSTYLED*/ 11630Sstevel@tonic-gate "%o (%s/%s)? "), 11640Sstevel@tonic-gate /*CSTYLED*/ 11650Sstevel@tonic-gate cmd, target, FMODE(s2) & MODEBITS, 11664774Sas145665 yesstr, nostr); 11674774Sas145665 if (yes() == 0) { 11683819Sas145665 if (buf != NULL) 11693819Sas145665 free(buf); 11703819Sas145665 return (2); 11710Sstevel@tonic-gate } 11720Sstevel@tonic-gate } 11733819Sas145665 11740Sstevel@tonic-gate if (lnk && unlink(target) < 0) { 11750Sstevel@tonic-gate (void) fprintf(stderr, 11760Sstevel@tonic-gate gettext("%s: cannot unlink %s: "), 11770Sstevel@tonic-gate cmd, target); 11780Sstevel@tonic-gate perror(""); 11790Sstevel@tonic-gate return (1); 11800Sstevel@tonic-gate } 11810Sstevel@tonic-gate } 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate return (0); 11840Sstevel@tonic-gate } 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate /* 11870Sstevel@tonic-gate * check whether source and target are different 11880Sstevel@tonic-gate * return 1 when they are different 11890Sstevel@tonic-gate * return 0 when they are identical, or when unable to resolve a pathname 11900Sstevel@tonic-gate */ 11910Sstevel@tonic-gate static int 11920Sstevel@tonic-gate chk_different(char *source, char *target) 11930Sstevel@tonic-gate { 11940Sstevel@tonic-gate char rtarget[PATH_MAX], rsource[PATH_MAX]; 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate if (IDENTICAL(s1, s2)) { 11970Sstevel@tonic-gate /* 11980Sstevel@tonic-gate * IDENTICAL will be true for hard links, therefore 11990Sstevel@tonic-gate * check whether the filenames are different 12000Sstevel@tonic-gate */ 12010Sstevel@tonic-gate if ((getrealpath(source, rsource) == 0) || 12020Sstevel@tonic-gate (getrealpath(target, rtarget) == 0)) { 12030Sstevel@tonic-gate return (0); 12040Sstevel@tonic-gate } 12050Sstevel@tonic-gate if (strncmp(rsource, rtarget, PATH_MAX) == 0) { 12060Sstevel@tonic-gate (void) fprintf(stderr, gettext( 12070Sstevel@tonic-gate "%s: %s and %s are identical\n"), 12080Sstevel@tonic-gate cmd, source, target); 12090Sstevel@tonic-gate return (0); 12100Sstevel@tonic-gate } 12110Sstevel@tonic-gate } 12120Sstevel@tonic-gate return (1); 12130Sstevel@tonic-gate } 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate /* 12160Sstevel@tonic-gate * get real path (resolved absolute pathname) 12170Sstevel@tonic-gate * return 1 on success, 0 on failure 12180Sstevel@tonic-gate */ 12190Sstevel@tonic-gate static int 12200Sstevel@tonic-gate getrealpath(char *path, char *rpath) 12210Sstevel@tonic-gate { 12220Sstevel@tonic-gate if (realpath(path, rpath) == NULL) { 12230Sstevel@tonic-gate int errno_save = errno; 12240Sstevel@tonic-gate (void) fprintf(stderr, gettext( 1225*10294Sdannywebster@googlemail.com "%s: cannot resolve path %s: "), cmd, path); 12260Sstevel@tonic-gate errno = errno_save; 12270Sstevel@tonic-gate perror(""); 12280Sstevel@tonic-gate return (0); 12290Sstevel@tonic-gate } 12300Sstevel@tonic-gate return (1); 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate 12330Sstevel@tonic-gate static int 12340Sstevel@tonic-gate rcopy(char *from, char *to) 12350Sstevel@tonic-gate { 12360Sstevel@tonic-gate DIR *fold = opendir(from); 12370Sstevel@tonic-gate struct dirent *dp; 12380Sstevel@tonic-gate struct stat statb, s1save; 12390Sstevel@tonic-gate int errs = 0; 12400Sstevel@tonic-gate char fromname[PATH_MAX]; 12410Sstevel@tonic-gate 12420Sstevel@tonic-gate if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) { 12430Sstevel@tonic-gate Perror(from); 12440Sstevel@tonic-gate return (1); 12450Sstevel@tonic-gate } 12460Sstevel@tonic-gate if (pflg || mve) { 12470Sstevel@tonic-gate /* 12480Sstevel@tonic-gate * Save s1 (stat information for source dir) so that 12490Sstevel@tonic-gate * mod and access times can be reserved during "cp -p" 12500Sstevel@tonic-gate * or mv, since s1 gets overwritten. 12510Sstevel@tonic-gate */ 12520Sstevel@tonic-gate s1save = s1; 12530Sstevel@tonic-gate } 12540Sstevel@tonic-gate for (;;) { 12550Sstevel@tonic-gate dp = readdir(fold); 12560Sstevel@tonic-gate if (dp == 0) { 12570Sstevel@tonic-gate (void) closedir(fold); 12580Sstevel@tonic-gate if (pflg || mve) 12590Sstevel@tonic-gate return (chg_time(to, s1save) + errs); 12600Sstevel@tonic-gate return (errs); 12610Sstevel@tonic-gate } 12620Sstevel@tonic-gate if (dp->d_ino == 0) 12630Sstevel@tonic-gate continue; 12640Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) || 12650Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0)) 12660Sstevel@tonic-gate continue; 12670Sstevel@tonic-gate if (strlen(from)+1+strlen(dp->d_name) >= 12680Sstevel@tonic-gate sizeof (fromname) - 1) { 12690Sstevel@tonic-gate (void) fprintf(stderr, 12700Sstevel@tonic-gate gettext("%s : %s/%s: Name too long\n"), 12710Sstevel@tonic-gate cmd, from, dp->d_name); 12720Sstevel@tonic-gate errs++; 12730Sstevel@tonic-gate continue; 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate (void) snprintf(fromname, sizeof (fromname), 12760Sstevel@tonic-gate "%s/%s", from, dp->d_name); 12770Sstevel@tonic-gate errs += cpymve(fromname, to); 12780Sstevel@tonic-gate } 12790Sstevel@tonic-gate } 12800Sstevel@tonic-gate 12810Sstevel@tonic-gate static char * 12820Sstevel@tonic-gate dname(char *name) 12830Sstevel@tonic-gate { 12840Sstevel@tonic-gate register char *p; 12850Sstevel@tonic-gate 12860Sstevel@tonic-gate /* 12870Sstevel@tonic-gate * Return just the file name given the complete path. 12880Sstevel@tonic-gate * Like basename(1). 12890Sstevel@tonic-gate */ 12900Sstevel@tonic-gate 12910Sstevel@tonic-gate p = name; 12920Sstevel@tonic-gate 12930Sstevel@tonic-gate /* 12940Sstevel@tonic-gate * While there are characters left, 12950Sstevel@tonic-gate * set name to start after last 12960Sstevel@tonic-gate * delimiter. 12970Sstevel@tonic-gate */ 12980Sstevel@tonic-gate 12990Sstevel@tonic-gate while (*p) 13000Sstevel@tonic-gate if (*p++ == DELIM && *p) 13010Sstevel@tonic-gate name = p; 13020Sstevel@tonic-gate return (name); 13030Sstevel@tonic-gate } 13040Sstevel@tonic-gate 13050Sstevel@tonic-gate static void 13060Sstevel@tonic-gate usage(void) 13070Sstevel@tonic-gate { 13080Sstevel@tonic-gate /* 13090Sstevel@tonic-gate * Display usage message. 13100Sstevel@tonic-gate */ 13110Sstevel@tonic-gate 13120Sstevel@tonic-gate if (mve) { 13130Sstevel@tonic-gate (void) fprintf(stderr, gettext( 13140Sstevel@tonic-gate "Usage: mv [-f] [-i] f1 f2\n" 13150Sstevel@tonic-gate " mv [-f] [-i] f1 ... fn d1\n" 13160Sstevel@tonic-gate " mv [-f] [-i] d1 d2\n")); 13170Sstevel@tonic-gate } else if (lnk) { 13184774Sas145665 #ifdef XPG4 13190Sstevel@tonic-gate (void) fprintf(stderr, gettext( 13200Sstevel@tonic-gate "Usage: ln [-f] [-s] f1 [f2]\n" 13210Sstevel@tonic-gate " ln [-f] [-s] f1 ... fn d1\n" 13220Sstevel@tonic-gate " ln [-f] -s d1 d2\n")); 13230Sstevel@tonic-gate #else 13244774Sas145665 (void) fprintf(stderr, gettext( 13250Sstevel@tonic-gate "Usage: ln [-f] [-n] [-s] f1 [f2]\n" 13260Sstevel@tonic-gate " ln [-f] [-n] [-s] f1 ... fn d1\n" 13270Sstevel@tonic-gate " ln [-f] [-n] -s d1 d2\n")); 13280Sstevel@tonic-gate #endif 13290Sstevel@tonic-gate } else if (cpy) { 13300Sstevel@tonic-gate (void) fprintf(stderr, gettext( 13315331Samw "Usage: cp [-f] [-i] [-p] [-@] [-/] f1 f2\n" 13325331Samw " cp [-f] [-i] [-p] [-@] [-/] f1 ... fn d1\n" 13335331Samw " cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] [-/] " 13340Sstevel@tonic-gate "d1 ... dn-1 dn\n")); 13350Sstevel@tonic-gate } 13360Sstevel@tonic-gate exit(2); 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate 13390Sstevel@tonic-gate /* 13400Sstevel@tonic-gate * chg_time() 13410Sstevel@tonic-gate * 13420Sstevel@tonic-gate * Try to preserve modification and access time. 13430Sstevel@tonic-gate * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version, 13440Sstevel@tonic-gate * don't report a utime() failure. 13450Sstevel@tonic-gate * If this is the XPG4 version and utime fails, if 1) pflg is set (cp -p) 13460Sstevel@tonic-gate * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero 13470Sstevel@tonic-gate * exit status only if pflg is set. 13480Sstevel@tonic-gate * utimes(2) is being used to achieve granularity in 13490Sstevel@tonic-gate * microseconds while setting file times. 13500Sstevel@tonic-gate */ 13510Sstevel@tonic-gate static int 13520Sstevel@tonic-gate chg_time(char *to, struct stat ss) 13530Sstevel@tonic-gate { 13540Sstevel@tonic-gate struct timeval times[2]; 13550Sstevel@tonic-gate int rc; 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate timestruc_to_timeval(&ss.st_atim, times); 13580Sstevel@tonic-gate timestruc_to_timeval(&ss.st_mtim, times + 1); 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate rc = utimes(to, times); 13610Sstevel@tonic-gate #ifdef XPG4 13620Sstevel@tonic-gate if ((pflg || mve) && rc != 0) { 13630Sstevel@tonic-gate (void) fprintf(stderr, 13640Sstevel@tonic-gate gettext("%s: cannot set times for %s: "), cmd, to); 13650Sstevel@tonic-gate perror(""); 13660Sstevel@tonic-gate if (pflg) 13670Sstevel@tonic-gate return (1); 13680Sstevel@tonic-gate } 13690Sstevel@tonic-gate #endif 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate return (0); 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate } 13740Sstevel@tonic-gate 13750Sstevel@tonic-gate /* 13760Sstevel@tonic-gate * chg_mode() 13770Sstevel@tonic-gate * 13780Sstevel@tonic-gate * This function is called upon "cp -p" or mv across filesystems. 13790Sstevel@tonic-gate * 13800Sstevel@tonic-gate * Try to preserve the owner and group id. If chown() fails, 13810Sstevel@tonic-gate * only print a diagnostic message if doing a mv in the XPG4 version; 13820Sstevel@tonic-gate * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear 13830Sstevel@tonic-gate * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a 13840Sstevel@tonic-gate * non-zero exit status because this is a security violation. 13850Sstevel@tonic-gate * Try to preserve permissions. 13860Sstevel@tonic-gate * If this is the XPG4 version and chmod() fails, print a diagnostic message 13870Sstevel@tonic-gate * and arrange for a non-zero exit status. 13880Sstevel@tonic-gate * If this is the Solaris version and chmod() fails, do not print a 13890Sstevel@tonic-gate * diagnostic message or exit with a non-zero value. 13900Sstevel@tonic-gate */ 13910Sstevel@tonic-gate static int 13920Sstevel@tonic-gate chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode) 13930Sstevel@tonic-gate { 13940Sstevel@tonic-gate int clearflg = 0; /* controls message printed upon chown() error */ 13950Sstevel@tonic-gate 13960Sstevel@tonic-gate if (chown(target, uid, gid) != 0) { 13970Sstevel@tonic-gate #ifdef XPG4 13980Sstevel@tonic-gate if (mve) { 13990Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot change" 14000Sstevel@tonic-gate " owner and group of %s: "), cmd, target); 14010Sstevel@tonic-gate perror(""); 14020Sstevel@tonic-gate } 14030Sstevel@tonic-gate #endif 14040Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) { 14050Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */ 14060Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID; 14070Sstevel@tonic-gate ++clearflg; 14080Sstevel@tonic-gate } 14090Sstevel@tonic-gate } 14100Sstevel@tonic-gate if (chmod(target, mode) != 0) { 14110Sstevel@tonic-gate if (clearflg) { 14120Sstevel@tonic-gate (void) fprintf(stderr, gettext( 14130Sstevel@tonic-gate "%s: cannot clear S_ISUID and S_ISGID bits in" 14140Sstevel@tonic-gate " %s: "), cmd, target); 14150Sstevel@tonic-gate perror(""); 14160Sstevel@tonic-gate /* cp -p should get non-zero exit; mv should not */ 14170Sstevel@tonic-gate if (pflg) 14180Sstevel@tonic-gate return (1); 14190Sstevel@tonic-gate } 14200Sstevel@tonic-gate #ifdef XPG4 14210Sstevel@tonic-gate else { 14220Sstevel@tonic-gate (void) fprintf(stderr, gettext( 14230Sstevel@tonic-gate "%s: cannot set permissions for %s: "), cmd, target); 14240Sstevel@tonic-gate perror(""); 14250Sstevel@tonic-gate /* cp -p should get non-zero exit; mv should not */ 14260Sstevel@tonic-gate if (pflg) 14270Sstevel@tonic-gate return (1); 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate #endif 14300Sstevel@tonic-gate } 14310Sstevel@tonic-gate return (0); 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate } 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate static void 14360Sstevel@tonic-gate Perror(char *s) 14370Sstevel@tonic-gate { 14380Sstevel@tonic-gate char buf[PATH_MAX + 10]; 14390Sstevel@tonic-gate 14400Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s); 14410Sstevel@tonic-gate perror(buf); 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate static void 14450Sstevel@tonic-gate Perror2(char *s1, char *s2) 14460Sstevel@tonic-gate { 14470Sstevel@tonic-gate char buf[PATH_MAX + 20]; 14480Sstevel@tonic-gate 14490Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s: %s", 14500Sstevel@tonic-gate cmd, gettext(s1), gettext(s2)); 14510Sstevel@tonic-gate perror(buf); 14520Sstevel@tonic-gate } 14530Sstevel@tonic-gate 14540Sstevel@tonic-gate /* 14550Sstevel@tonic-gate * used for cp -R and for mv across file systems 14560Sstevel@tonic-gate */ 14570Sstevel@tonic-gate static int 14580Sstevel@tonic-gate copydir(char *source, char *target) 14590Sstevel@tonic-gate { 14600Sstevel@tonic-gate int ret, attret = 0; 14615374Sbasabi int sattret = 0; 14620Sstevel@tonic-gate int pret = 0; /* need separate flag if -p is specified */ 14630Sstevel@tonic-gate mode_t fixmode = (mode_t)0; /* cleanup mode after copy */ 14640Sstevel@tonic-gate struct stat s1save; 1465789Sahrens acl_t *s1acl_save; 14665374Sbasabi int error = 0; 1467789Sahrens 1468789Sahrens s1acl_save = NULL; 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate if (cpy && !rflg) { 14710Sstevel@tonic-gate (void) fprintf(stderr, 14720Sstevel@tonic-gate gettext("%s: %s: is a directory\n"), cmd, source); 14730Sstevel@tonic-gate return (1); 14740Sstevel@tonic-gate } 14750Sstevel@tonic-gate 14760Sstevel@tonic-gate if (stat(target, &s2) < 0) { 14770Sstevel@tonic-gate if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) { 14780Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", cmd); 14790Sstevel@tonic-gate perror(target); 14800Sstevel@tonic-gate return (1); 14810Sstevel@tonic-gate } 14820Sstevel@tonic-gate if (stat(target, &s2) == 0) { 14830Sstevel@tonic-gate fixmode = s2.st_mode; 14840Sstevel@tonic-gate } else { 14850Sstevel@tonic-gate fixmode = s1.st_mode; 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU)); 14880Sstevel@tonic-gate } else if (!(ISDIR(s2))) { 14890Sstevel@tonic-gate (void) fprintf(stderr, 14900Sstevel@tonic-gate gettext("%s: %s: not a directory.\n"), cmd, target); 14910Sstevel@tonic-gate return (1); 14920Sstevel@tonic-gate } 14930Sstevel@tonic-gate if (pflg || mve) { 14940Sstevel@tonic-gate /* 14950Sstevel@tonic-gate * Save s1 (stat information for source dir) and acl info, 14960Sstevel@tonic-gate * if any, so that ownership, modes, times, and acl's can 14970Sstevel@tonic-gate * be reserved during "cp -p" or mv. 14980Sstevel@tonic-gate * s1 gets overwritten when doing the recursive copy. 14990Sstevel@tonic-gate */ 15000Sstevel@tonic-gate s1save = s1; 1501789Sahrens if (s1acl != NULL) { 1502789Sahrens s1acl_save = acl_dup(s1acl); 1503789Sahrens if (s1acl_save == NULL) { 1504789Sahrens (void) fprintf(stderr, gettext("%s: " 1505789Sahrens "Insufficient memory to save acl" 1506789Sahrens " entry\n"), cmd); 1507789Sahrens if (pflg) 1508789Sahrens return (1); 1509789Sahrens 15100Sstevel@tonic-gate } 15114774Sas145665 #ifdef XPG4 15124774Sas145665 else { 15134774Sas145665 (void) fprintf(stderr, gettext("%s: " 15144774Sas145665 "Insufficient memory to save acl" 15154774Sas145665 " entry\n"), cmd); 15164774Sas145665 if (pflg) 15174774Sas145665 return (1); 15184774Sas145665 } 15190Sstevel@tonic-gate #endif 15200Sstevel@tonic-gate } 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate ret = rcopy(source, target); 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate /* 15260Sstevel@tonic-gate * Once we created a directory, go ahead and set 15270Sstevel@tonic-gate * its attributes, e.g. acls and time. The info 15280Sstevel@tonic-gate * may get overwritten if we continue traversing 15290Sstevel@tonic-gate * down the tree. 15300Sstevel@tonic-gate * 15310Sstevel@tonic-gate * ACL for directory 15320Sstevel@tonic-gate */ 15330Sstevel@tonic-gate if (pflg || mve) { 15341231Smarks if ((pret = chg_mode(target, UID(s1save), GID(s1save), 15351231Smarks FMODE(s1save))) == 0) 15361231Smarks pret = chg_time(target, s1save); 15371231Smarks ret += pret; 1538789Sahrens if (s1acl_save != NULL) { 1539789Sahrens if (acl_set(target, s1acl_save) < 0) { 15405374Sbasabi error++; 15410Sstevel@tonic-gate #ifdef XPG4 15420Sstevel@tonic-gate if (pflg || mve) { 15430Sstevel@tonic-gate #else 15440Sstevel@tonic-gate if (pflg) { 15450Sstevel@tonic-gate #endif 15460Sstevel@tonic-gate (void) fprintf(stderr, gettext( 15470Sstevel@tonic-gate "%s: failed to set acl entries " 15480Sstevel@tonic-gate "on %s\n"), cmd, target); 15490Sstevel@tonic-gate if (pflg) { 1550789Sahrens acl_free(s1acl_save); 1551789Sahrens s1acl_save = NULL; 15520Sstevel@tonic-gate ret++; 15530Sstevel@tonic-gate } 15540Sstevel@tonic-gate } 15550Sstevel@tonic-gate /* else: silent and continue */ 15560Sstevel@tonic-gate } 1557789Sahrens acl_free(s1acl_save); 1558789Sahrens s1acl_save = NULL; 15590Sstevel@tonic-gate } 15600Sstevel@tonic-gate } else if (fixmode != (mode_t)0) 15610Sstevel@tonic-gate (void) chmod(target, fixmode & MODEBITS); 15620Sstevel@tonic-gate 15635331Samw if (pflg || atflg || mve || saflg) { 15640Sstevel@tonic-gate attret = copyattributes(source, target); 15650Sstevel@tonic-gate if (!attrsilent && attret != 0) { 15660Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Failed to preserve" 15670Sstevel@tonic-gate " extended attributes of directory" 15680Sstevel@tonic-gate " %s\n"), cmd, source); 15690Sstevel@tonic-gate } else { 15700Sstevel@tonic-gate /* 15710Sstevel@tonic-gate * Otherwise ignore failure. 15720Sstevel@tonic-gate */ 15730Sstevel@tonic-gate attret = 0; 15740Sstevel@tonic-gate } 15755331Samw /* Copy extended system attributes */ 15765331Samw if (pflg || mve || saflg) { 15775374Sbasabi sattret = copy_sysattr(source, target); 15785374Sbasabi if (sattret != 0) { 15795331Samw (void) fprintf(stderr, gettext( 15805331Samw "%s: Failed to preserve " 15815331Samw "extended system attributes " 15825331Samw "of directory %s\n"), cmd, source); 15835331Samw } 15845331Samw } 15850Sstevel@tonic-gate } 15865374Sbasabi if (attret != 0 || sattret != 0 || error != 0) 15875374Sbasabi return (1); 15880Sstevel@tonic-gate return (ret); 15890Sstevel@tonic-gate } 15900Sstevel@tonic-gate 15910Sstevel@tonic-gate static int 15920Sstevel@tonic-gate copyspecial(char *target) 15930Sstevel@tonic-gate { 15940Sstevel@tonic-gate int ret = 0; 15950Sstevel@tonic-gate 15960Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) != 0) { 15970Sstevel@tonic-gate (void) fprintf(stderr, gettext( 15980Sstevel@tonic-gate "cp: cannot create special file %s: "), target); 15990Sstevel@tonic-gate perror(""); 16000Sstevel@tonic-gate return (1); 16010Sstevel@tonic-gate } 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate if (pflg) { 16040Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0) 16050Sstevel@tonic-gate ret = chg_time(target, s1); 16060Sstevel@tonic-gate } 16070Sstevel@tonic-gate 16080Sstevel@tonic-gate return (ret); 16090Sstevel@tonic-gate } 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate static int 16120Sstevel@tonic-gate use_stdin(void) 16130Sstevel@tonic-gate { 16140Sstevel@tonic-gate #ifdef XPG4 16150Sstevel@tonic-gate return (1); 16160Sstevel@tonic-gate #else 16170Sstevel@tonic-gate return (isatty(fileno(stdin))); 16180Sstevel@tonic-gate #endif 16190Sstevel@tonic-gate } 16200Sstevel@tonic-gate 16215331Samw /* Copy non-system extended attributes */ 16225331Samw 16230Sstevel@tonic-gate static int 16240Sstevel@tonic-gate copyattributes(char *source, char *target) 16250Sstevel@tonic-gate { 16260Sstevel@tonic-gate struct dirent *dp; 16270Sstevel@tonic-gate int error = 0; 1628789Sahrens int aclerror; 16290Sstevel@tonic-gate mode_t mode; 16300Sstevel@tonic-gate int clearflg = 0; 1631789Sahrens acl_t *xacl = NULL; 1632789Sahrens acl_t *attrdiracl = NULL; 16330Sstevel@tonic-gate struct timeval times[2]; 16340Sstevel@tonic-gate 16355331Samw 16365331Samw if (pathconf(source, _PC_XATTR_EXISTS) != 1) 16370Sstevel@tonic-gate return (0); 16380Sstevel@tonic-gate 16390Sstevel@tonic-gate if (pathconf(target, _PC_XATTR_ENABLED) != 1) { 16400Sstevel@tonic-gate if (!attrsilent) { 16410Sstevel@tonic-gate (void) fprintf(stderr, 16420Sstevel@tonic-gate gettext( 16430Sstevel@tonic-gate "%s: cannot preserve extended attributes, " 16440Sstevel@tonic-gate "operation not supported on file" 16450Sstevel@tonic-gate " %s\n"), cmd, target); 16460Sstevel@tonic-gate } 16470Sstevel@tonic-gate return (1); 16480Sstevel@tonic-gate } 16495331Samw if (open_source(source) != 0) 16505331Samw return (1); 16515331Samw if (open_target_srctarg_attrdirs(source, target) != 0) 16525331Samw return (1); 16535331Samw if (open_attrdirp(source) != 0) 16545331Samw return (1); 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate if (pflg || mve) { 16570Sstevel@tonic-gate if (fchmod(targetdirfd, attrdir.st_mode) == -1) { 16580Sstevel@tonic-gate if (!attrsilent) { 16590Sstevel@tonic-gate (void) fprintf(stderr, 16604774Sas145665 gettext("%s: failed to set file mode" 16614774Sas145665 " correctly on attribute directory of" 16624774Sas145665 " file %s: "), cmd, target); 16630Sstevel@tonic-gate perror(""); 16640Sstevel@tonic-gate ++error; 16650Sstevel@tonic-gate } 16660Sstevel@tonic-gate } 16670Sstevel@tonic-gate 16680Sstevel@tonic-gate if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) { 16690Sstevel@tonic-gate if (!attrsilent) { 16700Sstevel@tonic-gate (void) fprintf(stderr, 16710Sstevel@tonic-gate gettext("%s: failed to set file" 16720Sstevel@tonic-gate " ownership correctly on attribute" 16730Sstevel@tonic-gate " directory of file %s: "), cmd, target); 16740Sstevel@tonic-gate perror(""); 16750Sstevel@tonic-gate ++error; 16760Sstevel@tonic-gate } 16770Sstevel@tonic-gate } 16780Sstevel@tonic-gate /* 16790Sstevel@tonic-gate * Now that we are the owner we can update st_ctime by calling 16800Sstevel@tonic-gate * futimesat. 16810Sstevel@tonic-gate */ 16820Sstevel@tonic-gate times[0].tv_sec = attrdir.st_atime; 16830Sstevel@tonic-gate times[0].tv_usec = 0; 16840Sstevel@tonic-gate times[1].tv_sec = attrdir.st_mtime; 16850Sstevel@tonic-gate times[1].tv_usec = 0; 16860Sstevel@tonic-gate if (futimesat(targetdirfd, ".", times) < 0) { 16870Sstevel@tonic-gate if (!attrsilent) { 16880Sstevel@tonic-gate (void) fprintf(stderr, 16894774Sas145665 gettext("%s: cannot set attribute times" 16904774Sas145665 " for %s: "), cmd, target); 16910Sstevel@tonic-gate perror(""); 16920Sstevel@tonic-gate ++error; 16930Sstevel@tonic-gate } 16940Sstevel@tonic-gate } 16950Sstevel@tonic-gate 16960Sstevel@tonic-gate /* 16970Sstevel@tonic-gate * Now set owner and group of attribute directory, implies 16980Sstevel@tonic-gate * changing the ACL of the hidden attribute directory first. 16990Sstevel@tonic-gate */ 1700789Sahrens if ((aclerror = facl_get(sourcedirfd, 1701789Sahrens ACL_NO_TRIVIAL, &attrdiracl)) != 0) { 17020Sstevel@tonic-gate if (!attrsilent) { 17030Sstevel@tonic-gate (void) fprintf(stderr, gettext( 17040Sstevel@tonic-gate "%s: failed to get acl entries of" 17050Sstevel@tonic-gate " attribute directory for" 1706789Sahrens " %s : %s\n"), cmd, 1707789Sahrens source, acl_strerror(aclerror)); 17080Sstevel@tonic-gate ++error; 17090Sstevel@tonic-gate } 17100Sstevel@tonic-gate } 1711789Sahrens 1712789Sahrens if (attrdiracl) { 1713789Sahrens if (facl_set(targetdirfd, attrdiracl) != 0) { 17140Sstevel@tonic-gate if (!attrsilent) { 17150Sstevel@tonic-gate (void) fprintf(stderr, gettext( 1716789Sahrens "%s: failed to set acl entries" 1717789Sahrens " on attribute directory " 1718789Sahrens "for %s\n"), cmd, target); 17190Sstevel@tonic-gate ++error; 17200Sstevel@tonic-gate } 1721789Sahrens acl_free(attrdiracl); 1722789Sahrens attrdiracl = NULL; 17230Sstevel@tonic-gate } 17240Sstevel@tonic-gate } 17250Sstevel@tonic-gate } 17260Sstevel@tonic-gate 17275331Samw while ((dp = readdir(srcdirp)) != NULL) { 17285331Samw int ret; 17290Sstevel@tonic-gate 17305331Samw if ((ret = traverse_attrfile(dp, source, target, 1)) == -1) 17315331Samw continue; 17325331Samw else if (ret > 0) { 17330Sstevel@tonic-gate ++error; 17345331Samw goto out; 17350Sstevel@tonic-gate } 17360Sstevel@tonic-gate 17370Sstevel@tonic-gate if (pflg || mve) { 1738789Sahrens if ((aclerror = facl_get(srcattrfd, 1739789Sahrens ACL_NO_TRIVIAL, &xacl)) != 0) { 17400Sstevel@tonic-gate if (!attrsilent) { 17410Sstevel@tonic-gate (void) fprintf(stderr, gettext( 17420Sstevel@tonic-gate "%s: failed to get acl entries of" 17430Sstevel@tonic-gate " attribute %s for" 1744789Sahrens " %s: %s"), cmd, dp->d_name, 1745789Sahrens source, acl_strerror(aclerror)); 17460Sstevel@tonic-gate ++error; 17470Sstevel@tonic-gate } 17480Sstevel@tonic-gate } 17490Sstevel@tonic-gate } 17500Sstevel@tonic-gate 17510Sstevel@tonic-gate /* 17520Sstevel@tonic-gate * preserve ACL 17530Sstevel@tonic-gate */ 1754789Sahrens if ((pflg || mve) && xacl != NULL) { 1755789Sahrens if ((facl_set(targattrfd, xacl)) < 0) { 17560Sstevel@tonic-gate if (!attrsilent) { 17570Sstevel@tonic-gate (void) fprintf(stderr, gettext( 17580Sstevel@tonic-gate "%s: failed to set acl entries on" 17590Sstevel@tonic-gate " attribute %s for" 17600Sstevel@tonic-gate "%s\n"), cmd, dp->d_name, target); 17610Sstevel@tonic-gate ++error; 17620Sstevel@tonic-gate } 1763789Sahrens acl_free(xacl); 1764789Sahrens xacl = NULL; 17650Sstevel@tonic-gate } 17660Sstevel@tonic-gate } 17670Sstevel@tonic-gate 17685331Samw if (writefile(srcattrfd, targattrfd, source, target, 17695331Samw dp->d_name, dp->d_name, &s3, &s4) != 0) { 17700Sstevel@tonic-gate if (!attrsilent) { 17710Sstevel@tonic-gate ++error; 17720Sstevel@tonic-gate } 17730Sstevel@tonic-gate goto next; 17740Sstevel@tonic-gate } 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate if (pflg || mve) { 17770Sstevel@tonic-gate mode = FMODE(s3); 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate if (fchown(targattrfd, UID(s3), GID(s3)) != 0) { 17800Sstevel@tonic-gate if (!attrsilent) { 17810Sstevel@tonic-gate (void) fprintf(stderr, 17820Sstevel@tonic-gate gettext("%s: cannot change" 17830Sstevel@tonic-gate " owner and group of" 17840Sstevel@tonic-gate " attribute %s for" " file" 17850Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target); 17860Sstevel@tonic-gate perror(""); 17870Sstevel@tonic-gate ++error; 17880Sstevel@tonic-gate } 17890Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) { 17900Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */ 17910Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID; 17920Sstevel@tonic-gate ++clearflg; 17930Sstevel@tonic-gate } 17940Sstevel@tonic-gate } 17950Sstevel@tonic-gate /* tv_usec were cleared above */ 17960Sstevel@tonic-gate times[0].tv_sec = s3.st_atime; 17970Sstevel@tonic-gate times[1].tv_sec = s3.st_mtime; 17980Sstevel@tonic-gate if (futimesat(targetdirfd, dp->d_name, times) < 0) { 17990Sstevel@tonic-gate if (!attrsilent) { 18000Sstevel@tonic-gate (void) fprintf(stderr, 18010Sstevel@tonic-gate gettext("%s: cannot set attribute" 18020Sstevel@tonic-gate " times for %s: "), cmd, target); 18030Sstevel@tonic-gate perror(""); 18040Sstevel@tonic-gate ++error; 18050Sstevel@tonic-gate } 18060Sstevel@tonic-gate } 18070Sstevel@tonic-gate if (fchmod(targattrfd, mode) != 0) { 18080Sstevel@tonic-gate if (clearflg) { 18090Sstevel@tonic-gate (void) fprintf(stderr, gettext( 18100Sstevel@tonic-gate "%s: cannot clear S_ISUID and " 18110Sstevel@tonic-gate "S_ISGID bits in attribute %s" 18120Sstevel@tonic-gate " for file" 18130Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target); 18140Sstevel@tonic-gate } else { 18150Sstevel@tonic-gate if (!attrsilent) { 18160Sstevel@tonic-gate (void) fprintf(stderr, 18174774Sas145665 gettext( 18180Sstevel@tonic-gate "%s: cannot set permissions of attribute" 18190Sstevel@tonic-gate " %s for %s: "), cmd, dp->d_name, target); 18200Sstevel@tonic-gate perror(""); 18210Sstevel@tonic-gate ++error; 18220Sstevel@tonic-gate } 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate } 18251231Smarks if (xacl && ((facl_set(targattrfd, xacl)) < 0)) { 18261231Smarks if (!attrsilent) { 18271231Smarks (void) fprintf(stderr, gettext( 18281231Smarks "%s: failed to set acl entries on" 18291231Smarks " attribute %s for" 18301231Smarks "%s\n"), cmd, dp->d_name, target); 18311231Smarks ++error; 18321231Smarks } 18331231Smarks acl_free(xacl); 18341231Smarks xacl = NULL; 18351231Smarks } 18360Sstevel@tonic-gate } 18370Sstevel@tonic-gate next: 1838789Sahrens if (xacl != NULL) { 1839789Sahrens acl_free(xacl); 1840789Sahrens xacl = NULL; 18410Sstevel@tonic-gate } 18420Sstevel@tonic-gate if (srcattrfd != -1) 18430Sstevel@tonic-gate (void) close(srcattrfd); 18440Sstevel@tonic-gate if (targattrfd != -1) 18450Sstevel@tonic-gate (void) close(targattrfd); 18460Sstevel@tonic-gate srcattrfd = targattrfd = -1; 18470Sstevel@tonic-gate } 18480Sstevel@tonic-gate out: 1849789Sahrens if (xacl != NULL) { 1850789Sahrens acl_free(xacl); 1851789Sahrens xacl = NULL; 1852789Sahrens } 1853789Sahrens if (attrdiracl != NULL) { 1854789Sahrens acl_free(attrdiracl); 1855789Sahrens attrdiracl = NULL; 1856789Sahrens } 18575331Samw 18585331Samw if (!saflg && !pflg && !mve) 18595331Samw close_all(); 18605331Samw return (error == 0 ? 0 : 1); 18615331Samw } 18625331Samw 18635331Samw /* Copy extended system attributes from source to target */ 18645331Samw 18655331Samw static int 18665331Samw copy_sysattr(char *source, char *target) 18675331Samw { 18685331Samw struct dirent *dp; 18695331Samw nvlist_t *response; 18705331Samw int error = 0; 18715557Sbasabi int target_sa_support = 0; 18725331Samw 18735331Samw if (sysattr_support(source, _PC_SATTR_EXISTS) != 1) 18745331Samw return (0); 18755331Samw 18765331Samw if (open_source(source) != 0) 18775331Samw return (1); 18785331Samw 18795331Samw /* 18805331Samw * Gets non default extended system attributes from the 18815331Samw * source file to copy to the target. The target has 18825331Samw * the defaults set when its created and thus no need 18835331Samw * to copy the defaults. 18845331Samw */ 18855331Samw response = sysattr_list(cmd, srcfd, source); 18865331Samw 18875557Sbasabi if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) { 18885557Sbasabi if (response != NULL) { 18895557Sbasabi (void) fprintf(stderr, 18905557Sbasabi gettext( 18915557Sbasabi "%s: cannot preserve extended system " 18925557Sbasabi "attribute, operation not supported on file" 18935557Sbasabi " %s\n"), cmd, target); 18945331Samw error++; 18955331Samw goto out; 18965331Samw } 18975331Samw } else { 18985557Sbasabi target_sa_support = 1; 18995331Samw } 19005331Samw 19015557Sbasabi if (target_sa_support) { 19025557Sbasabi if (srcdirp == NULL) { 19035557Sbasabi if (open_target_srctarg_attrdirs(source, 19045557Sbasabi target) != 0) { 19055557Sbasabi error++; 19065557Sbasabi goto out; 19075557Sbasabi } 19085557Sbasabi if (open_attrdirp(source) != 0) { 19095557Sbasabi error++; 19105557Sbasabi goto out; 19115557Sbasabi } 19125557Sbasabi } else { 19135557Sbasabi rewind_attrdir(srcdirp); 19145331Samw } 19155557Sbasabi while ((dp = readdir(srcdirp)) != NULL) { 19165557Sbasabi nvlist_t *res; 19175557Sbasabi int ret; 19185331Samw 19195557Sbasabi if ((ret = traverse_attrfile(dp, source, target, 19205557Sbasabi 0)) == -1) 19215557Sbasabi continue; 19225557Sbasabi else if (ret > 0) { 19235557Sbasabi ++error; 19245557Sbasabi goto out; 19255331Samw } 19265557Sbasabi /* 19275557Sbasabi * Gets non default extended system attributes from the 19285557Sbasabi * attribute file to copy to the target. The target has 19295557Sbasabi * the defaults set when its created and thus no need 19305557Sbasabi * to copy the defaults. 19315557Sbasabi */ 19325557Sbasabi if (dp->d_name != NULL) { 19335557Sbasabi res = sysattr_list(cmd, srcattrfd, dp->d_name); 19345557Sbasabi if (res == NULL) 19355557Sbasabi goto next; 19365331Samw 19375331Samw /* 19385331Samw * Copy non default extended system attributes of named 19395331Samw * attribute file. 19405331Samw */ 19415557Sbasabi if (fsetattr(targattrfd, 19425557Sbasabi XATTR_VIEW_READWRITE, res) != 0) { 19435557Sbasabi ++error; 19445557Sbasabi (void) fprintf(stderr, gettext("%s: " 19455557Sbasabi "Failed to copy extended system " 19465557Sbasabi "attributes from attribute file " 19475557Sbasabi "%s of %s to %s\n"), cmd, 19485557Sbasabi dp->d_name, source, target); 19495557Sbasabi } 19505331Samw } 19515331Samw next: 19525557Sbasabi if (srcattrfd != -1) 19535557Sbasabi (void) close(srcattrfd); 19545557Sbasabi if (targattrfd != -1) 19555557Sbasabi (void) close(targattrfd); 19565557Sbasabi srcattrfd = targattrfd = -1; 19575557Sbasabi if (res != NULL) 19585557Sbasabi nvlist_free(res); 19595557Sbasabi } 19605331Samw } 19615331Samw /* Copy source file non default extended system attributes to target */ 19625557Sbasabi if (target_sa_support && (response != NULL) && 19635331Samw (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) { 19645331Samw ++error; 19655374Sbasabi (void) fprintf(stderr, gettext("%s: Failed to " 19665374Sbasabi "copy extended system attributes from " 19675374Sbasabi "%s to %s\n"), cmd, source, target); 19685331Samw } 19695331Samw out: 19705331Samw if (response != NULL) 19715331Samw nvlist_free(response); 19725331Samw close_all(); 19730Sstevel@tonic-gate return (error == 0 ? 0 : 1); 19740Sstevel@tonic-gate } 19750Sstevel@tonic-gate 19760Sstevel@tonic-gate /* 19770Sstevel@tonic-gate * nanoseconds are rounded off to microseconds by flooring. 19780Sstevel@tonic-gate */ 19790Sstevel@tonic-gate static void 19800Sstevel@tonic-gate timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 19810Sstevel@tonic-gate { 19820Sstevel@tonic-gate tv->tv_sec = ts->tv_sec; 19830Sstevel@tonic-gate tv->tv_usec = ts->tv_nsec / 1000; 19840Sstevel@tonic-gate } 19855331Samw 19865331Samw /* Open the source file */ 19875331Samw 19885331Samw int 19895331Samw open_source(char *src) 19905331Samw { 19915331Samw int error = 0; 19925331Samw 19935331Samw srcfd = -1; 19945331Samw if ((srcfd = open(src, O_RDONLY)) == -1) { 19955331Samw if (pflg && attrsilent) { 19965331Samw error++; 19975331Samw goto out; 19985331Samw } 19995331Samw if (!attrsilent) { 20005331Samw (void) fprintf(stderr, 20015331Samw gettext("%s: cannot open file" 20025331Samw " %s: "), cmd, src); 20035331Samw perror(""); 20045331Samw } 20055331Samw ++error; 20065331Samw } 20075331Samw out: 20085331Samw if (error) 20095331Samw close_all(); 20105331Samw return (error == 0 ? 0 : 1); 20115331Samw } 20125331Samw 20135331Samw /* Open source attribute dir, target and target attribute dir. */ 20145331Samw 20155331Samw int 20165331Samw open_target_srctarg_attrdirs(char *src, char *targ) 20175331Samw { 20185331Samw int error = 0; 20195331Samw 20205331Samw targfd = sourcedirfd = targetdirfd = -1; 20215331Samw 20225331Samw if ((targfd = open(targ, O_RDONLY)) == -1) { 20235331Samw if (pflg && attrsilent) { 20245331Samw error++; 20255331Samw goto out; 20265331Samw } 20275331Samw if (!attrsilent) { 20285331Samw (void) fprintf(stderr, 20295331Samw gettext("%s: cannot open file" 20305331Samw " %s: "), cmd, targ); 20315331Samw perror(""); 20325331Samw } 20335331Samw ++error; 20345331Samw goto out; 20355331Samw } 20365331Samw 20375331Samw if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { 20385331Samw if (pflg && attrsilent) { 20395331Samw error++; 20405331Samw goto out; 20415331Samw } 20425331Samw if (!attrsilent) { 20435331Samw (void) fprintf(stderr, 20445331Samw gettext("%s: cannot open attribute" 20455331Samw " directory for %s: "), cmd, src); 20465331Samw perror(""); 20475331Samw } 20485331Samw ++error; 20495331Samw goto out; 20505331Samw } 20515331Samw 20525331Samw if (fstat(sourcedirfd, &attrdir) == -1) { 20535331Samw if (pflg && attrsilent) { 20545331Samw error++; 20555331Samw goto out; 20565331Samw } 20575331Samw 20585331Samw if (!attrsilent) { 20595331Samw (void) fprintf(stderr, 20605331Samw gettext("%s: could not retrieve stat" 20615331Samw " information for attribute directory" 20625331Samw "of file %s: "), cmd, src); 20635331Samw perror(""); 20645331Samw } 20655331Samw ++error; 20665331Samw goto out; 20675331Samw } 20685331Samw if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) { 20695331Samw if (pflg && attrsilent) { 20705331Samw error++; 20715331Samw goto out; 20725331Samw } 20735331Samw if (!attrsilent) { 20745331Samw (void) fprintf(stderr, 20755331Samw gettext("%s: cannot open attribute" 20765331Samw " directory for %s: "), cmd, targ); 20775331Samw perror(""); 20785331Samw } 20795331Samw ++error; 20805331Samw } 20815331Samw out: 20825331Samw if (error) 20835331Samw close_all(); 20845331Samw return (error == 0 ? 0 : 1); 20855331Samw } 20865331Samw 20875331Samw int 20885331Samw open_attrdirp(char *source) 20895331Samw { 20905331Samw int tmpfd = -1; 20915331Samw int error = 0; 20925331Samw 20935331Samw /* 20945331Samw * dup sourcedirfd for use by fdopendir(). 20955331Samw * fdopendir will take ownership of given fd and will close 20965331Samw * it when closedir() is called. 20975331Samw */ 20985331Samw 20995331Samw if ((tmpfd = dup(sourcedirfd)) == -1) { 21005331Samw if (pflg && attrsilent) { 21015331Samw error++; 21025331Samw goto out; 21035331Samw } 21045331Samw if (!attrsilent) { 21055331Samw (void) fprintf(stderr, 21065331Samw gettext( 21075331Samw "%s: unable to dup attribute directory" 21085331Samw " file descriptor for %s: "), cmd, source); 21095331Samw perror(""); 21105331Samw ++error; 21115331Samw } 21125331Samw goto out; 21135331Samw } 21145331Samw if ((srcdirp = fdopendir(tmpfd)) == NULL) { 21155331Samw if (pflg && attrsilent) { 21165331Samw error++; 21175331Samw goto out; 21185331Samw } 21195331Samw if (!attrsilent) { 21205331Samw (void) fprintf(stderr, 21215331Samw gettext("%s: failed to open attribute" 21225331Samw " directory for %s: "), cmd, source); 21235331Samw perror(""); 21245331Samw ++error; 21255331Samw } 21265331Samw } 21275331Samw out: 21285331Samw if (error) 21295331Samw close_all(); 21305331Samw return (error == 0 ? 0 : 1); 21315331Samw } 21325331Samw 21335331Samw /* Skips through ., .., and system attribute 'view' files */ 21345331Samw int 21355331Samw traverse_attrfile(struct dirent *dp, char *source, char *target, int first) 21365331Samw { 21375331Samw int error = 0; 21385331Samw 21396091Sbasabi srcattrfd = targattrfd = -1; 21406091Sbasabi 21415331Samw if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 21425331Samw (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 21435331Samw dp->d_name[2] == '\0') || 21445331Samw (sysattr_type(dp->d_name) == _RO_SATTR) || 21455331Samw (sysattr_type(dp->d_name) == _RW_SATTR)) 21465331Samw return (-1); 21475331Samw 21485331Samw if ((srcattrfd = openat(sourcedirfd, dp->d_name, 21495331Samw O_RDONLY)) == -1) { 21505331Samw if (!attrsilent) { 21515331Samw (void) fprintf(stderr, 21525331Samw gettext("%s: cannot open attribute %s on" 21535331Samw " file %s: "), cmd, dp->d_name, source); 21545331Samw perror(""); 21555331Samw ++error; 21565331Samw goto out; 21575331Samw } 21585331Samw } 21595331Samw 21605331Samw if (fstat(srcattrfd, &s3) < 0) { 21615331Samw if (!attrsilent) { 21625331Samw (void) fprintf(stderr, 21635331Samw gettext("%s: could not stat attribute" 21645331Samw " %s on file" 21655331Samw " %s: "), cmd, dp->d_name, source); 21665331Samw perror(""); 21675331Samw ++error; 21685331Samw } 21695331Samw goto out; 21705331Samw } 21715331Samw 21725331Samw if (first) { 21735331Samw (void) unlinkat(targetdirfd, dp->d_name, 0); 21745500Sbasabi if ((targattrfd = openat(targetdirfd, dp->d_name, 21755500Sbasabi O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) { 21765500Sbasabi if (!attrsilent) { 21775500Sbasabi (void) fprintf(stderr, 21785500Sbasabi gettext("%s: could not create attribute" 21795500Sbasabi " %s on file %s: "), cmd, dp->d_name, 21805500Sbasabi target); 21815500Sbasabi perror(""); 21825500Sbasabi ++error; 21835500Sbasabi } 21845500Sbasabi goto out; 21855500Sbasabi } 21865331Samw } else { 21875500Sbasabi if ((targattrfd = openat(targetdirfd, dp->d_name, 21885500Sbasabi O_RDONLY)) == -1) { 21895500Sbasabi if (!attrsilent) { 21905500Sbasabi (void) fprintf(stderr, 21915500Sbasabi gettext("%s: could not open attribute" 21925500Sbasabi " %s on file %s: "), cmd, dp->d_name, 21935500Sbasabi target); 21945500Sbasabi perror(""); 21955500Sbasabi ++error; 21965500Sbasabi } 21975500Sbasabi goto out; 21985500Sbasabi } 21995331Samw } 22005331Samw 22015331Samw 22025331Samw if (fstat(targattrfd, &s4) < 0) { 22035331Samw if (!attrsilent) { 22045331Samw (void) fprintf(stderr, 22055331Samw gettext("%s: could not stat attribute" 22065331Samw " %s on file" 22075331Samw " %s: "), cmd, dp->d_name, target); 22085331Samw perror(""); 22095331Samw ++error; 22105331Samw } 22115331Samw } 22125331Samw 22135331Samw out: 22145331Samw if (error) { 22155331Samw if (srcattrfd != -1) 22165331Samw (void) close(srcattrfd); 22175331Samw if (targattrfd != -1) 22186091Sbasabi (void) close(targattrfd); 22196091Sbasabi srcattrfd = targattrfd = -1; 22205331Samw } 22215331Samw return (error == 0 ? 0 :1); 22225331Samw } 22235331Samw 22245331Samw void 22255331Samw rewind_attrdir(DIR * sdp) 22265331Samw { 22275331Samw int pwdfd; 22285331Samw 22295331Samw pwdfd = open(".", O_RDONLY); 22305331Samw if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) { 22315331Samw rewinddir(sdp); 22325331Samw (void) fchdir(pwdfd); 22335331Samw (void) close(pwdfd); 22345331Samw } else { 22355331Samw if (!attrsilent) { 22365331Samw (void) fprintf(stderr, gettext("%s: " 22375331Samw "failed to rewind attribute dir\n"), 22385331Samw cmd); 22395331Samw } 22405331Samw } 22415331Samw } 22425331Samw 22435331Samw void 22445331Samw close_all() 22455331Samw { 22465331Samw if (srcattrfd != -1) 22475331Samw (void) close(srcattrfd); 22485331Samw if (targattrfd != -1) 22495331Samw (void) close(targattrfd); 22505331Samw if (sourcedirfd != -1) 22515331Samw (void) close(sourcedirfd); 22525331Samw if (targetdirfd != -1) 22535331Samw (void) close(targetdirfd); 22545331Samw if (srcdirp != NULL) { 22555331Samw (void) closedir(srcdirp); 22565331Samw srcdirp = NULL; 22575331Samw } 22585331Samw if (srcfd != -1) 22595331Samw (void) close(srcfd); 22605331Samw if (targfd != -1) 22615331Samw (void) close(targfd); 22625331Samw } 2263