On Mon 01/04/2019 21:36, Björn Ketelaars wrote:
> Add --one-file-system, which prevents openrsync to cross filesystem
> boundaries. Option and behaviour is the same as GPL rsync.
Thanks for all the feedback!
New diff:
- Without the long option;
- Appropriate changes to usage and rsync.1;
- '-x' / '-xx' is passed to the other side;
- Special case for >1 '-x' option has been addressed;
- More important, permit fts to walk the whole filesystem and use dev_t
comparison.
Florian's proposed scenario seems to work, as do several variants of
'-x' / '-xx' and '--delete'.
Comments?
diff --git usr.bin/rsync/extern.h usr.bin/rsync/extern.h
index 9ae08e7515e..038997aa164 100644
--- usr.bin/rsync/extern.h
+++ usr.bin/rsync/extern.h
@@ -117,6 +117,7 @@ struct opts {
int devices; /* --devices */
int specials; /* --specials */
int numeric_ids; /* --numeric-ids */
+ int one_file_system; /* -x */
char *rsync_path; /* --rsync-path */
char *ssh_prog; /* --rsh or -e */
char *port; /* --port */
diff --git usr.bin/rsync/fargs.c usr.bin/rsync/fargs.c
index 55bf1b4b8af..a185d0c6a5d 100644
--- usr.bin/rsync/fargs.c
+++ usr.bin/rsync/fargs.c
@@ -106,6 +106,10 @@ fargs_cmdline(struct sess *sess, const struct fargs *f,
size_t *skip)
addargs(&args, "-v");
if (sess->opts->verbose > 0)
addargs(&args, "-v");
+ if (sess->opts->one_file_system)
+ addargs(&args, "-x");
+ if (sess->opts->one_file_system > 1)
+ addargs(&args, "-x");
if (sess->opts->specials && !sess->opts->devices)
addargs(&args, "--specials");
if (!sess->opts->specials && sess->opts->devices)
diff --git usr.bin/rsync/flist.c usr.bin/rsync/flist.c
index b39599583e3..a3d1f338b74 100644
--- usr.bin/rsync/flist.c
+++ usr.bin/rsync/flist.c
@@ -804,11 +804,12 @@ flist_gen_dirent(struct sess *sess, char *root, struct
flist **fl, size_t *sz,
size_t *max)
{
char *cargv[2], *cp;
- int rc = 0;
+ int rc = 0, nxdev = 0, flag, i;
FTS *fts;
FTSENT *ent;
struct flist *f;
size_t flsz = 0, stripdir;
+ dev_t *xdev;
struct stat st;
cargv[0] = root;
@@ -913,6 +914,48 @@ flist_gen_dirent(struct sess *sess, char *root, struct
flist **fl, size_t *sz,
continue;
}
+ /*
+ * If rsync is told to avoid crossing a filesystem
+ * boundary when recursing, then replace all mount point
+ * directories with empty directories. The latter is
+ * prevented by telling rsync multiple times to avoid
+ * crossing a filesystem boundary when recursing.
+ * Replacing mount point directories is tricky. We need
+ * to sort out which directories to include. As such,
+ * keep track of unique device inodes, and use these for
+ * comparison.
+ */
+
+ if (sess->opts->one_file_system &&
+ ent->fts_statp->st_dev != st.st_dev) {
+ if (sess->opts->one_file_system > 1 ||
+ !S_ISDIR(ent->fts_statp->st_mode))
+ continue;
+
+ if ((xdev = malloc(sizeof(dev_t))) == NULL) {
+ ERRX1(sess, "malloc");
+ goto out;
+ }
+
+ flag = 0;
+ for (i = 0; i < nxdev; i++)
+ if (xdev[i] == ent->fts_statp->st_dev) {
+ flag = 1;
+ break;
+ }
+ if (flag)
+ continue;
+
+ if (nxdev)
+ if ((xdev = realloc(xdev, sizeof(dev_t))) ==
+ NULL) {
+ ERRX1(sess, "realloc");
+ goto out;
+ }
+ xdev[nxdev] = ent->fts_statp->st_dev;
+ nxdev++;
+ }
+
/* Allocate a new file entry. */
if (!flist_realloc(sess, fl, sz, max)) {
@@ -966,6 +1009,8 @@ flist_gen_dirent(struct sess *sess, char *root, struct
flist **fl, size_t *sz,
rc = 1;
out:
fts_close(fts);
+ if (sess->opts->one_file_system)
+ free(xdev);
return rc;
}
@@ -1129,10 +1174,11 @@ flist_gen_dels(struct sess *sess, const char *root,
struct flist **fl,
size_t *sz, const struct flist *wfl, size_t wflsz)
{
char **cargv = NULL;
- int rc = 0, c;
+ int rc = 0, c, flag;
FTS *fts = NULL;
FTSENT *ent;
struct flist *f;
+ struct stat st;
size_t cargvs = 0, i, j, max = 0, stripdir;
ENTRY hent;
ENTRY *hentp;
@@ -1253,6 +1299,31 @@ flist_gen_dels(struct sess *sess, const char *root,
struct flist **fl,
} else if (stripdir >= ent->fts_pathlen)
continue;
+ assert(ent->fts_statp != NULL);
+
+ /*
+ * If rsync is told to avoid crossing a filesystem
+ * boundary when recursing, then exclude all entries
+ * from the list with a device inode, which does not
+ * match that of one of the top-level directories.
+ */
+
+ if (sess->opts->one_file_system) {
+ flag = 0;
+ for (i = 0; i < wflsz; i++) {
+ if (stat(wfl[i].path, &st) == -1) {
+ ERR(sess, "%s: stat", wfl[i].path);
+ goto out;
+ }
+ if (ent->fts_statp->st_dev == st.st_dev) {
+ flag = 1;
+ break;
+ }
+ }
+ if (!flag)
+ continue;
+ }
+
/* Look up in hashtable. */
memset(&hent, 0, sizeof(ENTRY));
@@ -1273,7 +1344,6 @@ flist_gen_dels(struct sess *sess, const char *root,
struct flist **fl,
goto out;
}
f->wpath = f->path + stripdir;
- assert(ent->fts_statp != NULL);
flist_copy_stat(f, ent->fts_statp);
errno = 0;
}
diff --git usr.bin/rsync/main.c usr.bin/rsync/main.c
index dd598d93eb2..53c42351789 100644
--- usr.bin/rsync/main.c
+++ usr.bin/rsync/main.c
@@ -315,7 +315,7 @@ main(int argc, char *argv[])
memset(&opts, 0, sizeof(struct opts));
- while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvz", lopts, NULL))
+ while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, NULL))
!= -1) {
switch (c) {
case 'D':
@@ -359,6 +359,9 @@ main(int argc, char *argv[])
case 'v':
opts.verbose++;
break;
+ case 'x':
+ opts.one_file_system++;
+ break;
case 'z':
fprintf(stderr, "%s: -z not supported yet\n",
getprogname());
break;
@@ -505,7 +508,7 @@ main(int argc, char *argv[])
exit(rc);
usage:
fprintf(stderr, "usage: %s"
- " [-aDglnoprtv] [-e program] [--del] [--numeric-ids]\n"
+ " [-aDglnoprtvx] [-e program] [--del] [--numeric-ids]\n"
"\t[--port=portnumber] [--rsync-path=program] [--version]\n"
"\tsource ... directory\n",
getprogname());
diff --git usr.bin/rsync/rsync.1 usr.bin/rsync/rsync.1
index c16168fff8f..cd1513dc89a 100644
--- usr.bin/rsync/rsync.1
+++ usr.bin/rsync/rsync.1
@@ -22,7 +22,7 @@
.Nd synchronise local and remote files
.Sh SYNOPSIS
.Nm openrsync
-.Op Fl aDglnoprtv
+.Op Fl aDglnoprtvx
.Op Fl e Ar program
.Op Fl -del
.Op Fl -numeric-ids
@@ -141,6 +141,11 @@ Increase verbosity.
Specify once for files being transferred, twice for specific status,
thrice for per-file transfer information, and four times for per-file
breakdowns.
+.It Fl x
+Do not cross filesystem boundaries.
+If this option is repeated, all mount point directories from the copy are
+omitted.
+Otherwise, it includes an empty directory at each mount point it encounters.
.It Fl -version
Print version and exit.
.El