xref: /illumos-gate/usr/src/cmd/mv/mv.c (revision d840867f3a8b0ba209ef90762b3f9c72a5f92cc5)
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 2007 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 				if (getresp()) {
1237 					if (buf != NULL)
1238 						free(buf);
1239 					return (2);
1240 				}
1241 			} else if (overwrite && ISREG(s2)) {
1242 				(void) fprintf(stderr,
1243 				    gettext("%s: overwrite %s (%s/%s)? "),
1244 				    cmd, target, yeschr, nochr);
1245 				if (getresp()) {
1246 					if (buf != NULL)
1247 						free(buf);
1248 					return (2);
1249 				}
1250 			} else if (override) {
1251 				(void) fprintf(stderr,
1252 				    gettext("%s: %s: override protection "
1253 				    /*CSTYLED*/
1254 				    "%o (%s/%s)? "),
1255 				    /*CSTYLED*/
1256 				    cmd, target, FMODE(s2) & MODEBITS,
1257 				    yeschr, nochr);
1258 				if (getresp()) {
1259 					if (buf != NULL)
1260 						free(buf);
1261 					return (2);
1262 				}
1263 			}
1264 
1265 			if (lnk && unlink(target) < 0) {
1266 				(void) fprintf(stderr,
1267 				    gettext("%s: cannot unlink %s: "),
1268 				    cmd, target);
1269 				perror("");
1270 				return (1);
1271 			}
1272 		}
1273 	}
1274 	return (0);
1275 }
1276 
1277 /*
1278  * check whether source and target are different
1279  * return 1 when they are different
1280  * return 0 when they are identical, or when unable to resolve a pathname
1281  */
1282 static int
1283 chk_different(char *source, char *target)
1284 {
1285 	char	rtarget[PATH_MAX], rsource[PATH_MAX];
1286 
1287 	if (IDENTICAL(s1, s2)) {
1288 		/*
1289 		 * IDENTICAL will be true for hard links, therefore
1290 		 * check whether the filenames are different
1291 		 */
1292 		if ((getrealpath(source, rsource) == 0) ||
1293 		    (getrealpath(target, rtarget) == 0)) {
1294 			return (0);
1295 		}
1296 		if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
1297 			(void) fprintf(stderr, gettext(
1298 			    "%s: %s and %s are identical\n"),
1299 			    cmd, source, target);
1300 			return (0);
1301 		}
1302 	}
1303 	return (1);
1304 }
1305 
1306 /*
1307  * get real path (resolved absolute pathname)
1308  * return 1 on success, 0 on failure
1309  */
1310 static int
1311 getrealpath(char *path, char *rpath)
1312 {
1313 	if (realpath(path, rpath) == NULL) {
1314 		int	errno_save = errno;
1315 		(void) fprintf(stderr, gettext(
1316 		    "%s: can't resolve path %s: "), cmd, path);
1317 		errno = errno_save;
1318 		perror("");
1319 		return (0);
1320 	}
1321 	return (1);
1322 }
1323 
1324 static int
1325 rcopy(char *from, char *to)
1326 {
1327 	DIR *fold = opendir(from);
1328 	struct dirent *dp;
1329 	struct stat statb, s1save;
1330 	int errs = 0;
1331 	char fromname[PATH_MAX];
1332 
1333 	if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
1334 		Perror(from);
1335 		return (1);
1336 	}
1337 	if (pflg || mve) {
1338 		/*
1339 		 * Save s1 (stat information for source dir) so that
1340 		 * mod and access times can be reserved during "cp -p"
1341 		 * or mv, since s1 gets overwritten.
1342 		 */
1343 		s1save = s1;
1344 	}
1345 	for (;;) {
1346 		dp = readdir(fold);
1347 		if (dp == 0) {
1348 			(void) closedir(fold);
1349 			if (pflg || mve)
1350 				return (chg_time(to, s1save) + errs);
1351 			return (errs);
1352 		}
1353 		if (dp->d_ino == 0)
1354 			continue;
1355 		if ((strcmp(dp->d_name, ".") == 0) ||
1356 		    (strcmp(dp->d_name, "..") == 0))
1357 			continue;
1358 		if (strlen(from)+1+strlen(dp->d_name) >=
1359 		    sizeof (fromname) - 1) {
1360 			(void) fprintf(stderr,
1361 			    gettext("%s : %s/%s: Name too long\n"),
1362 			    cmd, from, dp->d_name);
1363 			errs++;
1364 			continue;
1365 		}
1366 		(void) snprintf(fromname, sizeof (fromname),
1367 		    "%s/%s", from, dp->d_name);
1368 		errs += cpymve(fromname, to);
1369 	}
1370 }
1371 
1372 static char *
1373 dname(char *name)
1374 {
1375 	register char *p;
1376 
1377 	/*
1378 	 * Return just the file name given the complete path.
1379 	 * Like basename(1).
1380 	 */
1381 
1382 	p = name;
1383 
1384 	/*
1385 	 * While there are characters left,
1386 	 * set name to start after last
1387 	 * delimiter.
1388 	 */
1389 
1390 	while (*p)
1391 		if (*p++ == DELIM && *p)
1392 			name = p;
1393 	return (name);
1394 }
1395 
1396 static int
1397 getresp(void)
1398 {
1399 	register int	c, i;
1400 	char	ans_buf[SCHAR_MAX + 1];
1401 
1402 	/*
1403 	 * Get response from user. Based on
1404 	 * first character, make decision.
1405 	 * Discard rest of line.
1406 	 */
1407 	for (i = 0; ; i++) {
1408 		c = getchar();
1409 		if (c == '\n' || c == 0 || c == EOF) {
1410 			ans_buf[i] = 0;
1411 			break;
1412 		}
1413 		if (i < SCHAR_MAX)
1414 			ans_buf[i] = c;
1415 	}
1416 	if (i >= SCHAR_MAX) {
1417 		i = SCHAR_MAX;
1418 		ans_buf[SCHAR_MAX] = 0;
1419 	}
1420 	if ((i == 0) | (strncmp(yeschr, ans_buf, i)))
1421 		return (1);
1422 	return (0);
1423 }
1424 
1425 static void
1426 usage(void)
1427 {
1428 	/*
1429 	 * Display usage message.
1430 	 */
1431 
1432 	if (mve) {
1433 		(void) fprintf(stderr, gettext(
1434 		    "Usage: mv [-f] [-i] f1 f2\n"
1435 		    "       mv [-f] [-i] f1 ... fn d1\n"
1436 		    "       mv [-f] [-i] d1 d2\n"));
1437 	} else if (lnk) {
1438 		(void) fprintf(stderr, gettext(
1439 #ifdef XPG4
1440 		    "Usage: ln [-f] [-s] f1 [f2]\n"
1441 		    "       ln [-f] [-s] f1 ... fn d1\n"
1442 		    "       ln [-f] -s d1 d2\n"));
1443 #else
1444 		    "Usage: ln [-f] [-n] [-s] f1 [f2]\n"
1445 		    "       ln [-f] [-n] [-s] f1 ... fn d1\n"
1446 		    "       ln [-f] [-n] -s d1 d2\n"));
1447 #endif
1448 	} else if (cpy) {
1449 		(void) fprintf(stderr, gettext(
1450 		    "Usage: cp [-f] [-i] [-p] [-@] f1 f2\n"
1451 		    "       cp [-f] [-i] [-p] [-@] f1 ... fn d1\n"
1452 		    "       cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] "
1453 		    "d1 ... dn-1 dn\n"));
1454 	}
1455 	exit(2);
1456 }
1457 
1458 /*
1459  * chg_time()
1460  *
1461  * Try to preserve modification and access time.
1462  * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
1463  * don't report a utime() failure.
1464  * If this is the XPG4 version and utime fails, if 1) pflg is set (cp -p)
1465  * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
1466  * exit status only if pflg is set.
1467  * utimes(2) is being used to achieve granularity in
1468  * microseconds while setting file times.
1469  */
1470 static int
1471 chg_time(char *to, struct stat ss)
1472 {
1473 	struct timeval times[2];
1474 	int rc;
1475 
1476 	timestruc_to_timeval(&ss.st_atim, times);
1477 	timestruc_to_timeval(&ss.st_mtim, times + 1);
1478 
1479 	rc = utimes(to, times);
1480 #ifdef XPG4
1481 	if ((pflg || mve) && rc != 0) {
1482 		(void) fprintf(stderr,
1483 		    gettext("%s: cannot set times for %s: "), cmd, to);
1484 		perror("");
1485 		if (pflg)
1486 			return (1);
1487 	}
1488 #endif
1489 
1490 	return (0);
1491 
1492 }
1493 
1494 /*
1495  * chg_mode()
1496  *
1497  * This function is called upon "cp -p" or mv across filesystems.
1498  *
1499  * Try to preserve the owner and group id.  If chown() fails,
1500  * only print a diagnostic message if doing a mv in the XPG4 version;
1501  * try to clear S_ISUID and S_ISGID bits in the target.  If unable to clear
1502  * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1503  * non-zero exit status because this is a security violation.
1504  * Try to preserve permissions.
1505  * If this is the XPG4 version and chmod() fails, print a diagnostic message
1506  * and arrange for a non-zero exit status.
1507  * If this is the Solaris version and chmod() fails, do not print a
1508  * diagnostic message or exit with a non-zero value.
1509  */
1510 static int
1511 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1512 {
1513 	int clearflg = 0; /* controls message printed upon chown() error */
1514 
1515 	if (chown(target, uid, gid) != 0) {
1516 #ifdef XPG4
1517 		if (mve) {
1518 			(void) fprintf(stderr, gettext("%s: cannot change"
1519 			    " owner and group of %s: "), cmd, target);
1520 			perror("");
1521 		}
1522 #endif
1523 		if (mode & (S_ISUID | S_ISGID)) {
1524 			/* try to clear S_ISUID and S_ISGID */
1525 			mode &= ~S_ISUID & ~S_ISGID;
1526 			++clearflg;
1527 		}
1528 	}
1529 	if (chmod(target, mode) != 0) {
1530 		if (clearflg) {
1531 			(void) fprintf(stderr, gettext(
1532 			    "%s: cannot clear S_ISUID and S_ISGID bits in"
1533 			    " %s: "), cmd, target);
1534 			perror("");
1535 			/* cp -p should get non-zero exit; mv should not */
1536 			if (pflg)
1537 				return (1);
1538 		}
1539 #ifdef XPG4
1540 		else {
1541 			(void) fprintf(stderr, gettext(
1542 			"%s: cannot set permissions for %s: "), cmd, target);
1543 			perror("");
1544 			/* cp -p should get non-zero exit; mv should not */
1545 			if (pflg)
1546 				return (1);
1547 		}
1548 #endif
1549 	}
1550 	return (0);
1551 
1552 }
1553 
1554 static void
1555 Perror(char *s)
1556 {
1557 	char buf[PATH_MAX + 10];
1558 
1559 	(void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1560 	perror(buf);
1561 }
1562 
1563 static void
1564 Perror2(char *s1, char *s2)
1565 {
1566 	char buf[PATH_MAX + 20];
1567 
1568 	(void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1569 	    cmd, gettext(s1), gettext(s2));
1570 	perror(buf);
1571 }
1572 
1573 /*
1574  * used for cp -R and for mv across file systems
1575  */
1576 static int
1577 copydir(char *source, char *target)
1578 {
1579 	int ret, attret = 0;
1580 	int pret = 0;		/* need separate flag if -p is specified */
1581 	mode_t	fixmode = (mode_t)0;	/* cleanup mode after copy */
1582 	struct stat s1save;
1583 	acl_t  *s1acl_save;
1584 
1585 	s1acl_save = NULL;
1586 
1587 	if (cpy && !rflg) {
1588 		(void) fprintf(stderr,
1589 		    gettext("%s: %s: is a directory\n"), cmd, source);
1590 		return (1);
1591 	}
1592 
1593 	if (stat(target, &s2) < 0) {
1594 		if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1595 			(void) fprintf(stderr, "%s: ", cmd);
1596 			perror(target);
1597 			return (1);
1598 		}
1599 		if (stat(target, &s2) == 0) {
1600 			fixmode = s2.st_mode;
1601 		} else {
1602 			fixmode = s1.st_mode;
1603 		}
1604 		(void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1605 	} else if (!(ISDIR(s2))) {
1606 		(void) fprintf(stderr,
1607 		    gettext("%s: %s: not a directory.\n"), cmd, target);
1608 		return (1);
1609 	}
1610 	if (pflg || mve) {
1611 		/*
1612 		 * Save s1 (stat information for source dir) and acl info,
1613 		 * if any, so that ownership, modes, times, and acl's can
1614 		 * be reserved during "cp -p" or mv.
1615 		 * s1 gets overwritten when doing the recursive copy.
1616 		 */
1617 		s1save = s1;
1618 		if (s1acl != NULL) {
1619 			s1acl_save = acl_dup(s1acl);
1620 #ifdef XPG4
1621 			if (s1acl_save == NULL) {
1622 				(void) fprintf(stderr, gettext("%s: "
1623 				    "Insufficient memory to save acl"
1624 				    " entry\n"), cmd);
1625 				if (pflg)
1626 					return (1);
1627 
1628 			}
1629 #endif
1630 		}
1631 	}
1632 
1633 	ret = rcopy(source, target);
1634 
1635 	/*
1636 	 * Once we created a directory, go ahead and set
1637 	 * its attributes, e.g. acls and time. The info
1638 	 * may get overwritten if we continue traversing
1639 	 * down the tree.
1640 	 *
1641 	 * ACL for directory
1642 	 */
1643 	if (pflg || mve) {
1644 		if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1645 		    FMODE(s1save))) == 0)
1646 			pret = chg_time(target, s1save);
1647 		ret += pret;
1648 		if (s1acl_save != NULL) {
1649 			if (acl_set(target, s1acl_save) < 0) {
1650 #ifdef XPG4
1651 				if (pflg || mve) {
1652 #else
1653 				if (pflg) {
1654 #endif
1655 					(void) fprintf(stderr, gettext(
1656 					    "%s: failed to set acl entries "
1657 					    "on %s\n"), cmd, target);
1658 					if (pflg) {
1659 						acl_free(s1acl_save);
1660 						s1acl_save = NULL;
1661 						ret++;
1662 					}
1663 				}
1664 				/* else: silent and continue */
1665 			}
1666 			acl_free(s1acl_save);
1667 			s1acl_save = NULL;
1668 		}
1669 	} else if (fixmode != (mode_t)0)
1670 		(void) chmod(target, fixmode & MODEBITS);
1671 
1672 	if (pflg || atflg || mve) {
1673 		attret = copyattributes(source, target);
1674 		if (!attrsilent && attret != 0) {
1675 			(void) fprintf(stderr, gettext("%s: Failed to preserve"
1676 			    " extended attributes of directory"
1677 			    " %s\n"), cmd, source);
1678 		} else {
1679 			/*
1680 			 * Otherwise ignore failure.
1681 			 */
1682 			attret = 0;
1683 		}
1684 	}
1685 	if (attret != 0)
1686 		return (attret);
1687 	return (ret);
1688 }
1689 
1690 static int
1691 copyspecial(char *target)
1692 {
1693 	int ret = 0;
1694 
1695 	if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1696 		(void) fprintf(stderr, gettext(
1697 		    "cp: cannot create special file %s: "), target);
1698 		perror("");
1699 		return (1);
1700 	}
1701 
1702 	if (pflg) {
1703 		if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1704 			ret = chg_time(target, s1);
1705 	}
1706 
1707 	return (ret);
1708 }
1709 
1710 static int
1711 use_stdin(void)
1712 {
1713 #ifdef XPG4
1714 	return (1);
1715 #else
1716 	return (isatty(fileno(stdin)));
1717 #endif
1718 }
1719 
1720 static int
1721 copyattributes(char *source, char *target)
1722 {
1723 	int sourcedirfd, targetdirfd;
1724 	int srcfd, targfd;
1725 	int tmpfd;
1726 	DIR *srcdirp;
1727 	int srcattrfd, targattrfd;
1728 	struct dirent *dp;
1729 	char *attrstr;
1730 	char *srcbuf, *targbuf;
1731 	size_t src_size, targ_size;
1732 	int error = 0;
1733 	int aclerror;
1734 	mode_t mode;
1735 	int clearflg = 0;
1736 	acl_t *xacl = NULL;
1737 	acl_t *attrdiracl = NULL;
1738 	struct stat attrdir, s3, s4;
1739 	struct timeval times[2];
1740 	mode_t	targmode;
1741 
1742 	srcdirp = NULL;
1743 	srcfd = targfd = tmpfd = -1;
1744 	sourcedirfd = targetdirfd = srcattrfd = targattrfd = -1;
1745 	srcbuf = targbuf = NULL;
1746 
1747 	if (pathconf(source, _PC_XATTR_EXISTS) != 1)
1748 		return (0);
1749 
1750 	if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1751 		if (!attrsilent) {
1752 			(void) fprintf(stderr,
1753 			    gettext(
1754 			    "%s: cannot preserve extended attributes, "
1755 			    "operation not supported on file"
1756 			    " %s\n"), cmd, target);
1757 		}
1758 		return (1);
1759 	}
1760 
1761 
1762 	if ((srcfd = open(source, O_RDONLY)) == -1) {
1763 		if (pflg && attrsilent) {
1764 			error = 0;
1765 			goto out;
1766 		}
1767 		if (!attrsilent) {
1768 			(void) fprintf(stderr,
1769 			    gettext("%s: cannot open file"
1770 			    " %s: "), cmd, source);
1771 			perror("");
1772 		}
1773 		++error;
1774 		goto out;
1775 	}
1776 	if ((targfd = open(target, O_RDONLY)) == -1) {
1777 
1778 		if (pflg && attrsilent) {
1779 			error = 0;
1780 			goto out;
1781 		}
1782 		if (!attrsilent) {
1783 			(void) fprintf(stderr,
1784 			    gettext("%s: cannot open file"
1785 			    " %s: "), cmd, source);
1786 			perror("");
1787 		}
1788 		++error;
1789 		goto out;
1790 	}
1791 
1792 	if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
1793 		if (pflg && attrsilent) {
1794 			error = 0;
1795 			goto out;
1796 		}
1797 		if (!attrsilent) {
1798 			(void) fprintf(stderr,
1799 			    gettext("%s: cannot open attribute"
1800 			    " directory for %s: "), cmd, source);
1801 			perror("");
1802 			++error;
1803 		}
1804 		goto out;
1805 	}
1806 
1807 	if (fstat(sourcedirfd, &attrdir) == -1) {
1808 		if (pflg && attrsilent) {
1809 			error = 0;
1810 			goto out;
1811 		}
1812 
1813 		if (!attrsilent) {
1814 			(void) fprintf(stderr,
1815 				gettext("%s: could not retrieve stat"
1816 					" information for attribute directory"
1817 					"of file %s: "), cmd, source);
1818 			perror("");
1819 			++error;
1820 		}
1821 		goto out;
1822 	}
1823 	if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
1824 		/*
1825 		 * We couldn't create the attribute directory
1826 		 *
1827 		 * Lets see if we can add write support to the mode
1828 		 * and create the directory and then put the mode back
1829 		 * to way it should be.
1830 		 */
1831 
1832 		targmode = FMODE(s1) | S_IWUSR;
1833 		if (fchmod(targfd, targmode) == 0) {
1834 			targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR);
1835 			/*
1836 			 * Put mode back to what it was
1837 			 */
1838 			targmode = FMODE(s1) & MODEBITS;
1839 			if (fchmod(targfd, targmode) == -1) {
1840 				if (pflg && attrsilent) {
1841 					error = 0;
1842 					goto out;
1843 				}
1844 				if (!attrsilent) {
1845 					(void) fprintf(stderr,
1846 					    gettext("%s: failed to set"
1847 					    " mode correctly on file"
1848 					    " %s: "), cmd, target);
1849 					perror("");
1850 					++error;
1851 					goto out;
1852 				}
1853 			}
1854 		} else {
1855 			if (pflg && attrsilent) {
1856 				error = 0;
1857 				goto out;
1858 			}
1859 			if (!attrsilent) {
1860 				(void) fprintf(stderr,
1861 				    gettext("%s: cannot open attribute"
1862 				    " directory for %s: "), cmd, target);
1863 				perror("");
1864 				++error;
1865 			}
1866 			goto out;
1867 		}
1868 	}
1869 
1870 	if (targetdirfd == -1) {
1871 		if (pflg && attrsilent) {
1872 			error = 0;
1873 			goto out;
1874 		}
1875 		if (!attrsilent) {
1876 			(void) fprintf(stderr,
1877 			    gettext("%s: cannot open attribute directory"
1878 			    " for %s: "), cmd, target);
1879 			perror("");
1880 			++error;
1881 		}
1882 		goto out;
1883 	}
1884 
1885 	/*
1886 	 * Set mode of attribute directory same as on source,
1887 	 * if pflg set or this is a move.
1888 	 */
1889 
1890 	if (pflg || mve) {
1891 		if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1892 			if (!attrsilent) {
1893 				(void) fprintf(stderr,
1894 					gettext("%s: failed to set file mode"
1895 					" correctly on attribute directory of"
1896 					" file %s: "), cmd, target);
1897 				perror("");
1898 				++error;
1899 			}
1900 		}
1901 
1902 		if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1903 			if (!attrsilent) {
1904 				(void) fprintf(stderr,
1905 				    gettext("%s: failed to set file"
1906 				    " ownership correctly on attribute"
1907 				    " directory of file %s: "), cmd, target);
1908 				perror("");
1909 				++error;
1910 			}
1911 		}
1912 		/*
1913 		 * Now that we are the owner we can update st_ctime by calling
1914 		 * futimesat.
1915 		 */
1916 		times[0].tv_sec = attrdir.st_atime;
1917 		times[0].tv_usec = 0;
1918 		times[1].tv_sec = attrdir.st_mtime;
1919 		times[1].tv_usec = 0;
1920 		if (futimesat(targetdirfd, ".", times) < 0) {
1921 			if (!attrsilent) {
1922 				(void) fprintf(stderr,
1923 					gettext("%s: cannot set attribute times"
1924 					" for %s: "), cmd, target);
1925 				perror("");
1926 				++error;
1927 			}
1928 		}
1929 
1930 		/*
1931 		 * Now set owner and group of attribute directory, implies
1932 		 * changing the ACL of the hidden attribute directory first.
1933 		 */
1934 		if ((aclerror = facl_get(sourcedirfd,
1935 		    ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1936 			if (!attrsilent) {
1937 				(void) fprintf(stderr, gettext(
1938 				    "%s: failed to get acl entries of"
1939 				    " attribute directory for"
1940 				    " %s : %s\n"), cmd,
1941 				    source, acl_strerror(aclerror));
1942 				++error;
1943 			}
1944 		}
1945 
1946 		if (attrdiracl) {
1947 			if (facl_set(targetdirfd, attrdiracl) != 0) {
1948 				if (!attrsilent) {
1949 					(void) fprintf(stderr, gettext(
1950 					"%s: failed to set acl entries"
1951 					" on attribute directory "
1952 					"for %s\n"), cmd, target);
1953 					++error;
1954 				}
1955 				acl_free(attrdiracl);
1956 				attrdiracl = NULL;
1957 			}
1958 		}
1959 	}
1960 
1961 	/*
1962 	 * dup sourcedirfd for use by fdopendir().
1963 	 * fdopendir will take ownership of given fd and will close
1964 	 * it when closedir() is called.
1965 	 */
1966 
1967 	if ((tmpfd = dup(sourcedirfd)) == -1) {
1968 		if (pflg && attrsilent) {
1969 			error = 0;
1970 			goto out;
1971 		}
1972 		if (!attrsilent) {
1973 			(void) fprintf(stderr,
1974 			    gettext(
1975 			    "%s: unable to dup attribute directory"
1976 			    " file descriptor for %s: "), cmd, source);
1977 			perror("");
1978 			++error;
1979 		}
1980 		goto out;
1981 	}
1982 	if ((srcdirp = fdopendir(tmpfd)) == NULL) {
1983 		if (pflg && attrsilent) {
1984 			error = 0;
1985 			goto out;
1986 		}
1987 		if (!attrsilent) {
1988 			(void) fprintf(stderr,
1989 			    gettext("%s: failed to open attribute"
1990 			    " directory for %s: "), cmd, source);
1991 			perror("");
1992 			++error;
1993 		}
1994 		goto out;
1995 	}
1996 
1997 	while (dp = readdir(srcdirp)) {
1998 		if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
1999 			(dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2000 			dp->d_name[2] == '\0'))
2001 			continue;
2002 
2003 		if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2004 		    O_RDONLY)) == -1) {
2005 			if (!attrsilent) {
2006 				(void) fprintf(stderr,
2007 				    gettext("%s: cannot open attribute %s on"
2008 				    " file %s: "), cmd, dp->d_name, source);
2009 				perror("");
2010 				++error;
2011 				goto next;
2012 			}
2013 		}
2014 
2015 		if (fstat(srcattrfd, &s3) < 0) {
2016 			if (!attrsilent) {
2017 				(void) fprintf(stderr,
2018 				    gettext("%s: could not stat attribute"
2019 				    " %s on file"
2020 				    " %s: "), cmd, dp->d_name, source);
2021 				perror("");
2022 				++error;
2023 			}
2024 			goto next;
2025 		}
2026 
2027 		if (pflg || mve) {
2028 			if ((aclerror = facl_get(srcattrfd,
2029 			    ACL_NO_TRIVIAL, &xacl)) != 0) {
2030 				if (!attrsilent) {
2031 					(void) fprintf(stderr, gettext(
2032 					    "%s: failed to get acl entries of"
2033 					    " attribute %s for"
2034 					    " %s: %s"), cmd, dp->d_name,
2035 					    source, acl_strerror(aclerror));
2036 					++error;
2037 				}
2038 			}
2039 		}
2040 
2041 		(void) unlinkat(targetdirfd, dp->d_name, 0);
2042 		if ((targattrfd = openat(targetdirfd, dp->d_name,
2043 		    O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2044 			if (!attrsilent) {
2045 				(void) fprintf(stderr,
2046 				    gettext("%s: could not create attribute"
2047 				    " %s on file"
2048 				    " %s: "), cmd, dp->d_name, target);
2049 				perror("");
2050 				++error;
2051 			}
2052 			goto next;
2053 		}
2054 
2055 		/*
2056 		 * preserve ACL
2057 		 */
2058 		if ((pflg || mve) && xacl != NULL) {
2059 			if ((facl_set(targattrfd, xacl)) < 0) {
2060 				if (!attrsilent) {
2061 					(void) fprintf(stderr, gettext(
2062 					    "%s: failed to set acl entries on"
2063 					    " attribute %s for"
2064 					    "%s\n"), cmd, dp->d_name, target);
2065 					++error;
2066 				}
2067 				acl_free(xacl);
2068 				xacl = NULL;
2069 			}
2070 		}
2071 
2072 		if (fstat(targattrfd, &s4) < 0) {
2073 			if (!attrsilent) {
2074 				(void) fprintf(stderr,
2075 				    gettext("%s: could not stat attribute"
2076 				    " %s on file"
2077 				    " %s: "), cmd, dp->d_name, source);
2078 				perror("");
2079 				++error;
2080 			}
2081 			goto next;
2082 		}
2083 
2084 /*
2085  * setup path string to be passed to writefile
2086  *
2087  * We need to include attribute in the string so that
2088  * a useful error message can be printed in the case of a failure.
2089  */
2090 		attrstr = gettext(" attribute ");
2091 		src_size = strlen(source) +
2092 		    strlen(dp->d_name) + strlen(attrstr) + 1;
2093 		srcbuf = malloc(src_size);
2094 
2095 		if (srcbuf == NULL) {
2096 			if (!attrsilent) {
2097 			(void) fprintf(stderr,
2098 				gettext("%s: could not allocate memory"
2099 					" for path buffer: "), cmd);
2100 				perror("");
2101 				++error;
2102 			}
2103 			goto next;
2104 		}
2105 		targ_size = strlen(target) +
2106 		    strlen(dp->d_name) + strlen(attrstr) + 1;
2107 		targbuf = malloc(targ_size);
2108 		if (targbuf == NULL) {
2109 			if (!attrsilent) {
2110 				(void) fprintf(stderr,
2111 					gettext("%s: could not allocate memory"
2112 						" for path buffer: "), cmd);
2113 				perror("");
2114 				++error;
2115 			}
2116 			goto next;
2117 		}
2118 
2119 		(void) snprintf(srcbuf, src_size, "%s%s%s",
2120 		    source, attrstr, dp->d_name);
2121 		(void) snprintf(targbuf, targ_size, "%s%s%s",
2122 		    target, attrstr, dp->d_name);
2123 
2124 		if (writefile(srcattrfd, targattrfd,
2125 		    srcbuf, targbuf, &s3, &s4) != 0) {
2126 			if (!attrsilent) {
2127 				++error;
2128 			}
2129 			goto next;
2130 		}
2131 
2132 		if (pflg || mve) {
2133 			mode = FMODE(s3);
2134 
2135 			if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
2136 				if (!attrsilent) {
2137 					(void) fprintf(stderr,
2138 					    gettext("%s: cannot change"
2139 					    " owner and group of"
2140 					    " attribute %s for" " file"
2141 					    " %s: "), cmd, dp->d_name, target);
2142 					perror("");
2143 					++error;
2144 				}
2145 				if (mode & (S_ISUID | S_ISGID)) {
2146 					/* try to clear S_ISUID and S_ISGID */
2147 					mode &= ~S_ISUID & ~S_ISGID;
2148 					++clearflg;
2149 				}
2150 			}
2151 			/* tv_usec were cleared above */
2152 			times[0].tv_sec = s3.st_atime;
2153 			times[1].tv_sec = s3.st_mtime;
2154 			if (futimesat(targetdirfd, dp->d_name, times) < 0) {
2155 				if (!attrsilent) {
2156 					(void) fprintf(stderr,
2157 					    gettext("%s: cannot set attribute"
2158 					    " times for %s: "), cmd, target);
2159 					perror("");
2160 					++error;
2161 				}
2162 			}
2163 			if (fchmod(targattrfd, mode) != 0) {
2164 				if (clearflg) {
2165 					(void) fprintf(stderr, gettext(
2166 					    "%s: cannot clear S_ISUID and "
2167 					    "S_ISGID bits in attribute %s"
2168 					    " for file"
2169 					    " %s: "), cmd, dp->d_name, target);
2170 				} else {
2171 					if (!attrsilent) {
2172 						(void) fprintf(stderr,
2173 							gettext(
2174 				"%s: cannot set permissions of attribute"
2175 				" %s for %s: "), cmd, dp->d_name, target);
2176 						perror("");
2177 						++error;
2178 					}
2179 				}
2180 			}
2181 			if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
2182 				if (!attrsilent) {
2183 					(void) fprintf(stderr, gettext(
2184 					    "%s: failed to set acl entries on"
2185 					    " attribute %s for"
2186 					    "%s\n"), cmd, dp->d_name, target);
2187 					++error;
2188 				}
2189 				acl_free(xacl);
2190 				xacl = NULL;
2191 			}
2192 		}
2193 next:
2194 		if (xacl != NULL) {
2195 			acl_free(xacl);
2196 			xacl = NULL;
2197 		}
2198 		if (srcbuf != NULL)
2199 			free(srcbuf);
2200 		if (targbuf != NULL)
2201 			free(targbuf);
2202 		if (srcattrfd != -1)
2203 			(void) close(srcattrfd);
2204 		if (targattrfd != -1)
2205 			(void) close(targattrfd);
2206 		srcattrfd = targattrfd = -1;
2207 		srcbuf = targbuf = NULL;
2208 	}
2209 out:
2210 	if (xacl != NULL) {
2211 		acl_free(xacl);
2212 		xacl = NULL;
2213 	}
2214 	if (attrdiracl != NULL) {
2215 		acl_free(attrdiracl);
2216 		attrdiracl = NULL;
2217 	}
2218 	if (srcbuf)
2219 		free(srcbuf);
2220 	if (targbuf)
2221 		free(targbuf);
2222 	if (sourcedirfd != -1)
2223 		(void) close(sourcedirfd);
2224 	if (targetdirfd != -1)
2225 		(void) close(targetdirfd);
2226 	if (srcdirp != NULL)
2227 		(void) closedir(srcdirp);
2228 	if (srcfd != -1)
2229 		(void) close(srcfd);
2230 	if (targfd != -1)
2231 		(void) close(targfd);
2232 	return (error == 0 ? 0 : 1);
2233 }
2234 
2235 /*
2236  * nanoseconds are rounded off to microseconds by flooring.
2237  */
2238 static void
2239 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
2240 {
2241 	tv->tv_sec = ts->tv_sec;
2242 	tv->tv_usec = ts->tv_nsec / 1000;
2243 }
2244