diff options
Diffstat (limited to 'src/basic/mount-util.c')
-rw-r--r-- | src/basic/mount-util.c | 689 |
1 files changed, 0 insertions, 689 deletions
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c deleted file mode 100644 index c8f8022578..0000000000 --- a/src/basic/mount-util.c +++ /dev/null @@ -1,689 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "mount-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "set.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.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 -errno; - - 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) { - union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; - 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(fd, filename, &h.handle, &mount_id, flags); - if (r < 0) { - if (errno == ENOSYS) - /* This kernel does not support name_to_handle_at() - * fall back to simpler logic. */ - 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 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.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) - 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)) - 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, int flags) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *canonical = NULL, *parent = NULL; - - assert(t); - - 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) { - canonical = canonicalize_file_name(t); - if (!canonical) - return -errno; - - t = canonical; - } - - parent = dirname_malloc(t); - if (!parent) - return -ENOMEM; - - fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_mount_point(fd, basename(t), flags); -} - -int umount_recursive(const char *prefix, int flags) { - bool again; - int n = 0, r; - - /* Try to umount everything recursively below a - * directory. Also, take care of stacked mounts, and keep - * unmounting them until they are gone. */ - - do { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - - again = false; - r = 0; - - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; - - for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path); - if (k != 1) { - if (k == EOF) - break; - - continue; - } - - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; - - if (!path_startswith(p, prefix)) - continue; - - if (umount2(p, flags) < 0) { - r = log_debug_errno(errno, "Failed to umount %s: %m", p); - continue; - } - - log_debug("Successfully unmounted %s", p); - - again = true; - n++; - - break; - } - - } while (again); - - return r ? r : n; -} - -static int get_mount_flags(const char *path, unsigned long *flags) { - struct statvfs buf; - - if (statvfs(path, &buf) < 0) - return -errno; - *flags = buf.f_flag; - return 0; -} - -int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { - _cleanup_set_free_free_ Set *done = NULL; - _cleanup_free_ char *cleaned = NULL; - int r; - - /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already - * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write - * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to - * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each - * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We - * do not have any effect on future submounts that might get propagated, they migt be writable. This includes - * future submounts that have been triggered via autofs. - * - * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the - * remount operation. Note that we'll ignore the blacklist for the top-level path. */ - - cleaned = strdup(prefix); - if (!cleaned) - return -ENOMEM; - - path_kill_slashes(cleaned); - - done = set_new(&string_hash_ops); - if (!done) - return -ENOMEM; - - for (;;) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - _cleanup_set_free_free_ Set *todo = NULL; - bool top_autofs = false; - char *x; - unsigned long orig_flags; - - todo = set_new(&string_hash_ops); - if (!todo) - return -ENOMEM; - - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; - - for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options (superblock) */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options (bind mount) */ - "%*[^\n]", /* some rubbish at the end */ - &path, - &type); - if (k != 2) { - if (k == EOF) - break; - - continue; - } - - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; - - if (!path_startswith(p, cleaned)) - continue; - - /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall - * operate on. */ - if (!path_equal(cleaned, p)) { - bool blacklisted = false; - char **i; - - STRV_FOREACH(i, blacklist) { - - if (path_equal(*i, cleaned)) - continue; - - if (!path_startswith(*i, cleaned)) - continue; - - if (path_startswith(p, *i)) { - blacklisted = true; - log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned); - break; - } - } - if (blacklisted) - continue; - } - - /* Let's ignore autofs mounts. If they aren't - * triggered yet, we want to avoid triggering - * them, as we don't make any guarantees for - * future submounts anyway. If they are - * already triggered, then we will find - * another entry for this. */ - if (streq(type, "autofs")) { - top_autofs = top_autofs || path_equal(cleaned, p); - continue; - } - - if (!set_contains(done, p)) { - r = set_consume(todo, p); - p = NULL; - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - /* If we have no submounts to process anymore and if - * the root is either already done, or an autofs, we - * are done */ - if (set_isempty(todo) && - (top_autofs || set_contains(done, cleaned))) - return 0; - - if (!set_contains(done, cleaned) && - !set_contains(todo, cleaned)) { - /* The prefix directory itself is not yet a mount, make it one. */ - if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) - return -errno; - - orig_flags = 0; - (void) get_mount_flags(cleaned, &orig_flags); - orig_flags &= ~MS_RDONLY; - - if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) - return -errno; - - log_debug("Made top-level directory %s a mount point.", prefix); - - x = strdup(cleaned); - if (!x) - return -ENOMEM; - - r = set_consume(done, x); - if (r < 0) - return r; - } - - while ((x = set_steal_first(todo))) { - - r = set_consume(done, x); - if (r == -EEXIST || r == 0) - continue; - if (r < 0) - return r; - - /* Deal with mount points that are obstructed by a later mount */ - r = path_is_mount_point(x, 0); - if (r == -ENOENT || r == 0) - continue; - if (r < 0) - return r; - - /* Try to reuse the original flag set */ - orig_flags = 0; - (void) get_mount_flags(x, &orig_flags); - orig_flags &= ~MS_RDONLY; - - if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) - return -errno; - - log_debug("Remounted %s read-only.", x); - } - } -} - -int mount_move_root(const char *path) { - assert(path); - - if (chdir(path) < 0) - return -errno; - - if (mount(path, "/", NULL, MS_MOVE, NULL) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - - if (chdir("/") < 0) - return -errno; - - return 0; -} - -bool fstype_is_network(const char *fstype) { - static const char table[] = - "afs\0" - "cifs\0" - "smbfs\0" - "sshfs\0" - "ncpfs\0" - "ncp\0" - "nfs\0" - "nfs4\0" - "gfs\0" - "gfs2\0" - "glusterfs\0" - "pvfs2\0" /* OrangeFS */ - "ocfs2\0" - "lustre\0" - ; - - const char *x; - - x = startswith(fstype, "fuse."); - if (x) - fstype = x; - - return nulstr_contains(table, fstype); -} - -int repeat_unmount(const char *path, int flags) { - bool done = false; - - assert(path); - - /* If there are multiple mounts on a mount point, this - * removes them all */ - - for (;;) { - if (umount2(path, flags) < 0) { - - if (errno == EINVAL) - return done; - - return -errno; - } - - done = true; - } -} - -const char* mode_to_inaccessible_node(mode_t mode) { - /* This function maps a node type to the correspondent inaccessible node type. - * Character and block inaccessible devices may not be created (because major=0 and minor=0), - * in such case we map character and block devices to the inaccessible node type socket. */ - switch(mode & S_IFMT) { - case S_IFREG: - return "/run/systemd/inaccessible/reg"; - case S_IFDIR: - return "/run/systemd/inaccessible/dir"; - case S_IFCHR: - if (access("/run/systemd/inaccessible/chr", F_OK) == 0) - return "/run/systemd/inaccessible/chr"; - return "/run/systemd/inaccessible/sock"; - case S_IFBLK: - if (access("/run/systemd/inaccessible/blk", F_OK) == 0) - return "/run/systemd/inaccessible/blk"; - return "/run/systemd/inaccessible/sock"; - case S_IFIFO: - return "/run/systemd/inaccessible/fifo"; - case S_IFSOCK: - return "/run/systemd/inaccessible/sock"; - } - return NULL; -} - -#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "") -static char* mount_flags_to_string(long unsigned flags) { - char *x; - _cleanup_free_ char *y = NULL; - long unsigned overflow; - - overflow = flags & ~(MS_RDONLY | - MS_NOSUID | - MS_NODEV | - MS_NOEXEC | - MS_SYNCHRONOUS | - MS_REMOUNT | - MS_MANDLOCK | - MS_DIRSYNC | - MS_NOATIME | - MS_NODIRATIME | - MS_BIND | - MS_MOVE | - MS_REC | - MS_SILENT | - MS_POSIXACL | - MS_UNBINDABLE | - MS_PRIVATE | - MS_SLAVE | - MS_SHARED | - MS_RELATIME | - MS_KERNMOUNT | - MS_I_VERSION | - MS_STRICTATIME | - MS_LAZYTIME); - - if (flags == 0 || overflow != 0) - if (asprintf(&y, "%lx", overflow) < 0) - return NULL; - - x = strjoin(FLAG(MS_RDONLY), - FLAG(MS_NOSUID), - FLAG(MS_NODEV), - FLAG(MS_NOEXEC), - FLAG(MS_SYNCHRONOUS), - FLAG(MS_REMOUNT), - FLAG(MS_MANDLOCK), - FLAG(MS_DIRSYNC), - FLAG(MS_NOATIME), - FLAG(MS_NODIRATIME), - FLAG(MS_BIND), - FLAG(MS_MOVE), - FLAG(MS_REC), - FLAG(MS_SILENT), - FLAG(MS_POSIXACL), - FLAG(MS_UNBINDABLE), - FLAG(MS_PRIVATE), - FLAG(MS_SLAVE), - FLAG(MS_SHARED), - FLAG(MS_RELATIME), - FLAG(MS_KERNMOUNT), - FLAG(MS_I_VERSION), - FLAG(MS_STRICTATIME), - FLAG(MS_LAZYTIME), - y, NULL); - if (!x) - return NULL; - if (!y) - x[strlen(x) - 1] = '\0'; /* truncate the last | */ - return x; -} - -int mount_verbose( - int error_log_level, - const char *what, - const char *where, - const char *type, - unsigned long flags, - const char *options) { - - _cleanup_free_ char *fl = NULL; - - fl = mount_flags_to_string(flags); - - if ((flags & MS_REMOUNT) && !what && !type) - log_debug("Remounting %s (%s \"%s\")...", - where, strnull(fl), strempty(options)); - else if (!what && !type) - log_debug("Mounting %s (%s \"%s\")...", - where, strnull(fl), strempty(options)); - else if ((flags & MS_BIND) && !type) - log_debug("Bind-mounting %s on %s (%s \"%s\")...", - what, where, strnull(fl), strempty(options)); - else - log_debug("Mounting %s on %s (%s \"%s\")...", - strna(type), where, strnull(fl), strempty(options)); - if (mount(what, where, type, flags, options) < 0) - return log_full_errno(error_log_level, errno, - "Failed to mount %s on %s (%s \"%s\"): %m", - strna(type), where, strnull(fl), strempty(options)); - return 0; -} - -int umount_verbose(const char *what) { - log_debug("Umounting %s...", what); - if (umount(what) < 0) - return log_error_errno(errno, "Failed to unmount %s: %m", what); - return 0; -} |