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