]> git.saurik.com Git - apple/file_cmds.git/blobdiff - cp/utils.c
file_cmds-321.100.10.0.1.tar.gz
[apple/file_cmds.git] / cp / utils.c
index 55356889732191a4fb594277f0858b9644baae82..feecc0c85528a475ec18da490a513ddd2258e741 100644 (file)
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
 #include <stdlib.h>
 #include <sysexits.h>
 #include <unistd.h>
+#include <locale.h>
 
 #ifdef __APPLE__
 #include <sys/time.h>
@@ -59,6 +60,8 @@ __FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
 #include <string.h>
 #include <sys/mount.h>
 #include <get_compat.h> 
+#include <sys/attr.h>
+#include <sys/clonefile.h>
 #else 
 #define COMPAT_MODE(a,b) (1)
 #endif /* __APPLE__ */
@@ -66,16 +69,30 @@ __FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
 #include "extern.h"
 #define        cp_pct(x,y)     (int)(100.0 * (double)(x) / (double)(y))
 
+/* Memory strategy threshold, in pages: if physmem is larger then this, use a 
+ * large buffer */
+#define PHYSPAGES_THRESHOLD (32*1024)
+
+/* Maximum buffer size in bytes - do not allow it to grow larger than this */
+#define BUFSIZE_MAX (2*1024*1024)
+
+/* Small (default) buffer size in bytes. It's inefficient for this to be
+ * smaller than MAXPHYS */
+#define BUFSIZE_SMALL (MAXPHYS)
+
 int
 copy_file(const FTSENT *entp, int dne)
 {
-       static char buf[MAXBSIZE];
+       static char *buf = NULL;
+       static size_t bufsize;
        struct stat *fs;
-       int ch, checkch, from_fd, rcount, rval, to_fd;
+       int ch, checkch, from_fd, rval, to_fd;
+       ssize_t rcount;
        ssize_t wcount;
        size_t wresid;
        off_t wtotal;
        char *bufp;
+       char resp[] = {'\0', '\0'};
 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
        char *p;
 #endif
@@ -107,16 +124,33 @@ copy_file(const FTSENT *entp, int dne)
                } else if (iflag) {
                        (void)fprintf(stderr, "overwrite %s? %s", 
                                        to.p_path, YESNO);
+
+                       /* Load user specified locale */
+                       setlocale(LC_MESSAGES, "");
+
                        checkch = ch = getchar();
                        while (ch != '\n' && ch != EOF)
                                ch = getchar();
-                       if (checkch != 'y' && checkch != 'Y') {
+
+                       /* only care about the first character */
+                       resp[0] = checkch;
+
+                       if (rpmatch(resp) != 1) {
                                (void)close(from_fd);
                                (void)fprintf(stderr, "not overwritten\n");
                                return (1);
                        }
                }
                
+               if (cflag) {
+                       (void)unlink(to.p_path);
+                       int error = clonefile(entp->fts_path, to.p_path, 0);
+                       if (error)
+                               warn("%s: clonefile failed", to.p_path);
+                       (void)close(from_fd);
+                       return error == 0 ? 0 : 1;
+               }
+
                if (COMPAT_MODE("bin/cp", "unix2003")) {
                    /* first try to overwrite existing destination file name */
                    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
@@ -139,9 +173,19 @@ copy_file(const FTSENT *entp, int dne)
                            /* overwrite existing destination file name */
                            to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
                }
-       } else
+       } else {
+
+               if (cflag) {
+                       int error = clonefile(entp->fts_path, to.p_path, 0);
+                       if (error)
+                               warn("%s: clonefile failed", to.p_path);
+                       (void)close(from_fd);
+                       return error == 0 ? 0 : 1;
+               }
+
                to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
                    fs->st_mode & ~(S_ISUID | S_ISGID));
