17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
594d2b9abSmarks * Common Development and Distribution License (the "License").
694d2b9abSmarks * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
2178cca7e2SRoger A. Faulkner
227c478bd9Sstevel@tonic-gate /*
236cdb7222SAlexander Eremin * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
246cdb7222SAlexander Eremin */
256cdb7222SAlexander Eremin
266cdb7222SAlexander Eremin /*
27ee9c203bSRenaud Manus * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
287c478bd9Sstevel@tonic-gate * Use is subject to license terms.
297c478bd9Sstevel@tonic-gate */
307c478bd9Sstevel@tonic-gate
317c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
327c478bd9Sstevel@tonic-gate /* All Rights Reserved */
337c478bd9Sstevel@tonic-gate
347c478bd9Sstevel@tonic-gate /*
3531b6814eSJohn Levon * Copyright (c) 2018, Joyent, Inc.
36*6cf3cc9dSRobert Mustacchi * Copyright 2024 Oxide Computer Company
3731b6814eSJohn Levon */
3831b6814eSJohn Levon
3931b6814eSJohn Levon /*
407c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988
417c478bd9Sstevel@tonic-gate * The Regents of the University of California
427c478bd9Sstevel@tonic-gate * All Rights Reserved
437c478bd9Sstevel@tonic-gate *
447c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from
457c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its
467c478bd9Sstevel@tonic-gate * contributors.
477c478bd9Sstevel@tonic-gate */
487c478bd9Sstevel@tonic-gate
497c478bd9Sstevel@tonic-gate /*
507c478bd9Sstevel@tonic-gate * Combined mv/cp/ln command:
517c478bd9Sstevel@tonic-gate * mv file1 file2
527c478bd9Sstevel@tonic-gate * mv dir1 dir2
537c478bd9Sstevel@tonic-gate * mv file1 ... filen dir1
547c478bd9Sstevel@tonic-gate */
557c478bd9Sstevel@tonic-gate #include <sys/time.h>
567c478bd9Sstevel@tonic-gate #include <signal.h>
577c478bd9Sstevel@tonic-gate #include <locale.h>
587c478bd9Sstevel@tonic-gate #include <stdarg.h>
597c478bd9Sstevel@tonic-gate #include <sys/acl.h>
607c478bd9Sstevel@tonic-gate #include <libcmdutils.h>
61fa9e4066Sahrens #include <aclutils.h>
62*6cf3cc9dSRobert Mustacchi #include <assert.h>
633d63ea05Sas145665 #include "getresponse.h"
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate #define FTYPE(A) (A.st_mode)
667c478bd9Sstevel@tonic-gate #define FMODE(A) (A.st_mode)
677c478bd9Sstevel@tonic-gate #define UID(A) (A.st_uid)
687c478bd9Sstevel@tonic-gate #define GID(A) (A.st_gid)
697c478bd9Sstevel@tonic-gate #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
707c478bd9Sstevel@tonic-gate #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
717c478bd9Sstevel@tonic-gate #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR)
727c478bd9Sstevel@tonic-gate #define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK)
737c478bd9Sstevel@tonic-gate #define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
747c478bd9Sstevel@tonic-gate #define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \
757c478bd9Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFBLK || \
767c478bd9Sstevel@tonic-gate (A.st_mode & S_IFMT) == S_IFIFO)
77e1636a06Sdanny webster #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK)
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate #define DELIM '/'
807c478bd9Sstevel@tonic-gate #define EQ(x, y) (strcmp(x, y) == 0)
817c478bd9Sstevel@tonic-gate #define FALSE 0
827c478bd9Sstevel@tonic-gate #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
837c478bd9Sstevel@tonic-gate #define TRUE 1
847c478bd9Sstevel@tonic-gate
85*6cf3cc9dSRobert Mustacchi typedef enum {
86*6cf3cc9dSRobert Mustacchi /*
87*6cf3cc9dSRobert Mustacchi * Indicates that after checking the file we should proceed with the
88*6cf3cc9dSRobert Mustacchi * action.
89*6cf3cc9dSRobert Mustacchi */
90*6cf3cc9dSRobert Mustacchi CHK_CONT,
91*6cf3cc9dSRobert Mustacchi /*
92*6cf3cc9dSRobert Mustacchi * Indicates that after checking the file we encountered an error that
93*6cf3cc9dSRobert Mustacchi * should be percolated up.
94*6cf3cc9dSRobert Mustacchi */
95*6cf3cc9dSRobert Mustacchi CHK_ERROR,
96*6cf3cc9dSRobert Mustacchi /*
97*6cf3cc9dSRobert Mustacchi * Indicate that the user opted to skip this file. No action should be
98*6cf3cc9dSRobert Mustacchi * taken and this should be treated as successful.
99*6cf3cc9dSRobert Mustacchi */
100*6cf3cc9dSRobert Mustacchi CHK_SKIP
101*6cf3cc9dSRobert Mustacchi } chkfiles_t;
102*6cf3cc9dSRobert Mustacchi
103*6cf3cc9dSRobert Mustacchi static chkfiles_t chkfiles(const char *, char **);
104*6cf3cc9dSRobert Mustacchi static const char *dname(const char *);
105*6cf3cc9dSRobert Mustacchi static int lnkfil(const char *, char *);
106*6cf3cc9dSRobert Mustacchi static int cpymve(const char *, char *);
107*6cf3cc9dSRobert Mustacchi static int rcopy(const char *, char *);
108*6cf3cc9dSRobert Mustacchi static int chk_different(const char *, const char *);
109*6cf3cc9dSRobert Mustacchi static int chg_time(const char *, struct stat);
110*6cf3cc9dSRobert Mustacchi static int chg_mode(const char *, uid_t, gid_t, mode_t);
111*6cf3cc9dSRobert Mustacchi static int copydir(const char *, char *);
112*6cf3cc9dSRobert Mustacchi static int copyspecial(const char *);
113*6cf3cc9dSRobert Mustacchi static int getrealpath(const char *, char *);
1147c478bd9Sstevel@tonic-gate static void usage(void);
115*6cf3cc9dSRobert Mustacchi static void Perror(const char *);
116*6cf3cc9dSRobert Mustacchi static void Perror2(const char *, const char *);
1177c478bd9Sstevel@tonic-gate static int use_stdin(void);
118*6cf3cc9dSRobert Mustacchi static int copyattributes(const char *, const char *);
119*6cf3cc9dSRobert Mustacchi static int copy_sysattr(const char *, const char *);
1207c478bd9Sstevel@tonic-gate static tree_node_t *create_tnode(dev_t, ino_t);
1217c478bd9Sstevel@tonic-gate
122da6c28aaSamw static struct stat s1, s2, s3, s4;
1237c478bd9Sstevel@tonic-gate static int cpy = FALSE;
1247c478bd9Sstevel@tonic-gate static int mve = FALSE;
1257c478bd9Sstevel@tonic-gate static int lnk = FALSE;
1267c478bd9Sstevel@tonic-gate static char *cmd;
1277c478bd9Sstevel@tonic-gate static int fflg = 0;
1287c478bd9Sstevel@tonic-gate static int pflg = 0;
1297c478bd9Sstevel@tonic-gate static int Rflg = 0; /* recursive copy */
1307c478bd9Sstevel@tonic-gate static int rflg = 0; /* recursive copy */
1317c478bd9Sstevel@tonic-gate static int sflg = 0;
1327c478bd9Sstevel@tonic-gate static int Hflg = 0; /* follow cmd line arg symlink to dir */
1337c478bd9Sstevel@tonic-gate static int Lflg = 0; /* follow symlinks */
1347c478bd9Sstevel@tonic-gate static int Pflg = 0; /* do not follow symlinks */
1357c478bd9Sstevel@tonic-gate static int atflg = 0;
1367c478bd9Sstevel@tonic-gate static int attrsilent = 0;
1377c478bd9Sstevel@tonic-gate static int targetexists = 0;
1387c478bd9Sstevel@tonic-gate static int cmdarg; /* command line argument */
1397c478bd9Sstevel@tonic-gate static avl_tree_t *stree = NULL; /* source file inode search tree */
140fa9e4066Sahrens static acl_t *s1acl;
141da6c28aaSamw static int saflg = 0; /* 'cp' extended system attr. */
142beab0215Sbasabi static int srcfd = -1;
143beab0215Sbasabi static int targfd = -1;
144beab0215Sbasabi static int sourcedirfd = -1;
145beab0215Sbasabi static int targetdirfd = -1;
146beab0215Sbasabi static DIR *srcdirp = NULL;
147beab0215Sbasabi static int srcattrfd = -1;
148beab0215Sbasabi static int targattrfd = -1;
149da6c28aaSamw static struct stat attrdir;
150da6c28aaSamw
151*6cf3cc9dSRobert Mustacchi /*
152*6cf3cc9dSRobert Mustacchi * cp, mv, and ln all have behaviors around what happens when a file already
153*6cf3cc9dSRobert Mustacchi * exists at the target. These behaviors depend on a combination of the program,
154*6cf3cc9dSRobert Mustacchi * the options specified, and the permissions of the target file.
155*6cf3cc9dSRobert Mustacchi *
156*6cf3cc9dSRobert Mustacchi * 1) Explicitly remove any target file
157*6cf3cc9dSRobert Mustacchi * 2) Always ask the user
158*6cf3cc9dSRobert Mustacchi * 3) Take no action and treat as successful
159*6cf3cc9dSRobert Mustacchi * 4) Take no action and treat as a failure
160*6cf3cc9dSRobert Mustacchi * 5) Replace the target file if permissions align, otherwise fail
161*6cf3cc9dSRobert Mustacchi * 6) Replace the target file if permissions align, otherwise prompt if stdin
162*6cf3cc9dSRobert Mustacchi * is a tty
163*6cf3cc9dSRobert Mustacchi *
164*6cf3cc9dSRobert Mustacchi * The default action varies based on the program. cp defaults to (5), mv to
165*6cf3cc9dSRobert Mustacchi * (6), and ln to (4). There are three flags that depending on the program
166*6cf3cc9dSRobert Mustacchi * will change the behavior that is taken: -f, -i, and -n. Of these only -i has
167*6cf3cc9dSRobert Mustacchi * the same meaning across all three programs. In this context, we treat these
168*6cf3cc9dSRobert Mustacchi * flags as:
169*6cf3cc9dSRobert Mustacchi *
170*6cf3cc9dSRobert Mustacchi * -f: take action (1)
171*6cf3cc9dSRobert Mustacchi * -i: take action (2)
172*6cf3cc9dSRobert Mustacchi * -n: take action (3)
173*6cf3cc9dSRobert Mustacchi *
174*6cf3cc9dSRobert Mustacchi * The following table shows which programs honor which of these flags:
175*6cf3cc9dSRobert Mustacchi *
176*6cf3cc9dSRobert Mustacchi * CP LN MV
177*6cf3cc9dSRobert Mustacchi * -f N Y Y
178*6cf3cc9dSRobert Mustacchi * -i Y Y Y
179*6cf3cc9dSRobert Mustacchi * -n Y Y N
180*6cf3cc9dSRobert Mustacchi *
181*6cf3cc9dSRobert Mustacchi * Any case where you see a 'N' above means the program has a different meaning
182*6cf3cc9dSRobert Mustacchi * for the flag. These four actions are summarized in the following enumeration.
183*6cf3cc9dSRobert Mustacchi *
184*6cf3cc9dSRobert Mustacchi * The last wrinkle with these is how they are processed on the command line. In
185*6cf3cc9dSRobert Mustacchi * general, we treat these as the last one wins. That is, if you specified -i -n
186*6cf3cc9dSRobert Mustacchi * then we would use the -n behavior. The only wrinkle for this is with mv. The
187*6cf3cc9dSRobert Mustacchi * non-POSIX form of mv any -f trump all -i options, but the xpg4 behavior was
188*6cf3cc9dSRobert Mustacchi * the last one wins. In this case -n takes the last one wins behavior with mv
189*6cf3cc9dSRobert Mustacchi * to try to make it as similar to everything else as we can.
190*6cf3cc9dSRobert Mustacchi */
191*6cf3cc9dSRobert Mustacchi typedef enum {
192*6cf3cc9dSRobert Mustacchi /*
193*6cf3cc9dSRobert Mustacchi * Take the program's default behavior.
194*6cf3cc9dSRobert Mustacchi */
195*6cf3cc9dSRobert Mustacchi TA_DEFAULT,
196*6cf3cc9dSRobert Mustacchi /*
197*6cf3cc9dSRobert Mustacchi * The user has explicitly said we should remove the file.
198*6cf3cc9dSRobert Mustacchi */
199*6cf3cc9dSRobert Mustacchi TA_OVERWRITE,
200*6cf3cc9dSRobert Mustacchi /*
201*6cf3cc9dSRobert Mustacchi * The user has said we should always prompt about the file.
202*6cf3cc9dSRobert Mustacchi */
203*6cf3cc9dSRobert Mustacchi TA_PROMPT,
204*6cf3cc9dSRobert Mustacchi /*
205*6cf3cc9dSRobert Mustacchi * The user has said we should always leave it be.
206*6cf3cc9dSRobert Mustacchi */
207*6cf3cc9dSRobert Mustacchi TA_SKIP
208*6cf3cc9dSRobert Mustacchi } target_action_t;
209*6cf3cc9dSRobert Mustacchi
210*6cf3cc9dSRobert Mustacchi target_action_t targact = TA_DEFAULT;
211*6cf3cc9dSRobert Mustacchi
212da6c28aaSamw /* Extended system attributes support */
213da6c28aaSamw
214*6cf3cc9dSRobert Mustacchi static int open_source(const char *);
215*6cf3cc9dSRobert Mustacchi static int open_target_srctarg_attrdirs(const char *, const char *);
216*6cf3cc9dSRobert Mustacchi static int open_attrdirp(const char *);
217*6cf3cc9dSRobert Mustacchi static int traverse_attrfile(struct dirent *, const char *, const char *, int);
218da6c28aaSamw static void rewind_attrdir(DIR *);
219*6cf3cc9dSRobert Mustacchi static void close_all(void);
220da6c28aaSamw
2217c478bd9Sstevel@tonic-gate
222a77d64afScf46844 int
main(int argc,char * argv[])2237c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
2247c478bd9Sstevel@tonic-gate {
2257c478bd9Sstevel@tonic-gate int c, i, r, errflg = 0;
2267c478bd9Sstevel@tonic-gate char target[PATH_MAX];
227*6cf3cc9dSRobert Mustacchi int (*move)(const char *, char *);
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate * Determine command invoked (mv, cp, or ln)
2317c478bd9Sstevel@tonic-gate */
2327c478bd9Sstevel@tonic-gate
2337c478bd9Sstevel@tonic-gate if (cmd = strrchr(argv[0], '/'))
2347c478bd9Sstevel@tonic-gate ++cmd;
2357c478bd9Sstevel@tonic-gate else
2367c478bd9Sstevel@tonic-gate cmd = argv[0];
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate /*
2397c478bd9Sstevel@tonic-gate * Set flags based on command.
2407c478bd9Sstevel@tonic-gate */
2417c478bd9Sstevel@tonic-gate
2427c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
2437c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
2447c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
2457c478bd9Sstevel@tonic-gate #endif
2467c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
2473d63ea05Sas145665 if (init_yes() < 0) {
2483d63ea05Sas145665 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
2493d63ea05Sas145665 strerror(errno));
2503d63ea05Sas145665 exit(3);
2513d63ea05Sas145665 }
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate if (EQ(cmd, "mv"))
2547c478bd9Sstevel@tonic-gate mve = TRUE;
2557c478bd9Sstevel@tonic-gate else if (EQ(cmd, "ln"))
2567c478bd9Sstevel@tonic-gate lnk = TRUE;
2577c478bd9Sstevel@tonic-gate else if (EQ(cmd, "cp"))
2587c478bd9Sstevel@tonic-gate cpy = TRUE;
2597c478bd9Sstevel@tonic-gate else {
2607c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
2617c478bd9Sstevel@tonic-gate gettext("Invalid command name (%s); expecting "
2627c478bd9Sstevel@tonic-gate "mv, cp, or ln.\n"), cmd);
2637c478bd9Sstevel@tonic-gate exit(1);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate /*
2677c478bd9Sstevel@tonic-gate * Check for options:
268*6cf3cc9dSRobert Mustacchi * cp [ -r|-R [-H|-L|-P]] [-afinp@/] file1 [file2 ...] target
269*6cf3cc9dSRobert Mustacchi * cp [-afinprR@/] file1 [file2 ...] target
270*6cf3cc9dSRobert Mustacchi * ln [-fi] [-n] [-s] file1 [file2 ...] target
271*6cf3cc9dSRobert Mustacchi * ln [-fi] [-n] [-s] file1 [file2 ...]
272*6cf3cc9dSRobert Mustacchi * mv [-f|i] [-n] file1 [file2 ...] target
273*6cf3cc9dSRobert Mustacchi * mv [-f|i] [-n] dir1 target
2747c478bd9Sstevel@tonic-gate */
2757c478bd9Sstevel@tonic-gate
2767c478bd9Sstevel@tonic-gate if (cpy) {
277*6cf3cc9dSRobert Mustacchi while ((c = getopt(argc, argv, "afHinLpPrR@/")) != EOF)
2787c478bd9Sstevel@tonic-gate switch (c) {
2797c478bd9Sstevel@tonic-gate case 'f':
2807c478bd9Sstevel@tonic-gate fflg++;
2817c478bd9Sstevel@tonic-gate break;
2827c478bd9Sstevel@tonic-gate case 'i':
283*6cf3cc9dSRobert Mustacchi targact = TA_PROMPT;
284*6cf3cc9dSRobert Mustacchi break;
285*6cf3cc9dSRobert Mustacchi case 'n':
286*6cf3cc9dSRobert Mustacchi targact = TA_SKIP;
2877c478bd9Sstevel@tonic-gate break;
2887c478bd9Sstevel@tonic-gate case 'p':
2897c478bd9Sstevel@tonic-gate pflg++;
2907c478bd9Sstevel@tonic-gate #ifdef XPG4
2917c478bd9Sstevel@tonic-gate attrsilent = 1;
2927c478bd9Sstevel@tonic-gate atflg = 0;
293da6c28aaSamw saflg = 0;
2947c478bd9Sstevel@tonic-gate #else
295cee1ddadSbasabi if (atflg == 0)
2967c478bd9Sstevel@tonic-gate attrsilent = 1;
2977c478bd9Sstevel@tonic-gate #endif
2987c478bd9Sstevel@tonic-gate break;
2997c478bd9Sstevel@tonic-gate case 'H':
3007c478bd9Sstevel@tonic-gate /*
3017c478bd9Sstevel@tonic-gate * If more than one of -H, -L, or -P are
3027c478bd9Sstevel@tonic-gate * specified, only the last option specified
3037c478bd9Sstevel@tonic-gate * determines the behavior.
3047c478bd9Sstevel@tonic-gate */
3057c478bd9Sstevel@tonic-gate Lflg = Pflg = 0;
3067c478bd9Sstevel@tonic-gate Hflg++;
3077c478bd9Sstevel@tonic-gate break;
3087c478bd9Sstevel@tonic-gate case 'L':
3097c478bd9Sstevel@tonic-gate Hflg = Pflg = 0;
3107c478bd9Sstevel@tonic-gate Lflg++;
3117c478bd9Sstevel@tonic-gate break;
3127c478bd9Sstevel@tonic-gate case 'P':
3137c478bd9Sstevel@tonic-gate Lflg = Hflg = 0;
3147c478bd9Sstevel@tonic-gate Pflg++;
3157c478bd9Sstevel@tonic-gate break;
3167c478bd9Sstevel@tonic-gate case 'R':
3177c478bd9Sstevel@tonic-gate /*
3187c478bd9Sstevel@tonic-gate * The default behavior of cp -R|-r
3197c478bd9Sstevel@tonic-gate * when specified without -H|-L|-P
3207c478bd9Sstevel@tonic-gate * is -L.
3217c478bd9Sstevel@tonic-gate */
3227c478bd9Sstevel@tonic-gate Rflg++;
3237c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/
3247c478bd9Sstevel@tonic-gate case 'r':
3257c478bd9Sstevel@tonic-gate rflg++;
3267c478bd9Sstevel@tonic-gate break;
3279e647765SAlexander Eremin case 'a':
3289e647765SAlexander Eremin Lflg = Hflg = 0;
3299e647765SAlexander Eremin pflg++;
3309e647765SAlexander Eremin Pflg++;
3319e647765SAlexander Eremin Rflg++;
3329e647765SAlexander Eremin rflg++;
3339e647765SAlexander Eremin break;
3347c478bd9Sstevel@tonic-gate case '@':
3357c478bd9Sstevel@tonic-gate atflg++;
3367c478bd9Sstevel@tonic-gate attrsilent = 0;
3377c478bd9Sstevel@tonic-gate #ifdef XPG4
3387c478bd9Sstevel@tonic-gate pflg = 0;
3397c478bd9Sstevel@tonic-gate #endif
3407c478bd9Sstevel@tonic-gate break;
341da6c28aaSamw case '/':
342da6c28aaSamw saflg++;
343da6c28aaSamw attrsilent = 0;
344da6c28aaSamw #ifdef XPG4
345da6c28aaSamw pflg = 0;
346da6c28aaSamw #endif
347da6c28aaSamw break;
3487c478bd9Sstevel@tonic-gate default:
3497c478bd9Sstevel@tonic-gate errflg++;
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate /* -R or -r must be specified with -H, -L, or -P */
3537c478bd9Sstevel@tonic-gate if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
3547c478bd9Sstevel@tonic-gate errflg++;
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate } else if (mve) {
358*6cf3cc9dSRobert Mustacchi while ((c = getopt(argc, argv, "fins")) != EOF)
3597c478bd9Sstevel@tonic-gate switch (c) {
3607c478bd9Sstevel@tonic-gate case 'f':
361*6cf3cc9dSRobert Mustacchi targact = TA_OVERWRITE;
3627c478bd9Sstevel@tonic-gate break;
3637c478bd9Sstevel@tonic-gate case 'i':
3647c478bd9Sstevel@tonic-gate #ifdef XPG4
365*6cf3cc9dSRobert Mustacchi targact = TA_PROMPT;
366*6cf3cc9dSRobert Mustacchi #else
367*6cf3cc9dSRobert Mustacchi if (targact != TA_OVERWRITE) {
368*6cf3cc9dSRobert Mustacchi targact = TA_PROMPT;
369*6cf3cc9dSRobert Mustacchi }
3707c478bd9Sstevel@tonic-gate #endif
3717c478bd9Sstevel@tonic-gate break;
372*6cf3cc9dSRobert Mustacchi case 'n':
373*6cf3cc9dSRobert Mustacchi targact = TA_SKIP;
374*6cf3cc9dSRobert Mustacchi break;
3757c478bd9Sstevel@tonic-gate default:
3767c478bd9Sstevel@tonic-gate errflg++;
3777c478bd9Sstevel@tonic-gate }
3787c478bd9Sstevel@tonic-gate } else { /* ln */
379*6cf3cc9dSRobert Mustacchi while ((c = getopt(argc, argv, "fins")) != EOF)
3807c478bd9Sstevel@tonic-gate switch (c) {
3817c478bd9Sstevel@tonic-gate case 'f':
382*6cf3cc9dSRobert Mustacchi targact = TA_OVERWRITE;
383*6cf3cc9dSRobert Mustacchi break;
384*6cf3cc9dSRobert Mustacchi case 'i':
385*6cf3cc9dSRobert Mustacchi targact = TA_PROMPT;
3867c478bd9Sstevel@tonic-gate break;
3877c478bd9Sstevel@tonic-gate case 'n':
3887c478bd9Sstevel@tonic-gate /* silently ignored; this is the default */
3897c478bd9Sstevel@tonic-gate break;
3907c478bd9Sstevel@tonic-gate case 's':
3917c478bd9Sstevel@tonic-gate sflg++;
3927c478bd9Sstevel@tonic-gate break;
3937c478bd9Sstevel@tonic-gate default:
3947c478bd9Sstevel@tonic-gate errflg++;
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate }
3977c478bd9Sstevel@tonic-gate
3987c478bd9Sstevel@tonic-gate /*
3997c478bd9Sstevel@tonic-gate * For BSD compatibility allow - to delimit the end of
4007c478bd9Sstevel@tonic-gate * options for mv.
4017c478bd9Sstevel@tonic-gate */
4027c478bd9Sstevel@tonic-gate if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
4037c478bd9Sstevel@tonic-gate optind++;
4047c478bd9Sstevel@tonic-gate
4057c478bd9Sstevel@tonic-gate /*
4067c478bd9Sstevel@tonic-gate * Check for sufficient arguments
4077c478bd9Sstevel@tonic-gate * or a usage error.
4087c478bd9Sstevel@tonic-gate */
4097c478bd9Sstevel@tonic-gate
4107c478bd9Sstevel@tonic-gate argc -= optind;
4117c478bd9Sstevel@tonic-gate argv = &argv[optind];
4127c478bd9Sstevel@tonic-gate
4137c478bd9Sstevel@tonic-gate if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
4147c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4157c478bd9Sstevel@tonic-gate gettext("%s: Insufficient arguments (%d)\n"),
4167c478bd9Sstevel@tonic-gate cmd, argc);
4177c478bd9Sstevel@tonic-gate usage();
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate
4207c478bd9Sstevel@tonic-gate if (errflg != 0)
4217c478bd9Sstevel@tonic-gate usage();
4227c478bd9Sstevel@tonic-gate
4237c478bd9Sstevel@tonic-gate /*
4247c478bd9Sstevel@tonic-gate * If there is more than a source and target,
4257c478bd9Sstevel@tonic-gate * the last argument (the target) must be a directory
4267c478bd9Sstevel@tonic-gate * which really exists.
4277c478bd9Sstevel@tonic-gate */
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate if (argc > 2) {
4307c478bd9Sstevel@tonic-gate if (stat(argv[argc-1], &s2) < 0) {
4317c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4327c478bd9Sstevel@tonic-gate gettext("%s: %s not found\n"),
4337c478bd9Sstevel@tonic-gate cmd, argv[argc-1]);
4347c478bd9Sstevel@tonic-gate exit(2);
4357c478bd9Sstevel@tonic-gate }
4367c478bd9Sstevel@tonic-gate
4377c478bd9Sstevel@tonic-gate if (!ISDIR(s2)) {
4387c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4397c478bd9Sstevel@tonic-gate gettext("%s: Target %s must be a directory\n"),
4407c478bd9Sstevel@tonic-gate cmd, argv[argc-1]);
4417c478bd9Sstevel@tonic-gate usage();
4427c478bd9Sstevel@tonic-gate }
4437c478bd9Sstevel@tonic-gate }
4447c478bd9Sstevel@tonic-gate
4457c478bd9Sstevel@tonic-gate if (strlen(argv[argc-1]) >= PATH_MAX) {
4467c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4477c478bd9Sstevel@tonic-gate gettext("%s: Target %s file name length exceeds PATH_MAX"
4487c478bd9Sstevel@tonic-gate " %d\n"), cmd, argv[argc-1], PATH_MAX);
4497c478bd9Sstevel@tonic-gate exit(78);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate
4527c478bd9Sstevel@tonic-gate if (argc == 1) {
4537c478bd9Sstevel@tonic-gate if (!lnk)
4547c478bd9Sstevel@tonic-gate usage();
4557c478bd9Sstevel@tonic-gate (void) strcpy(target, ".");
4567c478bd9Sstevel@tonic-gate } else {
4577c478bd9Sstevel@tonic-gate (void) strcpy(target, argv[--argc]);
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate * Perform a multiple argument mv|cp|ln by
4627c478bd9Sstevel@tonic-gate * multiple invocations of cpymve() or lnkfil().
4637c478bd9Sstevel@tonic-gate */
4647c478bd9Sstevel@tonic-gate if (lnk)
4657c478bd9Sstevel@tonic-gate move = lnkfil;
4667c478bd9Sstevel@tonic-gate else
4677c478bd9Sstevel@tonic-gate move = cpymve;
4687c478bd9Sstevel@tonic-gate
4697c478bd9Sstevel@tonic-gate r = 0;
4707c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) {
4717c478bd9Sstevel@tonic-gate stree = NULL;
4727c478bd9Sstevel@tonic-gate cmdarg = 1;
4737c478bd9Sstevel@tonic-gate r += move(argv[i], target);
4747c478bd9Sstevel@tonic-gate }
4757c478bd9Sstevel@tonic-gate
4767c478bd9Sstevel@tonic-gate /*
4777c478bd9Sstevel@tonic-gate * Show errors by nonzero exit code.
4787c478bd9Sstevel@tonic-gate */
479a77d64afScf46844 return (r ? 2 : 0);
4807c478bd9Sstevel@tonic-gate }
4817c478bd9Sstevel@tonic-gate
4827c478bd9Sstevel@tonic-gate static int
lnkfil(const char * source,char * target)483*6cf3cc9dSRobert Mustacchi lnkfil(const char *source, char *target)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate char *buf = NULL;
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate if (sflg) {
4887c478bd9Sstevel@tonic-gate
4897c478bd9Sstevel@tonic-gate /*
4907c478bd9Sstevel@tonic-gate * If target is a directory make complete
4917c478bd9Sstevel@tonic-gate * name of the new symbolic link within that
4927c478bd9Sstevel@tonic-gate * directory.
4937c478bd9Sstevel@tonic-gate */
4947c478bd9Sstevel@tonic-gate
4957c478bd9Sstevel@tonic-gate if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
4967c478bd9Sstevel@tonic-gate size_t len;
4977c478bd9Sstevel@tonic-gate
4987c478bd9Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4;
4997c478bd9Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) {
5007c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
5017c478bd9Sstevel@tonic-gate gettext("%s: Insufficient memory "
5027c478bd9Sstevel@tonic-gate "to %s %s\n"), cmd, cmd, source);
5037c478bd9Sstevel@tonic-gate exit(3);
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s",
5067c478bd9Sstevel@tonic-gate target, dname(source));
5077c478bd9Sstevel@tonic-gate target = buf;
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate /*
5116fcd7f75Schin * Check to see if the file exists already.
5126fcd7f75Schin * In this case we use lstat() instead of stat():
5136fcd7f75Schin * unlink(2) and symlink(2) will operate on the file
5146fcd7f75Schin * itself, not its reference, if the file is a symlink.
5157c478bd9Sstevel@tonic-gate */
5167c478bd9Sstevel@tonic-gate
5176fcd7f75Schin if ((lstat(target, &s2) == 0)) {
5187c478bd9Sstevel@tonic-gate /*
519*6cf3cc9dSRobert Mustacchi * Check what our current overwrite behavior is i.e. the
520*6cf3cc9dSRobert Mustacchi * -f or -n options. If prompting is set (-i), ask the
521*6cf3cc9dSRobert Mustacchi * user. If overwrite is set (-n) then we attempt to
522*6cf3cc9dSRobert Mustacchi * remove it. In both cases if the target is a directory
523*6cf3cc9dSRobert Mustacchi * then we refuse to remove this. Once this is done, the
524*6cf3cc9dSRobert Mustacchi * program will proceed with creating the symlink.
5257c478bd9Sstevel@tonic-gate */
526*6cf3cc9dSRobert Mustacchi switch (targact) {
527*6cf3cc9dSRobert Mustacchi case TA_OVERWRITE:
528*6cf3cc9dSRobert Mustacchi case TA_PROMPT:
5296fcd7f75Schin if (ISDIR(s2)) {
5306fcd7f75Schin (void) fprintf(stderr,
5316fcd7f75Schin gettext("%s: cannot create link "
5326fcd7f75Schin "over directory %s\n"), cmd,
5336fcd7f75Schin target);
5346fcd7f75Schin return (1);
5356fcd7f75Schin }
536*6cf3cc9dSRobert Mustacchi
537*6cf3cc9dSRobert Mustacchi /*
538*6cf3cc9dSRobert Mustacchi * See the longer discussion in chkfiles about
539*6cf3cc9dSRobert Mustacchi * the use of use_stdin() while prompting.
540*6cf3cc9dSRobert Mustacchi */
541*6cf3cc9dSRobert Mustacchi if (targact == TA_PROMPT && use_stdin()) {
542*6cf3cc9dSRobert Mustacchi (void) fprintf(stderr,
543*6cf3cc9dSRobert Mustacchi gettext("%s: overwrite %s "
544*6cf3cc9dSRobert Mustacchi "(%s/%s)? "), cmd, target, yesstr,
545*6cf3cc9dSRobert Mustacchi nostr);
546*6cf3cc9dSRobert Mustacchi if (yes() == 0) {
547*6cf3cc9dSRobert Mustacchi return (0);
548*6cf3cc9dSRobert Mustacchi }
549*6cf3cc9dSRobert Mustacchi }
550*6cf3cc9dSRobert Mustacchi
5517c478bd9Sstevel@tonic-gate if (unlink(target) < 0) {
5527c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
5537c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
5547c478bd9Sstevel@tonic-gate cmd, target);
5557c478bd9Sstevel@tonic-gate perror("");
5567c478bd9Sstevel@tonic-gate return (1);
5577c478bd9Sstevel@tonic-gate }
558*6cf3cc9dSRobert Mustacchi case TA_DEFAULT:
559*6cf3cc9dSRobert Mustacchi break;
560*6cf3cc9dSRobert Mustacchi case TA_SKIP:
561*6cf3cc9dSRobert Mustacchi /*
562*6cf3cc9dSRobert Mustacchi * This shouldn't be selectable.
563*6cf3cc9dSRobert Mustacchi */
564*6cf3cc9dSRobert Mustacchi abort();
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate
5697c478bd9Sstevel@tonic-gate /*
5707c478bd9Sstevel@tonic-gate * Create a symbolic link to the source.
5717c478bd9Sstevel@tonic-gate */
5727c478bd9Sstevel@tonic-gate
5737c478bd9Sstevel@tonic-gate if (symlink(source, target) < 0) {
5747c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
5757c478bd9Sstevel@tonic-gate gettext("%s: cannot create %s: "),
5767c478bd9Sstevel@tonic-gate cmd, target);
5777c478bd9Sstevel@tonic-gate perror("");
5787c478bd9Sstevel@tonic-gate if (buf != NULL)
5797c478bd9Sstevel@tonic-gate free(buf);
5807c478bd9Sstevel@tonic-gate return (1);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate if (buf != NULL)
5837c478bd9Sstevel@tonic-gate free(buf);
5847c478bd9Sstevel@tonic-gate return (0);
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate
5877c478bd9Sstevel@tonic-gate switch (chkfiles(source, &target)) {
588*6cf3cc9dSRobert Mustacchi case CHK_ERROR:
589*6cf3cc9dSRobert Mustacchi return (1);
590*6cf3cc9dSRobert Mustacchi case CHK_SKIP:
591*6cf3cc9dSRobert Mustacchi return (0);
592*6cf3cc9dSRobert Mustacchi case CHK_CONT:
593*6cf3cc9dSRobert Mustacchi break;
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate * Make sure source file is not a directory,
598e1636a06Sdanny webster * we cannot link directories...
5997c478bd9Sstevel@tonic-gate */
6007c478bd9Sstevel@tonic-gate
6017c478bd9Sstevel@tonic-gate if (ISDIR(s1)) {
6027c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
6037c478bd9Sstevel@tonic-gate gettext("%s: %s is a directory\n"), cmd, source);
6047c478bd9Sstevel@tonic-gate return (1);
6057c478bd9Sstevel@tonic-gate }
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate * hard link, call link() and return.
6097c478bd9Sstevel@tonic-gate */
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate if (link(source, target) < 0) {
6127c478bd9Sstevel@tonic-gate if (errno == EXDEV)
6137c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
6147c478bd9Sstevel@tonic-gate gettext("%s: %s is on a different file system\n"),
6157c478bd9Sstevel@tonic-gate cmd, target);
6167c478bd9Sstevel@tonic-gate else {
6177c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
6187c478bd9Sstevel@tonic-gate gettext("%s: cannot create link %s: "),
6197c478bd9Sstevel@tonic-gate cmd, target);
6207c478bd9Sstevel@tonic-gate perror("");
6217c478bd9Sstevel@tonic-gate }
6227c478bd9Sstevel@tonic-gate if (buf != NULL)
6237c478bd9Sstevel@tonic-gate free(buf);
6247c478bd9Sstevel@tonic-gate return (1);
6257c478bd9Sstevel@tonic-gate } else {
6267c478bd9Sstevel@tonic-gate if (buf != NULL)
6277c478bd9Sstevel@tonic-gate free(buf);
6287c478bd9Sstevel@tonic-gate return (0);
6297c478bd9Sstevel@tonic-gate }
6307c478bd9Sstevel@tonic-gate }
6317c478bd9Sstevel@tonic-gate
6327c478bd9Sstevel@tonic-gate static int
cpymve(const char * source,char * target)633*6cf3cc9dSRobert Mustacchi cpymve(const char *source, char *target)
6347c478bd9Sstevel@tonic-gate {
6357c478bd9Sstevel@tonic-gate int n;
6367c478bd9Sstevel@tonic-gate int fi, fo;
6377c478bd9Sstevel@tonic-gate int ret = 0;
6387c478bd9Sstevel@tonic-gate int attret = 0;
639da6c28aaSamw int sattret = 0;
6407c478bd9Sstevel@tonic-gate int errno_save;
641cee1ddadSbasabi int error = 0;
6427c478bd9Sstevel@tonic-gate
6437c478bd9Sstevel@tonic-gate switch (chkfiles(source, &target)) {
644*6cf3cc9dSRobert Mustacchi case CHK_ERROR:
645*6cf3cc9dSRobert Mustacchi return (1);
646*6cf3cc9dSRobert Mustacchi case CHK_SKIP:
647*6cf3cc9dSRobert Mustacchi return (0);
648*6cf3cc9dSRobert Mustacchi case CHK_CONT:
649*6cf3cc9dSRobert Mustacchi break;
6507c478bd9Sstevel@tonic-gate }
6517c478bd9Sstevel@tonic-gate
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate * If it's a recursive copy and source
6547c478bd9Sstevel@tonic-gate * is a directory, then call rcopy (from copydir).
6557c478bd9Sstevel@tonic-gate */
6567c478bd9Sstevel@tonic-gate if (cpy) {
6577c478bd9Sstevel@tonic-gate if (ISDIR(s1)) {
6587c478bd9Sstevel@tonic-gate int rc;
6597c478bd9Sstevel@tonic-gate avl_index_t where = 0;
6607c478bd9Sstevel@tonic-gate tree_node_t *tnode;
6617c478bd9Sstevel@tonic-gate tree_node_t *tptr;
6627c478bd9Sstevel@tonic-gate dev_t save_dev = s1.st_dev;
6637c478bd9Sstevel@tonic-gate ino_t save_ino = s1.st_ino;
6647c478bd9Sstevel@tonic-gate
6657c478bd9Sstevel@tonic-gate /*
6667c478bd9Sstevel@tonic-gate * We will be recursing into the directory so
6677c478bd9Sstevel@tonic-gate * save the inode information to a search tree
6687c478bd9Sstevel@tonic-gate * to avoid getting into an endless loop.
6697c478bd9Sstevel@tonic-gate */
6707c478bd9Sstevel@tonic-gate if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
6717c478bd9Sstevel@tonic-gate if (rc == 0) {
6727c478bd9Sstevel@tonic-gate /*
6737c478bd9Sstevel@tonic-gate * We've already visited this directory.
6747c478bd9Sstevel@tonic-gate * Don't remove the search tree entry
6757c478bd9Sstevel@tonic-gate * to make sure we don't get into an
6767c478bd9Sstevel@tonic-gate * endless loop if revisited from a
6777c478bd9Sstevel@tonic-gate * different part of the hierarchy.
6787c478bd9Sstevel@tonic-gate */
6797c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
6807c478bd9Sstevel@tonic-gate "%s: cycle detected: %s\n"),
6817c478bd9Sstevel@tonic-gate cmd, source);
6827c478bd9Sstevel@tonic-gate } else {
6837c478bd9Sstevel@tonic-gate Perror(source);
6847c478bd9Sstevel@tonic-gate }
6857c478bd9Sstevel@tonic-gate return (1);
6867c478bd9Sstevel@tonic-gate }
6877c478bd9Sstevel@tonic-gate
6887c478bd9Sstevel@tonic-gate cmdarg = 0;
6897c478bd9Sstevel@tonic-gate rc = copydir(source, target);
6907c478bd9Sstevel@tonic-gate
6917c478bd9Sstevel@tonic-gate /*
6927c478bd9Sstevel@tonic-gate * Create a tnode to get an index to the matching
6937c478bd9Sstevel@tonic-gate * node (same dev and inode) in the search tree,
6947c478bd9Sstevel@tonic-gate * then use the index to remove the matching node
6957c478bd9Sstevel@tonic-gate * so it we do not wrongly detect a cycle when
6967c478bd9Sstevel@tonic-gate * revisiting this directory from another part of
6977c478bd9Sstevel@tonic-gate * the hierarchy.
6987c478bd9Sstevel@tonic-gate */
6997c478bd9Sstevel@tonic-gate if ((tnode = create_tnode(save_dev,
7007c478bd9Sstevel@tonic-gate save_ino)) == NULL) {
7017c478bd9Sstevel@tonic-gate Perror(source);
7027c478bd9Sstevel@tonic-gate return (1);
7037c478bd9Sstevel@tonic-gate }
7047c478bd9Sstevel@tonic-gate if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
7057c478bd9Sstevel@tonic-gate avl_remove(stree, tptr);
7067c478bd9Sstevel@tonic-gate }
7077c478bd9Sstevel@tonic-gate free(tptr);
7087c478bd9Sstevel@tonic-gate free(tnode);
7097c478bd9Sstevel@tonic-gate return (rc);
7107c478bd9Sstevel@tonic-gate
7117c478bd9Sstevel@tonic-gate } else if (ISDEV(s1) && Rflg) {
7127c478bd9Sstevel@tonic-gate return (copyspecial(target));
7137c478bd9Sstevel@tonic-gate } else {
7147c478bd9Sstevel@tonic-gate goto copy;
7157c478bd9Sstevel@tonic-gate }
7167c478bd9Sstevel@tonic-gate }
7177c478bd9Sstevel@tonic-gate
7187c478bd9Sstevel@tonic-gate if (mve) {
7197c478bd9Sstevel@tonic-gate if (rename(source, target) >= 0)
7207c478bd9Sstevel@tonic-gate return (0);
7217c478bd9Sstevel@tonic-gate if (errno != EXDEV) {
7227c478bd9Sstevel@tonic-gate if (errno == ENOTDIR && ISDIR(s1)) {
7237c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7247c478bd9Sstevel@tonic-gate gettext("%s: %s is a directory\n"),
7257c478bd9Sstevel@tonic-gate cmd, source);
7267c478bd9Sstevel@tonic-gate return (1);
7277c478bd9Sstevel@tonic-gate }
7287c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7297c478bd9Sstevel@tonic-gate gettext("%s: cannot rename %s to %s: "),
7307c478bd9Sstevel@tonic-gate cmd, source, target);
7317c478bd9Sstevel@tonic-gate perror("");
7327c478bd9Sstevel@tonic-gate return (1);
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate
7357c478bd9Sstevel@tonic-gate /*
7367c478bd9Sstevel@tonic-gate * cannot move a non-directory (source) onto an existing
7377c478bd9Sstevel@tonic-gate * directory (target)
7387c478bd9Sstevel@tonic-gate *
7397c478bd9Sstevel@tonic-gate */
7407c478bd9Sstevel@tonic-gate if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
7417c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7427c478bd9Sstevel@tonic-gate gettext("%s: cannot mv a non directory %s "
7437c478bd9Sstevel@tonic-gate "over existing directory"
7447c478bd9Sstevel@tonic-gate " %s \n"), cmd, source, target);
7457c478bd9Sstevel@tonic-gate return (1);
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate if (ISDIR(s1)) {
7487c478bd9Sstevel@tonic-gate #ifdef XPG4
7497c478bd9Sstevel@tonic-gate if (targetexists && ISDIR(s2)) {
7507c478bd9Sstevel@tonic-gate /* existing target dir must be empty */
7517c478bd9Sstevel@tonic-gate if (rmdir(target) < 0) {
7527c478bd9Sstevel@tonic-gate errno_save = errno;
7537c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7547c478bd9Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "),
7557c478bd9Sstevel@tonic-gate cmd, target);
7567c478bd9Sstevel@tonic-gate errno = errno_save;
7577c478bd9Sstevel@tonic-gate perror("");
7587c478bd9Sstevel@tonic-gate return (1);
7597c478bd9Sstevel@tonic-gate }
7607c478bd9Sstevel@tonic-gate }
7617c478bd9Sstevel@tonic-gate #endif
7627c478bd9Sstevel@tonic-gate if ((n = copydir(source, target)) == 0)
7637c478bd9Sstevel@tonic-gate (void) rmdir(source);
7647c478bd9Sstevel@tonic-gate return (n);
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate
767e1636a06Sdanny webster /* doors cannot be moved across filesystems */
7687c478bd9Sstevel@tonic-gate if (ISDOOR(s1)) {
7697c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
770e1636a06Sdanny webster gettext("%s: %s: cannot move door "
7717c478bd9Sstevel@tonic-gate "across file systems\n"), cmd, source);
7727c478bd9Sstevel@tonic-gate return (1);
7737c478bd9Sstevel@tonic-gate }
774e1636a06Sdanny webster
775e1636a06Sdanny webster /* sockets cannot be moved across filesystems */
776e1636a06Sdanny webster if (ISSOCK(s1)) {
777e1636a06Sdanny webster (void) fprintf(stderr,
778e1636a06Sdanny webster gettext("%s: %s: cannot move socket "
779e1636a06Sdanny webster "across file systems\n"), cmd, source);
780e1636a06Sdanny webster return (1);
781e1636a06Sdanny webster }
782e1636a06Sdanny webster
7837c478bd9Sstevel@tonic-gate /*
784e1636a06Sdanny webster * File cannot be renamed, try to recreate the symbolic
7857c478bd9Sstevel@tonic-gate * link or special device, or copy the file wholesale
7867c478bd9Sstevel@tonic-gate * between file systems.
7877c478bd9Sstevel@tonic-gate */
7887c478bd9Sstevel@tonic-gate if (ISLNK(s1)) {
7897c478bd9Sstevel@tonic-gate register int m;
7907c478bd9Sstevel@tonic-gate register mode_t md;
7917c478bd9Sstevel@tonic-gate char symln[PATH_MAX + 1];
7927c478bd9Sstevel@tonic-gate
7937c478bd9Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
7947c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7957c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
7967c478bd9Sstevel@tonic-gate cmd, target);
7977c478bd9Sstevel@tonic-gate perror("");
7987c478bd9Sstevel@tonic-gate return (1);
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate
8017c478bd9Sstevel@tonic-gate if ((m = readlink(source, symln,
8027c478bd9Sstevel@tonic-gate sizeof (symln) - 1)) < 0) {
8037c478bd9Sstevel@tonic-gate Perror(source);
8047c478bd9Sstevel@tonic-gate return (1);
8057c478bd9Sstevel@tonic-gate }
8067c478bd9Sstevel@tonic-gate symln[m] = '\0';
8077c478bd9Sstevel@tonic-gate
8087c478bd9Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS));
8097c478bd9Sstevel@tonic-gate if (symlink(symln, target) < 0) {
8107c478bd9Sstevel@tonic-gate Perror(target);
8117c478bd9Sstevel@tonic-gate return (1);
8127c478bd9Sstevel@tonic-gate }
8137c478bd9Sstevel@tonic-gate (void) umask(md);
8147c478bd9Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1));
8157c478bd9Sstevel@tonic-gate #ifdef XPG4
8167c478bd9Sstevel@tonic-gate if (m < 0) {
8177c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot"
8187c478bd9Sstevel@tonic-gate " change owner and group of"
8197c478bd9Sstevel@tonic-gate " %s: "), cmd, target);
8207c478bd9Sstevel@tonic-gate perror("");
8217c478bd9Sstevel@tonic-gate }
8227c478bd9Sstevel@tonic-gate #endif
8237c478bd9Sstevel@tonic-gate goto cleanup;
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate if (ISDEV(s1)) {
8267c478bd9Sstevel@tonic-gate
8277c478bd9Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
8287c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8297c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
8307c478bd9Sstevel@tonic-gate cmd, target);
8317c478bd9Sstevel@tonic-gate perror("");
8327c478bd9Sstevel@tonic-gate return (1);
8337c478bd9Sstevel@tonic-gate }
8347c478bd9Sstevel@tonic-gate
8357c478bd9Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
8367c478bd9Sstevel@tonic-gate Perror(target);
8377c478bd9Sstevel@tonic-gate return (1);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
8417c478bd9Sstevel@tonic-gate (void) chg_time(target, s1);
8427c478bd9Sstevel@tonic-gate goto cleanup;
8437c478bd9Sstevel@tonic-gate }
8447c478bd9Sstevel@tonic-gate
8457c478bd9Sstevel@tonic-gate if (ISREG(s1)) {
8467c478bd9Sstevel@tonic-gate if (ISDIR(s2)) {
8477c478bd9Sstevel@tonic-gate if (targetexists && rmdir(target) < 0) {
8487c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8497c478bd9Sstevel@tonic-gate gettext("%s: cannot rmdir %s: "),
8507c478bd9Sstevel@tonic-gate cmd, target);
8517c478bd9Sstevel@tonic-gate perror("");
8527c478bd9Sstevel@tonic-gate return (1);
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate } else {
8557c478bd9Sstevel@tonic-gate if (targetexists && unlink(target) < 0) {
8567c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8577c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
8587c478bd9Sstevel@tonic-gate cmd, target);
8597c478bd9Sstevel@tonic-gate perror("");
8607c478bd9Sstevel@tonic-gate return (1);
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate }
8637c478bd9Sstevel@tonic-gate
8647c478bd9Sstevel@tonic-gate
8657c478bd9Sstevel@tonic-gate copy:
8667c478bd9Sstevel@tonic-gate /*
8677c478bd9Sstevel@tonic-gate * If the source file is a symlink, and either
8687c478bd9Sstevel@tonic-gate * -P or -H flag (only if -H is specified and the
8697c478bd9Sstevel@tonic-gate * source file is not a command line argument)
8707c478bd9Sstevel@tonic-gate * were specified, then action is taken on the symlink
8717c478bd9Sstevel@tonic-gate * itself, not the file referenced by the symlink.
8727c478bd9Sstevel@tonic-gate * Note: this is executed for 'cp' only.
8737c478bd9Sstevel@tonic-gate */
8747c478bd9Sstevel@tonic-gate if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
8757c478bd9Sstevel@tonic-gate int m;
8767c478bd9Sstevel@tonic-gate mode_t md;
8777c478bd9Sstevel@tonic-gate char symln[PATH_MAX + 1];
8787c478bd9Sstevel@tonic-gate
8797c478bd9Sstevel@tonic-gate m = readlink(source, symln, sizeof (symln) - 1);
8807c478bd9Sstevel@tonic-gate
8817c478bd9Sstevel@tonic-gate if (m < 0) {
8827c478bd9Sstevel@tonic-gate Perror(source);
8837c478bd9Sstevel@tonic-gate return (1);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate symln[m] = '\0';
8867c478bd9Sstevel@tonic-gate
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate * Copy the sym link to the target.
8897c478bd9Sstevel@tonic-gate * Note: If the target exists, write a
8907c478bd9Sstevel@tonic-gate * diagnostic message, do nothing more
8917c478bd9Sstevel@tonic-gate * with the source file, and return to
8927c478bd9Sstevel@tonic-gate * process any remaining files.
8937c478bd9Sstevel@tonic-gate */
8947c478bd9Sstevel@tonic-gate md = umask(~(s1.st_mode & MODEBITS));
8957c478bd9Sstevel@tonic-gate if (symlink(symln, target) < 0) {
8967c478bd9Sstevel@tonic-gate Perror(target);
8977c478bd9Sstevel@tonic-gate return (1);
8987c478bd9Sstevel@tonic-gate }
8997c478bd9Sstevel@tonic-gate (void) umask(md);
9007c478bd9Sstevel@tonic-gate m = lchown(target, UID(s1), GID(s1));
9017c478bd9Sstevel@tonic-gate
9027c478bd9Sstevel@tonic-gate if (m < 0) {
9037c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
9047c478bd9Sstevel@tonic-gate "cp: cannot change owner and "
9057c478bd9Sstevel@tonic-gate "group of %s:"), target);
9067c478bd9Sstevel@tonic-gate perror("");
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate } else {
9097c478bd9Sstevel@tonic-gate /*
9107c478bd9Sstevel@tonic-gate * Copy the file. If it happens to be a
9117c478bd9Sstevel@tonic-gate * symlink, copy the file referenced
9127c478bd9Sstevel@tonic-gate * by the symlink.
9137c478bd9Sstevel@tonic-gate */
9147c478bd9Sstevel@tonic-gate fi = open(source, O_RDONLY);
9157c478bd9Sstevel@tonic-gate if (fi < 0) {
9167c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
9177c478bd9Sstevel@tonic-gate gettext("%s: cannot open %s: "),
9187c478bd9Sstevel@tonic-gate cmd, source);
9197c478bd9Sstevel@tonic-gate perror("");
9207c478bd9Sstevel@tonic-gate return (1);
9217c478bd9Sstevel@tonic-gate }
9227c478bd9Sstevel@tonic-gate
9237c478bd9Sstevel@tonic-gate fo = creat(target, s1.st_mode & MODEBITS);
9247c478bd9Sstevel@tonic-gate if (fo < 0) {
9257c478bd9Sstevel@tonic-gate /*
9267c478bd9Sstevel@tonic-gate * If -f and creat() failed, unlink
9277c478bd9Sstevel@tonic-gate * and try again.
9287c478bd9Sstevel@tonic-gate */
9297c478bd9Sstevel@tonic-gate if (fflg) {
9307c478bd9Sstevel@tonic-gate (void) unlink(target);
9317c478bd9Sstevel@tonic-gate fo = creat(target,
9327c478bd9Sstevel@tonic-gate s1.st_mode & MODEBITS);
9337c478bd9Sstevel@tonic-gate }
9347c478bd9Sstevel@tonic-gate }
9357c478bd9Sstevel@tonic-gate if (fo < 0) {
9367c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
9377c478bd9Sstevel@tonic-gate gettext("%s: cannot create %s: "),
9387c478bd9Sstevel@tonic-gate cmd, target);
9397c478bd9Sstevel@tonic-gate perror("");
9407c478bd9Sstevel@tonic-gate (void) close(fi);
9417c478bd9Sstevel@tonic-gate return (1);
9427c478bd9Sstevel@tonic-gate } else {
9437c478bd9Sstevel@tonic-gate /* stat the new file, its used below */
9447c478bd9Sstevel@tonic-gate (void) stat(target, &s2);
9457c478bd9Sstevel@tonic-gate }
9467c478bd9Sstevel@tonic-gate
9477c478bd9Sstevel@tonic-gate /*
9487c478bd9Sstevel@tonic-gate * Set target's permissions to the source
9497c478bd9Sstevel@tonic-gate * before any copying so that any partially
9507c478bd9Sstevel@tonic-gate * copied file will have the source's
9517c478bd9Sstevel@tonic-gate * permissions (at most) or umask permissions
9527c478bd9Sstevel@tonic-gate * whichever is the most restrictive.
9537c478bd9Sstevel@tonic-gate *
9547c478bd9Sstevel@tonic-gate * ACL for regular files
9557c478bd9Sstevel@tonic-gate */
9567c478bd9Sstevel@tonic-gate
9577c478bd9Sstevel@tonic-gate if (pflg || mve) {
9587c478bd9Sstevel@tonic-gate (void) chmod(target, FMODE(s1));
959fa9e4066Sahrens if (s1acl != NULL) {
960fa9e4066Sahrens if ((acl_set(target,
961fa9e4066Sahrens s1acl)) < 0) {
962cee1ddadSbasabi error++;
963cee1ddadSbasabi (void) fprintf(stderr,
964cee1ddadSbasabi gettext("%s: "
965cee1ddadSbasabi "Failed to set "
966cee1ddadSbasabi "acl entries "
967cee1ddadSbasabi "on %s\n"), cmd,
9687c478bd9Sstevel@tonic-gate target);
96994d2b9abSmarks acl_free(s1acl);
97094d2b9abSmarks s1acl = NULL;
9717c478bd9Sstevel@tonic-gate /*
9727c478bd9Sstevel@tonic-gate * else: silent and
9737c478bd9Sstevel@tonic-gate * continue
9747c478bd9Sstevel@tonic-gate */
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate }
9777c478bd9Sstevel@tonic-gate }
9787c478bd9Sstevel@tonic-gate
9797c478bd9Sstevel@tonic-gate if (fstat(fi, &s1) < 0) {
9807c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
9817c478bd9Sstevel@tonic-gate gettext("%s: cannot access %s\n"),
9827c478bd9Sstevel@tonic-gate cmd, source);
9837c478bd9Sstevel@tonic-gate return (1);
9847c478bd9Sstevel@tonic-gate }
9857c478bd9Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
9867c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
9877c478bd9Sstevel@tonic-gate gettext(
9887c478bd9Sstevel@tonic-gate "%s: %s and %s are identical\n"),
9897c478bd9Sstevel@tonic-gate cmd, source, target);
9907c478bd9Sstevel@tonic-gate return (1);
9917c478bd9Sstevel@tonic-gate }
9927c478bd9Sstevel@tonic-gate
993da6c28aaSamw if (writefile(fi, fo, source, target, NULL,
994da6c28aaSamw NULL, &s1, &s2) != 0) {
9957c478bd9Sstevel@tonic-gate return (1);
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate
9987c478bd9Sstevel@tonic-gate (void) close(fi);
9997c478bd9Sstevel@tonic-gate if (close(fo) < 0) {
10007c478bd9Sstevel@tonic-gate Perror2(target, "write");
10017c478bd9Sstevel@tonic-gate return (1);
10027c478bd9Sstevel@tonic-gate }
10037c478bd9Sstevel@tonic-gate }
1004da6c28aaSamw /* Copy regular extended attributes */
1005da6c28aaSamw if (pflg || atflg || mve || saflg) {
10067c478bd9Sstevel@tonic-gate attret = copyattributes(source, target);
10077c478bd9Sstevel@tonic-gate if (attret != 0 && !attrsilent) {
10087c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
10097c478bd9Sstevel@tonic-gate "%s: Failed to preserve"
10107c478bd9Sstevel@tonic-gate " extended attributes of file"
10117c478bd9Sstevel@tonic-gate " %s\n"), cmd, source);
10127c478bd9Sstevel@tonic-gate }
1013da6c28aaSamw /* Copy extended system attributes */
1014cee1ddadSbasabi if (pflg || mve || saflg)
1015da6c28aaSamw sattret = copy_sysattr(source, target);
10167c478bd9Sstevel@tonic-gate if (mve && attret != 0) {
10177c478bd9Sstevel@tonic-gate (void) unlink(target);
10187c478bd9Sstevel@tonic-gate return (1);
10197c478bd9Sstevel@tonic-gate }
1020da6c28aaSamw if (attrsilent) {
10217c478bd9Sstevel@tonic-gate attret = 0;
1022da6c28aaSamw }
10237c478bd9Sstevel@tonic-gate }
10247c478bd9Sstevel@tonic-gate
10257c478bd9Sstevel@tonic-gate /*
10267c478bd9Sstevel@tonic-gate * XPG4: the write system call will clear setgid
10277c478bd9Sstevel@tonic-gate * and setuid bits, so set them again.
10287c478bd9Sstevel@tonic-gate */
10297c478bd9Sstevel@tonic-gate if (pflg || mve) {
10307c478bd9Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1),
10317c478bd9Sstevel@tonic-gate FMODE(s1))) > 0)
10327c478bd9Sstevel@tonic-gate return (1);
1033d2443e76Smarks /*
1034d2443e76Smarks * Reapply ACL, since chmod may have
1035d2443e76Smarks * altered ACL
1036d2443e76Smarks */
1037d2443e76Smarks if (s1acl != NULL) {
1038d2443e76Smarks if ((acl_set(target, s1acl)) < 0) {
1039cee1ddadSbasabi error++;
1040cee1ddadSbasabi (void) fprintf(stderr,
1041cee1ddadSbasabi gettext("%s: Failed to "
1042cee1ddadSbasabi "set acl entries "
1043cee1ddadSbasabi "on %s\n"), cmd, target);
1044d2443e76Smarks /*
1045d2443e76Smarks * else: silent and
1046d2443e76Smarks * continue
1047d2443e76Smarks */
1048d2443e76Smarks }
1049d2443e76Smarks }
10507c478bd9Sstevel@tonic-gate if ((ret = chg_time(target, s1)) > 0)
10517c478bd9Sstevel@tonic-gate return (1);
10527c478bd9Sstevel@tonic-gate }
10537c478bd9Sstevel@tonic-gate if (cpy) {
1054cee1ddadSbasabi if (error != 0 || attret != 0 || sattret != 0)
10557c478bd9Sstevel@tonic-gate return (1);
10567c478bd9Sstevel@tonic-gate return (0);
10577c478bd9Sstevel@tonic-gate }
10587c478bd9Sstevel@tonic-gate goto cleanup;
10597c478bd9Sstevel@tonic-gate }
10607c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10617c478bd9Sstevel@tonic-gate gettext("%s: %s: unknown file type 0x%x\n"), cmd,
10627c478bd9Sstevel@tonic-gate source, (s1.st_mode & S_IFMT));
10637c478bd9Sstevel@tonic-gate return (1);
10647c478bd9Sstevel@tonic-gate
10657c478bd9Sstevel@tonic-gate cleanup:
10667c478bd9Sstevel@tonic-gate if (unlink(source) < 0) {
10677c478bd9Sstevel@tonic-gate (void) unlink(target);
10687c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10697c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
10707c478bd9Sstevel@tonic-gate cmd, source);
10717c478bd9Sstevel@tonic-gate perror("");
10727c478bd9Sstevel@tonic-gate return (1);
10737c478bd9Sstevel@tonic-gate }
1074cee1ddadSbasabi if (error != 0 || attret != 0 || sattret != 0)
1075cee1ddadSbasabi return (1);
10767c478bd9Sstevel@tonic-gate return (ret);
10777c478bd9Sstevel@tonic-gate }
10787c478bd9Sstevel@tonic-gate /*NOTREACHED*/
1079a77d64afScf46844 return (ret);
10807c478bd9Sstevel@tonic-gate }
10817c478bd9Sstevel@tonic-gate
10827c478bd9Sstevel@tonic-gate /*
10837c478bd9Sstevel@tonic-gate * create_tnode()
10847c478bd9Sstevel@tonic-gate *
10857c478bd9Sstevel@tonic-gate * Create a node for use with the search tree which contains the
10867c478bd9Sstevel@tonic-gate * inode information (device id and inode number).
10877c478bd9Sstevel@tonic-gate *
10887c478bd9Sstevel@tonic-gate * Input
10897c478bd9Sstevel@tonic-gate * dev - device id
10907c478bd9Sstevel@tonic-gate * ino - inode number
10917c478bd9Sstevel@tonic-gate *
10927c478bd9Sstevel@tonic-gate * Output
10937c478bd9Sstevel@tonic-gate * tnode - NULL on error, otherwise returns a tnode structure
10947c478bd9Sstevel@tonic-gate * which contains the input device id and inode number.
10957c478bd9Sstevel@tonic-gate */
10967c478bd9Sstevel@tonic-gate static tree_node_t *
create_tnode(dev_t dev,ino_t ino)10977c478bd9Sstevel@tonic-gate create_tnode(dev_t dev, ino_t ino)
10987c478bd9Sstevel@tonic-gate {
10997c478bd9Sstevel@tonic-gate tree_node_t *tnode;
11007c478bd9Sstevel@tonic-gate
11017c478bd9Sstevel@tonic-gate if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
11027c478bd9Sstevel@tonic-gate tnode->node_dev = dev;
11037c478bd9Sstevel@tonic-gate tnode->node_ino = ino;
11047c478bd9Sstevel@tonic-gate }
11057c478bd9Sstevel@tonic-gate
11067c478bd9Sstevel@tonic-gate return (tnode);
11077c478bd9Sstevel@tonic-gate }
11087c478bd9Sstevel@tonic-gate
1109*6cf3cc9dSRobert Mustacchi static chkfiles_t
chkfiles(const char * source,char ** to)1110*6cf3cc9dSRobert Mustacchi chkfiles(const char *source, char **to)
11117c478bd9Sstevel@tonic-gate {
11127c478bd9Sstevel@tonic-gate char *buf = (char *)NULL;
11137c478bd9Sstevel@tonic-gate int (*statf)() = (cpy &&
11147c478bd9Sstevel@tonic-gate !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
11157c478bd9Sstevel@tonic-gate char *target = *to;
1116fa9e4066Sahrens int error;
11177c478bd9Sstevel@tonic-gate
11187c478bd9Sstevel@tonic-gate /*
11197c478bd9Sstevel@tonic-gate * Make sure source file exists.
11207c478bd9Sstevel@tonic-gate */
11217c478bd9Sstevel@tonic-gate if ((*statf)(source, &s1) < 0) {
11227c478bd9Sstevel@tonic-gate /*
11237c478bd9Sstevel@tonic-gate * Keep the old error message except when someone tries to
11247c478bd9Sstevel@tonic-gate * mv/cp/ln a symbolic link that has a trailing slash and
11257c478bd9Sstevel@tonic-gate * points to a file.
11267c478bd9Sstevel@tonic-gate */
11277c478bd9Sstevel@tonic-gate if (errno == ENOTDIR)
11287c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
11297c478bd9Sstevel@tonic-gate strerror(errno));
11307c478bd9Sstevel@tonic-gate else
11317c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11327c478bd9Sstevel@tonic-gate gettext("%s: cannot access %s\n"), cmd, source);
1133*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
11347c478bd9Sstevel@tonic-gate }
11357c478bd9Sstevel@tonic-gate
11367c478bd9Sstevel@tonic-gate /*
1137ee9c203bSRenaud Manus * Get ACL info: don't bother with ln or cp/mv'ing symlinks
11387c478bd9Sstevel@tonic-gate */
1139ee9c203bSRenaud Manus if (!lnk && !ISLNK(s1)) {
1140fa9e4066Sahrens if (s1acl != NULL) {
1141fa9e4066Sahrens acl_free(s1acl);
1142fa9e4066Sahrens s1acl = NULL;
11437c478bd9Sstevel@tonic-gate }
1144fa9e4066Sahrens if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
11457c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
1146fa9e4066Sahrens "%s: failed to get acl entries: %s\n", source,
1147fa9e4066Sahrens acl_strerror(error));
1148*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate /* else: just permission bits */
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate
11537c478bd9Sstevel@tonic-gate /*
11547c478bd9Sstevel@tonic-gate * If stat fails, then the target doesn't exist,
11557c478bd9Sstevel@tonic-gate * we will create a new target with default file type of regular.
11567c478bd9Sstevel@tonic-gate */
11577c478bd9Sstevel@tonic-gate
11587c478bd9Sstevel@tonic-gate FTYPE(s2) = S_IFREG;
11597c478bd9Sstevel@tonic-gate targetexists = 0;
11607c478bd9Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) {
11617c478bd9Sstevel@tonic-gate if (ISLNK(s2))
11627c478bd9Sstevel@tonic-gate (void) stat(target, &s2);
11637c478bd9Sstevel@tonic-gate /*
11647c478bd9Sstevel@tonic-gate * If target is a directory,
11657c478bd9Sstevel@tonic-gate * make complete name of new file
11667c478bd9Sstevel@tonic-gate * within that directory.
11677c478bd9Sstevel@tonic-gate */
11687c478bd9Sstevel@tonic-gate if (ISDIR(s2)) {
11697c478bd9Sstevel@tonic-gate size_t len;
11707c478bd9Sstevel@tonic-gate
11717c478bd9Sstevel@tonic-gate len = strlen(target) + strlen(dname(source)) + 4;
11727c478bd9Sstevel@tonic-gate if ((buf = (char *)malloc(len)) == NULL) {
11737c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11747c478bd9Sstevel@tonic-gate gettext("%s: Insufficient memory to "
11757c478bd9Sstevel@tonic-gate "%s %s\n "), cmd, cmd, source);
11767c478bd9Sstevel@tonic-gate exit(3);
11777c478bd9Sstevel@tonic-gate }
11787c478bd9Sstevel@tonic-gate (void) snprintf(buf, len, "%s/%s",
11797c478bd9Sstevel@tonic-gate target, dname(source));
11807c478bd9Sstevel@tonic-gate *to = target = buf;
11817c478bd9Sstevel@tonic-gate }
11827c478bd9Sstevel@tonic-gate
11837c478bd9Sstevel@tonic-gate if ((*statf)(target, &s2) >= 0) {
1184*6cf3cc9dSRobert Mustacchi boolean_t prompt = B_FALSE;
1185*6cf3cc9dSRobert Mustacchi boolean_t overwrite = B_FALSE;
1186*6cf3cc9dSRobert Mustacchi boolean_t override = B_FALSE;
11877c478bd9Sstevel@tonic-gate
11887c478bd9Sstevel@tonic-gate targetexists++;
11897c478bd9Sstevel@tonic-gate if (cpy || mve) {
11907c478bd9Sstevel@tonic-gate /*
11917c478bd9Sstevel@tonic-gate * For cp and mv, it is an error if the
11927c478bd9Sstevel@tonic-gate * source and target are the same file.
11937c478bd9Sstevel@tonic-gate * Check for the same inode and file
11947c478bd9Sstevel@tonic-gate * system, but don't check for the same
11957c478bd9Sstevel@tonic-gate * absolute pathname because it is an
11967c478bd9Sstevel@tonic-gate * error when the source and target are
11977c478bd9Sstevel@tonic-gate * hard links to the same file.
11987c478bd9Sstevel@tonic-gate */
11997c478bd9Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
12007c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12017c478bd9Sstevel@tonic-gate gettext(
12027c478bd9Sstevel@tonic-gate "%s: %s and %s are identical\n"),
12037c478bd9Sstevel@tonic-gate cmd, source, target);
12047c478bd9Sstevel@tonic-gate if (buf != NULL)
12057c478bd9Sstevel@tonic-gate free(buf);
1206*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
12077c478bd9Sstevel@tonic-gate }
12087c478bd9Sstevel@tonic-gate }
12097c478bd9Sstevel@tonic-gate if (lnk) {
12107c478bd9Sstevel@tonic-gate /*
12117c478bd9Sstevel@tonic-gate * For ln, it is an error if the source and
12127c478bd9Sstevel@tonic-gate * target are identical files (same inode,
12137c478bd9Sstevel@tonic-gate * same file system, and filenames resolve
12147c478bd9Sstevel@tonic-gate * to same absolute pathname).
12157c478bd9Sstevel@tonic-gate */
12167c478bd9Sstevel@tonic-gate if (!chk_different(source, target)) {
12177c478bd9Sstevel@tonic-gate if (buf != NULL)
12187c478bd9Sstevel@tonic-gate free(buf);
1219*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
12207c478bd9Sstevel@tonic-gate }
12217c478bd9Sstevel@tonic-gate }
1222*6cf3cc9dSRobert Mustacchi
1223*6cf3cc9dSRobert Mustacchi /*
1224*6cf3cc9dSRobert Mustacchi * Determine if we need to prompt for overwriting the
1225*6cf3cc9dSRobert Mustacchi * file or for overriding its permissions. There is a
1226*6cf3cc9dSRobert Mustacchi * lot of thorny history here.
1227*6cf3cc9dSRobert Mustacchi *
1228*6cf3cc9dSRobert Mustacchi * The action we take depends on targact, which is the
1229*6cf3cc9dSRobert Mustacchi * user's selection or the command's default behavior.
1230*6cf3cc9dSRobert Mustacchi * If the user has explicitly opted into prompting,
1231*6cf3cc9dSRobert Mustacchi * explicitly asked invoked a force option, or said to
1232*6cf3cc9dSRobert Mustacchi * ignore things when there is a file here, then our
1233*6cf3cc9dSRobert Mustacchi * options are straightforward. When we're with the
1234*6cf3cc9dSRobert Mustacchi * program defaults, things get a bit more nuanced and
1235*6cf3cc9dSRobert Mustacchi * vary by program:
1236*6cf3cc9dSRobert Mustacchi *
1237*6cf3cc9dSRobert Mustacchi * ln: by default always fail with an error if target
1238*6cf3cc9dSRobert Mustacchi * exists, regardless of what kind of entity it is.
1239*6cf3cc9dSRobert Mustacchi *
1240*6cf3cc9dSRobert Mustacchi * cp: overwriting is allowed by default, overriding is
1241*6cf3cc9dSRobert Mustacchi * not. To override the -f flag will be specified which
1242*6cf3cc9dSRobert Mustacchi * will force removal.
1243*6cf3cc9dSRobert Mustacchi *
1244*6cf3cc9dSRobert Mustacchi * mv: overwriting is allowed by default, overriding
1245*6cf3cc9dSRobert Mustacchi * requires prompting if on stdin, otherwise it
1246*6cf3cc9dSRobert Mustacchi * proceeds. Note, "on stdin" varies based on XPG4 or
1247*6cf3cc9dSRobert Mustacchi * not.
1248*6cf3cc9dSRobert Mustacchi *
1249*6cf3cc9dSRobert Mustacchi * The history here is messy. Logically speaking the way
1250*6cf3cc9dSRobert Mustacchi * that overwriting and overriding was checked in the
1251*6cf3cc9dSRobert Mustacchi * past was the following rough logic:
1252*6cf3cc9dSRobert Mustacchi *
1253*6cf3cc9dSRobert Mustacchi * 1) Overwriting is considered if -i is set, -f (mv
1254*6cf3cc9dSRobert Mustacchi * only) wasn't set, and use_stdin() was true (always
1255*6cf3cc9dSRobert Mustacchi * true for XPG4, otherwise only if it was a tty).
1256*6cf3cc9dSRobert Mustacchi *
1257*6cf3cc9dSRobert Mustacchi * 2) Overriding is considered if it was mv, the file
1258*6cf3cc9dSRobert Mustacchi * was not write accessible, -f wasn't specified and the
1259*6cf3cc9dSRobert Mustacchi * target wasn't a symbolic link.
1260*6cf3cc9dSRobert Mustacchi *
1261*6cf3cc9dSRobert Mustacchi * 3) If both overwrite and override were set, it would
1262*6cf3cc9dSRobert Mustacchi * always prompt. If just override was set, it would
1263*6cf3cc9dSRobert Mustacchi * always prompt. However, if only overwrite was set, it
1264*6cf3cc9dSRobert Mustacchi * would only prompt if the target was a regular file!
1265*6cf3cc9dSRobert Mustacchi *
1266*6cf3cc9dSRobert Mustacchi * Based on this, you can see that cp/mv -i didn't
1267*6cf3cc9dSRobert Mustacchi * actually prompt for any number of cases as it didn't
1268*6cf3cc9dSRobert Mustacchi * consider if -i had been specified, which is
1269*6cf3cc9dSRobert Mustacchi * definitely against the spirit of -i (and POSIX). If
1270*6cf3cc9dSRobert Mustacchi * -i is specified we will **always** consider the
1271*6cf3cc9dSRobert Mustacchi * prompt based on use_stdin() because of history. If we
1272*6cf3cc9dSRobert Mustacchi * are looking at defaults, then we will honor the
1273*6cf3cc9dSRobert Mustacchi * historical conditions that were used to check for
1274*6cf3cc9dSRobert Mustacchi * overwriting and overriding.
1275*6cf3cc9dSRobert Mustacchi */
1276*6cf3cc9dSRobert Mustacchi switch (targact) {
1277*6cf3cc9dSRobert Mustacchi case TA_SKIP:
1278*6cf3cc9dSRobert Mustacchi if (buf != NULL)
1279*6cf3cc9dSRobert Mustacchi free(buf);
1280*6cf3cc9dSRobert Mustacchi return (CHK_SKIP);
1281*6cf3cc9dSRobert Mustacchi case TA_OVERWRITE:
1282*6cf3cc9dSRobert Mustacchi break;
1283*6cf3cc9dSRobert Mustacchi case TA_PROMPT:
1284*6cf3cc9dSRobert Mustacchi if (use_stdin()) {
1285*6cf3cc9dSRobert Mustacchi prompt = B_TRUE;
1286*6cf3cc9dSRobert Mustacchi overwrite = B_TRUE;
1287*6cf3cc9dSRobert Mustacchi if (mve && access(target, W_OK) < 0 &&
1288*6cf3cc9dSRobert Mustacchi !ISLNK(s2)) {
1289*6cf3cc9dSRobert Mustacchi override = B_TRUE;
1290*6cf3cc9dSRobert Mustacchi }
1291*6cf3cc9dSRobert Mustacchi }
1292*6cf3cc9dSRobert Mustacchi break;
1293*6cf3cc9dSRobert Mustacchi case TA_DEFAULT:
1294*6cf3cc9dSRobert Mustacchi if (lnk) {
12957c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12967c478bd9Sstevel@tonic-gate gettext("%s: %s: File exists\n"),
12977c478bd9Sstevel@tonic-gate cmd, target);
12987c478bd9Sstevel@tonic-gate if (buf != NULL)
12997c478bd9Sstevel@tonic-gate free(buf);
1300*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
13017c478bd9Sstevel@tonic-gate }
13027c478bd9Sstevel@tonic-gate
13037c478bd9Sstevel@tonic-gate /*
1304*6cf3cc9dSRobert Mustacchi * Now we have to figure out what we're going to
1305*6cf3cc9dSRobert Mustacchi * do here. Determine if this meets the
1306*6cf3cc9dSRobert Mustacchi * traditional prompting guidelines.
13077c478bd9Sstevel@tonic-gate */
1308*6cf3cc9dSRobert Mustacchi if (mve && access(target, W_OK) < 0 &&
1309*6cf3cc9dSRobert Mustacchi use_stdin() && !ISLNK(s2)) {
1310*6cf3cc9dSRobert Mustacchi prompt = B_TRUE;
1311*6cf3cc9dSRobert Mustacchi override = B_TRUE;
1312*6cf3cc9dSRobert Mustacchi }
1313*6cf3cc9dSRobert Mustacchi break;
1314*6cf3cc9dSRobert Mustacchi }
13157c478bd9Sstevel@tonic-gate
1316*6cf3cc9dSRobert Mustacchi /*
1317*6cf3cc9dSRobert Mustacchi * We've been asked to prompt. Determine the appropriate
1318*6cf3cc9dSRobert Mustacchi * message for the command and the type of action that
1319*6cf3cc9dSRobert Mustacchi * is going on.
1320*6cf3cc9dSRobert Mustacchi */
1321*6cf3cc9dSRobert Mustacchi if (prompt) {
1322*6cf3cc9dSRobert Mustacchi assert(overwrite || override);
13230f6e7ba6Sas145665 if (overwrite && override) {
1324*6cf3cc9dSRobert Mustacchi (void) fprintf(stderr, gettext("%s: "
1325*6cf3cc9dSRobert Mustacchi "overwrite %s and override "
1326*6cf3cc9dSRobert Mustacchi "protection %o (%s/%s)? "), cmd,
1327*6cf3cc9dSRobert Mustacchi target, FMODE(s2) & MODEBITS,
13283d63ea05Sas145665 yesstr, nostr);
1329*6cf3cc9dSRobert Mustacchi } else if (overwrite) {
1330*6cf3cc9dSRobert Mustacchi (void) fprintf(stderr, gettext("%s: "
1331*6cf3cc9dSRobert Mustacchi "overwrite %s (%s/%s)? "), cmd,
1332*6cf3cc9dSRobert Mustacchi target, yesstr, nostr);
1333*6cf3cc9dSRobert Mustacchi } else if (override) {
1334*6cf3cc9dSRobert Mustacchi (void) fprintf(stderr, gettext("%s: "
1335*6cf3cc9dSRobert Mustacchi "%s: override protection %o "
1336*6cf3cc9dSRobert Mustacchi "(%s/%s)? "), cmd, target,
1337*6cf3cc9dSRobert Mustacchi FMODE(s2) & MODEBITS, yesstr,
1338*6cf3cc9dSRobert Mustacchi nostr);
1339*6cf3cc9dSRobert Mustacchi }
13403d63ea05Sas145665 if (yes() == 0) {
13417c478bd9Sstevel@tonic-gate if (buf != NULL)
13427c478bd9Sstevel@tonic-gate free(buf);
1343*6cf3cc9dSRobert Mustacchi return (CHK_SKIP);
13447c478bd9Sstevel@tonic-gate }
13457c478bd9Sstevel@tonic-gate }
13460f6e7ba6Sas145665
13477c478bd9Sstevel@tonic-gate if (lnk && unlink(target) < 0) {
13487c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
13497c478bd9Sstevel@tonic-gate gettext("%s: cannot unlink %s: "),
13507c478bd9Sstevel@tonic-gate cmd, target);
13517c478bd9Sstevel@tonic-gate perror("");
1352*6cf3cc9dSRobert Mustacchi return (CHK_ERROR);
13537c478bd9Sstevel@tonic-gate }
13547c478bd9Sstevel@tonic-gate }
13557c478bd9Sstevel@tonic-gate }
1356*6cf3cc9dSRobert Mustacchi return (CHK_CONT);
13577c478bd9Sstevel@tonic-gate }
13587c478bd9Sstevel@tonic-gate
13597c478bd9Sstevel@tonic-gate /*
13607c478bd9Sstevel@tonic-gate * check whether source and target are different
13617c478bd9Sstevel@tonic-gate * return 1 when they are different
13627c478bd9Sstevel@tonic-gate * return 0 when they are identical, or when unable to resolve a pathname
13637c478bd9Sstevel@tonic-gate */
13647c478bd9Sstevel@tonic-gate static int
chk_different(const char * source,const char * target)1365*6cf3cc9dSRobert Mustacchi chk_different(const char *source, const char *target)
13667c478bd9Sstevel@tonic-gate {
13677c478bd9Sstevel@tonic-gate char rtarget[PATH_MAX], rsource[PATH_MAX];
13687c478bd9Sstevel@tonic-gate
13697c478bd9Sstevel@tonic-gate if (IDENTICAL(s1, s2)) {
13707c478bd9Sstevel@tonic-gate /*
13717c478bd9Sstevel@tonic-gate * IDENTICAL will be true for hard links, therefore
13727c478bd9Sstevel@tonic-gate * check whether the filenames are different
13737c478bd9Sstevel@tonic-gate */
13747c478bd9Sstevel@tonic-gate if ((getrealpath(source, rsource) == 0) ||
13757c478bd9Sstevel@tonic-gate (getrealpath(target, rtarget) == 0)) {
13767c478bd9Sstevel@tonic-gate return (0);
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
13797c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
13807c478bd9Sstevel@tonic-gate "%s: %s and %s are identical\n"),
13817c478bd9Sstevel@tonic-gate cmd, source, target);
13827c478bd9Sstevel@tonic-gate return (0);
13837c478bd9Sstevel@tonic-gate }
13847c478bd9Sstevel@tonic-gate }
13857c478bd9Sstevel@tonic-gate return (1);
13867c478bd9Sstevel@tonic-gate }
13877c478bd9Sstevel@tonic-gate
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate * get real path (resolved absolute pathname)
13907c478bd9Sstevel@tonic-gate * return 1 on success, 0 on failure
13917c478bd9Sstevel@tonic-gate */
13927c478bd9Sstevel@tonic-gate static int
getrealpath(const char * path,char * rpath)1393*6cf3cc9dSRobert Mustacchi getrealpath(const char *path, char *rpath)
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate if (realpath(path, rpath) == NULL) {
13967c478bd9Sstevel@tonic-gate int errno_save = errno;
13977c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
1398e1636a06Sdanny webster "%s: cannot resolve path %s: "), cmd, path);
13997c478bd9Sstevel@tonic-gate errno = errno_save;
14007c478bd9Sstevel@tonic-gate perror("");
14017c478bd9Sstevel@tonic-gate return (0);
14027c478bd9Sstevel@tonic-gate }
14037c478bd9Sstevel@tonic-gate return (1);
14047c478bd9Sstevel@tonic-gate }
14057c478bd9Sstevel@tonic-gate
14067c478bd9Sstevel@tonic-gate static int
rcopy(const char * from,char * to)1407*6cf3cc9dSRobert Mustacchi rcopy(const char *from, char *to)
14087c478bd9Sstevel@tonic-gate {
14097c478bd9Sstevel@tonic-gate DIR *fold = opendir(from);
14107c478bd9Sstevel@tonic-gate struct dirent *dp;
14117c478bd9Sstevel@tonic-gate struct stat statb, s1save;
14127c478bd9Sstevel@tonic-gate int errs = 0;
14137c478bd9Sstevel@tonic-gate char fromname[PATH_MAX];
14147c478bd9Sstevel@tonic-gate
14157c478bd9Sstevel@tonic-gate if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
14167c478bd9Sstevel@tonic-gate Perror(from);
14177c478bd9Sstevel@tonic-gate return (1);
14187c478bd9Sstevel@tonic-gate }
14197c478bd9Sstevel@tonic-gate if (pflg || mve) {
14207c478bd9Sstevel@tonic-gate /*
14217c478bd9Sstevel@tonic-gate * Save s1 (stat information for source dir) so that
14227c478bd9Sstevel@tonic-gate * mod and access times can be reserved during "cp -p"
14237c478bd9Sstevel@tonic-gate * or mv, since s1 gets overwritten.
14247c478bd9Sstevel@tonic-gate */
14257c478bd9Sstevel@tonic-gate s1save = s1;
14267c478bd9Sstevel@tonic-gate }
14277c478bd9Sstevel@tonic-gate for (;;) {
14287c478bd9Sstevel@tonic-gate dp = readdir(fold);
14297c478bd9Sstevel@tonic-gate if (dp == 0) {
14307c478bd9Sstevel@tonic-gate (void) closedir(fold);
14317c478bd9Sstevel@tonic-gate if (pflg || mve)
14327c478bd9Sstevel@tonic-gate return (chg_time(to, s1save) + errs);
14337c478bd9Sstevel@tonic-gate return (errs);
14347c478bd9Sstevel@tonic-gate }
14357c478bd9Sstevel@tonic-gate if (dp->d_ino == 0)
14367c478bd9Sstevel@tonic-gate continue;
14377c478bd9Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) ||
14387c478bd9Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0))
14397c478bd9Sstevel@tonic-gate continue;
14407c478bd9Sstevel@tonic-gate if (strlen(from)+1+strlen(dp->d_name) >=
14417c478bd9Sstevel@tonic-gate sizeof (fromname) - 1) {
14427c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
14437c478bd9Sstevel@tonic-gate gettext("%s : %s/%s: Name too long\n"),
14447c478bd9Sstevel@tonic-gate cmd, from, dp->d_name);
14457c478bd9Sstevel@tonic-gate errs++;
14467c478bd9Sstevel@tonic-gate continue;
14477c478bd9Sstevel@tonic-gate }
14487c478bd9Sstevel@tonic-gate (void) snprintf(fromname, sizeof (fromname),
14497c478bd9Sstevel@tonic-gate "%s/%s", from, dp->d_name);
14507c478bd9Sstevel@tonic-gate errs += cpymve(fromname, to);
14517c478bd9Sstevel@tonic-gate }
14527c478bd9Sstevel@tonic-gate }
14537c478bd9Sstevel@tonic-gate
1454*6cf3cc9dSRobert Mustacchi static const char *
dname(const char * name)1455*6cf3cc9dSRobert Mustacchi dname(const char *name)
14567c478bd9Sstevel@tonic-gate {
1457*6cf3cc9dSRobert Mustacchi const char *p;
14587c478bd9Sstevel@tonic-gate
14597c478bd9Sstevel@tonic-gate /*
14607c478bd9Sstevel@tonic-gate * Return just the file name given the complete path.
14617c478bd9Sstevel@tonic-gate * Like basename(1).
14627c478bd9Sstevel@tonic-gate */
14637c478bd9Sstevel@tonic-gate
14647c478bd9Sstevel@tonic-gate p = name;
14657c478bd9Sstevel@tonic-gate
14667c478bd9Sstevel@tonic-gate /*
14677c478bd9Sstevel@tonic-gate * While there are characters left,
14687c478bd9Sstevel@tonic-gate * set name to start after last
14697c478bd9Sstevel@tonic-gate * delimiter.
14707c478bd9Sstevel@tonic-gate */
14717c478bd9Sstevel@tonic-gate
14727c478bd9Sstevel@tonic-gate while (*p)
14737c478bd9Sstevel@tonic-gate if (*p++ == DELIM && *p)
14747c478bd9Sstevel@tonic-gate name = p;
14757c478bd9Sstevel@tonic-gate return (name);
14767c478bd9Sstevel@tonic-gate }
14777c478bd9Sstevel@tonic-gate
14787c478bd9Sstevel@tonic-gate static void
usage(void)14797c478bd9Sstevel@tonic-gate usage(void)
14807c478bd9Sstevel@tonic-gate {
14817c478bd9Sstevel@tonic-gate /*
14827c478bd9Sstevel@tonic-gate * Display usage message.
14837c478bd9Sstevel@tonic-gate */
14847c478bd9Sstevel@tonic-gate
14857c478bd9Sstevel@tonic-gate if (mve) {
14867c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
1487*6cf3cc9dSRobert Mustacchi "Usage: mv [-fin] f1 f2\n"
1488*6cf3cc9dSRobert Mustacchi " mv [-fin] f1 ... fn d1\n"
1489*6cf3cc9dSRobert Mustacchi " mv [-fin] d1 d2\n"));
14907c478bd9Sstevel@tonic-gate } else if (lnk) {
14917c478bd9Sstevel@tonic-gate #ifdef XPG4
14923d63ea05Sas145665 (void) fprintf(stderr, gettext(
1493*6cf3cc9dSRobert Mustacchi "Usage: ln [-fi] [-s] f1 [f2]\n"
1494*6cf3cc9dSRobert Mustacchi " ln [-fi] [-s] f1 ... fn d1\n"
1495*6cf3cc9dSRobert Mustacchi " ln [-fi] -s d1 d2\n"));
14967c478bd9Sstevel@tonic-gate #else
14973d63ea05Sas145665 (void) fprintf(stderr, gettext(
1498*6cf3cc9dSRobert Mustacchi "Usage: ln [-fi] [-n] [-s] f1 [f2]\n"
1499*6cf3cc9dSRobert Mustacchi " ln [-fi] [-n] [-s] f1 ... fn d1\n"
1500*6cf3cc9dSRobert Mustacchi " ln [-fi] [-n] -s d1 d2\n"));
15017c478bd9Sstevel@tonic-gate #endif
15027c478bd9Sstevel@tonic-gate } else if (cpy) {
15037c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
1504*6cf3cc9dSRobert Mustacchi "Usage: cp [-afinp@/] f1 f2\n"
1505*6cf3cc9dSRobert Mustacchi " cp [-afinp@/] f1 ... fn d1\n"
1506*6cf3cc9dSRobert Mustacchi " cp [-r|-R [-H|-L|-P]] [-afinp@/] "
1507*6cf3cc9dSRobert Mustacchi "d1 ... dn-1 dn\n"));
15087c478bd9Sstevel@tonic-gate }
15097c478bd9Sstevel@tonic-gate exit(2);
15107c478bd9Sstevel@tonic-gate }
15117c478bd9Sstevel@tonic-gate
15127c478bd9Sstevel@tonic-gate /*
15137c478bd9Sstevel@tonic-gate * chg_time()
15147c478bd9Sstevel@tonic-gate *
15157c478bd9Sstevel@tonic-gate * Try to preserve modification and access time.
15167c478bd9Sstevel@tonic-gate * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
151778cca7e2SRoger A. Faulkner * don't report a utimensat() failure.
151878cca7e2SRoger A. Faulkner * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
15197c478bd9Sstevel@tonic-gate * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
15207c478bd9Sstevel@tonic-gate * exit status only if pflg is set.
152178cca7e2SRoger A. Faulkner * utimensat(2) is being used to achieve granularity in nanoseconds
152278cca7e2SRoger A. Faulkner * (if supported by the underlying file system) while setting file times.
15237c478bd9Sstevel@tonic-gate */
15247c478bd9Sstevel@tonic-gate static int
chg_time(const char * to,struct stat ss)1525*6cf3cc9dSRobert Mustacchi chg_time(const char *to, struct stat ss)
15267c478bd9Sstevel@tonic-gate {
152778cca7e2SRoger A. Faulkner struct timespec times[2];
1528dfe02591SToomas Soome #ifdef XPG4
15297c478bd9Sstevel@tonic-gate int rc;
1530dfe02591SToomas Soome #endif
15317c478bd9Sstevel@tonic-gate
153278cca7e2SRoger A. Faulkner times[0] = ss.st_atim;
153378cca7e2SRoger A. Faulkner times[1] = ss.st_mtim;
15347c478bd9Sstevel@tonic-gate
1535dfe02591SToomas Soome #ifdef XPG4
15366cdb7222SAlexander Eremin rc = utimensat(AT_FDCWD, to, times,
15376cdb7222SAlexander Eremin ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
15387c478bd9Sstevel@tonic-gate if ((pflg || mve) && rc != 0) {
15397c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
15407c478bd9Sstevel@tonic-gate gettext("%s: cannot set times for %s: "), cmd, to);
15417c478bd9Sstevel@tonic-gate perror("");
15427c478bd9Sstevel@tonic-gate if (pflg)
15437c478bd9Sstevel@tonic-gate return (1);
15447c478bd9Sstevel@tonic-gate }
1545dfe02591SToomas Soome #else
1546dfe02591SToomas Soome (void) utimensat(AT_FDCWD, to, times,
1547dfe02591SToomas Soome ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
15487c478bd9Sstevel@tonic-gate #endif
15497c478bd9Sstevel@tonic-gate
15507c478bd9Sstevel@tonic-gate return (0);
15517c478bd9Sstevel@tonic-gate
15527c478bd9Sstevel@tonic-gate }
15537c478bd9Sstevel@tonic-gate
15547c478bd9Sstevel@tonic-gate /*
15557c478bd9Sstevel@tonic-gate * chg_mode()
15567c478bd9Sstevel@tonic-gate *
15577c478bd9Sstevel@tonic-gate * This function is called upon "cp -p" or mv across filesystems.
15587c478bd9Sstevel@tonic-gate *
15597c478bd9Sstevel@tonic-gate * Try to preserve the owner and group id. If chown() fails,
15607c478bd9Sstevel@tonic-gate * only print a diagnostic message if doing a mv in the XPG4 version;
15617c478bd9Sstevel@tonic-gate * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear
15627c478bd9Sstevel@tonic-gate * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
15637c478bd9Sstevel@tonic-gate * non-zero exit status because this is a security violation.
15647c478bd9Sstevel@tonic-gate * Try to preserve permissions.
15657c478bd9Sstevel@tonic-gate * If this is the XPG4 version and chmod() fails, print a diagnostic message
15667c478bd9Sstevel@tonic-gate * and arrange for a non-zero exit status.
15677c478bd9Sstevel@tonic-gate * If this is the Solaris version and chmod() fails, do not print a
15687c478bd9Sstevel@tonic-gate * diagnostic message or exit with a non-zero value.
15697c478bd9Sstevel@tonic-gate */
15707c478bd9Sstevel@tonic-gate static int
chg_mode(const char * target,uid_t uid,gid_t gid,mode_t mode)1571*6cf3cc9dSRobert Mustacchi chg_mode(const char *target, uid_t uid, gid_t gid, mode_t mode)
15727c478bd9Sstevel@tonic-gate {
15737c478bd9Sstevel@tonic-gate int clearflg = 0; /* controls message printed upon chown() error */
15746cdb7222SAlexander Eremin struct stat st;
15756cdb7222SAlexander Eremin
15766cdb7222SAlexander Eremin /* Don't change mode if target is symlink */
15776cdb7222SAlexander Eremin if (lstat(target, &st) == 0 && ISLNK(st))
15786cdb7222SAlexander Eremin return (0);
15797c478bd9Sstevel@tonic-gate
15807c478bd9Sstevel@tonic-gate if (chown(target, uid, gid) != 0) {
15817c478bd9Sstevel@tonic-gate #ifdef XPG4
15827c478bd9Sstevel@tonic-gate if (mve) {
15837c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: cannot change"
15847c478bd9Sstevel@tonic-gate " owner and group of %s: "), cmd, target);
15857c478bd9Sstevel@tonic-gate perror("");
15867c478bd9Sstevel@tonic-gate }
15877c478bd9Sstevel@tonic-gate #endif
15887c478bd9Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) {
15897c478bd9Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */
15907c478bd9Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID;
15917c478bd9Sstevel@tonic-gate ++clearflg;
15927c478bd9Sstevel@tonic-gate }
15937c478bd9Sstevel@tonic-gate }
15947c478bd9Sstevel@tonic-gate if (chmod(target, mode) != 0) {
15957c478bd9Sstevel@tonic-gate if (clearflg) {
15967c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
15977c478bd9Sstevel@tonic-gate "%s: cannot clear S_ISUID and S_ISGID bits in"
15987c478bd9Sstevel@tonic-gate " %s: "), cmd, target);
15997c478bd9Sstevel@tonic-gate perror("");
16007c478bd9Sstevel@tonic-gate /* cp -p should get non-zero exit; mv should not */
16017c478bd9Sstevel@tonic-gate if (pflg)
16027c478bd9Sstevel@tonic-gate return (1);
16037c478bd9Sstevel@tonic-gate }
16047c478bd9Sstevel@tonic-gate #ifdef XPG4
16057c478bd9Sstevel@tonic-gate else {
16067c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
16077c478bd9Sstevel@tonic-gate "%s: cannot set permissions for %s: "), cmd, target);
16087c478bd9Sstevel@tonic-gate perror("");
16097c478bd9Sstevel@tonic-gate /* cp -p should get non-zero exit; mv should not */
16107c478bd9Sstevel@tonic-gate if (pflg)
16117c478bd9Sstevel@tonic-gate return (1);
16127c478bd9Sstevel@tonic-gate }
16137c478bd9Sstevel@tonic-gate #endif
16147c478bd9Sstevel@tonic-gate }
16157c478bd9Sstevel@tonic-gate return (0);
16167c478bd9Sstevel@tonic-gate
16177c478bd9Sstevel@tonic-gate }
16187c478bd9Sstevel@tonic-gate
16197c478bd9Sstevel@tonic-gate static void
Perror(const char * s)1620*6cf3cc9dSRobert Mustacchi Perror(const char *s)
16217c478bd9Sstevel@tonic-gate {
16227c478bd9Sstevel@tonic-gate char buf[PATH_MAX + 10];
16237c478bd9Sstevel@tonic-gate
16247c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
16257c478bd9Sstevel@tonic-gate perror(buf);
16267c478bd9Sstevel@tonic-gate }
16277c478bd9Sstevel@tonic-gate
16287c478bd9Sstevel@tonic-gate static void
Perror2(const char * s1,const char * s2)1629*6cf3cc9dSRobert Mustacchi Perror2(const char *s1, const char *s2)
16307c478bd9Sstevel@tonic-gate {
16317c478bd9Sstevel@tonic-gate char buf[PATH_MAX + 20];
16327c478bd9Sstevel@tonic-gate
16337c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
16347c478bd9Sstevel@tonic-gate cmd, gettext(s1), gettext(s2));
16357c478bd9Sstevel@tonic-gate perror(buf);
16367c478bd9Sstevel@tonic-gate }
16377c478bd9Sstevel@tonic-gate
16387c478bd9Sstevel@tonic-gate /*
16397c478bd9Sstevel@tonic-gate * used for cp -R and for mv across file systems
16407c478bd9Sstevel@tonic-gate */
16417c478bd9Sstevel@tonic-gate static int
copydir(const char * source,char * target)1642*6cf3cc9dSRobert Mustacchi copydir(const char *source, char *target)
16437c478bd9Sstevel@tonic-gate {
16447c478bd9Sstevel@tonic-gate int ret, attret = 0;
1645cee1ddadSbasabi int sattret = 0;
16467c478bd9Sstevel@tonic-gate int pret = 0; /* need separate flag if -p is specified */
16477c478bd9Sstevel@tonic-gate mode_t fixmode = (mode_t)0; /* cleanup mode after copy */
16487c478bd9Sstevel@tonic-gate struct stat s1save;
1649fa9e4066Sahrens acl_t *s1acl_save;
1650cee1ddadSbasabi int error = 0;
1651fa9e4066Sahrens
1652fa9e4066Sahrens s1acl_save = NULL;
16537c478bd9Sstevel@tonic-gate
16547c478bd9Sstevel@tonic-gate if (cpy && !rflg) {
16557c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16567c478bd9Sstevel@tonic-gate gettext("%s: %s: is a directory\n"), cmd, source);
16577c478bd9Sstevel@tonic-gate return (1);
16587c478bd9Sstevel@tonic-gate }
16597c478bd9Sstevel@tonic-gate
16607c478bd9Sstevel@tonic-gate if (stat(target, &s2) < 0) {
16617c478bd9Sstevel@tonic-gate if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
16627c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", cmd);
16637c478bd9Sstevel@tonic-gate perror(target);
16647c478bd9Sstevel@tonic-gate return (1);
16657c478bd9Sstevel@tonic-gate }
16667c478bd9Sstevel@tonic-gate if (stat(target, &s2) == 0) {
16677c478bd9Sstevel@tonic-gate fixmode = s2.st_mode;
16687c478bd9Sstevel@tonic-gate } else {
16697c478bd9Sstevel@tonic-gate fixmode = s1.st_mode;
16707c478bd9Sstevel@tonic-gate }
16717c478bd9Sstevel@tonic-gate (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
16727c478bd9Sstevel@tonic-gate } else if (!(ISDIR(s2))) {
16737c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16747c478bd9Sstevel@tonic-gate gettext("%s: %s: not a directory.\n"), cmd, target);
16757c478bd9Sstevel@tonic-gate return (1);
16767c478bd9Sstevel@tonic-gate }
16777c478bd9Sstevel@tonic-gate if (pflg || mve) {
16787c478bd9Sstevel@tonic-gate /*
16797c478bd9Sstevel@tonic-gate * Save s1 (stat information for source dir) and acl info,
16807c478bd9Sstevel@tonic-gate * if any, so that ownership, modes, times, and acl's can
16817c478bd9Sstevel@tonic-gate * be reserved during "cp -p" or mv.
16827c478bd9Sstevel@tonic-gate * s1 gets overwritten when doing the recursive copy.
16837c478bd9Sstevel@tonic-gate */
16847c478bd9Sstevel@tonic-gate s1save = s1;
1685fa9e4066Sahrens if (s1acl != NULL) {
1686fa9e4066Sahrens s1acl_save = acl_dup(s1acl);
1687fa9e4066Sahrens if (s1acl_save == NULL) {
1688fa9e4066Sahrens (void) fprintf(stderr, gettext("%s: "
1689fa9e4066Sahrens "Insufficient memory to save acl"
1690fa9e4066Sahrens " entry\n"), cmd);
1691fa9e4066Sahrens if (pflg)
1692fa9e4066Sahrens return (1);
1693fa9e4066Sahrens
16947c478bd9Sstevel@tonic-gate }
16953d63ea05Sas145665 #ifdef XPG4
16963d63ea05Sas145665 else {
16973d63ea05Sas145665 (void) fprintf(stderr, gettext("%s: "
16983d63ea05Sas145665 "Insufficient memory to save acl"
16993d63ea05Sas145665 " entry\n"), cmd);
17003d63ea05Sas145665 if (pflg)
17013d63ea05Sas145665 return (1);
17023d63ea05Sas145665 }
17037c478bd9Sstevel@tonic-gate #endif
17047c478bd9Sstevel@tonic-gate }
17057c478bd9Sstevel@tonic-gate }
17067c478bd9Sstevel@tonic-gate
17077c478bd9Sstevel@tonic-gate ret = rcopy(source, target);
17087c478bd9Sstevel@tonic-gate
17097c478bd9Sstevel@tonic-gate /*
17107c478bd9Sstevel@tonic-gate * Once we created a directory, go ahead and set
17117c478bd9Sstevel@tonic-gate * its attributes, e.g. acls and time. The info
17127c478bd9Sstevel@tonic-gate * may get overwritten if we continue traversing
17137c478bd9Sstevel@tonic-gate * down the tree.
17147c478bd9Sstevel@tonic-gate *
17157c478bd9Sstevel@tonic-gate * ACL for directory
17167c478bd9Sstevel@tonic-gate */
17177c478bd9Sstevel@tonic-gate if (pflg || mve) {
1718d2443e76Smarks if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1719d2443e76Smarks FMODE(s1save))) == 0)
1720d2443e76Smarks pret = chg_time(target, s1save);
1721d2443e76Smarks ret += pret;
1722fa9e4066Sahrens if (s1acl_save != NULL) {
1723fa9e4066Sahrens if (acl_set(target, s1acl_save) < 0) {
1724cee1ddadSbasabi error++;
17257c478bd9Sstevel@tonic-gate #ifdef XPG4
17267c478bd9Sstevel@tonic-gate if (pflg || mve) {
17277c478bd9Sstevel@tonic-gate #else
17287c478bd9Sstevel@tonic-gate if (pflg) {
17297c478bd9Sstevel@tonic-gate #endif
17307c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
17317c478bd9Sstevel@tonic-gate "%s: failed to set acl entries "
17327c478bd9Sstevel@tonic-gate "on %s\n"), cmd, target);
17337c478bd9Sstevel@tonic-gate if (pflg) {
1734fa9e4066Sahrens acl_free(s1acl_save);
1735fa9e4066Sahrens s1acl_save = NULL;
17367c478bd9Sstevel@tonic-gate ret++;
17377c478bd9Sstevel@tonic-gate }
17387c478bd9Sstevel@tonic-gate }
17397c478bd9Sstevel@tonic-gate /* else: silent and continue */
17407c478bd9Sstevel@tonic-gate }
1741fa9e4066Sahrens acl_free(s1acl_save);
1742fa9e4066Sahrens s1acl_save = NULL;
17437c478bd9Sstevel@tonic-gate }
17447c478bd9Sstevel@tonic-gate } else if (fixmode != (mode_t)0)
17457c478bd9Sstevel@tonic-gate (void) chmod(target, fixmode & MODEBITS);
17467c478bd9Sstevel@tonic-gate
1747da6c28aaSamw if (pflg || atflg || mve || saflg) {
17487c478bd9Sstevel@tonic-gate attret = copyattributes(source, target);
17497c478bd9Sstevel@tonic-gate if (!attrsilent && attret != 0) {
17507c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Failed to preserve"
17517c478bd9Sstevel@tonic-gate " extended attributes of directory"
17527c478bd9Sstevel@tonic-gate " %s\n"), cmd, source);
17537c478bd9Sstevel@tonic-gate } else {
17547c478bd9Sstevel@tonic-gate /*
17557c478bd9Sstevel@tonic-gate * Otherwise ignore failure.
17567c478bd9Sstevel@tonic-gate */
17577c478bd9Sstevel@tonic-gate attret = 0;
17587c478bd9Sstevel@tonic-gate }
1759da6c28aaSamw /* Copy extended system attributes */
1760da6c28aaSamw if (pflg || mve || saflg) {
1761cee1ddadSbasabi sattret = copy_sysattr(source, target);
1762cee1ddadSbasabi if (sattret != 0) {
1763da6c28aaSamw (void) fprintf(stderr, gettext(
1764da6c28aaSamw "%s: Failed to preserve "
1765da6c28aaSamw "extended system attributes "
1766da6c28aaSamw "of directory %s\n"), cmd, source);
1767da6c28aaSamw }
1768da6c28aaSamw }
17697c478bd9Sstevel@tonic-gate }
1770cee1ddadSbasabi if (attret != 0 || sattret != 0 || error != 0)
1771cee1ddadSbasabi return (1);
17727c478bd9Sstevel@tonic-gate return (ret);
17737c478bd9Sstevel@tonic-gate }
17747c478bd9Sstevel@tonic-gate
17757c478bd9Sstevel@tonic-gate static int
1776*6cf3cc9dSRobert Mustacchi copyspecial(const char *target)
17777c478bd9Sstevel@tonic-gate {
17787c478bd9Sstevel@tonic-gate int ret = 0;
17797c478bd9Sstevel@tonic-gate
17807c478bd9Sstevel@tonic-gate if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
17817c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
17827c478bd9Sstevel@tonic-gate "cp: cannot create special file %s: "), target);
17837c478bd9Sstevel@tonic-gate perror("");
17847c478bd9Sstevel@tonic-gate return (1);
17857c478bd9Sstevel@tonic-gate }
17867c478bd9Sstevel@tonic-gate
17877c478bd9Sstevel@tonic-gate if (pflg) {
17887c478bd9Sstevel@tonic-gate if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
17897c478bd9Sstevel@tonic-gate ret = chg_time(target, s1);
17907c478bd9Sstevel@tonic-gate }
17917c478bd9Sstevel@tonic-gate
17927c478bd9Sstevel@tonic-gate return (ret);
17937c478bd9Sstevel@tonic-gate }
17947c478bd9Sstevel@tonic-gate
17957c478bd9Sstevel@tonic-gate static int
17967c478bd9Sstevel@tonic-gate use_stdin(void)
17977c478bd9Sstevel@tonic-gate {
17987c478bd9Sstevel@tonic-gate #ifdef XPG4
17997c478bd9Sstevel@tonic-gate return (1);
18007c478bd9Sstevel@tonic-gate #else
18017c478bd9Sstevel@tonic-gate return (isatty(fileno(stdin)));
18027c478bd9Sstevel@tonic-gate #endif
18037c478bd9Sstevel@tonic-gate }
18047c478bd9Sstevel@tonic-gate
1805da6c28aaSamw /* Copy non-system extended attributes */
1806da6c28aaSamw
18077c478bd9Sstevel@tonic-gate static int
1808*6cf3cc9dSRobert Mustacchi copyattributes(const char *source, const char *target)
18097c478bd9Sstevel@tonic-gate {
18107c478bd9Sstevel@tonic-gate struct dirent *dp;
18117c478bd9Sstevel@tonic-gate int error = 0;
1812fa9e4066Sahrens int aclerror;
18137c478bd9Sstevel@tonic-gate mode_t mode;
18147c478bd9Sstevel@tonic-gate int clearflg = 0;
1815fa9e4066Sahrens acl_t *xacl = NULL;
1816fa9e4066Sahrens acl_t *attrdiracl = NULL;
181778cca7e2SRoger A. Faulkner struct timespec times[2];
18187c478bd9Sstevel@tonic-gate
1819da6c28aaSamw
18207c478bd9Sstevel@tonic-gate if (pathconf(source, _PC_XATTR_EXISTS) != 1)
18217c478bd9Sstevel@tonic-gate return (0);
18227c478bd9Sstevel@tonic-gate
18237c478bd9Sstevel@tonic-gate if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
18247c478bd9Sstevel@tonic-gate if (!attrsilent) {
18257c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18267c478bd9Sstevel@tonic-gate gettext(
18277c478bd9Sstevel@tonic-gate "%s: cannot preserve extended attributes, "
18287c478bd9Sstevel@tonic-gate "operation not supported on file"
18297c478bd9Sstevel@tonic-gate " %s\n"), cmd, target);
18307c478bd9Sstevel@tonic-gate }
18317c478bd9Sstevel@tonic-gate return (1);
18327c478bd9Sstevel@tonic-gate }
1833da6c28aaSamw if (open_source(source) != 0)
1834da6c28aaSamw return (1);
1835da6c28aaSamw if (open_target_srctarg_attrdirs(source, target) != 0)
1836da6c28aaSamw return (1);
1837da6c28aaSamw if (open_attrdirp(source) != 0)
1838da6c28aaSamw return (1);
18397c478bd9Sstevel@tonic-gate
18407c478bd9Sstevel@tonic-gate if (pflg || mve) {
18417c478bd9Sstevel@tonic-gate if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
18427c478bd9Sstevel@tonic-gate if (!attrsilent) {
18437c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18447c478bd9Sstevel@tonic-gate gettext("%s: failed to set file mode"
18457c478bd9Sstevel@tonic-gate " correctly on attribute directory of"
18467c478bd9Sstevel@tonic-gate " file %s: "), cmd, target);
18477c478bd9Sstevel@tonic-gate perror("");
18487c478bd9Sstevel@tonic-gate ++error;
18497c478bd9Sstevel@tonic-gate }
18507c478bd9Sstevel@tonic-gate }
18517c478bd9Sstevel@tonic-gate
18527c478bd9Sstevel@tonic-gate if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
18537c478bd9Sstevel@tonic-gate if (!attrsilent) {
18547c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18557c478bd9Sstevel@tonic-gate gettext("%s: failed to set file"
18567c478bd9Sstevel@tonic-gate " ownership correctly on attribute"
18577c478bd9Sstevel@tonic-gate " directory of file %s: "), cmd, target);
18587c478bd9Sstevel@tonic-gate perror("");
18597c478bd9Sstevel@tonic-gate ++error;
18607c478bd9Sstevel@tonic-gate }
18617c478bd9Sstevel@tonic-gate }
18627c478bd9Sstevel@tonic-gate /*
18637c478bd9Sstevel@tonic-gate * Now that we are the owner we can update st_ctime by calling
186478cca7e2SRoger A. Faulkner * utimensat.
18657c478bd9Sstevel@tonic-gate */
186678cca7e2SRoger A. Faulkner times[0] = attrdir.st_atim;
186778cca7e2SRoger A. Faulkner times[1] = attrdir.st_mtim;
186878cca7e2SRoger A. Faulkner if (utimensat(targetdirfd, ".", times, 0) < 0) {
18697c478bd9Sstevel@tonic-gate if (!attrsilent) {
18707c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18717c478bd9Sstevel@tonic-gate gettext("%s: cannot set attribute times"
18727c478bd9Sstevel@tonic-gate " for %s: "), cmd, target);
18737c478bd9Sstevel@tonic-gate perror("");
18747c478bd9Sstevel@tonic-gate ++error;
18757c478bd9Sstevel@tonic-gate }
18767c478bd9Sstevel@tonic-gate }
18777c478bd9Sstevel@tonic-gate
18787c478bd9Sstevel@tonic-gate /*
18797c478bd9Sstevel@tonic-gate * Now set owner and group of attribute directory, implies
18807c478bd9Sstevel@tonic-gate * changing the ACL of the hidden attribute directory first.
18817c478bd9Sstevel@tonic-gate */
1882fa9e4066Sahrens if ((aclerror = facl_get(sourcedirfd,
1883fa9e4066Sahrens ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
18847c478bd9Sstevel@tonic-gate if (!attrsilent) {
18857c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
18867c478bd9Sstevel@tonic-gate "%s: failed to get acl entries of"
18877c478bd9Sstevel@tonic-gate " attribute directory for"
1888fa9e4066Sahrens " %s : %s\n"), cmd,
1889fa9e4066Sahrens source, acl_strerror(aclerror));
18907c478bd9Sstevel@tonic-gate ++error;
18917c478bd9Sstevel@tonic-gate }
18927c478bd9Sstevel@tonic-gate }
18937c478bd9Sstevel@tonic-gate
1894fa9e4066Sahrens if (attrdiracl) {
1895fa9e4066Sahrens if (facl_set(targetdirfd, attrdiracl) != 0) {
18967c478bd9Sstevel@tonic-gate if (!attrsilent) {
18977c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
18987c478bd9Sstevel@tonic-gate "%s: failed to set acl entries"
18997c478bd9Sstevel@tonic-gate " on attribute directory "
19007c478bd9Sstevel@tonic-gate "for %s\n"), cmd, target);
19017c478bd9Sstevel@tonic-gate ++error;
19027c478bd9Sstevel@tonic-gate }
1903fa9e4066Sahrens acl_free(attrdiracl);
1904fa9e4066Sahrens attrdiracl = NULL;
19057c478bd9Sstevel@tonic-gate }
19067c478bd9Sstevel@tonic-gate }
19077c478bd9Sstevel@tonic-gate }
19087c478bd9Sstevel@tonic-gate
1909da6c28aaSamw while ((dp = readdir(srcdirp)) != NULL) {
1910da6c28aaSamw int ret;
19117c478bd9Sstevel@tonic-gate
1912da6c28aaSamw if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
19137c478bd9Sstevel@tonic-gate continue;
1914da6c28aaSamw else if (ret > 0) {
19157c478bd9Sstevel@tonic-gate ++error;
1916da6c28aaSamw goto out;
19177c478bd9Sstevel@tonic-gate }
19187c478bd9Sstevel@tonic-gate
19197c478bd9Sstevel@tonic-gate if (pflg || mve) {
1920fa9e4066Sahrens if ((aclerror = facl_get(srcattrfd,
1921fa9e4066Sahrens ACL_NO_TRIVIAL, &xacl)) != 0) {
19227c478bd9Sstevel@tonic-gate if (!attrsilent) {
19237c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
19247c478bd9Sstevel@tonic-gate "%s: failed to get acl entries of"
19257c478bd9Sstevel@tonic-gate " attribute %s for"
1926fa9e4066Sahrens " %s: %s"), cmd, dp->d_name,
1927fa9e4066Sahrens source, acl_strerror(aclerror));
19287c478bd9Sstevel@tonic-gate ++error;
19297c478bd9Sstevel@tonic-gate }
19307c478bd9Sstevel@tonic-gate }
19317c478bd9Sstevel@tonic-gate }
19327c478bd9Sstevel@tonic-gate
19337c478bd9Sstevel@tonic-gate /*
19347c478bd9Sstevel@tonic-gate * preserve ACL
19357c478bd9Sstevel@tonic-gate */
1936fa9e4066Sahrens if ((pflg || mve) && xacl != NULL) {
1937fa9e4066Sahrens if ((facl_set(targattrfd, xacl)) < 0) {
19387c478bd9Sstevel@tonic-gate if (!attrsilent) {
19397c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
19407c478bd9Sstevel@tonic-gate "%s: failed to set acl entries on"
19417c478bd9Sstevel@tonic-gate " attribute %s for"
19427c478bd9Sstevel@tonic-gate "%s\n"), cmd, dp->d_name, target);
19437c478bd9Sstevel@tonic-gate ++error;
19447c478bd9Sstevel@tonic-gate }
1945fa9e4066Sahrens acl_free(xacl);
1946fa9e4066Sahrens xacl = NULL;
19477c478bd9Sstevel@tonic-gate }
19487c478bd9Sstevel@tonic-gate }
19497c478bd9Sstevel@tonic-gate
1950da6c28aaSamw if (writefile(srcattrfd, targattrfd, source, target,
1951da6c28aaSamw dp->d_name, dp->d_name, &s3, &s4) != 0) {
19527c478bd9Sstevel@tonic-gate if (!attrsilent) {
19537c478bd9Sstevel@tonic-gate ++error;
19547c478bd9Sstevel@tonic-gate }
19557c478bd9Sstevel@tonic-gate goto next;
19567c478bd9Sstevel@tonic-gate }
19577c478bd9Sstevel@tonic-gate
19587c478bd9Sstevel@tonic-gate if (pflg || mve) {
19597c478bd9Sstevel@tonic-gate mode = FMODE(s3);
19607c478bd9Sstevel@tonic-gate
19617c478bd9Sstevel@tonic-gate if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
19627c478bd9Sstevel@tonic-gate if (!attrsilent) {
19637c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19647c478bd9Sstevel@tonic-gate gettext("%s: cannot change"
19657c478bd9Sstevel@tonic-gate " owner and group of"
19667c478bd9Sstevel@tonic-gate " attribute %s for" " file"
19677c478bd9Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target);
19687c478bd9Sstevel@tonic-gate perror("");
19697c478bd9Sstevel@tonic-gate ++error;
19707c478bd9Sstevel@tonic-gate }
19717c478bd9Sstevel@tonic-gate if (mode & (S_ISUID | S_ISGID)) {
19727c478bd9Sstevel@tonic-gate /* try to clear S_ISUID and S_ISGID */
19737c478bd9Sstevel@tonic-gate mode &= ~S_ISUID & ~S_ISGID;
19747c478bd9Sstevel@tonic-gate ++clearflg;
19757c478bd9Sstevel@tonic-gate }
19767c478bd9Sstevel@tonic-gate }
197778cca7e2SRoger A. Faulkner times[0] = s3.st_atim;
197878cca7e2SRoger A. Faulkner times[1] = s3.st_mtim;
197978cca7e2SRoger A. Faulkner if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
19807c478bd9Sstevel@tonic-gate if (!attrsilent) {
19817c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19827c478bd9Sstevel@tonic-gate gettext("%s: cannot set attribute"
19837c478bd9Sstevel@tonic-gate " times for %s: "), cmd, target);
19847c478bd9Sstevel@tonic-gate perror("");
19857c478bd9Sstevel@tonic-gate ++error;
19867c478bd9Sstevel@tonic-gate }
19877c478bd9Sstevel@tonic-gate }
19887c478bd9Sstevel@tonic-gate if (fchmod(targattrfd, mode) != 0) {
19897c478bd9Sstevel@tonic-gate if (clearflg) {
19907c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(
19917c478bd9Sstevel@tonic-gate "%s: cannot clear S_ISUID and "
19927c478bd9Sstevel@tonic-gate "S_ISGID bits in attribute %s"
19937c478bd9Sstevel@tonic-gate " for file"
19947c478bd9Sstevel@tonic-gate " %s: "), cmd, dp->d_name, target);
19957c478bd9Sstevel@tonic-gate } else {
19967c478bd9Sstevel@tonic-gate if (!attrsilent) {
19977c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19987c478bd9Sstevel@tonic-gate gettext(
19997c478bd9Sstevel@tonic-gate "%s: cannot set permissions of attribute"
20007c478bd9Sstevel@tonic-gate " %s for %s: "), cmd, dp->d_name, target);
20017c478bd9Sstevel@tonic-gate perror("");
20027c478bd9Sstevel@tonic-gate ++error;
20037c478bd9Sstevel@tonic-gate }
20047c478bd9Sstevel@tonic-gate }
20057c478bd9Sstevel@tonic-gate }
2006d2443e76Smarks if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
2007d2443e76Smarks if (!attrsilent) {
2008d2443e76Smarks (void) fprintf(stderr, gettext(
2009d2443e76Smarks "%s: failed to set acl entries on"
2010d2443e76Smarks " attribute %s for"
2011d2443e76Smarks "%s\n"), cmd, dp->d_name, target);
2012d2443e76Smarks ++error;
2013d2443e76Smarks }
2014d2443e76Smarks acl_free(xacl);
2015d2443e76Smarks xacl = NULL;
2016d2443e76Smarks }
20177c478bd9Sstevel@tonic-gate }
20187c478bd9Sstevel@tonic-gate next:
2019fa9e4066Sahrens if (xacl != NULL) {
2020fa9e4066Sahrens acl_free(xacl);
2021fa9e4066Sahrens xacl = NULL;
20227c478bd9Sstevel@tonic-gate }
20237c478bd9Sstevel@tonic-gate if (srcattrfd != -1)
20247c478bd9Sstevel@tonic-gate (void) close(srcattrfd);
20257c478bd9Sstevel@tonic-gate if (targattrfd != -1)
20267c478bd9Sstevel@tonic-gate (void) close(targattrfd);
20277c478bd9Sstevel@tonic-gate srcattrfd = targattrfd = -1;
20287c478bd9Sstevel@tonic-gate }
20297c478bd9Sstevel@tonic-gate out:
2030fa9e4066Sahrens if (xacl != NULL) {
2031fa9e4066Sahrens acl_free(xacl);
2032fa9e4066Sahrens xacl = NULL;
2033fa9e4066Sahrens }
2034fa9e4066Sahrens if (attrdiracl != NULL) {
2035fa9e4066Sahrens acl_free(attrdiracl);
2036fa9e4066Sahrens attrdiracl = NULL;
2037fa9e4066Sahrens }
2038da6c28aaSamw
2039da6c28aaSamw if (!saflg && !pflg && !mve)
2040da6c28aaSamw close_all();
2041da6c28aaSamw return (error == 0 ? 0 : 1);
2042da6c28aaSamw }
2043da6c28aaSamw
2044da6c28aaSamw /* Copy extended system attributes from source to target */
2045da6c28aaSamw
2046da6c28aaSamw static int
2047*6cf3cc9dSRobert Mustacchi copy_sysattr(const char *source, const char *target)
2048da6c28aaSamw {
2049da6c28aaSamw struct dirent *dp;
2050da6c28aaSamw nvlist_t *response;
2051da6c28aaSamw int error = 0;
20522b9240ecSbasabi int target_sa_support = 0;
2053da6c28aaSamw
2054da6c28aaSamw if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
2055da6c28aaSamw return (0);
2056da6c28aaSamw
2057da6c28aaSamw if (open_source(source) != 0)
2058da6c28aaSamw return (1);
2059da6c28aaSamw
2060da6c28aaSamw /*
2061da6c28aaSamw * Gets non default extended system attributes from the
2062da6c28aaSamw * source file to copy to the target. The target has
2063da6c28aaSamw * the defaults set when its created and thus no need
2064da6c28aaSamw * to copy the defaults.
2065da6c28aaSamw */
2066da6c28aaSamw response = sysattr_list(cmd, srcfd, source);
2067da6c28aaSamw
20682b9240ecSbasabi if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
20692b9240ecSbasabi if (response != NULL) {
2070da6c28aaSamw (void) fprintf(stderr,
2071da6c28aaSamw gettext(
2072da6c28aaSamw "%s: cannot preserve extended system "
2073da6c28aaSamw "attribute, operation not supported on file"
2074da6c28aaSamw " %s\n"), cmd, target);
2075da6c28aaSamw error++;
2076da6c28aaSamw goto out;
2077da6c28aaSamw }
20782b9240ecSbasabi } else {
20792b9240ecSbasabi target_sa_support = 1;
20802b9240ecSbasabi }
2081da6c28aaSamw
20822b9240ecSbasabi if (target_sa_support) {
2083da6c28aaSamw if (srcdirp == NULL) {
20842b9240ecSbasabi if (open_target_srctarg_attrdirs(source,
20852b9240ecSbasabi target) != 0) {
2086da6c28aaSamw error++;
2087da6c28aaSamw goto out;
2088da6c28aaSamw }
2089da6c28aaSamw if (open_attrdirp(source) != 0) {
2090da6c28aaSamw error++;
2091da6c28aaSamw goto out;
2092da6c28aaSamw }
2093da6c28aaSamw } else {
2094da6c28aaSamw rewind_attrdir(srcdirp);
2095da6c28aaSamw }
20962b9240ecSbasabi while ((dp = readdir(srcdirp)) != NULL) {
2097da6c28aaSamw nvlist_t *res;
2098da6c28aaSamw int ret;
2099da6c28aaSamw
21002b9240ecSbasabi if ((ret = traverse_attrfile(dp, source, target,
21012b9240ecSbasabi 0)) == -1)
2102da6c28aaSamw continue;
2103da6c28aaSamw else if (ret > 0) {
2104da6c28aaSamw ++error;
2105da6c28aaSamw goto out;
2106da6c28aaSamw }
2107da6c28aaSamw /*
2108da6c28aaSamw * Gets non default extended system attributes from the
2109da6c28aaSamw * attribute file to copy to the target. The target has
2110da6c28aaSamw * the defaults set when its created and thus no need
2111da6c28aaSamw * to copy the defaults.
2112da6c28aaSamw */
2113da6c28aaSamw res = sysattr_list(cmd, srcattrfd, dp->d_name);
2114da6c28aaSamw if (res == NULL)
2115da6c28aaSamw goto next;
2116da6c28aaSamw
2117da6c28aaSamw /*
2118da6c28aaSamw * Copy non default extended system attributes of named
2119da6c28aaSamw * attribute file.
2120da6c28aaSamw */
21212b9240ecSbasabi if (fsetattr(targattrfd,
2122da6c28aaSamw XATTR_VIEW_READWRITE, res) != 0) {
2123da6c28aaSamw ++error;
2124da6c28aaSamw (void) fprintf(stderr, gettext("%s: "
2125da6c28aaSamw "Failed to copy extended system "
2126da6c28aaSamw "attributes from attribute file "
2127da6c28aaSamw "%s of %s to %s\n"), cmd,
2128da6c28aaSamw dp->d_name, source, target);
2129da6c28aaSamw }
213031b6814eSJohn Levon
2131da6c28aaSamw next:
2132da6c28aaSamw if (srcattrfd != -1)
2133da6c28aaSamw (void) close(srcattrfd);
2134da6c28aaSamw if (targattrfd != -1)
2135da6c28aaSamw (void) close(targattrfd);
2136da6c28aaSamw srcattrfd = targattrfd = -1;
2137da6c28aaSamw nvlist_free(res);
2138da6c28aaSamw }
21392b9240ecSbasabi }
2140da6c28aaSamw /* Copy source file non default extended system attributes to target */
21412b9240ecSbasabi if (target_sa_support && (response != NULL) &&
2142da6c28aaSamw (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
2143da6c28aaSamw ++error;
2144da6c28aaSamw (void) fprintf(stderr, gettext("%s: Failed to "
2145da6c28aaSamw "copy extended system attributes from "
2146da6c28aaSamw "%s to %s\n"), cmd, source, target);
2147da6c28aaSamw }
2148da6c28aaSamw out:
2149da6c28aaSamw nvlist_free(response);
2150da6c28aaSamw close_all();
21517c478bd9Sstevel@tonic-gate return (error == 0 ? 0 : 1);
21527c478bd9Sstevel@tonic-gate }
21537c478bd9Sstevel@tonic-gate
2154da6c28aaSamw /* Open the source file */
2155da6c28aaSamw
2156*6cf3cc9dSRobert Mustacchi static int
2157*6cf3cc9dSRobert Mustacchi open_source(const char *src)
2158da6c28aaSamw {
2159da6c28aaSamw int error = 0;
2160da6c28aaSamw
2161da6c28aaSamw srcfd = -1;
2162da6c28aaSamw if ((srcfd = open(src, O_RDONLY)) == -1) {
2163da6c28aaSamw if (pflg && attrsilent) {
2164da6c28aaSamw error++;
2165da6c28aaSamw goto out;
2166da6c28aaSamw }
2167da6c28aaSamw if (!attrsilent) {
2168da6c28aaSamw (void) fprintf(stderr,
2169da6c28aaSamw gettext("%s: cannot open file"
2170da6c28aaSamw " %s: "), cmd, src);
2171da6c28aaSamw perror("");
2172da6c28aaSamw }
2173da6c28aaSamw ++error;
2174da6c28aaSamw }
2175da6c28aaSamw out:
2176da6c28aaSamw if (error)
2177da6c28aaSamw close_all();
2178da6c28aaSamw return (error == 0 ? 0 : 1);
2179da6c28aaSamw }
2180da6c28aaSamw
2181da6c28aaSamw /* Open source attribute dir, target and target attribute dir. */
2182da6c28aaSamw
2183*6cf3cc9dSRobert Mustacchi static int
2184*6cf3cc9dSRobert Mustacchi open_target_srctarg_attrdirs(const char *src, const char *targ)
2185da6c28aaSamw {
2186da6c28aaSamw int error = 0;
2187da6c28aaSamw
2188da6c28aaSamw targfd = sourcedirfd = targetdirfd = -1;
2189da6c28aaSamw
2190da6c28aaSamw if ((targfd = open(targ, O_RDONLY)) == -1) {
2191da6c28aaSamw if (pflg && attrsilent) {
2192da6c28aaSamw error++;
2193da6c28aaSamw goto out;
2194da6c28aaSamw }
2195da6c28aaSamw if (!attrsilent) {
2196da6c28aaSamw (void) fprintf(stderr,
2197da6c28aaSamw gettext("%s: cannot open file"
2198da6c28aaSamw " %s: "), cmd, targ);
2199da6c28aaSamw perror("");
2200da6c28aaSamw }
2201da6c28aaSamw ++error;
2202da6c28aaSamw goto out;
2203da6c28aaSamw }
2204da6c28aaSamw
2205da6c28aaSamw if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2206da6c28aaSamw if (pflg && attrsilent) {
2207da6c28aaSamw error++;
2208da6c28aaSamw goto out;
2209da6c28aaSamw }
2210da6c28aaSamw if (!attrsilent) {
2211da6c28aaSamw (void) fprintf(stderr,
2212da6c28aaSamw gettext("%s: cannot open attribute"
2213da6c28aaSamw " directory for %s: "), cmd, src);
2214da6c28aaSamw perror("");
2215da6c28aaSamw }
2216da6c28aaSamw ++error;
2217da6c28aaSamw goto out;
2218da6c28aaSamw }
2219da6c28aaSamw
2220da6c28aaSamw if (fstat(sourcedirfd, &attrdir) == -1) {
2221da6c28aaSamw if (pflg && attrsilent) {
2222da6c28aaSamw error++;
2223da6c28aaSamw goto out;
2224da6c28aaSamw }
2225da6c28aaSamw
2226da6c28aaSamw if (!attrsilent) {
2227da6c28aaSamw (void) fprintf(stderr,
2228da6c28aaSamw gettext("%s: could not retrieve stat"
2229da6c28aaSamw " information for attribute directory"
2230da6c28aaSamw "of file %s: "), cmd, src);
2231da6c28aaSamw perror("");
2232da6c28aaSamw }
2233da6c28aaSamw ++error;
2234da6c28aaSamw goto out;
2235da6c28aaSamw }
2236da6c28aaSamw if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2237da6c28aaSamw if (pflg && attrsilent) {
2238da6c28aaSamw error++;
2239da6c28aaSamw goto out;
2240da6c28aaSamw }
2241da6c28aaSamw if (!attrsilent) {
2242da6c28aaSamw (void) fprintf(stderr,
2243da6c28aaSamw gettext("%s: cannot open attribute"
2244da6c28aaSamw " directory for %s: "), cmd, targ);
2245da6c28aaSamw perror("");
2246da6c28aaSamw }
2247da6c28aaSamw ++error;
2248da6c28aaSamw }
2249da6c28aaSamw out:
2250da6c28aaSamw if (error)
2251da6c28aaSamw close_all();
2252da6c28aaSamw return (error == 0 ? 0 : 1);
2253da6c28aaSamw }
2254da6c28aaSamw
2255*6cf3cc9dSRobert Mustacchi static int
2256*6cf3cc9dSRobert Mustacchi open_attrdirp(const char *source)
2257da6c28aaSamw {
2258da6c28aaSamw int tmpfd = -1;
2259da6c28aaSamw int error = 0;
2260da6c28aaSamw
2261da6c28aaSamw /*
2262da6c28aaSamw * dup sourcedirfd for use by fdopendir().
2263da6c28aaSamw * fdopendir will take ownership of given fd and will close
2264da6c28aaSamw * it when closedir() is called.
2265da6c28aaSamw */
2266da6c28aaSamw
2267da6c28aaSamw if ((tmpfd = dup(sourcedirfd)) == -1) {
2268da6c28aaSamw if (pflg && attrsilent) {
2269da6c28aaSamw error++;
2270da6c28aaSamw goto out;
2271da6c28aaSamw }
2272da6c28aaSamw if (!attrsilent) {
2273da6c28aaSamw (void) fprintf(stderr,
2274da6c28aaSamw gettext(
2275da6c28aaSamw "%s: unable to dup attribute directory"
2276da6c28aaSamw " file descriptor for %s: "), cmd, source);
2277da6c28aaSamw perror("");
2278da6c28aaSamw ++error;
2279da6c28aaSamw }
2280da6c28aaSamw goto out;
2281da6c28aaSamw }
2282da6c28aaSamw if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2283da6c28aaSamw if (pflg && attrsilent) {
2284da6c28aaSamw error++;
2285da6c28aaSamw goto out;
2286da6c28aaSamw }
2287da6c28aaSamw if (!attrsilent) {
2288da6c28aaSamw (void) fprintf(stderr,
2289da6c28aaSamw gettext("%s: failed to open attribute"
2290da6c28aaSamw " directory for %s: "), cmd, source);
2291da6c28aaSamw perror("");
2292da6c28aaSamw ++error;
2293da6c28aaSamw }
2294da6c28aaSamw }
2295da6c28aaSamw out:
2296da6c28aaSamw if (error)
2297da6c28aaSamw close_all();
2298da6c28aaSamw return (error == 0 ? 0 : 1);
2299da6c28aaSamw }
2300da6c28aaSamw
2301da6c28aaSamw /* Skips through ., .., and system attribute 'view' files */
2302*6cf3cc9dSRobert Mustacchi static int
2303*6cf3cc9dSRobert Mustacchi traverse_attrfile(struct dirent *dp, const char *source, const char *target,
2304*6cf3cc9dSRobert Mustacchi int first)
2305da6c28aaSamw {
2306da6c28aaSamw int error = 0;
2307da6c28aaSamw
2308beab0215Sbasabi srcattrfd = targattrfd = -1;
2309beab0215Sbasabi
2310da6c28aaSamw if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2311da6c28aaSamw (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2312da6c28aaSamw dp->d_name[2] == '\0') ||
2313da6c28aaSamw (sysattr_type(dp->d_name) == _RO_SATTR) ||
2314da6c28aaSamw (sysattr_type(dp->d_name) == _RW_SATTR))
2315da6c28aaSamw return (-1);
2316da6c28aaSamw
2317da6c28aaSamw if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2318da6c28aaSamw O_RDONLY)) == -1) {
2319da6c28aaSamw if (!attrsilent) {
2320da6c28aaSamw (void) fprintf(stderr,
2321da6c28aaSamw gettext("%s: cannot open attribute %s on"
2322da6c28aaSamw " file %s: "), cmd, dp->d_name, source);
2323da6c28aaSamw perror("");
2324da6c28aaSamw ++error;
2325da6c28aaSamw goto out;
2326da6c28aaSamw }
2327da6c28aaSamw }
2328da6c28aaSamw
2329da6c28aaSamw if (fstat(srcattrfd, &s3) < 0) {
2330da6c28aaSamw if (!attrsilent) {
2331da6c28aaSamw (void) fprintf(stderr,
2332da6c28aaSamw gettext("%s: could not stat attribute"
2333da6c28aaSamw " %s on file"
2334da6c28aaSamw " %s: "), cmd, dp->d_name, source);
2335da6c28aaSamw perror("");
2336da6c28aaSamw ++error;
2337da6c28aaSamw }
2338da6c28aaSamw goto out;
2339da6c28aaSamw }
2340da6c28aaSamw
2341da6c28aaSamw if (first) {
2342da6c28aaSamw (void) unlinkat(targetdirfd, dp->d_name, 0);
23433a1874bcSbasabi if ((targattrfd = openat(targetdirfd, dp->d_name,
23443a1874bcSbasabi O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2345da6c28aaSamw if (!attrsilent) {
2346da6c28aaSamw (void) fprintf(stderr,
2347da6c28aaSamw gettext("%s: could not create attribute"
2348da6c28aaSamw " %s on file %s: "), cmd, dp->d_name,
2349da6c28aaSamw target);
2350da6c28aaSamw perror("");
2351da6c28aaSamw ++error;
2352da6c28aaSamw }
2353da6c28aaSamw goto out;
2354da6c28aaSamw }
23553a1874bcSbasabi } else {
23563a1874bcSbasabi if ((targattrfd = openat(targetdirfd, dp->d_name,
23573a1874bcSbasabi O_RDONLY)) == -1) {
23583a1874bcSbasabi if (!attrsilent) {
23593a1874bcSbasabi (void) fprintf(stderr,
23603a1874bcSbasabi gettext("%s: could not open attribute"
23613a1874bcSbasabi " %s on file %s: "), cmd, dp->d_name,
23623a1874bcSbasabi target);
23633a1874bcSbasabi perror("");
23643a1874bcSbasabi ++error;
23653a1874bcSbasabi }
23663a1874bcSbasabi goto out;
23673a1874bcSbasabi }
23683a1874bcSbasabi }
23693a1874bcSbasabi
2370da6c28aaSamw
2371da6c28aaSamw if (fstat(targattrfd, &s4) < 0) {
2372da6c28aaSamw if (!attrsilent) {
2373da6c28aaSamw (void) fprintf(stderr,
2374da6c28aaSamw gettext("%s: could not stat attribute"
2375da6c28aaSamw " %s on file"
2376da6c28aaSamw " %s: "), cmd, dp->d_name, target);
2377da6c28aaSamw perror("");
2378da6c28aaSamw ++error;
2379da6c28aaSamw }
2380da6c28aaSamw }
2381da6c28aaSamw
2382da6c28aaSamw out:
2383da6c28aaSamw if (error) {
2384da6c28aaSamw if (srcattrfd != -1)
2385da6c28aaSamw (void) close(srcattrfd);
2386da6c28aaSamw if (targattrfd != -1)
2387da6c28aaSamw (void) close(targattrfd);
2388beab0215Sbasabi srcattrfd = targattrfd = -1;
2389da6c28aaSamw }
2390da6c28aaSamw return (error == 0 ? 0 :1);
2391da6c28aaSamw }
2392da6c28aaSamw
2393*6cf3cc9dSRobert Mustacchi static void
2394da6c28aaSamw rewind_attrdir(DIR * sdp)
2395da6c28aaSamw {
2396da6c28aaSamw int pwdfd;
2397da6c28aaSamw
2398da6c28aaSamw pwdfd = open(".", O_RDONLY);
2399da6c28aaSamw if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2400da6c28aaSamw rewinddir(sdp);
2401da6c28aaSamw (void) fchdir(pwdfd);
2402da6c28aaSamw (void) close(pwdfd);
2403da6c28aaSamw } else {
2404da6c28aaSamw if (!attrsilent) {
2405da6c28aaSamw (void) fprintf(stderr, gettext("%s: "
2406da6c28aaSamw "failed to rewind attribute dir\n"),
2407da6c28aaSamw cmd);
2408da6c28aaSamw }
2409da6c28aaSamw }
2410da6c28aaSamw }
2411da6c28aaSamw
2412*6cf3cc9dSRobert Mustacchi static void
2413*6cf3cc9dSRobert Mustacchi close_all(void)
2414da6c28aaSamw {
2415da6c28aaSamw if (srcattrfd != -1)
2416da6c28aaSamw (void) close(srcattrfd);
2417da6c28aaSamw if (targattrfd != -1)
2418da6c28aaSamw (void) close(targattrfd);
2419da6c28aaSamw if (sourcedirfd != -1)
2420da6c28aaSamw (void) close(sourcedirfd);
2421da6c28aaSamw if (targetdirfd != -1)
2422da6c28aaSamw (void) close(targetdirfd);
2423da6c28aaSamw if (srcdirp != NULL) {
2424da6c28aaSamw (void) closedir(srcdirp);
2425da6c28aaSamw srcdirp = NULL;
2426da6c28aaSamw }
2427da6c28aaSamw if (srcfd != -1)
2428da6c28aaSamw (void) close(srcfd);
2429da6c28aaSamw if (targfd != -1)
2430da6c28aaSamw (void) close(targfd);
2431da6c28aaSamw }
2432