Skip to content

Commit

Permalink
mount-util: make path_get_mount_info() work arbitrary inode
Browse files Browse the repository at this point in the history
Follow-up for d49d95d.
Replaces 9a032ec.
Fixes systemd#35075.
  • Loading branch information
yuwata authored and poettering committed Nov 8, 2024
1 parent 514d9e1 commit 5261c52
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 41 deletions.
92 changes: 55 additions & 37 deletions src/shared/mount-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1808,63 +1808,81 @@ char* umount_and_unlink_and_free(char *p) {
return mfree(p);
}

static int path_get_mount_info(
static int path_get_mount_info_at(
int dir_fd,
const char *path,
char **ret_fstype,
char **ret_options) {

_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_free_ char *fstype = NULL, *options = NULL;
struct libmnt_fs *fs;
int r;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
int r, mnt_id;

assert(path);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);

table = mnt_new_table();
if (!table)
return -ENOMEM;
r = path_get_mnt_id_at(dir_fd, path, &mnt_id);
if (r < 0)
return log_debug_errno(r, "Failed to get mount ID: %m");

r = mnt_table_parse_mtab(table, /* filename = */ NULL);
r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");

fs = mnt_table_find_mountpoint(table, path, MNT_ITER_FORWARD);
if (!fs)
return -EINVAL;
for (;;) {
struct libmnt_fs *fs;

if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
if (!fstype)
return -ENOMEM;
}
r = mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break; /* EOF */
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");

if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
if (!options)
return -ENOMEM;
}
if (mnt_fs_get_id(fs) != mnt_id)
continue;

if (ret_fstype)
*ret_fstype = TAKE_PTR(fstype);
if (ret_options)
*ret_options = TAKE_PTR(options);
_cleanup_free_ char *fstype = NULL, *options = NULL;

return 0;
if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
if (!fstype)
return log_oom_debug();
}

if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
if (!options)
return log_oom_debug();
}

if (ret_fstype)
*ret_fstype = TAKE_PTR(fstype);
if (ret_options)
*ret_options = TAKE_PTR(options);

return 0;
}

return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find mount ID %i from /proc/self/mountinfo.", mnt_id);
}

int path_is_network_fs_harder(const char *path) {
_cleanup_free_ char *fstype = NULL, *options = NULL;
int r, ret;
int path_is_network_fs_harder_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -EBADF;
int r;

assert(path);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);

ret = path_is_network_fs(path);
if (ret > 0)
return true;
fd = xopenat(dir_fd, path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
if (fd < 0)
return fd;

r = fd_is_network_fs(fd);
if (r != 0)
return r;

r = path_get_mount_info(path, &fstype, &options);
_cleanup_free_ char *fstype = NULL, *options = NULL;
r = path_get_mount_info_at(fd, /* path = */ NULL, &fstype, &options);
if (r < 0)
return RET_GATHER(ret, r);
return r;

if (fstype_is_network(fstype))
return true;
Expand Down
5 changes: 4 additions & 1 deletion src/shared/mount-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,7 @@ int mount_credentials_fs(const char *path, size_t size, bool ro);

int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);

int path_is_network_fs_harder(const char *path);
int path_is_network_fs_harder_at(int dir_fd, const char *path);
static inline int path_is_network_fs_harder(const char *path) {
return path_is_network_fs_harder_at(AT_FDCWD, path);
}
50 changes: 47 additions & 3 deletions src/test/test-mount-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,53 @@ TEST(bind_mount_submounts) {
}

TEST(path_is_network_fs_harder) {
ASSERT_OK_ZERO(path_is_network_fs_harder("/dev"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/sys"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/run"));
_cleanup_close_ int dir_fd = -EBADF;
int r;

ASSERT_OK(dir_fd = open("/", O_PATH | O_CLOEXEC));
FOREACH_STRING(s,
"/", "/dev/", "/proc/", "/run/", "/sys/", "/tmp/", "/usr/", "/var/tmp/",
"", ".", "../../../", "/this/path/should/not/exist/for/test-mount-util/") {

r = path_is_network_fs_harder(s);
log_debug("path_is_network_fs_harder(%s) → %i: %s", s, r, r < 0 ? STRERROR(r) : yes_no(r));

const char *q = path_startswith(s, "/") ?: s;
r = path_is_network_fs_harder_at(dir_fd, q);
log_debug("path_is_network_fs_harder_at(root, %s) → %i: %s", q, r, r < 0 ? STRERROR(r) : yes_no(r));
}

if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
(void) log_tests_skipped("not running privileged");
return;
}

_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
assert_se(mkdtemp_malloc("/tmp/test-mount-util.path_is_network_fs_harder.XXXXXXX", &t) >= 0);

r = safe_fork("(make_mount-point)",
FORK_RESET_SIGNALS |
FORK_CLOSE_ALL_FDS |
FORK_DEATHSIG_SIGTERM |
FORK_WAIT |
FORK_REOPEN_LOG |
FORK_LOG |
FORK_NEW_MOUNTNS |
FORK_MOUNTNS_SLAVE,
NULL);
ASSERT_OK(r);

if (r == 0) {
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, NULL));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));

ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, "x-systemd-growfs,x-systemd-automount"));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));

_exit(EXIT_SUCCESS);
}
}

DEFINE_TEST_MAIN(LOG_DEBUG);

0 comments on commit 5261c52

Please sign in to comment.