xref: /freebsd/bin/cp/cp.c (revision 2d6b33f801d5352b8e078db83f6c90f6fe8291bb)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1988, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * David Hitz of Auspex Systems Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * Cp copies source files to target files.
37  *
38  * The global PATH_T structure "to" always contains the path to the
39  * current target file.  Since fts(3) does not change directories,
40  * this path can be either absolute or dot-relative.
41  *
42  * The basic algorithm is to initialize "to" and use fts(3) to traverse
43  * the file hierarchy rooted in the argument list.  A trivial case is the
44  * case of 'cp file1 file2'.  The more interesting case is the case of
45  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
46  * path (relative to the root of the traversal) is appended to dir (stored
47  * in "to") to form the final target path.
48  */
49 
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 
53 #include <assert.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <fts.h>
58 #include <getopt.h>
59 #include <limits.h>
60 #include <signal.h>
61 #include <stdbool.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 
67 #include "extern.h"
68 
69 static char dot[] = ".";
70 
71 #define END(buf) (buf + sizeof(buf))
72 PATH_T to = { .dir = -1, .end = to.path };
73 bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
74 static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag;
75 volatile sig_atomic_t info;
76 
77 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
78 
79 static int copy(char *[], enum op, int, struct stat *);
80 static void siginfo(int __unused);
81 
82 enum {
83 	SORT_OPT = CHAR_MAX,
84 };
85 
86 static const struct option long_opts[] =
87 {
88 	{ "archive",		no_argument,		NULL,	'a' },
89 	{ "force",		no_argument,		NULL,	'f' },
90 	{ "interactive",	no_argument,		NULL,	'i' },
91 	{ "dereference",	no_argument,		NULL,	'L' },
92 	{ "link",		no_argument,		NULL,	'l' },
93 	{ "no-clobber",		no_argument,		NULL,	'n' },
94 	{ "no-dereference",	no_argument,		NULL,	'P' },
95 	{ "recursive",		no_argument,		NULL,	'R' },
96 	{ "symbolic-link",	no_argument,		NULL,	's' },
97 	{ "verbose",		no_argument,		NULL,	'v' },
98 	{ "one-file-system",	no_argument,		NULL,	'x' },
99 	{ "sort",		no_argument,		NULL,	SORT_OPT },
100 	{ 0 }
101 };
102 
103 int
main(int argc,char * argv[])104 main(int argc, char *argv[])
105 {
106 	struct stat to_stat, tmp_stat;
107 	enum op type;
108 	int ch, fts_options, r;
109 	char *sep, *target;
110 	bool have_trailing_slash = false;
111 
112 	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
113 	while ((ch = getopt_long(argc, argv, "+HLPRafilNnprsvx", long_opts,
114 	    NULL)) != -1)
115 		switch (ch) {
116 		case 'H':
117 			Hflag = true;
118 			Lflag = Pflag = false;
119 			break;
120 		case 'L':
121 			Lflag = true;
122 			Hflag = Pflag = false;
123 			break;
124 		case 'P':
125 			Pflag = true;
126 			Hflag = Lflag = false;
127 			break;
128 		case 'R':
129 			Rflag = true;
130 			break;
131 		case 'a':
132 			pflag = true;
133 			Rflag = true;
134 			Pflag = true;
135 			Hflag = Lflag = false;
136 			break;
137 		case 'f':
138 			fflag = true;
139 			iflag = nflag = false;
140 			break;
141 		case 'i':
142 			iflag = true;
143 			fflag = nflag = false;
144 			break;
145 		case 'l':
146 			lflag = true;
147 			break;
148 		case 'N':
149 			Nflag = true;
150 			break;
151 		case 'n':
152 			nflag = true;
153 			fflag = iflag = false;
154 			break;
155 		case 'p':
156 			pflag = true;
157 			break;
158 		case 'r':
159 			rflag = Lflag = true;
160 			Hflag = Pflag = false;
161 			break;
162 		case 's':
163 			sflag = true;
164 			break;
165 		case 'v':
166 			vflag = true;
167 			break;
168 		case 'x':
169 			fts_options |= FTS_XDEV;
170 			break;
171 		case SORT_OPT:
172 			Sflag = true;
173 			break;
174 		default:
175 			usage();
176 		}
177 	argc -= optind;
178 	argv += optind;
179 
180 	if (argc < 2)
181 		usage();
182 
183 	if (Rflag && rflag)
184 		errx(1, "the -R and -r options may not be specified together");
185 	if (lflag && sflag)
186 		errx(1, "the -l and -s options may not be specified together");
187 	if (rflag)
188 		Rflag = true;
189 	if (Rflag) {
190 		if (Hflag)
191 			fts_options |= FTS_COMFOLLOW;
192 		if (Lflag) {
193 			fts_options &= ~FTS_PHYSICAL;
194 			fts_options |= FTS_LOGICAL;
195 		}
196 	} else if (!Pflag) {
197 		fts_options &= ~FTS_PHYSICAL;
198 		fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
199 	}
200 	(void)signal(SIGINFO, siginfo);
201 
202 	/* Save the target base in "to". */
203 	target = argv[--argc];
204 	if (*target == '\0') {
205 		target = dot;
206 	} else if ((sep = strrchr(target, '/')) != NULL && sep[1] == '\0') {
207 		have_trailing_slash = true;
208 		while (sep > target && *sep == '/')
209 			sep--;
210 		sep[1] = '\0';
211 	}
212 	/*
213 	 * Copy target into to.base, leaving room for a possible separator
214 	 * which will be appended later in the non-FILE_TO_FILE cases.
215 	 */
216 	if (strlcpy(to.base, target, sizeof(to.base) - 1) >=
217 	    sizeof(to.base) - 1)
218 		errc(1, ENAMETOOLONG, "%s", target);
219 
220 	/* Set end of argument list for fts(3). */
221 	argv[argc] = NULL;
222 
223 	/*
224 	 * Cp has two distinct cases:
225 	 *
226 	 * cp [-R] source target
227 	 * cp [-R] source1 ... sourceN directory
228 	 *
229 	 * In both cases, source can be either a file or a directory.
230 	 *
231 	 * In (1), the target becomes a copy of the source. That is, if the
232 	 * source is a file, the target will be a file, and likewise for
233 	 * directories.
234 	 *
235 	 * In (2), the real target is not directory, but "directory/source".
236 	 */
237 	r = stat(to.base, &to_stat);
238 	if (r == -1 && errno != ENOENT)
239 		err(1, "%s", target);
240 	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
241 		/*
242 		 * Case (1).  Target is not a directory.
243 		 */
244 		if (argc > 1)
245 			errc(1, ENOTDIR, "%s", target);
246 
247 		/*
248 		 * Need to detect the case:
249 		 *	cp -R dir foo
250 		 * Where dir is a directory and foo does not exist, where
251 		 * we want pathname concatenations turned on but not for
252 		 * the initial mkdir().
253 		 */
254 		if (r == -1) {
255 			if (Rflag && (Lflag || Hflag))
256 				stat(*argv, &tmp_stat);
257 			else
258 				lstat(*argv, &tmp_stat);
259 
260 			if (S_ISDIR(tmp_stat.st_mode) && Rflag)
261 				type = DIR_TO_DNE;
262 			else
263 				type = FILE_TO_FILE;
264 		} else
265 			type = FILE_TO_FILE;
266 
267 		if (have_trailing_slash && type == FILE_TO_FILE) {
268 			if (r == -1)
269 				errc(1, ENOENT, "%s", target);
270 			else
271 				errc(1, ENOTDIR, "%s", target);
272 		}
273 	} else {
274 		/*
275 		 * Case (2).  Target is a directory.
276 		 */
277 		type = FILE_TO_DIR;
278 	}
279 
280 	/*
281 	 * For DIR_TO_DNE, we could provide copy() with the to_stat we've
282 	 * already allocated on the stack here that isn't being used for
283 	 * anything.  Not doing so, though, simplifies later logic a little bit
284 	 * as we need to skip checking root_stat on the first iteration and
285 	 * ensure that we set it with the first mkdir().
286 	 */
287 	exit (copy(argv, type, fts_options, (type == DIR_TO_DNE ? NULL :
288 	    &to_stat)));
289 }
290 
291 static int
ftscmp(const FTSENT * const * a,const FTSENT * const * b)292 ftscmp(const FTSENT * const *a, const FTSENT * const *b)
293 {
294 	return (strcmp((*a)->fts_name, (*b)->fts_name));
295 }
296 
297 static int
copy(char * argv[],enum op type,int fts_options,struct stat * root_stat)298 copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
299 {
300 	char rootname[NAME_MAX];
301 	struct stat created_root_stat, to_stat, *curr_stat;
302 	FTS *ftsp;
303 	FTSENT *curr;
304 	char *recpath = NULL, *sep;
305 	int atflags, dne, badcp, len, level, rval;
306 	mode_t mask, mode;
307 	bool beneath = Rflag && type != FILE_TO_FILE;
308 
309 	/*
310 	 * Keep an inverted copy of the umask, for use in correcting
311 	 * permissions on created directories when not using -p.
312 	 */
313 	mask = ~umask(0777);
314 	umask(~mask);
315 
316 	if (type == FILE_TO_FILE) {
317 		to.dir = AT_FDCWD;
318 		to.end = to.path + strlcpy(to.path, to.base, sizeof(to.path));
319 		to.base[0] = '\0';
320 	} else if (type == FILE_TO_DIR) {
321 		to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
322 		if (to.dir < 0)
323 			err(1, "%s", to.base);
324 		/*
325 		 * We have previously made sure there is room for this.
326 		 */
327 		if (strcmp(to.base, "/") != 0) {
328 			sep = strchr(to.base, '\0');
329 			sep[0] = '/';
330 			sep[1] = '\0';
331 		}
332 	} else {
333 		/*
334 		 * We will create the destination directory imminently.
335 		 */
336 		to.dir = -1;
337 	}
338 
339 	level = FTS_ROOTLEVEL;
340 	if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL)
341 		err(1, "fts_open");
342 	for (badcp = rval = 0;
343 	     (curr = fts_read(ftsp)) != NULL;
344 	     badcp = 0, *to.end = '\0') {
345 		curr_stat = curr->fts_statp;
346 		switch (curr->fts_info) {
347 		case FTS_NS:
348 		case FTS_DNR:
349 		case FTS_ERR:
350 			if (level > curr->fts_level) {
351 				/* leaving a directory; remove its name from to.path */
352 				if (type == DIR_TO_DNE &&
353 				    curr->fts_level == FTS_ROOTLEVEL) {
354 					/* this is actually our created root */
355 				} else {
356 					while (to.end > to.path && *to.end != '/')
357 						to.end--;
358 					assert(strcmp(to.end + (*to.end == '/'),
359 					    curr->fts_name) == 0);
360 					*to.end = '\0';
361 				}
362 				level--;
363 			}
364 			warnc(curr->fts_errno, "%s", curr->fts_path);
365 			badcp = rval = 1;
366 			continue;
367 		case FTS_DC:			/* Warn, continue. */
368 			warnx("%s: directory causes a cycle", curr->fts_path);
369 			badcp = rval = 1;
370 			continue;
371 		case FTS_D:
372 			/*
373 			 * Stash the root basename off for detecting
374 			 * recursion later.
375 			 *
376 			 * This will be essential if the root is a symlink
377 			 * and we're rolling with -L or -H.  The later
378 			 * bits will need this bit in particular.
379 			 */
380 			if (curr->fts_level == FTS_ROOTLEVEL) {
381 				strlcpy(rootname, curr->fts_name,
382 				    sizeof(rootname));
383 			}
384 			/* we must have a destination! */
385 			if (type == DIR_TO_DNE &&
386 			    curr->fts_level == FTS_ROOTLEVEL) {
387 				assert(to.dir < 0);
388 				assert(root_stat == NULL);
389 				mode = curr_stat->st_mode | S_IRWXU;
390 				/*
391 				 * Will our umask prevent us from entering
392 				 * the directory after we create it?
393 				 */
394 				if (~mask & S_IRWXU)
395 					umask(~mask & ~S_IRWXU);
396 				if (mkdir(to.base, mode) != 0) {
397 					warn("%s", to.base);
398 					fts_set(ftsp, curr, FTS_SKIP);
399 					badcp = rval = 1;
400 					if (~mask & S_IRWXU)
401 						umask(~mask);
402 					continue;
403 				}
404 				to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
405 				if (to.dir < 0) {
406 					warn("%s", to.base);
407 					(void)rmdir(to.base);
408 					fts_set(ftsp, curr, FTS_SKIP);
409 					badcp = rval = 1;
410 					if (~mask & S_IRWXU)
411 						umask(~mask);
412 					continue;
413 				}
414 				if (fstat(to.dir, &created_root_stat) != 0) {
415 					warn("%s", to.base);
416 					(void)close(to.dir);
417 					(void)rmdir(to.base);
418 					fts_set(ftsp, curr, FTS_SKIP);
419 					to.dir = -1;
420 					badcp = rval = 1;
421 					if (~mask & S_IRWXU)
422 						umask(~mask);
423 					continue;
424 				}
425 				if (~mask & S_IRWXU)
426 					umask(~mask);
427 				root_stat = &created_root_stat;
428 				curr->fts_number = 1;
429 				/*
430 				 * We have previously made sure there is
431 				 * room for this.
432 				 */
433 				sep = strchr(to.base, '\0');
434 				sep[0] = '/';
435 				sep[1] = '\0';
436 			} else {
437 				/* entering a directory; append its name to to.path */
438 				len = snprintf(to.end, END(to.path) - to.end, "%s%s",
439 				    to.end > to.path ? "/" : "", curr->fts_name);
440 				if (to.end + len >= END(to.path)) {
441 					*to.end = '\0';
442 					warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
443 					    to.path, to.end > to.path ? "/" : "",
444 					    curr->fts_name);
445 					fts_set(ftsp, curr, FTS_SKIP);
446 					badcp = rval = 1;
447 					continue;
448 				}
449 				to.end += len;
450 			}
451 			level++;
452 			/*
453 			 * We're on the verge of recursing on ourselves.
454 			 * Either we need to stop right here (we knowingly
455 			 * just created it), or we will in an immediate
456 			 * descendant.  Record the path of the immediate
457 			 * descendant to make our lives a little less
458 			 * complicated looking.
459 			 */
460 			if (type != FILE_TO_FILE &&
461 			    root_stat->st_dev == curr_stat->st_dev &&
462 			    root_stat->st_ino == curr_stat->st_ino) {
463 				assert(recpath == NULL);
464 				if (root_stat == &created_root_stat) {
465 					/*
466 					 * This directory didn't exist
467 					 * when we started, we created it
468 					 * as part of traversal.  Stop
469 					 * right here before we do
470 					 * something silly.
471 					 */
472 					fts_set(ftsp, curr, FTS_SKIP);
473 					continue;
474 				}
475 				if (asprintf(&recpath, "%s/%s", to.path,
476 				    rootname) < 0) {
477 					warnc(ENOMEM, NULL);
478 					fts_set(ftsp, curr, FTS_SKIP);
479 					badcp = rval = 1;
480 					continue;
481 				}
482 			}
483 			if (recpath != NULL &&
484 			    strcmp(recpath, to.path) == 0) {
485 				fts_set(ftsp, curr, FTS_SKIP);
486 				continue;
487 			}
488 			break;
489 		case FTS_DP:
490 			/*
491 			 * We are nearly finished with this directory.  If we
492 			 * didn't actually copy it, or otherwise don't need to
493 			 * change its attributes, then we are done.
494 			 *
495 			 * If -p is in effect, set all the attributes.
496 			 * Otherwise, set the correct permissions, limited
497 			 * by the umask.  Optimise by avoiding a chmod()
498 			 * if possible (which is usually the case if we
499 			 * made the directory).  Note that mkdir() does not
500 			 * honour setuid, setgid and sticky bits, but we
501 			 * normally want to preserve them on directories.
502 			 */
503 			if (curr->fts_number && pflag) {
504 				int fd = *to.path ? -1 : to.dir;
505 				if (setfile(curr_stat, fd, true))
506 					rval = 1;
507 				if (preserve_dir_acls(curr->fts_accpath,
508 				    to.path) != 0)
509 					rval = 1;
510 			} else if (curr->fts_number) {
511 				const char *path = *to.path ? to.path : dot;
512 				mode = curr_stat->st_mode;
513 				if (fchmodat(to.dir, path, mode & mask, 0) != 0) {
514 					warn("chmod: %s%s", to.base, to.path);
515 					rval = 1;
516 				}
517 			}
518 			if (level > curr->fts_level) {
519 				/* leaving a directory; remove its name from to.path */
520 				if (type == DIR_TO_DNE &&
521 				    curr->fts_level == FTS_ROOTLEVEL) {
522 					/* this is actually our created root */
523 				} else {
524 					while (to.end > to.path && *to.end != '/')
525 						to.end--;
526 					assert(strcmp(to.end + (*to.end == '/'),
527 					    curr->fts_name) == 0);
528 					*to.end = '\0';
529 				}
530 				level--;
531 			}
532 			continue;
533 		default:
534 			/* something else: append its name to to.path */
535 			if (type == FILE_TO_FILE)
536 				break;
537 			len = snprintf(to.end, END(to.path) - to.end, "%s%s",
538 			    to.end > to.path ? "/" : "", curr->fts_name);
539 			if (to.end + len >= END(to.path)) {
540 				*to.end = '\0';
541 				warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
542 				    to.path, to.end > to.path ? "/" : "",
543 				    curr->fts_name);
544 				badcp = rval = 1;
545 				continue;
546 			}
547 			/* intentionally do not update to.end */
548 			break;
549 		}
550 
551 		/* Not an error but need to remember it happened. */
552 		if (to.path[0] == '\0') {
553 			/*
554 			 * This can happen in two cases:
555 			 * - DIR_TO_DNE; we created the directory and
556 			 *   populated root_stat earlier.
557 			 * - FILE_TO_DIR if a source has a trailing slash;
558 			 *   the caller populated root_stat.
559 			 */
560 			dne = false;
561 			to_stat = *root_stat;
562 		} else {
563 			atflags = beneath ? AT_RESOLVE_BENEATH : 0;
564 			if (curr->fts_info == FTS_D || curr->fts_info == FTS_SL)
565 				atflags |= AT_SYMLINK_NOFOLLOW;
566 			dne = fstatat(to.dir, to.path, &to_stat, atflags) != 0;
567 		}
568 
569 		/* Check if source and destination are identical. */
570 		if (!dne &&
571 		    to_stat.st_dev == curr_stat->st_dev &&
572 		    to_stat.st_ino == curr_stat->st_ino) {
573 			warnx("%s%s and %s are identical (not copied).",
574 			    to.base, to.path, curr->fts_path);
575 			badcp = rval = 1;
576 			if (S_ISDIR(curr_stat->st_mode))
577 				fts_set(ftsp, curr, FTS_SKIP);
578 			continue;
579 		}
580 
581 		switch (curr_stat->st_mode & S_IFMT) {
582 		case S_IFLNK:
583 			if ((fts_options & FTS_LOGICAL) ||
584 			    ((fts_options & FTS_COMFOLLOW) &&
585 			    curr->fts_level == 0)) {
586 				/*
587 				 * We asked FTS to follow links but got
588 				 * here anyway, which means the target is
589 				 * nonexistent or inaccessible.  Let
590 				 * copy_file() deal with the error.
591 				 */
592 				if (copy_file(curr, dne, beneath))
593 					badcp = rval = 1;
594 			} else {
595 				/* Copy the link. */
596 				if (copy_link(curr, dne, beneath))
597 					badcp = rval = 1;
598 			}
599 			break;
600 		case S_IFDIR:
601 			if (!Rflag) {
602 				warnx("%s is a directory (not copied).",
603 				    curr->fts_path);
604 				fts_set(ftsp, curr, FTS_SKIP);
605 				badcp = rval = 1;
606 				break;
607 			}
608 			/*
609 			 * If the directory doesn't exist, create the new
610 			 * one with the from file mode plus owner RWX bits,
611 			 * modified by the umask.  Trade-off between being
612 			 * able to write the directory (if from directory is
613 			 * 555) and not causing a permissions race.  If the
614 			 * umask blocks owner writes, we fail.
615 			 */
616 			if (dne) {
617 				mode = curr_stat->st_mode | S_IRWXU;
618 				/*
619 				 * Will our umask prevent us from entering
620 				 * the directory after we create it?
621 				 */
622 				if (~mask & S_IRWXU)
623 					umask(~mask & ~S_IRWXU);
624 				if (mkdirat(to.dir, to.path, mode) != 0) {
625 					warn("%s%s", to.base, to.path);
626 					fts_set(ftsp, curr, FTS_SKIP);
627 					badcp = rval = 1;
628 					if (~mask & S_IRWXU)
629 						umask(~mask);
630 					break;
631 				}
632 				if (~mask & S_IRWXU)
633 					umask(~mask);
634 			} else if (!S_ISDIR(to_stat.st_mode)) {
635 				warnc(ENOTDIR, "%s%s", to.base, to.path);
636 				fts_set(ftsp, curr, FTS_SKIP);
637 				badcp = rval = 1;
638 				break;
639 			}
640 			/*
641 			 * Arrange to correct directory attributes later
642 			 * (in the post-order phase) if this is a new
643 			 * directory, or if the -p flag is in effect.
644 			 * Note that fts_number may already be set if this
645 			 * is the newly created destination directory.
646 			 */
647 			curr->fts_number |= pflag || dne;
648 			break;
649 		case S_IFBLK:
650 		case S_IFCHR:
651 			if (Rflag && !sflag) {
652 				if (copy_special(curr_stat, dne, beneath))
653 					badcp = rval = 1;
654 			} else {
655 				if (copy_file(curr, dne, beneath))
656 					badcp = rval = 1;
657 			}
658 			break;
659 		case S_IFSOCK:
660 			warnx("%s is a socket (not copied).",
661 			    curr->fts_path);
662 			break;
663 		case S_IFIFO:
664 			if (Rflag && !sflag) {
665 				if (copy_fifo(curr_stat, dne, beneath))
666 					badcp = rval = 1;
667 			} else {
668 				if (copy_file(curr, dne, beneath))
669 					badcp = rval = 1;
670 			}
671 			break;
672 		default:
673 			if (copy_file(curr, dne, beneath))
674 				badcp = rval = 1;
675 			break;
676 		}
677 		if (vflag && !badcp)
678 			(void)printf("%s -> %s%s\n", curr->fts_path, to.base, to.path);
679 	}
680 	assert(level == FTS_ROOTLEVEL);
681 	if (errno)
682 		err(1, "fts_read");
683 	(void)fts_close(ftsp);
684 	if (to.dir != AT_FDCWD && to.dir >= 0)
685 		(void)close(to.dir);
686 	free(recpath);
687 	return (rval);
688 }
689 
690 static void
siginfo(int sig __unused)691 siginfo(int sig __unused)
692 {
693 
694 	info = 1;
695 }
696