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;
}
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);