-/*
+/*-
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* SUCH DAMAGE.
*/
+#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1988, 1993, 1994\n\
#endif /* not lint */
#ifndef lint
-#if 0
static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94";
-#endif
#endif /* not lint */
+#endif
#include <sys/cdefs.h>
-__RCSID("$FreeBSD: src/bin/cp/cp.c,v 1.42 2002/09/22 11:15:56 mckay Exp $");
+__FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.52 2005/09/05 04:36:08 csjp Exp $");
/*
* Cp copies source files to target files.
* in "to") to form the final target path.
*/
-#include <sys/param.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef __APPLE__
+#include <copyfile.h>
+#include <get_compat.h>
+#else /* !__APPLE__ */
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
#include "extern.h"
#define STRIP_TRAILING_SLASH(p) { \
PATH_T to = { to.p_path, emptystring, "" };
int fflag, iflag, nflag, pflag, vflag;
+#ifdef __APPLE__
+int Xflag;
+#endif /* __APPLE__ */
static int Rflag, rflag;
+volatile sig_atomic_t info;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
static int copy(char *[], enum op, int);
-static int mastercmp(const FTSENT * const *, const FTSENT * const *);
+static void siginfo(int __unused);
int
main(int argc, char *argv[])
char *target;
Hflag = Lflag = Pflag = 0;
- while ((ch = getopt(argc, argv, "HLPRfinprv")) != -1)
+ while ((ch = getopt(argc, argv, "HLPRXafinprv")) != -1)
switch (ch) {
case 'H':
Hflag = 1;
case 'R':
Rflag = 1;
break;
+ case 'X':
+ Xflag = 1;
+ break;
case 'f':
fflag = 1;
- iflag = nflag = 0;
+ /* Determine if the STD is SUSv3 or Legacy */
+ if (COMPAT_MODE("bin/cp", "unix2003"))
+ nflag = 0; /* reset nflag, but not iflag */
+ else
+ iflag = nflag = 0; /* reset both */
break;
case 'i':
iflag = 1;
- fflag = nflag = 0;
+ if (COMPAT_MODE("bin/cp", "unix2003"))
+ nflag = 0; /* reset nflag, but not fflag */
+ else
+ fflag = nflag = 0;
break;
case 'n':
nflag = 1;
case 'v':
vflag = 1;
break;
+ case 'a':
+ pflag = 1;
+ Pflag = 1;
+ Rflag = 1;
+ break;
default:
usage();
break;
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
}
+ (void)signal(SIGINFO, siginfo);
/* Save the target base in "to". */
target = argv[--argc];
exit (copy(argv, type, fts_options));
}
-int
+static int
copy(char *argv[], enum op type, int fts_options)
{
struct stat to_stat;
mask = ~umask(0777);
umask(~mask);
- if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
+ if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
err(1, "fts_open");
for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
switch (curr->fts_info) {
case FTS_ERR:
warnx("%s: %s",
curr->fts_path, strerror(curr->fts_errno));
- badcp = rval = 1;
+ rval = 1;
continue;
case FTS_DC: /* Warn, continue. */
warnx("%s: directory causes a cycle", curr->fts_path);
- badcp = rval = 1;
+ rval = 1;
continue;
default:
;
}
+#ifdef __APPLE__
+#ifdef __clang__
+#pragma clang diagnostic push
+/* clang doesn't like fts_name[1], but we know better... */
+#pragma clang diagnostic ignored "-Warray-bounds"
+#endif
+ /* Skip ._<file> when using copyfile and <file> exists */
+ if ((pflag || !Xflag) && (curr->fts_level != FTS_ROOTLEVEL) &&
+ (curr->fts_namelen > 2) && /* ._\0 is not AppleDouble */
+ (curr->fts_name[0] == '.') && (curr->fts_name[1] == '_')) {
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ struct stat statbuf;
+ char path[PATH_MAX];
+ char *p = strrchr(curr->fts_path, '/');
+ if (p) {
+ size_t s = p + 2 - curr->fts_path;
+ if (s > sizeof(path)) s = sizeof(path);
+ strlcpy(path, curr->fts_path, s);
+ strlcat(path, curr->fts_name+2, sizeof(path));
+ } else {
+ strlcpy(path, curr->fts_name+2, sizeof(path));
+ }
+ if (!lstat(path, &statbuf)) {
+ continue;
+ }
+ }
+#endif /* __APPLE__ */
/*
* If we are in case (2) or (3) above, we need to append the
* source name to the target name.
if (target_mid - to.p_path + nlen >= PATH_MAX) {
warnx("%s%s: name too long (not copied)",
to.p_path, p);
- badcp = rval = 1;
+ rval = 1;
continue;
}
(void)strncat(target_mid, p, nlen);
* normally want to preserve them on directories.
*/
if (pflag) {
- if (setfile(curr->fts_statp, 0))
- rval = 1;
+ if (setfile(curr->fts_statp, -1))
+ rval = 1;
+#ifdef __APPLE__
+ /* setfile will fail if writeattr is denied */
+ if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_ACL)<0)
+ warn("%s: unable to copy ACL to %s", curr->fts_path, to.p_path);
+#else /* !__APPLE__ */
+ if (preserve_dir_acls(curr->fts_statp,
+ curr->fts_accpath, to.p_path) != 0)
+ rval = 1;
+#endif /* __APPLE__ */
} else {
mode = curr->fts_statp->st_mode;
if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
to_stat.st_ino == curr->fts_statp->st_ino) {
warnx("%s and %s are identical (not copied).",
to.p_path, curr->fts_path);
- badcp = rval = 1;
+ rval = 1;
if (S_ISDIR(curr->fts_statp->st_mode))
(void)fts_set(ftsp, curr, FTS_SKIP);
continue;
warnx("cannot overwrite directory %s with "
"non-directory %s",
to.p_path, curr->fts_path);
- badcp = rval = 1;
+ rval = 1;
continue;
}
dne = 0;
case S_IFDIR:
if (!Rflag && !rflag) {
warnx("%s is a directory (not copied).",
- curr->fts_path);
+ curr->fts_path);
(void)fts_set(ftsp, curr, FTS_SKIP);
badcp = rval = 1;
break;
*/
if (dne) {
if (mkdir(to.p_path,
- curr->fts_statp->st_mode | S_IRWXU) < 0)
- err(1, "%s", to.p_path);
+ curr->fts_statp->st_mode | S_IRWXU) < 0) {
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ warn("%s", to.p_path);
+ } else {
+ err(1, "%s", to.p_path);
+ }
+ }
} else if (!S_ISDIR(to_stat.st_mode)) {
errno = ENOTDIR;
- err(1, "%s", to.p_path);
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ warn("%s", to.p_path);
+ } else {
+ err(1, "%s", to.p_path);
+ }
}
/*
* Arrange to correct directory attributes later
* directory, or if the -p flag is in effect.
*/
curr->fts_number = pflag || dne;
+#ifdef __APPLE__
+ if (!Xflag) {
+ if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_XATTR) < 0)
+ warn("%s: unable to copy extended attributes to %s", curr->fts_path, to.p_path);
+ /* ACL and mtime set in postorder traversal */
+ }
+#endif /* __APPLE__ */
break;
case S_IFBLK:
case S_IFCHR:
}
if (errno)
err(1, "fts_read");
+ fts_close(ftsp);
return (rval);
}
-/*
- * mastercmp --
- * The comparison function for the copy order. The order is to copy
- * non-directory files before directory files. The reason for this
- * is because files tend to be in the same cylinder group as their
- * parent directory, whereas directories tend not to be. Copying the
- * files first reduces seeking.
- */
-int
-mastercmp(const FTSENT * const *a, const FTSENT * const *b)
+static void
+siginfo(int sig __unused)
{
- int a_info, b_info;
-
- a_info = (*a)->fts_info;
- if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
- return (0);
- b_info = (*b)->fts_info;
- if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
- return (0);
- if (a_info == FTS_D)
- return (-1);
- if (b_info == FTS_D)
- return (1);
- return (0);
+
+ info = 1;
}