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