xref: /illumos-gate/usr/src/cmd/mv/mv.c (revision 6cf3cc9d1e40f89e90135a48f74f03f879fce639)
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