summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2015-05-27 09:56:03 +0200
committerMartin Pitt <martin.pitt@ubuntu.com>2015-05-29 17:40:12 +0200
commit5d409034017e9f9f8c4392157d95511fc2e05d87 (patch)
treee0223a0ec6b7ec9c516e00370da305e5f8c2d96a /src/shared
parent2fe9e87a2475d5c2f5e2bb1a7e711c59fd0b2c6d (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.c31
-rw-r--r--src/shared/path-util.h2
-rw-r--r--src/shared/rm-rf.c2
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;