| /* |
| * Copyright (C) 2010,2011,2012 Karel Zak <kzak@redhat.com> |
| * |
| * This file may be redistributed under the terms of the |
| * GNU Lesser General Public License. |
| */ |
| |
| /** |
| * 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(); |
| #endif |
| if (!cxt->utab_path) |
| cxt->utab_path = mnt_get_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. |
| * |
| * 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) |
| { |
| int errsv = 0; |
| int tmp; |
| |
| 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; |
| } |
| |
| 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; |
| 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; |
| |
| if (!cxt || !ns) |
| return NULL; |
| |
| old = cxt->ns_cur; |
| if (ns == old || ns->fd == -1) |
| return old; |
| |
| /* 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); |
| |
| 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 */ |