xref: /freebsd/bin/cp/cp.c (revision 2d6b33f801d5352b8e078db83f6c90f6fe8291bb)
19ddb49cbSWarner Losh /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
44b88c807SRodney W. Grimes  * Copyright (c) 1988, 1993, 1994
54b88c807SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
64b88c807SRodney W. Grimes  *
74b88c807SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
84b88c807SRodney W. Grimes  * David Hitz of Auspex Systems Inc.
94b88c807SRodney W. Grimes  *
104b88c807SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
114b88c807SRodney W. Grimes  * modification, are permitted provided that the following conditions
124b88c807SRodney W. Grimes  * are met:
134b88c807SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
144b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
154b88c807SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
164b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
174b88c807SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
194b88c807SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
204b88c807SRodney W. Grimes  *    without specific prior written permission.
214b88c807SRodney W. Grimes  *
224b88c807SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
234b88c807SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
244b88c807SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254b88c807SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
264b88c807SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
274b88c807SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
284b88c807SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
294b88c807SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
304b88c807SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
314b88c807SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
324b88c807SRodney W. Grimes  * SUCH DAMAGE.
334b88c807SRodney W. Grimes  */
344b88c807SRodney W. Grimes 
354b88c807SRodney W. Grimes /*
364b88c807SRodney W. Grimes  * Cp copies source files to target files.
374b88c807SRodney W. Grimes  *
384b88c807SRodney W. Grimes  * The global PATH_T structure "to" always contains the path to the
394b88c807SRodney W. Grimes  * current target file.  Since fts(3) does not change directories,
40890acb95SSteve Price  * this path can be either absolute or dot-relative.
414b88c807SRodney W. Grimes  *
424b88c807SRodney W. Grimes  * The basic algorithm is to initialize "to" and use fts(3) to traverse
434b88c807SRodney W. Grimes  * the file hierarchy rooted in the argument list.  A trivial case is the
444b88c807SRodney W. Grimes  * case of 'cp file1 file2'.  The more interesting case is the case of
454b88c807SRodney W. Grimes  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
464b88c807SRodney W. Grimes  * path (relative to the root of the traversal) is appended to dir (stored
474b88c807SRodney W. Grimes  * in "to") to form the final target path.
484b88c807SRodney W. Grimes  */
494b88c807SRodney W. Grimes 
5082fdc5e6SBruce Evans #include <sys/types.h>
514b88c807SRodney W. Grimes #include <sys/stat.h>
524b88c807SRodney W. Grimes 
53848263aaSKyle Evans #include <assert.h>
544b88c807SRodney W. Grimes #include <err.h>
554b88c807SRodney W. Grimes #include <errno.h>
5682fc0d09SDag-Erling Smørgrav #include <fcntl.h>
574b88c807SRodney W. Grimes #include <fts.h>
58c3efa16dSDag-Erling Smørgrav #include <getopt.h>
5934f9c106SWarner Losh #include <limits.h>
6082fdc5e6SBruce Evans #include <signal.h>
6182fc0d09SDag-Erling Smørgrav #include <stdbool.h>
624506e907SMichael Haro #include <stdio.h>
6326f6b0fbSDag-Erling Smørgrav #include <stdlib.h>
644b88c807SRodney W. Grimes #include <string.h>
654b88c807SRodney W. Grimes #include <unistd.h>
664b88c807SRodney W. Grimes 
674b88c807SRodney W. Grimes #include "extern.h"
684b88c807SRodney W. Grimes 
6982fc0d09SDag-Erling Smørgrav static char dot[] = ".";
704b88c807SRodney W. Grimes 
7182fc0d09SDag-Erling Smørgrav #define END(buf) (buf + sizeof(buf))
7282fc0d09SDag-Erling Smørgrav PATH_T to = { .dir = -1, .end = to.path };
73c3efa16dSDag-Erling Smørgrav bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
74*2d6b33f8SDag-Erling Smørgrav static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag;
75947193d9SMatthew N. Dodd volatile sig_atomic_t info;
7600d321a2SMatthew N. Dodd 
774b88c807SRodney W. Grimes enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
784b88c807SRodney W. Grimes 
79848263aaSKyle Evans static int copy(char *[], enum op, int, struct stat *);
804901f51bSBruce Evans static void siginfo(int __unused);
814b88c807SRodney W. Grimes 
82c3efa16dSDag-Erling Smørgrav enum {
83c3efa16dSDag-Erling Smørgrav 	SORT_OPT = CHAR_MAX,
84c3efa16dSDag-Erling Smørgrav };
85c3efa16dSDag-Erling Smørgrav 
86c3efa16dSDag-Erling Smørgrav static const struct option long_opts[] =
87c3efa16dSDag-Erling Smørgrav {
88c3efa16dSDag-Erling Smørgrav 	{ "archive",		no_argument,		NULL,	'a' },
89c3efa16dSDag-Erling Smørgrav 	{ "force",		no_argument,		NULL,	'f' },
90c3efa16dSDag-Erling Smørgrav 	{ "interactive",	no_argument,		NULL,	'i' },
91c3efa16dSDag-Erling Smørgrav 	{ "dereference",	no_argument,		NULL,	'L' },
92c3efa16dSDag-Erling Smørgrav 	{ "link",		no_argument,		NULL,	'l' },
93c3efa16dSDag-Erling Smørgrav 	{ "no-clobber",		no_argument,		NULL,	'n' },
94c3efa16dSDag-Erling Smørgrav 	{ "no-dereference",	no_argument,		NULL,	'P' },
95c3efa16dSDag-Erling Smørgrav 	{ "recursive",		no_argument,		NULL,	'R' },
96c3efa16dSDag-Erling Smørgrav 	{ "symbolic-link",	no_argument,		NULL,	's' },
97c3efa16dSDag-Erling Smørgrav 	{ "verbose",		no_argument,		NULL,	'v' },
98c3efa16dSDag-Erling Smørgrav 	{ "one-file-system",	no_argument,		NULL,	'x' },
99*2d6b33f8SDag-Erling Smørgrav 	{ "sort",		no_argument,		NULL,	SORT_OPT },
100c3efa16dSDag-Erling Smørgrav 	{ 0 }
101c3efa16dSDag-Erling Smørgrav };
102c3efa16dSDag-Erling Smørgrav 
1034b88c807SRodney W. Grimes int
main(int argc,char * argv[])1045dce647cSWarner Losh main(int argc, char *argv[])
1054b88c807SRodney W. Grimes {
1064b88c807SRodney W. Grimes 	struct stat to_stat, tmp_stat;
1074b88c807SRodney W. Grimes 	enum op type;
10882fc0d09SDag-Erling Smørgrav 	int ch, fts_options, r;
10982fc0d09SDag-Erling Smørgrav 	char *sep, *target;
11082fc0d09SDag-Erling Smørgrav 	bool have_trailing_slash = false;
1114b88c807SRodney W. Grimes 
112f815125fSGavin Atkinson 	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
113c3efa16dSDag-Erling Smørgrav 	while ((ch = getopt_long(argc, argv, "+HLPRafilNnprsvx", long_opts,
114c3efa16dSDag-Erling Smørgrav 	    NULL)) != -1)
1154b88c807SRodney W. Grimes 		switch (ch) {
1164b88c807SRodney W. Grimes 		case 'H':
117c3efa16dSDag-Erling Smørgrav 			Hflag = true;
118c3efa16dSDag-Erling Smørgrav 			Lflag = Pflag = false;
1194b88c807SRodney W. Grimes 			break;
1204b88c807SRodney W. Grimes 		case 'L':
121c3efa16dSDag-Erling Smørgrav 			Lflag = true;
122c3efa16dSDag-Erling Smørgrav 			Hflag = Pflag = false;
1234b88c807SRodney W. Grimes 			break;
1244b88c807SRodney W. Grimes 		case 'P':
125c3efa16dSDag-Erling Smørgrav 			Pflag = true;
126c3efa16dSDag-Erling Smørgrav 			Hflag = Lflag = false;
1274b88c807SRodney W. Grimes 			break;
1284b88c807SRodney W. Grimes 		case 'R':
129c3efa16dSDag-Erling Smørgrav 			Rflag = true;
1304b88c807SRodney W. Grimes 			break;
131d140946cSJohn Baldwin 		case 'a':
132c3efa16dSDag-Erling Smørgrav 			pflag = true;
133c3efa16dSDag-Erling Smørgrav 			Rflag = true;
134c3efa16dSDag-Erling Smørgrav 			Pflag = true;
135c3efa16dSDag-Erling Smørgrav 			Hflag = Lflag = false;
136d140946cSJohn Baldwin 			break;
1374b88c807SRodney W. Grimes 		case 'f':
138c3efa16dSDag-Erling Smørgrav 			fflag = true;
139c3efa16dSDag-Erling Smørgrav 			iflag = nflag = false;
1404b88c807SRodney W. Grimes 			break;
1414b88c807SRodney W. Grimes 		case 'i':
142c3efa16dSDag-Erling Smørgrav 			iflag = true;
143c3efa16dSDag-Erling Smørgrav 			fflag = nflag = false;
144786c276fSJohan Karlsson 			break;
14564941e9dSRuslan Ermilov 		case 'l':
146c3efa16dSDag-Erling Smørgrav 			lflag = true;
14764941e9dSRuslan Ermilov 			break;
1480f4467ceSDag-Erling Smørgrav 		case 'N':
149c3efa16dSDag-Erling Smørgrav 			Nflag = true;
1500f4467ceSDag-Erling Smørgrav 			break;
151786c276fSJohan Karlsson 		case 'n':
152c3efa16dSDag-Erling Smørgrav 			nflag = true;
153c3efa16dSDag-Erling Smørgrav 			fflag = iflag = false;
1544b88c807SRodney W. Grimes 			break;
1554b88c807SRodney W. Grimes 		case 'p':
156c3efa16dSDag-Erling Smørgrav 			pflag = true;
1574b88c807SRodney W. Grimes 			break;
1584b88c807SRodney W. Grimes 		case 'r':
159c3efa16dSDag-Erling Smørgrav 			rflag = Lflag = true;
160c3efa16dSDag-Erling Smørgrav 			Hflag = Pflag = false;
1614b88c807SRodney W. Grimes 			break;
1627ea2450fSBryan Drewery 		case 's':
163c3efa16dSDag-Erling Smørgrav 			sflag = true;
1647ea2450fSBryan Drewery 			break;
1654506e907SMichael Haro 		case 'v':
166c3efa16dSDag-Erling Smørgrav 			vflag = true;
1674506e907SMichael Haro 			break;
168f815125fSGavin Atkinson 		case 'x':
169f815125fSGavin Atkinson 			fts_options |= FTS_XDEV;
170f815125fSGavin Atkinson 			break;
171*2d6b33f8SDag-Erling Smørgrav 		case SORT_OPT:
172*2d6b33f8SDag-Erling Smørgrav 			Sflag = true;
173*2d6b33f8SDag-Erling Smørgrav 			break;
1744b88c807SRodney W. Grimes 		default:
1754b88c807SRodney W. Grimes 			usage();
1764b88c807SRodney W. Grimes 		}
1774b88c807SRodney W. Grimes 	argc -= optind;
1784b88c807SRodney W. Grimes 	argv += optind;
1794b88c807SRodney W. Grimes 
1804b88c807SRodney W. Grimes 	if (argc < 2)
1814b88c807SRodney W. Grimes 		usage();
1824b88c807SRodney W. Grimes 
18343d8847eSTom Rhodes 	if (Rflag && rflag)
18443d8847eSTom Rhodes 		errx(1, "the -R and -r options may not be specified together");
1857ea2450fSBryan Drewery 	if (lflag && sflag)
1867ea2450fSBryan Drewery 		errx(1, "the -l and -s options may not be specified together");
18743d8847eSTom Rhodes 	if (rflag)
188c3efa16dSDag-Erling Smørgrav 		Rflag = true;
1894b88c807SRodney W. Grimes 	if (Rflag) {
1904b88c807SRodney W. Grimes 		if (Hflag)
1914b88c807SRodney W. Grimes 			fts_options |= FTS_COMFOLLOW;
1924b88c807SRodney W. Grimes 		if (Lflag) {
1934b88c807SRodney W. Grimes 			fts_options &= ~FTS_PHYSICAL;
1944b88c807SRodney W. Grimes 			fts_options |= FTS_LOGICAL;
1954b88c807SRodney W. Grimes 		}
19697e13037SCameron Katri 	} else if (!Pflag) {
1974b88c807SRodney W. Grimes 		fts_options &= ~FTS_PHYSICAL;
198f9dc2a8bSTim J. Robbins 		fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
1994b88c807SRodney W. Grimes 	}
20000d321a2SMatthew N. Dodd 	(void)signal(SIGINFO, siginfo);
2014b88c807SRodney W. Grimes 
2024b88c807SRodney W. Grimes 	/* Save the target base in "to". */
2034b88c807SRodney W. Grimes 	target = argv[--argc];
20482fc0d09SDag-Erling Smørgrav 	if (*target == '\0') {
20582fc0d09SDag-Erling Smørgrav 		target = dot;
20682fc0d09SDag-Erling Smørgrav 	} else if ((sep = strrchr(target, '/')) != NULL && sep[1] == '\0') {
20782fc0d09SDag-Erling Smørgrav 		have_trailing_slash = true;
208537fbf70SDag-Erling Smørgrav 		while (sep > target && *sep == '/')
20982fc0d09SDag-Erling Smørgrav 			sep--;
210537fbf70SDag-Erling Smørgrav 		sep[1] = '\0';
2114b88c807SRodney W. Grimes 	}
212b3fe9014SDag-Erling Smørgrav 	/*
213b3fe9014SDag-Erling Smørgrav 	 * Copy target into to.base, leaving room for a possible separator
214b3fe9014SDag-Erling Smørgrav 	 * which will be appended later in the non-FILE_TO_FILE cases.
215b3fe9014SDag-Erling Smørgrav 	 */
216b3fe9014SDag-Erling Smørgrav 	if (strlcpy(to.base, target, sizeof(to.base) - 1) >=
217b3fe9014SDag-Erling Smørgrav 	    sizeof(to.base) - 1)
21882fc0d09SDag-Erling Smørgrav 		errc(1, ENAMETOOLONG, "%s", target);
2194b88c807SRodney W. Grimes 
2204b88c807SRodney W. Grimes 	/* Set end of argument list for fts(3). */
2214b88c807SRodney W. Grimes 	argv[argc] = NULL;
2224b88c807SRodney W. Grimes 
2234b88c807SRodney W. Grimes 	/*
2244b88c807SRodney W. Grimes 	 * Cp has two distinct cases:
2254b88c807SRodney W. Grimes 	 *
2264b88c807SRodney W. Grimes 	 * cp [-R] source target
2274b88c807SRodney W. Grimes 	 * cp [-R] source1 ... sourceN directory
2284b88c807SRodney W. Grimes 	 *
2294b88c807SRodney W. Grimes 	 * In both cases, source can be either a file or a directory.
2304b88c807SRodney W. Grimes 	 *
2314b88c807SRodney W. Grimes 	 * In (1), the target becomes a copy of the source. That is, if the
2324b88c807SRodney W. Grimes 	 * source is a file, the target will be a file, and likewise for
2334b88c807SRodney W. Grimes 	 * directories.
2344b88c807SRodney W. Grimes 	 *
2354b88c807SRodney W. Grimes 	 * In (2), the real target is not directory, but "directory/source".
2364b88c807SRodney W. Grimes 	 */
23782fc0d09SDag-Erling Smørgrav 	r = stat(to.base, &to_stat);
2384b88c807SRodney W. Grimes 	if (r == -1 && errno != ENOENT)
23982fc0d09SDag-Erling Smørgrav 		err(1, "%s", target);
2404b88c807SRodney W. Grimes 	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
2414b88c807SRodney W. Grimes 		/*
2424b88c807SRodney W. Grimes 		 * Case (1).  Target is not a directory.
2434b88c807SRodney W. Grimes 		 */
244a76b9b69SEdwin Groothuis 		if (argc > 1)
24582fc0d09SDag-Erling Smørgrav 			errc(1, ENOTDIR, "%s", target);
246a76b9b69SEdwin Groothuis 
2474b88c807SRodney W. Grimes 		/*
2484b88c807SRodney W. Grimes 		 * Need to detect the case:
2494b88c807SRodney W. Grimes 		 *	cp -R dir foo
2504b88c807SRodney W. Grimes 		 * Where dir is a directory and foo does not exist, where
2514b88c807SRodney W. Grimes 		 * we want pathname concatenations turned on but not for
2524b88c807SRodney W. Grimes 		 * the initial mkdir().
2534b88c807SRodney W. Grimes 		 */
2544b88c807SRodney W. Grimes 		if (r == -1) {
255ba3fe6d1STom Rhodes 			if (Rflag && (Lflag || Hflag))
2564b88c807SRodney W. Grimes 				stat(*argv, &tmp_stat);
2574b88c807SRodney W. Grimes 			else
2584b88c807SRodney W. Grimes 				lstat(*argv, &tmp_stat);
2594b88c807SRodney W. Grimes 
26043d8847eSTom Rhodes 			if (S_ISDIR(tmp_stat.st_mode) && Rflag)
2614b88c807SRodney W. Grimes 				type = DIR_TO_DNE;
2624b88c807SRodney W. Grimes 			else
2634b88c807SRodney W. Grimes 				type = FILE_TO_FILE;
2644b88c807SRodney W. Grimes 		} else
2654b88c807SRodney W. Grimes 			type = FILE_TO_FILE;
26627d3ae35SAndrey A. Chernov 
26727d3ae35SAndrey A. Chernov 		if (have_trailing_slash && type == FILE_TO_FILE) {
2687622e2d2SDag-Erling Smørgrav 			if (r == -1)
26982fc0d09SDag-Erling Smørgrav 				errc(1, ENOENT, "%s", target);
2707622e2d2SDag-Erling Smørgrav 			else
27182fc0d09SDag-Erling Smørgrav 				errc(1, ENOTDIR, "%s", target);
27227d3ae35SAndrey A. Chernov 		}
2737622e2d2SDag-Erling Smørgrav 	} else {
2744b88c807SRodney W. Grimes 		/*
2754b88c807SRodney W. Grimes 		 * Case (2).  Target is a directory.
2764b88c807SRodney W. Grimes 		 */
2774b88c807SRodney W. Grimes 		type = FILE_TO_DIR;
2787622e2d2SDag-Erling Smørgrav 	}
2794b88c807SRodney W. Grimes 
280848263aaSKyle Evans 	/*
281848263aaSKyle Evans 	 * For DIR_TO_DNE, we could provide copy() with the to_stat we've
282848263aaSKyle Evans 	 * already allocated on the stack here that isn't being used for
283848263aaSKyle Evans 	 * anything.  Not doing so, though, simplifies later logic a little bit
284848263aaSKyle Evans 	 * as we need to skip checking root_stat on the first iteration and
285848263aaSKyle Evans 	 * ensure that we set it with the first mkdir().
286848263aaSKyle Evans 	 */
287848263aaSKyle Evans 	exit (copy(argv, type, fts_options, (type == DIR_TO_DNE ? NULL :
288848263aaSKyle Evans 	    &to_stat)));
2894b88c807SRodney W. Grimes }
2904b88c807SRodney W. Grimes 
291ba8acd9dSMark Murray static int
ftscmp(const FTSENT * const * a,const FTSENT * const * b)292*2d6b33f8SDag-Erling Smørgrav ftscmp(const FTSENT * const *a, const FTSENT * const *b)
293*2d6b33f8SDag-Erling Smørgrav {
294*2d6b33f8SDag-Erling Smørgrav 	return (strcmp((*a)->fts_name, (*b)->fts_name));
295*2d6b33f8SDag-Erling Smørgrav }
296*2d6b33f8SDag-Erling Smørgrav 
297*2d6b33f8SDag-Erling Smørgrav static int
copy(char * argv[],enum op type,int fts_options,struct stat * root_stat)298848263aaSKyle Evans copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
2994b88c807SRodney W. Grimes {
300f00f8b4fSKyle Evans 	char rootname[NAME_MAX];
30182fc0d09SDag-Erling Smørgrav 	struct stat created_root_stat, to_stat, *curr_stat;
3024b88c807SRodney W. Grimes 	FTS *ftsp;
3034b88c807SRodney W. Grimes 	FTSENT *curr;
304b3fe9014SDag-Erling Smørgrav 	char *recpath = NULL, *sep;
305eb439266SDag-Erling Smørgrav 	int atflags, dne, badcp, len, level, rval;
30662de071bSStephen McKay 	mode_t mask, mode;
307be783915SDag-Erling Smørgrav 	bool beneath = Rflag && type != FILE_TO_FILE;
3084a5db7acSStephen McKay 
3094a5db7acSStephen McKay 	/*
3104a5db7acSStephen McKay 	 * Keep an inverted copy of the umask, for use in correcting
3114a5db7acSStephen McKay 	 * permissions on created directories when not using -p.
3124a5db7acSStephen McKay 	 */
3134a5db7acSStephen McKay 	mask = ~umask(0777);
3144a5db7acSStephen McKay 	umask(~mask);
3154b88c807SRodney W. Grimes 
31682fc0d09SDag-Erling Smørgrav 	if (type == FILE_TO_FILE) {
31782fc0d09SDag-Erling Smørgrav 		to.dir = AT_FDCWD;
31882fc0d09SDag-Erling Smørgrav 		to.end = to.path + strlcpy(to.path, to.base, sizeof(to.path));
319b3fe9014SDag-Erling Smørgrav 		to.base[0] = '\0';
32082fc0d09SDag-Erling Smørgrav 	} else if (type == FILE_TO_DIR) {
32182fc0d09SDag-Erling Smørgrav 		to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
32282fc0d09SDag-Erling Smørgrav 		if (to.dir < 0)
32382fc0d09SDag-Erling Smørgrav 			err(1, "%s", to.base);
324b3fe9014SDag-Erling Smørgrav 		/*
325b3fe9014SDag-Erling Smørgrav 		 * We have previously made sure there is room for this.
326b3fe9014SDag-Erling Smørgrav 		 */
327537fbf70SDag-Erling Smørgrav 		if (strcmp(to.base, "/") != 0) {
328b3fe9014SDag-Erling Smørgrav 			sep = strchr(to.base, '\0');
329b3fe9014SDag-Erling Smørgrav 			sep[0] = '/';
330b3fe9014SDag-Erling Smørgrav 			sep[1] = '\0';
331537fbf70SDag-Erling Smørgrav 		}
332a8aaf803SDag-Erling Smørgrav 	} else {
333a8aaf803SDag-Erling Smørgrav 		/*
334a8aaf803SDag-Erling Smørgrav 		 * We will create the destination directory imminently.
335a8aaf803SDag-Erling Smørgrav 		 */
336a8aaf803SDag-Erling Smørgrav 		to.dir = -1;
33782fc0d09SDag-Erling Smørgrav 	}
33882fc0d09SDag-Erling Smørgrav 
339eb439266SDag-Erling Smørgrav 	level = FTS_ROOTLEVEL;
340*2d6b33f8SDag-Erling Smørgrav 	if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL)
3415ad9e45fSMatthew Dillon 		err(1, "fts_open");
34282fc0d09SDag-Erling Smørgrav 	for (badcp = rval = 0;
34382fc0d09SDag-Erling Smørgrav 	     (curr = fts_read(ftsp)) != NULL;
34482fc0d09SDag-Erling Smørgrav 	     badcp = 0, *to.end = '\0') {
34582fc0d09SDag-Erling Smørgrav 		curr_stat = curr->fts_statp;
3464b88c807SRodney W. Grimes 		switch (curr->fts_info) {
3474b88c807SRodney W. Grimes 		case FTS_NS:
348f6fa1b35SDmitrij Tejblum 		case FTS_DNR:
3494b88c807SRodney W. Grimes 		case FTS_ERR:
350eb439266SDag-Erling Smørgrav 			if (level > curr->fts_level) {
351eb439266SDag-Erling Smørgrav 				/* leaving a directory; remove its name from to.path */
352eb439266SDag-Erling Smørgrav 				if (type == DIR_TO_DNE &&
353eb439266SDag-Erling Smørgrav 				    curr->fts_level == FTS_ROOTLEVEL) {
354eb439266SDag-Erling Smørgrav 					/* this is actually our created root */
355eb439266SDag-Erling Smørgrav 				} else {
356eb439266SDag-Erling Smørgrav 					while (to.end > to.path && *to.end != '/')
357eb439266SDag-Erling Smørgrav 						to.end--;
358eb439266SDag-Erling Smørgrav 					assert(strcmp(to.end + (*to.end == '/'),
359eb439266SDag-Erling Smørgrav 					    curr->fts_name) == 0);
360eb439266SDag-Erling Smørgrav 					*to.end = '\0';
361eb439266SDag-Erling Smørgrav 				}
362eb439266SDag-Erling Smørgrav 				level--;
363eb439266SDag-Erling Smørgrav 			}
364f070188cSDag-Erling Smørgrav 			warnc(curr->fts_errno, "%s", curr->fts_path);
3650efa2040SMichael Haro 			badcp = rval = 1;
3664b88c807SRodney W. Grimes 			continue;
3674b88c807SRodney W. Grimes 		case FTS_DC:			/* Warn, continue. */
3684b88c807SRodney W. Grimes 			warnx("%s: directory causes a cycle", curr->fts_path);
3690efa2040SMichael Haro 			badcp = rval = 1;
3704b88c807SRodney W. Grimes 			continue;
37182fc0d09SDag-Erling Smørgrav 		case FTS_D:
372848263aaSKyle Evans 			/*
37382fc0d09SDag-Erling Smørgrav 			 * Stash the root basename off for detecting
37482fc0d09SDag-Erling Smørgrav 			 * recursion later.
375f00f8b4fSKyle Evans 			 *
37682fc0d09SDag-Erling Smørgrav 			 * This will be essential if the root is a symlink
37782fc0d09SDag-Erling Smørgrav 			 * and we're rolling with -L or -H.  The later
37882fc0d09SDag-Erling Smørgrav 			 * bits will need this bit in particular.
379848263aaSKyle Evans 			 */
380f00f8b4fSKyle Evans 			if (curr->fts_level == FTS_ROOTLEVEL) {
38182fc0d09SDag-Erling Smørgrav 				strlcpy(rootname, curr->fts_name,
38282fc0d09SDag-Erling Smørgrav 				    sizeof(rootname));
383848263aaSKyle Evans 			}
38482fc0d09SDag-Erling Smørgrav 			/* we must have a destination! */
38582fc0d09SDag-Erling Smørgrav 			if (type == DIR_TO_DNE &&
38682fc0d09SDag-Erling Smørgrav 			    curr->fts_level == FTS_ROOTLEVEL) {
38782fc0d09SDag-Erling Smørgrav 				assert(to.dir < 0);
38882fc0d09SDag-Erling Smørgrav 				assert(root_stat == NULL);
38982fc0d09SDag-Erling Smørgrav 				mode = curr_stat->st_mode | S_IRWXU;
39048578dcbSDag-Erling Smørgrav 				/*
39148578dcbSDag-Erling Smørgrav 				 * Will our umask prevent us from entering
39248578dcbSDag-Erling Smørgrav 				 * the directory after we create it?
39348578dcbSDag-Erling Smørgrav 				 */
39448578dcbSDag-Erling Smørgrav 				if (~mask & S_IRWXU)
39548578dcbSDag-Erling Smørgrav 					umask(~mask & ~S_IRWXU);
39682fc0d09SDag-Erling Smørgrav 				if (mkdir(to.base, mode) != 0) {
39782fc0d09SDag-Erling Smørgrav 					warn("%s", to.base);
398a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
3990efa2040SMichael Haro 					badcp = rval = 1;
40048578dcbSDag-Erling Smørgrav 					if (~mask & S_IRWXU)
40148578dcbSDag-Erling Smørgrav 						umask(~mask);
4027658fd20SBruce Evans 					continue;
4037658fd20SBruce Evans 				}
40482fc0d09SDag-Erling Smørgrav 				to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
40582fc0d09SDag-Erling Smørgrav 				if (to.dir < 0) {
40682fc0d09SDag-Erling Smørgrav 					warn("%s", to.base);
40782fc0d09SDag-Erling Smørgrav 					(void)rmdir(to.base);
408a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
40982fc0d09SDag-Erling Smørgrav 					badcp = rval = 1;
41048578dcbSDag-Erling Smørgrav 					if (~mask & S_IRWXU)
41148578dcbSDag-Erling Smørgrav 						umask(~mask);
41282fc0d09SDag-Erling Smørgrav 					continue;
41382fc0d09SDag-Erling Smørgrav 				}
41482fc0d09SDag-Erling Smørgrav 				if (fstat(to.dir, &created_root_stat) != 0) {
41582fc0d09SDag-Erling Smørgrav 					warn("%s", to.base);
41682fc0d09SDag-Erling Smørgrav 					(void)close(to.dir);
41782fc0d09SDag-Erling Smørgrav 					(void)rmdir(to.base);
418a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
41982fc0d09SDag-Erling Smørgrav 					to.dir = -1;
42082fc0d09SDag-Erling Smørgrav 					badcp = rval = 1;
42148578dcbSDag-Erling Smørgrav 					if (~mask & S_IRWXU)
42248578dcbSDag-Erling Smørgrav 						umask(~mask);
42382fc0d09SDag-Erling Smørgrav 					continue;
42482fc0d09SDag-Erling Smørgrav 				}
42548578dcbSDag-Erling Smørgrav 				if (~mask & S_IRWXU)
42648578dcbSDag-Erling Smørgrav 					umask(~mask);
42782fc0d09SDag-Erling Smørgrav 				root_stat = &created_root_stat;
42848578dcbSDag-Erling Smørgrav 				curr->fts_number = 1;
429b3fe9014SDag-Erling Smørgrav 				/*
430b3fe9014SDag-Erling Smørgrav 				 * We have previously made sure there is
431b3fe9014SDag-Erling Smørgrav 				 * room for this.
432b3fe9014SDag-Erling Smørgrav 				 */
433b3fe9014SDag-Erling Smørgrav 				sep = strchr(to.base, '\0');
434b3fe9014SDag-Erling Smørgrav 				sep[0] = '/';
435b3fe9014SDag-Erling Smørgrav 				sep[1] = '\0';
43682fc0d09SDag-Erling Smørgrav 			} else {
43782fc0d09SDag-Erling Smørgrav 				/* entering a directory; append its name to to.path */
43882fc0d09SDag-Erling Smørgrav 				len = snprintf(to.end, END(to.path) - to.end, "%s%s",
43982fc0d09SDag-Erling Smørgrav 				    to.end > to.path ? "/" : "", curr->fts_name);
44082fc0d09SDag-Erling Smørgrav 				if (to.end + len >= END(to.path)) {
44182fc0d09SDag-Erling Smørgrav 					*to.end = '\0';
442b3fe9014SDag-Erling Smørgrav 					warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
44382fc0d09SDag-Erling Smørgrav 					    to.path, to.end > to.path ? "/" : "",
44482fc0d09SDag-Erling Smørgrav 					    curr->fts_name);
44582fc0d09SDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
44682fc0d09SDag-Erling Smørgrav 					badcp = rval = 1;
44782fc0d09SDag-Erling Smørgrav 					continue;
44882fc0d09SDag-Erling Smørgrav 				}
44982fc0d09SDag-Erling Smørgrav 				to.end += len;
45082fc0d09SDag-Erling Smørgrav 			}
451eb439266SDag-Erling Smørgrav 			level++;
452f00f8b4fSKyle Evans 			/*
45382fc0d09SDag-Erling Smørgrav 			 * We're on the verge of recursing on ourselves.
45482fc0d09SDag-Erling Smørgrav 			 * Either we need to stop right here (we knowingly
45582fc0d09SDag-Erling Smørgrav 			 * just created it), or we will in an immediate
45682fc0d09SDag-Erling Smørgrav 			 * descendant.  Record the path of the immediate
45782fc0d09SDag-Erling Smørgrav 			 * descendant to make our lives a little less
45882fc0d09SDag-Erling Smørgrav 			 * complicated looking.
459f00f8b4fSKyle Evans 			 */
46082fc0d09SDag-Erling Smørgrav 			if (type != FILE_TO_FILE &&
46182fc0d09SDag-Erling Smørgrav 			    root_stat->st_dev == curr_stat->st_dev &&
46282fc0d09SDag-Erling Smørgrav 			    root_stat->st_ino == curr_stat->st_ino) {
46382fc0d09SDag-Erling Smørgrav 				assert(recpath == NULL);
464f00f8b4fSKyle Evans 				if (root_stat == &created_root_stat) {
465f00f8b4fSKyle Evans 					/*
46682fc0d09SDag-Erling Smørgrav 					 * This directory didn't exist
46782fc0d09SDag-Erling Smørgrav 					 * when we started, we created it
46882fc0d09SDag-Erling Smørgrav 					 * as part of traversal.  Stop
46982fc0d09SDag-Erling Smørgrav 					 * right here before we do
47082fc0d09SDag-Erling Smørgrav 					 * something silly.
471f00f8b4fSKyle Evans 					 */
472a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
473f00f8b4fSKyle Evans 					continue;
474f00f8b4fSKyle Evans 				}
47582fc0d09SDag-Erling Smørgrav 				if (asprintf(&recpath, "%s/%s", to.path,
47682fc0d09SDag-Erling Smørgrav 				    rootname) < 0) {
47782fc0d09SDag-Erling Smørgrav 					warnc(ENOMEM, NULL);
478a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
47982fc0d09SDag-Erling Smørgrav 					badcp = rval = 1;
480f00f8b4fSKyle Evans 					continue;
481f00f8b4fSKyle Evans 				}
4824b88c807SRodney W. Grimes 			}
48382fc0d09SDag-Erling Smørgrav 			if (recpath != NULL &&
48482fc0d09SDag-Erling Smørgrav 			    strcmp(recpath, to.path) == 0) {
48582fc0d09SDag-Erling Smørgrav 				fts_set(ftsp, curr, FTS_SKIP);
48682fc0d09SDag-Erling Smørgrav 				continue;
48782fc0d09SDag-Erling Smørgrav 			}
48882fc0d09SDag-Erling Smørgrav 			break;
48982fc0d09SDag-Erling Smørgrav 		case FTS_DP:
4904a5db7acSStephen McKay 			/*
49116ef4ac3SStephen McKay 			 * We are nearly finished with this directory.  If we
49216ef4ac3SStephen McKay 			 * didn't actually copy it, or otherwise don't need to
49316ef4ac3SStephen McKay 			 * change its attributes, then we are done.
49482fc0d09SDag-Erling Smørgrav 			 *
49562de071bSStephen McKay 			 * If -p is in effect, set all the attributes.
49662de071bSStephen McKay 			 * Otherwise, set the correct permissions, limited
49716ef4ac3SStephen McKay 			 * by the umask.  Optimise by avoiding a chmod()
49816ef4ac3SStephen McKay 			 * if possible (which is usually the case if we
49916ef4ac3SStephen McKay 			 * made the directory).  Note that mkdir() does not
50016ef4ac3SStephen McKay 			 * honour setuid, setgid and sticky bits, but we
50116ef4ac3SStephen McKay 			 * normally want to preserve them on directories.
5024a5db7acSStephen McKay 			 */
50382fc0d09SDag-Erling Smørgrav 			if (curr->fts_number && pflag) {
50482fc0d09SDag-Erling Smørgrav 				int fd = *to.path ? -1 : to.dir;
50582fc0d09SDag-Erling Smørgrav 				if (setfile(curr_stat, fd, true))
506eedc99e7SStephen McKay 					rval = 1;
50782fc0d09SDag-Erling Smørgrav 				if (preserve_dir_acls(curr->fts_accpath,
50882fc0d09SDag-Erling Smørgrav 				    to.path) != 0)
5099b4261c9SChristian S.J. Peron 					rval = 1;
51082fc0d09SDag-Erling Smørgrav 			} else if (curr->fts_number) {
51182fc0d09SDag-Erling Smørgrav 				const char *path = *to.path ? to.path : dot;
51282fc0d09SDag-Erling Smørgrav 				mode = curr_stat->st_mode;
51348578dcbSDag-Erling Smørgrav 				if (fchmodat(to.dir, path, mode & mask, 0) != 0) {
514b3fe9014SDag-Erling Smørgrav 					warn("chmod: %s%s", to.base, to.path);
5154a5db7acSStephen McKay 					rval = 1;
5164a5db7acSStephen McKay 				}
5174a5db7acSStephen McKay 			}
518eb439266SDag-Erling Smørgrav 			if (level > curr->fts_level) {
51982fc0d09SDag-Erling Smørgrav 				/* leaving a directory; remove its name from to.path */
52082fc0d09SDag-Erling Smørgrav 				if (type == DIR_TO_DNE &&
52182fc0d09SDag-Erling Smørgrav 				    curr->fts_level == FTS_ROOTLEVEL) {
52282fc0d09SDag-Erling Smørgrav 					/* this is actually our created root */
52382fc0d09SDag-Erling Smørgrav 				} else {
52482fc0d09SDag-Erling Smørgrav 					while (to.end > to.path && *to.end != '/')
52582fc0d09SDag-Erling Smørgrav 						to.end--;
526eb439266SDag-Erling Smørgrav 					assert(strcmp(to.end + (*to.end == '/'),
527eb439266SDag-Erling Smørgrav 					    curr->fts_name) == 0);
52882fc0d09SDag-Erling Smørgrav 					*to.end = '\0';
52982fc0d09SDag-Erling Smørgrav 				}
530eb439266SDag-Erling Smørgrav 				level--;
531eb439266SDag-Erling Smørgrav 			}
5324a5db7acSStephen McKay 			continue;
53382fc0d09SDag-Erling Smørgrav 		default:
53482fc0d09SDag-Erling Smørgrav 			/* something else: append its name to to.path */
53582fc0d09SDag-Erling Smørgrav 			if (type == FILE_TO_FILE)
53682fc0d09SDag-Erling Smørgrav 				break;
53782fc0d09SDag-Erling Smørgrav 			len = snprintf(to.end, END(to.path) - to.end, "%s%s",
53882fc0d09SDag-Erling Smørgrav 			    to.end > to.path ? "/" : "", curr->fts_name);
53982fc0d09SDag-Erling Smørgrav 			if (to.end + len >= END(to.path)) {
54082fc0d09SDag-Erling Smørgrav 				*to.end = '\0';
541b3fe9014SDag-Erling Smørgrav 				warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
54282fc0d09SDag-Erling Smørgrav 				    to.path, to.end > to.path ? "/" : "",
54382fc0d09SDag-Erling Smørgrav 				    curr->fts_name);
54482fc0d09SDag-Erling Smørgrav 				badcp = rval = 1;
54582fc0d09SDag-Erling Smørgrav 				continue;
54682fc0d09SDag-Erling Smørgrav 			}
54782fc0d09SDag-Erling Smørgrav 			/* intentionally do not update to.end */
54882fc0d09SDag-Erling Smørgrav 			break;
54982fc0d09SDag-Erling Smørgrav 		}
55082fc0d09SDag-Erling Smørgrav 
55182fc0d09SDag-Erling Smørgrav 		/* Not an error but need to remember it happened. */
55282fc0d09SDag-Erling Smørgrav 		if (to.path[0] == '\0') {
55382fc0d09SDag-Erling Smørgrav 			/*
55482fc0d09SDag-Erling Smørgrav 			 * This can happen in two cases:
55582fc0d09SDag-Erling Smørgrav 			 * - DIR_TO_DNE; we created the directory and
55682fc0d09SDag-Erling Smørgrav 			 *   populated root_stat earlier.
55782fc0d09SDag-Erling Smørgrav 			 * - FILE_TO_DIR if a source has a trailing slash;
55882fc0d09SDag-Erling Smørgrav 			 *   the caller populated root_stat.
55982fc0d09SDag-Erling Smørgrav 			 */
56082fc0d09SDag-Erling Smørgrav 			dne = false;
56182fc0d09SDag-Erling Smørgrav 			to_stat = *root_stat;
56282fc0d09SDag-Erling Smørgrav 		} else {
56382fc0d09SDag-Erling Smørgrav 			atflags = beneath ? AT_RESOLVE_BENEATH : 0;
56482fc0d09SDag-Erling Smørgrav 			if (curr->fts_info == FTS_D || curr->fts_info == FTS_SL)
56582fc0d09SDag-Erling Smørgrav 				atflags |= AT_SYMLINK_NOFOLLOW;
56682fc0d09SDag-Erling Smørgrav 			dne = fstatat(to.dir, to.path, &to_stat, atflags) != 0;
5674a5db7acSStephen McKay 		}
5684a5db7acSStephen McKay 
5690729d1e8SDag-Erling Smørgrav 		/* Check if source and destination are identical. */
57082fc0d09SDag-Erling Smørgrav 		if (!dne &&
57182fc0d09SDag-Erling Smørgrav 		    to_stat.st_dev == curr_stat->st_dev &&
57282fc0d09SDag-Erling Smørgrav 		    to_stat.st_ino == curr_stat->st_ino) {
573b3fe9014SDag-Erling Smørgrav 			warnx("%s%s and %s are identical (not copied).",
57482fc0d09SDag-Erling Smørgrav 			    to.base, to.path, curr->fts_path);
5750efa2040SMichael Haro 			badcp = rval = 1;
57682fc0d09SDag-Erling Smørgrav 			if (S_ISDIR(curr_stat->st_mode))
577a4f6317bSDag-Erling Smørgrav 				fts_set(ftsp, curr, FTS_SKIP);
5784b88c807SRodney W. Grimes 			continue;
5794b88c807SRodney W. Grimes 		}
5800729d1e8SDag-Erling Smørgrav 
58182fc0d09SDag-Erling Smørgrav 		switch (curr_stat->st_mode & S_IFMT) {
5824b88c807SRodney W. Grimes 		case S_IFLNK:
583f9dc2a8bSTim J. Robbins 			if ((fts_options & FTS_LOGICAL) ||
584f9dc2a8bSTim J. Robbins 			    ((fts_options & FTS_COMFOLLOW) &&
585f9dc2a8bSTim J. Robbins 			    curr->fts_level == 0)) {
58664d6925dSDag-Erling Smørgrav 				/*
58764d6925dSDag-Erling Smørgrav 				 * We asked FTS to follow links but got
58864d6925dSDag-Erling Smørgrav 				 * here anyway, which means the target is
58964d6925dSDag-Erling Smørgrav 				 * nonexistent or inaccessible.  Let
59064d6925dSDag-Erling Smørgrav 				 * copy_file() deal with the error.
59164d6925dSDag-Erling Smørgrav 				 */
59282fc0d09SDag-Erling Smørgrav 				if (copy_file(curr, dne, beneath))
593f9dc2a8bSTim J. Robbins 					badcp = rval = 1;
594f9dc2a8bSTim J. Robbins 			} else {
59564d6925dSDag-Erling Smørgrav 				/* Copy the link. */
59682fc0d09SDag-Erling Smørgrav 				if (copy_link(curr, dne, beneath))
5970efa2040SMichael Haro 					badcp = rval = 1;
598f9dc2a8bSTim J. Robbins 			}
5994b88c807SRodney W. Grimes 			break;
6004b88c807SRodney W. Grimes 		case S_IFDIR:
601ba3fe6d1STom Rhodes 			if (!Rflag) {
6024b88c807SRodney W. Grimes 				warnx("%s is a directory (not copied).",
6034b88c807SRodney W. Grimes 				    curr->fts_path);
604a4f6317bSDag-Erling Smørgrav 				fts_set(ftsp, curr, FTS_SKIP);
6050efa2040SMichael Haro 				badcp = rval = 1;
6064b88c807SRodney W. Grimes 				break;
6074b88c807SRodney W. Grimes 			}
6084b88c807SRodney W. Grimes 			/*
6094b88c807SRodney W. Grimes 			 * If the directory doesn't exist, create the new
6104b88c807SRodney W. Grimes 			 * one with the from file mode plus owner RWX bits,
6114b88c807SRodney W. Grimes 			 * modified by the umask.  Trade-off between being
6124b88c807SRodney W. Grimes 			 * able to write the directory (if from directory is
6134b88c807SRodney W. Grimes 			 * 555) and not causing a permissions race.  If the
614724fd448SBryan Drewery 			 * umask blocks owner writes, we fail.
6154b88c807SRodney W. Grimes 			 */
6164b88c807SRodney W. Grimes 			if (dne) {
61782fc0d09SDag-Erling Smørgrav 				mode = curr_stat->st_mode | S_IRWXU;
61848578dcbSDag-Erling Smørgrav 				/*
61948578dcbSDag-Erling Smørgrav 				 * Will our umask prevent us from entering
62048578dcbSDag-Erling Smørgrav 				 * the directory after we create it?
62148578dcbSDag-Erling Smørgrav 				 */
62248578dcbSDag-Erling Smørgrav 				if (~mask & S_IRWXU)
62348578dcbSDag-Erling Smørgrav 					umask(~mask & ~S_IRWXU);
62482fc0d09SDag-Erling Smørgrav 				if (mkdirat(to.dir, to.path, mode) != 0) {
625b3fe9014SDag-Erling Smørgrav 					warn("%s%s", to.base, to.path);
626a4f6317bSDag-Erling Smørgrav 					fts_set(ftsp, curr, FTS_SKIP);
627dd286b0dSDag-Erling Smørgrav 					badcp = rval = 1;
62848578dcbSDag-Erling Smørgrav 					if (~mask & S_IRWXU)
62948578dcbSDag-Erling Smørgrav 						umask(~mask);
630dd286b0dSDag-Erling Smørgrav 					break;
631dd286b0dSDag-Erling Smørgrav 				}
63248578dcbSDag-Erling Smørgrav 				if (~mask & S_IRWXU)
63348578dcbSDag-Erling Smørgrav 					umask(~mask);
6344b88c807SRodney W. Grimes 			} else if (!S_ISDIR(to_stat.st_mode)) {
635b3fe9014SDag-Erling Smørgrav 				warnc(ENOTDIR, "%s%s", to.base, to.path);
636a4f6317bSDag-Erling Smørgrav 				fts_set(ftsp, curr, FTS_SKIP);
637dd286b0dSDag-Erling Smørgrav 				badcp = rval = 1;
638dd286b0dSDag-Erling Smørgrav 				break;
6394b88c807SRodney W. Grimes 			}
6404b88c807SRodney W. Grimes 			/*
64162de071bSStephen McKay 			 * Arrange to correct directory attributes later
6424a5db7acSStephen McKay 			 * (in the post-order phase) if this is a new
64362de071bSStephen McKay 			 * directory, or if the -p flag is in effect.
64448578dcbSDag-Erling Smørgrav 			 * Note that fts_number may already be set if this
64548578dcbSDag-Erling Smørgrav 			 * is the newly created destination directory.
6464b88c807SRodney W. Grimes 			 */
64748578dcbSDag-Erling Smørgrav 			curr->fts_number |= pflag || dne;
6484b88c807SRodney W. Grimes 			break;
6494b88c807SRodney W. Grimes 		case S_IFBLK:
6504b88c807SRodney W. Grimes 		case S_IFCHR:
6517ea2450fSBryan Drewery 			if (Rflag && !sflag) {
65282fc0d09SDag-Erling Smørgrav 				if (copy_special(curr_stat, dne, beneath))
6530efa2040SMichael Haro 					badcp = rval = 1;
654439b2b1eSBruce Evans 			} else {
65582fc0d09SDag-Erling Smørgrav 				if (copy_file(curr, dne, beneath))
6560efa2040SMichael Haro 					badcp = rval = 1;
657439b2b1eSBruce Evans 			}
6584b88c807SRodney W. Grimes 			break;
6596fa36377SJulian Elischer 		case S_IFSOCK:
6606fa36377SJulian Elischer 			warnx("%s is a socket (not copied).",
6616fa36377SJulian Elischer 			    curr->fts_path);
6629fa5f90fSEdward Tomasz Napierala 			break;
6634b88c807SRodney W. Grimes 		case S_IFIFO:
6647ea2450fSBryan Drewery 			if (Rflag && !sflag) {
66582fc0d09SDag-Erling Smørgrav 				if (copy_fifo(curr_stat, dne, beneath))
6660efa2040SMichael Haro 					badcp = rval = 1;
667439b2b1eSBruce Evans 			} else {
66882fc0d09SDag-Erling Smørgrav 				if (copy_file(curr, dne, beneath))
6690efa2040SMichael Haro 					badcp = rval = 1;
670439b2b1eSBruce Evans 			}
6714b88c807SRodney W. Grimes 			break;
6724b88c807SRodney W. Grimes 		default:
67382fc0d09SDag-Erling Smørgrav 			if (copy_file(curr, dne, beneath))
6740efa2040SMichael Haro 				badcp = rval = 1;
6754b88c807SRodney W. Grimes 			break;
6764b88c807SRodney W. Grimes 		}
6770efa2040SMichael Haro 		if (vflag && !badcp)
678b3fe9014SDag-Erling Smørgrav 			(void)printf("%s -> %s%s\n", curr->fts_path, to.base, to.path);
6794b88c807SRodney W. Grimes 	}
680eb439266SDag-Erling Smørgrav 	assert(level == FTS_ROOTLEVEL);
6814b88c807SRodney W. Grimes 	if (errno)
6824b88c807SRodney W. Grimes 		err(1, "fts_read");
683a8aaf803SDag-Erling Smørgrav 	(void)fts_close(ftsp);
684a8aaf803SDag-Erling Smørgrav 	if (to.dir != AT_FDCWD && to.dir >= 0)
685a8aaf803SDag-Erling Smørgrav 		(void)close(to.dir);
68682fc0d09SDag-Erling Smørgrav 	free(recpath);
6874b88c807SRodney W. Grimes 	return (rval);
6884b88c807SRodney W. Grimes }
6894b88c807SRodney W. Grimes 
69000d321a2SMatthew N. Dodd static void
siginfo(int sig __unused)6914901f51bSBruce Evans siginfo(int sig __unused)
69200d321a2SMatthew N. Dodd {
69300d321a2SMatthew N. Dodd 
69400d321a2SMatthew N. Dodd 	info = 1;
69500d321a2SMatthew N. Dodd }
696