summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2018-11-29 10:24:39 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2018-11-29 21:03:44 +0100
commit049af8ad0c6d8c6471ad2d13f29514ecc1bf10c8 (patch)
tree8b4782a62b99fd64d831165167985ac336827c50
parentMerge pull request #10987 from poettering/index-md-work-around (diff)
downloadsystemd-049af8ad0c6d8c6471ad2d13f29514ecc1bf10c8.tar.xz
systemd-049af8ad0c6d8c6471ad2d13f29514ecc1bf10c8.zip
Split out part of mount-util.c into mountpoint-util.c
The idea is that anything which is related to actually manipulating mounts is in mount-util.c, but functions for mountpoint introspection are moved to the new file. Anything which requires libmount must be in mount-util.c. This was supposed to be a preparation for further changes, with no functional difference, but it results in a significant change in linkage: $ ldd build/libnss_*.so.2 (before) build/libnss_myhostname.so.2: linux-vdso.so.1 (0x00007fff77bf5000) librt.so.1 => /lib64/librt.so.1 (0x00007f4bbb7b2000) libmount.so.1 => /lib64/libmount.so.1 (0x00007f4bbb755000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4bbb734000) libc.so.6 => /lib64/libc.so.6 (0x00007f4bbb56e000) /lib64/ld-linux-x86-64.so.2 (0x00007f4bbb8c1000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f4bbb51b000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f4bbb512000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f4bbb4e3000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f4bbb45e000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f4bbb458000) build/libnss_mymachines.so.2: linux-vdso.so.1 (0x00007ffc19cc0000) librt.so.1 => /lib64/librt.so.1 (0x00007fdecb74b000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fdecb744000) libmount.so.1 => /lib64/libmount.so.1 (0x00007fdecb6e7000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fdecb6c6000) libc.so.6 => /lib64/libc.so.6 (0x00007fdecb500000) /lib64/ld-linux-x86-64.so.2 (0x00007fdecb8a9000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fdecb4ad000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007fdecb4a2000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fdecb475000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fdecb3f0000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fdecb3ea000) build/libnss_resolve.so.2: linux-vdso.so.1 (0x00007ffe8ef8e000) librt.so.1 => /lib64/librt.so.1 (0x00007fcf314bd000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fcf314b6000) libmount.so.1 => /lib64/libmount.so.1 (0x00007fcf31459000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fcf31438000) libc.so.6 => /lib64/libc.so.6 (0x00007fcf31272000) /lib64/ld-linux-x86-64.so.2 (0x00007fcf31615000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fcf3121f000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007fcf31214000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fcf311e7000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fcf31162000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fcf3115c000) build/libnss_systemd.so.2: linux-vdso.so.1 (0x00007ffda6d17000) librt.so.1 => /lib64/librt.so.1 (0x00007f610b83c000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f610b835000) libmount.so.1 => /lib64/libmount.so.1 (0x00007f610b7d8000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f610b7b7000) libc.so.6 => /lib64/libc.so.6 (0x00007f610b5f1000) /lib64/ld-linux-x86-64.so.2 (0x00007f610b995000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f610b59e000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f610b593000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f610b566000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f610b4e1000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f610b4db000) (after) build/libnss_myhostname.so.2: linux-vdso.so.1 (0x00007fff0b5e2000) librt.so.1 => /lib64/librt.so.1 (0x00007fde0c328000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fde0c307000) libc.so.6 => /lib64/libc.so.6 (0x00007fde0c141000) /lib64/ld-linux-x86-64.so.2 (0x00007fde0c435000) build/libnss_mymachines.so.2: linux-vdso.so.1 (0x00007ffdc30a7000) librt.so.1 => /lib64/librt.so.1 (0x00007f06ecabb000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f06ecab4000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f06eca93000) libc.so.6 => /lib64/libc.so.6 (0x00007f06ec8cd000) /lib64/ld-linux-x86-64.so.2 (0x00007f06ecc15000) build/libnss_resolve.so.2: linux-vdso.so.1 (0x00007ffe95747000) librt.so.1 => /lib64/librt.so.1 (0x00007fa56a80f000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fa56a808000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fa56a7e7000) libc.so.6 => /lib64/libc.so.6 (0x00007fa56a621000) /lib64/ld-linux-x86-64.so.2 (0x00007fa56a964000) build/libnss_systemd.so.2: linux-vdso.so.1 (0x00007ffe67b51000) librt.so.1 => /lib64/librt.so.1 (0x00007ffb32113000) libcap.so.2 => /lib64/libcap.so.2 (0x00007ffb3210c000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffb320eb000) libc.so.6 => /lib64/libc.so.6 (0x00007ffb31f25000) /lib64/ld-linux-x86-64.so.2 (0x00007ffb3226a000) I don't quite understand what is going on here, but let's not be too picky.
-rw-r--r--src/basic/copy.c2
-rw-r--r--src/basic/meson.build2
-rw-r--r--src/basic/mount-util.c428
-rw-r--r--src/basic/mount-util.h26
-rw-r--r--src/basic/mountpoint-util.c444
-rw-r--r--src/basic/mountpoint-util.h24
-rw-r--r--src/basic/rm-rf.c2
-rw-r--r--src/core/automount.c1
-rw-r--r--src/core/dbus-execute.c2
-rw-r--r--src/core/load-fragment.c2
-rw-r--r--src/core/machine-id-setup.c2
-rw-r--r--src/core/mount-setup.c2
-rw-r--r--src/core/mount.c2
-rw-r--r--src/core/namespace.c1
-rw-r--r--src/core/umount.c1
-rw-r--r--src/fstab-generator/fstab-generator.c1
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rw-r--r--src/libsystemd/sd-device/device-monitor.c2
-rw-r--r--src/login/user-runtime-dir.c2
-rw-r--r--src/mount/mount-tool.c1
-rw-r--r--src/nspawn/nspawn-cgroup.c1
-rw-r--r--src/nspawn/nspawn-mount.c1
-rw-r--r--src/nspawn/nspawn.c1
-rw-r--r--src/partition/growfs.c2
-rw-r--r--src/shared/bus-unit-util.c2
-rw-r--r--src/shared/bus-util.c2
-rw-r--r--src/shared/condition.c2
-rw-r--r--src/shared/dissect-image.c1
-rw-r--r--src/shared/switch-root.c1
-rw-r--r--src/test/meson.build4
-rw-r--r--src/test/test-mount-util.c244
-rw-r--r--src/test/test-mountpoint-util.c258
-rw-r--r--src/test/test-path-util.c2
-rw-r--r--src/test/test-stat-util.c2
-rw-r--r--src/tmpfiles/tmpfiles.c2
-rw-r--r--src/volatile-root/volatile-root.c1
36 files changed, 762 insertions, 713 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 7a4db459e3..50240de8b6 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -24,7 +24,7 @@
#include "io-util.h"
#include "macro.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
diff --git a/src/basic/meson.build b/src/basic/meson.build
index b13897f334..906d1c1b3d 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -96,6 +96,8 @@ basic_sources = files('''
mkdir.h
mount-util.c
mount-util.h
+ mountpoint-util.c
+ mountpoint-util.h
nss-util.h
ordered-set.c
ordered-set.h
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index ec607c7360..9fa995f693 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -20,6 +20,7 @@
#include "fs-util.h"
#include "hashmap.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "set.h"
@@ -27,279 +28,6 @@
#include "string-util.h"
#include "strv.h"
-/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
- * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
- * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
- * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
- * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
- * with large file handles anyway. */
-#define ORIGINAL_MAX_HANDLE_SZ 128
-
-int name_to_handle_at_loop(
- int fd,
- const char *path,
- struct file_handle **ret_handle,
- int *ret_mnt_id,
- int flags) {
-
- _cleanup_free_ struct file_handle *h = NULL;
- size_t n = ORIGINAL_MAX_HANDLE_SZ;
-
- /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
- * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
- * start value, it is not an upper bound on the buffer size required.
- *
- * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
- * as NULL if there's no interest in either. */
-
- for (;;) {
- int mnt_id = -1;
-
- h = malloc0(offsetof(struct file_handle, f_handle) + n);
- if (!h)
- return -ENOMEM;
-
- h->handle_bytes = n;
-
- if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
-
- if (ret_handle)
- *ret_handle = TAKE_PTR(h);
-
- if (ret_mnt_id)
- *ret_mnt_id = mnt_id;
-
- return 0;
- }
- if (errno != EOVERFLOW)
- return -errno;
-
- if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
-
- /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
- * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
- * be filled in, and the caller was interested in only the mount ID an nothing else. */
-
- *ret_mnt_id = mnt_id;
- return 0;
- }
-
- /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
- * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
- * buffer. In that case propagate EOVERFLOW */
- if (h->handle_bytes <= n)
- return -EOVERFLOW;
-
- /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
- n = h->handle_bytes;
- if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
- return -EOVERFLOW;
-
- h = mfree(h);
- }
-}
-
-static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
- char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *fdinfo = NULL;
- _cleanup_close_ int subfd = -1;
- char *p;
- int r;
-
- if ((flags & AT_EMPTY_PATH) && isempty(filename))
- xsprintf(path, "/proc/self/fdinfo/%i", fd);
- else {
- subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
- if (subfd < 0)
- return -errno;
-
- xsprintf(path, "/proc/self/fdinfo/%i", subfd);
- }
-
- r = read_full_file(path, &fdinfo, NULL);
- if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
- return -EOPNOTSUPP;
- if (r < 0)
- return r;
-
- p = startswith(fdinfo, "mnt_id:");
- if (!p) {
- p = strstr(fdinfo, "\nmnt_id:");
- if (!p) /* The mnt_id field is a relatively new addition */
- return -EOPNOTSUPP;
-
- p += 8;
- }
-
- p += strspn(p, WHITESPACE);
- p[strcspn(p, WHITESPACE)] = 0;
-
- return safe_atoi(p, mnt_id);
-}
-
-int fd_is_mount_point(int fd, const char *filename, int flags) {
- _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
- int mount_id = -1, mount_id_parent = -1;
- bool nosupp = false, check_st_dev = true;
- struct stat a, b;
- 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
- * not supported everywhere though (kernel compile-time
- * option, not all file systems are hooked up). If it works
- * the mount id is usually good enough to tell us whether
- * something is a mount point.
- *
- * If that didn't work we will try to read the mount id from
- * /proc/self/fdinfo/<fd>. This is almost as good as
- * name_to_handle_at(), however, does not return the
- * opaque file handle. The opaque file handle is pretty useful
- * to detect the root directory, which we should always
- * consider a mount point. Hence we use this only as
- * fallback. Exporting the mnt_id in fdinfo is a pretty recent
- * kernel addition.
- *
- * As last fallback we do traditional fstat() based st_dev
- * comparisons. This is how things were traditionally done,
- * but unionfs breaks this since it exposes file
- * systems with a variety of st_dev reported. Also, btrfs
- * subvolumes have different st_dev, even though they aren't
- * real mounts of their own. */
-
- r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
- if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
- /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
- * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
- * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
- * (EINVAL): fall back to simpler logic. */
- goto fallback_fdinfo;
- else if (r == -EOPNOTSUPP)
- /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
- * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
- * logic */
- nosupp = true;
- else if (r < 0)
- return r;
-
- r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
- if (r == -EOPNOTSUPP) {
- if (nosupp)
- /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
- goto fallback_fdinfo;
- else
- /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
- * it must be a mount point. */
- return 1;
- } else if (r < 0)
- return r;
-
- /* The parent can do name_to_handle_at() but the
- * directory we are interested in can't? If so, it
- * must be a mount point. */
- if (nosupp)
- return 1;
-
- /* If the file handle for the directory we are
- * interested in and its parent are identical, we
- * assume this is the root directory, which is a mount
- * point. */
-
- if (h->handle_bytes == h_parent->handle_bytes &&
- h->handle_type == h_parent->handle_type &&
- memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
- return 1;
-
- return mount_id != mount_id_parent;
-
-fallback_fdinfo:
- r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
- goto fallback_fstat;
- if (r < 0)
- return r;
-
- r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
- if (r < 0)
- return r;
-
- if (mount_id != mount_id_parent)
- return 1;
-
- /* Hmm, so, the mount ids are the same. This leaves one
- * special case though for the root file system. For that,
- * let's see if the parent directory has the same inode as we
- * are interested in. Hence, let's also do fstat() checks now,
- * too, but avoid the st_dev comparisons, since they aren't
- * that useful on unionfs mounts. */
- check_st_dev = false;
-
-fallback_fstat:
- /* 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, AT_EMPTY_PATH) < 0)
- return -errno;
-
- /* A directory with same device and inode as its parent? Must
- * be the root directory */
- if (a.st_dev == b.st_dev &&
- a.st_ino == b.st_ino)
- return 1;
-
- return check_st_dev && (a.st_dev != b.st_dev);
-}
-
-/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, const char *root, int flags) {
- _cleanup_free_ char *canonical = NULL;
- _cleanup_close_ int fd = -1;
- int r;
-
- assert(t);
- assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
-
- if (path_equal(t, "/"))
- return 1;
-
- /* we need to resolve symlinks manually, we can't just rely on
- * fd_is_mount_point() to do that for us; if we have a structure like
- * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
- * look at needs to be /usr, not /. */
- if (flags & AT_SYMLINK_FOLLOW) {
- r = chase_symlinks(t, root, CHASE_TRAIL_SLASH, &canonical);
- if (r < 0)
- return r;
-
- t = canonical;
- }
-
- fd = open_parent(t, O_PATH|O_CLOEXEC, 0);
- if (fd < 0)
- return -errno;
-
- return fd_is_mount_point(fd, last_path_component(t), flags);
-}
-
-int path_get_mnt_id(const char *path, int *ret) {
- int r;
-
- r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
- if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
- return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
-
- return r;
-}
-
int umount_recursive(const char *prefix, int flags) {
bool again;
int n = 0, r;
@@ -601,86 +329,6 @@ int mount_move_root(const char *path) {
return 0;
}
-bool fstype_is_network(const char *fstype) {
- const char *x;
-
- x = startswith(fstype, "fuse.");
- if (x)
- fstype = x;
-
- return STR_IN_SET(fstype,
- "afs",
- "cifs",
- "smbfs",
- "sshfs",
- "ncpfs",
- "ncp",
- "nfs",
- "nfs4",
- "gfs",
- "gfs2",
- "glusterfs",
- "pvfs2", /* OrangeFS */
- "ocfs2",
- "lustre");
-}
-
-bool fstype_is_api_vfs(const char *fstype) {
- return STR_IN_SET(fstype,
- "autofs",
- "bpf",
- "cgroup",
- "cgroup2",
- "configfs",
- "cpuset",
- "debugfs",
- "devpts",
- "devtmpfs",
- "efivarfs",
- "fusectl",
- "hugetlbfs",
- "mqueue",
- "proc",
- "pstore",
- "ramfs",
- "securityfs",
- "sysfs",
- "tmpfs",
- "tracefs");
-}
-
-bool fstype_is_ro(const char *fstype) {
- /* All Linux file systems that are necessarily read-only */
- return STR_IN_SET(fstype,
- "DM_verity_hash",
- "iso9660",
- "squashfs");
-}
-
-bool fstype_can_discard(const char *fstype) {
- return STR_IN_SET(fstype,
- "btrfs",
- "ext4",
- "vfat",
- "xfs");
-}
-
-bool fstype_can_uid_gid(const char *fstype) {
-
- /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
- * current and future. */
-
- return STR_IN_SET(fstype,
- "adfs",
- "fat",
- "hfs",
- "hpfs",
- "iso9660",
- "msdos",
- "ntfs",
- "vfat");
-}
-
int repeat_unmount(const char *path, int flags) {
bool done = false;
@@ -852,37 +500,6 @@ int umount_verbose(const char *what) {
return 0;
}
-const char *mount_propagation_flags_to_string(unsigned long flags) {
-
- switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
- case 0:
- return "";
- case MS_SHARED:
- return "shared";
- case MS_SLAVE:
- return "slave";
- case MS_PRIVATE:
- return "private";
- }
-
- return NULL;
-}
-
-int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
-
- if (isempty(name))
- *ret = 0;
- else if (streq(name, "shared"))
- *ret = MS_SHARED;
- else if (streq(name, "slave"))
- *ret = MS_SLAVE;
- else if (streq(name, "private"))
- *ret = MS_PRIVATE;
- else
- return -EINVAL;
- return 0;
-}
-
int mount_option_mangle(
const char *options,
unsigned long mount_flags,
@@ -951,46 +568,3 @@ int mount_option_mangle(
return 0;
}
-
-int dev_is_devtmpfs(void) {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
- int mount_id, r;
- char *e;
-
- r = path_get_mnt_id("/dev", &mount_id);
- if (r < 0)
- return r;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
-
- for (;;) {
- _cleanup_free_ char *line = NULL;
- int mid;
-
- r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (sscanf(line, "%i", &mid) != 1)
- continue;
-
- if (mid != mount_id)
- continue;
-
- e = strstr(line, " - ");
- if (!e)
- continue;
-
- /* accept any name that starts with the currently expected type */
- if (startswith(e + 3, "devtmpfs"))
- return true;
- }
-
- return false;
-}
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
index ad8c47cb32..00df1b0e55 100644
--- a/src/basic/mount-util.h
+++ b/src/basic/mount-util.h
@@ -1,25 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <fcntl.h>
#include <mntent.h>
-#include <stdbool.h>
#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "macro.h"
-#include "missing.h"
-
-int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
-
-int path_get_mnt_id(const char *path, int *ret);
-
-int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, const char *root, int flags);
int repeat_unmount(const char *path, int flags);
-
int umount_recursive(const char *target, int flags);
int bind_remount_recursive(const char *prefix, bool ro, char **blacklist);
int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo);
@@ -29,14 +16,6 @@ int mount_move_root(const char *path);
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
#define _cleanup_endmntent_ _cleanup_(endmntentp)
-bool fstype_is_network(const char *fstype);
-bool fstype_is_api_vfs(const char *fstype);
-bool fstype_is_ro(const char *fsype);
-bool fstype_can_discard(const char *fstype);
-bool fstype_can_uid_gid(const char *fstype);
-
-const char* mode_to_inaccessible_node(mode_t mode);
-
int mount_verbose(
int error_log_level,
const char *what,
@@ -46,13 +25,10 @@ int mount_verbose(
const char *options);
int umount_verbose(const char *where);
-const char *mount_propagation_flags_to_string(unsigned long flags);
-int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
-
int mount_option_mangle(
const char *options,
unsigned long mount_flags,
unsigned long *ret_mount_flags,
char **ret_remaining_options);
-int dev_is_devtmpfs(void);
+const char* mode_to_inaccessible_node(mode_t mode);
diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
new file mode 100644
index 0000000000..0a9f479e42
--- /dev/null
+++ b/src/basic/mountpoint-util.c
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio_ext.h>
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "missing.h"
+#include "mountpoint-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+
+/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+ * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+ * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
+ * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
+ * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
+ * with large file handles anyway. */
+#define ORIGINAL_MAX_HANDLE_SZ 128
+
+int name_to_handle_at_loop(
+ int fd,
+ const char *path,
+ struct file_handle **ret_handle,
+ int *ret_mnt_id,
+ int flags) {
+
+ _cleanup_free_ struct file_handle *h = NULL;
+ size_t n = ORIGINAL_MAX_HANDLE_SZ;
+
+ /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
+ * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
+ * start value, it is not an upper bound on the buffer size required.
+ *
+ * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
+ * as NULL if there's no interest in either. */
+
+ for (;;) {
+ int mnt_id = -1;
+
+ h = malloc0(offsetof(struct file_handle, f_handle) + n);
+ if (!h)
+ return -ENOMEM;
+
+ h->handle_bytes = n;
+
+ if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
+
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(h);
+
+ if (ret_mnt_id)
+ *ret_mnt_id = mnt_id;
+
+ return 0;
+ }
+ if (errno != EOVERFLOW)
+ return -errno;
+
+ if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
+
+ /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
+ * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
+ * be filled in, and the caller was interested in only the mount ID an nothing else. */
+
+ *ret_mnt_id = mnt_id;
+ return 0;
+ }
+
+ /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
+ * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
+ * buffer. In that case propagate EOVERFLOW */
+ if (h->handle_bytes <= n)
+ return -EOVERFLOW;
+
+ /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
+ n = h->handle_bytes;
+ if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
+ return -EOVERFLOW;
+
+ h = mfree(h);
+ }
+}
+
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+ char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_close_ int subfd = -1;
+ char *p;
+ int r;
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ r = read_full_file(path, &fdinfo, NULL);
+ if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+ return -EOPNOTSUPP;
+ if (r < 0)
+ return r;
+
+ p = startswith(fdinfo, "mnt_id:");
+ if (!p) {
+ p = strstr(fdinfo, "\nmnt_id:");
+ if (!p) /* The mnt_id field is a relatively new addition */
+ return -EOPNOTSUPP;
+
+ p += 8;
+ }
+
+ p += strspn(p, WHITESPACE);
+ p[strcspn(p, WHITESPACE)] = 0;
+
+ return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+ _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
+ int mount_id = -1, mount_id_parent = -1;
+ bool nosupp = false, check_st_dev = true;
+ struct stat a, b;
+ 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
+ * not supported everywhere though (kernel compile-time
+ * option, not all file systems are hooked up). If it works
+ * the mount id is usually good enough to tell us whether
+ * something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from
+ * /proc/self/fdinfo/<fd>. This is almost as good as
+ * name_to_handle_at(), however, does not return the
+ * opaque file handle. The opaque file handle is pretty useful
+ * to detect the root directory, which we should always
+ * consider a mount point. Hence we use this only as
+ * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+ * kernel addition.
+ *
+ * As last fallback we do traditional fstat() based st_dev
+ * comparisons. This is how things were traditionally done,
+ * but unionfs breaks this since it exposes file
+ * systems with a variety of st_dev reported. Also, btrfs
+ * subvolumes have different st_dev, even though they aren't
+ * real mounts of their own. */
+
+ r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
+ if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
+ /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
+ * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
+ * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
+ * (EINVAL): fall back to simpler logic. */
+ goto fallback_fdinfo;
+ else if (r == -EOPNOTSUPP)
+ /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
+ * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
+ * logic */
+ nosupp = true;
+ else if (r < 0)
+ return r;
+
+ r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+ if (r == -EOPNOTSUPP) {
+ if (nosupp)
+ /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
+ goto fallback_fdinfo;
+ else
+ /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
+ * it must be a mount point. */
+ return 1;
+ } else if (r < 0)
+ return r;
+
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ if (nosupp)
+ return 1;
+
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h->handle_bytes == h_parent->handle_bytes &&
+ h->handle_type == h_parent->handle_type &&
+ memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
+ goto fallback_fstat;
+ if (r < 0)
+ return r;
+
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+
+ /* Hmm, so, the mount ids are the same. This leaves one
+ * special case though for the root file system. For that,
+ * let's see if the parent directory has the same inode as we
+ * are interested in. Hence, let's also do fstat() checks now,
+ * too, but avoid the st_dev comparisons, since they aren't
+ * that useful on unionfs mounts. */
+ check_st_dev = false;
+
+fallback_fstat:
+ /* 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, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
+ return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, const char *root, int flags) {
+ _cleanup_free_ char *canonical = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(t);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+ if (path_equal(t, "/"))
+ return 1;
+
+ /* we need to resolve symlinks manually, we can't just rely on
+ * fd_is_mount_point() to do that for us; if we have a structure like
+ * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ * look at needs to be /usr, not /. */
+ if (flags & AT_SYMLINK_FOLLOW) {
+ r = chase_symlinks(t, root, CHASE_TRAIL_SLASH, &canonical);
+ if (r < 0)
+ return r;
+
+ t = canonical;
+ }
+
+ fd = open_parent(t, O_PATH|O_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_mount_point(fd, last_path_component(t), flags);
+}
+
+int path_get_mnt_id(const char *path, int *ret) {
+ int r;
+
+ r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
+ if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
+ return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
+
+ return r;
+}
+
+bool fstype_is_network(const char *fstype) {
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ return STR_IN_SET(fstype,
+ "afs",
+ "cifs",
+ "smbfs",
+ "sshfs",
+ "ncpfs",
+ "ncp",
+ "nfs",
+ "nfs4",
+ "gfs",
+ "gfs2",
+ "glusterfs",
+ "pvfs2", /* OrangeFS */
+ "ocfs2",
+ "lustre");
+}
+
+bool fstype_is_api_vfs(const char *fstype) {
+ return STR_IN_SET(fstype,
+ "autofs",
+ "bpf",
+ "cgroup",
+ "cgroup2",
+ "configfs",
+ "cpuset",
+ "debugfs",
+ "devpts",
+ "devtmpfs",
+ "efivarfs",
+ "fusectl",
+ "hugetlbfs",
+ "mqueue",
+ "proc",
+ "pstore",
+ "ramfs",
+ "securityfs",
+ "sysfs",
+ "tmpfs",
+ "tracefs");
+}
+
+bool fstype_is_ro(const char *fstype) {
+ /* All Linux file systems that are necessarily read-only */
+ return STR_IN_SET(fstype,
+ "DM_verity_hash",
+ "iso9660",
+ "squashfs");
+}
+
+bool fstype_can_discard(const char *fstype) {
+ return STR_IN_SET(fstype,
+ "btrfs",
+ "ext4",
+ "vfat",
+ "xfs");
+}
+
+bool fstype_can_uid_gid(const char *fstype) {
+
+ /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
+ * current and future. */
+
+ return STR_IN_SET(fstype,
+ "adfs",
+ "fat",
+ "hfs",
+ "hpfs",
+ "iso9660",
+ "msdos",
+ "ntfs",
+ "vfat");
+}
+
+int dev_is_devtmpfs(void) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ int mount_id, r;
+ char *e;
+
+ r = path_get_mnt_id("/dev", &mount_id);
+ if (r < 0)
+ return r;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ int mid;
+
+ r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (sscanf(line, "%i", &mid) != 1)
+ continue;
+
+ if (mid != mount_id)
+ continue;
+
+ e = strstr(line, " - ");
+ if (!e)
+ continue;
+
+ /* accept any name that starts with the currently expected type */
+ if (startswith(e + 3, "devtmpfs"))
+ return true;
+ }
+
+ return false;
+}
+
+const char *mount_propagation_flags_to_string(unsigned long flags) {
+
+ switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
+ case 0:
+ return "";
+ case MS_SHARED:
+ return "shared";
+ case MS_SLAVE:
+ return "slave";
+ case MS_PRIVATE:
+ return "private";
+ }
+
+ return NULL;
+}
+
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
+
+ if (isempty(name))
+ *ret = 0;
+ else if (streq(name, "shared"))
+ *ret = MS_SHARED;
+ else if (streq(name, "slave"))
+ *ret = MS_SLAVE;
+ else if (streq(name, "private"))
+ *ret = MS_PRIVATE;
+ else
+ return -EINVAL;
+ return 0;
+}
diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h
new file mode 100644
index 0000000000..5398836fed
--- /dev/null
+++ b/src/basic/mountpoint-util.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+
+int path_get_mnt_id(const char *path, int *ret);
+
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, const char *root, int flags);
+
+bool fstype_is_network(const char *fstype);
+bool fstype_is_api_vfs(const char *fstype);
+bool fstype_is_ro(const char *fsype);
+bool fstype_can_discard(const char *fstype);
+bool fstype_can_uid_gid(const char *fstype);
+
+int dev_is_devtmpfs(void);
+
+const char *mount_propagation_flags_to_string(unsigned long flags);
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index 1503f1032a..0c957c9b3a 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -15,7 +15,7 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stat-util.h"
diff --git a/src/core/automount.c b/src/core/automount.c
index 5c1748542e..3d8348e0b7 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -23,6 +23,7 @@
#include "mkdir.h"
#include "mount-util.h"
#include "mount.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 31f9099b98..11301e4b69 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -27,7 +27,7 @@
#include "ioprio.h"
#include "journal-util.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace.h"
#include "parse-util.h"
#include "path-util.h"
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 067d36dad5..273d0b96fc 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -38,7 +38,7 @@
#include "load-fragment.h"
#include "log.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 4dfdc475ba..aae548064e 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -15,7 +15,7 @@
#include "machine-id-setup.h"
#include "macro.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "process-util.h"
#include "stat-util.h"
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index e15d94d98a..a118d83b1b 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -20,7 +20,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-setup.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "set.h"
#include "smack-util.h"
diff --git a/src/core/mount.c b/src/core/mount.c
index 8c19150112..99b2aa0904 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -20,8 +20,8 @@
#include "manager.h"
#include "mkdir.h"
#include "mount-setup.h"
-#include "mount-util.h"
#include "mount.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 01516072a0..c2ca3e0334 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -20,6 +20,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace.h"
#include "path-util.h"
#include "selinux-util.h"
diff --git a/src/core/umount.c b/src/core/umount.c
index bd4e4e7257..7af0195aab 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -25,6 +25,7 @@
#include "linux-3.13/dm-ioctl.h"
#include "mount-setup.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index c74ad075c0..85cc36d80d 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -18,6 +18,7 @@
#include "mkdir.h"
#include "mount-setup.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 1ca071e999..d188927398 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -23,7 +23,7 @@
#include "gpt.h"
#include "missing.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c
index 3ab3ce0dca..fcf9b761ee 100644
--- a/src/libsystemd/sd-device/device-monitor.c
+++ b/src/libsystemd/sd-device/device-monitor.c
@@ -18,7 +18,7 @@
#include "hashmap.h"
#include "io-util.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "set.h"
#include "socket-util.h"
#include "string-util.h"
diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c
index edb62a2d30..5e58e4baad 100644
--- a/src/login/user-runtime-dir.c
+++ b/src/login/user-runtime-dir.c
@@ -10,7 +10,7 @@
#include "label.h"
#include "main-func.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "selinux-util.h"
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index 4d53bc5597..bbbc91c38e 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -17,6 +17,7 @@
#include "fstab-util.h"
#include "main-func.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index 53c42f0ee4..97fa092cae 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -8,6 +8,7 @@
#include "fs-util.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nspawn-cgroup.h"
#include "nspawn-mount.h"
#include "path-util.h"
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 48187079b3..f85c0d9ab3 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -11,6 +11,7 @@
#include "label.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nspawn-mount.h"
#include "parse-util.h"
#include "path-util.h"
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 6eddae49d9..8c6a74bea2 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -60,6 +60,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "netlink-util.h"
#include "nspawn-cgroup.h"
#include "nspawn-def.h"
diff --git a/src/partition/growfs.c b/src/partition/growfs.c
index 8e04eb3c23..7e5642fe80 100644
--- a/src/partition/growfs.c
+++ b/src/partition/growfs.c
@@ -19,7 +19,7 @@
#include "format-util.h"
#include "log.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index d11b8c26c7..78a2be341c 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -17,7 +17,7 @@
#include "in-addr-util.h"
#include "list.h"
#include "locale-util.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "path-util.h"
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 760be38c85..b9ef0243a3 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -28,7 +28,7 @@
#include "escape.h"
#include "fd-util.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "proc-cmdline.h"
diff --git a/src/shared/condition.c b/src/shared/condition.c
index ecc32beadf..35c9aa4caf 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -30,7 +30,7 @@
#include "ima-util.h"
#include "list.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 18620a3b19..384c7ecf4e 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -26,6 +26,7 @@
#include "id128-util.h"
#include "linux-3.13/dm-ioctl.h"
#include "missing.h"
+#include "mountpoint-util.h"
#include "mount-util.h"
#include "os-util.h"
#include "path-util.h"
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index c0dfa4bc48..ee31c448fc 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -16,6 +16,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stdio-util.h"
diff --git a/src/test/meson.build b/src/test/meson.build
index ade905733e..410f4fc344 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -213,6 +213,10 @@ tests += [
[],
[]],
+ [['src/test/test-mountpoint-util.c'],
+ [],
+ []],
+
[['src/test/test-exec-util.c'],
[],
[]],
diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c
index 56e385aa11..6986405dc6 100644
--- a/src/test/test-mount-util.c
+++ b/src/test/test-mount-util.c
@@ -3,243 +3,10 @@
#include <sys/mount.h>
#include "alloc-util.h"
-#include "def.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "hashmap.h"
-#include "log.h"
-#include "log.h"
#include "mount-util.h"
-#include "path-util.h"
-#include "rm-rf.h"
#include "string-util.h"
#include "tests.h"
-static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
- long unsigned flags;
-
- assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
-
- if (ret >= 0) {
- const char *c;
-
- assert_se(flags == expected);
-
- c = mount_propagation_flags_to_string(flags);
- if (isempty(name))
- assert_se(isempty(c));
- else
- assert_se(streq(c, name));
- }
-}
-
-static void test_mnt_id(void) {
- _cleanup_fclose_ FILE *f = NULL;
- Hashmap *h;
- Iterator i;
- char *p;
- void *k;
- int r;
-
- assert_se(f = fopen("/proc/self/mountinfo", "re"));
- assert_se(h = hashmap_new(&trivial_hash_ops));
-
- for (;;) {
- _cleanup_free_ char *line = NULL, *path = NULL;
- int mnt_id;
-
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r == 0)
- break;
- assert_se(r > 0);
-
- assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
-
- assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
- path = NULL;
- }
-
- HASHMAP_FOREACH_KEY(p, k, h, i) {
- int mnt_id = PTR_TO_INT(k), mnt_id2;
-
- r = path_get_mnt_id(p, &mnt_id2);
- if (r < 0) {
- log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
- continue;
- }
-
- log_debug("mnt id of %s is %i\n", p, mnt_id2);
-
- if (mnt_id == mnt_id2)
- continue;
-
- /* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
- * the case */
- assert_se(path_equal_ptr(hashmap_get(h, INT_TO_PTR(mnt_id2)), p));
- }
-
- hashmap_free_free(h);
-}
-
-static void test_path_is_mount_point(void) {
- int fd;
- char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
- _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
- _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
- _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
-
- assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/", NULL, 0) > 0);
- assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("//", NULL, 0) > 0);
-
- assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
- assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
-
- assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
- assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
-
- assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
-
- /* we'll create a hierarchy of different kinds of dir/file/link
- * layouts:
- *
- * <tmp>/file1, <tmp>/file2
- * <tmp>/link1 -> file1, <tmp>/link2 -> file2
- * <tmp>/dir1/
- * <tmp>/dir1/file
- * <tmp>/dirlink1 -> dir1
- * <tmp>/dirlink1file -> dirlink1/file
- * <tmp>/dir2/
- * <tmp>/dir2/file
- */
-
- /* file mountpoints */
- assert_se(mkdtemp(tmp_dir) != NULL);
- file1 = path_join(NULL, tmp_dir, "file1");
- assert_se(file1);
- file2 = path_join(NULL, tmp_dir, "file2");
- assert_se(file2);
- fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
- fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
- link1 = path_join(NULL, tmp_dir, "link1");
- assert_se(link1);
- assert_se(symlink("file1", link1) == 0);
- link2 = path_join(NULL, tmp_dir, "link2");
- assert_se(link1);
- assert_se(symlink("file2", link2) == 0);
-
- assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(file1, NULL, 0) == 0);
- assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(link1, NULL, 0) == 0);
-
- /* directory mountpoints */
- dir1 = path_join(NULL, tmp_dir, "dir1");
- assert_se(dir1);
- assert_se(mkdir(dir1, 0755) == 0);
- dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
- assert_se(dirlink1);
- assert_se(symlink("dir1", dirlink1) == 0);
- dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
- assert_se(dirlink1file);
- assert_se(symlink("dirlink1/file", dirlink1file) == 0);
- dir2 = path_join(NULL, tmp_dir, "dir2");
- assert_se(dir2);
- assert_se(mkdir(dir2, 0755) == 0);
-
- assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
- assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
-
- /* file in subdirectory mountpoints */
- dir1file = path_join(NULL, dir1, "file");
- assert_se(dir1file);
- fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
-
- assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
- assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
-
- /* these tests will only work as root */
- if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
- int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
- const char *file2d;
-
- /* files */
- /* capture results in vars, to avoid dangling mounts on failure */
- log_info("%s: %s", __func__, file2);
- rf = path_is_mount_point(file2, NULL, 0);
- rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
-
- file2d = strjoina(file2, "/");
- log_info("%s: %s", __func__, file2d);
- rdf = path_is_mount_point(file2d, NULL, 0);
- rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
-
- log_info("%s: %s", __func__, link2);
- rlf = path_is_mount_point(link2, NULL, 0);
- rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
-
- assert_se(umount(file2) == 0);
-
- assert_se(rf == 1);
- assert_se(rt == 1);
- assert_se(rdf == -ENOTDIR);
- assert_se(rdt == -ENOTDIR);
- assert_se(rlf == 0);
- assert_se(rlt == 1);
-
- /* dirs */
- dir2file = path_join(NULL, dir2, "file");
- assert_se(dir2file);
- fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
-
- assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
-
- log_info("%s: %s", __func__, dir1);
- rf = path_is_mount_point(dir1, NULL, 0);
- rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
- log_info("%s: %s", __func__, dirlink1);
- rlf = path_is_mount_point(dirlink1, NULL, 0);
- rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
- log_info("%s: %s", __func__, dirlink1file);
- /* its parent is a mount point, but not /file itself */
- rl1f = path_is_mount_point(dirlink1file, NULL, 0);
- rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
-
- assert_se(umount(dir1) == 0);
-
- assert_se(rf == 1);
- assert_se(rt == 1);
- assert_se(rlf == 0);
- assert_se(rlt == 1);
- assert_se(rl1f == 0);
- assert_se(rl1t == 0);
-
- } else
- printf("Skipping bind mount file test: %m\n");
-
- assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
-}
-
static void test_mount_option_mangle(void) {
char *opts = NULL;
unsigned long f;
@@ -295,19 +62,8 @@ static void test_mount_option_mangle(void) {
}
int main(int argc, char *argv[]) {
-
test_setup_logging(LOG_DEBUG);
- test_mount_propagation_flags("shared", 0, MS_SHARED);
- test_mount_propagation_flags("slave", 0, MS_SLAVE);
- test_mount_propagation_flags("private", 0, MS_PRIVATE);
- test_mount_propagation_flags(NULL, 0, 0);
- test_mount_propagation_flags("", 0, 0);
- test_mount_propagation_flags("xxxx", -EINVAL, 0);
- test_mount_propagation_flags(" ", -EINVAL, 0);
-
- test_mnt_id();
- test_path_is_mount_point();
test_mount_option_mangle();
return 0;
diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c
new file mode 100644
index 0000000000..b8b37cf600
--- /dev/null
+++ b/src/test/test-mountpoint-util.c
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "def.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "log.h"
+#include "log.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
+ long unsigned flags;
+
+ assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
+
+ if (ret >= 0) {
+ const char *c;
+
+ assert_se(flags == expected);
+
+ c = mount_propagation_flags_to_string(flags);
+ if (isempty(name))
+ assert_se(isempty(c));
+ else
+ assert_se(streq(c, name));
+ }
+}
+
+static void test_mnt_id(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ Hashmap *h;
+ Iterator i;
+ char *p;
+ void *k;
+ int r;
+
+ assert_se(f = fopen("/proc/self/mountinfo", "re"));
+ assert_se(h = hashmap_new(&trivial_hash_ops));
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL, *path = NULL;
+ int mnt_id;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r == 0)
+ break;
+ assert_se(r > 0);
+
+ assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
+
+ assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
+ path = NULL;
+ }
+
+ HASHMAP_FOREACH_KEY(p, k, h, i) {
+ int mnt_id = PTR_TO_INT(k), mnt_id2;
+
+ r = path_get_mnt_id(p, &mnt_id2);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
+ continue;
+ }
+
+ log_debug("mnt id of %s is %i\n", p, mnt_id2);
+
+ if (mnt_id == mnt_id2)
+ continue;
+
+ /* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
+ * the case */
+ assert_se(path_equal_ptr(hashmap_get(h, INT_TO_PTR(mnt_id2)), p));
+ }
+
+ hashmap_free_free(h);
+}
+
+static void test_path_is_mount_point(void) {
+ int fd;
+ char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+ _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+ _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
+ _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
+
+ assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/", NULL, 0) > 0);
+ assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("//", NULL, 0) > 0);
+
+ assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
+ assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
+
+ assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
+ assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
+
+ assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
+ assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
+
+ /* we'll create a hierarchy of different kinds of dir/file/link
+ * layouts:
+ *
+ * <tmp>/file1, <tmp>/file2
+ * <tmp>/link1 -> file1, <tmp>/link2 -> file2
+ * <tmp>/dir1/
+ * <tmp>/dir1/file
+ * <tmp>/dirlink1 -> dir1
+ * <tmp>/dirlink1file -> dirlink1/file
+ * <tmp>/dir2/
+ * <tmp>/dir2/file
+ */
+
+ /* file mountpoints */
+ assert_se(mkdtemp(tmp_dir) != NULL);
+ file1 = path_join(NULL, tmp_dir, "file1");
+ assert_se(file1);
+ file2 = path_join(NULL, tmp_dir, "file2");
+ assert_se(file2);
+ fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ link1 = path_join(NULL, tmp_dir, "link1");
+ assert_se(link1);
+ assert_se(symlink("file1", link1) == 0);
+ link2 = path_join(NULL, tmp_dir, "link2");
+ assert_se(link1);
+ assert_se(symlink("file2", link2) == 0);
+
+ assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(file1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(link1, NULL, 0) == 0);
+
+ /* directory mountpoints */
+ dir1 = path_join(NULL, tmp_dir, "dir1");
+ assert_se(dir1);
+ assert_se(mkdir(dir1, 0755) == 0);
+ dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
+ assert_se(dirlink1);
+ assert_se(symlink("dir1", dirlink1) == 0);
+ dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
+ assert_se(dirlink1file);
+ assert_se(symlink("dirlink1/file", dirlink1file) == 0);
+ dir2 = path_join(NULL, tmp_dir, "dir2");
+ assert_se(dir2);
+ assert_se(mkdir(dir2, 0755) == 0);
+
+ assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
+
+ /* file in subdirectory mountpoints */
+ dir1file = path_join(NULL, dir1, "file");
+ assert_se(dir1file);
+ fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+
+ assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
+
+ /* these tests will only work as root */
+ if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+ int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
+ const char *file2d;
+
+ /* files */
+ /* capture results in vars, to avoid dangling mounts on failure */
+ log_info("%s: %s", __func__, file2);
+ rf = path_is_mount_point(file2, NULL, 0);
+ rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
+
+ file2d = strjoina(file2, "/");
+ log_info("%s: %s", __func__, file2d);
+ rdf = path_is_mount_point(file2d, NULL, 0);
+ rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
+
+ log_info("%s: %s", __func__, link2);
+ rlf = path_is_mount_point(link2, NULL, 0);
+ rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(file2) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rdf == -ENOTDIR);
+ assert_se(rdt == -ENOTDIR);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+
+ /* dirs */
+ dir2file = path_join(NULL, dir2, "file");
+ assert_se(dir2file);
+ fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+
+ assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
+
+ log_info("%s: %s", __func__, dir1);
+ rf = path_is_mount_point(dir1, NULL, 0);
+ rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
+ log_info("%s: %s", __func__, dirlink1);
+ rlf = path_is_mount_point(dirlink1, NULL, 0);
+ rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
+ log_info("%s: %s", __func__, dirlink1file);
+ /* its parent is a mount point, but not /file itself */
+ rl1f = path_is_mount_point(dirlink1file, NULL, 0);
+ rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(dir1) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+ assert_se(rl1f == 0);
+ assert_se(rl1t == 0);
+
+ } else
+ printf("Skipping bind mount file test: %m\n");
+
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ test_mount_propagation_flags("shared", 0, MS_SHARED);
+ test_mount_propagation_flags("slave", 0, MS_SLAVE);
+ test_mount_propagation_flags("private", 0, MS_PRIVATE);
+ test_mount_propagation_flags(NULL, 0, 0);
+ test_mount_propagation_flags("", 0, 0);
+ test_mount_propagation_flags("xxxx", -EINVAL, 0);
+ test_mount_propagation_flags(" ", -EINVAL, 0);
+
+ test_mnt_id();
+ test_path_is_mount_point();
+
+ return 0;
+}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 7feae54068..d2ccb41fd9 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -6,7 +6,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stat-util.h"
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 2b0564d8a0..2f0538a4bb 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -9,7 +9,7 @@
#include "fileio.h"
#include "macro.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "stat-util.h"
static void test_files_same(void) {
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index eeeb1d1850..e31af282a4 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -41,7 +41,7 @@
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-lookup.h"
diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c
index ffe63d5717..5da9ce1681 100644
--- a/src/volatile-root/volatile-root.c
+++ b/src/volatile-root/volatile-root.c
@@ -7,6 +7,7 @@
#include "main-func.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"