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