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