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