mirror of
https://github.com/systemd/systemd.git
synced 2024-10-27 01:55:22 +03:00
mountpoint-util: make fd_is_mountpoint() work reasonably with AT_EMPTYPATH
Inspired by: https://github.com/systemd/systemd/pull/24141 Calling fd_is_mountpoint() with AT_EMPTYPATH and an empty filename can only work if we have new statx() available. If we do not, we can still make things work for directories, but not for other inodes (since there we cannot query information about the parent inode to compare things.) Hence, let's handle and test this explicitly, to support this to the level this is possible.
This commit is contained in:
parent
ff0a5070d4
commit
71c943dc40
@ -182,12 +182,18 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(filename);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
/* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
|
||||
* up or down the tree then immediately below the specified directory fd. */
|
||||
if (!filename_possibly_with_slash_suffix(filename))
|
||||
if (!filename) {
|
||||
/* If the file name is specified as NULL we'll see if the specified 'fd' is a mount
|
||||
* point. That's only supported if the kernel supports statx(), or if the inode specified via
|
||||
* 'fd' refers to a directory. Otherwise, we'll have to fail (ENOTDIR), because we have no
|
||||
* kernel API to query the information we need. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
filename = "";
|
||||
} else if (!filename_possibly_with_slash_suffix(filename))
|
||||
/* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
|
||||
* up or down the tree then immediately below the specified directory fd. */
|
||||
return -EINVAL;
|
||||
|
||||
/* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
|
||||
@ -234,7 +240,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
nosupp = true;
|
||||
}
|
||||
|
||||
r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
|
||||
if (isempty(filename))
|
||||
r = name_to_handle_at_loop(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
|
||||
if (r < 0) {
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
@ -271,7 +280,10 @@ fallback_fdinfo:
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
|
||||
if (isempty(filename))
|
||||
r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -295,7 +307,11 @@ fallback_fstat:
|
||||
if (S_ISLNK(a.st_mode)) /* Symlinks are never mount points */
|
||||
return false;
|
||||
|
||||
if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
|
||||
if (isempty(filename))
|
||||
r = fstatat(fd, "..", &b, 0);
|
||||
else
|
||||
r = fstatat(fd, "", &b, AT_EMPTY_PATH);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
/* A directory with same device and inode as its parent? Must be the root directory */
|
||||
|
@ -265,6 +265,7 @@ TEST(path_is_mount_point) {
|
||||
|
||||
TEST(fd_is_mount_point) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
@ -290,6 +291,22 @@ TEST(fd_is_mount_point) {
|
||||
* the system is borked. Let's allow for it to be missing though. */
|
||||
assert_se(IN_SET(fd_is_mount_point(fd, "root", 0), -ENOENT, 0));
|
||||
assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0));
|
||||
|
||||
safe_close(fd);
|
||||
fd = open("/proc", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
assert_se(fd_is_mount_point(fd, NULL, 0) > 0);
|
||||
assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "version", 0) == 0);
|
||||
|
||||
safe_close(fd);
|
||||
fd = open("/proc/version", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
r = fd_is_mount_point(fd, NULL, 0);
|
||||
assert_se(IN_SET(r, 0, -ENOTDIR)); /* on old kernels we can't determine if regular files are mount points if we have no directory fd */
|
||||
assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
|
Loading…
Reference in New Issue
Block a user