summaryrefslogtreecommitdiff
path: root/src/basic/mount-util.c
diff options
context:
space:
mode:
authorSven Eden <yamakuzure@gmx.net>2018-03-13 19:11:43 +0100
committerSven Eden <yamakuzure@gmx.net>2018-03-26 18:25:45 +0200
commit5a1765290e87f45b970657cfc8ac1e7ca5c50d6a (patch)
tree316b8b753c02e894a621976a888d8741e3cf9156 /src/basic/mount-util.c
parent869eeae5727fa42bbb442ef10c0dde985fcdce4d (diff)
Prep v236 : Add missing SPDX-License-Identifier (2/9) src/basic
Diffstat (limited to 'src/basic/mount-util.c')
-rw-r--r--src/basic/mount-util.c174
1 files changed, 137 insertions, 37 deletions
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 890bc41af..03e1b9a42 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
@@ -18,6 +19,7 @@
***/
#include <errno.h>
+//#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
@@ -39,8 +41,82 @@
#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 = h;
+ h = NULL;
+ }
+
+ 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)];
+ char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
_cleanup_close_ int subfd = -1;
char *p;
@@ -78,7 +154,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
}
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;
+ _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;
@@ -110,39 +186,32 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
* subvolumes have different st_dev, even though they aren't
* real mounts of their own. */
- r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
- if (r < 0) {
- if (IN_SET(errno, ENOSYS, EACCES, EPERM))
- /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe
- * through seccomp, because we are running inside of a container?): fall back to simpler
- * logic. */
+ 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 if (errno == 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
- return -errno;
- }
-
- r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
- if (r < 0) {
- if (errno == 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
- return -errno;
- }
+ /* 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
@@ -155,9 +224,9 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
* assume this is the root directory, which is a mount
* point. */
- if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
- h.handle.handle_type == h_parent.handle.handle_type &&
- memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+ 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;
@@ -213,6 +282,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
int r;
assert(t);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
if (path_equal(t, "/"))
return 1;
@@ -237,7 +307,17 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
if (fd < 0)
return -errno;
- return fd_is_mount_point(fd, basename(t), flags);
+ 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;
}
#if 0 /// UNNEEDED by elogind
@@ -259,6 +339,8 @@ int umount_recursive(const char *prefix, int flags) {
if (!proc_self_mountinfo)
return -errno;
+ (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
+
for (;;) {
_cleanup_free_ char *path = NULL, *p = NULL;
int k;
@@ -505,6 +587,8 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
if (!proc_self_mountinfo)
return -errno;
+ (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
+
return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo);
}
@@ -590,6 +674,22 @@ bool fstype_can_discard(const char *fstype) {
"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;