Hello tech@,
Attached is a proposed diff for cp(1). It adds the -l (hard link) and -s
(symlink) options. These options are available in GNU cp, FreeBSD cp,
and the -l option is at least in NetBSD and Dragonfly.
I needed the -l option to use the system cp for rsnapshots, instead of
their native_cl_al function. Hopefully, this will speed up my backups.
Thanks for your consideration,
Tracey
Index: bin/cp/cp.1
===================================================================
RCS file: /cvs/src/bin/cp/cp.1,v
retrieving revision 1.40
diff -u -p -u -r1.40 cp.1
--- bin/cp/cp.1 28 Jan 2019 18:58:42 -0000 1.40
+++ bin/cp/cp.1 23 May 2019 20:28:29 -0000
@@ -103,6 +103,8 @@ The
option overrides any previous
.Fl f
options.
+.It Fl l
+Create hard links to regular files in a hierarchy instead of copying.
.It Fl L
If the
.Fl R
@@ -128,6 +130,8 @@ If the source file has both its set-user
and either the user ID or group ID cannot be preserved, neither
the set-user-ID nor set-group-ID bits are preserved in the copy's
permissions.
+.It Fl s
+Create symbolic links to regular files in a hirarcy instead of copying.
.It Fl R
If
.Ar source
Index: bin/cp/cp.c
===================================================================
RCS file: /cvs/src/bin/cp/cp.c,v
retrieving revision 1.52
diff -u -p -u -r1.52 cp.c
--- bin/cp/cp.c 28 Jan 2019 18:58:42 -0000 1.52
+++ bin/cp/cp.c 23 May 2019 20:28:30 -0000
@@ -71,7 +71,7 @@
PATH_T to = { to.p_path, "" };
uid_t myuid;
-int Rflag, fflag, iflag, pflag, rflag, vflag;
+int Rflag, fflag, iflag, lflag, pflag, rflag, sflag, vflag;
mode_t myumask;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@@ -88,7 +88,7 @@ main(int argc, char *argv[])
char *target;
Hflag = Lflag = Pflag = Rflag = 0;
- while ((ch = getopt(argc, argv, "HLPRafiprv")) != -1)
+ while ((ch = getopt(argc, argv, "HLPRafiprvls")) != -1)
switch (ch) {
case 'H':
Hflag = 1;
@@ -119,12 +119,18 @@ main(int argc, char *argv[])
iflag = 1;
fflag = 0;
break;
+ case 'l':
+ lflag = 1;
+ break;
case 'p':
pflag = 1;
break;
case 'r':
rflag = 1;
break;
+ case 's':
+ sflag = 1;
+ break;
case 'v':
vflag = 1;
break;
@@ -157,6 +163,8 @@ main(int argc, char *argv[])
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
}
+ if (lflag && sflag)
+ errx(1, "the -l and -s options may not be specified together.");
if (Rflag) {
if (Hflag)
fts_options |= FTS_COMFOLLOW;
@@ -319,7 +327,7 @@ copy(char *argv[], enum op type, int fts
if (type != DIR_TO_DNE) {
p = find_last_component(curr->fts_path);
base = p - curr->fts_path;
-
+
if (!strcmp(&curr->fts_path[base],
".."))
base += 1;
@@ -435,7 +443,7 @@ copy(char *argv[], enum op type, int fts
break;
case S_IFBLK:
case S_IFCHR:
- if (Rflag) {
+ if (Rflag && !sflag) {
if ((cval = copy_special(curr->fts_statp,
!fts_dne(curr))) == 1)
rval = 1;
@@ -448,7 +456,7 @@ copy(char *argv[], enum op type, int fts
cval = 0;
break;
case S_IFIFO:
- if (Rflag) {
+ if (Rflag && !sflag) {
if ((cval = copy_fifo(curr->fts_statp,
!fts_dne(curr))) == 1)
rval = 1;
Index: bin/cp/extern.h
===================================================================
RCS file: /cvs/src/bin/cp/extern.h,v
retrieving revision 1.15
diff -u -p -u -r1.15 extern.h
--- bin/cp/extern.h 26 Dec 2015 18:11:43 -0000 1.15
+++ bin/cp/extern.h 23 May 2019 20:28:30 -0000
@@ -40,7 +40,7 @@ typedef struct {
extern PATH_T to;
extern uid_t myuid;
-extern int fflag, iflag, pflag;
+extern int fflag, iflag, lflag, pflag, sflag;
extern mode_t myumask;
extern char *__progname;
Index: bin/cp/utils.c
===================================================================
RCS file: /cvs/src/bin/cp/utils.c,v
retrieving revision 1.47
diff -u -p -u -r1.47 utils.c
--- bin/cp/utils.c 28 Jan 2019 18:58:42 -0000 1.47
+++ bin/cp/utils.c 23 May 2019 20:28:30 -0000
@@ -71,7 +71,8 @@ copy_file(FTSENT *entp, int exists)
err(1, "calloc");
}
- if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
+ if (!lflag && !sflag &&
+ (from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
warn("%s", entp->fts_path);
return (1);
}
@@ -97,12 +98,13 @@ copy_file(FTSENT *entp, int exists)
(void)close(from_fd);
return 2;
}
- to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
- } else
+ if (!lflag && !sflag)
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+ } else if (!lflag && !sflag)
to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID));
- if (to_fd == -1) {
+ if (!lflag && !sflag && to_fd == -1) {
warn("%s", to.p_path);
(void)close(from_fd);
return (1);
@@ -110,52 +112,69 @@ copy_file(FTSENT *entp, int exists)
rval = 0;
- /*
- * Mmap and write if less than 8M (the limit is so we don't totally
- * trash memory on big files. This is really a minor hack, but it
- * wins some CPU back.
- */
+ if (!lflag && !sflag) {
+ /*
+ * Mmap and write if less than 8M (the limit is so we don't
+ * totally trash memory on big files. This is really a minor
+ * hack, but it wins some CPU back.
+ */
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
- /* XXX broken for 0-size mmap */
- if (fs->st_size <= 8 * 1048576) {
- if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
- MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
- warn("mmap: %s", entp->fts_path);
- rval = 1;
- } else {
- madvise(p, fs->st_size, MADV_SEQUENTIAL);
- if (write(to_fd, p, fs->st_size) != fs->st_size) {
- warn("%s", to.p_path);
+ /* XXX broken for 0-size mmap */
+ if (fs->st_size <= 8 * 1048576) {
+ if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
+ MAP_FILE|MAP_SHARED, from_fd,
+ (off_t)0)) == MAP_FAILED) {
+ warn("mmap: %s", entp->fts_path);
rval = 1;
+ } else {
+ madvise(p, fs->st_size, MADV_SEQUENTIAL);
+ if (write(to_fd, p,
+ fs->st_size) != fs->st_size) {
+ warn("%s", to.p_path);
+ rval = 1;
+ }
+ /* Some systems don't unmap on close(2). */
+ if (munmap(p, fs->st_size) < 0) {
+ warn("%s", entp->fts_path);
+ rval = 1;
+ }
}
- /* Some systems don't unmap on close(2). */
- if (munmap(p, fs->st_size) < 0) {
+ } else
+#endif
+ {
+ int skipholes = 0;
+ struct stat tosb;
+ if (!fstat(to_fd, &tosb) && S_ISREG(tosb.st_mode))
+ skipholes = 1;
+ while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ if (skipholes && memcmp(buf, zeroes,
+ rcount) == 0)
+ wcount = lseek(to_fd, rcount,
+ SEEK_CUR) == -1 ? -1 : rcount;
+ else
+ wcount = write(to_fd, buf, rcount);
+ if (rcount != wcount || wcount == -1) {
+ warn("%s", to.p_path);
+ rval = 1;
+ break;
+ }
+ }
+ if (skipholes && rcount >= 0)
+ rcount = ftruncate(to_fd, lseek(to_fd, 0,
+ SEEK_CUR));
+ if (rcount < 0) {
warn("%s", entp->fts_path);
rval = 1;
}
}
- } else
-#endif
- {
- int skipholes = 0;
- struct stat tosb;
- if (!fstat(to_fd, &tosb) && S_ISREG(tosb.st_mode))
- skipholes = 1;
- while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
- if (skipholes && memcmp(buf, zeroes, rcount) == 0)
- wcount = lseek(to_fd, rcount, SEEK_CUR) == -1 ?
-1 : rcount;
- else
- wcount = write(to_fd, buf, rcount);
- if (rcount != wcount || wcount == -1) {
- warn("%s", to.p_path);
- rval = 1;
- break;
- }
+ } else if (lflag) {
+ if (link(entp->fts_path, to.p_path) == -1) {
+ warn("%s", to.p_path);
+ rval = 1;
}
- if (skipholes && rcount >= 0)
- rcount = ftruncate(to_fd, lseek(to_fd, 0, SEEK_CUR));
- if (rcount < 0) {
- warn("%s", entp->fts_path);
+ } else if (sflag) {
+ if (symlink(entp->fts_path, to.p_path) == -1) {
+ warn("%s", to.p_path);
rval = 1;
}
}
@@ -166,30 +185,34 @@ copy_file(FTSENT *entp, int exists)
return (1);
}
- if (pflag && setfile(fs, to_fd))
- rval = 1;
- /*
- * If the source was setuid or setgid, lose the bits unless the
- * copy is owned by the same user and group.
- */
+ if (!lflag && !sflag) {
+ if (pflag && setfile(fs, to_fd))
+ rval = 1;
+ /*
+ * If the source was setuid or setgid, lose the bits unless the
+ * copy is owned by the same user and group.
+ */
#define RETAINBITS \
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
- if (!pflag && !exists &&
- fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
- if (fstat(to_fd, &to_stat)) {
- warn("%s", to.p_path);
- rval = 1;
- } else if (fs->st_gid == to_stat.st_gid &&
- fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
+ if (!pflag && !exists &&
+ fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
+ if (fstat(to_fd, &to_stat)) {
+ warn("%s", to.p_path);
+ rval = 1;
+ } else if (fs->st_gid == to_stat.st_gid &&
+ fchmod(to_fd,
+ fs->st_mode & RETAINBITS & ~myumask)) {
+ warn("%s", to.p_path);
+ rval = 1;
+ }
+ }
+ if (close(to_fd)) {
warn("%s", to.p_path);
rval = 1;
}
}
+
(void)close(from_fd);
- if (close(to_fd)) {
- warn("%s", to.p_path);
- rval = 1;
- }
return (rval);
}
@@ -326,9 +349,10 @@ void
usage(void)
{
(void)fprintf(stderr,
- "usage: %s [-afipv] [-R [-H | -L | -P]] source target\n",
__progname);
+ "usage: %s [-afilpsv] [-R [-H | -L | -P]] source target\n",
+ __progname);
(void)fprintf(stderr,
- " %s [-afipv] [-R [-H | -L | -P]] source ... directory\n",
+ " %s [-afilpsv] [-R [-H | -L | -P]] source ... directory\n",
__progname);
exit(1);
}