8672 proc_t changes broke genunix dmods and walker
[unleashed.git] / usr / src / cmd / mv / mv.c
blobc82887a807d9e366313e42c302b52fc455f43a0a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
35 * University Copyright- Copyright (c) 1982, 1986, 1988
36 * The Regents of the University of California
37 * All Rights Reserved
39 * University Acknowledgment- Portions of this document are derived from
40 * software developed by the University of California, Berkeley, and its
41 * contributors.
45 * Combined mv/cp/ln command:
46 * mv file1 file2
47 * mv dir1 dir2
48 * mv file1 ... filen dir1
50 #include <sys/time.h>
51 #include <signal.h>
52 #include <locale.h>
53 #include <stdarg.h>
54 #include <sys/acl.h>
55 #include <libcmdutils.h>
56 #include <aclutils.h>
57 #include "getresponse.h"
59 #define FTYPE(A) (A.st_mode)
60 #define FMODE(A) (A.st_mode)
61 #define UID(A) (A.st_uid)
62 #define GID(A) (A.st_gid)
63 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
64 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
65 #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR)
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 #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK)
73 #define DELIM '/'
74 #define EQ(x, y) (strcmp(x, y) == 0)
75 #define FALSE 0
76 #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
77 #define TRUE 1
79 static char *dname(char *);
80 static int lnkfil(char *, char *);
81 static int cpymve(char *, char *);
82 static int chkfiles(char *, char **);
83 static int rcopy(char *, char *);
84 static int chk_different(char *, char *);
85 static int chg_time(char *, struct stat);
86 static int chg_mode(char *, uid_t, gid_t, mode_t);
87 static int copydir(char *, char *);
88 static int copyspecial(char *);
89 static int getrealpath(char *, char *);
90 static void usage(void);
91 static void Perror(char *);
92 static void Perror2(char *, char *);
93 static int use_stdin(void);
94 static int copyattributes(char *, char *);
95 static int copy_sysattr(char *, char *);
96 static tree_node_t *create_tnode(dev_t, ino_t);
98 static struct stat s1, s2, s3, s4;
99 static int cpy = FALSE;
100 static int mve = FALSE;
101 static int lnk = FALSE;
102 static char *cmd;
103 static int silent = 0;
104 static int fflg = 0;
105 static int iflg = 0;
106 static int pflg = 0;
107 static int Rflg = 0; /* recursive copy */
108 static int rflg = 0; /* recursive copy */
109 static int sflg = 0;
110 static int Hflg = 0; /* follow cmd line arg symlink to dir */
111 static int Lflg = 0; /* follow symlinks */
112 static int Pflg = 0; /* do not follow symlinks */
113 static int atflg = 0;
114 static int attrsilent = 0;
115 static int targetexists = 0;
116 static int cmdarg; /* command line argument */
117 static avl_tree_t *stree = NULL; /* source file inode search tree */
118 static acl_t *s1acl;
119 static int saflg = 0; /* 'cp' extended system attr. */
120 static int srcfd = -1;
121 static int targfd = -1;
122 static int sourcedirfd = -1;
123 static int targetdirfd = -1;
124 static DIR *srcdirp = NULL;
125 static int srcattrfd = -1;
126 static int targattrfd = -1;
127 static struct stat attrdir;
129 /* Extended system attributes support */
131 static int open_source(char *);
132 static int open_target_srctarg_attrdirs(char *, char *);
133 static int open_attrdirp(char *);
134 static int traverse_attrfile(struct dirent *, char *, char *, int);
135 static void rewind_attrdir(DIR *);
136 static void close_all();
140 main(int argc, char *argv[])
142 int c, i, r, errflg = 0;
143 char target[PATH_MAX];
144 int (*move)(char *, char *);
147 * Determine command invoked (mv, cp, or ln)
150 if (cmd = strrchr(argv[0], '/'))
151 ++cmd;
152 else
153 cmd = argv[0];
156 * Set flags based on command.
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);
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);
184 * Check for options:
185 * cp [ -r|-R [-H|-L|-P]] [-afip@/] file1 [file2 ...] target
186 * cp [-afiprR@/] 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
193 if (cpy) {
194 while ((c = getopt(argc, argv, "afHiLpPrR@/")) != 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 saflg = 0;
208 #else
209 if (atflg == 0)
210 attrsilent = 1;
211 #endif
212 break;
213 case 'H':
215 * If more than one of -H, -L, or -P are
216 * specified, only the last option specified
217 * determines the behavior.
219 Lflg = Pflg = 0;
220 Hflg++;
221 break;
222 case 'L':
223 Hflg = Pflg = 0;
224 Lflg++;
225 break;
226 case 'P':
227 Lflg = Hflg = 0;
228 Pflg++;
229 break;
230 case 'R':
232 * The default behavior of cp -R|-r
233 * when specified without -H|-L|-P
234 * is -L.
236 Rflg++;
237 /*FALLTHROUGH*/
238 case 'r':
239 rflg++;
240 break;
241 case 'a':
242 Lflg = Hflg = 0;
243 pflg++;
244 Pflg++;
245 Rflg++;
246 rflg++;
247 break;
248 case '@':
249 atflg++;
250 attrsilent = 0;
251 #ifdef XPG4
252 pflg = 0;
253 #endif
254 break;
255 case '/':
256 saflg++;
257 attrsilent = 0;
258 #ifdef XPG4
259 pflg = 0;
260 #endif
261 break;
262 default:
263 errflg++;
266 /* -R or -r must be specified with -H, -L, or -P */
267 if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
268 errflg++;
271 } else if (mve) {
272 while ((c = getopt(argc, argv, "fis")) != EOF)
273 switch (c) {
274 case 'f':
275 silent++;
276 #ifdef XPG4
277 iflg = 0;
278 #endif
279 break;
280 case 'i':
281 iflg++;
282 #ifdef XPG4
283 silent = 0;
284 #endif
285 break;
286 default:
287 errflg++;
289 } else { /* ln */
290 while ((c = getopt(argc, argv, "fns")) != EOF)
291 switch (c) {
292 case 'f':
293 silent++;
294 break;
295 case 'n':
296 /* silently ignored; this is the default */
297 break;
298 case 's':
299 sflg++;
300 break;
301 default:
302 errflg++;
307 * For BSD compatibility allow - to delimit the end of
308 * options for mv.
310 if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
311 optind++;
314 * Check for sufficient arguments
315 * or a usage error.
318 argc -= optind;
319 argv = &argv[optind];
321 if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
322 (void) fprintf(stderr,
323 gettext("%s: Insufficient arguments (%d)\n"),
324 cmd, argc);
325 usage();
328 if (errflg != 0)
329 usage();
332 * If there is more than a source and target,
333 * the last argument (the target) must be a directory
334 * which really exists.
337 if (argc > 2) {
338 if (stat(argv[argc-1], &s2) < 0) {
339 (void) fprintf(stderr,
340 gettext("%s: %s not found\n"),
341 cmd, argv[argc-1]);
342 exit(2);
345 if (!ISDIR(s2)) {
346 (void) fprintf(stderr,
347 gettext("%s: Target %s must be a directory\n"),
348 cmd, argv[argc-1]);
349 usage();
353 if (strlen(argv[argc-1]) >= PATH_MAX) {
354 (void) fprintf(stderr,
355 gettext("%s: Target %s file name length exceeds PATH_MAX"
356 " %d\n"), cmd, argv[argc-1], PATH_MAX);
357 exit(78);
360 if (argc == 1) {
361 if (!lnk)
362 usage();
363 (void) strcpy(target, ".");
364 } else {
365 (void) strcpy(target, argv[--argc]);
369 * Perform a multiple argument mv|cp|ln by
370 * multiple invocations of cpymve() or lnkfil().
372 if (lnk)
373 move = lnkfil;
374 else
375 move = cpymve;
377 r = 0;
378 for (i = 0; i < argc; i++) {
379 stree = NULL;
380 cmdarg = 1;
381 r += move(argv[i], target);
385 * Show errors by nonzero exit code.
388 return (r?2:0);
391 static int
392 lnkfil(char *source, char *target)
394 char *buf = NULL;
396 if (sflg) {
399 * If target is a directory make complete
400 * name of the new symbolic link within that
401 * directory.
404 if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
405 size_t len;
407 len = strlen(target) + strlen(dname(source)) + 4;
408 if ((buf = (char *)malloc(len)) == NULL) {
409 (void) fprintf(stderr,
410 gettext("%s: Insufficient memory "
411 "to %s %s\n"), cmd, cmd, source);
412 exit(3);
414 (void) snprintf(buf, len, "%s/%s",
415 target, dname(source));
416 target = buf;
420 * Check to see if the file exists already.
421 * In this case we use lstat() instead of stat():
422 * unlink(2) and symlink(2) will operate on the file
423 * itself, not its reference, if the file is a symlink.
426 if ((lstat(target, &s2) == 0)) {
428 * Check if the silent flag is set ie. the -f option
429 * is used. If so, use unlink to remove the current
430 * target to replace with the new target, specified
431 * on the command line. Proceed with symlink.
433 if (silent) {
435 * Don't allow silent (-f) removal of an existing
436 * directory; could leave unreferenced directory
437 * entries.
439 if (ISDIR(s2)) {
440 (void) fprintf(stderr,
441 gettext("%s: cannot create link "
442 "over directory %s\n"), cmd,
443 target);
444 return (1);
446 if (unlink(target) < 0) {
447 (void) fprintf(stderr,
448 gettext("%s: cannot unlink %s: "),
449 cmd, target);
450 perror("");
451 return (1);
458 * Create a symbolic link to the source.
461 if (symlink(source, target) < 0) {
462 (void) fprintf(stderr,
463 gettext("%s: cannot create %s: "),
464 cmd, target);
465 perror("");
466 if (buf != NULL)
467 free(buf);
468 return (1);
470 if (buf != NULL)
471 free(buf);
472 return (0);
475 switch (chkfiles(source, &target)) {
476 case 1: return (1);
477 case 2: return (0);
478 /* default - fall through */
482 * Make sure source file is not a directory,
483 * we cannot link directories...
486 if (ISDIR(s1)) {
487 (void) fprintf(stderr,
488 gettext("%s: %s is a directory\n"), cmd, source);
489 return (1);
493 * hard link, call link() and return.
496 if (link(source, target) < 0) {
497 if (errno == EXDEV)
498 (void) fprintf(stderr,
499 gettext("%s: %s is on a different file system\n"),
500 cmd, target);
501 else {
502 (void) fprintf(stderr,
503 gettext("%s: cannot create link %s: "),
504 cmd, target);
505 perror("");
507 if (buf != NULL)
508 free(buf);
509 return (1);
510 } else {
511 if (buf != NULL)
512 free(buf);
513 return (0);
517 static int
518 cpymve(char *source, char *target)
520 int n;
521 int fi, fo;
522 int ret = 0;
523 int attret = 0;
524 int sattret = 0;
525 int errno_save;
526 int error = 0;
528 switch (chkfiles(source, &target)) {
529 case 1: return (1);
530 case 2: return (0);
531 /* default - fall through */
535 * If it's a recursive copy and source
536 * is a directory, then call rcopy (from copydir).
538 if (cpy) {
539 if (ISDIR(s1)) {
540 int rc;
541 avl_index_t where = 0;
542 tree_node_t *tnode;
543 tree_node_t *tptr;
544 dev_t save_dev = s1.st_dev;
545 ino_t save_ino = s1.st_ino;
548 * We will be recursing into the directory so
549 * save the inode information to a search tree
550 * to avoid getting into an endless loop.
552 if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
553 if (rc == 0) {
555 * We've already visited this directory.
556 * Don't remove the search tree entry
557 * to make sure we don't get into an
558 * endless loop if revisited from a
559 * different part of the hierarchy.
561 (void) fprintf(stderr, gettext(
562 "%s: cycle detected: %s\n"),
563 cmd, source);
564 } else {
565 Perror(source);
567 return (1);
570 cmdarg = 0;
571 rc = copydir(source, target);
574 * Create a tnode to get an index to the matching
575 * node (same dev and inode) in the search tree,
576 * then use the index to remove the matching node
577 * so it we do not wrongly detect a cycle when
578 * revisiting this directory from another part of
579 * the hierarchy.
581 if ((tnode = create_tnode(save_dev,
582 save_ino)) == NULL) {
583 Perror(source);
584 return (1);
586 if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
587 avl_remove(stree, tptr);
589 free(tptr);
590 free(tnode);
591 return (rc);
593 } else if (ISDEV(s1) && Rflg) {
594 return (copyspecial(target));
595 } else {
596 goto copy;
600 if (mve) {
601 if (rename(source, target) >= 0)
602 return (0);
603 if (errno != EXDEV) {
604 if (errno == ENOTDIR && ISDIR(s1)) {
605 (void) fprintf(stderr,
606 gettext("%s: %s is a directory\n"),
607 cmd, source);
608 return (1);
610 (void) fprintf(stderr,
611 gettext("%s: cannot rename %s to %s: "),
612 cmd, source, target);
613 perror("");
614 return (1);
618 * cannot move a non-directory (source) onto an existing
619 * directory (target)
622 if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
623 (void) fprintf(stderr,
624 gettext("%s: cannot mv a non directory %s "
625 "over existing directory"
626 " %s \n"), cmd, source, target);
627 return (1);
629 if (ISDIR(s1)) {
630 #ifdef XPG4
631 if (targetexists && ISDIR(s2)) {
632 /* existing target dir must be empty */
633 if (rmdir(target) < 0) {
634 errno_save = errno;
635 (void) fprintf(stderr,
636 gettext("%s: cannot rmdir %s: "),
637 cmd, target);
638 errno = errno_save;
639 perror("");
640 return (1);
643 #endif
644 if ((n = copydir(source, target)) == 0)
645 (void) rmdir(source);
646 return (n);
649 /* doors cannot be moved across filesystems */
650 if (ISDOOR(s1)) {
651 (void) fprintf(stderr,
652 gettext("%s: %s: cannot move door "
653 "across file systems\n"), cmd, source);
654 return (1);
657 /* sockets cannot be moved across filesystems */
658 if (ISSOCK(s1)) {
659 (void) fprintf(stderr,
660 gettext("%s: %s: cannot move socket "
661 "across file systems\n"), cmd, source);
662 return (1);
666 * File cannot be renamed, try to recreate the symbolic
667 * link or special device, or copy the file wholesale
668 * between file systems.
670 if (ISLNK(s1)) {
671 register int m;
672 register mode_t md;
673 char symln[PATH_MAX + 1];
675 if (targetexists && unlink(target) < 0) {
676 (void) fprintf(stderr,
677 gettext("%s: cannot unlink %s: "),
678 cmd, target);
679 perror("");
680 return (1);
683 if ((m = readlink(source, symln,
684 sizeof (symln) - 1)) < 0) {
685 Perror(source);
686 return (1);
688 symln[m] = '\0';
690 md = umask(~(s1.st_mode & MODEBITS));
691 if (symlink(symln, target) < 0) {
692 Perror(target);
693 return (1);
695 (void) umask(md);
696 m = lchown(target, UID(s1), GID(s1));
697 #ifdef XPG4
698 if (m < 0) {
699 (void) fprintf(stderr, gettext("%s: cannot"
700 " change owner and group of"
701 " %s: "), cmd, target);
702 perror("");
704 #endif
705 goto cleanup;
707 if (ISDEV(s1)) {
709 if (targetexists && unlink(target) < 0) {
710 (void) fprintf(stderr,
711 gettext("%s: cannot unlink %s: "),
712 cmd, target);
713 perror("");
714 return (1);
717 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
718 Perror(target);
719 return (1);
722 (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
723 (void) chg_time(target, s1);
724 goto cleanup;
727 if (ISREG(s1)) {
728 if (ISDIR(s2)) {
729 if (targetexists && rmdir(target) < 0) {
730 (void) fprintf(stderr,
731 gettext("%s: cannot rmdir %s: "),
732 cmd, target);
733 perror("");
734 return (1);
736 } else {
737 if (targetexists && unlink(target) < 0) {
738 (void) fprintf(stderr,
739 gettext("%s: cannot unlink %s: "),
740 cmd, target);
741 perror("");
742 return (1);
747 copy:
749 * If the source file is a symlink, and either
750 * -P or -H flag (only if -H is specified and the
751 * source file is not a command line argument)
752 * were specified, then action is taken on the symlink
753 * itself, not the file referenced by the symlink.
754 * Note: this is executed for 'cp' only.
756 if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
757 int m;
758 mode_t md;
759 char symln[PATH_MAX + 1];
761 m = readlink(source, symln, sizeof (symln) - 1);
763 if (m < 0) {
764 Perror(source);
765 return (1);
767 symln[m] = '\0';
770 * Copy the sym link to the target.
771 * Note: If the target exists, write a
772 * diagnostic message, do nothing more
773 * with the source file, and return to
774 * process any remaining files.
776 md = umask(~(s1.st_mode & MODEBITS));
777 if (symlink(symln, target) < 0) {
778 Perror(target);
779 return (1);
781 (void) umask(md);
782 m = lchown(target, UID(s1), GID(s1));
784 if (m < 0) {
785 (void) fprintf(stderr, gettext(
786 "cp: cannot change owner and "
787 "group of %s:"), target);
788 perror("");
790 } else {
792 * Copy the file. If it happens to be a
793 * symlink, copy the file referenced
794 * by the symlink.
796 fi = open(source, O_RDONLY);
797 if (fi < 0) {
798 (void) fprintf(stderr,
799 gettext("%s: cannot open %s: "),
800 cmd, source);
801 perror("");
802 return (1);
805 fo = creat(target, s1.st_mode & MODEBITS);
806 if (fo < 0) {
808 * If -f and creat() failed, unlink
809 * and try again.
811 if (fflg) {
812 (void) unlink(target);
813 fo = creat(target,
814 s1.st_mode & MODEBITS);
817 if (fo < 0) {
818 (void) fprintf(stderr,
819 gettext("%s: cannot create %s: "),
820 cmd, target);
821 perror("");
822 (void) close(fi);
823 return (1);
824 } else {
825 /* stat the new file, its used below */
826 (void) stat(target, &s2);
830 * Set target's permissions to the source
831 * before any copying so that any partially
832 * copied file will have the source's
833 * permissions (at most) or umask permissions
834 * whichever is the most restrictive.
836 * ACL for regular files
839 if (pflg || mve) {
840 (void) chmod(target, FMODE(s1));
841 if (s1acl != NULL) {
842 if ((acl_set(target,
843 s1acl)) < 0) {
844 error++;
845 (void) fprintf(stderr,
846 gettext("%s: "
847 "Failed to set "
848 "acl entries "
849 "on %s\n"), cmd,
850 target);
851 acl_free(s1acl);
852 s1acl = NULL;
854 * else: silent and
855 * continue
861 if (fstat(fi, &s1) < 0) {
862 (void) fprintf(stderr,
863 gettext("%s: cannot access %s\n"),
864 cmd, source);
865 return (1);
867 if (IDENTICAL(s1, s2)) {
868 (void) fprintf(stderr,
869 gettext(
870 "%s: %s and %s are identical\n"),
871 cmd, source, target);
872 return (1);
875 if (writefile(fi, fo, source, target, NULL,
876 NULL, &s1, &s2) != 0) {
877 return (1);
880 (void) close(fi);
881 if (close(fo) < 0) {
882 Perror2(target, "write");
883 return (1);
886 /* Copy regular extended attributes */
887 if (pflg || atflg || mve || saflg) {
888 attret = copyattributes(source, target);
889 if (attret != 0 && !attrsilent) {
890 (void) fprintf(stderr, gettext(
891 "%s: Failed to preserve"
892 " extended attributes of file"
893 " %s\n"), cmd, source);
895 /* Copy extended system attributes */
896 if (pflg || mve || saflg)
897 sattret = copy_sysattr(source, target);
898 if (mve && attret != 0) {
899 (void) unlink(target);
900 return (1);
902 if (attrsilent) {
903 attret = 0;
908 * XPG4: the write system call will clear setgid
909 * and setuid bits, so set them again.
911 if (pflg || mve) {
912 if ((ret = chg_mode(target, UID(s1), GID(s1),
913 FMODE(s1))) > 0)
914 return (1);
916 * Reapply ACL, since chmod may have
917 * altered ACL
919 if (s1acl != NULL) {
920 if ((acl_set(target, s1acl)) < 0) {
921 error++;
922 (void) fprintf(stderr,
923 gettext("%s: Failed to "
924 "set acl entries "
925 "on %s\n"), cmd, target);
927 * else: silent and
928 * continue
932 if ((ret = chg_time(target, s1)) > 0)
933 return (1);
935 if (cpy) {
936 if (error != 0 || attret != 0 || sattret != 0)
937 return (1);
938 return (0);
940 goto cleanup;
942 (void) fprintf(stderr,
943 gettext("%s: %s: unknown file type 0x%x\n"), cmd,
944 source, (s1.st_mode & S_IFMT));
945 return (1);
947 cleanup:
948 if (unlink(source) < 0) {
949 (void) unlink(target);
950 (void) fprintf(stderr,
951 gettext("%s: cannot unlink %s: "),
952 cmd, source);
953 perror("");
954 return (1);
956 if (error != 0 || attret != 0 || sattret != 0)
957 return (1);
958 return (ret);
960 /*NOTREACHED*/
961 return (ret);
965 * create_tnode()
967 * Create a node for use with the search tree which contains the
968 * inode information (device id and inode number).
970 * Input
971 * dev - device id
972 * ino - inode number
974 * Output
975 * tnode - NULL on error, otherwise returns a tnode structure
976 * which contains the input device id and inode number.
978 static tree_node_t *
979 create_tnode(dev_t dev, ino_t ino)
981 tree_node_t *tnode;
983 if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
984 tnode->node_dev = dev;
985 tnode->node_ino = ino;
988 return (tnode);
991 static int
992 chkfiles(char *source, char **to)
994 char *buf = (char *)NULL;
995 int (*statf)() = (cpy &&
996 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
997 char *target = *to;
998 int error;
1001 * Make sure source file exists.
1003 if ((*statf)(source, &s1) < 0) {
1005 * Keep the old error message except when someone tries to
1006 * mv/cp/ln a symbolic link that has a trailing slash and
1007 * points to a file.
1009 if (errno == ENOTDIR)
1010 (void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
1011 strerror(errno));
1012 else
1013 (void) fprintf(stderr,
1014 gettext("%s: cannot access %s\n"), cmd, source);
1015 return (1);
1019 * Get ACL info: don't bother with ln or cp/mv'ing symlinks
1021 if (!lnk && !ISLNK(s1)) {
1022 if (s1acl != NULL) {
1023 acl_free(s1acl);
1024 s1acl = NULL;
1026 if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
1027 (void) fprintf(stderr,
1028 "%s: failed to get acl entries: %s\n", source,
1029 acl_strerror(error));
1030 return (1);
1032 /* else: just permission bits */
1036 * If stat fails, then the target doesn't exist,
1037 * we will create a new target with default file type of regular.
1040 FTYPE(s2) = S_IFREG;
1041 targetexists = 0;
1042 if ((*statf)(target, &s2) >= 0) {
1043 if (ISLNK(s2))
1044 (void) stat(target, &s2);
1046 * If target is a directory,
1047 * make complete name of new file
1048 * within that directory.
1050 if (ISDIR(s2)) {
1051 size_t len;
1053 len = strlen(target) + strlen(dname(source)) + 4;
1054 if ((buf = (char *)malloc(len)) == NULL) {
1055 (void) fprintf(stderr,
1056 gettext("%s: Insufficient memory to "
1057 "%s %s\n "), cmd, cmd, source);
1058 exit(3);
1060 (void) snprintf(buf, len, "%s/%s",
1061 target, dname(source));
1062 *to = target = buf;
1065 if ((*statf)(target, &s2) >= 0) {
1066 int overwrite = FALSE;
1067 int override = FALSE;
1069 targetexists++;
1070 if (cpy || mve) {
1072 * For cp and mv, it is an error if the
1073 * source and target are the same file.
1074 * Check for the same inode and file
1075 * system, but don't check for the same
1076 * absolute pathname because it is an
1077 * error when the source and target are
1078 * hard links to the same file.
1080 if (IDENTICAL(s1, s2)) {
1081 (void) fprintf(stderr,
1082 gettext(
1083 "%s: %s and %s are identical\n"),
1084 cmd, source, target);
1085 if (buf != NULL)
1086 free(buf);
1087 return (1);
1090 if (lnk) {
1092 * For ln, it is an error if the source and
1093 * target are identical files (same inode,
1094 * same file system, and filenames resolve
1095 * to same absolute pathname).
1097 if (!chk_different(source, target)) {
1098 if (buf != NULL)
1099 free(buf);
1100 return (1);
1103 if (lnk && !silent) {
1104 (void) fprintf(stderr,
1105 gettext("%s: %s: File exists\n"),
1106 cmd, target);
1107 if (buf != NULL)
1108 free(buf);
1109 return (1);
1113 * overwrite:
1114 * If the user does not have access to
1115 * the target, ask ----if it is not
1116 * silent and user invoked command
1117 * interactively.
1119 * override:
1120 * If not silent, and stdin is a terminal, and
1121 * there's no write access, and the file isn't a
1122 * symbolic link, ask for permission.
1124 * XPG4: both overwrite and override:
1125 * ask only one question.
1127 * TRANSLATION_NOTE - The following messages will
1128 * contain the first character of the strings for
1129 * "yes" and "no" defined in the file
1130 * "nl_langinfo.po". After substitution, the
1131 * message will appear as follows:
1132 * <cmd>: overwrite <filename> (y/n)?
1133 * where <cmd> is the name of the command
1134 * (cp, mv) and <filename> is the destination file
1138 overwrite = iflg && !silent && use_stdin();
1139 override = !cpy && (access(target, 2) < 0) &&
1140 !silent && use_stdin() && !ISLNK(s2);
1142 if (overwrite && override) {
1143 (void) fprintf(stderr,
1144 gettext("%s: overwrite %s and override "
1145 "protection %o (%s/%s)? "), cmd, target,
1146 FMODE(s2) & MODEBITS, yesstr, nostr);
1147 if (yes() == 0) {
1148 if (buf != NULL)
1149 free(buf);
1150 return (2);
1152 } else if (overwrite && ISREG(s2)) {
1153 (void) fprintf(stderr,
1154 gettext("%s: overwrite %s (%s/%s)? "),
1155 cmd, target, yesstr, nostr);
1156 if (yes() == 0) {
1157 if (buf != NULL)
1158 free(buf);
1159 return (2);
1161 } else if (override) {
1162 (void) fprintf(stderr,
1163 gettext("%s: %s: override protection "
1164 /*CSTYLED*/
1165 "%o (%s/%s)? "),
1166 /*CSTYLED*/
1167 cmd, target, FMODE(s2) & MODEBITS,
1168 yesstr, nostr);
1169 if (yes() == 0) {
1170 if (buf != NULL)
1171 free(buf);
1172 return (2);
1176 if (lnk && unlink(target) < 0) {
1177 (void) fprintf(stderr,
1178 gettext("%s: cannot unlink %s: "),
1179 cmd, target);
1180 perror("");
1181 return (1);
1185 return (0);
1189 * check whether source and target are different
1190 * return 1 when they are different
1191 * return 0 when they are identical, or when unable to resolve a pathname
1193 static int
1194 chk_different(char *source, char *target)
1196 char rtarget[PATH_MAX], rsource[PATH_MAX];
1198 if (IDENTICAL(s1, s2)) {
1200 * IDENTICAL will be true for hard links, therefore
1201 * check whether the filenames are different
1203 if ((getrealpath(source, rsource) == 0) ||
1204 (getrealpath(target, rtarget) == 0)) {
1205 return (0);
1207 if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
1208 (void) fprintf(stderr, gettext(
1209 "%s: %s and %s are identical\n"),
1210 cmd, source, target);
1211 return (0);
1214 return (1);
1218 * get real path (resolved absolute pathname)
1219 * return 1 on success, 0 on failure
1221 static int
1222 getrealpath(char *path, char *rpath)
1224 if (realpath(path, rpath) == NULL) {
1225 int errno_save = errno;
1226 (void) fprintf(stderr, gettext(
1227 "%s: cannot resolve path %s: "), cmd, path);
1228 errno = errno_save;
1229 perror("");
1230 return (0);
1232 return (1);
1235 static int
1236 rcopy(char *from, char *to)
1238 DIR *fold = opendir(from);
1239 struct dirent *dp;
1240 struct stat statb, s1save;
1241 int errs = 0;
1242 char fromname[PATH_MAX];
1244 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
1245 Perror(from);
1246 return (1);
1248 if (pflg || mve) {
1250 * Save s1 (stat information for source dir) so that
1251 * mod and access times can be reserved during "cp -p"
1252 * or mv, since s1 gets overwritten.
1254 s1save = s1;
1256 for (;;) {
1257 dp = readdir(fold);
1258 if (dp == 0) {
1259 (void) closedir(fold);
1260 if (pflg || mve)
1261 return (chg_time(to, s1save) + errs);
1262 return (errs);
1264 if (dp->d_ino == 0)
1265 continue;
1266 if ((strcmp(dp->d_name, ".") == 0) ||
1267 (strcmp(dp->d_name, "..") == 0))
1268 continue;
1269 if (strlen(from)+1+strlen(dp->d_name) >=
1270 sizeof (fromname) - 1) {
1271 (void) fprintf(stderr,
1272 gettext("%s : %s/%s: Name too long\n"),
1273 cmd, from, dp->d_name);
1274 errs++;
1275 continue;
1277 (void) snprintf(fromname, sizeof (fromname),
1278 "%s/%s", from, dp->d_name);
1279 errs += cpymve(fromname, to);
1283 static char *
1284 dname(char *name)
1286 register char *p;
1289 * Return just the file name given the complete path.
1290 * Like basename(1).
1293 p = name;
1296 * While there are characters left,
1297 * set name to start after last
1298 * delimiter.
1301 while (*p)
1302 if (*p++ == DELIM && *p)
1303 name = p;
1304 return (name);
1307 static void
1308 usage(void)
1311 * Display usage message.
1314 if (mve) {
1315 (void) fprintf(stderr, gettext(
1316 "Usage: mv [-f] [-i] f1 f2\n"
1317 " mv [-f] [-i] f1 ... fn d1\n"
1318 " mv [-f] [-i] d1 d2\n"));
1319 } else if (lnk) {
1320 #ifdef XPG4
1321 (void) fprintf(stderr, gettext(
1322 "Usage: ln [-f] [-s] f1 [f2]\n"
1323 " ln [-f] [-s] f1 ... fn d1\n"
1324 " ln [-f] -s d1 d2\n"));
1325 #else
1326 (void) fprintf(stderr, gettext(
1327 "Usage: ln [-f] [-n] [-s] f1 [f2]\n"
1328 " ln [-f] [-n] [-s] f1 ... fn d1\n"
1329 " ln [-f] [-n] -s d1 d2\n"));
1330 #endif
1331 } else if (cpy) {
1332 (void) fprintf(stderr, gettext(
1333 "Usage: cp [-a] [-f] [-i] [-p] [-@] [-/] f1 f2\n"
1334 " cp [-a] [-f] [-i] [-p] [-@] [-/] f1 ... fn d1\n"
1335 " cp [-r|-R [-H|-L|-P]] [-a] [-f] [-i] [-p] [-@] "
1336 "[-/] d1 ... dn-1 dn\n"));
1338 exit(2);
1342 * chg_time()
1344 * Try to preserve modification and access time.
1345 * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
1346 * don't report a utimensat() failure.
1347 * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
1348 * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
1349 * exit status only if pflg is set.
1350 * utimensat(2) is being used to achieve granularity in nanoseconds
1351 * (if supported by the underlying file system) while setting file times.
1353 static int
1354 chg_time(char *to, struct stat ss)
1356 struct timespec times[2];
1357 int rc;
1359 times[0] = ss.st_atim;
1360 times[1] = ss.st_mtim;
1362 rc = utimensat(AT_FDCWD, to, times,
1363 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
1364 #ifdef XPG4
1365 if ((pflg || mve) && rc != 0) {
1366 (void) fprintf(stderr,
1367 gettext("%s: cannot set times for %s: "), cmd, to);
1368 perror("");
1369 if (pflg)
1370 return (1);
1372 #endif
1374 return (0);
1379 * chg_mode()
1381 * This function is called upon "cp -p" or mv across filesystems.
1383 * Try to preserve the owner and group id. If chown() fails,
1384 * only print a diagnostic message if doing a mv in the XPG4 version;
1385 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear
1386 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1387 * non-zero exit status because this is a security violation.
1388 * Try to preserve permissions.
1389 * If this is the XPG4 version and chmod() fails, print a diagnostic message
1390 * and arrange for a non-zero exit status.
1391 * If this is the Solaris version and chmod() fails, do not print a
1392 * diagnostic message or exit with a non-zero value.
1394 static int
1395 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1397 int clearflg = 0; /* controls message printed upon chown() error */
1398 struct stat st;
1400 /* Don't change mode if target is symlink */
1401 if (lstat(target, &st) == 0 && ISLNK(st))
1402 return (0);
1404 if (chown(target, uid, gid) != 0) {
1405 #ifdef XPG4
1406 if (mve) {
1407 (void) fprintf(stderr, gettext("%s: cannot change"
1408 " owner and group of %s: "), cmd, target);
1409 perror("");
1411 #endif
1412 if (mode & (S_ISUID | S_ISGID)) {
1413 /* try to clear S_ISUID and S_ISGID */
1414 mode &= ~S_ISUID & ~S_ISGID;
1415 ++clearflg;
1418 if (chmod(target, mode) != 0) {
1419 if (clearflg) {
1420 (void) fprintf(stderr, gettext(
1421 "%s: cannot clear S_ISUID and S_ISGID bits in"
1422 " %s: "), cmd, target);
1423 perror("");
1424 /* cp -p should get non-zero exit; mv should not */
1425 if (pflg)
1426 return (1);
1428 #ifdef XPG4
1429 else {
1430 (void) fprintf(stderr, gettext(
1431 "%s: cannot set permissions for %s: "), cmd, target);
1432 perror("");
1433 /* cp -p should get non-zero exit; mv should not */
1434 if (pflg)
1435 return (1);
1437 #endif
1439 return (0);
1443 static void
1444 Perror(char *s)
1446 char buf[PATH_MAX + 10];
1448 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1449 perror(buf);
1452 static void
1453 Perror2(char *s1, char *s2)
1455 char buf[PATH_MAX + 20];
1457 (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1458 cmd, gettext(s1), gettext(s2));
1459 perror(buf);
1463 * used for cp -R and for mv across file systems
1465 static int
1466 copydir(char *source, char *target)
1468 int ret, attret = 0;
1469 int sattret = 0;
1470 int pret = 0; /* need separate flag if -p is specified */
1471 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */
1472 struct stat s1save;
1473 acl_t *s1acl_save;
1474 int error = 0;
1476 s1acl_save = NULL;
1478 if (cpy && !rflg) {
1479 (void) fprintf(stderr,
1480 gettext("%s: %s: is a directory\n"), cmd, source);
1481 return (1);
1484 if (stat(target, &s2) < 0) {
1485 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1486 (void) fprintf(stderr, "%s: ", cmd);
1487 perror(target);
1488 return (1);
1490 if (stat(target, &s2) == 0) {
1491 fixmode = s2.st_mode;
1492 } else {
1493 fixmode = s1.st_mode;
1495 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1496 } else if (!(ISDIR(s2))) {
1497 (void) fprintf(stderr,
1498 gettext("%s: %s: not a directory.\n"), cmd, target);
1499 return (1);
1501 if (pflg || mve) {
1503 * Save s1 (stat information for source dir) and acl info,
1504 * if any, so that ownership, modes, times, and acl's can
1505 * be reserved during "cp -p" or mv.
1506 * s1 gets overwritten when doing the recursive copy.
1508 s1save = s1;
1509 if (s1acl != NULL) {
1510 s1acl_save = acl_dup(s1acl);
1511 if (s1acl_save == NULL) {
1512 (void) fprintf(stderr, gettext("%s: "
1513 "Insufficient memory to save acl"
1514 " entry\n"), cmd);
1515 if (pflg)
1516 return (1);
1519 #ifdef XPG4
1520 else {
1521 (void) fprintf(stderr, gettext("%s: "
1522 "Insufficient memory to save acl"
1523 " entry\n"), cmd);
1524 if (pflg)
1525 return (1);
1527 #endif
1531 ret = rcopy(source, target);
1534 * Once we created a directory, go ahead and set
1535 * its attributes, e.g. acls and time. The info
1536 * may get overwritten if we continue traversing
1537 * down the tree.
1539 * ACL for directory
1541 if (pflg || mve) {
1542 if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1543 FMODE(s1save))) == 0)
1544 pret = chg_time(target, s1save);
1545 ret += pret;
1546 if (s1acl_save != NULL) {
1547 if (acl_set(target, s1acl_save) < 0) {
1548 error++;
1549 #ifdef XPG4
1550 if (pflg || mve) {
1551 #else
1552 if (pflg) {
1553 #endif
1554 (void) fprintf(stderr, gettext(
1555 "%s: failed to set acl entries "
1556 "on %s\n"), cmd, target);
1557 if (pflg) {
1558 acl_free(s1acl_save);
1559 s1acl_save = NULL;
1560 ret++;
1563 /* else: silent and continue */
1565 acl_free(s1acl_save);
1566 s1acl_save = NULL;
1568 } else if (fixmode != (mode_t)0)
1569 (void) chmod(target, fixmode & MODEBITS);
1571 if (pflg || atflg || mve || saflg) {
1572 attret = copyattributes(source, target);
1573 if (!attrsilent && attret != 0) {
1574 (void) fprintf(stderr, gettext("%s: Failed to preserve"
1575 " extended attributes of directory"
1576 " %s\n"), cmd, source);
1577 } else {
1579 * Otherwise ignore failure.
1581 attret = 0;
1583 /* Copy extended system attributes */
1584 if (pflg || mve || saflg) {
1585 sattret = copy_sysattr(source, target);
1586 if (sattret != 0) {
1587 (void) fprintf(stderr, gettext(
1588 "%s: Failed to preserve "
1589 "extended system attributes "
1590 "of directory %s\n"), cmd, source);
1594 if (attret != 0 || sattret != 0 || error != 0)
1595 return (1);
1596 return (ret);
1599 static int
1600 copyspecial(char *target)
1602 int ret = 0;
1604 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1605 (void) fprintf(stderr, gettext(
1606 "cp: cannot create special file %s: "), target);
1607 perror("");
1608 return (1);
1611 if (pflg) {
1612 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1613 ret = chg_time(target, s1);
1616 return (ret);
1619 static int
1620 use_stdin(void)
1622 #ifdef XPG4
1623 return (1);
1624 #else
1625 return (isatty(fileno(stdin)));
1626 #endif
1629 /* Copy non-system extended attributes */
1631 static int
1632 copyattributes(char *source, char *target)
1634 struct dirent *dp;
1635 int error = 0;
1636 int aclerror;
1637 mode_t mode;
1638 int clearflg = 0;
1639 acl_t *xacl = NULL;
1640 acl_t *attrdiracl = NULL;
1641 struct timespec times[2];
1644 if (pathconf(source, _PC_XATTR_EXISTS) != 1)
1645 return (0);
1647 if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1648 if (!attrsilent) {
1649 (void) fprintf(stderr,
1650 gettext(
1651 "%s: cannot preserve extended attributes, "
1652 "operation not supported on file"
1653 " %s\n"), cmd, target);
1655 return (1);
1657 if (open_source(source) != 0)
1658 return (1);
1659 if (open_target_srctarg_attrdirs(source, target) != 0)
1660 return (1);
1661 if (open_attrdirp(source) != 0)
1662 return (1);
1664 if (pflg || mve) {
1665 if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1666 if (!attrsilent) {
1667 (void) fprintf(stderr,
1668 gettext("%s: failed to set file mode"
1669 " correctly on attribute directory of"
1670 " file %s: "), cmd, target);
1671 perror("");
1672 ++error;
1676 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1677 if (!attrsilent) {
1678 (void) fprintf(stderr,
1679 gettext("%s: failed to set file"
1680 " ownership correctly on attribute"
1681 " directory of file %s: "), cmd, target);
1682 perror("");
1683 ++error;
1687 * Now that we are the owner we can update st_ctime by calling
1688 * utimensat.
1690 times[0] = attrdir.st_atim;
1691 times[1] = attrdir.st_mtim;
1692 if (utimensat(targetdirfd, ".", times, 0) < 0) {
1693 if (!attrsilent) {
1694 (void) fprintf(stderr,
1695 gettext("%s: cannot set attribute times"
1696 " for %s: "), cmd, target);
1697 perror("");
1698 ++error;
1703 * Now set owner and group of attribute directory, implies
1704 * changing the ACL of the hidden attribute directory first.
1706 if ((aclerror = facl_get(sourcedirfd,
1707 ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1708 if (!attrsilent) {
1709 (void) fprintf(stderr, gettext(
1710 "%s: failed to get acl entries of"
1711 " attribute directory for"
1712 " %s : %s\n"), cmd,
1713 source, acl_strerror(aclerror));
1714 ++error;
1718 if (attrdiracl) {
1719 if (facl_set(targetdirfd, attrdiracl) != 0) {
1720 if (!attrsilent) {
1721 (void) fprintf(stderr, gettext(
1722 "%s: failed to set acl entries"
1723 " on attribute directory "
1724 "for %s\n"), cmd, target);
1725 ++error;
1727 acl_free(attrdiracl);
1728 attrdiracl = NULL;
1733 while ((dp = readdir(srcdirp)) != NULL) {
1734 int ret;
1736 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
1737 continue;
1738 else if (ret > 0) {
1739 ++error;
1740 goto out;
1743 if (pflg || mve) {
1744 if ((aclerror = facl_get(srcattrfd,
1745 ACL_NO_TRIVIAL, &xacl)) != 0) {
1746 if (!attrsilent) {
1747 (void) fprintf(stderr, gettext(
1748 "%s: failed to get acl entries of"
1749 " attribute %s for"
1750 " %s: %s"), cmd, dp->d_name,
1751 source, acl_strerror(aclerror));
1752 ++error;
1758 * preserve ACL
1760 if ((pflg || mve) && xacl != NULL) {
1761 if ((facl_set(targattrfd, xacl)) < 0) {
1762 if (!attrsilent) {
1763 (void) fprintf(stderr, gettext(
1764 "%s: failed to set acl entries on"
1765 " attribute %s for"
1766 "%s\n"), cmd, dp->d_name, target);
1767 ++error;
1769 acl_free(xacl);
1770 xacl = NULL;
1774 if (writefile(srcattrfd, targattrfd, source, target,
1775 dp->d_name, dp->d_name, &s3, &s4) != 0) {
1776 if (!attrsilent) {
1777 ++error;
1779 goto next;
1782 if (pflg || mve) {
1783 mode = FMODE(s3);
1785 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
1786 if (!attrsilent) {
1787 (void) fprintf(stderr,
1788 gettext("%s: cannot change"
1789 " owner and group of"
1790 " attribute %s for" " file"
1791 " %s: "), cmd, dp->d_name, target);
1792 perror("");
1793 ++error;
1795 if (mode & (S_ISUID | S_ISGID)) {
1796 /* try to clear S_ISUID and S_ISGID */
1797 mode &= ~S_ISUID & ~S_ISGID;
1798 ++clearflg;
1801 times[0] = s3.st_atim;
1802 times[1] = s3.st_mtim;
1803 if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
1804 if (!attrsilent) {
1805 (void) fprintf(stderr,
1806 gettext("%s: cannot set attribute"
1807 " times for %s: "), cmd, target);
1808 perror("");
1809 ++error;
1812 if (fchmod(targattrfd, mode) != 0) {
1813 if (clearflg) {
1814 (void) fprintf(stderr, gettext(
1815 "%s: cannot clear S_ISUID and "
1816 "S_ISGID bits in attribute %s"
1817 " for file"
1818 " %s: "), cmd, dp->d_name, target);
1819 } else {
1820 if (!attrsilent) {
1821 (void) fprintf(stderr,
1822 gettext(
1823 "%s: cannot set permissions of attribute"
1824 " %s for %s: "), cmd, dp->d_name, target);
1825 perror("");
1826 ++error;
1830 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
1831 if (!attrsilent) {
1832 (void) fprintf(stderr, gettext(
1833 "%s: failed to set acl entries on"
1834 " attribute %s for"
1835 "%s\n"), cmd, dp->d_name, target);
1836 ++error;
1838 acl_free(xacl);
1839 xacl = NULL;
1842 next:
1843 if (xacl != NULL) {
1844 acl_free(xacl);
1845 xacl = NULL;
1847 if (srcattrfd != -1)
1848 (void) close(srcattrfd);
1849 if (targattrfd != -1)
1850 (void) close(targattrfd);
1851 srcattrfd = targattrfd = -1;
1853 out:
1854 if (xacl != NULL) {
1855 acl_free(xacl);
1856 xacl = NULL;
1858 if (attrdiracl != NULL) {
1859 acl_free(attrdiracl);
1860 attrdiracl = NULL;
1863 if (!saflg && !pflg && !mve)
1864 close_all();
1865 return (error == 0 ? 0 : 1);
1868 /* Copy extended system attributes from source to target */
1870 static int
1871 copy_sysattr(char *source, char *target)
1873 struct dirent *dp;
1874 nvlist_t *response;
1875 int error = 0;
1876 int target_sa_support = 0;
1878 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
1879 return (0);
1881 if (open_source(source) != 0)
1882 return (1);
1885 * Gets non default extended system attributes from the
1886 * source file to copy to the target. The target has
1887 * the defaults set when its created and thus no need
1888 * to copy the defaults.
1890 response = sysattr_list(cmd, srcfd, source);
1892 if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
1893 if (response != NULL) {
1894 (void) fprintf(stderr,
1895 gettext(
1896 "%s: cannot preserve extended system "
1897 "attribute, operation not supported on file"
1898 " %s\n"), cmd, target);
1899 error++;
1900 goto out;
1902 } else {
1903 target_sa_support = 1;
1906 if (target_sa_support) {
1907 if (srcdirp == NULL) {
1908 if (open_target_srctarg_attrdirs(source,
1909 target) != 0) {
1910 error++;
1911 goto out;
1913 if (open_attrdirp(source) != 0) {
1914 error++;
1915 goto out;
1917 } else {
1918 rewind_attrdir(srcdirp);
1920 while ((dp = readdir(srcdirp)) != NULL) {
1921 nvlist_t *res;
1922 int ret;
1924 if ((ret = traverse_attrfile(dp, source, target,
1925 0)) == -1)
1926 continue;
1927 else if (ret > 0) {
1928 ++error;
1929 goto out;
1932 * Gets non default extended system attributes from the
1933 * attribute file to copy to the target. The target has
1934 * the defaults set when its created and thus no need
1935 * to copy the defaults.
1937 if (dp->d_name != NULL) {
1938 res = sysattr_list(cmd, srcattrfd, dp->d_name);
1939 if (res == NULL)
1940 goto next;
1943 * Copy non default extended system attributes of named
1944 * attribute file.
1946 if (fsetattr(targattrfd,
1947 XATTR_VIEW_READWRITE, res) != 0) {
1948 ++error;
1949 (void) fprintf(stderr, gettext("%s: "
1950 "Failed to copy extended system "
1951 "attributes from attribute file "
1952 "%s of %s to %s\n"), cmd,
1953 dp->d_name, source, target);
1956 next:
1957 if (srcattrfd != -1)
1958 (void) close(srcattrfd);
1959 if (targattrfd != -1)
1960 (void) close(targattrfd);
1961 srcattrfd = targattrfd = -1;
1962 nvlist_free(res);
1965 /* Copy source file non default extended system attributes to target */
1966 if (target_sa_support && (response != NULL) &&
1967 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
1968 ++error;
1969 (void) fprintf(stderr, gettext("%s: Failed to "
1970 "copy extended system attributes from "
1971 "%s to %s\n"), cmd, source, target);
1973 out:
1974 nvlist_free(response);
1975 close_all();
1976 return (error == 0 ? 0 : 1);
1979 /* Open the source file */
1982 open_source(char *src)
1984 int error = 0;
1986 srcfd = -1;
1987 if ((srcfd = open(src, O_RDONLY)) == -1) {
1988 if (pflg && attrsilent) {
1989 error++;
1990 goto out;
1992 if (!attrsilent) {
1993 (void) fprintf(stderr,
1994 gettext("%s: cannot open file"
1995 " %s: "), cmd, src);
1996 perror("");
1998 ++error;
2000 out:
2001 if (error)
2002 close_all();
2003 return (error == 0 ? 0 : 1);
2006 /* Open source attribute dir, target and target attribute dir. */
2009 open_target_srctarg_attrdirs(char *src, char *targ)
2011 int error = 0;
2013 targfd = sourcedirfd = targetdirfd = -1;
2015 if ((targfd = open(targ, O_RDONLY)) == -1) {
2016 if (pflg && attrsilent) {
2017 error++;
2018 goto out;
2020 if (!attrsilent) {
2021 (void) fprintf(stderr,
2022 gettext("%s: cannot open file"
2023 " %s: "), cmd, targ);
2024 perror("");
2026 ++error;
2027 goto out;
2030 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2031 if (pflg && attrsilent) {
2032 error++;
2033 goto out;
2035 if (!attrsilent) {
2036 (void) fprintf(stderr,
2037 gettext("%s: cannot open attribute"
2038 " directory for %s: "), cmd, src);
2039 perror("");
2041 ++error;
2042 goto out;
2045 if (fstat(sourcedirfd, &attrdir) == -1) {
2046 if (pflg && attrsilent) {
2047 error++;
2048 goto out;
2051 if (!attrsilent) {
2052 (void) fprintf(stderr,
2053 gettext("%s: could not retrieve stat"
2054 " information for attribute directory"
2055 "of file %s: "), cmd, src);
2056 perror("");
2058 ++error;
2059 goto out;
2061 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2062 if (pflg && attrsilent) {
2063 error++;
2064 goto out;
2066 if (!attrsilent) {
2067 (void) fprintf(stderr,
2068 gettext("%s: cannot open attribute"
2069 " directory for %s: "), cmd, targ);
2070 perror("");
2072 ++error;
2074 out:
2075 if (error)
2076 close_all();
2077 return (error == 0 ? 0 : 1);
2081 open_attrdirp(char *source)
2083 int tmpfd = -1;
2084 int error = 0;
2087 * dup sourcedirfd for use by fdopendir().
2088 * fdopendir will take ownership of given fd and will close
2089 * it when closedir() is called.
2092 if ((tmpfd = dup(sourcedirfd)) == -1) {
2093 if (pflg && attrsilent) {
2094 error++;
2095 goto out;
2097 if (!attrsilent) {
2098 (void) fprintf(stderr,
2099 gettext(
2100 "%s: unable to dup attribute directory"
2101 " file descriptor for %s: "), cmd, source);
2102 perror("");
2103 ++error;
2105 goto out;
2107 if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2108 if (pflg && attrsilent) {
2109 error++;
2110 goto out;
2112 if (!attrsilent) {
2113 (void) fprintf(stderr,
2114 gettext("%s: failed to open attribute"
2115 " directory for %s: "), cmd, source);
2116 perror("");
2117 ++error;
2120 out:
2121 if (error)
2122 close_all();
2123 return (error == 0 ? 0 : 1);
2126 /* Skips through ., .., and system attribute 'view' files */
2128 traverse_attrfile(struct dirent *dp, char *source, char *target, int first)
2130 int error = 0;
2132 srcattrfd = targattrfd = -1;
2134 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2135 (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2136 dp->d_name[2] == '\0') ||
2137 (sysattr_type(dp->d_name) == _RO_SATTR) ||
2138 (sysattr_type(dp->d_name) == _RW_SATTR))
2139 return (-1);
2141 if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2142 O_RDONLY)) == -1) {
2143 if (!attrsilent) {
2144 (void) fprintf(stderr,
2145 gettext("%s: cannot open attribute %s on"
2146 " file %s: "), cmd, dp->d_name, source);
2147 perror("");
2148 ++error;
2149 goto out;
2153 if (fstat(srcattrfd, &s3) < 0) {
2154 if (!attrsilent) {
2155 (void) fprintf(stderr,
2156 gettext("%s: could not stat attribute"
2157 " %s on file"
2158 " %s: "), cmd, dp->d_name, source);
2159 perror("");
2160 ++error;
2162 goto out;
2165 if (first) {
2166 (void) unlinkat(targetdirfd, dp->d_name, 0);
2167 if ((targattrfd = openat(targetdirfd, dp->d_name,
2168 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2169 if (!attrsilent) {
2170 (void) fprintf(stderr,
2171 gettext("%s: could not create attribute"
2172 " %s on file %s: "), cmd, dp->d_name,
2173 target);
2174 perror("");
2175 ++error;
2177 goto out;
2179 } else {
2180 if ((targattrfd = openat(targetdirfd, dp->d_name,
2181 O_RDONLY)) == -1) {
2182 if (!attrsilent) {
2183 (void) fprintf(stderr,
2184 gettext("%s: could not open attribute"
2185 " %s on file %s: "), cmd, dp->d_name,
2186 target);
2187 perror("");
2188 ++error;
2190 goto out;
2195 if (fstat(targattrfd, &s4) < 0) {
2196 if (!attrsilent) {
2197 (void) fprintf(stderr,
2198 gettext("%s: could not stat attribute"
2199 " %s on file"
2200 " %s: "), cmd, dp->d_name, target);
2201 perror("");
2202 ++error;
2206 out:
2207 if (error) {
2208 if (srcattrfd != -1)
2209 (void) close(srcattrfd);
2210 if (targattrfd != -1)
2211 (void) close(targattrfd);
2212 srcattrfd = targattrfd = -1;
2214 return (error == 0 ? 0 :1);
2217 void
2218 rewind_attrdir(DIR * sdp)
2220 int pwdfd;
2222 pwdfd = open(".", O_RDONLY);
2223 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2224 rewinddir(sdp);
2225 (void) fchdir(pwdfd);
2226 (void) close(pwdfd);
2227 } else {
2228 if (!attrsilent) {
2229 (void) fprintf(stderr, gettext("%s: "
2230 "failed to rewind attribute dir\n"),
2231 cmd);
2236 void
2237 close_all()
2239 if (srcattrfd != -1)
2240 (void) close(srcattrfd);
2241 if (targattrfd != -1)
2242 (void) close(targattrfd);
2243 if (sourcedirfd != -1)
2244 (void) close(sourcedirfd);
2245 if (targetdirfd != -1)
2246 (void) close(targetdirfd);
2247 if (srcdirp != NULL) {
2248 (void) closedir(srcdirp);
2249 srcdirp = NULL;
2251 if (srcfd != -1)
2252 (void) close(srcfd);
2253 if (targfd != -1)
2254 (void) close(targfd);