diff options
Diffstat (limited to '')
-rw-r--r-- | libmount/src/context.c | 3162 |
1 files changed, 3162 insertions, 0 deletions
diff --git a/libmount/src/context.c b/libmount/src/context.c new file mode 100644 index 0000000..b9c18d9 --- /dev/null +++ b/libmount/src/context.c @@ -0,0 +1,3162 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2010-2018 Karel Zak <kzak@redhat.com> + * + * libmount 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. + */ + +/** + * SECTION: context + * @title: Library high-level context + * @short_description: high-level API to mount/umount devices. + * + * <informalexample> + * <programlisting> + * struct libmnt_context *cxt = mnt_new_context(); + * + * mnt_context_set_options(cxt, "aaa,bbb,ccc=CCC"); + * mnt_context_set_mflags(cxt, MS_NOATIME|MS_NOEXEC); + * mnt_context_set_target(cxt, "/mnt/foo"); + * + * if (!mnt_context_mount(cxt)) + * printf("successfully mounted\n"); + * mnt_free_context(cxt); + * + * </programlisting> + * </informalexample> + * + * This code is similar to: + * + * mount -o aaa,bbb,ccc=CCC,noatime,noexec /mnt/foo + * + */ + +#include "mountP.h" +#include "fileutils.h" +#include "strutils.h" +#include "namespace.h" + +#include <sys/wait.h> + +/** + * mnt_new_context: + * + * Returns: newly allocated mount context + */ +struct libmnt_context *mnt_new_context(void) +{ + struct libmnt_context *cxt; + uid_t ruid, euid; + + cxt = calloc(1, sizeof(*cxt)); + if (!cxt) + return NULL; + + INIT_LIST_HEAD(&cxt->addmounts); + + ruid = getuid(); + euid = geteuid(); + + mnt_context_reset_status(cxt); + + cxt->loopdev_fd = -1; + + cxt->ns_orig.fd = -1; + cxt->ns_tgt.fd = -1; + cxt->ns_cur = &cxt->ns_orig; + + /* if we're really root and aren't running setuid */ + cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1; + + DBG(CXT, ul_debugobj(cxt, "----> allocate %s", + cxt->restricted ? "[RESTRICTED]" : "")); + + + return cxt; +} + +/** + * mnt_free_context: + * @cxt: mount context + * + * Deallocates context struct. + */ +void mnt_free_context(struct libmnt_context *cxt) +{ + if (!cxt) + return; + + mnt_reset_context(cxt); + + free(cxt->fstype_pattern); + free(cxt->optstr_pattern); + + mnt_unref_table(cxt->fstab); + mnt_unref_cache(cxt->cache); + + mnt_context_clear_loopdev(cxt); + mnt_free_lock(cxt->lock); + mnt_free_update(cxt->update); + + mnt_context_set_target_ns(cxt, NULL); + + free(cxt->children); + + DBG(CXT, ul_debugobj(cxt, "<---- free")); + free(cxt); +} + +/** + * mnt_reset_context: + * @cxt: mount context + * + * Resets all information in the context that is directly related to + * the latest mount (spec, source, target, mount options, ...). + * + * The match patterns, target namespace, cached fstab, cached canonicalized + * paths and tags and [e]uid are not reset. You have to use + * + * mnt_context_set_fstab(cxt, NULL); + * mnt_context_set_cache(cxt, NULL); + * mnt_context_set_fstype_pattern(cxt, NULL); + * mnt_context_set_options_pattern(cxt, NULL); + * mnt_context_set_target_ns(cxt, NULL); + * + * + * to reset this stuff. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_reset_context(struct libmnt_context *cxt) +{ + int fl; + + if (!cxt) + return -EINVAL; + + DBG(CXT, ul_debugobj(cxt, "<---- reset [status=%d] ---->", + mnt_context_get_status(cxt))); + + fl = cxt->flags; + + mnt_unref_fs(cxt->fs); + mnt_unref_table(cxt->mtab); + mnt_unref_table(cxt->utab); + + free(cxt->helper); + free(cxt->orig_user); + + cxt->fs = NULL; + cxt->mtab = NULL; + cxt->utab = NULL; + cxt->helper = NULL; + cxt->orig_user = NULL; + cxt->mountflags = 0; + cxt->user_mountflags = 0; + cxt->mountdata = NULL; + cxt->flags = MNT_FL_DEFAULT; + + /* free additional mounts list */ + while (!list_empty(&cxt->addmounts)) { + struct libmnt_addmount *ad = list_entry(cxt->addmounts.next, + struct libmnt_addmount, + mounts); + mnt_free_addmount(ad); + } + + mnt_context_reset_status(cxt); + + if (cxt->table_fltrcb) + mnt_context_set_tabfilter(cxt, NULL, NULL); + + /* restore non-resettable flags */ + cxt->flags |= (fl & MNT_FL_NOMTAB); + cxt->flags |= (fl & MNT_FL_FAKE); + cxt->flags |= (fl & MNT_FL_SLOPPY); + cxt->flags |= (fl & MNT_FL_VERBOSE); + cxt->flags |= (fl & MNT_FL_NOHELPERS); + cxt->flags |= (fl & MNT_FL_LOOPDEL); + cxt->flags |= (fl & MNT_FL_LAZY); + cxt->flags |= (fl & MNT_FL_FORK); + cxt->flags |= (fl & MNT_FL_FORCE); + cxt->flags |= (fl & MNT_FL_NOCANONICALIZE); + cxt->flags |= (fl & MNT_FL_RDONLY_UMOUNT); + cxt->flags |= (fl & MNT_FL_RWONLY_MOUNT); + cxt->flags |= (fl & MNT_FL_NOSWAPMATCH); + cxt->flags |= (fl & MNT_FL_TABPATHS_CHECKED); + + return 0; +} + +/** + * mnt_context_reset_status: + * @cxt: context + * + * Resets mount(2) and mount.type statuses, so mnt_context_do_mount() or + * mnt_context_do_umount() could be again called with the same settings. + * + * BE CAREFUL -- after this soft reset the libmount will NOT parse mount + * options, evaluate permissions or apply stuff from fstab. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_reset_status(struct libmnt_context *cxt) +{ + if (!cxt) + return -EINVAL; + + cxt->syscall_status = 1; /* means not called yet */ + cxt->helper_exec_status = 1; + cxt->helper_status = 0; + return 0; +} + +static int context_init_paths(struct libmnt_context *cxt, int writable) +{ + struct libmnt_ns *ns_old; + + assert(cxt); + +#ifdef USE_LIBMOUNT_SUPPORT_MTAB + if (!cxt->mtab_path) { + cxt->mtab_path = mnt_get_mtab_path(); + DBG(CXT, ul_debugobj(cxt, "mtab path initialized to: %s", cxt->mtab_path)); + } +#endif + if (!cxt->utab_path) { + cxt->utab_path = mnt_get_utab_path(); + DBG(CXT, ul_debugobj(cxt, "utab path initialized to: %s", cxt->utab_path)); + } + + if (!writable) + return 0; /* only paths wanted */ + if (mnt_context_is_nomtab(cxt)) + return 0; /* write mode overridden by mount -n */ + if (cxt->flags & MNT_FL_TABPATHS_CHECKED) + return 0; + + DBG(CXT, ul_debugobj(cxt, "checking for writable tab files")); + + cxt->mtab_writable = 0; + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + +#ifdef USE_LIBMOUNT_SUPPORT_MTAB + mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable); + if (!cxt->mtab_writable) +#endif + /* use /run/mount/utab if /etc/mtab is useless */ + mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable); + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + cxt->flags |= MNT_FL_TABPATHS_CHECKED; + return 0; +} + +int mnt_context_mtab_writable(struct libmnt_context *cxt) +{ + assert(cxt); + + context_init_paths(cxt, 1); + return cxt->mtab_writable == 1; +} + +int mnt_context_utab_writable(struct libmnt_context *cxt) +{ + assert(cxt); + + context_init_paths(cxt, 1); + return cxt->utab_writable == 1; +} + +const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt) +{ + assert(cxt); + + context_init_paths(cxt, 1); + return cxt->mtab_writable ? cxt->mtab_path : cxt->utab_path; +} + + +static int set_flag(struct libmnt_context *cxt, int flag, int enable) +{ + if (!cxt) + return -EINVAL; + if (enable) { + DBG(CXT, ul_debugobj(cxt, "enabling flag %04x", flag)); + cxt->flags |= flag; + } else { + DBG(CXT, ul_debugobj(cxt, "disabling flag %04x", flag)); + cxt->flags &= ~flag; + } + return 0; +} + +/** + * mnt_context_is_restricted: + * @cxt: mount context + * + * Returns: 0 for an unrestricted mount (user is root), or 1 for non-root mounts + */ +int mnt_context_is_restricted(struct libmnt_context *cxt) +{ + return cxt->restricted; +} + +/** + * mnt_context_set_optsmode + * @cxt: mount context + * @mode: MNT_OMODE_* flags + * + * Controls how to use mount optionssource and target paths from fstab/mtab. + * + * @MNT_OMODE_IGNORE: ignore mtab/fstab options + * + * @MNT_OMODE_APPEND: append mtab/fstab options to existing options + * + * @MNT_OMODE_PREPEND: prepend mtab/fstab options to existing options + * + * @MNT_OMODE_REPLACE: replace existing options with options from mtab/fstab + * + * @MNT_OMODE_FORCE: always read mtab/fstab (although source and target are defined) + * + * @MNT_OMODE_FSTAB: read from fstab + * + * @MNT_OMODE_MTAB: read from mtab if fstab not enabled or failed + * + * @MNT_OMODE_NOTAB: do not read fstab/mtab at all + * + * @MNT_OMODE_AUTO: default mode (MNT_OMODE_PREPEND | MNT_OMODE_FSTAB | MNT_OMODE_MTAB) + * + * @MNT_OMODE_USER: default for non-root users (MNT_OMODE_REPLACE | MNT_OMODE_FORCE | MNT_OMODE_FSTAB) + * + * Notes: + * + * - MNT_OMODE_USER is always used if mount context is in restricted mode + * - MNT_OMODE_AUTO is used if nothing else is defined + * - the flags are evaluated in this order: MNT_OMODE_NOTAB, MNT_OMODE_FORCE, + * MNT_OMODE_FSTAB, MNT_OMODE_MTAB and then the mount options from fstab/mtab + * are set according to MNT_OMODE_{IGNORE,APPEND,PREPAND,REPLACE} + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode) +{ + if (!cxt) + return -EINVAL; + cxt->optsmode = mode; + return 0; +} + +/** + * mnt_context_get_optsmode + * @cxt: mount context + * + * Returns: MNT_OMODE_* mask or zero. + */ + +int mnt_context_get_optsmode(struct libmnt_context *cxt) +{ + return cxt->optsmode; +} + +/** + * mnt_context_disable_canonicalize: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Enable/disable paths canonicalization and tags evaluation. The libmount context + * canonicalizes paths when searching in fstab and when preparing source and target paths + * for mount(2) syscall. + * + * This function has an effect on the private (within context) fstab instance only + * (see mnt_context_set_fstab()). If you want to use an external fstab then you + * need to manage your private struct libmnt_cache (see mnt_table_set_cache(fstab, + * NULL). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOCANONICALIZE, disable); +} + +/** + * mnt_context_is_nocanonicalize: + * @cxt: mount context + * + * Returns: 1 if no-canonicalize mode is enabled or 0. + */ +int mnt_context_is_nocanonicalize(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_NOCANONICALIZE ? 1 : 0; +} + +/** + * mnt_context_enable_lazy: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable lazy umount (see umount(8) man page, option -l). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_LAZY, enable); +} + +/** + * mnt_context_is_lazy: + * @cxt: mount context + * + * Returns: 1 if lazy umount is enabled or 0 + */ +int mnt_context_is_lazy(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_LAZY ? 1 : 0; +} + +/** + * mnt_context_enable_fork: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable fork(2) call in mnt_context_next_mount() (see mount(8) man + * page, option -F). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_fork(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FORK, enable); +} + +/** + * mnt_context_is_fork: + * @cxt: mount context + * + * Returns: 1 if fork (mount -F) is enabled or 0 + */ +int mnt_context_is_fork(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_FORK ? 1 : 0; +} + +/** + * mnt_context_is_parent: + * @cxt: mount context + * + * Return: 1 if mount -F enabled and the current context is parent, or 0 + */ +int mnt_context_is_parent(struct libmnt_context *cxt) +{ + return mnt_context_is_fork(cxt) && cxt->pid == 0; +} + +/** + * mnt_context_is_child: + * @cxt: mount context + * + * Return: 1 f the current context is child, or 0 + */ +int mnt_context_is_child(struct libmnt_context *cxt) +{ + /* See mnt_fork_context(), the for fork flag is always disabled + * for children to avoid recursive forking. + */ + return !mnt_context_is_fork(cxt) && cxt->pid; +} + +/** + * mnt_context_enable_rdonly_umount: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable read-only remount on failed umount(2) + * (see umount(8) man page, option -r). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_RDONLY_UMOUNT, enable); +} + +/** + * mnt_context_is_rdonly_umount + * @cxt: mount context + * + * See also mnt_context_enable_rdonly_umount() and umount(8) man page, + * option -r. + * + * Returns: 1 if read-only remount failed umount(2) is enables or 0 + */ +int mnt_context_is_rdonly_umount(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_RDONLY_UMOUNT ? 1 : 0; +} + +/** + * mnt_context_enable_rwonly_mount: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Force read-write mount; if enabled libmount will never try MS_RDONLY + * after failed mount(2) EROFS. (See mount(8) man page, option -w). + * + * Since: 2.30 + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_rwonly_mount(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_RWONLY_MOUNT, enable); +} + +/** + * mnt_context_is_rwonly_mount + * @cxt: mount context + * + * See also mnt_context_enable_rwonly_mount() and mount(8) man page, + * option -w. + * + * Since: 2.30 + * + * Returns: 1 if only read-write mount is allowed. + */ +int mnt_context_is_rwonly_mount(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_RWONLY_MOUNT ? 1 : 0; +} + +/** + * mnt_context_forced_rdonly: + * @cxt: mount context + * + * See also mnt_context_enable_rwonly_mount(). + * + * Since: 2.30 + * + * Returns: 1 if mounted read-only on write-protected device. + */ +int mnt_context_forced_rdonly(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_FORCED_RDONLY ? 1 : 0; +} + +/** + * mnt_context_disable_helpers: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Enable/disable /sbin/[u]mount.* helpers (see mount(8) man page, option -i). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOHELPERS, disable); +} + +/** + * mnt_context_is_nohelpers + * @cxt: mount context + * + * Returns: 1 if helpers are disabled (mount -i) or 0 + */ +int mnt_context_is_nohelpers(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_NOHELPERS ? 1 : 0; +} + + +/** + * mnt_context_enable_sloppy: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Set/unset sloppy mounting (see mount(8) man page, option -s). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_SLOPPY, enable); +} + +/** + * mnt_context_is_sloppy: + * @cxt: mount context + * + * Returns: 1 if sloppy flag is enabled or 0 + */ +int mnt_context_is_sloppy(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_SLOPPY ? 1 : 0; +} + +/** + * mnt_context_enable_fake: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable fake mounting (see mount(8) man page, option -f). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_fake(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FAKE, enable); +} + +/** + * mnt_context_is_fake: + * @cxt: mount context + * + * Returns: 1 if fake flag is enabled or 0 + */ +int mnt_context_is_fake(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_FAKE ? 1 : 0; +} + +/** + * mnt_context_disable_mtab: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Disable/enable mtab update (see mount(8) man page, option -n). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOMTAB, disable); +} + +/** + * mnt_context_is_nomtab: + * @cxt: mount context + * + * Returns: 1 if no-mtab is enabled or 0 + */ +int mnt_context_is_nomtab(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_NOMTAB ? 1 : 0; +} + +/** + * mnt_context_disable_swapmatch: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Disable/enable swap between source and target for mount(8) if only one path + * is specified. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_swapmatch(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOSWAPMATCH, disable); +} + +/** + * mnt_context_is_swapmatch: + * @cxt: mount context + * + * Returns: 1 if swap between source and target is allowed (default is 1) or 0. + */ +int mnt_context_is_swapmatch(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_NOSWAPMATCH ? 0 : 1; +} + +/** + * mnt_context_enable_force: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable force umounting (see umount(8) man page, option -f). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_force(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FORCE, enable); +} + +/** + * mnt_context_is_force + * @cxt: mount context + * + * Returns: 1 if force umounting flag is enabled or 0 + */ +int mnt_context_is_force(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_FORCE ? 1 : 0; +} + +/** + * mnt_context_enable_verbose: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable verbose output (TODO: not implemented yet) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_VERBOSE, enable); +} + +/** + * mnt_context_is_verbose + * @cxt: mount context + * + * Returns: 1 if verbose flag is enabled or 0 + */ +int mnt_context_is_verbose(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_VERBOSE ? 1 : 0; +} + +/** + * mnt_context_enable_loopdel: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable the loop delete (destroy) after umount (see umount(8), option -d) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_LOOPDEL, enable); +} + +/** + * mnt_context_is_loopdel: + * @cxt: mount context + * + * Returns: 1 if loop device should be deleted after umount (umount -d) or 0. + */ +int mnt_context_is_loopdel(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_LOOPDEL ? 1 : 0; +} + +/** + * mnt_context_set_fs: + * @cxt: mount context + * @fs: filesystem description + * + * The mount context uses private @fs by default. This function allows to + * overwrite the private @fs with an external instance. This function + * increments @fs reference counter (and decrement reference counter of the + * old fs). + * + * The @fs will be modified by mnt_context_set_{source,target,options,fstype} + * functions, If the @fs is NULL, then all current FS specific settings (source, + * target, etc., exclude spec) are reset. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs) +{ + if (!cxt) + return -EINVAL; + + mnt_ref_fs(fs); /* new */ + mnt_unref_fs(cxt->fs); /* old */ + cxt->fs = fs; + return 0; +} + +/** + * mnt_context_get_fs: + * @cxt: mount context + * + * The FS contains the basic description of mountpoint, fs type and so on. + * Note that the FS is modified by mnt_context_set_{source,target,options,fstype} + * functions. + * + * Returns: pointer to FS description or NULL in case of a calloc() error. + */ +struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt) +{ + if (!cxt) + return NULL; + if (!cxt->fs) + cxt->fs = mnt_new_fs(); + return cxt->fs; +} + +/** + * mnt_context_get_fs_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_fs_userdata(struct libmnt_context *cxt) +{ + return cxt->fs ? mnt_fs_get_userdata(cxt->fs) : NULL; +} + +/** + * mnt_context_get_fstab_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_fstab_userdata(struct libmnt_context *cxt) +{ + return cxt->fstab ? mnt_table_get_userdata(cxt->fstab) : NULL; +} + +/** + * mnt_context_get_mtab_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_mtab_userdata(struct libmnt_context *cxt) +{ + return cxt->mtab ? mnt_table_get_userdata(cxt->mtab) : NULL; +} + +/** + * mnt_context_set_source: + * @cxt: mount context + * @source: mount source (device, directory, UUID, LABEL, ...) + * + * Note that libmount does not interpret "nofail" (MNT_MS_NOFAIL) + * mount option. The real return code is always returned, when + * the device does not exist then it's usually MNT_ERR_NOSOURCE + * from libmount or ENOENT, ENOTDIR, ENOTBLK, ENXIO from mount(2). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_source(struct libmnt_context *cxt, const char *source) +{ + return mnt_fs_set_source(mnt_context_get_fs(cxt), source); +} + +/** + * mnt_context_get_source: + * @cxt: mount context + * + * Returns: returns pointer or NULL in case of error or if not set. + */ +const char *mnt_context_get_source(struct libmnt_context *cxt) +{ + return mnt_fs_get_source(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_target: + * @cxt: mount context + * @target: mountpoint + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_target(struct libmnt_context *cxt, const char *target) +{ + return mnt_fs_set_target(mnt_context_get_fs(cxt), target); +} + +/** + * mnt_context_get_target: + * @cxt: mount context + * + * Returns: returns pointer or NULL in case of error or if not set. + */ +const char *mnt_context_get_target(struct libmnt_context *cxt) +{ + return mnt_fs_get_target(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_fstype: + * @cxt: mount context + * @fstype: filesystem type + * + * Note that the @fstype has to be a FS type. For patterns with + * comma-separated list of filesystems or for the "nofs" notation, use + * mnt_context_set_fstype_pattern(). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype) +{ + return mnt_fs_set_fstype(mnt_context_get_fs(cxt), fstype); +} + +/** + * mnt_context_get_fstype: + * @cxt: mount context + * + * Returns: pointer or NULL in case of error or if not set. + */ +const char *mnt_context_get_fstype(struct libmnt_context *cxt) +{ + return mnt_fs_get_fstype(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_options: + * @cxt: mount context + * @optstr: comma delimited mount options + * + * Note that that MS_MOVE cannot be specified as "string". It's operation that + * is no supported in fstab (etc.) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr) +{ + return mnt_fs_set_options(mnt_context_get_fs(cxt), optstr); +} + +/** + * mnt_context_append_options: + * @cxt: mount context + * @optstr: comma delimited mount options + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr) +{ + return mnt_fs_append_options(mnt_context_get_fs(cxt), optstr); +} + +/** + * mnt_context_get_options: + * @cxt: mount context + * + * This function returns mount options set by mnt_context_set_options() or + * mnt_context_append_options(). + * + * Note that *after* mnt_context_prepare_mount(), the mount options string + * may also include options set by mnt_context_set_mflags() or other options + * generated by this library. + * + * Returns: pointer or NULL + */ +const char *mnt_context_get_options(struct libmnt_context *cxt) +{ + return mnt_fs_get_options(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_fstype_pattern: + * @cxt: mount context + * @pattern: FS name pattern (or NULL to reset the current setting) + * + * See mount(8), option -t. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstype_pattern(struct libmnt_context *cxt, const char *pattern) +{ + char *p = NULL; + + if (!cxt) + return -EINVAL; + if (pattern) { + p = strdup(pattern); + if (!p) + return -ENOMEM; + } + free(cxt->fstype_pattern); + cxt->fstype_pattern = p; + return 0; +} + +/** + * mnt_context_set_options_pattern: + * @cxt: mount context + * @pattern: options pattern (or NULL to reset the current setting) + * + * See mount(8), option -O. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *pattern) +{ + char *p = NULL; + + if (!cxt) + return -EINVAL; + if (pattern) { + p = strdup(pattern); + if (!p) + return -ENOMEM; + } + free(cxt->optstr_pattern); + cxt->optstr_pattern = p; + return 0; +} + +/** + * mnt_context_set_fstab: + * @cxt: mount context + * @tb: fstab + * + * The mount context reads /etc/fstab to the private struct libmnt_table by default. + * This function allows to overwrite the private fstab with an external + * instance. + * + * This function modify the @tb reference counter. This function does not set + * the cache for the @tb. You have to explicitly call mnt_table_set_cache(tb, + * mnt_context_get_cache(cxt)); + * + * The fstab is used read-only and is not modified, it should be possible to + * share the fstab between more mount contexts (TODO: test it.) + * + * If the @tb argument is NULL, then the current private fstab instance is + * reset. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb) +{ + if (!cxt) + return -EINVAL; + + mnt_ref_table(tb); /* new */ + mnt_unref_table(cxt->fstab); /* old */ + + cxt->fstab = tb; + return 0; +} + +/** + * mnt_context_get_fstab: + * @cxt: mount context + * @tb: returns fstab + * + * See also mnt_table_parse_fstab() for more details about fstab. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb) +{ + struct libmnt_ns *ns_old; + + if (!cxt) + return -EINVAL; + if (!cxt->fstab) { + int rc; + + cxt->fstab = mnt_new_table(); + if (!cxt->fstab) + return -ENOMEM; + if (cxt->table_errcb) + mnt_table_set_parser_errcb(cxt->fstab, cxt->table_errcb); + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + mnt_table_set_cache(cxt->fstab, mnt_context_get_cache(cxt)); + rc = mnt_table_parse_fstab(cxt->fstab, NULL); + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + if (rc) + return rc; + } + + if (tb) + *tb = cxt->fstab; + return 0; +} + +/** + * mnt_context_get_mtab: + * @cxt: mount context + * @tb: returns mtab + * + * See also mnt_table_parse_mtab() for more details about mtab/mountinfo. The + * result will be deallocated by mnt_free_context(@cxt). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb) +{ + int rc = 0; + struct libmnt_ns *ns_old = NULL; + + if (!cxt) + return -EINVAL; + if (!cxt->mtab) { + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + context_init_paths(cxt, 0); + + cxt->mtab = mnt_new_table(); + if (!cxt->mtab) { + rc = -ENOMEM; + goto end; + } + + if (cxt->table_errcb) + mnt_table_set_parser_errcb(cxt->mtab, cxt->table_errcb); + if (cxt->table_fltrcb) + mnt_table_set_parser_fltrcb(cxt->mtab, + cxt->table_fltrcb, + cxt->table_fltrcb_data); + + mnt_table_set_cache(cxt->mtab, mnt_context_get_cache(cxt)); + + /* + * Note that mtab_path is NULL if mtab is useless or unsupported + */ + if (cxt->utab) + /* utab already parsed, don't parse it again */ + rc = __mnt_table_parse_mtab(cxt->mtab, + cxt->mtab_path, cxt->utab); + else + rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path); + if (rc) + goto end; + } + + if (tb) + *tb = cxt->mtab; + + DBG(CXT, ul_debugobj(cxt, "mtab requested [nents=%d]", + mnt_table_get_nents(cxt->mtab))); + +end: + if (ns_old && !mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + return rc; +} + +/* + * Called by mtab parser to filter out entries, non-zero means that + * an entry has to be filtered out. + */ +static int mtab_filter(struct libmnt_fs *fs, void *data) +{ + if (!fs || !data) + return 0; + if (mnt_fs_streq_target(fs, data)) + return 0; + if (mnt_fs_streq_srcpath(fs, data)) + return 0; + return 1; +} + +/* + * The same like mnt_context_get_mtab(), but does not read all mountinfo/mtab + * file, but only entries relevant for @tgt. + */ +int mnt_context_get_mtab_for_target(struct libmnt_context *cxt, + struct libmnt_table **mtab, + const char *tgt) +{ + struct stat st; + struct libmnt_cache *cache = NULL; + char *cn_tgt = NULL; + int rc; + struct libmnt_ns *ns_old; + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + if (mnt_context_is_nocanonicalize(cxt)) + mnt_context_set_tabfilter(cxt, mtab_filter, (void *) tgt); + + else if (mnt_stat_mountpoint(tgt, &st) == 0 && S_ISDIR(st.st_mode)) { + cache = mnt_context_get_cache(cxt); + cn_tgt = mnt_resolve_path(tgt, cache); + if (cn_tgt) + mnt_context_set_tabfilter(cxt, mtab_filter, cn_tgt); + } + + rc = mnt_context_get_mtab(cxt, mtab); + mnt_context_set_tabfilter(cxt, NULL, NULL); + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + if (cn_tgt && !cache) + free(cn_tgt); + + return rc; +} + +/* + * Allows to specify a filter for tab file entries. The filter is called by + * the table parser. Currently used for mtab and utab only. + */ +int mnt_context_set_tabfilter(struct libmnt_context *cxt, + int (*fltr)(struct libmnt_fs *, void *), + void *data) +{ + if (!cxt) + return -EINVAL; + + cxt->table_fltrcb = fltr; + cxt->table_fltrcb_data = data; + + if (cxt->mtab) + mnt_table_set_parser_fltrcb(cxt->mtab, + cxt->table_fltrcb, + cxt->table_fltrcb_data); + + DBG(CXT, ul_debugobj(cxt, "tabfilter %s", fltr ? "ENABLED!" : "disabled")); + return 0; +} + +/** + * mnt_context_get_table: + * @cxt: mount context + * @filename: e.g. /proc/self/mountinfo + * @tb: returns the table + * + * This function allocates a new table and parses the @file. The parser error + * callback and cache for tags and paths is set according to the @cxt setting. + * See also mnt_table_parse_file(). + * + * It's strongly recommended to use the mnt_context_get_mtab() and + * mnt_context_get_fstab() functions for mtab and fstab files. This function + * does not care about LIBMOUNT_* env.variables and does not merge userspace + * options. + * + * The result will NOT be deallocated by mnt_free_context(@cxt). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_table(struct libmnt_context *cxt, + const char *filename, struct libmnt_table **tb) +{ + int rc; + struct libmnt_ns *ns_old; + + if (!cxt || !tb) + return -EINVAL; + + *tb = mnt_new_table(); + if (!*tb) + return -ENOMEM; + + if (cxt->table_errcb) + mnt_table_set_parser_errcb(*tb, cxt->table_errcb); + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + rc = mnt_table_parse_file(*tb, filename); + + if (rc) { + mnt_unref_table(*tb); + goto end; + } + + mnt_table_set_cache(*tb, mnt_context_get_cache(cxt)); + +end: + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + return rc; +} + +/** + * mnt_context_set_tables_errcb + * @cxt: mount context + * @cb: pointer to callback function + * + * The error callback is used for all tab files (e.g. mtab, fstab) + * parsed within the context. + * + * See also mnt_context_get_mtab(), + * mnt_context_get_fstab(), + * mnt_table_set_parser_errcb(). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_tables_errcb(struct libmnt_context *cxt, + int (*cb)(struct libmnt_table *tb, const char *filename, int line)) +{ + if (!cxt) + return -EINVAL; + + if (cxt->mtab) + mnt_table_set_parser_errcb(cxt->mtab, cb); + if (cxt->fstab) + mnt_table_set_parser_errcb(cxt->fstab, cb); + + cxt->table_errcb = cb; + return 0; +} + +/** + * mnt_context_set_cache: + * @cxt: mount context + * @cache: cache instance or NULL + * + * The mount context maintains a private struct libmnt_cache by default. This + * function allows to overwrite the private cache with an external instance. + * This function increments cache reference counter. + * + * If the @cache argument is NULL, then the current cache instance is reset. + * This function apply the cache to fstab and mtab instances (if already + * exists). + * + * The old cache instance reference counter is de-incremented. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache) +{ + if (!cxt) + return -EINVAL; + + mnt_ref_cache(cache); /* new */ + mnt_unref_cache(cxt->cache); /* old */ + + cxt->cache = cache; + + if (cxt->mtab) + mnt_table_set_cache(cxt->mtab, cache); + if (cxt->fstab) + mnt_table_set_cache(cxt->fstab, cache); + + return 0; +} + +/** + * mnt_context_get_cache + * @cxt: mount context + * + * See also mnt_context_set_cache(). + * + * Returns: pointer to cache or NULL if canonicalization is disabled. + */ +struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt) +{ + if (!cxt || mnt_context_is_nocanonicalize(cxt)) + return NULL; + + if (!cxt->cache) { + struct libmnt_cache *cache = mnt_new_cache(); + mnt_context_set_cache(cxt, cache); + mnt_unref_cache(cache); + } + return cxt->cache; +} + +/** + * mnt_context_set_passwd_cb: + * @cxt: mount context + * @get: callback to get password + * @release: callback to release (deallocate) password + * + * Sets callbacks for encryption password (e.g encrypted loopdev). This + * function is deprecated (encrypted loops are no longer supported). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_passwd_cb(struct libmnt_context *cxt, + char *(*get)(struct libmnt_context *), + void (*release)(struct libmnt_context *, char *)) +{ + if (!cxt) + return -EINVAL; + cxt->pwd_get_cb = get; + cxt->pwd_release_cb = release; + return 0; +} + +/** + * mnt_context_get_lock: + * @cxt: mount context + * + * The libmount applications don't have to care about mtab locking, but with a + * small exception: the application has to be able to remove the lock file when + * interrupted by signal or signals have to be ignored when the lock is locked. + * + * The default behavior is to ignore all signals (except SIGALRM and + * SIGTRAP for mtab update) when the lock is locked. If this behavior + * is unacceptable, then use: + * + * lc = mnt_context_get_lock(cxt); + * if (lc) + * mnt_lock_block_signals(lc, FALSE); + * + * and don't forget to call mnt_unlock_file(lc) before exit. + * + * Returns: pointer to lock struct or NULL. + */ +struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt) +{ + /* + * DON'T call this function within libmount, it will always allocate + * the lock. The mnt_update_* functions are able to allocate the lock + * only when mtab/utab update is really necessary. + */ + if (!cxt || mnt_context_is_nomtab(cxt)) + return NULL; + + if (!cxt->lock) { + cxt->lock = mnt_new_lock( + mnt_context_get_writable_tabpath(cxt), 0); + if (cxt->lock) + mnt_lock_block_signals(cxt->lock, TRUE); + } + return cxt->lock; +} + +/** + * mnt_context_set_mflags: + * @cxt: mount context + * @flags: mount(2) flags (MS_* flags) + * + * Sets mount flags (see mount(2) man page). + * + * Note that mount context allows to define mount options by mount flags. It + * means you can for example use + * + * mnt_context_set_mflags(cxt, MS_NOEXEC | MS_NOSUID); + * + * rather than + * + * mnt_context_set_options(cxt, "noexec,nosuid"); + * + * both of these calls have the same effect. + * + * Be careful if you want to use MS_REC flag -- in this case the bit is applied + * to all bind/slave/etc. options. If you want to mix more propadation flags + * and/or bind operations than it's better to specify mount options by + * strings. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags) +{ + if (!cxt) + return -EINVAL; + + cxt->mountflags = flags; + + if ((cxt->flags & MNT_FL_MOUNTOPTS_FIXED) && cxt->fs) + /* + * the final mount options are already generated, refresh... + */ + return mnt_optstr_apply_flags( + &cxt->fs->vfs_optstr, + cxt->mountflags, + mnt_get_builtin_optmap(MNT_LINUX_MAP)); + + return 0; +} + +/** + * mnt_context_get_mflags: + * @cxt: mount context + * @flags: returns MS_* mount flags + * + * Converts mount options string to MS_* flags and bitwise-OR the result with + * the already defined flags (see mnt_context_set_mflags()). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags) +{ + int rc = 0; + struct list_head *p; + + if (!cxt || !flags) + return -EINVAL; + + *flags = 0; + if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) { + const char *o = mnt_fs_get_options(cxt->fs); + if (o) + rc = mnt_optstr_get_flags(o, flags, + mnt_get_builtin_optmap(MNT_LINUX_MAP)); + } + + list_for_each(p, &cxt->addmounts) { + struct libmnt_addmount *ad = + list_entry(p, struct libmnt_addmount, mounts); + + *flags |= ad->mountflags; + } + + if (!rc) + *flags |= cxt->mountflags; + return rc; +} + +/** + * mnt_context_set_user_mflags: + * @cxt: mount context + * @flags: mount(2) flags (MNT_MS_* flags, e.g. MNT_MS_LOOP) + * + * Sets userspace mount flags. + * + * See also notes for mnt_context_set_mflags(). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags) +{ + if (!cxt) + return -EINVAL; + cxt->user_mountflags = flags; + return 0; +} + +/** + * mnt_context_get_user_mflags: + * @cxt: mount context + * @flags: returns mount flags + * + * Converts mount options string to MNT_MS_* flags and bitwise-OR the result + * with the already defined flags (see mnt_context_set_user_mflags()). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags) +{ + int rc = 0; + + if (!cxt || !flags) + return -EINVAL; + + *flags = 0; + if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) { + const char *o = mnt_fs_get_user_options(cxt->fs); + if (o) + rc = mnt_optstr_get_flags(o, flags, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); + } + if (!rc) + *flags |= cxt->user_mountflags; + return rc; +} + +/** + * mnt_context_set_mountdata: + * @cxt: mount context + * @data: mount(2) data + * + * The mount context generates mountdata from mount options by default. This + * function allows to overwrite this behavior, and @data will be used instead + * of mount options. + * + * The libmount does not deallocate the data by mnt_free_context(). Note that + * NULL is also valid mount data. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data) +{ + if (!cxt) + return -EINVAL; + cxt->mountdata = data; + cxt->flags |= MNT_FL_MOUNTDATA; + return 0; +} + +/* + * Translates LABEL/UUID/path to mountable path + */ +int mnt_context_prepare_srcpath(struct libmnt_context *cxt) +{ + const char *path = NULL; + struct libmnt_cache *cache; + const char *t, *v, *src; + int rc = 0; + struct libmnt_ns *ns_old; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, ul_debugobj(cxt, "preparing source path")); + + src = mnt_fs_get_source(cxt->fs); + + if (!src && mnt_context_propagation_only(cxt)) + /* mount --make-{shared,private,...} */ + return mnt_fs_set_source(cxt->fs, "none"); + + /* ignore filesystems without source or filesystems + * where the source is a quasi-path (//foo/bar) + */ + if (!src || mnt_fs_is_netfs(cxt->fs)) + return 0; + + DBG(CXT, ul_debugobj(cxt, "srcpath '%s'", src)); + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + cache = mnt_context_get_cache(cxt); + + if (!mnt_fs_get_tag(cxt->fs, &t, &v)) { + /* + * Source is TAG (evaluate) + */ + if (cache) + path = mnt_resolve_tag(t, v, cache); + + rc = path ? mnt_fs_set_source(cxt->fs, path) : -MNT_ERR_NOSOURCE; + + } else if (cache && !mnt_fs_is_pseudofs(cxt->fs)) { + /* + * Source is PATH (canonicalize) + */ + path = mnt_resolve_path(src, cache); + if (path && strcmp(path, src)) + rc = mnt_fs_set_source(cxt->fs, path); + } + + if (rc) { + DBG(CXT, ul_debugobj(cxt, "failed to prepare srcpath [rc=%d]", rc)); + goto end; + } + + if (!path) + path = src; + + if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_REMOUNT)) + || mnt_fs_is_pseudofs(cxt->fs)) { + DBG(CXT, ul_debugobj(cxt, "REMOUNT/BIND/MOVE/pseudo FS source: %s", path)); + goto end; + } + + /* + * Initialize loop device + */ + if (mnt_context_is_loopdev(cxt)) { + rc = mnt_context_setup_loopdev(cxt); + if (rc) + goto end; + } + + DBG(CXT, ul_debugobj(cxt, "final srcpath '%s'", + mnt_fs_get_source(cxt->fs))); + +end: + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + return rc; +} + +/* create a mountpoint if X-mount.mkdir[=<mode>] specified */ +static int mkdir_target(const char *tgt, struct libmnt_fs *fs) +{ + char *mstr = NULL; + size_t mstr_sz = 0; + mode_t mode = 0; + struct stat st; + int rc; + + assert(tgt); + assert(fs); + + if (mnt_optstr_get_option(fs->user_optstr, "X-mount.mkdir", &mstr, &mstr_sz) != 0 && + mnt_optstr_get_option(fs->user_optstr, "x-mount.mkdir", &mstr, &mstr_sz) != 0) /* obsolete */ + return 0; + + DBG(CXT, ul_debug("mkdir %s (%s) wanted", tgt, mstr)); + + if (mnt_stat_mountpoint(tgt, &st) == 0) + return 0; + + if (mstr && mstr_sz) { + char *end = NULL; + + errno = 0; + mode = strtol(mstr, &end, 8); + + if (errno || !end || mstr + mstr_sz != end) { + DBG(CXT, ul_debug("failed to parse mkdir mode '%s'", mstr)); + return -MNT_ERR_MOUNTOPT; + } + } + + if (!mode) + mode = S_IRWXU | /* 0755 */ + S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + + rc = mkdir_p(tgt, mode); + if (rc) + DBG(CXT, ul_debug("mkdir %s failed: %m", tgt)); + + return rc; +} + +int mnt_context_prepare_target(struct libmnt_context *cxt) +{ + const char *tgt; + struct libmnt_cache *cache; + int rc = 0; + struct libmnt_ns *ns_old; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, ul_debugobj(cxt, "preparing target path")); + + tgt = mnt_fs_get_target(cxt->fs); + if (!tgt) + return 0; + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + /* mkdir target */ + if (cxt->action == MNT_ACT_MOUNT + && !mnt_context_is_restricted(cxt) + && (cxt->user_mountflags & MNT_MS_XCOMMENT || + cxt->user_mountflags & MNT_MS_XFSTABCOMM)) { + + rc = mkdir_target(tgt, cxt->fs); + if (rc) { + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + return rc; /* mkdir or parse error */ + } + } + + /* canonicalize the path */ + cache = mnt_context_get_cache(cxt); + if (cache) { + char *path = mnt_resolve_path(tgt, cache); + if (path && strcmp(path, tgt) != 0) + rc = mnt_fs_set_target(cxt->fs, path); + } + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + if (rc) + DBG(CXT, ul_debugobj(cxt, "failed to prepare target '%s'", tgt)); + else + DBG(CXT, ul_debugobj(cxt, "final target '%s'", + mnt_fs_get_target(cxt->fs))); + return 0; +} + +/* Guess type, but not set to cxt->fs, always use free() for the result. It's + * no error when we're not able to guess a filesystem type. Note that error + * does not mean that result in @type is NULL. + */ +int mnt_context_guess_srcpath_fstype(struct libmnt_context *cxt, char **type) +{ + int rc = 0; + struct libmnt_ns *ns_old; + const char *dev = mnt_fs_get_srcpath(cxt->fs); + + *type = NULL; + + if (!dev) + goto done; + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + if (access(dev, F_OK) == 0) { + struct libmnt_cache *cache = mnt_context_get_cache(cxt); + int ambi = 0; + + *type = mnt_get_fstype(dev, &ambi, cache); + if (cache && *type) + *type = strdup(*type); + if (ambi) + rc = -MNT_ERR_AMBIFS; + } else { + DBG(CXT, ul_debugobj(cxt, "access(%s) failed [%m]", dev)); + if (strchr(dev, ':') != NULL) + *type = strdup("nfs"); + else if (!strncmp(dev, "//", 2)) + *type = strdup("cifs"); + } + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + +done: + return rc; +} + +/* + * It's usually no error when we're not able to detect the filesystem type -- we + * will try to use the types from /{etc,proc}/filesystems. + */ +int mnt_context_guess_fstype(struct libmnt_context *cxt) +{ + char *type; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, ul_debugobj(cxt, "preparing fstype")); + + if ((cxt->mountflags & (MS_BIND | MS_MOVE)) + || mnt_context_propagation_only(cxt)) + goto none; + + type = (char *) mnt_fs_get_fstype(cxt->fs); + if (type && !strcmp(type, "auto")) { + mnt_fs_set_fstype(cxt->fs, NULL); + type = NULL; + } + + if (type) + goto done; + if (cxt->mountflags & MS_REMOUNT) + goto none; + if (cxt->fstype_pattern) + goto done; + + rc = mnt_context_guess_srcpath_fstype(cxt, &type); + if (rc == 0 && type) + __mnt_fs_set_fstype_ptr(cxt->fs, type); + else + free(type); +done: + DBG(CXT, ul_debugobj(cxt, "FS type: %s [rc=%d]", + mnt_fs_get_fstype(cxt->fs), rc)); + return rc; +none: + return mnt_fs_set_fstype(cxt->fs, "none"); +} + +/* + * The default is to use fstype from cxt->fs, this could be overwritten by + * @type. The @act is MNT_ACT_{MOUNT,UMOUNT}. + * + * Returns: 0 on success or negative number in case of error. Note that success + * does not mean that there is any usable helper, you have to check cxt->helper. + */ +int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name, + const char *type) +{ + char search_path[] = FS_SEARCH_PATH; /* from config.h */ + char *p = NULL, *path; + struct libmnt_ns *ns_old; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!type) + type = mnt_fs_get_fstype(cxt->fs); + + if (type && strchr(type, ',')) + return 0; /* type is fstype pattern */ + + if (mnt_context_is_nohelpers(cxt) + || !type + || !strcmp(type, "none") + || strstr(type, "/..") /* don't try to smuggle path */ + || mnt_fs_is_swaparea(cxt->fs)) + return 0; + + ns_old = mnt_context_switch_origin_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + path = strtok_r(search_path, ":", &p); + while (path) { + char helper[PATH_MAX]; + struct stat st; + int rc; + + rc = snprintf(helper, sizeof(helper), "%s/%s.%s", + path, name, type); + path = strtok_r(NULL, ":", &p); + + if (rc < 0 || (size_t) rc >= sizeof(helper)) + continue; + + rc = stat(helper, &st); + if (rc == -1 && errno == ENOENT && strchr(type, '.')) { + /* If type ends with ".subtype" try without it */ + char *hs = strrchr(helper, '.'); + if (hs) + *hs = '\0'; + rc = stat(helper, &st); + } + + DBG(CXT, ul_debugobj(cxt, "%-25s ... %s", helper, + rc ? "not found" : "found")); + if (rc) + continue; + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + free(cxt->helper); + cxt->helper = strdup(helper); + if (!cxt->helper) + return -ENOMEM; + return 0; + } + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + return 0; +} + +int mnt_context_merge_mflags(struct libmnt_context *cxt) +{ + unsigned long fl = 0; + int rc; + + assert(cxt); + + DBG(CXT, ul_debugobj(cxt, "merging mount flags")); + + rc = mnt_context_get_mflags(cxt, &fl); + if (rc) + return rc; + cxt->mountflags = fl; + + fl = 0; + rc = mnt_context_get_user_mflags(cxt, &fl); + if (rc) + return rc; + cxt->user_mountflags = fl; + + DBG(CXT, ul_debugobj(cxt, "final flags: VFS=%08lx user=%08lx", + cxt->mountflags, cxt->user_mountflags)); + + cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED; + return 0; +} + +/* + * Prepare /etc/mtab or /run/mount/utab + */ +int mnt_context_prepare_update(struct libmnt_context *cxt) +{ + int rc; + const char *target; + + assert(cxt); + assert(cxt->fs); + assert(cxt->action); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, ul_debugobj(cxt, "prepare update")); + + if (mnt_context_propagation_only(cxt)) { + DBG(CXT, ul_debugobj(cxt, "skip update: only MS_PROPAGATION")); + return 0; + } + + target = mnt_fs_get_target(cxt->fs); + + if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/")) { + DBG(CXT, ul_debugobj(cxt, "root umount: setting NOMTAB")); + mnt_context_disable_mtab(cxt, TRUE); + } + if (mnt_context_is_nomtab(cxt)) { + DBG(CXT, ul_debugobj(cxt, "skip update: NOMTAB flag")); + return 0; + } + if (!mnt_context_get_writable_tabpath(cxt)) { + DBG(CXT, ul_debugobj(cxt, "skip update: no writable destination")); + return 0; + } + /* 0 = success, 1 = not called yet */ + if (cxt->syscall_status != 1 && cxt->syscall_status != 0) { + DBG(CXT, ul_debugobj(cxt, + "skip update: syscall failed [status=%d]", + cxt->syscall_status)); + return 0; + } + + if (!cxt->update) { + const char *name = mnt_context_get_writable_tabpath(cxt); + + if (cxt->action == MNT_ACT_UMOUNT && is_file_empty(name)) { + DBG(CXT, ul_debugobj(cxt, + "skip update: umount, no table")); + return 0; + } + + cxt->update = mnt_new_update(); + if (!cxt->update) + return -ENOMEM; + + mnt_update_set_filename(cxt->update, name, + !mnt_context_mtab_writable(cxt)); + } + + if (cxt->action == MNT_ACT_UMOUNT) + rc = mnt_update_set_fs(cxt->update, cxt->mountflags, + mnt_context_get_target(cxt), NULL); + else + rc = mnt_update_set_fs(cxt->update, cxt->mountflags, + NULL, cxt->fs); + + return rc < 0 ? rc : 0; +} + +int mnt_context_update_tabs(struct libmnt_context *cxt) +{ + unsigned long fl; + int rc = 0; + struct libmnt_ns *ns_old; + + assert(cxt); + + if (mnt_context_is_nomtab(cxt)) { + DBG(CXT, ul_debugobj(cxt, "don't update: NOMTAB flag")); + return 0; + } + if (!cxt->update || !mnt_update_is_ready(cxt->update)) { + DBG(CXT, ul_debugobj(cxt, "don't update: no update prepared")); + return 0; + } + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + /* check utab update when external helper executed */ + if (mnt_context_helper_executed(cxt) + && mnt_context_get_helper_status(cxt) == 0 + && mnt_context_utab_writable(cxt)) { + + if (mnt_update_already_done(cxt->update, cxt->lock)) { + DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated")); + goto end; + } + } else if (cxt->helper) { + DBG(CXT, ul_debugobj(cxt, "don't update: external helper")); + goto end; + } + + if (cxt->syscall_status != 0 + && !(mnt_context_helper_executed(cxt) && + mnt_context_get_helper_status(cxt) == 0)) { + + DBG(CXT, ul_debugobj(cxt, "don't update: syscall/helper failed/not called")); + goto end; + } + + fl = mnt_update_get_mflags(cxt->update); + if ((cxt->mountflags & MS_RDONLY) != (fl & MS_RDONLY)) + /* + * fix MS_RDONLY in options + */ + mnt_update_force_rdonly(cxt->update, + cxt->mountflags & MS_RDONLY); + + rc = mnt_update_table(cxt->update, cxt->lock); + +end: + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + return rc; +} + +static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb, + int direction) +{ + struct libmnt_fs *fs = NULL; + const char *src, *tgt; + int rc; + + assert(cxt); + assert(cxt->fs); + + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + + if (tgt && src) + fs = mnt_table_find_pair(tb, src, tgt, direction); + else { + if (src) + fs = mnt_table_find_source(tb, src, direction); + else if (tgt) + fs = mnt_table_find_target(tb, tgt, direction); + + if (!fs && mnt_context_is_swapmatch(cxt)) { + /* swap source and target (if @src is not LABEL/UUID), + * for example in + * + * mount /foo/bar + * + * the path could be a mountpoint as well as a source (for + * example bind mount, symlink to a device, ...). + */ + if (src && !mnt_fs_get_tag(cxt->fs, NULL, NULL)) + fs = mnt_table_find_target(tb, src, direction); + if (!fs && tgt) + fs = mnt_table_find_source(tb, tgt, direction); + } + } + + if (!fs) + return -MNT_ERR_NOFSTAB; /* not found */ + + DBG(CXT, ul_debugobj(cxt, "apply entry:")); + DBG(CXT, mnt_fs_print_debug(fs, stderr)); + + /* copy from tab to our FS description + */ + rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs)); + if (!rc) + rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs)); + + if (!rc && !mnt_fs_get_fstype(cxt->fs)) + rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs)); + + if (!rc && !mnt_fs_get_root(cxt->fs) && mnt_fs_get_root(fs)) + rc = mnt_fs_set_root(cxt->fs, mnt_fs_get_root(fs)); + + if (rc) + return rc; + + if (cxt->optsmode & MNT_OMODE_IGNORE) + ; + else if (cxt->optsmode & MNT_OMODE_REPLACE) + rc = mnt_fs_set_options(cxt->fs, mnt_fs_get_options(fs)); + + else if (cxt->optsmode & MNT_OMODE_APPEND) + rc = mnt_fs_append_options(cxt->fs, mnt_fs_get_options(fs)); + + else if (cxt->optsmode & MNT_OMODE_PREPEND) + rc = mnt_fs_prepend_options(cxt->fs, mnt_fs_get_options(fs)); + + if (!rc) + cxt->flags |= MNT_FL_TAB_APPLIED; + return rc; +} + +/** + * mnt_context_apply_fstab: + * @cxt: mount context + * + * This function is optional. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_apply_fstab(struct libmnt_context *cxt) +{ + int rc = -1, isremount = 0, iscmdbind = 0; + struct libmnt_ns *ns_old; + struct libmnt_table *tab = NULL; + const char *src = NULL, *tgt = NULL; + unsigned long mflags = 0; + + if (!cxt || !cxt->fs) + return -EINVAL; + + if (mnt_context_tab_applied(cxt)) { /* already applied */ + DBG(CXT, ul_debugobj(cxt, "fstab already applied -- skip")); + return 0; + } + + if (mnt_context_is_restricted(cxt)) { + DBG(CXT, ul_debugobj(cxt, "force fstab usage for non-root users!")); + cxt->optsmode = MNT_OMODE_USER; + } else if (cxt->optsmode == 0) { + DBG(CXT, ul_debugobj(cxt, "use default optsmode")); + cxt->optsmode = MNT_OMODE_AUTO; + } else if (cxt->optsmode & MNT_OMODE_NOTAB) { + cxt->optsmode &= ~MNT_OMODE_FSTAB; + cxt->optsmode &= ~MNT_OMODE_MTAB; + cxt->optsmode &= ~MNT_OMODE_FORCE; + } + + if (mnt_context_get_mflags(cxt, &mflags) == 0) { + isremount = !!(mflags & MS_REMOUNT); + iscmdbind = !!(mflags & MS_BIND); + } + + if (cxt->fs) { + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + } + + DBG(CXT, ul_debugobj(cxt, "OPTSMODE: ignore=%d, append=%d, prepend=%d, " + "replace=%d, force=%d, fstab=%d, mtab=%d", + cxt->optsmode & MNT_OMODE_IGNORE ? 1 : 0, + cxt->optsmode & MNT_OMODE_APPEND ? 1 : 0, + cxt->optsmode & MNT_OMODE_PREPEND ? 1 : 0, + cxt->optsmode & MNT_OMODE_REPLACE ? 1 : 0, + cxt->optsmode & MNT_OMODE_FORCE ? 1 : 0, + cxt->optsmode & MNT_OMODE_FSTAB ? 1 : 0, + cxt->optsmode & MNT_OMODE_MTAB ? 1 : 0)); + + /* fstab is not required if source and target are specified */ + if (src && tgt && !(cxt->optsmode & MNT_OMODE_FORCE)) { + DBG(CXT, ul_debugobj(cxt, "fstab not required -- skip")); + return 0; + } + + if (!src && tgt + && !(cxt->optsmode & MNT_OMODE_FSTAB) + && !(cxt->optsmode & MNT_OMODE_MTAB)) { + DBG(CXT, ul_debugobj(cxt, "only target; fstab/mtab not required " + "-- skip, probably MS_PROPAGATION")); + return 0; + } + + /* let's initialize cxt->fs */ + ignore_result( mnt_context_get_fs(cxt) ); + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + /* try fstab */ + if (cxt->optsmode & MNT_OMODE_FSTAB) { + DBG(CXT, ul_debugobj(cxt, "trying to apply fstab (src=%s, target=%s)", src, tgt)); + rc = mnt_context_get_fstab(cxt, &tab); + if (!rc) + rc = apply_table(cxt, tab, MNT_ITER_FORWARD); + } + + /* try mtab */ + if (rc < 0 && (cxt->optsmode & MNT_OMODE_MTAB) + && (isremount || cxt->action == MNT_ACT_UMOUNT)) { + DBG(CXT, ul_debugobj(cxt, "trying to apply mtab (src=%s, target=%s)", src, tgt)); + if (tgt) + rc = mnt_context_get_mtab_for_target(cxt, &tab, tgt); + else + rc = mnt_context_get_mtab(cxt, &tab); + if (!rc) + rc = apply_table(cxt, tab, MNT_ITER_BACKWARD); + } + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + + if (rc) { + if (!mnt_context_is_restricted(cxt) + && tgt && !src + && isremount) { + DBG(CXT, ul_debugobj(cxt, "only target; ignore missing mtab entry on remount")); + return 0; + } + + DBG(CXT, ul_debugobj(cxt, "failed to find entry in fstab/mtab [rc=%d]: %m", rc)); + + /* force to "not found in fstab/mtab" error, the details why + * not found are not so important and may be misinterpreted by + * applications... */ + rc = -MNT_ERR_NOFSTAB; + + + } else if (isremount && !iscmdbind) { + + /* remove "bind" from fstab (or no-op if not present) */ + mnt_optstr_remove_option(&cxt->fs->optstr, "bind"); + } + return rc; +} + +/** + * mnt_context_tab_applied: + * @cxt: mount context + * + * Returns: 1 if fstab (or mtab) has been applied to the context, or 0. + */ +int mnt_context_tab_applied(struct libmnt_context *cxt) +{ + return cxt->flags & MNT_FL_TAB_APPLIED; +} + +/* + * This is not a public function! + * + * Returns 1 if *only propagation flags* change is requested. + */ +int mnt_context_propagation_only(struct libmnt_context *cxt) +{ + if (cxt->action != MNT_ACT_MOUNT) + return 0; + + /* has to be called after context_mount.c: fix_opts() */ + assert((cxt->flags & MNT_FL_MOUNTOPTS_FIXED)); + + /* all propagation mounts are in cxt->addmount */ + return !list_empty(&cxt->addmounts) + && (cxt->mountflags == 0 || cxt->mountflags == MS_SILENT) + && cxt->fs + && (!cxt->fs->fstype || strcmp(cxt->fs->fstype, "none") == 0) + && (!cxt->fs->source || strcmp(cxt->fs->source, "none") == 0); +} + +/** + * mnt_context_get_status: + * @cxt: mount context + * + * Global libmount status. + * + * The real exit code of the mount.type helper has to be tested by + * mnt_context_get_helper_status(). The mnt_context_get_status() only informs + * that exec() has been successful. + * + * Returns: 1 if mount.type or mount(2) syscall has been successfully called. + */ +int mnt_context_get_status(struct libmnt_context *cxt) +{ + return !cxt->syscall_status || !cxt->helper_exec_status; +} + +/** + * mnt_context_helper_executed: + * @cxt: mount context + * + * Returns: 1 if mount.type helper has been executed, or 0. + */ +int mnt_context_helper_executed(struct libmnt_context *cxt) +{ + return cxt->helper_exec_status != 1; +} + +/** + * mnt_context_get_helper_status: + * @cxt: mount context + * + * Return: mount.type helper exit status, result is reliable only if + * mnt_context_helper_executed() returns 1. + */ +int mnt_context_get_helper_status(struct libmnt_context *cxt) +{ + return cxt->helper_status; +} + +/** + * mnt_context_syscall_called: + * @cxt: mount context + * + * Returns: 1 if mount(2) syscall has been called, or 0. + */ +int mnt_context_syscall_called(struct libmnt_context *cxt) +{ + return cxt->syscall_status != 1; +} + +/** + * mnt_context_get_syscall_errno: + * @cxt: mount context + * + * The result from this function is reliable only if + * mnt_context_syscall_called() returns 1. + * + * Returns: mount(2) errno if the syscall failed or 0. + */ +int mnt_context_get_syscall_errno(struct libmnt_context *cxt) +{ + if (cxt->syscall_status < 0) + return -cxt->syscall_status; + return 0; +} + +/** + * mnt_context_set_syscall_status: + * @cxt: mount context + * @status: mount(2) status + * + * The @status should be 0 on success, or negative number on error (-errno). + * + * This function should only be used if the [u]mount(2) syscall is NOT called by + * libmount code. + * + * Returns: 0 or negative number in case of error. + */ +int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status) +{ + if (!cxt) + return -EINVAL; + + DBG(CXT, ul_debugobj(cxt, "syscall status set to: %d", status)); + cxt->syscall_status = status; + return 0; +} + +/** + * mnt_context_strerror + * @cxt: context + * @buf: buffer + * @bufsiz: size of the buffer + * + * Not implemented, deprecated in favor or mnt_context_get_excode(). + * + * Returns: 0 or negative number in case of error. + */ +int mnt_context_strerror(struct libmnt_context *cxt __attribute__((__unused__)), + char *buf __attribute__((__unused__)), + size_t bufsiz __attribute__((__unused__))) +{ + /* TODO: based on cxt->syscall_errno or cxt->helper_status */ + return 0; +} + + +int mnt_context_get_generic_excode(int rc, char *buf, size_t bufsz, char *fmt, ...) +{ + va_list va; + + if (rc == 0) + return MNT_EX_SUCCESS; + + va_start(va, fmt); + + /* we need to support "%m" */ + errno = rc < 0 ? -rc : rc; + + if (buf) + vsnprintf(buf, bufsz, fmt, va); + + switch (errno) { + case EINVAL: + case EPERM: + rc = MNT_EX_USAGE; + break; + case ENOMEM: + rc = MNT_EX_SYSERR; + break; + default: + rc = MNT_EX_FAIL; + break; + } + va_end(va); + return rc; +} + +/** + * mnt_context_get_excode: + * @cxt: context + * @rc: return code of the previous operation + * @buf: buffer to print error message (optional) + * @bufsz: size of the buffer + * + * This function analyzes context, [u]mount syscall and external helper status + * and @mntrc and generates unified return code (see MNT_EX_*) as expected + * from mount(8) or umount(8). + * + * If the external helper (e.g. /sbin/mount.type) has been executed than it + * returns status from wait() of the helper. It's not libmount fail if helper + * returns some crazy undocumented codes... See mnt_context_helper_executed() + * and mnt_context_get_helper_status(). Note that mount(8) and umount(8) utils + * always return code from helper without extra care about it. + * + * If the argument @buf is not NULL then error message is generated (if + * anything failed). + * + * The @mntrc is usually return code from mnt_context_mount(), + * mnt_context_umount(), or 'mntrc' as returned by mnt_context_next_mount(). + * + * Since: 2.30 + * + * Returns: MNT_EX_* codes. + */ +int mnt_context_get_excode( + struct libmnt_context *cxt, + int rc, + char *buf, + size_t bufsz) +{ + if (buf) { + *buf = '\0'; /* for sure */ + + if (!cxt->enabled_textdomain) { + bindtextdomain(LIBMOUNT_TEXTDOMAIN, LOCALEDIR); + cxt->enabled_textdomain = 1; + } + } + + switch (cxt->action) { + case MNT_ACT_MOUNT: + rc = mnt_context_get_mount_excode(cxt, rc, buf, bufsz); + break; + case MNT_ACT_UMOUNT: + rc = mnt_context_get_umount_excode(cxt, rc, buf, bufsz); + break; + default: + if (rc) + rc = mnt_context_get_generic_excode(rc, buf, bufsz, + _("operation failed: %m")); + else + rc = MNT_EX_SUCCESS; + break; + } + + DBG(CXT, ul_debugobj(cxt, "excode: rc=%d message=\"%s\"", rc, + buf ? buf : "<no-message>")); + return rc; +} + + +/** + * mnt_context_init_helper + * @cxt: mount context + * @action: MNT_ACT_{UMOUNT,MOUNT} + * @flags: not used now + * + * This function informs libmount that used from [u]mount.type helper. + * + * The function also calls mnt_context_disable_helpers() to avoid recursive + * mount.type helpers calling. It you really want to call another + * mount.type helper from your helper, then you have to explicitly enable this + * feature by: + * + * mnt_context_disable_helpers(cxt, FALSE); + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_init_helper(struct libmnt_context *cxt, int action, + int flags __attribute__((__unused__))) +{ + int rc; + + if (!cxt) + return -EINVAL; + + rc = mnt_context_disable_helpers(cxt, TRUE); + if (!rc) + rc = set_flag(cxt, MNT_FL_HELPER, 1); + if (!rc) + cxt->action = action; + + DBG(CXT, ul_debugobj(cxt, "initialized for [u]mount.<type> helper [rc=%d]", rc)); + return rc; +} + +/** + * mnt_context_helper_setopt: + * @cxt: context + * @c: getopt() result + * @arg: getopt() optarg + * + * This function applies the [u]mount.type command line option (for example parsed + * by getopt or getopt_long) to @cxt. All unknown options are ignored and + * then 1 is returned. + * + * Returns: negative number on error, 1 if @c is unknown option, 0 on success. + */ +int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg) +{ + if (cxt) { + switch(cxt->action) { + case MNT_ACT_MOUNT: + return mnt_context_mount_setopt(cxt, c, arg); + case MNT_ACT_UMOUNT: + return mnt_context_umount_setopt(cxt, c, arg); + } + } + return -EINVAL; +} + +/** + * mnt_context_is_fs_mounted: + * @cxt: context + * @fs: filesystem + * @mounted: returns 1 for mounted and 0 for non-mounted filesystems + * + * Please, read the mnt_table_is_fs_mounted() description! + * + * Returns: 0 on success and negative number in case of error. + */ +int mnt_context_is_fs_mounted(struct libmnt_context *cxt, + struct libmnt_fs *fs, int *mounted) +{ + struct libmnt_table *mtab, *orig; + int rc; + struct libmnt_ns *ns_old; + + if (!cxt || !fs || !mounted) + return -EINVAL; + + ns_old = mnt_context_switch_target_ns(cxt); + if (!ns_old) + return -MNT_ERR_NAMESPACE; + + orig = cxt->mtab; + rc = mnt_context_get_mtab(cxt, &mtab); + if (rc == -ENOENT && mnt_fs_streq_target(fs, "/proc") && + (!cxt->mtab_path || startswith(cxt->mtab_path, "/proc/"))) { + if (!orig) { + mnt_unref_table(cxt->mtab); + cxt->mtab = NULL; + } + *mounted = 0; + return 0; /* /proc not mounted */ + } else if (rc) + return rc; + + *mounted = mnt_table_is_fs_mounted(mtab, fs); + + if (!mnt_context_switch_ns(cxt, ns_old)) + return -MNT_ERR_NAMESPACE; + return 0; +} + +static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid) +{ + pid_t *pids; + + if (!cxt) + return -EINVAL; + + pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1); + if (!pids) + return -ENOMEM; + + DBG(CXT, ul_debugobj(cxt, "add new child %d", pid)); + cxt->children = pids; + cxt->children[cxt->nchildren++] = pid; + + return 0; +} + +int mnt_fork_context(struct libmnt_context *cxt) +{ + int rc = 0; + pid_t pid; + + assert(cxt); + if (!mnt_context_is_parent(cxt)) + return -EINVAL; + + DBG(CXT, ul_debugobj(cxt, "forking context")); + + DBG_FLUSH; + + pid = fork(); + + switch (pid) { + case -1: /* error */ + DBG(CXT, ul_debugobj(cxt, "fork failed %m")); + return -errno; + + case 0: /* child */ + cxt->pid = getpid(); + mnt_context_enable_fork(cxt, FALSE); + DBG(CXT, ul_debugobj(cxt, "child created")); + break; + + default: + rc = mnt_context_add_child(cxt, pid); + break; + } + + return rc; +} + +int mnt_context_wait_for_children(struct libmnt_context *cxt, + int *nchildren, int *nerrs) +{ + int i; + + if (!cxt) + return -EINVAL; + + assert(mnt_context_is_parent(cxt)); + + for (i = 0; i < cxt->nchildren; i++) { + pid_t pid = cxt->children[i]; + int rc = 0, ret = 0; + + if (!pid) + continue; + do { + DBG(CXT, ul_debugobj(cxt, + "waiting for child (%d/%d): %d", + i + 1, cxt->nchildren, pid)); + errno = 0; + rc = waitpid(pid, &ret, 0); + + } while (rc == -1 && errno == EINTR); + + if (nchildren) + (*nchildren)++; + + if (rc != -1 && nerrs) { + if (WIFEXITED(ret)) + (*nerrs) += WEXITSTATUS(ret) == 0 ? 0 : 1; + else + (*nerrs)++; + } + cxt->children[i] = 0; + } + + cxt->nchildren = 0; + free(cxt->children); + cxt->children = NULL; + return 0; +} + +static void close_ns(struct libmnt_ns *ns) +{ + if (ns->fd == -1) + return; + + close(ns->fd); + ns->fd = -1; + + mnt_unref_cache(ns->cache); + ns->cache = NULL; +} + +/** + * mnt_context_set_target_ns: + * @cxt: mount context + * @path: path to target namespace or NULL + * + * Sets target namespace to namespace represented by @path. If @path is NULL, + * target namespace is cleared. + * + * This function sets errno to ENOSYS and returns error if libmount is + * compiled without namespaces support. +* + * Returns: 0 on success, negative number in case of error. + * + * Since: 2.33 + */ +int mnt_context_set_target_ns(struct libmnt_context *cxt, const char *path) +{ + if (!cxt) + return -EINVAL; + + DBG(CXT, ul_debugobj(cxt, "Setting %s as target namespace", path)); + + /* cleanup only */ + if (!path) { + close_ns(&cxt->ns_orig); + close_ns(&cxt->ns_tgt); + return 0; + } + +#ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES + int errsv = 0; + int tmp; + + errno = 0; + + /* open original namespace */ + if (cxt->ns_orig.fd == -1) { + cxt->ns_orig.fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC); + if (cxt->ns_orig.fd == -1) + return -errno; + cxt->ns_orig.cache = NULL; + } + + /* open target (wanted) namespace */ + tmp = open(path, O_RDONLY | O_CLOEXEC); + if (tmp == -1) + return -errno; + + /* test whether namespace switching works */ + DBG(CXT, ul_debugobj(cxt, "Trying whether namespace is valid")); + if (setns(tmp, CLONE_NEWNS) + || setns(cxt->ns_orig.fd, CLONE_NEWNS)) { + errsv = errno; + DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno)); + goto err; + } + + close_ns(&cxt->ns_tgt); + + cxt->ns_tgt.fd = tmp; + cxt->ns_tgt.cache = NULL; + + return 0; +err: + close(tmp); + errno = errsv; + +#else /* ! USE_LIBMOUNT_SUPPORT_NAMESPACES */ + errno = ENOSYS; +#endif + return -errno; +} + +/** + * mnt_context_get_target_ns: + * @cxt: mount context + * + * Returns: pointer to target namespace + * + * Since: 2.33 + */ +struct libmnt_ns *mnt_context_get_target_ns(struct libmnt_context *cxt) +{ + return &cxt->ns_tgt; +} + +/** + * mnt_context_get_origin_ns: + * @cxt: mount context + * + * Returns: pointer to original namespace + * + * Since: 2.33 + */ +struct libmnt_ns *mnt_context_get_origin_ns(struct libmnt_context *cxt) +{ + return &cxt->ns_orig; +} + + +/** + * mnt_context_switch_ns: + * @cxt: mount context + * @ns: namespace to switch to + * + * Switch to namespace specified by ns + * + * Typical usage: + * <informalexample> + * <programlisting> + * struct libmnt_ns *ns_old; + * ns_old = mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt)); + * ... code ... + * mnt_context_switch_ns(cxt, ns_old); + * </programlisting> + * </informalexample> + * + * Returns: pointer to previous namespace or NULL on error + * + * Since: 2.33 + */ +struct libmnt_ns *mnt_context_switch_ns(struct libmnt_context *cxt, struct libmnt_ns *ns) +{ + struct libmnt_ns *old = NULL; + + if (!cxt || !ns) + return NULL; + + /* + * If mnt_context_set_target_ns() has never been used than @ns file + * descriptor is -1 and this function is noop. + */ + old = cxt->ns_cur; + if (ns == old || ns->fd == -1) + return old; + +#ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES + /* remember the curremt cache */ + if (old->cache != cxt->cache) { + mnt_unref_cache(old->cache); + old->cache = cxt->cache; + mnt_ref_cache(old->cache); + } + + /* switch */ + DBG(CXT, ul_debugobj(cxt, "Switching to %s namespace", + ns == mnt_context_get_target_ns(cxt) ? "target" : + ns == mnt_context_get_origin_ns(cxt) ? "original" : "other")); + + if (setns(ns->fd, CLONE_NEWNS)) { + int errsv = errno; + + DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno)); + errno = errsv; + return NULL; + } + + /* update pointer to the current namespace */ + cxt->ns_cur = ns; + + /* update pointer to the cache */ + mnt_unref_cache(cxt->cache); + cxt->cache = ns->cache; + mnt_ref_cache(cxt->cache); +#endif /* USE_LIBMOUNT_SUPPORT_NAMESPACES */ + + return old; +} + +/** + * mnt_context_switch_origin_ns: + * @cxt: mount context + * + * Switch to original namespace + * + * This is shorthand for + * <informalexample> + * <programlisting> + * mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt)); + * </programlisting> + * </informalexample> + * + * Returns: pointer to previous namespace or NULL on error + * + * Since: 2.33 + */ +struct libmnt_ns *mnt_context_switch_origin_ns(struct libmnt_context *cxt) +{ + return mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt)); +} + +/** + * mnt_context_switch_target_ns: + * @cxt: mount context + * + * Switch to target namespace + * + * This is shorthand for + * <informalexample> + * <programlisting> + * mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt)); + * </programlisting> + * </informalexample> + * + * Returns: pointer to previous namespace or NULL on error + * + * Since: 2.33 + */ +struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt) +{ + return mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt)); +} + + +#ifdef TEST_PROGRAM + +static struct libmnt_lock *lock; + +static void lock_fallback(void) +{ + if (lock) + mnt_unlock_file(lock); +} + +static int test_mount(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-o")) { + mnt_context_set_options(cxt, argv[idx + 1]); + idx += 2; + } + if (!strcmp(argv[idx], "-t")) { + /* TODO: use mnt_context_set_fstype_pattern() */ + mnt_context_set_fstype(cxt, argv[idx + 1]); + idx += 2; + } + + if (argc == idx + 1) + /* mount <mountpoint>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + + else if (argc == idx + 2) { + /* mount <device> <mountpoint> */ + mnt_context_set_source(cxt, argv[idx++]); + mnt_context_set_target(cxt, argv[idx++]); + } + + /* this is unnecessary! -- libmount is able to internally + * create and manage the lock + */ + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_mount(cxt); + if (rc) + warn("failed to mount"); + else + printf("successfully mounted\n"); + + lock = NULL; /* because we use atexit lock_fallback */ + mnt_free_context(cxt); + return rc; +} + +static int test_umount(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-t")) { + mnt_context_set_fstype(cxt, argv[idx + 1]); + idx += 2; + } + + if (!strcmp(argv[idx], "-f")) { + mnt_context_enable_force(cxt, TRUE); + idx++; + } + + if (!strcmp(argv[idx], "-l")) { + mnt_context_enable_lazy(cxt, TRUE); + idx++; + } + + if (!strcmp(argv[idx], "-r")) { + mnt_context_enable_rdonly_umount(cxt, TRUE); + idx++; + } + + if (argc == idx + 1) { + /* mount <mountpoint>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + } else { + rc = -EINVAL; + goto err; + } + + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_umount(cxt); + if (rc) + printf("failed to umount\n"); + else + printf("successfully umounted\n"); +err: + lock = NULL; /* because we use atexit lock_fallback */ + mnt_free_context(cxt); + return rc; +} + +static int test_flags(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + const char *opt = NULL; + unsigned long flags = 0; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-o")) { + mnt_context_set_options(cxt, argv[idx + 1]); + idx += 2; + } + + if (argc == idx + 1) + /* mount <mountpoint>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + + rc = mnt_context_prepare_mount(cxt); + if (rc) + printf("failed to prepare mount %s\n", strerror(-rc)); + + opt = mnt_fs_get_options(cxt->fs); + if (opt) + fprintf(stdout, "options: %s\n", opt); + + mnt_context_get_mflags(cxt, &flags); + fprintf(stdout, "flags: %08lx\n", flags); + + mnt_free_context(cxt); + return rc; +} + +static int test_mountall(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_context *cxt; + struct libmnt_iter *itr; + struct libmnt_fs *fs; + int mntrc, ignored, idx = 1; + + cxt = mnt_new_context(); + itr = mnt_new_iter(MNT_ITER_FORWARD); + + if (!cxt || !itr) + return -ENOMEM; + + if (argc > 2) { + if (argv[idx] && !strcmp(argv[idx], "-O")) { + mnt_context_set_options_pattern(cxt, argv[idx + 1]); + idx += 2; + } + if (argv[idx] && !strcmp(argv[idx], "-t")) { + mnt_context_set_fstype_pattern(cxt, argv[idx + 1]); + idx += 2; + } + } + + while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) { + + const char *tgt = mnt_fs_get_target(fs); + + if (ignored == 1) + printf("%s: ignored: not match\n", tgt); + else if (ignored == 2) + printf("%s: ignored: already mounted\n", tgt); + + else if (!mnt_context_get_status(cxt)) { + if (mntrc > 0) { + errno = mntrc; + warn("%s: mount failed", tgt); + } else + warnx("%s: mount failed", tgt); + } else + printf("%s: successfully mounted\n", tgt); + } + + mnt_free_context(cxt); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--mount", test_mount, "[-o <opts>] [-t <type>] <spec>|<src> <target>" }, + { "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" }, + { "--mount-all", test_mountall, "[-O <pattern>] [-t <pattern] mount all filesystems from fstab" }, + { "--flags", test_flags, "[-o <opts>] <spec>" }, + { NULL }}; + + umask(S_IWGRP|S_IWOTH); /* to be compatible with mount(8) */ + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ |