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