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