diff options
author | Sven Eden <yamakuzure@gmx.net> | 2018-03-13 19:11:43 +0100 |
---|---|---|
committer | Sven Eden <yamakuzure@gmx.net> | 2018-03-26 18:25:45 +0200 |
commit | 5a1765290e87f45b970657cfc8ac1e7ca5c50d6a (patch) | |
tree | 316b8b753c02e894a621976a888d8741e3cf9156 /src/basic/mount-util.c | |
parent | 869eeae5727fa42bbb442ef10c0dde985fcdce4d (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.c | 174 |
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; |