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