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