/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * This file is part of libmount from util-linux project. * * Copyright (C) 2010-2018 Karel Zak * * 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. * * * * 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); * * * * * 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 /** * 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); free(cxt->tgt_prefix); mnt_unref_table(cxt->fstab); mnt_unref_cache(cxt->cache); mnt_unref_fs(cxt->fs); mnt_unref_fs(cxt->fs_template); 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, prefix, 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); mnt_context_apply_template(cxt); return 0; } /* * Saves the current context FS setting (mount options, etc) to make it usable after * mnt_reset_context() or by mnt_context_apply_template(). This is usable for * example for mnt_context_next_mount() where for the next mount operation we * need to restore to the original context setting. * * Returns: 0 on success, negative number in case of error. */ int mnt_context_save_template(struct libmnt_context *cxt) { struct libmnt_fs *fs = NULL; if (!cxt) return -EINVAL; DBG(CXT, ul_debugobj(cxt, "save FS as template")); if (cxt->fs) { fs = mnt_copy_fs(NULL, cxt->fs); if (!fs) return -ENOMEM; } mnt_unref_fs(cxt->fs_template); cxt->fs_template = fs; return 0; } /* * Restores context FS setting from previously saved template (see * mnt_context_save_template()). * * Returns: 0 on success, negative number in case of error. */ int mnt_context_apply_template(struct libmnt_context *cxt) { struct libmnt_fs *fs = NULL; int rc = 0; if (!cxt) return -EINVAL; if (cxt->fs_template) { DBG(CXT, ul_debugobj(cxt, "copy FS from template")); fs = mnt_copy_fs(NULL, cxt->fs_template); if (!fs) return -ENOMEM; rc = mnt_context_set_fs(cxt, fs); mnt_unref_fs(fs); } else { DBG(CXT, ul_debugobj(cxt, "no FS template, reset only")); mnt_unref_fs(cxt->fs); cxt->fs = NULL; } return rc; } int mnt_context_has_template(struct libmnt_context *cxt) { return cxt && cxt->fs_template ? 1 : 0; } struct libmnt_context *mnt_copy_context(struct libmnt_context *o) { struct libmnt_context *n; n = mnt_new_context(); if (!n) return NULL; DBG(CXT, ul_debugobj(n, "<---- clone ---->")); n->flags = o->flags; if (o->fs) { n->fs = mnt_copy_fs(NULL, o->fs); if (!n->fs) goto failed; } n->mtab = o->mtab; mnt_ref_table(n->mtab); n->mtab = o->utab; mnt_ref_table(n->utab); if (strdup_between_structs(n, o, tgt_prefix)) goto failed; if (strdup_between_structs(n, o, helper)) goto failed; if (strdup_between_structs(n, o, orig_user)) goto failed; n->mountflags = o->mountflags; n->mountdata = o->mountdata; mnt_context_reset_status(n); n->table_fltrcb = o->table_fltrcb; n->table_fltrcb_data = o->table_fltrcb_data; return n; failed: mnt_free_context(n); return NULL; } /** * 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_force_unrestricted: * @cxt: mount context * * This function is DANGEROURS as it disables all security policies in libmount. * Don't use if not sure. It removes "restricted" flag from the context, so * libmount will use the current context as for root user. * * This function is designed for case you have no any suid permissions, so you * can depend on kernel. * * Returns: 0 on success, negative number in case of error. * * Since: 2.35 */ int mnt_context_force_unrestricted(struct libmnt_context *cxt) { if (mnt_context_is_restricted(cxt)) { DBG(CXT, ul_debugobj(cxt, "force UNRESTRICTED")); cxt->restricted = 0; } return 0; } /** * 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,PREPEND,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 can be used 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; DBG(CXT, ul_debugobj(cxt, "setting new FS")); 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_target_prefix: * @cxt: mount context * @path: mountpoint prefix * * Returns: 0 on success, negative number in case of error. */ int mnt_context_set_target_prefix(struct libmnt_context *cxt, const char *path) { char *p = NULL; if (!cxt) return -EINVAL; if (path) { p = strdup(path); if (!p) return -ENOMEM; } free(cxt->tgt_prefix); cxt->tgt_prefix = p; return 0; } /** * mnt_context_get_target_prefix: * @cxt: mount context * * Returns: returns pointer or NULL in case of error or if not set. */ const char *mnt_context_get_target_prefix(struct libmnt_context *cxt) { return cxt ? cxt->tgt_prefix : NULL; } /** * 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 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 can be used 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 can be used 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 can be used 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 can be used 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, *type; 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; /* ZFS source is always "dataset", not a real path */ type = mnt_fs_get_fstype(cxt->fs); if (type && strcmp(type, "zfs") == 0) 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) != 0) 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 verity or loop device * ENOTSUP means verity options were requested, but the library is built without * libcryptsetup so integrity cannot be enforced, and this should be an error * rather than a silent fallback to a simple loopdev mount */ rc = mnt_context_is_veritydev(cxt); if (rc == -ENOTSUP) { goto end; } else if (rc) { rc = mnt_context_setup_veritydev(cxt); if (rc) goto end; } else 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; } static int is_mkdir_required(const char *tgt, struct libmnt_fs *fs, mode_t *mode, int *rc) { char *mstr = NULL; size_t mstr_sz = 0; struct stat st; assert(tgt); assert(fs); assert(mode); assert(rc); *mode = 0; *rc = 0; 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; if (mnt_stat_mountpoint(tgt, &st) == 0) return 0; DBG(CXT, ul_debug("mkdir %s (%s) wanted", tgt, mstr)); 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)); *rc = -MNT_ERR_MOUNTOPT; return 0; } } if (!*mode) *mode = S_IRWXU | /* 0755 */ S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; return 1; } int mnt_context_prepare_target(struct libmnt_context *cxt) { const char *tgt, *prefix; int rc = 0; struct libmnt_ns *ns_old; mode_t mode = 0; 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; /* apply prefix */ prefix = mnt_context_get_target_prefix(cxt); if (prefix) { const char *p = *tgt == '/' ? tgt + 1 : tgt; if (!*p) /* target is "/", use "/prefix" */ rc = mnt_fs_set_target(cxt->fs, prefix); else { char *path = NULL; if (asprintf(&path, "%s/%s", prefix, p) <= 0) rc = -ENOMEM; else { rc = mnt_fs_set_target(cxt->fs, path); free(path); } } if (rc) return rc; tgt = mnt_fs_get_target(cxt->fs); } ns_old = mnt_context_switch_target_ns(cxt); if (!ns_old) return -MNT_ERR_NAMESPACE; /* X-mount.mkdir target */ if (cxt->action == MNT_ACT_MOUNT && (cxt->user_mountflags & MNT_MS_XCOMMENT || cxt->user_mountflags & MNT_MS_XFSTABCOMM) && is_mkdir_required(tgt, cxt->fs, &mode, &rc)) { /* supported only for root or non-suid mount(8) */ if (!mnt_context_is_restricted(cxt)) { rc = ul_mkdir_p(tgt, mode); if (rc) DBG(CXT, ul_debug("mkdir %s failed: %m", tgt)); } else rc = -EPERM; } /* canonicalize the path */ if (rc == 0) { struct libmnt_cache *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; DBG(CXT, ul_debugobj(cxt, "final target '%s' [rc=%d]", mnt_fs_get_target(cxt->fs), rc)); return rc; } /* 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; assert(type); assert(cxt); *type = NULL; dev = mnt_fs_get_srcpath(cxt->fs); if (!dev) return 0; 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 (ambi) rc = -MNT_ERR_AMBIFS; if (cache && *type) { *type = strdup(*type); if (!*type) rc = -ENOMEM; } } else { DBG(CXT, ul_debugobj(cxt, "access(%s) failed [%m]", dev)); if (strchr(dev, ':') != NULL) { *type = strdup("nfs"); if (!*type) rc = -ENOMEM; } else if (!strncmp(dev, "//", 2)) { *type = strdup("cifs"); if (!*type) rc = -ENOMEM; } } if (!mnt_context_switch_ns(cxt, ns_old)) return -MNT_ERR_NAMESPACE; 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; int rc = 0; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (cxt->helper) { free(cxt->helper); cxt->helper = NULL; } 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; /* Ignore errors when search in $PATH and do not modify * @rc due to stat() etc. */ path = strtok_r(search_path, ":", &p); while (path) { char helper[PATH_MAX]; struct stat st; int xrc; xrc = snprintf(helper, sizeof(helper), "%s/%s.%s", path, name, type); path = strtok_r(NULL, ":", &p); if (xrc < 0 || (size_t) xrc >= sizeof(helper)) continue; xrc = stat(helper, &st); if (xrc == -1 && errno == ENOENT && strchr(type, '.')) { /* If type ends with ".subtype" try without it */ char *hs = strrchr(helper, '.'); if (hs) *hs = '\0'; xrc = stat(helper, &st); } DBG(CXT, ul_debugobj(cxt, "%-25s ... %s", helper, xrc ? "not found" : "found")); if (xrc) continue; /* success */ rc = strdup_to_struct_member(cxt, helper, helper); break; } if (!mnt_context_switch_ns(cxt, ns_old)) rc = -MNT_ERR_NAMESPACE; /* make sure helper is not set on error */ if (rc) { free(cxt->helper); cxt->helper = NULL; } return rc; } 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; } /* apply @fs to @cxt; * * @mflags are mount flags as specified on command-line -- used only to save * MS_RDONLY which is allowed for non-root users. */ static int apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs, unsigned long mflags) { int rc; if (!cxt->optsmode) { if (mnt_context_is_restricted(cxt)) { DBG(CXT, ul_debugobj(cxt, "force fstab usage for non-root users!")); cxt->optsmode = MNT_OMODE_USER; } else { DBG(CXT, ul_debugobj(cxt, "use default optsmode")); cxt->optsmode = MNT_OMODE_AUTO; } } DBG(CXT, ul_debugobj(cxt, "apply entry:")); DBG(CXT, mnt_fs_print_debug(fs, stderr)); DBG(CXT, ul_debugobj(cxt, "OPTSMODE (opt-part): ignore=%d, append=%d, prepend=%d, replace=%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)); /* copy from fs 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) goto done; 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)); /* mount --read-only for non-root users is allowed */ if (rc == 0 && (mflags & MS_RDONLY) && mnt_context_is_restricted(cxt) && cxt->optsmode == MNT_OMODE_USER) rc = mnt_fs_append_options(cxt->fs, "ro"); } 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; done: DBG(CXT, ul_debugobj(cxt, "final entry [rc=%d]:", rc)); DBG(CXT, mnt_fs_print_debug(cxt->fs, stderr)); return rc; } static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb, int direction, unsigned long mflags) { struct libmnt_fs *fs = NULL; const char *src, *tgt; 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 */ return apply_fs(cxt, fs, mflags); } /* apply @fs to @cxt -- use mnt_context_apply_fstab() if not sure */ int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs) { return apply_fs(cxt, fs, 0); } /** * 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 (file-part): force=%d, fstab=%d, mtab=%d", 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, mflags); } /* 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, mflags); } 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, const 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 && bufsz && vsnprintf(buf, bufsz, fmt, va) < 0) *buf = '\0'; 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. * * The current implementation does not read error message from external * helper into @buf. * * 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 : "")); 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. 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 */ } if (rc) return rc; *mounted = __mnt_table_is_fs_mounted(mtab, fs, mnt_context_get_target_prefix(cxt)); 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: * * * 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); * * * * 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 current 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 * * * mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt)); * * * * 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 * * * mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt)); * * * * 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 int test_search_helper(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_context *cxt; const char *type; int rc; if (argc < 2) return -EINVAL; cxt = mnt_new_context(); if (!cxt) return -ENOMEM; type = argv[1]; mnt_context_get_fs(cxt); /* just to fill cxt->fs */ cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED; /* fake */ rc = mnt_context_prepare_helper(cxt, "mount", type); printf("helper is: %s\n", cxt->helper ? cxt->helper : "not found"); mnt_free_context(cxt); return rc; } 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 | */ mnt_context_set_target(cxt, argv[idx++]); else if (argc == idx + 2) { /* mount */ 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 | */ 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 | */ 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 ] [-t ] | " }, { "--umount", test_umount, "[-t ] [-f][-l][-r] |" }, { "--mount-all", test_mountall, "[-O ] [-t ] " }, { "--search-helper", test_search_helper, "" }, { NULL }}; umask(S_IWGRP|S_IWOTH); /* to be compatible with mount(8) */ return mnt_run_test(tss, argc, argv); } #endif /* TEST_PROGRAM */