diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2015-05-27 09:56:03 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2015-05-29 17:40:12 +0200 |
commit | 5d409034017e9f9f8c4392157d95511fc2e05d87 (patch) | |
tree | e0223a0ec6b7ec9c516e00370da305e5f8c2d96a /src/shared | |
parent | 2fe9e87a2475d5c2f5e2bb1a7e711c59fd0b2c6d (diff) |
path-util: Fix path_is_mount_point for files
Commits 27cc6f166 and f25afeb broke path_is_mount_point() for files (such as
/etc/machine-id → /run/machine-id bind mounts) as with the factorization of
fd_is_mount_point() we lost the parent directory. We cannot determine that from
an fd only as openat(fd, "..") only works for directory fds.
Change fd_is_mount_point() to behave like openat(): It now takes a file
descriptor of the containing directory, a file name in it, and flags (which can
be 0 or AT_SYMLINK_FOLLOW). Unlike name_to_handle_at() or openat(), fstatat()
only accepts the inverse flag AT_SYMLINK_NOFOLLOW and complains with EINVAL
about AT_SYMLINK_FOLLOW; so we need to transform the flags for that fallback.
Adjust rm_rf_children() accordingly (only other caller of fd_is_mount_point()
aside from path_is_mount_point()).
Add test cases for files, links, and file bind mounts (the latter will only
work when running as root). Split out a new test_path_is_mount_point() test
case function as it got significantly larger now.
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/path-util.c | 31 | ||||
-rw-r--r-- | src/shared/path-util.h | 2 | ||||
-rw-r--r-- | src/shared/rm-rf.c | 2 |
3 files changed, 24 insertions, 11 deletions
diff --git a/src/shared/path-util.c b/src/shared/path-util.c index 7090989fcb..8be479cd7f 100644 --- a/src/shared/path-util.c +++ b/src/shared/path-util.c @@ -509,7 +509,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id return safe_atoi(p, mnt_id); } -int fd_is_mount_point(int fd) { +int fd_is_mount_point(int fd, const char *filename, int flags) { union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; int mount_id = -1, mount_id_parent = -1; bool nosupp = false, check_st_dev = true; @@ -517,6 +517,7 @@ int fd_is_mount_point(int fd) { int r; assert(fd >= 0); + assert(filename); /* First we will try the name_to_handle_at() syscall, which * tells us the mount id and an opaque file "handle". It is @@ -541,7 +542,7 @@ int fd_is_mount_point(int fd) { * subvolumes have different st_dev, even though they aren't * real mounts of their own. */ - r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH); + r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags); if (r < 0) { if (errno == ENOSYS) /* This kernel does not support name_to_handle_at() @@ -558,7 +559,7 @@ int fd_is_mount_point(int fd) { return -errno; } - r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0); + r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH); if (r < 0) { if (errno == EOPNOTSUPP) { if (nosupp) @@ -593,13 +594,13 @@ int fd_is_mount_point(int fd) { return mount_id != mount_id_parent; fallback_fdinfo: - r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id); + r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id); if (r == -EOPNOTSUPP) goto fallback_fstat; if (r < 0) return r; - r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); + r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent); if (r < 0) return r; @@ -615,10 +616,16 @@ fallback_fdinfo: check_st_dev = false; fallback_fstat: - if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0) + /* yay for fstatat() taking a different set of flags than the other + * _at() above */ + if (flags & AT_SYMLINK_FOLLOW) + flags &= ~AT_SYMLINK_FOLLOW; + else + flags |= AT_SYMLINK_NOFOLLOW; + if (fstatat(fd, filename, &a, flags) < 0) return -errno; - if (fstatat(fd, "..", &b, 0) < 0) + if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0) return -errno; /* A directory with same device and inode as its parent? Must @@ -632,17 +639,23 @@ fallback_fstat: int path_is_mount_point(const char *t, bool allow_symlink) { _cleanup_close_ int fd = -1; + _cleanup_free_ char *parent = NULL; + int r; assert(t); if (path_equal(t, "/")) return 1; - fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH)); + r = path_get_parent(t, &parent); + if (r < 0) + return r; + + fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH); if (fd < 0) return -errno; - return fd_is_mount_point(fd); + return fd_is_mount_point(fd, basename(t), (allow_symlink ? AT_SYMLINK_FOLLOW : 0)); } int path_is_read_only_fs(const char *path) { diff --git a/src/shared/path-util.h b/src/shared/path-util.h index 4f45cfd2b7..38ad799ba0 100644 --- a/src/shared/path-util.h +++ b/src/shared/path-util.h @@ -53,7 +53,7 @@ char** path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *prefix); char** path_strv_resolve_uniq(char **l, const char *prefix); -int fd_is_mount_point(int fd); +int fd_is_mount_point(int fd, const char *filename, int flags); int path_is_mount_point(const char *path, bool allow_symlink); int path_is_read_only_fs(const char *path); int path_is_os_tree(const char *path); diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index a89e8afc2a..bafd483be2 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -103,7 +103,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { } /* Stop at mount points */ - r = fd_is_mount_point(subdir_fd); + r = fd_is_mount_point(fd, de->d_name, 0); if (r < 0) { if (ret == 0 && r != -ENOENT) ret = r; |