summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-06-06 17:33:28 +0200
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commit46d4c3a36a98c9f3add60e1d4e25b4fed1d304da (patch)
tree85c93ce893bfd32e2ad322eedd0b7d0a39b17b2e /src
parentcca09ee8d6ece8e22b638efd05c4c0eb49e91b91 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/basic/copy.c31
-rw-r--r--src/basic/copy.h7
2 files changed, 33 insertions, 5 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 5962e4ce7..c09292b94 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -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"
@@ -534,8 +535,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);
diff --git a/src/basic/copy.h b/src/basic/copy.h
index 8f5acdb7c..7c1d86fe3 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -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;
#if 0 /// UNNEEDED by elogind