copy: extend check for mount point crossing

We do this checks as protection against bind mount cycles on the same
file system. However, the check wasn't really effective for that, as
it would only detect cycles A → B → A this way. By using
fs_is_mount_point() we'll also detect cycles A → A.

Also, while we are at it, make these file system boundary checks
optional. This is not used anywhere, but might be eventually...

Most importantly though add a longer blurb explanation the why.
This commit is contained in:
Lennart Poettering 2018-06-06 17:33:28 +02:00
parent ef202b848b
commit f6a77804c9
2 changed files with 33 additions and 5 deletions

View file

@ -29,6 +29,7 @@
#include "io-util.h"
#include "macro.h"
#include "missing.h"
#include "mount-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@ -531,8 +532,34 @@ static int fd_copy_directory(
}
if (S_ISDIR(buf.st_mode)) {
if (buf.st_dev != original_device)
continue;
/*
* Don't descend into directories on other file systems, if this is requested. We do a simple
* .st_dev check here, which basically comes for free. Note that we do this check only on
* directories, not other kind of file system objects, for two reason:
*
* The kernel's overlayfs pseudo file system that overlays multiple real file systems
* propagates the .st_dev field of the file system a file originates from all the way up
* through the stack to stat(). It doesn't do that for directories however. This means that
* comparing .st_dev on non-directories suggests that they all are mount points. To avoid
* confusion we hence avoid relying on this check for regular files.
*
* The main reason we do this check at all is to protect ourselves from bind mount cycles,
* where we really want to avoid descending down in all eternity. However the .st_dev check
* is usually not sufficient for this protection anyway, as bind mount cycles from the same
* file system onto itself can't be detected that way.
*/
if (FLAGS_SET(copy_flags, COPY_SAME_MOUNT)) {
if (buf.st_dev != original_device)
continue;
r = fd_is_mount_point(dirfd(d), de->d_name, 0);
if (r < 0)
return r;
if (r > 0)
continue;
}
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
} else if (S_ISREG(buf.st_mode))
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);

View file

@ -13,9 +13,10 @@
#include <sys/types.h>
typedef enum CopyFlags {
COPY_REFLINK = 1U << 0, /* Try to reflink */
COPY_MERGE = 1U << 1, /* Merge existing trees with our new one to copy */
COPY_REPLACE = 1U << 2, /* Replace an existing file if there's one */
COPY_REFLINK = 1U << 0, /* Try to reflink */
COPY_MERGE = 1U << 1, /* Merge existing trees with our new one to copy */
COPY_REPLACE = 1U << 2, /* Replace an existing file if there's one */
COPY_SAME_MOUNT = 1U << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
} CopyFlags;
int copy_file_fd(const char *from, int to, CopyFlags copy_flags);