#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
+#include <locale.h>
#ifdef __APPLE__
#include <sys/time.h>
#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__ */
#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, rval, to_fd;
ssize_t rcount;
size_t wresid;
off_t wtotal;
char *bufp;
+ char resp[] = {'\0', '\0'};
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
char *p;
#endif
} 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);
/* 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);
} 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);
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;
+ unsigned int options = islink ? FSOPT_NOFOLLOW : 0;
+
+ 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;
- TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
- TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
- if (fdval ? futimes(fd, tv) : (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv))) {
- warn("%sutimes: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ 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) :
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);