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