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