cp.c revision 98171
11556Srgrimes/*
21556Srgrimes * Copyright (c) 1988, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * David Hitz of Auspex Systems Inc.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3820412Sstevestatic char const copyright[] =
391556Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
401556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes#ifndef lint
4435773Scharnier#if 0
4536003Scharnierstatic char sccsid[] = "@(#)cp.c	8.2 (Berkeley) 4/1/94";
4635773Scharnier#endif
4735773Scharnierstatic const char rcsid[] =
4850471Speter  "$FreeBSD: head/bin/cp/cp.c 98171 2002-06-13 15:48:36Z tjr $";
491556Srgrimes#endif /* not lint */
501556Srgrimes
511556Srgrimes/*
521556Srgrimes * Cp copies source files to target files.
538855Srgrimes *
541556Srgrimes * The global PATH_T structure "to" always contains the path to the
551556Srgrimes * current target file.  Since fts(3) does not change directories,
5620412Ssteve * this path can be either absolute or dot-relative.
578855Srgrimes *
581556Srgrimes * The basic algorithm is to initialize "to" and use fts(3) to traverse
591556Srgrimes * the file hierarchy rooted in the argument list.  A trivial case is the
601556Srgrimes * case of 'cp file1 file2'.  The more interesting case is the case of
611556Srgrimes * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
621556Srgrimes * path (relative to the root of the traversal) is appended to dir (stored
631556Srgrimes * in "to") to form the final target path.
641556Srgrimes */
651556Srgrimes
661556Srgrimes#include <sys/param.h>
671556Srgrimes#include <sys/stat.h>
681556Srgrimes
691556Srgrimes#include <err.h>
701556Srgrimes#include <errno.h>
711556Srgrimes#include <fts.h>
7276693Simp#include <limits.h>
7350381Smharo#include <stdio.h>
7478469Sdes#include <stdlib.h>
751556Srgrimes#include <string.h>
761556Srgrimes#include <unistd.h>
771556Srgrimes
781556Srgrimes#include "extern.h"
791556Srgrimes
801556Srgrimes#define	STRIP_TRAILING_SLASH(p) {					\
815292Sbde        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')	\
821556Srgrimes                *--(p).p_end = 0;					\
831556Srgrimes}
841556Srgrimes
8591087Smarkmstatic char emptystring[] = "";
861556Srgrimes
8791087SmarkmPATH_T to = { to.p_path, emptystring, "" };
881556Srgrimes
8991087Smarkmint iflag, pflag, fflag;
9091087Smarkmstatic int Rflag, rflag, vflag;
9191087Smarkm
921556Srgrimesenum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
931556Srgrimes
9490107Simpint copy(char *[], enum op, int);
9590107Simpint mastercmp(const FTSENT **, const FTSENT **);
961556Srgrimes
971556Srgrimesint
9890107Simpmain(int argc, char *argv[])
991556Srgrimes{
1001556Srgrimes	struct stat to_stat, tmp_stat;
1011556Srgrimes	enum op type;
10296808Sache	int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
10396809Sache	char *target;
1041556Srgrimes
10514157Spst	Hflag = Lflag = Pflag = 0;
10650381Smharo	while ((ch = getopt(argc, argv, "HLPRfiprv")) != -1)
1071556Srgrimes		switch (ch) {
1081556Srgrimes		case 'H':
1091556Srgrimes			Hflag = 1;
1101556Srgrimes			Lflag = Pflag = 0;
1111556Srgrimes			break;
1121556Srgrimes		case 'L':
1131556Srgrimes			Lflag = 1;
1141556Srgrimes			Hflag = Pflag = 0;
1151556Srgrimes			break;
1161556Srgrimes		case 'P':
1171556Srgrimes			Pflag = 1;
1181556Srgrimes			Hflag = Lflag = 0;
1191556Srgrimes			break;
1201556Srgrimes		case 'R':
1211556Srgrimes			Rflag = 1;
1221556Srgrimes			break;
1231556Srgrimes		case 'f':
12414416Swosch			fflag = 1;
1251556Srgrimes			iflag = 0;
1261556Srgrimes			break;
1271556Srgrimes		case 'i':
12814416Swosch			iflag = 1;
12914416Swosch			fflag = 0;
1301556Srgrimes			break;
1311556Srgrimes		case 'p':
1321556Srgrimes			pflag = 1;
1331556Srgrimes			break;
1341556Srgrimes		case 'r':
1351556Srgrimes			rflag = 1;
1361556Srgrimes			break;
13750381Smharo		case 'v':
13850381Smharo			vflag = 1;
13950381Smharo			break;
1401556Srgrimes		default:
1411556Srgrimes			usage();
1421556Srgrimes			break;
1431556Srgrimes		}
1441556Srgrimes	argc -= optind;
1451556Srgrimes	argv += optind;
1461556Srgrimes
1471556Srgrimes	if (argc < 2)
1481556Srgrimes		usage();
1491556Srgrimes
1501556Srgrimes	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
1511556Srgrimes	if (rflag) {
1521556Srgrimes		if (Rflag)
1531556Srgrimes			errx(1,
1541556Srgrimes		    "the -R and -r options may not be specified together.");
1551556Srgrimes		if (Hflag || Lflag || Pflag)
1561556Srgrimes			errx(1,
1571556Srgrimes	"the -H, -L, and -P options may not be specified with the -r option.");
1581556Srgrimes		fts_options &= ~FTS_PHYSICAL;
1591556Srgrimes		fts_options |= FTS_LOGICAL;
1601556Srgrimes	}
1611556Srgrimes	if (Rflag) {
1621556Srgrimes		if (Hflag)
1631556Srgrimes			fts_options |= FTS_COMFOLLOW;
1641556Srgrimes		if (Lflag) {
1651556Srgrimes			fts_options &= ~FTS_PHYSICAL;
1661556Srgrimes			fts_options |= FTS_LOGICAL;
1671556Srgrimes		}
1681556Srgrimes	} else {
1691556Srgrimes		fts_options &= ~FTS_PHYSICAL;
17098171Stjr		fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
1711556Srgrimes	}
1721556Srgrimes
1731556Srgrimes	/* Save the target base in "to". */
1741556Srgrimes	target = argv[--argc];
17576693Simp	if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
1761556Srgrimes		errx(1, "%s: name too long", target);
1771556Srgrimes	to.p_end = to.p_path + strlen(to.p_path);
1781556Srgrimes        if (to.p_path == to.p_end) {
1791556Srgrimes		*to.p_end++ = '.';
1801556Srgrimes		*to.p_end = 0;
1811556Srgrimes	}
18296809Sache	have_trailing_slash = (to.p_end[-1] == '/');
18396809Sache	if (have_trailing_slash)
18496809Sache		STRIP_TRAILING_SLASH(to);
1851556Srgrimes	to.target_end = to.p_end;
1861556Srgrimes
1871556Srgrimes	/* Set end of argument list for fts(3). */
1888855Srgrimes	argv[argc] = NULL;
1898855Srgrimes
1901556Srgrimes	/*
1911556Srgrimes	 * Cp has two distinct cases:
1921556Srgrimes	 *
1931556Srgrimes	 * cp [-R] source target
1941556Srgrimes	 * cp [-R] source1 ... sourceN directory
1951556Srgrimes	 *
1961556Srgrimes	 * In both cases, source can be either a file or a directory.
1971556Srgrimes	 *
1981556Srgrimes	 * In (1), the target becomes a copy of the source. That is, if the
1991556Srgrimes	 * source is a file, the target will be a file, and likewise for
2001556Srgrimes	 * directories.
2011556Srgrimes	 *
2021556Srgrimes	 * In (2), the real target is not directory, but "directory/source".
2031556Srgrimes	 */
2041556Srgrimes	r = stat(to.p_path, &to_stat);
2051556Srgrimes	if (r == -1 && errno != ENOENT)
2061556Srgrimes		err(1, "%s", to.p_path);
2071556Srgrimes	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
2081556Srgrimes		/*
2091556Srgrimes		 * Case (1).  Target is not a directory.
2108855Srgrimes		 */
2111556Srgrimes		if (argc > 1) {
2121556Srgrimes			usage();
2131556Srgrimes			exit(1);
2141556Srgrimes		}
2151556Srgrimes		/*
2161556Srgrimes		 * Need to detect the case:
2171556Srgrimes		 *	cp -R dir foo
2181556Srgrimes		 * Where dir is a directory and foo does not exist, where
2191556Srgrimes		 * we want pathname concatenations turned on but not for
2201556Srgrimes		 * the initial mkdir().
2211556Srgrimes		 */
2221556Srgrimes		if (r == -1) {
2231556Srgrimes			if (rflag || (Rflag && (Lflag || Hflag)))
2241556Srgrimes				stat(*argv, &tmp_stat);
2251556Srgrimes			else
2261556Srgrimes				lstat(*argv, &tmp_stat);
2278855Srgrimes
2281556Srgrimes			if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
2291556Srgrimes				type = DIR_TO_DNE;
2301556Srgrimes			else
2311556Srgrimes				type = FILE_TO_FILE;
2321556Srgrimes		} else
2331556Srgrimes			type = FILE_TO_FILE;
23496808Sache
23596808Sache		if (have_trailing_slash && type == FILE_TO_FILE) {
23696808Sache			if (r == -1)
23796808Sache				errx(1, "directory %s does not exist",
23896808Sache				     to.p_path);
23996808Sache			else
24096808Sache				errx(1, "%s is not a directory", to.p_path);
24196808Sache		}
2421556Srgrimes	} else
2431556Srgrimes		/*
2441556Srgrimes		 * Case (2).  Target is a directory.
2451556Srgrimes		 */
2461556Srgrimes		type = FILE_TO_DIR;
2471556Srgrimes
2481556Srgrimes	exit (copy(argv, type, fts_options));
2491556Srgrimes}
2501556Srgrimes
2511556Srgrimesint
25290107Simpcopy(char *argv[], enum op type, int fts_options)
2531556Srgrimes{
2541556Srgrimes	struct stat to_stat;
2551556Srgrimes	FTS *ftsp;
2561556Srgrimes	FTSENT *curr;
25791087Smarkm	int base = 0, dne, badcp, rval;
25891087Smarkm	size_t nlen;
2595292Sbde	char *p, *target_mid;
26088439Smckay	mode_t mask, mode;
2611556Srgrimes
26287655Smckay	/*
26387655Smckay	 * Keep an inverted copy of the umask, for use in correcting
26487655Smckay	 * permissions on created directories when not using -p.
26587655Smckay	 */
26687655Smckay	mask = ~umask(0777);
26787655Smckay	umask(~mask);
26887655Smckay
2691556Srgrimes	if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
2701556Srgrimes		err(1, NULL);
27153819Smharo	for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
2721556Srgrimes		switch (curr->fts_info) {
2731556Srgrimes		case FTS_NS:
27436812Sdt		case FTS_DNR:
2751556Srgrimes		case FTS_ERR:
2761556Srgrimes			warnx("%s: %s",
2771556Srgrimes			    curr->fts_path, strerror(curr->fts_errno));
27853819Smharo			badcp = rval = 1;
2791556Srgrimes			continue;
2801556Srgrimes		case FTS_DC:			/* Warn, continue. */
2811556Srgrimes			warnx("%s: directory causes a cycle", curr->fts_path);
28253819Smharo			badcp = rval = 1;
2831556Srgrimes			continue;
28491087Smarkm		default:
28596371Salfred			;
2861556Srgrimes		}
2871556Srgrimes
2881556Srgrimes		/*
2898855Srgrimes		 * If we are in case (2) or (3) above, we need to append the
2908855Srgrimes                 * source name to the target name.
2911556Srgrimes                 */
2921556Srgrimes		if (type != FILE_TO_FILE) {
2931556Srgrimes			/*
2941556Srgrimes			 * Need to remember the roots of traversals to create
2951556Srgrimes			 * correct pathnames.  If there's a directory being
2961556Srgrimes			 * copied to a non-existent directory, e.g.
2971556Srgrimes			 *	cp -R a/dir noexist
2981556Srgrimes			 * the resulting path name should be noexist/foo, not
2991556Srgrimes			 * noexist/dir/foo (where foo is a file in dir), which
3001556Srgrimes			 * is the case where the target exists.
3011556Srgrimes			 *
3021556Srgrimes			 * Also, check for "..".  This is for correct path
30346684Skris			 * concatenation for paths ending in "..", e.g.
3041556Srgrimes			 *	cp -R .. /tmp
3051556Srgrimes			 * Paths ending in ".." are changed to ".".  This is
3061556Srgrimes			 * tricky, but seems the easiest way to fix the problem.
3071556Srgrimes			 *
3081556Srgrimes			 * XXX
3091556Srgrimes			 * Since the first level MUST be FTS_ROOTLEVEL, base
3101556Srgrimes			 * is always initialized.
3111556Srgrimes			 */
31246073Simp			if (curr->fts_level == FTS_ROOTLEVEL) {
3131556Srgrimes				if (type != DIR_TO_DNE) {
3141556Srgrimes					p = strrchr(curr->fts_path, '/');
3158855Srgrimes					base = (p == NULL) ? 0 :
3161556Srgrimes					    (int)(p - curr->fts_path + 1);
3171556Srgrimes
3188855Srgrimes					if (!strcmp(&curr->fts_path[base],
3191556Srgrimes					    ".."))
3201556Srgrimes						base += 1;
3211556Srgrimes				} else
3221556Srgrimes					base = curr->fts_pathlen;
32346073Simp			}
3241556Srgrimes
3251556Srgrimes			p = &curr->fts_path[base];
3261556Srgrimes			nlen = curr->fts_pathlen - base;
3275292Sbde			target_mid = to.target_end;
3285292Sbde			if (*p != '/' && target_mid[-1] != '/')
3295292Sbde				*target_mid++ = '/';
3305292Sbde			*target_mid = 0;
33176693Simp			if (target_mid - to.p_path + nlen >= PATH_MAX) {
3328855Srgrimes				warnx("%s%s: name too long (not copied)",
3335292Sbde				    to.p_path, p);
33453819Smharo				badcp = rval = 1;
3355292Sbde				continue;
3365292Sbde			}
3375292Sbde			(void)strncat(target_mid, p, nlen);
3385292Sbde			to.p_end = target_mid + nlen;
3391556Srgrimes			*to.p_end = 0;
3401556Srgrimes			STRIP_TRAILING_SLASH(to);
3411556Srgrimes		}
3421556Srgrimes
34387655Smckay		if (curr->fts_info == FTS_DP) {
34487655Smckay			/*
34588755Smckay			 * We are nearly finished with this directory.  If we
34688755Smckay			 * didn't actually copy it, or otherwise don't need to
34788755Smckay			 * change its attributes, then we are done.
34887655Smckay			 */
34988439Smckay			if (!curr->fts_number)
35088439Smckay				continue;
35188439Smckay			/*
35288439Smckay			 * If -p is in effect, set all the attributes.
35388439Smckay			 * Otherwise, set the correct permissions, limited
35488755Smckay			 * by the umask.  Optimise by avoiding a chmod()
35588755Smckay			 * if possible (which is usually the case if we
35688755Smckay			 * made the directory).  Note that mkdir() does not
35788755Smckay			 * honour setuid, setgid and sticky bits, but we
35888755Smckay			 * normally want to preserve them on directories.
35988439Smckay			 */
36087655Smckay			if (pflag)
36187655Smckay				rval = setfile(curr->fts_statp, 0);
36288439Smckay			else {
36388439Smckay				mode = curr->fts_statp->st_mode;
36488439Smckay				if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
36588439Smckay				    ((mode | S_IRWXU) & mask) != (mode & mask))
36688439Smckay					if (chmod(to.p_path, mode & mask) != 0){
36788439Smckay						warn("chmod: %s", to.p_path);
36888439Smckay						rval = 1;
36988439Smckay					}
37087655Smckay			}
37187655Smckay			continue;
37287655Smckay		}
37387655Smckay
3741556Srgrimes		/* Not an error but need to remember it happened */
3751556Srgrimes		if (stat(to.p_path, &to_stat) == -1)
3761556Srgrimes			dne = 1;
3771556Srgrimes		else {
3781556Srgrimes			if (to_stat.st_dev == curr->fts_statp->st_dev &&
3791556Srgrimes			    to_stat.st_ino == curr->fts_statp->st_ino) {
3801556Srgrimes				warnx("%s and %s are identical (not copied).",
3811556Srgrimes				    to.p_path, curr->fts_path);
38253819Smharo				badcp = rval = 1;
3831556Srgrimes				if (S_ISDIR(curr->fts_statp->st_mode))
3841556Srgrimes					(void)fts_set(ftsp, curr, FTS_SKIP);
3851556Srgrimes				continue;
3861556Srgrimes			}
38720412Ssteve			if (!S_ISDIR(curr->fts_statp->st_mode) &&
38820412Ssteve			    S_ISDIR(to_stat.st_mode)) {
38991087Smarkm				warnx("cannot overwrite directory %s with "
39091087Smarkm				    "non-directory %s",
39120412Ssteve				    to.p_path, curr->fts_path);
39253819Smharo				badcp = rval = 1;
39320412Ssteve				continue;
39420412Ssteve			}
3951556Srgrimes			dne = 0;
3961556Srgrimes		}
3971556Srgrimes
3981556Srgrimes		switch (curr->fts_statp->st_mode & S_IFMT) {
3991556Srgrimes		case S_IFLNK:
40098171Stjr			/* Catch special case of a non-dangling symlink */
40198171Stjr			if ((fts_options & FTS_LOGICAL) ||
40298171Stjr			    ((fts_options & FTS_COMFOLLOW) &&
40398171Stjr			    curr->fts_level == 0)) {
40498171Stjr				if (copy_file(curr, dne))
40598171Stjr					badcp = rval = 1;
40698171Stjr			} else {
40798171Stjr				if (copy_link(curr, !dne))
40898171Stjr					badcp = rval = 1;
40998171Stjr			}
4101556Srgrimes			break;
4111556Srgrimes		case S_IFDIR:
4121556Srgrimes			if (!Rflag && !rflag) {
4131556Srgrimes				warnx("%s is a directory (not copied).",
4141556Srgrimes				    curr->fts_path);
4151556Srgrimes				(void)fts_set(ftsp, curr, FTS_SKIP);
41653819Smharo				badcp = rval = 1;
4171556Srgrimes				break;
4181556Srgrimes			}
4191556Srgrimes			/*
4201556Srgrimes			 * If the directory doesn't exist, create the new
4211556Srgrimes			 * one with the from file mode plus owner RWX bits,
4221556Srgrimes			 * modified by the umask.  Trade-off between being
4231556Srgrimes			 * able to write the directory (if from directory is
4241556Srgrimes			 * 555) and not causing a permissions race.  If the
4251556Srgrimes			 * umask blocks owner writes, we fail..
4261556Srgrimes			 */
4271556Srgrimes			if (dne) {
4288855Srgrimes				if (mkdir(to.p_path,
4291556Srgrimes				    curr->fts_statp->st_mode | S_IRWXU) < 0)
4301556Srgrimes					err(1, "%s", to.p_path);
4311556Srgrimes			} else if (!S_ISDIR(to_stat.st_mode)) {
4321556Srgrimes				errno = ENOTDIR;
4335879Sdg				err(1, "%s", to.p_path);
4341556Srgrimes			}
4351556Srgrimes			/*
43688439Smckay			 * Arrange to correct directory attributes later
43787655Smckay			 * (in the post-order phase) if this is a new
43888439Smckay			 * directory, or if the -p flag is in effect.
4391556Srgrimes			 */
44088439Smckay			curr->fts_number = pflag || dne;
4411556Srgrimes			break;
4421556Srgrimes		case S_IFBLK:
4431556Srgrimes		case S_IFCHR:
4441556Srgrimes			if (Rflag) {
4451556Srgrimes				if (copy_special(curr->fts_statp, !dne))
44653819Smharo					badcp = rval = 1;
4477572Sbde			} else {
4481556Srgrimes				if (copy_file(curr, dne))
44953819Smharo					badcp = rval = 1;
4507572Sbde			}
4511556Srgrimes			break;
4521556Srgrimes		case S_IFIFO:
4537572Sbde			if (Rflag) {
4541556Srgrimes				if (copy_fifo(curr->fts_statp, !dne))
45553819Smharo					badcp = rval = 1;
4567572Sbde			} else {
4571556Srgrimes				if (copy_file(curr, dne))
45853819Smharo					badcp = rval = 1;
4597572Sbde			}
4601556Srgrimes			break;
4611556Srgrimes		default:
4621556Srgrimes			if (copy_file(curr, dne))
46353819Smharo				badcp = rval = 1;
4641556Srgrimes			break;
4651556Srgrimes		}
46653819Smharo		if (vflag && !badcp)
46750543Smharo			(void)printf("%s -> %s\n", curr->fts_path, to.p_path);
4681556Srgrimes	}
4691556Srgrimes	if (errno)
4701556Srgrimes		err(1, "fts_read");
4711556Srgrimes	return (rval);
4721556Srgrimes}
4731556Srgrimes
4741556Srgrimes/*
4751556Srgrimes * mastercmp --
4761556Srgrimes *	The comparison function for the copy order.  The order is to copy
4771556Srgrimes *	non-directory files before directory files.  The reason for this
4781556Srgrimes *	is because files tend to be in the same cylinder group as their
4791556Srgrimes *	parent directory, whereas directories tend not to be.  Copying the
4801556Srgrimes *	files first reduces seeking.
4811556Srgrimes */
4821556Srgrimesint
48390107Simpmastercmp(const FTSENT **a, const FTSENT **b)
4841556Srgrimes{
4851556Srgrimes	int a_info, b_info;
4861556Srgrimes
4871556Srgrimes	a_info = (*a)->fts_info;
4881556Srgrimes	if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
4891556Srgrimes		return (0);
4901556Srgrimes	b_info = (*b)->fts_info;
4911556Srgrimes	if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
4921556Srgrimes		return (0);
4931556Srgrimes	if (a_info == FTS_D)
4941556Srgrimes		return (-1);
4951556Srgrimes	if (b_info == FTS_D)
4961556Srgrimes		return (1);
4971556Srgrimes	return (0);
4981556Srgrimes}
499