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 /*
23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
33
34 /*
35 * University Copyright- Copyright (c) 1982, 1986, 1988
36 * The Regents of the University of California
37 * All Rights Reserved
38 *
39 * University Acknowledgment- Portions of this document are derived from
40 * software developed by the University of California, Berkeley, and its
41 * contributors.
42 */
43
44 /*
45 * Combined mv/cp/ln command:
46 * mv file1 file2
47 * mv dir1 dir2
48 * mv file1 ... filen dir1
49 */
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"
58
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)
72
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
78
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);
97
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;
128
129 /* Extended system attributes support */
130
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();
137
138
139 int
140 main(int argc, char *argv[])
141 {
142 int c, i, r, errflg = 0;
143 char target[PATH_MAX];
144 int (*move)(char *, char *);
145
146 /*
147 * Determine command invoked (mv, cp, or ln)
148 */
149
150 if (cmd = strrchr(argv[0], '/'))
151 ++cmd;
152 else
153 cmd = argv[0];
154
155 /*
156 * Set flags based on command.
157 */
158
159 (void) setlocale(LC_ALL, "");
160 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
161 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
162 #endif
163 (void) textdomain(TEXT_DOMAIN);
164 if (init_yes() < 0) {
165 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
166 strerror(errno));
167 exit(3);
168 }
169
170 if (EQ(cmd, "mv"))
171 mve = TRUE;
172 else if (EQ(cmd, "ln"))
173 lnk = TRUE;
174 else if (EQ(cmd, "cp"))
175 cpy = TRUE;
176 else {
177 (void) fprintf(stderr,
178 gettext("Invalid command name (%s); expecting "
179 "mv, cp, or ln.\n"), cmd);
180 exit(1);
181 }
182
183 /*
184 * Check for options:
185 * cp [ -r|-R [-H|-L|-P]] [-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
191 */
192
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':
214 /*
215 * If more than one of -H, -L, or -P are
216 * specified, only the last option specified
217 * determines the behavior.
218 */
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':
231 /*
232 * The default behavior of cp -R|-r
233 * when specified without -H|-L|-P
234 * is -L.
235 */
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++;
264 }
265
266 /* -R or -r must be specified with -H, -L, or -P */
267 if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
268 errflg++;
269 }
270
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++;
288 }
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++;
303 }
304 }
305
306 /*
307 * For BSD compatibility allow - to delimit the end of
308 * options for mv.
309 */
310 if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
311 optind++;
312
313 /*
314 * Check for sufficient arguments
315 * or a usage error.
316 */
317
318 argc -= optind;
319 argv = &argv[optind];
320
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();
326 }
327
328 if (errflg != 0)
329 usage();
330
331 /*
332 * If there is more than a source and target,
333 * the last argument (the target) must be a directory
334 * which really exists.
335 */
336
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);
343 }
344
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();
350 }
351 }
352
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);
358 }
359
360 if (argc == 1) {
361 if (!lnk)
362 usage();
363 (void) strcpy(target, ".");
364 } else {
365 (void) strcpy(target, argv[--argc]);
366 }
367
368 /*
369 * Perform a multiple argument mv|cp|ln by
370 * multiple invocations of cpymve() or lnkfil().
371 */
372 if (lnk)
373 move = lnkfil;
374 else
375 move = cpymve;
376
377 r = 0;
378 for (i = 0; i < argc; i++) {
379 stree = NULL;
380 cmdarg = 1;
381 r += move(argv[i], target);
382 }
383
384 /*
385 * Show errors by nonzero exit code.
386 */
387
388 return (r?2:0);
389 }
390
391 static int
392 lnkfil(char *source, char *target)
393 {
394 char *buf = NULL;
395
396 if (sflg) {
397
398 /*
399 * If target is a directory make complete
400 * name of the new symbolic link within that
401 * directory.
402 */
403
404 if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
405 size_t len;
406
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);
413 }
414 (void) snprintf(buf, len, "%s/%s",
415 target, dname(source));
416 target = buf;
417 }
418
419 /*
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.
424 */
425
426 if ((lstat(target, &s2) == 0)) {
427 /*
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.
432 */
433 if (silent) {
434 /*
435 * Don't allow silent (-f) removal of an existing
436 * directory; could leave unreferenced directory
437 * entries.
438 */
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);
445 }
446 if (unlink(target) < 0) {
447 (void) fprintf(stderr,
448 gettext("%s: cannot unlink %s: "),
449 cmd, target);
450 perror("");
451 return (1);
452 }
453 }
454 }
455
456
457 /*
458 * Create a symbolic link to the source.
459 */
460
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);
469 }
470 if (buf != NULL)
471 free(buf);
472 return (0);
473 }
474
475 switch (chkfiles(source, &target)) {
476 case 1: return (1);
477 case 2: return (0);
478 /* default - fall through */
479 }
480
481 /*
482 * Make sure source file is not a directory,
483 * we cannot link directories...
484 */
485
486 if (ISDIR(s1)) {
487 (void) fprintf(stderr,
488 gettext("%s: %s is a directory\n"), cmd, source);
489 return (1);
490 }
491
492 /*
493 * hard link, call link() and return.
494 */
495
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("");
506 }
507 if (buf != NULL)
508 free(buf);
509 return (1);
510 } else {
511 if (buf != NULL)
512 free(buf);
513 return (0);
514 }
515 }
516
517 static int
518 cpymve(char *source, char *target)
519 {
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;
527
528 switch (chkfiles(source, &target)) {
529 case 1: return (1);
530 case 2: return (0);
531 /* default - fall through */
532 }
533
534 /*
535 * If it's a recursive copy and source
536 * is a directory, then call rcopy (from copydir).
537 */
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;
546
547 /*
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.
551 */
552 if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
553 if (rc == 0) {
554 /*
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.
560 */
561 (void) fprintf(stderr, gettext(
562 "%s: cycle detected: %s\n"),
563 cmd, source);
564 } else {
565 Perror(source);
566 }
567 return (1);
568 }
569
570 cmdarg = 0;
571 rc = copydir(source, target);
572
573 /*
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.
580 */
581 if ((tnode = create_tnode(save_dev,
582 save_ino)) == NULL) {
583 Perror(source);
584 return (1);
585 }
586 if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
587 avl_remove(stree, tptr);
588 }
589 free(tptr);
590 free(tnode);
591 return (rc);
592
593 } else if (ISDEV(s1) && Rflg) {
594 return (copyspecial(target));
595 } else {
596 goto copy;
597 }
598 }
599
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);
609 }
610 (void) fprintf(stderr,
611 gettext("%s: cannot rename %s to %s: "),
612 cmd, source, target);
613 perror("");
614 return (1);
615 }
616
617 /*
618 * cannot move a non-directory (source) onto an existing
619 * directory (target)
620 *
621 */
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);
628 }
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);
641 }
642 }
643 #endif
644 if ((n = copydir(source, target)) == 0)
645 (void) rmdir(source);
646 return (n);
647 }
648
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);
655 }
656
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);
663 }
664
665 /*
666 * File cannot be renamed, try to recreate the symbolic
667 * link or special device, or copy the file wholesale
668 * between file systems.
669 */
670 if (ISLNK(s1)) {
671 register int m;
672 register mode_t md;
673 char symln[PATH_MAX + 1];
674
675 if (targetexists && unlink(target) < 0) {
676 (void) fprintf(stderr,
677 gettext("%s: cannot unlink %s: "),
678 cmd, target);
679 perror("");
680 return (1);
681 }
682
683 if ((m = readlink(source, symln,
684 sizeof (symln) - 1)) < 0) {
685 Perror(source);
686 return (1);
687 }
688 symln[m] = '\0';
689
690 md = umask(~(s1.st_mode & MODEBITS));
691 if (symlink(symln, target) < 0) {
692 Perror(target);
693 return (1);
694 }
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("");
703 }
704 #endif
705 goto cleanup;
706 }
707 if (ISDEV(s1)) {
708
709 if (targetexists && unlink(target) < 0) {
710 (void) fprintf(stderr,
711 gettext("%s: cannot unlink %s: "),
712 cmd, target);
713 perror("");
714 return (1);
715 }
716
717 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
718 Perror(target);
719 return (1);
720 }
721
722 (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
723 (void) chg_time(target, s1);
724 goto cleanup;
725 }
726
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);
735 }
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);
743 }
744 }
745
746
747 copy:
748 /*
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.
755 */
756 if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
757 int m;
758 mode_t md;
759 char symln[PATH_MAX + 1];
760
761 m = readlink(source, symln, sizeof (symln) - 1);
762
763 if (m < 0) {
764 Perror(source);
765 return (1);
766 }
767 symln[m] = '\0';
768
769 /*
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.
775 */
776 md = umask(~(s1.st_mode & MODEBITS));
777 if (symlink(symln, target) < 0) {
778 Perror(target);
779 return (1);
780 }
781 (void) umask(md);
782 m = lchown(target, UID(s1), GID(s1));
783
784 if (m < 0) {
785 (void) fprintf(stderr, gettext(
786 "cp: cannot change owner and "
787 "group of %s:"), target);
788 perror("");
789 }
790 } else {
791 /*
792 * Copy the file. If it happens to be a
793 * symlink, copy the file referenced
794 * by the symlink.
795 */
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);
803 }
804
805 fo = creat(target, s1.st_mode & MODEBITS);
806 if (fo < 0) {
807 /*
808 * If -f and creat() failed, unlink
809 * and try again.
810 */
811 if (fflg) {
812 (void) unlink(target);
813 fo = creat(target,
814 s1.st_mode & MODEBITS);
815 }
816 }
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);
827 }
828
829 /*
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.
835 *
836 * ACL for regular files
837 */
838
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;
853 /*
854 * else: silent and
855 * continue
856 */
857 }
858 }
859 }
860
861 if (fstat(fi, &s1) < 0) {
862 (void) fprintf(stderr,
863 gettext("%s: cannot access %s\n"),
864 cmd, source);
865 return (1);
866 }
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);
873 }
874
875 if (writefile(fi, fo, source, target, NULL,
876 NULL, &s1, &s2) != 0) {
877 return (1);
878 }
879
880 (void) close(fi);
881 if (close(fo) < 0) {
882 Perror2(target, "write");
883 return (1);
884 }
885 }
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);
894 }
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);
901 }
902 if (attrsilent) {
903 attret = 0;
904 }
905 }
906
907 /*
908 * XPG4: the write system call will clear setgid
909 * and setuid bits, so set them again.
910 */
911 if (pflg || mve) {
912 if ((ret = chg_mode(target, UID(s1), GID(s1),
913 FMODE(s1))) > 0)
914 return (1);
915 /*
916 * Reapply ACL, since chmod may have
917 * altered ACL
918 */
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);
926 /*
927 * else: silent and
928 * continue
929 */
930 }
931 }
932 if ((ret = chg_time(target, s1)) > 0)
933 return (1);
934 }
935 if (cpy) {
936 if (error != 0 || attret != 0 || sattret != 0)
937 return (1);
938 return (0);
939 }
940 goto cleanup;
941 }
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);
946
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);
955 }
956 if (error != 0 || attret != 0 || sattret != 0)
957 return (1);
958 return (ret);
959 }
960 /*NOTREACHED*/
961 return (ret);
962 }
963
964 /*
965 * create_tnode()
966 *
967 * Create a node for use with the search tree which contains the
968 * inode information (device id and inode number).
969 *
970 * Input
971 * dev - device id
972 * ino - inode number
973 *
974 * Output
975 * tnode - NULL on error, otherwise returns a tnode structure
976 * which contains the input device id and inode number.
977 */
978 static tree_node_t *
979 create_tnode(dev_t dev, ino_t ino)
980 {
981 tree_node_t *tnode;
982
983 if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
984 tnode->node_dev = dev;
985 tnode->node_ino = ino;
986 }
987
988 return (tnode);
989 }
990
991 static int
992 chkfiles(char *source, char **to)
993 {
994 char *buf = (char *)NULL;
995 int (*statf)() = (cpy &&
996 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
997 char *target = *to;
998 int error;
999
1000 /*
1001 * Make sure source file exists.
1002 */
1003 if ((*statf)(source, &s1) < 0) {
1004 /*
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.
1008 */
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);
1016 }
1017
1018 /*
1019 * Get ACL info: don't bother with ln or cp/mv'ing symlinks
1020 */
1021 if (!lnk && !ISLNK(s1)) {
1022 if (s1acl != NULL) {
1023 acl_free(s1acl);
1024 s1acl = NULL;
1025 }
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);
1031 }
1032 /* else: just permission bits */
1033 }
1034
1035 /*
1036 * If stat fails, then the target doesn't exist,
1037 * we will create a new target with default file type of regular.
1038 */
1039
1040 FTYPE(s2) = S_IFREG;
1041 targetexists = 0;
1042 if ((*statf)(target, &s2) >= 0) {
1043 if (ISLNK(s2))
1044 (void) stat(target, &s2);
1045 /*
1046 * If target is a directory,
1047 * make complete name of new file
1048 * within that directory.
1049 */
1050 if (ISDIR(s2)) {
1051 size_t len;
1052
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);
1059 }
1060 (void) snprintf(buf, len, "%s/%s",
1061 target, dname(source));
1062 *to = target = buf;
1063 }
1064
1065 if ((*statf)(target, &s2) >= 0) {
1066 int overwrite = FALSE;
1067 int override = FALSE;
1068
1069 targetexists++;
1070 if (cpy || mve) {
1071 /*
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.
1079 */
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);
1088 }
1089 }
1090 if (lnk) {
1091 /*
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).
1096 */
1097 if (!chk_different(source, target)) {
1098 if (buf != NULL)
1099 free(buf);
1100 return (1);
1101 }
1102 }
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);
1110 }
1111
1112 /*
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.
1118 *
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.
1123 *
1124 * XPG4: both overwrite and override:
1125 * ask only one question.
1126 *
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
1135 */
1136
1137
1138 overwrite = iflg && !silent && use_stdin();
1139 override = !cpy && (access(target, 2) < 0) &&
1140 !silent && use_stdin() && !ISLNK(s2);
1141
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);
1151 }
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);
1160 }
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);
1173 }
1174 }
1175
1176 if (lnk && unlink(target) < 0) {
1177 (void) fprintf(stderr,
1178 gettext("%s: cannot unlink %s: "),
1179 cmd, target);
1180 perror("");
1181 return (1);
1182 }
1183 }
1184 }
1185 return (0);
1186 }
1187
1188 /*
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
1192 */
1193 static int
1194 chk_different(char *source, char *target)
1195 {
1196 char rtarget[PATH_MAX], rsource[PATH_MAX];
1197
1198 if (IDENTICAL(s1, s2)) {
1199 /*
1200 * IDENTICAL will be true for hard links, therefore
1201 * check whether the filenames are different
1202 */
1203 if ((getrealpath(source, rsource) == 0) ||
1204 (getrealpath(target, rtarget) == 0)) {
1205 return (0);
1206 }
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);
1212 }
1213 }
1214 return (1);
1215 }
1216
1217 /*
1218 * get real path (resolved absolute pathname)
1219 * return 1 on success, 0 on failure
1220 */
1221 static int
1222 getrealpath(char *path, char *rpath)
1223 {
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);
1231 }
1232 return (1);
1233 }
1234
1235 static int
1236 rcopy(char *from, char *to)
1237 {
1238 DIR *fold = opendir(from);
1239 struct dirent *dp;
1240 struct stat statb, s1save;
1241 int errs = 0;
1242 char fromname[PATH_MAX];
1243
1244 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
1245 Perror(from);
1246 return (1);
1247 }
1248 if (pflg || mve) {
1249 /*
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.
1253 */
1254 s1save = s1;
1255 }
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);
1263 }
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;
1276 }
1277 (void) snprintf(fromname, sizeof (fromname),
1278 "%s/%s", from, dp->d_name);
1279 errs += cpymve(fromname, to);
1280 }
1281 }
1282
1283 static char *
1284 dname(char *name)
1285 {
1286 register char *p;
1287
1288 /*
1289 * Return just the file name given the complete path.
1290 * Like basename(1).
1291 */
1292
1293 p = name;
1294
1295 /*
1296 * While there are characters left,
1297 * set name to start after last
1298 * delimiter.
1299 */
1300
1301 while (*p)
1302 if (*p++ == DELIM && *p)
1303 name = p;
1304 return (name);
1305 }
1306
1307 static void
1308 usage(void)
1309 {
1310 /*
1311 * Display usage message.
1312 */
1313
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"));
1337 }
1338 exit(2);
1339 }
1340
1341 /*
1342 * chg_time()
1343 *
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.
1352 */
1353 static int
1354 chg_time(char *to, struct stat ss)
1355 {
1356 struct timespec times[2];
1357 #ifdef XPG4
1358 int rc;
1359 #endif
1360
1361 times[0] = ss.st_atim;
1362 times[1] = ss.st_mtim;
1363
1364 #ifdef XPG4
1365 rc = utimensat(AT_FDCWD, to, times,
1366 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
1367 if ((pflg || mve) && rc != 0) {
1368 (void) fprintf(stderr,
1369 gettext("%s: cannot set times for %s: "), cmd, to);
1370 perror("");
1371 if (pflg)
1372 return (1);
1373 }
1374 #else
1375 (void) utimensat(AT_FDCWD, to, times,
1376 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
1377 #endif
1378
1379 return (0);
1380
1381 }
1382
1383 /*
1384 * chg_mode()
1385 *
1386 * This function is called upon "cp -p" or mv across filesystems.
1387 *
1388 * Try to preserve the owner and group id. If chown() fails,
1389 * only print a diagnostic message if doing a mv in the XPG4 version;
1390 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear
1391 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1392 * non-zero exit status because this is a security violation.
1393 * Try to preserve permissions.
1394 * If this is the XPG4 version and chmod() fails, print a diagnostic message
1395 * and arrange for a non-zero exit status.
1396 * If this is the Solaris version and chmod() fails, do not print a
1397 * diagnostic message or exit with a non-zero value.
1398 */
1399 static int
1400 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1401 {
1402 int clearflg = 0; /* controls message printed upon chown() error */
1403 struct stat st;
1404
1405 /* Don't change mode if target is symlink */
1406 if (lstat(target, &st) == 0 && ISLNK(st))
1407 return (0);
1408
1409 if (chown(target, uid, gid) != 0) {
1410 #ifdef XPG4
1411 if (mve) {
1412 (void) fprintf(stderr, gettext("%s: cannot change"
1413 " owner and group of %s: "), cmd, target);
1414 perror("");
1415 }
1416 #endif
1417 if (mode & (S_ISUID | S_ISGID)) {
1418 /* try to clear S_ISUID and S_ISGID */
1419 mode &= ~S_ISUID & ~S_ISGID;
1420 ++clearflg;
1421 }
1422 }
1423 if (chmod(target, mode) != 0) {
1424 if (clearflg) {
1425 (void) fprintf(stderr, gettext(
1426 "%s: cannot clear S_ISUID and S_ISGID bits in"
1427 " %s: "), cmd, target);
1428 perror("");
1429 /* cp -p should get non-zero exit; mv should not */
1430 if (pflg)
1431 return (1);
1432 }
1433 #ifdef XPG4
1434 else {
1435 (void) fprintf(stderr, gettext(
1436 "%s: cannot set permissions for %s: "), cmd, target);
1437 perror("");
1438 /* cp -p should get non-zero exit; mv should not */
1439 if (pflg)
1440 return (1);
1441 }
1442 #endif
1443 }
1444 return (0);
1445
1446 }
1447
1448 static void
1449 Perror(char *s)
1450 {
1451 char buf[PATH_MAX + 10];
1452
1453 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1454 perror(buf);
1455 }
1456
1457 static void
1458 Perror2(char *s1, char *s2)
1459 {
1460 char buf[PATH_MAX + 20];
1461
1462 (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1463 cmd, gettext(s1), gettext(s2));
1464 perror(buf);
1465 }
1466
1467 /*
1468 * used for cp -R and for mv across file systems
1469 */
1470 static int
1471 copydir(char *source, char *target)
1472 {
1473 int ret, attret = 0;
1474 int sattret = 0;
1475 int pret = 0; /* need separate flag if -p is specified */
1476 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */
1477 struct stat s1save;
1478 acl_t *s1acl_save;
1479 int error = 0;
1480
1481 s1acl_save = NULL;
1482
1483 if (cpy && !rflg) {
1484 (void) fprintf(stderr,
1485 gettext("%s: %s: is a directory\n"), cmd, source);
1486 return (1);
1487 }
1488
1489 if (stat(target, &s2) < 0) {
1490 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1491 (void) fprintf(stderr, "%s: ", cmd);
1492 perror(target);
1493 return (1);
1494 }
1495 if (stat(target, &s2) == 0) {
1496 fixmode = s2.st_mode;
1497 } else {
1498 fixmode = s1.st_mode;
1499 }
1500 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1501 } else if (!(ISDIR(s2))) {
1502 (void) fprintf(stderr,
1503 gettext("%s: %s: not a directory.\n"), cmd, target);
1504 return (1);
1505 }
1506 if (pflg || mve) {
1507 /*
1508 * Save s1 (stat information for source dir) and acl info,
1509 * if any, so that ownership, modes, times, and acl's can
1510 * be reserved during "cp -p" or mv.
1511 * s1 gets overwritten when doing the recursive copy.
1512 */
1513 s1save = s1;
1514 if (s1acl != NULL) {
1515 s1acl_save = acl_dup(s1acl);
1516 if (s1acl_save == NULL) {
1517 (void) fprintf(stderr, gettext("%s: "
1518 "Insufficient memory to save acl"
1519 " entry\n"), cmd);
1520 if (pflg)
1521 return (1);
1522
1523 }
1524 #ifdef XPG4
1525 else {
1526 (void) fprintf(stderr, gettext("%s: "
1527 "Insufficient memory to save acl"
1528 " entry\n"), cmd);
1529 if (pflg)
1530 return (1);
1531 }
1532 #endif
1533 }
1534 }
1535
1536 ret = rcopy(source, target);
1537
1538 /*
1539 * Once we created a directory, go ahead and set
1540 * its attributes, e.g. acls and time. The info
1541 * may get overwritten if we continue traversing
1542 * down the tree.
1543 *
1544 * ACL for directory
1545 */
1546 if (pflg || mve) {
1547 if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1548 FMODE(s1save))) == 0)
1549 pret = chg_time(target, s1save);
1550 ret += pret;
1551 if (s1acl_save != NULL) {
1552 if (acl_set(target, s1acl_save) < 0) {
1553 error++;
1554 #ifdef XPG4
1555 if (pflg || mve) {
1556 #else
1557 if (pflg) {
1558 #endif
1559 (void) fprintf(stderr, gettext(
1560 "%s: failed to set acl entries "
1561 "on %s\n"), cmd, target);
1562 if (pflg) {
1563 acl_free(s1acl_save);
1564 s1acl_save = NULL;
1565 ret++;
1566 }
1567 }
1568 /* else: silent and continue */
1569 }
1570 acl_free(s1acl_save);
1571 s1acl_save = NULL;
1572 }
1573 } else if (fixmode != (mode_t)0)
1574 (void) chmod(target, fixmode & MODEBITS);
1575
1576 if (pflg || atflg || mve || saflg) {
1577 attret = copyattributes(source, target);
1578 if (!attrsilent && attret != 0) {
1579 (void) fprintf(stderr, gettext("%s: Failed to preserve"
1580 " extended attributes of directory"
1581 " %s\n"), cmd, source);
1582 } else {
1583 /*
1584 * Otherwise ignore failure.
1585 */
1586 attret = 0;
1587 }
1588 /* Copy extended system attributes */
1589 if (pflg || mve || saflg) {
1590 sattret = copy_sysattr(source, target);
1591 if (sattret != 0) {
1592 (void) fprintf(stderr, gettext(
1593 "%s: Failed to preserve "
1594 "extended system attributes "
1595 "of directory %s\n"), cmd, source);
1596 }
1597 }
1598 }
1599 if (attret != 0 || sattret != 0 || error != 0)
1600 return (1);
1601 return (ret);
1602 }
1603
1604 static int
1605 copyspecial(char *target)
1606 {
1607 int ret = 0;
1608
1609 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1610 (void) fprintf(stderr, gettext(
1611 "cp: cannot create special file %s: "), target);
1612 perror("");
1613 return (1);
1614 }
1615
1616 if (pflg) {
1617 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1618 ret = chg_time(target, s1);
1619 }
1620
1621 return (ret);
1622 }
1623
1624 static int
1625 use_stdin(void)
1626 {
1627 #ifdef XPG4
1628 return (1);
1629 #else
1630 return (isatty(fileno(stdin)));
1631 #endif
1632 }
1633
1634 /* Copy non-system extended attributes */
1635
1636 static int
1637 copyattributes(char *source, char *target)
1638 {
1639 struct dirent *dp;
1640 int error = 0;
1641 int aclerror;
1642 mode_t mode;
1643 int clearflg = 0;
1644 acl_t *xacl = NULL;
1645 acl_t *attrdiracl = NULL;
1646 struct timespec times[2];
1647
1648
1649 if (pathconf(source, _PC_XATTR_EXISTS) != 1)
1650 return (0);
1651
1652 if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1653 if (!attrsilent) {
1654 (void) fprintf(stderr,
1655 gettext(
1656 "%s: cannot preserve extended attributes, "
1657 "operation not supported on file"
1658 " %s\n"), cmd, target);
1659 }
1660 return (1);
1661 }
1662 if (open_source(source) != 0)
1663 return (1);
1664 if (open_target_srctarg_attrdirs(source, target) != 0)
1665 return (1);
1666 if (open_attrdirp(source) != 0)
1667 return (1);
1668
1669 if (pflg || mve) {
1670 if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1671 if (!attrsilent) {
1672 (void) fprintf(stderr,
1673 gettext("%s: failed to set file mode"
1674 " correctly on attribute directory of"
1675 " file %s: "), cmd, target);
1676 perror("");
1677 ++error;
1678 }
1679 }
1680
1681 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1682 if (!attrsilent) {
1683 (void) fprintf(stderr,
1684 gettext("%s: failed to set file"
1685 " ownership correctly on attribute"
1686 " directory of file %s: "), cmd, target);
1687 perror("");
1688 ++error;
1689 }
1690 }
1691 /*
1692 * Now that we are the owner we can update st_ctime by calling
1693 * utimensat.
1694 */
1695 times[0] = attrdir.st_atim;
1696 times[1] = attrdir.st_mtim;
1697 if (utimensat(targetdirfd, ".", times, 0) < 0) {
1698 if (!attrsilent) {
1699 (void) fprintf(stderr,
1700 gettext("%s: cannot set attribute times"
1701 " for %s: "), cmd, target);
1702 perror("");
1703 ++error;
1704 }
1705 }
1706
1707 /*
1708 * Now set owner and group of attribute directory, implies
1709 * changing the ACL of the hidden attribute directory first.
1710 */
1711 if ((aclerror = facl_get(sourcedirfd,
1712 ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1713 if (!attrsilent) {
1714 (void) fprintf(stderr, gettext(
1715 "%s: failed to get acl entries of"
1716 " attribute directory for"
1717 " %s : %s\n"), cmd,
1718 source, acl_strerror(aclerror));
1719 ++error;
1720 }
1721 }
1722
1723 if (attrdiracl) {
1724 if (facl_set(targetdirfd, attrdiracl) != 0) {
1725 if (!attrsilent) {
1726 (void) fprintf(stderr, gettext(
1727 "%s: failed to set acl entries"
1728 " on attribute directory "
1729 "for %s\n"), cmd, target);
1730 ++error;
1731 }
1732 acl_free(attrdiracl);
1733 attrdiracl = NULL;
1734 }
1735 }
1736 }
1737
1738 while ((dp = readdir(srcdirp)) != NULL) {
1739 int ret;
1740
1741 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
1742 continue;
1743 else if (ret > 0) {
1744 ++error;
1745 goto out;
1746 }
1747
1748 if (pflg || mve) {
1749 if ((aclerror = facl_get(srcattrfd,
1750 ACL_NO_TRIVIAL, &xacl)) != 0) {
1751 if (!attrsilent) {
1752 (void) fprintf(stderr, gettext(
1753 "%s: failed to get acl entries of"
1754 " attribute %s for"
1755 " %s: %s"), cmd, dp->d_name,
1756 source, acl_strerror(aclerror));
1757 ++error;
1758 }
1759 }
1760 }
1761
1762 /*
1763 * preserve ACL
1764 */
1765 if ((pflg || mve) && xacl != NULL) {
1766 if ((facl_set(targattrfd, xacl)) < 0) {
1767 if (!attrsilent) {
1768 (void) fprintf(stderr, gettext(
1769 "%s: failed to set acl entries on"
1770 " attribute %s for"
1771 "%s\n"), cmd, dp->d_name, target);
1772 ++error;
1773 }
1774 acl_free(xacl);
1775 xacl = NULL;
1776 }
1777 }
1778
1779 if (writefile(srcattrfd, targattrfd, source, target,
1780 dp->d_name, dp->d_name, &s3, &s4) != 0) {
1781 if (!attrsilent) {
1782 ++error;
1783 }
1784 goto next;
1785 }
1786
1787 if (pflg || mve) {
1788 mode = FMODE(s3);
1789
1790 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
1791 if (!attrsilent) {
1792 (void) fprintf(stderr,
1793 gettext("%s: cannot change"
1794 " owner and group of"
1795 " attribute %s for" " file"
1796 " %s: "), cmd, dp->d_name, target);
1797 perror("");
1798 ++error;
1799 }
1800 if (mode & (S_ISUID | S_ISGID)) {
1801 /* try to clear S_ISUID and S_ISGID */
1802 mode &= ~S_ISUID & ~S_ISGID;
1803 ++clearflg;
1804 }
1805 }
1806 times[0] = s3.st_atim;
1807 times[1] = s3.st_mtim;
1808 if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
1809 if (!attrsilent) {
1810 (void) fprintf(stderr,
1811 gettext("%s: cannot set attribute"
1812 " times for %s: "), cmd, target);
1813 perror("");
1814 ++error;
1815 }
1816 }
1817 if (fchmod(targattrfd, mode) != 0) {
1818 if (clearflg) {
1819 (void) fprintf(stderr, gettext(
1820 "%s: cannot clear S_ISUID and "
1821 "S_ISGID bits in attribute %s"
1822 " for file"
1823 " %s: "), cmd, dp->d_name, target);
1824 } else {
1825 if (!attrsilent) {
1826 (void) fprintf(stderr,
1827 gettext(
1828 "%s: cannot set permissions of attribute"
1829 " %s for %s: "), cmd, dp->d_name, target);
1830 perror("");
1831 ++error;
1832 }
1833 }
1834 }
1835 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
1836 if (!attrsilent) {
1837 (void) fprintf(stderr, gettext(
1838 "%s: failed to set acl entries on"
1839 " attribute %s for"
1840 "%s\n"), cmd, dp->d_name, target);
1841 ++error;
1842 }
1843 acl_free(xacl);
1844 xacl = NULL;
1845 }
1846 }
1847 next:
1848 if (xacl != NULL) {
1849 acl_free(xacl);
1850 xacl = NULL;
1851 }
1852 if (srcattrfd != -1)
1853 (void) close(srcattrfd);
1854 if (targattrfd != -1)
1855 (void) close(targattrfd);
1856 srcattrfd = targattrfd = -1;
1857 }
1858 out:
1859 if (xacl != NULL) {
1860 acl_free(xacl);
1861 xacl = NULL;
1862 }
1863 if (attrdiracl != NULL) {
1864 acl_free(attrdiracl);
1865 attrdiracl = NULL;
1866 }
1867
1868 if (!saflg && !pflg && !mve)
1869 close_all();
1870 return (error == 0 ? 0 : 1);
1871 }
1872
1873 /* Copy extended system attributes from source to target */
1874
1875 static int
1876 copy_sysattr(char *source, char *target)
1877 {
1878 struct dirent *dp;
1879 nvlist_t *response;
1880 int error = 0;
1881 int target_sa_support = 0;
1882
1883 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
1884 return (0);
1885
1886 if (open_source(source) != 0)
1887 return (1);
1888
1889 /*
1890 * Gets non default extended system attributes from the
1891 * source file to copy to the target. The target has
1892 * the defaults set when its created and thus no need
1893 * to copy the defaults.
1894 */
1895 response = sysattr_list(cmd, srcfd, source);
1896
1897 if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
1898 if (response != NULL) {
1899 (void) fprintf(stderr,
1900 gettext(
1901 "%s: cannot preserve extended system "
1902 "attribute, operation not supported on file"
1903 " %s\n"), cmd, target);
1904 error++;
1905 goto out;
1906 }
1907 } else {
1908 target_sa_support = 1;
1909 }
1910
1911 if (target_sa_support) {
1912 if (srcdirp == NULL) {
1913 if (open_target_srctarg_attrdirs(source,
1914 target) != 0) {
1915 error++;
1916 goto out;
1917 }
1918 if (open_attrdirp(source) != 0) {
1919 error++;
1920 goto out;
1921 }
1922 } else {
1923 rewind_attrdir(srcdirp);
1924 }
1925 while ((dp = readdir(srcdirp)) != NULL) {
1926 nvlist_t *res;
1927 int ret;
1928
1929 if ((ret = traverse_attrfile(dp, source, target,
1930 0)) == -1)
1931 continue;
1932 else if (ret > 0) {
1933 ++error;
1934 goto out;
1935 }
1936 /*
1937 * Gets non default extended system attributes from the
1938 * attribute file to copy to the target. The target has
1939 * the defaults set when its created and thus no need
1940 * to copy the defaults.
1941 */
1942 if (dp->d_name != NULL) {
1943 res = sysattr_list(cmd, srcattrfd, dp->d_name);
1944 if (res == NULL)
1945 goto next;
1946
1947 /*
1948 * Copy non default extended system attributes of named
1949 * attribute file.
1950 */
1951 if (fsetattr(targattrfd,
1952 XATTR_VIEW_READWRITE, res) != 0) {
1953 ++error;
1954 (void) fprintf(stderr, gettext("%s: "
1955 "Failed to copy extended system "
1956 "attributes from attribute file "
1957 "%s of %s to %s\n"), cmd,
1958 dp->d_name, source, target);
1959 }
1960 }
1961 next:
1962 if (srcattrfd != -1)
1963 (void) close(srcattrfd);
1964 if (targattrfd != -1)
1965 (void) close(targattrfd);
1966 srcattrfd = targattrfd = -1;
1967 nvlist_free(res);
1968 }
1969 }
1970 /* Copy source file non default extended system attributes to target */
1971 if (target_sa_support && (response != NULL) &&
1972 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
1973 ++error;
1974 (void) fprintf(stderr, gettext("%s: Failed to "
1975 "copy extended system attributes from "
1976 "%s to %s\n"), cmd, source, target);
1977 }
1978 out:
1979 nvlist_free(response);
1980 close_all();
1981 return (error == 0 ? 0 : 1);
1982 }
1983
1984 /* Open the source file */
1985
1986 int
1987 open_source(char *src)
1988 {
1989 int error = 0;
1990
1991 srcfd = -1;
1992 if ((srcfd = open(src, O_RDONLY)) == -1) {
1993 if (pflg && attrsilent) {
1994 error++;
1995 goto out;
1996 }
1997 if (!attrsilent) {
1998 (void) fprintf(stderr,
1999 gettext("%s: cannot open file"
2000 " %s: "), cmd, src);
2001 perror("");
2002 }
2003 ++error;
2004 }
2005 out:
2006 if (error)
2007 close_all();
2008 return (error == 0 ? 0 : 1);
2009 }
2010
2011 /* Open source attribute dir, target and target attribute dir. */
2012
2013 int
2014 open_target_srctarg_attrdirs(char *src, char *targ)
2015 {
2016 int error = 0;
2017
2018 targfd = sourcedirfd = targetdirfd = -1;
2019
2020 if ((targfd = open(targ, O_RDONLY)) == -1) {
2021 if (pflg && attrsilent) {
2022 error++;
2023 goto out;
2024 }
2025 if (!attrsilent) {
2026 (void) fprintf(stderr,
2027 gettext("%s: cannot open file"
2028 " %s: "), cmd, targ);
2029 perror("");
2030 }
2031 ++error;
2032 goto out;
2033 }
2034
2035 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2036 if (pflg && attrsilent) {
2037 error++;
2038 goto out;
2039 }
2040 if (!attrsilent) {
2041 (void) fprintf(stderr,
2042 gettext("%s: cannot open attribute"
2043 " directory for %s: "), cmd, src);
2044 perror("");
2045 }
2046 ++error;
2047 goto out;
2048 }
2049
2050 if (fstat(sourcedirfd, &attrdir) == -1) {
2051 if (pflg && attrsilent) {
2052 error++;
2053 goto out;
2054 }
2055
2056 if (!attrsilent) {
2057 (void) fprintf(stderr,
2058 gettext("%s: could not retrieve stat"
2059 " information for attribute directory"
2060 "of file %s: "), cmd, src);
2061 perror("");
2062 }
2063 ++error;
2064 goto out;
2065 }
2066 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2067 if (pflg && attrsilent) {
2068 error++;
2069 goto out;
2070 }
2071 if (!attrsilent) {
2072 (void) fprintf(stderr,
2073 gettext("%s: cannot open attribute"
2074 " directory for %s: "), cmd, targ);
2075 perror("");
2076 }
2077 ++error;
2078 }
2079 out:
2080 if (error)
2081 close_all();
2082 return (error == 0 ? 0 : 1);
2083 }
2084
2085 int
2086 open_attrdirp(char *source)
2087 {
2088 int tmpfd = -1;
2089 int error = 0;
2090
2091 /*
2092 * dup sourcedirfd for use by fdopendir().
2093 * fdopendir will take ownership of given fd and will close
2094 * it when closedir() is called.
2095 */
2096
2097 if ((tmpfd = dup(sourcedirfd)) == -1) {
2098 if (pflg && attrsilent) {
2099 error++;
2100 goto out;
2101 }
2102 if (!attrsilent) {
2103 (void) fprintf(stderr,
2104 gettext(
2105 "%s: unable to dup attribute directory"
2106 " file descriptor for %s: "), cmd, source);
2107 perror("");
2108 ++error;
2109 }
2110 goto out;
2111 }
2112 if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2113 if (pflg && attrsilent) {
2114 error++;
2115 goto out;
2116 }
2117 if (!attrsilent) {
2118 (void) fprintf(stderr,
2119 gettext("%s: failed to open attribute"
2120 " directory for %s: "), cmd, source);
2121 perror("");
2122 ++error;
2123 }
2124 }
2125 out:
2126 if (error)
2127 close_all();
2128 return (error == 0 ? 0 : 1);
2129 }
2130
2131 /* Skips through ., .., and system attribute 'view' files */
2132 int
2133 traverse_attrfile(struct dirent *dp, char *source, char *target, int first)
2134 {
2135 int error = 0;
2136
2137 srcattrfd = targattrfd = -1;
2138
2139 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2140 (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2141 dp->d_name[2] == '\0') ||
2142 (sysattr_type(dp->d_name) == _RO_SATTR) ||
2143 (sysattr_type(dp->d_name) == _RW_SATTR))
2144 return (-1);
2145
2146 if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2147 O_RDONLY)) == -1) {
2148 if (!attrsilent) {
2149 (void) fprintf(stderr,
2150 gettext("%s: cannot open attribute %s on"
2151 " file %s: "), cmd, dp->d_name, source);
2152 perror("");
2153 ++error;
2154 goto out;
2155 }
2156 }
2157
2158 if (fstat(srcattrfd, &s3) < 0) {
2159 if (!attrsilent) {
2160 (void) fprintf(stderr,
2161 gettext("%s: could not stat attribute"
2162 " %s on file"
2163 " %s: "), cmd, dp->d_name, source);
2164 perror("");
2165 ++error;
2166 }
2167 goto out;
2168 }
2169
2170 if (first) {
2171 (void) unlinkat(targetdirfd, dp->d_name, 0);
2172 if ((targattrfd = openat(targetdirfd, dp->d_name,
2173 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2174 if (!attrsilent) {
2175 (void) fprintf(stderr,
2176 gettext("%s: could not create attribute"
2177 " %s on file %s: "), cmd, dp->d_name,
2178 target);
2179 perror("");
2180 ++error;
2181 }
2182 goto out;
2183 }
2184 } else {
2185 if ((targattrfd = openat(targetdirfd, dp->d_name,
2186 O_RDONLY)) == -1) {
2187 if (!attrsilent) {
2188 (void) fprintf(stderr,
2189 gettext("%s: could not open attribute"
2190 " %s on file %s: "), cmd, dp->d_name,
2191 target);
2192 perror("");
2193 ++error;
2194 }
2195 goto out;
2196 }
2197 }
2198
2199
2200 if (fstat(targattrfd, &s4) < 0) {
2201 if (!attrsilent) {
2202 (void) fprintf(stderr,
2203 gettext("%s: could not stat attribute"
2204 " %s on file"
2205 " %s: "), cmd, dp->d_name, target);
2206 perror("");
2207 ++error;
2208 }
2209 }
2210
2211 out:
2212 if (error) {
2213 if (srcattrfd != -1)
2214 (void) close(srcattrfd);
2215 if (targattrfd != -1)
2216 (void) close(targattrfd);
2217 srcattrfd = targattrfd = -1;
2218 }
2219 return (error == 0 ? 0 :1);
2220 }
2221
2222 void
2223 rewind_attrdir(DIR * sdp)
2224 {
2225 int pwdfd;
2226
2227 pwdfd = open(".", O_RDONLY);
2228 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2229 rewinddir(sdp);
2230 (void) fchdir(pwdfd);
2231 (void) close(pwdfd);
2232 } else {
2233 if (!attrsilent) {
2234 (void) fprintf(stderr, gettext("%s: "
2235 "failed to rewind attribute dir\n"),
2236 cmd);
2237 }
2238 }
2239 }
2240
2241 void
2242 close_all()
2243 {
2244 if (srcattrfd != -1)
2245 (void) close(srcattrfd);
2246 if (targattrfd != -1)
2247 (void) close(targattrfd);
2248 if (sourcedirfd != -1)
2249 (void) close(sourcedirfd);
2250 if (targetdirfd != -1)
2251 (void) close(targetdirfd);
2252 if (srcdirp != NULL) {
2253 (void) closedir(srcdirp);
2254 srcdirp = NULL;
2255 }
2256 if (srcfd != -1)
2257 (void) close(srcfd);
2258 if (targfd != -1)
2259 (void) close(targfd);
2260 }