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 */
21*10709SRoger.Faulkner@Sun.COM
220Sstevel@tonic-gate /*
239324SRenaud.Manus@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
280Sstevel@tonic-gate /* All Rights Reserved */
290Sstevel@tonic-gate
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988
320Sstevel@tonic-gate * The Regents of the University of California
330Sstevel@tonic-gate * All Rights Reserved
340Sstevel@tonic-gate *
350Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from
360Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its
370Sstevel@tonic-gate * contributors.
380Sstevel@tonic-gate */
390Sstevel@tonic-gate
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate * Combined mv/cp/ln command:
420Sstevel@tonic-gate * mv file1 file2
430Sstevel@tonic-gate * mv dir1 dir2
440Sstevel@tonic-gate * mv file1 ... filen dir1
450Sstevel@tonic-gate */
460Sstevel@tonic-gate #include <sys/time.h>
470Sstevel@tonic-gate #include <signal.h>
480Sstevel@tonic-gate #include <locale.h>
490Sstevel@tonic-gate #include <stdarg.h>
500Sstevel@tonic-gate #include <sys/acl.h>
510Sstevel@tonic-gate #include <libcmdutils.h>
52789Sahrens #include <aclutils.h>
534774Sas145665 #include "getresponse.h"
540Sstevel@tonic-gate
550Sstevel@tonic-gate #define FTYPE(A) (A.st_mode)
560Sstevel@tonic-gate #define FMODE(A) (A.st_mode)
570Sstevel@tonic-gate #define UID(A) (A.st_uid)
580Sstevel@tonic-gate #define GID(A) (A.st_gid)
590Sstevel@tonic-gate #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
600Sstevel@tonic-gate #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
610Sstevel@tonic-gate #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR)
620Sstevel@tonic-gate #define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK)
630Sstevel@tonic-gate #define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
640Sstevel@tonic-gate #define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \
650Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFBLK || \
660Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFIFO)
6710294Sdannywebster@googlemail.com #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK)
680Sstevel@tonic-gate
690Sstevel@tonic-gate #define DELIM '/'
700Sstevel@tonic-gate #define EQ(x, y) (strcmp(x, y) == 0)
710Sstevel@tonic-gate #define FALSE 0
720Sstevel@tonic-gate #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
730Sstevel@tonic-gate #define TRUE 1
740Sstevel@tonic-gate
750Sstevel@tonic-gate static char *dname(char *);
760Sstevel@tonic-gate static int lnkfil(char *, char *);
770Sstevel@tonic-gate static int cpymve(char *, char *);
780Sstevel@tonic-gate static int chkfiles(char *, char **);
790Sstevel@tonic-gate static int rcopy(char *, char *);
800Sstevel@tonic-gate static int chk_different(char *, char *);
810Sstevel@tonic-gate static int chg_time(char *, struct stat);
820Sstevel@tonic-gate static int chg_mode(char *, uid_t, gid_t, mode_t);
830Sstevel@tonic-gate static int copydir(char *, char *);
840Sstevel@tonic-gate static int copyspecial(char *);
850Sstevel@tonic-gate static int getrealpath(char *, char *);
860Sstevel@tonic-gate static void usage(void);
870Sstevel@tonic-gate static void Perror(char *);
880Sstevel@tonic-gate static void Perror2(char *, char *);
890Sstevel@tonic-gate static int use_stdin(void);
900Sstevel@tonic-gate static int copyattributes(char *, char *);
915331Samw static int copy_sysattr(char *, char *);
920Sstevel@tonic-gate static tree_node_t *create_tnode(dev_t, ino_t);
930Sstevel@tonic-gate
945331Samw static struct stat s1, s2, s3, s4;
950Sstevel@tonic-gate static int cpy = FALSE;
960Sstevel@tonic-gate static int mve = FALSE;
970Sstevel@tonic-gate static int lnk = FALSE;
980Sstevel@tonic-gate static char *cmd;
990Sstevel@tonic-gate static int silent = 0;
1000Sstevel@tonic-gate static int fflg = 0;
1010Sstevel@tonic-gate static int iflg = 0;
1020Sstevel@tonic-gate static int pflg = 0;
1030Sstevel@tonic-gate static int Rflg = 0; /* recursive copy */
1040Sstevel@tonic-gate static int rflg = 0; /* recursive copy */
1050Sstevel@tonic-gate static int sflg = 0;
1060Sstevel@tonic-gate static int Hflg = 0; /* follow cmd line arg symlink to dir */
1070Sstevel@tonic-gate static int Lflg = 0; /* follow symlinks */
1080Sstevel@tonic-gate static int Pflg = 0; /* do not follow symlinks */
1090Sstevel@tonic-gate static int atflg = 0;
1100Sstevel@tonic-gate static int attrsilent = 0;
1110Sstevel@tonic-gate static int targetexists = 0;
1120Sstevel@tonic-gate static int cmdarg; /* command line argument */
1130Sstevel@tonic-gate static avl_tree_t *stree = NULL; /* source file inode search tree */
114789Sahrens static acl_t *s1acl;
1155331Samw static int saflg = 0; /* 'cp' extended system attr. */
1166091Sbasabi static int srcfd = -1;
1176091Sbasabi static int targfd = -1;
1186091Sbasabi static int sourcedirfd = -1;
1196091Sbasabi static int targetdirfd = -1;
1206091Sbasabi static DIR *srcdirp = NULL;
1216091Sbasabi static int srcattrfd = -1;
1226091Sbasabi static int targattrfd = -1;
1235331Samw static struct stat attrdir;
1245331Samw
1255331Samw /* Extended system attributes support */
1265331Samw
1275331Samw static int open_source(char *);
1285331Samw static int open_target_srctarg_attrdirs(char *, char *);
1295331Samw static int open_attrdirp(char *);
1305331Samw static int traverse_attrfile(struct dirent *, char *, char *, int);
1315331Samw static void rewind_attrdir(DIR *);
1325331Samw static void close_all();
1335331Samw
1340Sstevel@tonic-gate
135212Scf46844 int
main(int argc,char * argv[])1360Sstevel@tonic-gate main(int argc, char *argv[])
1370Sstevel@tonic-gate {
1380Sstevel@tonic-gate int c, i, r, errflg = 0;
1390Sstevel@tonic-gate char target[PATH_MAX];
1400Sstevel@tonic-gate int (*move)(char *, char *);
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate * Determine command invoked (mv, cp, or ln)
1440Sstevel@tonic-gate */
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate if (cmd = strrchr(argv[0], '/'))
1470Sstevel@tonic-gate ++cmd;
1480Sstevel@tonic-gate else
1490Sstevel@tonic-gate cmd = argv[0];
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate * Set flags based on command.
1530Sstevel@tonic-gate */
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
1560Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
1570Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
1580Sstevel@tonic-gate #endif
1590Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
1604774Sas145665 if (init_yes() < 0) {
1614774Sas145665 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
1624774Sas145665 strerror(errno));
1634774Sas145665 exit(3);
1644774Sas145665 }
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate if (EQ(cmd, "mv"))
1670Sstevel@tonic-gate mve = TRUE;
1680Sstevel@tonic-gate else if (EQ(cmd, "ln"))
1690Sstevel@tonic-gate lnk = TRUE;
1700Sstevel@tonic-gate else if (EQ(cmd, "cp"))
1710Sstevel@tonic-gate cpy = TRUE;
1720Sstevel@tonic-gate else {
1730Sstevel@tonic-gate (void) fprintf(stderr,
1740Sstevel@tonic-gate gettext("Invalid command name (%s); expecting "
1750Sstevel@tonic-gate "mv, cp, or ln.\n"), cmd);
1760Sstevel@tonic-gate exit(1);
1770Sstevel@tonic-gate }
1780Sstevel@tonic-gate
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate * Check for options:
1815331Samw * cp -r|-R [-H|-L|-P] [-fip@/] file1 [file2 ...] target
1825331Samw * cp [-fiprR@/] file1 [file2 ...] target
1830Sstevel@tonic-gate * ln [-f] [-n] [-s] file1 [file2 ...] target
1840Sstevel@tonic-gate * ln [-f] [-n] [-s] file1 [file2 ...]
1850Sstevel@tonic-gate * mv [-f|i] file1 [file2 ...] target
1860Sstevel@tonic-gate * mv [-f|i] dir1 target
1870Sstevel@tonic-gate */
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate if (cpy) {
1905331Samw while ((c = getopt(argc, argv, "fHiLpPrR@/")) != EOF)
1910Sstevel@tonic-gate switch (c) {
1920Sstevel@tonic-gate case 'f':
1930Sstevel@tonic-gate fflg++;
1940Sstevel@tonic-gate break;
1950Sstevel@tonic-gate case 'i':
1960Sstevel@tonic-gate iflg++;
1970Sstevel@tonic-gate break;
1980Sstevel@tonic-gate case 'p':
1990Sstevel@tonic-gate pflg++;
2000Sstevel@tonic-gate #ifdef XPG4
2010Sstevel@tonic-gate attrsilent = 1;
2020Sstevel@tonic-gate atflg = 0;
2035331Samw saflg = 0;
2040Sstevel@tonic-gate #else
2055374Sbasabi if (atflg == 0)
2060Sstevel@tonic-gate attrsilent = 1;
2070Sstevel@tonic-gate #endif
2080Sstevel@tonic-gate break;
2090Sstevel@tonic-gate case 'H':
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate * If more than one of -H, -L, or -P are
2120Sstevel@tonic-gate * specified, only the last option specified
2130Sstevel@tonic-gate * determines the behavior.
2140Sstevel@tonic-gate */
2150Sstevel@tonic-gate Lflg = Pflg = 0;
2160Sstevel@tonic-gate Hflg++;
2170Sstevel@tonic-gate break;
2180Sstevel@tonic-gate case 'L':
2190Sstevel@tonic-gate Hflg = Pflg = 0;
2200Sstevel@tonic-gate Lflg++;
2210Sstevel@tonic-gate break;
2220Sstevel@tonic-gate case 'P':
2230Sstevel@tonic-gate Lflg = Hflg = 0;
2240Sstevel@tonic-gate Pflg++;
2250Sstevel@tonic-gate break;
2260Sstevel@tonic-gate case 'R':
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate * The default behavior of cp -R|-r
2290Sstevel@tonic-gate * when specified without -H|-L|-P
2300Sstevel@tonic-gate * is -L.
2310Sstevel@tonic-gate */
2320Sstevel@tonic-gate Rflg++;
2330Sstevel@tonic-gate /*FALLTHROUGH*/
2340Sstevel@tonic-gate case 'r':
2350Sstevel@tonic-gate rflg++;
2360Sstevel@tonic-gate break;
2370Sstevel@tonic-gate case '@':
2380Sstevel@tonic-gate atflg++;
2390Sstevel@tonic-gate attrsilent = 0;
2400Sstevel@tonic-gate #ifdef XPG4
2410Sstevel@tonic-gate pflg = 0;
2420Sstevel@tonic-gate #endif
2430Sstevel@tonic-gate break;
2445331Samw case '/':
2455331Samw saflg++;
2465331Samw attrsilent = 0;
2475331Samw #ifdef XPG4
2485331Samw pflg = 0;
2495331Samw #endif
2505331Samw break;
2510Sstevel@tonic-gate default:
2520Sstevel@tonic-gate errflg++;
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate /* -R or -r must be specified with -H, -L, or -P */
2560Sstevel@tonic-gate if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
2570Sstevel@tonic-gate errflg++;
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate } else if (mve) {
2610Sstevel@tonic-gate while ((c = getopt(argc, argv, "fis")) != EOF)
2620Sstevel@tonic-gate switch (c) {
2630Sstevel@tonic-gate case 'f':
2640Sstevel@tonic-gate silent++;
2650Sstevel@tonic-gate #ifdef XPG4
2660Sstevel@tonic-gate iflg = 0;
2670Sstevel@tonic-gate #endif
2680Sstevel@tonic-gate break;
2690Sstevel@tonic-gate case 'i':
2700Sstevel@tonic-gate iflg++;
2710Sstevel@tonic-gate #ifdef XPG4
2720Sstevel@tonic-gate silent = 0;
2730Sstevel@tonic-gate #endif
2740Sstevel@tonic-gate break;
2750Sstevel@tonic-gate default:
2760Sstevel@tonic-gate errflg++;
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate } else { /* ln */
2790Sstevel@tonic-gate while ((c = getopt(argc, argv, "fns")) != EOF)
2800Sstevel@tonic-gate switch (c) {
2810Sstevel@tonic-gate case 'f':
2820Sstevel@tonic-gate silent++;
2830Sstevel@tonic-gate break;
2840Sstevel@tonic-gate case 'n':
2850Sstevel@tonic-gate /* silently ignored; this is the default */
2860Sstevel@tonic-gate break;
2870Sstevel@tonic-gate case 's':
2880Sstevel@tonic-gate sflg++;
2890Sstevel@tonic-gate break;
2900Sstevel@tonic-gate default:
2910Sstevel@tonic-gate errflg++;
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate }
2940Sstevel@tonic-gate
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate * For BSD compatibility allow - to delimit the end of
2970Sstevel@tonic-gate * options for mv.
2980Sstevel@tonic-gate */
2990Sstevel@tonic-gate if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
3000Sstevel@tonic-gate optind++;
3010Sstevel@tonic-gate
3020Sstevel@tonic-gate /*
3030Sstevel@tonic-gate * Check for sufficient arguments
3040Sstevel@tonic-gate * or a usage error.
3050Sstevel@tonic-gate */
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate argc -= optind;
3080Sstevel@tonic-gate argv = &argv[optind];
3090Sstevel@tonic-gate
3100Sstevel@tonic-gate if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
3110Sstevel@tonic-gate (void) fprintf(stderr,
3120Sstevel@tonic-gate gettext("%s: Insufficient arguments (%d)\n"),
3130Sstevel@tonic-gate cmd, argc);
3140Sstevel@tonic-gate usage();
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate if (errflg != 0)
3180Sstevel@tonic-gate usage();
3190Sstevel@tonic-gate
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate * If there is more than a source and target,
3220Sstevel@tonic-gate * the last argument (the target) must be a directory
3230Sstevel@tonic-gate * which really exists.
3240Sstevel@tonic-gate */
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate if (argc > 2) {
3270Sstevel@tonic-gate if (stat(argv[argc-1], &s2) < 0) {
3280Sstevel@tonic-gate (void) fprintf(stderr,
3290Sstevel@tonic-gate gettext("%s: %s not found\n"),
3300Sstevel@tonic-gate cmd, argv[argc-1]);
3310Sstevel@tonic-gate exit(2);
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate
3340Sstevel@tonic-gate if (!ISDIR(s2)) {
3350Sstevel@tonic-gate (void) fprintf(stderr,
3360Sstevel@tonic-gate gettext("%s: Target %s must be a directory\n"),
3370Sstevel@tonic-gate cmd, argv[argc-1]);
3380Sstevel@tonic-gate usage();
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate if (strlen(argv[argc-1]) >= PATH_MAX) {
3430Sstevel@tonic-gate (void) fprintf(stderr,
3440Sstevel@tonic-gate gettext("%s: Target %s file name length exceeds PATH_MAX"
3450Sstevel@tonic-gate " %d\n"), cmd, argv[argc-1], PATH_MAX);
3460Sstevel@tonic-gate exit(78);
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate
3490Sstevel@tonic-gate if (argc == 1) {
3500Sstevel@tonic-gate if (!lnk)
3510Sstevel@tonic-gate usage();
3520Sstevel@tonic-gate (void) strcpy(target, ".");
3530Sstevel@tonic-gate } else {
3540Sstevel@tonic-gate (void) strcpy(target, argv[--argc]);
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate * Perform a multiple argument mv|cp|ln by
3590Sstevel@tonic-gate * multiple invocations of cpymve() or lnkfil().
3600Sstevel@tonic-gate */
3610Sstevel@tonic-gate if (lnk)
3620Sstevel@tonic-gate move = lnkfil;
3630Sstevel@tonic-gate else
3640Sstevel@tonic-gate move = cpymve;
3650Sstevel@tonic-gate
3660Sstevel@tonic-gate r = 0;
3670Sstevel@tonic-gate for (i = 0; i < argc; i++) {
3680Sstevel@tonic-gate stree = NULL;
3690Sstevel@tonic-gate cmdarg = 1;
3700Sstevel@tonic-gate r += move(argv[i], target);
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate * Show errors by nonzero exit code.
3750Sstevel@tonic-gate */
3760Sstevel@tonic-gate
377212Scf46844 return (r?2:0);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate static int
lnkfil(char * source,char * target)3810Sstevel@tonic-gate lnkfil(char *source, char *target)
3820Sstevel@tonic-gate {
3830Sstevel@tonic-gate char *buf = NULL;
3840Sstevel@tonic-gate
3850Sstevel@tonic-gate if (sflg) {
3860Sstevel@tonic-gate
3870Sstevel@tonic-gate /*
3880Sstevel@tonic-gate * If target is a directory make complete
3890Sstevel@tonic-gate * name of the new symbolic link within that
3900Sstevel@tonic-gate * directory.
3910Sstevel@tonic-gate */
3920Sstevel@tonic-gate
3930Sstevel@tonic-gate if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
3940Sstevel@tonic-gate size_t len;
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4;
3970Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) {
3980Sstevel@tonic-gate (void) fprintf(stderr,
3990Sstevel@tonic-gate gettext("%s: Insufficient memory "
4000Sstevel@tonic-gate "to %s %s\n"), cmd, cmd, source);
4010Sstevel@tonic-gate exit(3);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s",
4040Sstevel@tonic-gate target, dname(source));
4050Sstevel@tonic-gate target = buf;
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate /*
4096555Schin * Check to see if the file exists already.
4106555Schin * In this case we use lstat() instead of stat():
4116555Schin * unlink(2) and symlink(2) will operate on the file
4126555Schin * itself, not its reference, if the file is a symlink.
4130Sstevel@tonic-gate */
4140Sstevel@tonic-gate
4156555Schin if ((lstat(target, &s2) == 0)) {
4160Sstevel@tonic-gate /*
4170Sstevel@tonic-gate * Check if the silent flag is set ie. the -f option
4180Sstevel@tonic-gate * is used. If so, use unlink to remove the current
4190Sstevel@tonic-gate * target to replace with the new target, specified
4200Sstevel@tonic-gate * on the command line. Proceed with symlink.
4210Sstevel@tonic-gate */
4220Sstevel@tonic-gate if (silent) {
4236555Schin /*
4246555Schin * Don't allow silent (-f) removal of an existing
4256555Schin * directory; could leave unreferenced directory
4266555Schin * entries.
4276555Schin */
4286555Schin if (ISDIR(s2)) {
4296555Schin (void) fprintf(stderr,
4306555Schin gettext("%s: cannot create link "
4316555Schin "over directory %s\n"), cmd,
4326555Schin target);
4336555Schin return (1);
4346555Schin }
4350Sstevel@tonic-gate if (unlink(target) < 0) {
4360Sstevel@tonic-gate (void) fprintf(stderr,
4370Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
4380Sstevel@tonic-gate cmd, target);
4390Sstevel@tonic-gate perror("");
4400Sstevel@tonic-gate return (1);
4410Sstevel@tonic-gate }
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate /*
4470Sstevel@tonic-gate * Create a symbolic link to the source.
4480Sstevel@tonic-gate */
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate if (symlink(source, target) < 0) {
4510Sstevel@tonic-gate (void) fprintf(stderr,
4520Sstevel@tonic-gate gettext("%s: cannot create %s: "),
4530Sstevel@tonic-gate cmd, target);
4540Sstevel@tonic-gate perror("");
4550Sstevel@tonic-gate if (buf != NULL)
4560Sstevel@tonic-gate free(buf);
4570Sstevel@tonic-gate return (1);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate if (buf != NULL)
4600Sstevel@tonic-gate free(buf);
4610Sstevel@tonic-gate return (0);
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate switch (chkfiles(source, &target)) {
4650Sstevel@tonic-gate case 1: return (1);
4660Sstevel@tonic-gate case 2: return (0);
4670Sstevel@tonic-gate /* default - fall through */
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate /*
4710Sstevel@tonic-gate * Make sure source file is not a directory,
47210294Sdannywebster@googlemail.com * we cannot link directories...
4730Sstevel@tonic-gate */
4740Sstevel@tonic-gate
4750Sstevel@tonic-gate if (ISDIR(s1)) {
4760Sstevel@tonic-gate (void) fprintf(stderr,
4770Sstevel@tonic-gate gettext("%s: %s is a directory\n"), cmd, source);
4780Sstevel@tonic-gate return (1);
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate * hard link, call link() and return.
4830Sstevel@tonic-gate */
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate if (link(source, target) < 0) {
4860Sstevel@tonic-gate if (errno == EXDEV)
4870Sstevel@tonic-gate (void) fprintf(stderr,
4880Sstevel@tonic-gate gettext("%s: %s is on a different file system\n"),
4890Sstevel@tonic-gate cmd, target);
4900Sstevel@tonic-gate else {
4910Sstevel@tonic-gate (void) fprintf(stderr,
4920Sstevel@tonic-gate gettext("%s: cannot create link %s: "),
4930Sstevel@tonic-gate cmd, target);
4940Sstevel@tonic-gate perror("");
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate if (buf != NULL)
4970Sstevel@tonic-gate free(buf);
4980Sstevel@tonic-gate return (1);
4990Sstevel@tonic-gate } else {
5000Sstevel@tonic-gate if (buf != NULL)
5010Sstevel@tonic-gate free(buf);
5020Sstevel@tonic-gate return (0);
5030Sstevel@tonic-gate }
5040Sstevel@tonic-gate }
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate static int
cpymve(char * source,char * target)5070Sstevel@tonic-gate cpymve(char *source, char *target)
5080Sstevel@tonic-gate {
5090Sstevel@tonic-gate int n;
5100Sstevel@tonic-gate int fi, fo;
5110Sstevel@tonic-gate int ret = 0;
5120Sstevel@tonic-gate int attret = 0;
5135331Samw int sattret = 0;
5140Sstevel@tonic-gate int errno_save;
5155374Sbasabi int error = 0;
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate switch (chkfiles(source, &target)) {
5180Sstevel@tonic-gate case 1: return (1);
5190Sstevel@tonic-gate case 2: return (0);
5200Sstevel@tonic-gate /* default - fall through */
5210Sstevel@tonic-gate }
5220Sstevel@tonic-gate
5230Sstevel@tonic-gate /*
5240Sstevel@tonic-gate * If it's a recursive copy and source
5250Sstevel@tonic-gate * is a directory, then call rcopy (from copydir).
5260Sstevel@tonic-gate */
5270Sstevel@tonic-gate if (cpy) {
5280Sstevel@tonic-gate if (ISDIR(s1)) {
5290Sstevel@tonic-gate int rc;
5300Sstevel@tonic-gate avl_index_t where = 0;
5310Sstevel@tonic-gate tree_node_t *tnode;
5320Sstevel@tonic-gate tree_node_t *tptr;
5330Sstevel@tonic-gate dev_t save_dev = s1.st_dev;
5340Sstevel@tonic-gate ino_t save_ino = s1.st_ino;
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate /*
5370Sstevel@tonic-gate * We will be recursing into the directory so
5380Sstevel@tonic-gate * save the inode information to a search tree
5390Sstevel@tonic-gate * to avoid getting into an endless loop.
5400Sstevel@tonic-gate */
5410Sstevel@tonic-gate if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
5420Sstevel@tonic-gate if (rc == 0) {
5430Sstevel@tonic-gate /*
5440Sstevel@tonic-gate * We've already visited this directory.
5450Sstevel@tonic-gate * Don't remove the search tree entry
5460Sstevel@tonic-gate * to make sure we don't get into an
5470Sstevel@tonic-gate * endless loop if revisited from a
5480Sstevel@tonic-gate * different part of the hierarchy.
5490Sstevel@tonic-gate */
5500Sstevel@tonic-gate (void) fprintf(stderr, gettext(
5510Sstevel@tonic-gate "%s: cycle detected: %s\n"),
5520Sstevel@tonic-gate cmd, source);
5530Sstevel@tonic-gate } else {
5540Sstevel@tonic-gate Perror(source);
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate return (1);
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate cmdarg = 0;
5600Sstevel@tonic-gate rc = copydir(source, target);
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate /*
5630Sstevel@tonic-gate * Create a tnode to get an index to the matching
5640Sstevel@tonic-gate * node (same dev and inode) in the search tree,
5650Sstevel@tonic-gate * then use the index to remove the matching node
5660Sstevel@tonic-gate * so it we do not wrongly detect a cycle when
5670Sstevel@tonic-gate * revisiting this directory from another part of
5680Sstevel@tonic-gate * the hierarchy.
5690Sstevel@tonic-gate */
5700Sstevel@tonic-gate if ((tnode = create_tnode(save_dev,
5710Sstevel@tonic-gate save_ino)) == NULL) {
5720Sstevel@tonic-gate Perror(source);
5730Sstevel@tonic-gate return (1);
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
5760Sstevel@tonic-gate avl_remove(stree, tptr);
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate free(tptr);
5790Sstevel@tonic-gate free(tnode);
5800Sstevel@tonic-gate return (rc);
5810Sstevel@tonic-gate
5820Sstevel@tonic-gate } else if (ISDEV(s1) && Rflg) {
5830Sstevel@tonic-gate return (copyspecial(target));
5840Sstevel@tonic-gate } else {
5850Sstevel@tonic-gate goto copy;
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate if (mve) {
5900Sstevel@tonic-gate if (rename(source, target) >= 0)
5910Sstevel@tonic-gate return (0);
5920Sstevel@tonic-gate if (errno != EXDEV) {
5930Sstevel@tonic-gate if (errno == ENOTDIR && ISDIR(s1)) {
5940Sstevel@tonic-gate (void) fprintf(stderr,
5950Sstevel@tonic-gate gettext("%s: %s is a directory\n"),
5960Sstevel@tonic-gate cmd, source);
5970Sstevel@tonic-gate return (1);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate (void) fprintf(stderr,
6000Sstevel@tonic-gate gettext("%s: cannot rename %s to %s: "),
6010Sstevel@tonic-gate cmd, source, target);
6020Sstevel@tonic-gate perror("");
6030Sstevel@tonic-gate return (1);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate
6060Sstevel@tonic-gate /*
6070Sstevel@tonic-gate * cannot move a non-directory (source) onto an existing
6080Sstevel@tonic-gate * directory (target)
6090Sstevel@tonic-gate *
6100Sstevel@tonic-gate */
6110Sstevel@tonic-gate if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
6120Sstevel@tonic-gate (void) fprintf(stderr,
6130Sstevel@tonic-gate gettext("%s: cannot mv a non directory %s "
6140Sstevel@tonic-gate "over existing directory"
6150Sstevel@tonic-gate " %s \n"), cmd, source, target);
6160Sstevel@tonic-gate return (1);
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate if (ISDIR(s1)) {
6190Sstevel@tonic-gate #ifdef XPG4
6200Sstevel@tonic-gate if (targetexists && ISDIR(s2)) {
6210Sstevel@tonic-gate /* existing target dir must be empty */
6220Sstevel@tonic-gate if (rmdir(target) < 0) {
6230Sstevel@tonic-gate errno_save = errno;
6240Sstevel@tonic-gate (void) fprintf(stderr,
6250Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "),
6260Sstevel@tonic-gate cmd, target);
6270Sstevel@tonic-gate errno = errno_save;
6280Sstevel@tonic-gate perror("");
6290Sstevel@tonic-gate return (1);
6300Sstevel@tonic-gate }
6310Sstevel@tonic-gate }
6320Sstevel@tonic-gate #endif
6330Sstevel@tonic-gate if ((n = copydir(source, target)) == 0)
6340Sstevel@tonic-gate (void) rmdir(source);
6350Sstevel@tonic-gate return (n);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate
63810294Sdannywebster@googlemail.com /* doors cannot be moved across filesystems */
6390Sstevel@tonic-gate if (ISDOOR(s1)) {
6400Sstevel@tonic-gate (void) fprintf(stderr,
64110294Sdannywebster@googlemail.com gettext("%s: %s: cannot move door "
6420Sstevel@tonic-gate "across file systems\n"), cmd, source);
6430Sstevel@tonic-gate return (1);
6440Sstevel@tonic-gate }
64510294Sdannywebster@googlemail.com
64610294Sdannywebster@googlemail.com /* sockets cannot be moved across filesystems */
64710294Sdannywebster@googlemail.com if (ISSOCK(s1)) {
64810294Sdannywebster@googlemail.com (void) fprintf(stderr,
64910294Sdannywebster@googlemail.com gettext("%s: %s: cannot move socket "
65010294Sdannywebster@googlemail.com "across file systems\n"), cmd, source);
65110294Sdannywebster@googlemail.com return (1);
65210294Sdannywebster@googlemail.com }
65310294Sdannywebster@googlemail.com
6540Sstevel@tonic-gate /*
65510294Sdannywebster@googlemail.com * File cannot be renamed, try to recreate the symbolic
6560Sstevel@tonic-gate * link or special device, or copy the file wholesale
6570Sstevel@tonic-gate * between file systems.
6580Sstevel@tonic-gate */
6590Sstevel@tonic-gate if (ISLNK(s1)) {
6600Sstevel@tonic-gate register int m;
6610Sstevel@tonic-gate register mode_t md;
6620Sstevel@tonic-gate char symln[PATH_MAX + 1];
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
6650Sstevel@tonic-gate (void) fprintf(stderr,
6660Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
6670Sstevel@tonic-gate cmd, target);
6680Sstevel@tonic-gate perror("");
6690Sstevel@tonic-gate return (1);
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate
6720Sstevel@tonic-gate if ((m = readlink(source, symln,
6730Sstevel@tonic-gate sizeof (symln) - 1)) < 0) {
6740Sstevel@tonic-gate Perror(source);
6750Sstevel@tonic-gate return (1);
6760Sstevel@tonic-gate }
6770Sstevel@tonic-gate symln[m] = '\0';
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS));
6800Sstevel@tonic-gate if (symlink(symln, target) < 0) {
6810Sstevel@tonic-gate Perror(target);
6820Sstevel@tonic-gate return (1);
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate (void) umask(md);
6850Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1));
6860Sstevel@tonic-gate #ifdef XPG4
6870Sstevel@tonic-gate if (m < 0) {
6880Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot"
6890Sstevel@tonic-gate " change owner and group of"
6900Sstevel@tonic-gate " %s: "), cmd, target);
6910Sstevel@tonic-gate perror("");
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate #endif
6940Sstevel@tonic-gate goto cleanup;
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate if (ISDEV(s1)) {
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
6990Sstevel@tonic-gate (void) fprintf(stderr,
7000Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
7010Sstevel@tonic-gate cmd, target);
7020Sstevel@tonic-gate perror("");
7030Sstevel@tonic-gate return (1);
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate
7060Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
7070Sstevel@tonic-gate Perror(target);
7080Sstevel@tonic-gate return (1);
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
7120Sstevel@tonic-gate (void) chg_time(target, s1);
7130Sstevel@tonic-gate goto cleanup;
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate
7160Sstevel@tonic-gate if (ISREG(s1)) {
7170Sstevel@tonic-gate if (ISDIR(s2)) {
7180Sstevel@tonic-gate if (targetexists && rmdir(target) < 0) {
7190Sstevel@tonic-gate (void) fprintf(stderr,
7200Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "),
7210Sstevel@tonic-gate cmd, target);
7220Sstevel@tonic-gate perror("");
7230Sstevel@tonic-gate return (1);
7240Sstevel@tonic-gate }
7250Sstevel@tonic-gate } else {
7260Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
7270Sstevel@tonic-gate (void) fprintf(stderr,
7280Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
7290Sstevel@tonic-gate cmd, target);
7300Sstevel@tonic-gate perror("");
7310Sstevel@tonic-gate return (1);
7320Sstevel@tonic-gate }
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate
7360Sstevel@tonic-gate copy:
7370Sstevel@tonic-gate /*
7380Sstevel@tonic-gate * If the source file is a symlink, and either
7390Sstevel@tonic-gate * -P or -H flag (only if -H is specified and the
7400Sstevel@tonic-gate * source file is not a command line argument)
7410Sstevel@tonic-gate * were specified, then action is taken on the symlink
7420Sstevel@tonic-gate * itself, not the file referenced by the symlink.
7430Sstevel@tonic-gate * Note: this is executed for 'cp' only.
7440Sstevel@tonic-gate */
7450Sstevel@tonic-gate if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
7460Sstevel@tonic-gate int m;
7470Sstevel@tonic-gate mode_t md;
7480Sstevel@tonic-gate char symln[PATH_MAX + 1];
7490Sstevel@tonic-gate
7500Sstevel@tonic-gate m = readlink(source, symln, sizeof (symln) - 1);
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate if (m < 0) {
7530Sstevel@tonic-gate Perror(source);
7540Sstevel@tonic-gate return (1);
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate symln[m] = '\0';
7570Sstevel@tonic-gate
7580Sstevel@tonic-gate /*
7590Sstevel@tonic-gate * Copy the sym link to the target.
7600Sstevel@tonic-gate * Note: If the target exists, write a
7610Sstevel@tonic-gate * diagnostic message, do nothing more
7620Sstevel@tonic-gate * with the source file, and return to
7630Sstevel@tonic-gate * process any remaining files.
7640Sstevel@tonic-gate */
7650Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS));
7660Sstevel@tonic-gate if (symlink(symln, target) < 0) {
7670Sstevel@tonic-gate Perror(target);
7680Sstevel@tonic-gate return (1);
7690Sstevel@tonic-gate }
7700Sstevel@tonic-gate (void) umask(md);
7710Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1));
7720Sstevel@tonic-gate
7730Sstevel@tonic-gate if (m < 0) {
7740Sstevel@tonic-gate (void) fprintf(stderr, gettext(
7750Sstevel@tonic-gate "cp: cannot change owner and "
7760Sstevel@tonic-gate "group of %s:"), target);
7770Sstevel@tonic-gate perror("");
7780Sstevel@tonic-gate }
7790Sstevel@tonic-gate } else {
7800Sstevel@tonic-gate /*
7810Sstevel@tonic-gate * Copy the file. If it happens to be a
7820Sstevel@tonic-gate * symlink, copy the file referenced
7830Sstevel@tonic-gate * by the symlink.
7840Sstevel@tonic-gate */
7850Sstevel@tonic-gate fi = open(source, O_RDONLY);
7860Sstevel@tonic-gate if (fi < 0) {
7870Sstevel@tonic-gate (void) fprintf(stderr,
7880Sstevel@tonic-gate gettext("%s: cannot open %s: "),
7890Sstevel@tonic-gate cmd, source);
7900Sstevel@tonic-gate perror("");
7910Sstevel@tonic-gate return (1);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate fo = creat(target, s1.st_mode & MODEBITS);
7950Sstevel@tonic-gate if (fo < 0) {
7960Sstevel@tonic-gate /*
7970Sstevel@tonic-gate * If -f and creat() failed, unlink
7980Sstevel@tonic-gate * and try again.
7990Sstevel@tonic-gate */
8000Sstevel@tonic-gate if (fflg) {
8010Sstevel@tonic-gate (void) unlink(target);
8020Sstevel@tonic-gate fo = creat(target,
8030Sstevel@tonic-gate s1.st_mode & MODEBITS);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate }
8060Sstevel@tonic-gate if (fo < 0) {
8070Sstevel@tonic-gate (void) fprintf(stderr,
8080Sstevel@tonic-gate gettext("%s: cannot create %s: "),
8090Sstevel@tonic-gate cmd, target);
8100Sstevel@tonic-gate perror("");
8110Sstevel@tonic-gate (void) close(fi);
8120Sstevel@tonic-gate return (1);
8130Sstevel@tonic-gate } else {
8140Sstevel@tonic-gate /* stat the new file, its used below */
8150Sstevel@tonic-gate (void) stat(target, &s2);
8160Sstevel@tonic-gate }
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate /*
8190Sstevel@tonic-gate * Set target's permissions to the source
8200Sstevel@tonic-gate * before any copying so that any partially
8210Sstevel@tonic-gate * copied file will have the source's
8220Sstevel@tonic-gate * permissions (at most) or umask permissions
8230Sstevel@tonic-gate * whichever is the most restrictive.
8240Sstevel@tonic-gate *
8250Sstevel@tonic-gate * ACL for regular files
8260Sstevel@tonic-gate */
8270Sstevel@tonic-gate
8280Sstevel@tonic-gate if (pflg || mve) {
8290Sstevel@tonic-gate (void) chmod(target, FMODE(s1));
830789Sahrens if (s1acl != NULL) {
831789Sahrens if ((acl_set(target,
832789Sahrens s1acl)) < 0) {
8335374Sbasabi error++;
8345374Sbasabi (void) fprintf(stderr,
8355374Sbasabi gettext("%s: "
8365374Sbasabi "Failed to set "
8375374Sbasabi "acl entries "
8385374Sbasabi "on %s\n"), cmd,
8395374Sbasabi target);
8401465Smarks acl_free(s1acl);
8411465Smarks s1acl = NULL;
8420Sstevel@tonic-gate /*
8430Sstevel@tonic-gate * else: silent and
8440Sstevel@tonic-gate * continue
8450Sstevel@tonic-gate */
8460Sstevel@tonic-gate }
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate }
8490Sstevel@tonic-gate
8500Sstevel@tonic-gate if (fstat(fi, &s1) < 0) {
8510Sstevel@tonic-gate (void) fprintf(stderr,
8520Sstevel@tonic-gate gettext("%s: cannot access %s\n"),
8530Sstevel@tonic-gate cmd, source);
8540Sstevel@tonic-gate return (1);
8550Sstevel@tonic-gate }
8560Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
8570Sstevel@tonic-gate (void) fprintf(stderr,
8580Sstevel@tonic-gate gettext(
8590Sstevel@tonic-gate "%s: %s and %s are identical\n"),
8600Sstevel@tonic-gate cmd, source, target);
8610Sstevel@tonic-gate return (1);
8620Sstevel@tonic-gate }
8630Sstevel@tonic-gate
8645331Samw if (writefile(fi, fo, source, target, NULL,
8655331Samw NULL, &s1, &s2) != 0) {
8660Sstevel@tonic-gate return (1);
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate
8690Sstevel@tonic-gate (void) close(fi);
8700Sstevel@tonic-gate if (close(fo) < 0) {
8710Sstevel@tonic-gate Perror2(target, "write");
8720Sstevel@tonic-gate return (1);
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate }
8755331Samw /* Copy regular extended attributes */
8765331Samw if (pflg || atflg || mve || saflg) {
8770Sstevel@tonic-gate attret = copyattributes(source, target);
8780Sstevel@tonic-gate if (attret != 0 && !attrsilent) {
8790Sstevel@tonic-gate (void) fprintf(stderr, gettext(
8804774Sas145665 "%s: Failed to preserve"
8814774Sas145665 " extended attributes of file"
8824774Sas145665 " %s\n"), cmd, source);
8830Sstevel@tonic-gate }
8845331Samw /* Copy extended system attributes */
8855374Sbasabi if (pflg || mve || saflg)
8865331Samw sattret = copy_sysattr(source, target);
8870Sstevel@tonic-gate if (mve && attret != 0) {
8880Sstevel@tonic-gate (void) unlink(target);
8890Sstevel@tonic-gate return (1);
8900Sstevel@tonic-gate }
8915331Samw if (attrsilent) {
8920Sstevel@tonic-gate attret = 0;
8935331Samw }
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate
8960Sstevel@tonic-gate /*
8970Sstevel@tonic-gate * XPG4: the write system call will clear setgid
8980Sstevel@tonic-gate * and setuid bits, so set them again.
8990Sstevel@tonic-gate */
9000Sstevel@tonic-gate if (pflg || mve) {
9010Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1),
9020Sstevel@tonic-gate FMODE(s1))) > 0)
9030Sstevel@tonic-gate return (1);
9041231Smarks /*
9051231Smarks * Reapply ACL, since chmod may have
9061231Smarks * altered ACL
9071231Smarks */
9081231Smarks if (s1acl != NULL) {
9091231Smarks if ((acl_set(target, s1acl)) < 0) {
9105374Sbasabi error++;
9115374Sbasabi (void) fprintf(stderr,
9125374Sbasabi gettext("%s: Failed to "
9135374Sbasabi "set acl entries "
9145374Sbasabi "on %s\n"), cmd, target);
9151231Smarks /*
9161231Smarks * else: silent and
9171231Smarks * continue
9181231Smarks */
9191231Smarks }
9201231Smarks }
9210Sstevel@tonic-gate if ((ret = chg_time(target, s1)) > 0)
9220Sstevel@tonic-gate return (1);
9230Sstevel@tonic-gate }
9240Sstevel@tonic-gate if (cpy) {
9255374Sbasabi if (error != 0 || attret != 0 || sattret != 0)
9260Sstevel@tonic-gate return (1);
9270Sstevel@tonic-gate return (0);
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate goto cleanup;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate (void) fprintf(stderr,
9320Sstevel@tonic-gate gettext("%s: %s: unknown file type 0x%x\n"), cmd,
9334774Sas145665 source, (s1.st_mode & S_IFMT));
9340Sstevel@tonic-gate return (1);
9350Sstevel@tonic-gate
9360Sstevel@tonic-gate cleanup:
9370Sstevel@tonic-gate if (unlink(source) < 0) {
9380Sstevel@tonic-gate (void) unlink(target);
9390Sstevel@tonic-gate (void) fprintf(stderr,
9400Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
9410Sstevel@tonic-gate cmd, source);
9420Sstevel@tonic-gate perror("");
9430Sstevel@tonic-gate return (1);
9440Sstevel@tonic-gate }
9455374Sbasabi if (error != 0 || attret != 0 || sattret != 0)
9465374Sbasabi return (1);
9470Sstevel@tonic-gate return (ret);
9480Sstevel@tonic-gate }
9490Sstevel@tonic-gate /*NOTREACHED*/
950212Scf46844 return (ret);
9510Sstevel@tonic-gate }
9520Sstevel@tonic-gate
9530Sstevel@tonic-gate /*
9540Sstevel@tonic-gate * create_tnode()
9550Sstevel@tonic-gate *
9560Sstevel@tonic-gate * Create a node for use with the search tree which contains the
9570Sstevel@tonic-gate * inode information (device id and inode number).
9580Sstevel@tonic-gate *
9590Sstevel@tonic-gate * Input
9600Sstevel@tonic-gate * dev - device id
9610Sstevel@tonic-gate * ino - inode number
9620Sstevel@tonic-gate *
9630Sstevel@tonic-gate * Output
9640Sstevel@tonic-gate * tnode - NULL on error, otherwise returns a tnode structure
9650Sstevel@tonic-gate * which contains the input device id and inode number.
9660Sstevel@tonic-gate */
9670Sstevel@tonic-gate static tree_node_t *
create_tnode(dev_t dev,ino_t ino)9680Sstevel@tonic-gate create_tnode(dev_t dev, ino_t ino)
9690Sstevel@tonic-gate {
9700Sstevel@tonic-gate tree_node_t *tnode;
9710Sstevel@tonic-gate
9720Sstevel@tonic-gate if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
9730Sstevel@tonic-gate tnode->node_dev = dev;
9740Sstevel@tonic-gate tnode->node_ino = ino;
9750Sstevel@tonic-gate }
9760Sstevel@tonic-gate
9770Sstevel@tonic-gate return (tnode);
9780Sstevel@tonic-gate }
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate static int
chkfiles(char * source,char ** to)9810Sstevel@tonic-gate chkfiles(char *source, char **to)
9820Sstevel@tonic-gate {
9830Sstevel@tonic-gate char *buf = (char *)NULL;
9840Sstevel@tonic-gate int (*statf)() = (cpy &&
9854774Sas145665 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
9860Sstevel@tonic-gate char *target = *to;
987789Sahrens int error;
9880Sstevel@tonic-gate
9890Sstevel@tonic-gate /*
9900Sstevel@tonic-gate * Make sure source file exists.
9910Sstevel@tonic-gate */
9920Sstevel@tonic-gate if ((*statf)(source, &s1) < 0) {
9930Sstevel@tonic-gate /*
9940Sstevel@tonic-gate * Keep the old error message except when someone tries to
9950Sstevel@tonic-gate * mv/cp/ln a symbolic link that has a trailing slash and
9960Sstevel@tonic-gate * points to a file.
9970Sstevel@tonic-gate */
9980Sstevel@tonic-gate if (errno == ENOTDIR)
9990Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
10000Sstevel@tonic-gate strerror(errno));
10010Sstevel@tonic-gate else
10020Sstevel@tonic-gate (void) fprintf(stderr,
10030Sstevel@tonic-gate gettext("%s: cannot access %s\n"), cmd, source);
10040Sstevel@tonic-gate return (1);
10050Sstevel@tonic-gate }
10060Sstevel@tonic-gate
10070Sstevel@tonic-gate /*
10089324SRenaud.Manus@Sun.COM * Get ACL info: don't bother with ln or cp/mv'ing symlinks
10090Sstevel@tonic-gate */
10109324SRenaud.Manus@Sun.COM if (!lnk && !ISLNK(s1)) {
1011789Sahrens if (s1acl != NULL) {
1012789Sahrens acl_free(s1acl);
1013789Sahrens s1acl = NULL;
10140Sstevel@tonic-gate }
1015789Sahrens if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
1016789Sahrens (void) fprintf(stderr,
1017789Sahrens "%s: failed to get acl entries: %s\n", source,
1018789Sahrens acl_strerror(error));
1019789Sahrens return (1);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate /* else: just permission bits */
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate /*
10250Sstevel@tonic-gate * If stat fails, then the target doesn't exist,
10260Sstevel@tonic-gate * we will create a new target with default file type of regular.
10270Sstevel@tonic-gate */
10280Sstevel@tonic-gate
10290Sstevel@tonic-gate FTYPE(s2) = S_IFREG;
10300Sstevel@tonic-gate targetexists = 0;
10310Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) {
10320Sstevel@tonic-gate if (ISLNK(s2))
10330Sstevel@tonic-gate (void) stat(target, &s2);
10340Sstevel@tonic-gate /*
10350Sstevel@tonic-gate * If target is a directory,
10360Sstevel@tonic-gate * make complete name of new file
10370Sstevel@tonic-gate * within that directory.
10380Sstevel@tonic-gate */
10390Sstevel@tonic-gate if (ISDIR(s2)) {
10400Sstevel@tonic-gate size_t len;
10410Sstevel@tonic-gate
10420Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4;
10430Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) {
10440Sstevel@tonic-gate (void) fprintf(stderr,
10450Sstevel@tonic-gate gettext("%s: Insufficient memory to "
10464774Sas145665 "%s %s\n "), cmd, cmd, source);
10470Sstevel@tonic-gate exit(3);
10480Sstevel@tonic-gate }
10490Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s",
10500Sstevel@tonic-gate target, dname(source));
10510Sstevel@tonic-gate *to = target = buf;
10520Sstevel@tonic-gate }
10530Sstevel@tonic-gate
10540Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) {
10550Sstevel@tonic-gate int overwrite = FALSE;
10560Sstevel@tonic-gate int override = FALSE;
10570Sstevel@tonic-gate
10580Sstevel@tonic-gate targetexists++;
10590Sstevel@tonic-gate if (cpy || mve) {
10600Sstevel@tonic-gate /*
10610Sstevel@tonic-gate * For cp and mv, it is an error if the
10620Sstevel@tonic-gate * source and target are the same file.
10630Sstevel@tonic-gate * Check for the same inode and file
10640Sstevel@tonic-gate * system, but don't check for the same
10650Sstevel@tonic-gate * absolute pathname because it is an
10660Sstevel@tonic-gate * error when the source and target are
10670Sstevel@tonic-gate * hard links to the same file.
10680Sstevel@tonic-gate */
10690Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
10700Sstevel@tonic-gate (void) fprintf(stderr,
10710Sstevel@tonic-gate gettext(
10720Sstevel@tonic-gate "%s: %s and %s are identical\n"),
10730Sstevel@tonic-gate cmd, source, target);
10740Sstevel@tonic-gate if (buf != NULL)
10750Sstevel@tonic-gate free(buf);
10760Sstevel@tonic-gate return (1);
10770Sstevel@tonic-gate }
10780Sstevel@tonic-gate }
10790Sstevel@tonic-gate if (lnk) {
10800Sstevel@tonic-gate /*
10810Sstevel@tonic-gate * For ln, it is an error if the source and
10820Sstevel@tonic-gate * target are identical files (same inode,
10830Sstevel@tonic-gate * same file system, and filenames resolve
10840Sstevel@tonic-gate * to same absolute pathname).
10850Sstevel@tonic-gate */
10860Sstevel@tonic-gate if (!chk_different(source, target)) {
10870Sstevel@tonic-gate if (buf != NULL)
10880Sstevel@tonic-gate free(buf);
10890Sstevel@tonic-gate return (1);
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate }
10920Sstevel@tonic-gate if (lnk && !silent) {
10930Sstevel@tonic-gate (void) fprintf(stderr,
10940Sstevel@tonic-gate gettext("%s: %s: File exists\n"),
10950Sstevel@tonic-gate cmd, 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 /*
11020Sstevel@tonic-gate * overwrite:
11030Sstevel@tonic-gate * If the user does not have access to
11040Sstevel@tonic-gate * the target, ask ----if it is not
11050Sstevel@tonic-gate * silent and user invoked command
11060Sstevel@tonic-gate * interactively.
11070Sstevel@tonic-gate *
11080Sstevel@tonic-gate * override:
11090Sstevel@tonic-gate * If not silent, and stdin is a terminal, and
11100Sstevel@tonic-gate * there's no write access, and the file isn't a
11110Sstevel@tonic-gate * symbolic link, ask for permission.
11120Sstevel@tonic-gate *
11130Sstevel@tonic-gate * XPG4: both overwrite and override:
11140Sstevel@tonic-gate * ask only one question.
11150Sstevel@tonic-gate *
11160Sstevel@tonic-gate * TRANSLATION_NOTE - The following messages will
11170Sstevel@tonic-gate * contain the first character of the strings for
11180Sstevel@tonic-gate * "yes" and "no" defined in the file
11190Sstevel@tonic-gate * "nl_langinfo.po". After substitution, the
11200Sstevel@tonic-gate * message will appear as follows:
11210Sstevel@tonic-gate * <cmd>: overwrite <filename> (y/n)?
11220Sstevel@tonic-gate * where <cmd> is the name of the command
11230Sstevel@tonic-gate * (cp, mv) and <filename> is the destination file
11240Sstevel@tonic-gate */
11250Sstevel@tonic-gate
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate overwrite = iflg && !silent && use_stdin();
11280Sstevel@tonic-gate override = !cpy && (access(target, 2) < 0) &&
11290Sstevel@tonic-gate !silent && use_stdin() && !ISLNK(s2);
11300Sstevel@tonic-gate
11313819Sas145665 if (overwrite && override) {
11320Sstevel@tonic-gate (void) fprintf(stderr,
11330Sstevel@tonic-gate gettext("%s: overwrite %s and override "
11340Sstevel@tonic-gate "protection %o (%s/%s)? "), cmd, target,
11354774Sas145665 FMODE(s2) & MODEBITS, yesstr, nostr);
11364774Sas145665 if (yes() == 0) {
11373819Sas145665 if (buf != NULL)
11383819Sas145665 free(buf);
11393819Sas145665 return (2);
11403819Sas145665 }
11413819Sas145665 } else if (overwrite && ISREG(s2)) {
11420Sstevel@tonic-gate (void) fprintf(stderr,
11430Sstevel@tonic-gate gettext("%s: overwrite %s (%s/%s)? "),
11444774Sas145665 cmd, target, yesstr, nostr);
11454774Sas145665 if (yes() == 0) {
11463819Sas145665 if (buf != NULL)
11473819Sas145665 free(buf);
11483819Sas145665 return (2);
11493819Sas145665 }
11503819Sas145665 } else if (override) {
11510Sstevel@tonic-gate (void) fprintf(stderr,
11520Sstevel@tonic-gate gettext("%s: %s: override protection "
11530Sstevel@tonic-gate /*CSTYLED*/
11540Sstevel@tonic-gate "%o (%s/%s)? "),
11550Sstevel@tonic-gate /*CSTYLED*/
11560Sstevel@tonic-gate cmd, target, FMODE(s2) & MODEBITS,
11574774Sas145665 yesstr, nostr);
11584774Sas145665 if (yes() == 0) {
11593819Sas145665 if (buf != NULL)
11603819Sas145665 free(buf);
11613819Sas145665 return (2);
11620Sstevel@tonic-gate }
11630Sstevel@tonic-gate }
11643819Sas145665
11650Sstevel@tonic-gate if (lnk && unlink(target) < 0) {
11660Sstevel@tonic-gate (void) fprintf(stderr,
11670Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
11680Sstevel@tonic-gate cmd, target);
11690Sstevel@tonic-gate perror("");
11700Sstevel@tonic-gate return (1);
11710Sstevel@tonic-gate }
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate }
11740Sstevel@tonic-gate return (0);
11750Sstevel@tonic-gate }
11760Sstevel@tonic-gate
11770Sstevel@tonic-gate /*
11780Sstevel@tonic-gate * check whether source and target are different
11790Sstevel@tonic-gate * return 1 when they are different
11800Sstevel@tonic-gate * return 0 when they are identical, or when unable to resolve a pathname
11810Sstevel@tonic-gate */
11820Sstevel@tonic-gate static int
chk_different(char * source,char * target)11830Sstevel@tonic-gate chk_different(char *source, char *target)
11840Sstevel@tonic-gate {
11850Sstevel@tonic-gate char rtarget[PATH_MAX], rsource[PATH_MAX];
11860Sstevel@tonic-gate
11870Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
11880Sstevel@tonic-gate /*
11890Sstevel@tonic-gate * IDENTICAL will be true for hard links, therefore
11900Sstevel@tonic-gate * check whether the filenames are different
11910Sstevel@tonic-gate */
11920Sstevel@tonic-gate if ((getrealpath(source, rsource) == 0) ||
11930Sstevel@tonic-gate (getrealpath(target, rtarget) == 0)) {
11940Sstevel@tonic-gate return (0);
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
11970Sstevel@tonic-gate (void) fprintf(stderr, gettext(
11980Sstevel@tonic-gate "%s: %s and %s are identical\n"),
11990Sstevel@tonic-gate cmd, source, target);
12000Sstevel@tonic-gate return (0);
12010Sstevel@tonic-gate }
12020Sstevel@tonic-gate }
12030Sstevel@tonic-gate return (1);
12040Sstevel@tonic-gate }
12050Sstevel@tonic-gate
12060Sstevel@tonic-gate /*
12070Sstevel@tonic-gate * get real path (resolved absolute pathname)
12080Sstevel@tonic-gate * return 1 on success, 0 on failure
12090Sstevel@tonic-gate */
12100Sstevel@tonic-gate static int
getrealpath(char * path,char * rpath)12110Sstevel@tonic-gate getrealpath(char *path, char *rpath)
12120Sstevel@tonic-gate {
12130Sstevel@tonic-gate if (realpath(path, rpath) == NULL) {
12140Sstevel@tonic-gate int errno_save = errno;
12150Sstevel@tonic-gate (void) fprintf(stderr, gettext(
121610294Sdannywebster@googlemail.com "%s: cannot resolve path %s: "), cmd, path);
12170Sstevel@tonic-gate errno = errno_save;
12180Sstevel@tonic-gate perror("");
12190Sstevel@tonic-gate return (0);
12200Sstevel@tonic-gate }
12210Sstevel@tonic-gate return (1);
12220Sstevel@tonic-gate }
12230Sstevel@tonic-gate
12240Sstevel@tonic-gate static int
rcopy(char * from,char * to)12250Sstevel@tonic-gate rcopy(char *from, char *to)
12260Sstevel@tonic-gate {
12270Sstevel@tonic-gate DIR *fold = opendir(from);
12280Sstevel@tonic-gate struct dirent *dp;
12290Sstevel@tonic-gate struct stat statb, s1save;
12300Sstevel@tonic-gate int errs = 0;
12310Sstevel@tonic-gate char fromname[PATH_MAX];
12320Sstevel@tonic-gate
12330Sstevel@tonic-gate if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
12340Sstevel@tonic-gate Perror(from);
12350Sstevel@tonic-gate return (1);
12360Sstevel@tonic-gate }
12370Sstevel@tonic-gate if (pflg || mve) {
12380Sstevel@tonic-gate /*
12390Sstevel@tonic-gate * Save s1 (stat information for source dir) so that
12400Sstevel@tonic-gate * mod and access times can be reserved during "cp -p"
12410Sstevel@tonic-gate * or mv, since s1 gets overwritten.
12420Sstevel@tonic-gate */
12430Sstevel@tonic-gate s1save = s1;
12440Sstevel@tonic-gate }
12450Sstevel@tonic-gate for (;;) {
12460Sstevel@tonic-gate dp = readdir(fold);
12470Sstevel@tonic-gate if (dp == 0) {
12480Sstevel@tonic-gate (void) closedir(fold);
12490Sstevel@tonic-gate if (pflg || mve)
12500Sstevel@tonic-gate return (chg_time(to, s1save) + errs);
12510Sstevel@tonic-gate return (errs);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate if (dp->d_ino == 0)
12540Sstevel@tonic-gate continue;
12550Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) ||
12560Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0))
12570Sstevel@tonic-gate continue;
12580Sstevel@tonic-gate if (strlen(from)+1+strlen(dp->d_name) >=
12590Sstevel@tonic-gate sizeof (fromname) - 1) {
12600Sstevel@tonic-gate (void) fprintf(stderr,
12610Sstevel@tonic-gate gettext("%s : %s/%s: Name too long\n"),
12620Sstevel@tonic-gate cmd, from, dp->d_name);
12630Sstevel@tonic-gate errs++;
12640Sstevel@tonic-gate continue;
12650Sstevel@tonic-gate }
12660Sstevel@tonic-gate (void) snprintf(fromname, sizeof (fromname),
12670Sstevel@tonic-gate "%s/%s", from, dp->d_name);
12680Sstevel@tonic-gate errs += cpymve(fromname, to);
12690Sstevel@tonic-gate }
12700Sstevel@tonic-gate }
12710Sstevel@tonic-gate
12720Sstevel@tonic-gate static char *
dname(char * name)12730Sstevel@tonic-gate dname(char *name)
12740Sstevel@tonic-gate {
12750Sstevel@tonic-gate register char *p;
12760Sstevel@tonic-gate
12770Sstevel@tonic-gate /*
12780Sstevel@tonic-gate * Return just the file name given the complete path.
12790Sstevel@tonic-gate * Like basename(1).
12800Sstevel@tonic-gate */
12810Sstevel@tonic-gate
12820Sstevel@tonic-gate p = name;
12830Sstevel@tonic-gate
12840Sstevel@tonic-gate /*
12850Sstevel@tonic-gate * While there are characters left,
12860Sstevel@tonic-gate * set name to start after last
12870Sstevel@tonic-gate * delimiter.
12880Sstevel@tonic-gate */
12890Sstevel@tonic-gate
12900Sstevel@tonic-gate while (*p)
12910Sstevel@tonic-gate if (*p++ == DELIM && *p)
12920Sstevel@tonic-gate name = p;
12930Sstevel@tonic-gate return (name);
12940Sstevel@tonic-gate }
12950Sstevel@tonic-gate
12960Sstevel@tonic-gate static void
usage(void)12970Sstevel@tonic-gate usage(void)
12980Sstevel@tonic-gate {
12990Sstevel@tonic-gate /*
13000Sstevel@tonic-gate * Display usage message.
13010Sstevel@tonic-gate */
13020Sstevel@tonic-gate
13030Sstevel@tonic-gate if (mve) {
13040Sstevel@tonic-gate (void) fprintf(stderr, gettext(
13050Sstevel@tonic-gate "Usage: mv [-f] [-i] f1 f2\n"
13060Sstevel@tonic-gate " mv [-f] [-i] f1 ... fn d1\n"
13070Sstevel@tonic-gate " mv [-f] [-i] d1 d2\n"));
13080Sstevel@tonic-gate } else if (lnk) {
13094774Sas145665 #ifdef XPG4
13100Sstevel@tonic-gate (void) fprintf(stderr, gettext(
13110Sstevel@tonic-gate "Usage: ln [-f] [-s] f1 [f2]\n"
13120Sstevel@tonic-gate " ln [-f] [-s] f1 ... fn d1\n"
13130Sstevel@tonic-gate " ln [-f] -s d1 d2\n"));
13140Sstevel@tonic-gate #else
13154774Sas145665 (void) fprintf(stderr, gettext(
13160Sstevel@tonic-gate "Usage: ln [-f] [-n] [-s] f1 [f2]\n"
13170Sstevel@tonic-gate " ln [-f] [-n] [-s] f1 ... fn d1\n"
13180Sstevel@tonic-gate " ln [-f] [-n] -s d1 d2\n"));
13190Sstevel@tonic-gate #endif
13200Sstevel@tonic-gate } else if (cpy) {
13210Sstevel@tonic-gate (void) fprintf(stderr, gettext(
13225331Samw "Usage: cp [-f] [-i] [-p] [-@] [-/] f1 f2\n"
13235331Samw " cp [-f] [-i] [-p] [-@] [-/] f1 ... fn d1\n"
13245331Samw " cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] [-/] "
13250Sstevel@tonic-gate "d1 ... dn-1 dn\n"));
13260Sstevel@tonic-gate }
13270Sstevel@tonic-gate exit(2);
13280Sstevel@tonic-gate }
13290Sstevel@tonic-gate
13300Sstevel@tonic-gate /*
13310Sstevel@tonic-gate * chg_time()
13320Sstevel@tonic-gate *
13330Sstevel@tonic-gate * Try to preserve modification and access time.
13340Sstevel@tonic-gate * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
1335*10709SRoger.Faulkner@Sun.COM * don't report a utimensat() failure.
1336*10709SRoger.Faulkner@Sun.COM * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
13370Sstevel@tonic-gate * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
13380Sstevel@tonic-gate * exit status only if pflg is set.
1339*10709SRoger.Faulkner@Sun.COM * utimensat(2) is being used to achieve granularity in nanoseconds
1340*10709SRoger.Faulkner@Sun.COM * (if supported by the underlying file system) while setting file times.
13410Sstevel@tonic-gate */
13420Sstevel@tonic-gate static int
chg_time(char * to,struct stat ss)13430Sstevel@tonic-gate chg_time(char *to, struct stat ss)
13440Sstevel@tonic-gate {
1345*10709SRoger.Faulkner@Sun.COM struct timespec times[2];
13460Sstevel@tonic-gate int rc;
13470Sstevel@tonic-gate
1348*10709SRoger.Faulkner@Sun.COM times[0] = ss.st_atim;
1349*10709SRoger.Faulkner@Sun.COM times[1] = ss.st_mtim;
13500Sstevel@tonic-gate
1351*10709SRoger.Faulkner@Sun.COM rc = utimensat(AT_FDCWD, to, times, 0);
13520Sstevel@tonic-gate #ifdef XPG4
13530Sstevel@tonic-gate if ((pflg || mve) && rc != 0) {
13540Sstevel@tonic-gate (void) fprintf(stderr,
13550Sstevel@tonic-gate gettext("%s: cannot set times for %s: "), cmd, to);
13560Sstevel@tonic-gate perror("");
13570Sstevel@tonic-gate if (pflg)
13580Sstevel@tonic-gate return (1);
13590Sstevel@tonic-gate }
13600Sstevel@tonic-gate #endif
13610Sstevel@tonic-gate
13620Sstevel@tonic-gate return (0);
13630Sstevel@tonic-gate
13640Sstevel@tonic-gate }
13650Sstevel@tonic-gate
13660Sstevel@tonic-gate /*
13670Sstevel@tonic-gate * chg_mode()
13680Sstevel@tonic-gate *
13690Sstevel@tonic-gate * This function is called upon "cp -p" or mv across filesystems.
13700Sstevel@tonic-gate *
13710Sstevel@tonic-gate * Try to preserve the owner and group id. If chown() fails,
13720Sstevel@tonic-gate * only print a diagnostic message if doing a mv in the XPG4 version;
13730Sstevel@tonic-gate * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear
13740Sstevel@tonic-gate * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
13750Sstevel@tonic-gate * non-zero exit status because this is a security violation.
13760Sstevel@tonic-gate * Try to preserve permissions.
13770Sstevel@tonic-gate * If this is the XPG4 version and chmod() fails, print a diagnostic message
13780Sstevel@tonic-gate * and arrange for a non-zero exit status.
13790Sstevel@tonic-gate * If this is the Solaris version and chmod() fails, do not print a
13800Sstevel@tonic-gate * diagnostic message or exit with a non-zero value.
13810Sstevel@tonic-gate */
13820Sstevel@tonic-gate static int
chg_mode(char * target,uid_t uid,gid_t gid,mode_t mode)13830Sstevel@tonic-gate chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
13840Sstevel@tonic-gate {
13850Sstevel@tonic-gate int clearflg = 0; /* controls message printed upon chown() error */
13860Sstevel@tonic-gate
13870Sstevel@tonic-gate if (chown(target, uid, gid) != 0) {
13880Sstevel@tonic-gate #ifdef XPG4
13890Sstevel@tonic-gate if (mve) {
13900Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot change"
13910Sstevel@tonic-gate " owner and group of %s: "), cmd, target);
13920Sstevel@tonic-gate perror("");
13930Sstevel@tonic-gate }
13940Sstevel@tonic-gate #endif
13950Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) {
13960Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */
13970Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID;
13980Sstevel@tonic-gate ++clearflg;
13990Sstevel@tonic-gate }
14000Sstevel@tonic-gate }
14010Sstevel@tonic-gate if (chmod(target, mode) != 0) {
14020Sstevel@tonic-gate if (clearflg) {
14030Sstevel@tonic-gate (void) fprintf(stderr, gettext(
14040Sstevel@tonic-gate "%s: cannot clear S_ISUID and S_ISGID bits in"
14050Sstevel@tonic-gate " %s: "), cmd, target);
14060Sstevel@tonic-gate perror("");
14070Sstevel@tonic-gate /* cp -p should get non-zero exit; mv should not */
14080Sstevel@tonic-gate if (pflg)
14090Sstevel@tonic-gate return (1);
14100Sstevel@tonic-gate }
14110Sstevel@tonic-gate #ifdef XPG4
14120Sstevel@tonic-gate else {
14130Sstevel@tonic-gate (void) fprintf(stderr, gettext(
14140Sstevel@tonic-gate "%s: cannot set permissions for %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 #endif
14210Sstevel@tonic-gate }
14220Sstevel@tonic-gate return (0);
14230Sstevel@tonic-gate
14240Sstevel@tonic-gate }
14250Sstevel@tonic-gate
14260Sstevel@tonic-gate static void
Perror(char * s)14270Sstevel@tonic-gate Perror(char *s)
14280Sstevel@tonic-gate {
14290Sstevel@tonic-gate char buf[PATH_MAX + 10];
14300Sstevel@tonic-gate
14310Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
14320Sstevel@tonic-gate perror(buf);
14330Sstevel@tonic-gate }
14340Sstevel@tonic-gate
14350Sstevel@tonic-gate static void
Perror2(char * s1,char * s2)14360Sstevel@tonic-gate Perror2(char *s1, char *s2)
14370Sstevel@tonic-gate {
14380Sstevel@tonic-gate char buf[PATH_MAX + 20];
14390Sstevel@tonic-gate
14400Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
14410Sstevel@tonic-gate cmd, gettext(s1), gettext(s2));
14420Sstevel@tonic-gate perror(buf);
14430Sstevel@tonic-gate }
14440Sstevel@tonic-gate
14450Sstevel@tonic-gate /*
14460Sstevel@tonic-gate * used for cp -R and for mv across file systems
14470Sstevel@tonic-gate */
14480Sstevel@tonic-gate static int
copydir(char * source,char * target)14490Sstevel@tonic-gate copydir(char *source, char *target)
14500Sstevel@tonic-gate {
14510Sstevel@tonic-gate int ret, attret = 0;
14525374Sbasabi int sattret = 0;
14530Sstevel@tonic-gate int pret = 0; /* need separate flag if -p is specified */
14540Sstevel@tonic-gate mode_t fixmode = (mode_t)0; /* cleanup mode after copy */
14550Sstevel@tonic-gate struct stat s1save;
1456789Sahrens acl_t *s1acl_save;
14575374Sbasabi int error = 0;
1458789Sahrens
1459789Sahrens s1acl_save = NULL;
14600Sstevel@tonic-gate
14610Sstevel@tonic-gate if (cpy && !rflg) {
14620Sstevel@tonic-gate (void) fprintf(stderr,
14630Sstevel@tonic-gate gettext("%s: %s: is a directory\n"), cmd, source);
14640Sstevel@tonic-gate return (1);
14650Sstevel@tonic-gate }
14660Sstevel@tonic-gate
14670Sstevel@tonic-gate if (stat(target, &s2) < 0) {
14680Sstevel@tonic-gate if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
14690Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", cmd);
14700Sstevel@tonic-gate perror(target);
14710Sstevel@tonic-gate return (1);
14720Sstevel@tonic-gate }
14730Sstevel@tonic-gate if (stat(target, &s2) == 0) {
14740Sstevel@tonic-gate fixmode = s2.st_mode;
14750Sstevel@tonic-gate } else {
14760Sstevel@tonic-gate fixmode = s1.st_mode;
14770Sstevel@tonic-gate }
14780Sstevel@tonic-gate (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
14790Sstevel@tonic-gate } else if (!(ISDIR(s2))) {
14800Sstevel@tonic-gate (void) fprintf(stderr,
14810Sstevel@tonic-gate gettext("%s: %s: not a directory.\n"), cmd, target);
14820Sstevel@tonic-gate return (1);
14830Sstevel@tonic-gate }
14840Sstevel@tonic-gate if (pflg || mve) {
14850Sstevel@tonic-gate /*
14860Sstevel@tonic-gate * Save s1 (stat information for source dir) and acl info,
14870Sstevel@tonic-gate * if any, so that ownership, modes, times, and acl's can
14880Sstevel@tonic-gate * be reserved during "cp -p" or mv.
14890Sstevel@tonic-gate * s1 gets overwritten when doing the recursive copy.
14900Sstevel@tonic-gate */
14910Sstevel@tonic-gate s1save = s1;
1492789Sahrens if (s1acl != NULL) {
1493789Sahrens s1acl_save = acl_dup(s1acl);
1494789Sahrens if (s1acl_save == NULL) {
1495789Sahrens (void) fprintf(stderr, gettext("%s: "
1496789Sahrens "Insufficient memory to save acl"
1497789Sahrens " entry\n"), cmd);
1498789Sahrens if (pflg)
1499789Sahrens return (1);
1500789Sahrens
15010Sstevel@tonic-gate }
15024774Sas145665 #ifdef XPG4
15034774Sas145665 else {
15044774Sas145665 (void) fprintf(stderr, gettext("%s: "
15054774Sas145665 "Insufficient memory to save acl"
15064774Sas145665 " entry\n"), cmd);
15074774Sas145665 if (pflg)
15084774Sas145665 return (1);
15094774Sas145665 }
15100Sstevel@tonic-gate #endif
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate }
15130Sstevel@tonic-gate
15140Sstevel@tonic-gate ret = rcopy(source, target);
15150Sstevel@tonic-gate
15160Sstevel@tonic-gate /*
15170Sstevel@tonic-gate * Once we created a directory, go ahead and set
15180Sstevel@tonic-gate * its attributes, e.g. acls and time. The info
15190Sstevel@tonic-gate * may get overwritten if we continue traversing
15200Sstevel@tonic-gate * down the tree.
15210Sstevel@tonic-gate *
15220Sstevel@tonic-gate * ACL for directory
15230Sstevel@tonic-gate */
15240Sstevel@tonic-gate if (pflg || mve) {
15251231Smarks if ((pret = chg_mode(target, UID(s1save), GID(s1save),
15261231Smarks FMODE(s1save))) == 0)
15271231Smarks pret = chg_time(target, s1save);
15281231Smarks ret += pret;
1529789Sahrens if (s1acl_save != NULL) {
1530789Sahrens if (acl_set(target, s1acl_save) < 0) {
15315374Sbasabi error++;
15320Sstevel@tonic-gate #ifdef XPG4
15330Sstevel@tonic-gate if (pflg || mve) {
15340Sstevel@tonic-gate #else
15350Sstevel@tonic-gate if (pflg) {
15360Sstevel@tonic-gate #endif
15370Sstevel@tonic-gate (void) fprintf(stderr, gettext(
15380Sstevel@tonic-gate "%s: failed to set acl entries "
15390Sstevel@tonic-gate "on %s\n"), cmd, target);
15400Sstevel@tonic-gate if (pflg) {
1541789Sahrens acl_free(s1acl_save);
1542789Sahrens s1acl_save = NULL;
15430Sstevel@tonic-gate ret++;
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate /* else: silent and continue */
15470Sstevel@tonic-gate }
1548789Sahrens acl_free(s1acl_save);
1549789Sahrens s1acl_save = NULL;
15500Sstevel@tonic-gate }
15510Sstevel@tonic-gate } else if (fixmode != (mode_t)0)
15520Sstevel@tonic-gate (void) chmod(target, fixmode & MODEBITS);
15530Sstevel@tonic-gate
15545331Samw if (pflg || atflg || mve || saflg) {
15550Sstevel@tonic-gate attret = copyattributes(source, target);
15560Sstevel@tonic-gate if (!attrsilent && attret != 0) {
15570Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Failed to preserve"
15580Sstevel@tonic-gate " extended attributes of directory"
15590Sstevel@tonic-gate " %s\n"), cmd, source);
15600Sstevel@tonic-gate } else {
15610Sstevel@tonic-gate /*
15620Sstevel@tonic-gate * Otherwise ignore failure.
15630Sstevel@tonic-gate */
15640Sstevel@tonic-gate attret = 0;
15650Sstevel@tonic-gate }
15665331Samw /* Copy extended system attributes */
15675331Samw if (pflg || mve || saflg) {
15685374Sbasabi sattret = copy_sysattr(source, target);
15695374Sbasabi if (sattret != 0) {
15705331Samw (void) fprintf(stderr, gettext(
15715331Samw "%s: Failed to preserve "
15725331Samw "extended system attributes "
15735331Samw "of directory %s\n"), cmd, source);
15745331Samw }
15755331Samw }
15760Sstevel@tonic-gate }
15775374Sbasabi if (attret != 0 || sattret != 0 || error != 0)
15785374Sbasabi return (1);
15790Sstevel@tonic-gate return (ret);
15800Sstevel@tonic-gate }
15810Sstevel@tonic-gate
15820Sstevel@tonic-gate static int
15830Sstevel@tonic-gate copyspecial(char *target)
15840Sstevel@tonic-gate {
15850Sstevel@tonic-gate int ret = 0;
15860Sstevel@tonic-gate
15870Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
15880Sstevel@tonic-gate (void) fprintf(stderr, gettext(
15890Sstevel@tonic-gate "cp: cannot create special file %s: "), target);
15900Sstevel@tonic-gate perror("");
15910Sstevel@tonic-gate return (1);
15920Sstevel@tonic-gate }
15930Sstevel@tonic-gate
15940Sstevel@tonic-gate if (pflg) {
15950Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
15960Sstevel@tonic-gate ret = chg_time(target, s1);
15970Sstevel@tonic-gate }
15980Sstevel@tonic-gate
15990Sstevel@tonic-gate return (ret);
16000Sstevel@tonic-gate }
16010Sstevel@tonic-gate
16020Sstevel@tonic-gate static int
16030Sstevel@tonic-gate use_stdin(void)
16040Sstevel@tonic-gate {
16050Sstevel@tonic-gate #ifdef XPG4
16060Sstevel@tonic-gate return (1);
16070Sstevel@tonic-gate #else
16080Sstevel@tonic-gate return (isatty(fileno(stdin)));
16090Sstevel@tonic-gate #endif
16100Sstevel@tonic-gate }
16110Sstevel@tonic-gate
16125331Samw /* Copy non-system extended attributes */
16135331Samw
16140Sstevel@tonic-gate static int
16150Sstevel@tonic-gate copyattributes(char *source, char *target)
16160Sstevel@tonic-gate {
16170Sstevel@tonic-gate struct dirent *dp;
16180Sstevel@tonic-gate int error = 0;
1619789Sahrens int aclerror;
16200Sstevel@tonic-gate mode_t mode;
16210Sstevel@tonic-gate int clearflg = 0;
1622789Sahrens acl_t *xacl = NULL;
1623789Sahrens acl_t *attrdiracl = NULL;
1624*10709SRoger.Faulkner@Sun.COM struct timespec times[2];
16250Sstevel@tonic-gate
16265331Samw
16275331Samw if (pathconf(source, _PC_XATTR_EXISTS) != 1)
16280Sstevel@tonic-gate return (0);
16290Sstevel@tonic-gate
16300Sstevel@tonic-gate if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
16310Sstevel@tonic-gate if (!attrsilent) {
16320Sstevel@tonic-gate (void) fprintf(stderr,
16330Sstevel@tonic-gate gettext(
16340Sstevel@tonic-gate "%s: cannot preserve extended attributes, "
16350Sstevel@tonic-gate "operation not supported on file"
16360Sstevel@tonic-gate " %s\n"), cmd, target);
16370Sstevel@tonic-gate }
16380Sstevel@tonic-gate return (1);
16390Sstevel@tonic-gate }
16405331Samw if (open_source(source) != 0)
16415331Samw return (1);
16425331Samw if (open_target_srctarg_attrdirs(source, target) != 0)
16435331Samw return (1);
16445331Samw if (open_attrdirp(source) != 0)
16455331Samw return (1);
16460Sstevel@tonic-gate
16470Sstevel@tonic-gate if (pflg || mve) {
16480Sstevel@tonic-gate if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
16490Sstevel@tonic-gate if (!attrsilent) {
16500Sstevel@tonic-gate (void) fprintf(stderr,
16514774Sas145665 gettext("%s: failed to set file mode"
16524774Sas145665 " correctly on attribute directory of"
16534774Sas145665 " file %s: "), cmd, target);
16540Sstevel@tonic-gate perror("");
16550Sstevel@tonic-gate ++error;
16560Sstevel@tonic-gate }
16570Sstevel@tonic-gate }
16580Sstevel@tonic-gate
16590Sstevel@tonic-gate if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
16600Sstevel@tonic-gate if (!attrsilent) {
16610Sstevel@tonic-gate (void) fprintf(stderr,
16620Sstevel@tonic-gate gettext("%s: failed to set file"
16630Sstevel@tonic-gate " ownership correctly on attribute"
16640Sstevel@tonic-gate " directory of file %s: "), cmd, target);
16650Sstevel@tonic-gate perror("");
16660Sstevel@tonic-gate ++error;
16670Sstevel@tonic-gate }
16680Sstevel@tonic-gate }
16690Sstevel@tonic-gate /*
16700Sstevel@tonic-gate * Now that we are the owner we can update st_ctime by calling
1671*10709SRoger.Faulkner@Sun.COM * utimensat.
16720Sstevel@tonic-gate */
1673*10709SRoger.Faulkner@Sun.COM times[0] = attrdir.st_atim;
1674*10709SRoger.Faulkner@Sun.COM times[1] = attrdir.st_mtim;
1675*10709SRoger.Faulkner@Sun.COM if (utimensat(targetdirfd, ".", times, 0) < 0) {
16760Sstevel@tonic-gate if (!attrsilent) {
16770Sstevel@tonic-gate (void) fprintf(stderr,
16784774Sas145665 gettext("%s: cannot set attribute times"
16794774Sas145665 " for %s: "), cmd, target);
16800Sstevel@tonic-gate perror("");
16810Sstevel@tonic-gate ++error;
16820Sstevel@tonic-gate }
16830Sstevel@tonic-gate }
16840Sstevel@tonic-gate
16850Sstevel@tonic-gate /*
16860Sstevel@tonic-gate * Now set owner and group of attribute directory, implies
16870Sstevel@tonic-gate * changing the ACL of the hidden attribute directory first.
16880Sstevel@tonic-gate */
1689789Sahrens if ((aclerror = facl_get(sourcedirfd,
1690789Sahrens ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
16910Sstevel@tonic-gate if (!attrsilent) {
16920Sstevel@tonic-gate (void) fprintf(stderr, gettext(
16930Sstevel@tonic-gate "%s: failed to get acl entries of"
16940Sstevel@tonic-gate " attribute directory for"
1695789Sahrens " %s : %s\n"), cmd,
1696789Sahrens source, acl_strerror(aclerror));
16970Sstevel@tonic-gate ++error;
16980Sstevel@tonic-gate }
16990Sstevel@tonic-gate }
1700789Sahrens
1701789Sahrens if (attrdiracl) {
1702789Sahrens if (facl_set(targetdirfd, attrdiracl) != 0) {
17030Sstevel@tonic-gate if (!attrsilent) {
17040Sstevel@tonic-gate (void) fprintf(stderr, gettext(
1705789Sahrens "%s: failed to set acl entries"
1706789Sahrens " on attribute directory "
1707789Sahrens "for %s\n"), cmd, target);
17080Sstevel@tonic-gate ++error;
17090Sstevel@tonic-gate }
1710789Sahrens acl_free(attrdiracl);
1711789Sahrens attrdiracl = NULL;
17120Sstevel@tonic-gate }
17130Sstevel@tonic-gate }
17140Sstevel@tonic-gate }
17150Sstevel@tonic-gate
17165331Samw while ((dp = readdir(srcdirp)) != NULL) {
17175331Samw int ret;
17180Sstevel@tonic-gate
17195331Samw if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
17205331Samw continue;
17215331Samw else if (ret > 0) {
17220Sstevel@tonic-gate ++error;
17235331Samw goto out;
17240Sstevel@tonic-gate }
17250Sstevel@tonic-gate
17260Sstevel@tonic-gate if (pflg || mve) {
1727789Sahrens if ((aclerror = facl_get(srcattrfd,
1728789Sahrens ACL_NO_TRIVIAL, &xacl)) != 0) {
17290Sstevel@tonic-gate if (!attrsilent) {
17300Sstevel@tonic-gate (void) fprintf(stderr, gettext(
17310Sstevel@tonic-gate "%s: failed to get acl entries of"
17320Sstevel@tonic-gate " attribute %s for"
1733789Sahrens " %s: %s"), cmd, dp->d_name,
1734789Sahrens source, acl_strerror(aclerror));
17350Sstevel@tonic-gate ++error;
17360Sstevel@tonic-gate }
17370Sstevel@tonic-gate }
17380Sstevel@tonic-gate }
17390Sstevel@tonic-gate
17400Sstevel@tonic-gate /*
17410Sstevel@tonic-gate * preserve ACL
17420Sstevel@tonic-gate */
1743789Sahrens if ((pflg || mve) && xacl != NULL) {
1744789Sahrens if ((facl_set(targattrfd, xacl)) < 0) {
17450Sstevel@tonic-gate if (!attrsilent) {
17460Sstevel@tonic-gate (void) fprintf(stderr, gettext(
17470Sstevel@tonic-gate "%s: failed to set acl entries on"
17480Sstevel@tonic-gate " attribute %s for"
17490Sstevel@tonic-gate "%s\n"), cmd, dp->d_name, target);
17500Sstevel@tonic-gate ++error;
17510Sstevel@tonic-gate }
1752789Sahrens acl_free(xacl);
1753789Sahrens xacl = NULL;
17540Sstevel@tonic-gate }
17550Sstevel@tonic-gate }
17560Sstevel@tonic-gate
17575331Samw if (writefile(srcattrfd, targattrfd, source, target,
17585331Samw dp->d_name, dp->d_name, &s3, &s4) != 0) {
17590Sstevel@tonic-gate if (!attrsilent) {
17600Sstevel@tonic-gate ++error;
17610Sstevel@tonic-gate }
17620Sstevel@tonic-gate goto next;
17630Sstevel@tonic-gate }
17640Sstevel@tonic-gate
17650Sstevel@tonic-gate if (pflg || mve) {
17660Sstevel@tonic-gate mode = FMODE(s3);
17670Sstevel@tonic-gate
17680Sstevel@tonic-gate if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
17690Sstevel@tonic-gate if (!attrsilent) {
17700Sstevel@tonic-gate (void) fprintf(stderr,
17710Sstevel@tonic-gate gettext("%s: cannot change"
17720Sstevel@tonic-gate " owner and group of"
17730Sstevel@tonic-gate " attribute %s for" " file"
17740Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target);
17750Sstevel@tonic-gate perror("");
17760Sstevel@tonic-gate ++error;
17770Sstevel@tonic-gate }
17780Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) {
17790Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */
17800Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID;
17810Sstevel@tonic-gate ++clearflg;
17820Sstevel@tonic-gate }
17830Sstevel@tonic-gate }
1784*10709SRoger.Faulkner@Sun.COM times[0] = s3.st_atim;
1785*10709SRoger.Faulkner@Sun.COM times[1] = s3.st_mtim;
1786*10709SRoger.Faulkner@Sun.COM if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
17870Sstevel@tonic-gate if (!attrsilent) {
17880Sstevel@tonic-gate (void) fprintf(stderr,
17890Sstevel@tonic-gate gettext("%s: cannot set attribute"
17900Sstevel@tonic-gate " times for %s: "), cmd, target);
17910Sstevel@tonic-gate perror("");
17920Sstevel@tonic-gate ++error;
17930Sstevel@tonic-gate }
17940Sstevel@tonic-gate }
17950Sstevel@tonic-gate if (fchmod(targattrfd, mode) != 0) {
17960Sstevel@tonic-gate if (clearflg) {
17970Sstevel@tonic-gate (void) fprintf(stderr, gettext(
17980Sstevel@tonic-gate "%s: cannot clear S_ISUID and "
17990Sstevel@tonic-gate "S_ISGID bits in attribute %s"
18000Sstevel@tonic-gate " for file"
18010Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target);
18020Sstevel@tonic-gate } else {
18030Sstevel@tonic-gate if (!attrsilent) {
18040Sstevel@tonic-gate (void) fprintf(stderr,
18054774Sas145665 gettext(
18060Sstevel@tonic-gate "%s: cannot set permissions of attribute"
18070Sstevel@tonic-gate " %s for %s: "), cmd, dp->d_name, target);
18080Sstevel@tonic-gate perror("");
18090Sstevel@tonic-gate ++error;
18100Sstevel@tonic-gate }
18110Sstevel@tonic-gate }
18120Sstevel@tonic-gate }
18131231Smarks if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
18141231Smarks if (!attrsilent) {
18151231Smarks (void) fprintf(stderr, gettext(
18161231Smarks "%s: failed to set acl entries on"
18171231Smarks " attribute %s for"
18181231Smarks "%s\n"), cmd, dp->d_name, target);
18191231Smarks ++error;
18201231Smarks }
18211231Smarks acl_free(xacl);
18221231Smarks xacl = NULL;
18231231Smarks }
18240Sstevel@tonic-gate }
18250Sstevel@tonic-gate next:
1826789Sahrens if (xacl != NULL) {
1827789Sahrens acl_free(xacl);
1828789Sahrens xacl = NULL;
18290Sstevel@tonic-gate }
18300Sstevel@tonic-gate if (srcattrfd != -1)
18310Sstevel@tonic-gate (void) close(srcattrfd);
18320Sstevel@tonic-gate if (targattrfd != -1)
18330Sstevel@tonic-gate (void) close(targattrfd);
18340Sstevel@tonic-gate srcattrfd = targattrfd = -1;
18350Sstevel@tonic-gate }
18360Sstevel@tonic-gate out:
1837789Sahrens if (xacl != NULL) {
1838789Sahrens acl_free(xacl);
1839789Sahrens xacl = NULL;
1840789Sahrens }
1841789Sahrens if (attrdiracl != NULL) {
1842789Sahrens acl_free(attrdiracl);
1843789Sahrens attrdiracl = NULL;
1844789Sahrens }
18455331Samw
18465331Samw if (!saflg && !pflg && !mve)
18475331Samw close_all();
18485331Samw return (error == 0 ? 0 : 1);
18495331Samw }
18505331Samw
18515331Samw /* Copy extended system attributes from source to target */
18525331Samw
18535331Samw static int
18545331Samw copy_sysattr(char *source, char *target)
18555331Samw {
18565331Samw struct dirent *dp;
18575331Samw nvlist_t *response;
18585331Samw int error = 0;
18595557Sbasabi int target_sa_support = 0;
18605331Samw
18615331Samw if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
18625331Samw return (0);
18635331Samw
18645331Samw if (open_source(source) != 0)
18655331Samw return (1);
18665331Samw
18675331Samw /*
18685331Samw * Gets non default extended system attributes from the
18695331Samw * source file to copy to the target. The target has
18705331Samw * the defaults set when its created and thus no need
18715331Samw * to copy the defaults.
18725331Samw */
18735331Samw response = sysattr_list(cmd, srcfd, source);
18745331Samw
18755557Sbasabi if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
18765557Sbasabi if (response != NULL) {
18775557Sbasabi (void) fprintf(stderr,
18785557Sbasabi gettext(
18795557Sbasabi "%s: cannot preserve extended system "
18805557Sbasabi "attribute, operation not supported on file"
18815557Sbasabi " %s\n"), cmd, target);
18825331Samw error++;
18835331Samw goto out;
18845331Samw }
18855331Samw } else {
18865557Sbasabi target_sa_support = 1;
18875331Samw }
18885331Samw
18895557Sbasabi if (target_sa_support) {
18905557Sbasabi if (srcdirp == NULL) {
18915557Sbasabi if (open_target_srctarg_attrdirs(source,
18925557Sbasabi target) != 0) {
18935557Sbasabi error++;
18945557Sbasabi goto out;
18955557Sbasabi }
18965557Sbasabi if (open_attrdirp(source) != 0) {
18975557Sbasabi error++;
18985557Sbasabi goto out;
18995557Sbasabi }
19005557Sbasabi } else {
19015557Sbasabi rewind_attrdir(srcdirp);
19025331Samw }
19035557Sbasabi while ((dp = readdir(srcdirp)) != NULL) {
19045557Sbasabi nvlist_t *res;
19055557Sbasabi int ret;
19065331Samw
19075557Sbasabi if ((ret = traverse_attrfile(dp, source, target,
19085557Sbasabi 0)) == -1)
19095557Sbasabi continue;
19105557Sbasabi else if (ret > 0) {
19115557Sbasabi ++error;
19125557Sbasabi goto out;
19135331Samw }
19145557Sbasabi /*
19155557Sbasabi * Gets non default extended system attributes from the
19165557Sbasabi * attribute file to copy to the target. The target has
19175557Sbasabi * the defaults set when its created and thus no need
19185557Sbasabi * to copy the defaults.
19195557Sbasabi */
19205557Sbasabi if (dp->d_name != NULL) {
19215557Sbasabi res = sysattr_list(cmd, srcattrfd, dp->d_name);
19225557Sbasabi if (res == NULL)
19235557Sbasabi goto next;
19245331Samw
19255331Samw /*
19265331Samw * Copy non default extended system attributes of named
19275331Samw * attribute file.
19285331Samw */
19295557Sbasabi if (fsetattr(targattrfd,
19305557Sbasabi XATTR_VIEW_READWRITE, res) != 0) {
19315557Sbasabi ++error;
19325557Sbasabi (void) fprintf(stderr, gettext("%s: "
19335557Sbasabi "Failed to copy extended system "
19345557Sbasabi "attributes from attribute file "
19355557Sbasabi "%s of %s to %s\n"), cmd,
19365557Sbasabi dp->d_name, source, target);
19375557Sbasabi }
19385331Samw }
19395331Samw next:
19405557Sbasabi if (srcattrfd != -1)
19415557Sbasabi (void) close(srcattrfd);
19425557Sbasabi if (targattrfd != -1)
19435557Sbasabi (void) close(targattrfd);
19445557Sbasabi srcattrfd = targattrfd = -1;
19455557Sbasabi if (res != NULL)
19465557Sbasabi nvlist_free(res);
19475557Sbasabi }
19485331Samw }
19495331Samw /* Copy source file non default extended system attributes to target */
19505557Sbasabi if (target_sa_support && (response != NULL) &&
19515331Samw (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
19525331Samw ++error;
19535374Sbasabi (void) fprintf(stderr, gettext("%s: Failed to "
19545374Sbasabi "copy extended system attributes from "
19555374Sbasabi "%s to %s\n"), cmd, source, target);
19565331Samw }
19575331Samw out:
19585331Samw if (response != NULL)
19595331Samw nvlist_free(response);
19605331Samw close_all();
19610Sstevel@tonic-gate return (error == 0 ? 0 : 1);
19620Sstevel@tonic-gate }
19630Sstevel@tonic-gate
19645331Samw /* Open the source file */
19655331Samw
19665331Samw int
19675331Samw open_source(char *src)
19685331Samw {
19695331Samw int error = 0;
19705331Samw
19715331Samw srcfd = -1;
19725331Samw if ((srcfd = open(src, O_RDONLY)) == -1) {
19735331Samw if (pflg && attrsilent) {
19745331Samw error++;
19755331Samw goto out;
19765331Samw }
19775331Samw if (!attrsilent) {
19785331Samw (void) fprintf(stderr,
19795331Samw gettext("%s: cannot open file"
19805331Samw " %s: "), cmd, src);
19815331Samw perror("");
19825331Samw }
19835331Samw ++error;
19845331Samw }
19855331Samw out:
19865331Samw if (error)
19875331Samw close_all();
19885331Samw return (error == 0 ? 0 : 1);
19895331Samw }
19905331Samw
19915331Samw /* Open source attribute dir, target and target attribute dir. */
19925331Samw
19935331Samw int
19945331Samw open_target_srctarg_attrdirs(char *src, char *targ)
19955331Samw {
19965331Samw int error = 0;
19975331Samw
19985331Samw targfd = sourcedirfd = targetdirfd = -1;
19995331Samw
20005331Samw if ((targfd = open(targ, O_RDONLY)) == -1) {
20015331Samw if (pflg && attrsilent) {
20025331Samw error++;
20035331Samw goto out;
20045331Samw }
20055331Samw if (!attrsilent) {
20065331Samw (void) fprintf(stderr,
20075331Samw gettext("%s: cannot open file"
20085331Samw " %s: "), cmd, targ);
20095331Samw perror("");
20105331Samw }
20115331Samw ++error;
20125331Samw goto out;
20135331Samw }
20145331Samw
20155331Samw if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
20165331Samw if (pflg && attrsilent) {
20175331Samw error++;
20185331Samw goto out;
20195331Samw }
20205331Samw if (!attrsilent) {
20215331Samw (void) fprintf(stderr,
20225331Samw gettext("%s: cannot open attribute"
20235331Samw " directory for %s: "), cmd, src);
20245331Samw perror("");
20255331Samw }
20265331Samw ++error;
20275331Samw goto out;
20285331Samw }
20295331Samw
20305331Samw if (fstat(sourcedirfd, &attrdir) == -1) {
20315331Samw if (pflg && attrsilent) {
20325331Samw error++;
20335331Samw goto out;
20345331Samw }
20355331Samw
20365331Samw if (!attrsilent) {
20375331Samw (void) fprintf(stderr,
20385331Samw gettext("%s: could not retrieve stat"
20395331Samw " information for attribute directory"
20405331Samw "of file %s: "), cmd, src);
20415331Samw perror("");
20425331Samw }
20435331Samw ++error;
20445331Samw goto out;
20455331Samw }
20465331Samw if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
20475331Samw if (pflg && attrsilent) {
20485331Samw error++;
20495331Samw goto out;
20505331Samw }
20515331Samw if (!attrsilent) {
20525331Samw (void) fprintf(stderr,
20535331Samw gettext("%s: cannot open attribute"
20545331Samw " directory for %s: "), cmd, targ);
20555331Samw perror("");
20565331Samw }
20575331Samw ++error;
20585331Samw }
20595331Samw out:
20605331Samw if (error)
20615331Samw close_all();
20625331Samw return (error == 0 ? 0 : 1);
20635331Samw }
20645331Samw
20655331Samw int
20665331Samw open_attrdirp(char *source)
20675331Samw {
20685331Samw int tmpfd = -1;
20695331Samw int error = 0;
20705331Samw
20715331Samw /*
20725331Samw * dup sourcedirfd for use by fdopendir().
20735331Samw * fdopendir will take ownership of given fd and will close
20745331Samw * it when closedir() is called.
20755331Samw */
20765331Samw
20775331Samw if ((tmpfd = dup(sourcedirfd)) == -1) {
20785331Samw if (pflg && attrsilent) {
20795331Samw error++;
20805331Samw goto out;
20815331Samw }
20825331Samw if (!attrsilent) {
20835331Samw (void) fprintf(stderr,
20845331Samw gettext(
20855331Samw "%s: unable to dup attribute directory"
20865331Samw " file descriptor for %s: "), cmd, source);
20875331Samw perror("");
20885331Samw ++error;
20895331Samw }
20905331Samw goto out;
20915331Samw }
20925331Samw if ((srcdirp = fdopendir(tmpfd)) == NULL) {
20935331Samw if (pflg && attrsilent) {
20945331Samw error++;
20955331Samw goto out;
20965331Samw }
20975331Samw if (!attrsilent) {
20985331Samw (void) fprintf(stderr,
20995331Samw gettext("%s: failed to open attribute"
21005331Samw " directory for %s: "), cmd, source);
21015331Samw perror("");
21025331Samw ++error;
21035331Samw }
21045331Samw }
21055331Samw out:
21065331Samw if (error)
21075331Samw close_all();
21085331Samw return (error == 0 ? 0 : 1);
21095331Samw }
21105331Samw
21115331Samw /* Skips through ., .., and system attribute 'view' files */
21125331Samw int
21135331Samw traverse_attrfile(struct dirent *dp, char *source, char *target, int first)
21145331Samw {
21155331Samw int error = 0;
21165331Samw
21176091Sbasabi srcattrfd = targattrfd = -1;
21186091Sbasabi
21195331Samw if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
21205331Samw (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
21215331Samw dp->d_name[2] == '\0') ||
21225331Samw (sysattr_type(dp->d_name) == _RO_SATTR) ||
21235331Samw (sysattr_type(dp->d_name) == _RW_SATTR))
21245331Samw return (-1);
21255331Samw
21265331Samw if ((srcattrfd = openat(sourcedirfd, dp->d_name,
21275331Samw O_RDONLY)) == -1) {
21285331Samw if (!attrsilent) {
21295331Samw (void) fprintf(stderr,
21305331Samw gettext("%s: cannot open attribute %s on"
21315331Samw " file %s: "), cmd, dp->d_name, source);
21325331Samw perror("");
21335331Samw ++error;
21345331Samw goto out;
21355331Samw }
21365331Samw }
21375331Samw
21385331Samw if (fstat(srcattrfd, &s3) < 0) {
21395331Samw if (!attrsilent) {
21405331Samw (void) fprintf(stderr,
21415331Samw gettext("%s: could not stat attribute"
21425331Samw " %s on file"
21435331Samw " %s: "), cmd, dp->d_name, source);
21445331Samw perror("");
21455331Samw ++error;
21465331Samw }
21475331Samw goto out;
21485331Samw }
21495331Samw
21505331Samw if (first) {
21515331Samw (void) unlinkat(targetdirfd, dp->d_name, 0);
21525500Sbasabi if ((targattrfd = openat(targetdirfd, dp->d_name,
21535500Sbasabi O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
21545500Sbasabi if (!attrsilent) {
21555500Sbasabi (void) fprintf(stderr,
21565500Sbasabi gettext("%s: could not create attribute"
21575500Sbasabi " %s on file %s: "), cmd, dp->d_name,
21585500Sbasabi target);
21595500Sbasabi perror("");
21605500Sbasabi ++error;
21615500Sbasabi }
21625500Sbasabi goto out;
21635500Sbasabi }
21645331Samw } else {
21655500Sbasabi if ((targattrfd = openat(targetdirfd, dp->d_name,
21665500Sbasabi O_RDONLY)) == -1) {
21675500Sbasabi if (!attrsilent) {
21685500Sbasabi (void) fprintf(stderr,
21695500Sbasabi gettext("%s: could not open attribute"
21705500Sbasabi " %s on file %s: "), cmd, dp->d_name,
21715500Sbasabi target);
21725500Sbasabi perror("");
21735500Sbasabi ++error;
21745500Sbasabi }
21755500Sbasabi goto out;
21765500Sbasabi }
21775331Samw }
21785331Samw
21795331Samw
21805331Samw if (fstat(targattrfd, &s4) < 0) {
21815331Samw if (!attrsilent) {
21825331Samw (void) fprintf(stderr,
21835331Samw gettext("%s: could not stat attribute"
21845331Samw " %s on file"
21855331Samw " %s: "), cmd, dp->d_name, target);
21865331Samw perror("");
21875331Samw ++error;
21885331Samw }
21895331Samw }
21905331Samw
21915331Samw out:
21925331Samw if (error) {
21935331Samw if (srcattrfd != -1)
21945331Samw (void) close(srcattrfd);
21955331Samw if (targattrfd != -1)
21966091Sbasabi (void) close(targattrfd);
21976091Sbasabi srcattrfd = targattrfd = -1;
21985331Samw }
21995331Samw return (error == 0 ? 0 :1);
22005331Samw }
22015331Samw
22025331Samw void
22035331Samw rewind_attrdir(DIR * sdp)
22045331Samw {
22055331Samw int pwdfd;
22065331Samw
22075331Samw pwdfd = open(".", O_RDONLY);
22085331Samw if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
22095331Samw rewinddir(sdp);
22105331Samw (void) fchdir(pwdfd);
22115331Samw (void) close(pwdfd);
22125331Samw } else {
22135331Samw if (!attrsilent) {
22145331Samw (void) fprintf(stderr, gettext("%s: "
22155331Samw "failed to rewind attribute dir\n"),
22165331Samw cmd);
22175331Samw }
22185331Samw }
22195331Samw }
22205331Samw
22215331Samw void
22225331Samw close_all()
22235331Samw {
22245331Samw if (srcattrfd != -1)
22255331Samw (void) close(srcattrfd);
22265331Samw if (targattrfd != -1)
22275331Samw (void) close(targattrfd);
22285331Samw if (sourcedirfd != -1)
22295331Samw (void) close(sourcedirfd);
22305331Samw if (targetdirfd != -1)
22315331Samw (void) close(targetdirfd);
22325331Samw if (srcdirp != NULL) {
22335331Samw (void) closedir(srcdirp);
22345331Samw srcdirp = NULL;
22355331Samw }
22365331Samw if (srcfd != -1)
22375331Samw (void) close(srcfd);
22385331Samw if (targfd != -1)
22395331Samw (void) close(targfd);
22405331Samw }
2241