=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/rsync/flist.c,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- src/usr.bin/rsync/flist.c 2019/03/31 09:26:05 1.23 +++ src/usr.bin/rsync/flist.c 2019/04/04 04:19:54 1.24 @@ -1,4 +1,4 @@ -/* $Id: flist.c,v 1.23 2019/03/31 09:26:05 deraadt Exp $ */ +/* $Id: flist.c,v 1.24 2019/04/04 04:19:54 bket Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * Copyright (c) 2019 Florian Obser @@ -804,11 +804,12 @@ 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 @@ 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 @@ rc = 1; out: fts_close(fts); + if (sess->opts->one_file_system) + free(xdev); return rc; } @@ -1129,10 +1174,11 @@ 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 @@ } 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 @@ goto out; } f->wpath = f->path + stripdir; - assert(ent->fts_statp != NULL); flist_copy_stat(f, ent->fts_statp); errno = 0; }