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

Reply via email to