+       }
 
        if (to_fd == -1) {
                warn("%s", to.p_path);
@@ -226,8 +270,23 @@ copy_file(const FTSENT *entp, int dne)
        } else
 #endif
        {
+               if (buf == NULL) {
+                       /*
+                        * Note that buf and bufsize are static. If
+                        * malloc() fails, it will fail at the start
+                        * and not copy only some files. 
+                        */ 
+                       if (sysconf(_SC_PHYS_PAGES) > 
+                           PHYSPAGES_THRESHOLD)
+                               bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
+                       else
+                               bufsize = BUFSIZE_SMALL;
+                       buf = malloc(bufsize);
+                       if (buf == NULL)
+                               err(1, "Not enough memory");
+               }
                wtotal = 0;
-               while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+               while ((rcount = read(from_fd, buf, bufsize)) > 0) {
                        for (bufp = buf, wresid = rcount; ;
                            bufp += wcount, wresid -= wcount) {
                                wcount = write(to_fd, bufp, wresid);
@@ -294,7 +353,7 @@ copy_file(const FTSENT *entp, int dne)
 int
 copy_link(const FTSENT *p, int exists)
 {
-       int len;
+       ssize_t len;
        char llink[PATH_MAX];
 
        if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
@@ -350,33 +409,36 @@ copy_special(struct stat *from_stat, int exists)
 int
 setfile(struct stat *fs, int fd)
 {
-       static struct timeval tv[2];
+       struct attrlist ts_req = {};
        struct stat ts;
        int rval, gotstat, islink, fdval;
+       struct {
+               struct timespec mtime;
+               struct timespec atime;
+       } set_ts;
 
        rval = 0;
        fdval = fd != -1;
        islink = !fdval && S_ISLNK(fs->st_mode);
-       fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
-                      S_IRWXU | S_IRWXG | S_IRWXO;
+       fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
+       unsigned int options = islink ? FSOPT_NOFOLLOW : 0;
 
-       TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
-       TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
-#ifdef __APPLE__
-       if (islink ? 0 : utimes(to.p_path, tv)) {
-#else
-       if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
-#endif /* __APPLE__ */
-               warn("%sutimes: %s", islink ? "l" : "", to.p_path);
+       ts_req.bitmapcount = ATTR_BIT_MAP_COUNT;
+       ts_req.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
+       set_ts.mtime = fs->st_mtimespec;
+       set_ts.atime = fs->st_atimespec;
+
+       if (fdval ? fsetattrlist(fd, &ts_req, &set_ts, sizeof(set_ts), options) :
+                   setattrlist(to.p_path, &ts_req, &set_ts, sizeof(set_ts), options)) {
+               warn("%ssetattrlist: %s", fdval ? "f" : "", to.p_path);
                rval = 1;
        }
-       if (fdval ? fstat(fd, &ts) :
-           (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
+       if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) :
+                                     stat(to.p_path, &ts))) {
                gotstat = 0;
-       else {
+       else {
                gotstat = 1;
-               ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
-                             S_IRWXU | S_IRWXG | S_IRWXO;
+               ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
        }
        /*
         * Changing the ownership probably won't succeed, unless we're root
@@ -384,34 +446,37 @@ setfile(struct stat *fs, int fd)
         * the mode; current BSD behavior is to remove all setuid bits on
         * chown.  If chown fails, lose setuid/setgid bits.
         */
-       if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
-               if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
-                   (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
-                   chown(to.p_path, fs->st_uid, fs->st_gid))) {
-                       if (errno != EPERM) {
-                               warn("%schown: %s", islink ? "l" : "", to.p_path);
-                               rval = 1;
-                       }
-                       fs->st_mode &= ~(S_ISUID | S_ISGID);
-               }
+       if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) {
+               if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ?
+                                                                 lchown(to.p_path, fs->st_uid, fs->st_gid) :
+                                                                 chown(to.p_path, fs->st_uid, fs->st_gid))) {
+                           if (errno != EPERM) {
+                                   warn("%schown: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+                                   rval = 1;
+                           }
+                           fs->st_mode &= ~(S_ISUID | S_ISGID);
+                   }
+       }
 
-       if (!gotstat || fs->st_mode != ts.st_mode)
-               if (fdval ? fchmod(fd, fs->st_mode) :
-                   (islink ? lchmod(to.p_path, fs->st_mode) :
-                   chmod(to.p_path, fs->st_mode))) {
-                       warn("%schmod: %s", islink ? "l" : "", to.p_path);
+       if (!gotstat || fs->st_mode != ts.st_mode) {
+               if (fdval ? fchmod(fd, fs->st_mode) : (islink ?
+                                                      lchmod(to.p_path, fs->st_mode) :
+                                                      chmod(to.p_path, fs->st_mode))) {
+                       warn("%schmod: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
                        rval = 1;
                }
+       }
 
-       if (!gotstat || fs->st_flags != ts.st_flags)
-               if (fdval ?
-                   fchflags(fd, fs->st_flags) :
-                   (islink ? lchflags(to.p_path, fs->st_flags) :
-                   chflags(to.p_path, fs->st_flags))) {
-                       warn("%schflags: %s", islink ? "l" : "", to.p_path);
-                       rval = 1;
+       if (!gotstat || fs->st_flags != ts.st_flags) {
+               if (fdval ? fchflags(fd, fs->st_flags) : (islink ?
+                                                         lchflags(to.p_path, fs->st_flags) :
+                                                         chflags(to.p_path, fs->st_flags))) {
+                       if (errno != EPERM) {
+                               warn("%schflags: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+                               rval = 1;
+                       }
                }
-
+       }
        return (rval);
 }
 
@@ -499,13 +564,13 @@ usage(void)
 
        if (COMPAT_MODE("bin/cp", "unix2003")) {
        (void)fprintf(stderr, "%s\n%s\n",
-"usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file",
-"       cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ... "
+"usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file target_file",
+"       cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file ... "
 "target_directory");
        } else {
        (void)fprintf(stderr, "%s\n%s\n",
-"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvX] source_file target_file",
-"       cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvX] source_file ... "
+"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file target_file",
+"       cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file ... "
 "target_directory");
        }
        exit(EX_USAGE);