diff options
Diffstat (limited to 'libmount/src/context.c')
-rw-r--r-- | libmount/src/context.c | 2297 |
1 files changed, 2297 insertions, 0 deletions
diff --git a/libmount/src/context.c b/libmount/src/context.c new file mode 100644 index 0000000..d6f9050 --- /dev/null +++ b/libmount/src/context.c @@ -0,0 +1,2297 @@ +/* + * 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 <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; + + ruid = getuid(); + euid = geteuid(); + + mnt_context_reset_status(cxt); + + cxt->loopdev_fd = -1; + + /* if we're really root and aren't running setuid */ + cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1; + + DBG(CXT, mnt_debug_h(cxt, "----> allocate %s", + cxt->restricted ? "[RESTRICTED]" : "")); + + mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable); + + if (!cxt->mtab_writable) + /* use /run/mount/utab if /etc/mtab is useless */ + mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable); + + 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); + + if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) + mnt_free_table(cxt->fstab); + if (!(cxt->flags & MNT_FL_EXTERN_CACHE)) + mnt_free_cache(cxt->cache); + + mnt_context_clear_loopdev(cxt); + mnt_free_lock(cxt->lock); + mnt_free_update(cxt->update); + + free(cxt->children); + + DBG(CXT, mnt_debug_h(cxt, "<---- free")); + free(cxt); +} + +/** + * mnt_reset_context: + * @cxt: mount context + * + * Resets all information in the context that are directly related to + * the latest mount (spec, source, target, mount options, ....) + * + * The match patters, cached fstab, cached canonicalized paths and tags and + * [e]uid are not reseted. 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); + * + * + * to reset these 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, mnt_debug_h(cxt, "<---- reset [status=%d] ---->", + mnt_context_get_status(cxt))); + + fl = cxt->flags; + + if (!(cxt->flags & MNT_FL_EXTERN_FS)) + mnt_free_fs(cxt->fs); + + mnt_free_table(cxt->mtab); + + free(cxt->helper); + free(cxt->orig_user); + + cxt->fs = NULL; + cxt->mtab = NULL; + cxt->ambi = 0; + cxt->helper = NULL; + cxt->orig_user = NULL; + cxt->mountflags = 0; + cxt->user_mountflags = 0; + cxt->mountdata = NULL; + cxt->flags = MNT_FL_DEFAULT; + + mnt_context_reset_status(cxt); + + /* restore non-resetable flags */ + cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB); + cxt->flags |= (fl & MNT_FL_EXTERN_CACHE); + 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); + 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 set_flag(struct libmnt_context *cxt, int flag, int enable) +{ + if (!cxt) + return -EINVAL; + if (enable) { + DBG(CXT, mnt_debug_h(cxt, "enabling flag %04x", flag)); + cxt->flags |= flag; + } else { + DBG(CXT, mnt_debug_h(cxt, "disabling flag %04x", flag)); + cxt->flags &= ~flag; + } + return 0; +} + +/** + * mnt_context_is_restricted: + * @cxt: mount context + * + * Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts + */ +int mnt_context_is_restricted(struct libmnt_context *cxt) +{ + assert(cxt); + return cxt->restricted; +} + +/** + * mnt_context_set_optsmode + * @cxt: mount context + * @mode: MNT_OMASK_* flags + * + * Controls how to use mount optionsmsource 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 is 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 other is defined + * - the flags are eavaluated 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_OMASK_* mask or zero. + */ + +int mnt_context_get_optsmode(struct libmnt_context *cxt) +{ + return cxt ? cxt->optsmode : 0; +} + +/** + * mnt_context_disable_canonicalize: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Enable/disable paths canonicalization and tags evaluation. The libmount context + * canonicalies paths when search in fstab and when prepare source and target paths + * for mount(2) syscall. + * + * This fuction has effect to the private (within context) fstab instance only + * (see mnt_context_set_fstab()). If you want to use an external fstab then you + * need 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 enabled or 0. + */ +int mnt_context_is_nocanonicalize(struct libmnt_context *cxt) +{ + return cxt && (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 && (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 && (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 if mount -F enabled and the current context is child, or 0 + */ +int mnt_context_is_child(struct libmnt_context *cxt) +{ + 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 see 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 && (cxt->flags & MNT_FL_RDONLY_UMOUNT) ? 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 && (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 && (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 && (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 && (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 && (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 && (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 && (cxt->flags & MNT_FL_VERBOSE) ? 1 : 0; +} + +/** + * mnt_context_enable_loopdel: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable 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 && (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. Note that the external + * @fs instance is not deallocated by mnt_free_context() or mnt_reset_context(). + * + * The @fs will be modified by mnt_context_set_{source,target,options,fstype} + * functions, If the @fs is NULL then all current FS specific setting (source, + * target, etc., exclude spec) is reseted. + * + * 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; + if (!(cxt->flags & MNT_FL_EXTERN_FS)) + mnt_free_fs(cxt->fs); + + set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL); + 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 calloc() errrr. + */ +struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt) +{ + if (!cxt) + return NULL; + if (!cxt->fs) { + cxt->fs = mnt_new_fs(); + cxt->flags &= ~MNT_FL_EXTERN_FS; + } + return cxt->fs; +} + +/** + * mnt_context_set_source: + * @cxt: mount context + * @source: mount source (device, directory, UUID, LABEL, ...) + * + * 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 pr 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 pr 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 the real FS type. For comma-separated list of + * filesystems or for "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) +{ + if (fstype && strchr(fstype, ',')) + return -EINVAL; + 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 pr 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 + * + * 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() may the mount options string + * also includes options set by mnt_context_set_mflags() or another 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 the private struct libmnt_table by default. + * This function allows to overwrite the private fstab with an external + * instance. Note that the external instance is not deallocated by mnt_free_context(). + * + * The fstab is used read-only and is not modified, it should be possible to + * share the fstab between more mount contexts (TODO: tests it.) + * + * If the @tb argument is NULL then the current private fstab instance is + * reseted. + * + * 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; + if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) + mnt_free_table(cxt->fstab); + + set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL); + 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_cache *cache; + + 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); + cxt->flags &= ~MNT_FL_EXTERN_FSTAB; + rc = mnt_table_parse_fstab(cxt->fstab, NULL); + if (rc) + return rc; + } + + cache = mnt_context_get_cache(cxt); + + /* never touch an external fstab */ + if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) + mnt_table_set_cache(cxt->fstab, cache); + + 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 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) +{ + struct libmnt_cache *cache; + + if (!cxt) + return -EINVAL; + + if (!cxt->mtab) { + int rc; + + cxt->mtab = mnt_new_table(); + if (!cxt->mtab) + return -ENOMEM; + + if (cxt->table_errcb) + mnt_table_set_parser_errcb(cxt->mtab, cxt->table_errcb); + + rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path); + if (rc) + return rc; + } + + cache = mnt_context_get_cache(cxt); + mnt_table_set_cache(cxt->mtab, cache); + + if (tb) + *tb = cxt->mtab; + 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 use 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) +{ + struct libmnt_cache *cache; + int rc; + + 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); + + rc = mnt_table_parse_file(*tb, filename); + if (rc) { + mnt_free_table(*tb); + return rc; + } + + cache = mnt_context_get_cache(cxt); + if (cache) + mnt_table_set_cache(*tb, cache); + + return 0; +} + +/** + * 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; + + 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. Note that + * the external instance is not deallocated by mnt_free_context(). + * + * If the @cache argument is NULL then the current private cache instance is + * reseted. + * + * 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; + if (!(cxt->flags & MNT_FL_EXTERN_CACHE)) + mnt_free_cache(cxt->cache); + + set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL); + cxt->cache = 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) { + cxt->cache = mnt_new_cache(); + if (!cxt->cache) + return NULL; + cxt->flags &= ~MNT_FL_EXTERN_CACHE; + } + return cxt->cache; +} + +/** + * mnt_context_set_passwd_cb: + * @cxt: mount context + * @get: callback to get password + * @release: callback to release (delallocate) password + * + * Sets callbacks for encryption password (e.g encrypted loopdev) + * + * 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 udate) 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(cxt->mtab_writable ? + cxt->mtab_path : cxt->utab_path, 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"); + * + * these both calls have the same effect. + * + * 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 bitewise-OR the result with + * 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; + 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)); + } + 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 notest 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 bitewise-OR the result + * with 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 deallocated 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; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "preparing source path")); + + src = mnt_fs_get_source(cxt->fs); + + if (!src && (cxt->mountflags & MS_PROPAGATION)) + /* mount --make-{shared,private,...} */ + return mnt_fs_set_source(cxt->fs, "none"); + + /* ignore filesystems without source or filesystems + * where the source is quasi-path (//foo/bar) + */ + if (!src || mnt_fs_is_netfs(cxt->fs)) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "srcpath '%s'", src)); + + 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, mnt_debug_h(cxt, "failed to prepare srcpath [rc=%d]", rc)); + return rc; + } + + if (!path) + path = src; + + if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) || + mnt_fs_is_pseudofs(cxt->fs)) { + DBG(CXT, mnt_debug_h(cxt, "BIND/MOVE/pseudo FS source: %s", path)); + return rc; + } + + /* + * Initialize loop device + */ + if (mnt_context_is_loopdev(cxt)) { + rc = mnt_context_setup_loopdev(cxt); + if (rc) + return rc; + } + + DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'", + mnt_fs_get_source(cxt->fs))); + return 0; +} + +int mnt_context_prepare_target(struct libmnt_context *cxt) +{ + const char *tgt; + struct libmnt_cache *cache; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "preparing target path")); + + tgt = mnt_fs_get_target(cxt->fs); + if (!tgt) + return 0; + + 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 (rc) + DBG(CXT, mnt_debug_h(cxt, "failed to prepare target '%s'", tgt)); + else + DBG(CXT, mnt_debug_h(cxt, "final target '%s'", + mnt_fs_get_target(cxt->fs))); + return 0; +} + +/* + * It's usully no error when we're not able to detect filesystem type -- we + * will try to use types from /{etc,proc}/filesystems. + */ +int mnt_context_guess_fstype(struct libmnt_context *cxt) +{ + char *type; + const char *dev; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) + 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->flags & MS_REMOUNT) + goto none; + if (cxt->fstype_pattern) + goto done; + + dev = mnt_fs_get_srcpath(cxt->fs); + if (!dev) + goto done; + + if (access(dev, F_OK) == 0) { + struct libmnt_cache *cache = mnt_context_get_cache(cxt); + + type = mnt_get_fstype(dev, &cxt->ambi, cache); + if (type) { + rc = mnt_fs_set_fstype(cxt->fs, type); + if (!cache) + free(type); /* type is not cached */ + } + } else { + if (strchr(dev, ':') != NULL) + rc = mnt_fs_set_fstype(cxt->fs, "nfs"); + else if (!strncmp(dev, "//", 2)) + rc = mnt_fs_set_fstype(cxt->fs, "cifs"); + } + +done: + DBG(CXT, mnt_debug_h(cxt, "FS type: %s", mnt_fs_get_fstype(cxt->fs))); + 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; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!type) + type = mnt_fs_get_fstype(cxt->fs); + + if (mnt_context_is_nohelpers(cxt) + || !type + || !strcmp(type, "none") + || mnt_fs_is_swaparea(cxt->fs)) + return 0; + + 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, mnt_debug_h(cxt, "%-25s ... %s", helper, + rc ? "not found" : "found")); + if (rc) + continue; + + free(cxt->helper); + cxt->helper = strdup(helper); + if (!cxt->helper) + return -ENOMEM; + return 0; + } + + return 0; +} + +int mnt_context_merge_mflags(struct libmnt_context *cxt) +{ + unsigned long fl = 0; + int rc; + + assert(cxt); + + DBG(CXT, mnt_debug_h(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, mnt_debug_h(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, mnt_debug_h(cxt, "prepare update")); + + if (cxt->mountflags & MS_PROPAGATION) { + DBG(CXT, mnt_debug_h(cxt, "skip update: MS_PROPAGATION")); + return 0; + } + + target = mnt_fs_get_target(cxt->fs); + + if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/")) + /* Don't try to touch mtab if umounting root FS */ + mnt_context_disable_mtab(cxt, TRUE); + + if (mnt_context_is_nomtab(cxt)) { + DBG(CXT, mnt_debug_h(cxt, "skip update: NOMTAB flag")); + return 0; + } + if (cxt->helper) { + DBG(CXT, mnt_debug_h(cxt, "skip update: external helper")); + return 0; + } + if (!cxt->mtab_writable && !cxt->utab_writable) { + DBG(CXT, mnt_debug_h(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, mnt_debug_h(cxt, + "skip update: syscall failed [status=%d]", + cxt->syscall_status)); + return 0; + } + if (!cxt->update) { + cxt->update = mnt_new_update(); + if (!cxt->update) + return -ENOMEM; + + mnt_update_set_filename(cxt->update, + cxt->mtab_writable ? cxt->mtab_path : cxt->utab_path, + !cxt->mtab_writable); + } + + 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; + + assert(cxt); + + if (mnt_context_is_nomtab(cxt)) { + DBG(CXT, mnt_debug_h(cxt, "don't update: NOMTAB flag")); + return 0; + } + if (cxt->helper) { + DBG(CXT, mnt_debug_h(cxt, "don't update: external helper")); + return 0; + } + if (!cxt->update || !mnt_update_is_ready(cxt->update)) { + DBG(CXT, mnt_debug_h(cxt, "don't update: no update prepared")); + return 0; + } + if (cxt->syscall_status) { + DBG(CXT, mnt_debug_h(cxt, "don't update: syscall failed/not called")); + return 0; + } + + 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); + + return mnt_update_table(cxt->update, cxt->lock); +} + +static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb, + int direction) +{ + struct libmnt_fs *fs = NULL; + const char *src = NULL, *tgt = NULL; + int rc; + + assert(cxt); + assert(cxt->fs); + + if (!cxt->fs) + return -EINVAL; + + 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 source (for + * example bind mount, symlink to 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, mnt_debug_h(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) + 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; + struct libmnt_table *tab = NULL; + const char *src = NULL, *tgt = NULL; + + assert(cxt); + assert(cxt->fs); + + if (!cxt) + return -EINVAL; + + if (mnt_context_tab_applied(cxt)) /* already applied */ + return 0; + + if (mnt_context_is_restricted(cxt)) { + DBG(CXT, mnt_debug_h(cxt, "force fstab usage for non-root users!")); + cxt->optsmode = MNT_OMODE_USER; + } else if (cxt->optsmode == 0) { + DBG(CXT, mnt_debug_h(cxt, "use default optmode")); + 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 (cxt->fs) { + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + } + + DBG(CXT, mnt_debug_h(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, mnt_debug_h(cxt, "fstab not required -- skip")); + return 0; + } + + if (!src && tgt + && !(cxt->optsmode & MNT_OMODE_FSTAB) + && !(cxt->optsmode & MNT_OMODE_MTAB)) { + DBG(CXT, mnt_debug_h(cxt, "only target; fstab/mtab not required " + "-- skip, probably MS_PROPAGATION")); + return 0; + } + + DBG(CXT, mnt_debug_h(cxt, + "trying to apply fstab (src=%s, target=%s)", src, tgt)); + + /* let's initialize cxt->fs */ + mnt_context_get_fs(cxt); + + /* try fstab */ + if (cxt->optsmode & MNT_OMODE_FSTAB) { + 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)) { + DBG(CXT, mnt_debug_h(cxt, "trying to apply from mtab")); + rc = mnt_context_get_mtab(cxt, &tab); + if (!rc) + rc = apply_table(cxt, tab, MNT_ITER_BACKWARD); + } + if (rc) + DBG(CXT, mnt_debug_h(cxt, "failed to find entry in fstab/mtab")); + 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 && (cxt->flags & MNT_FL_TAB_APPLIED); +} + +/** + * 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 inform + * 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 && (!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 be used only if [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, mnt_debug_h(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 yet. + * + * 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; +} + +/** + * mnt_context_init_helper + * @cxt: mount context + * @action: MNT_ACT_{UMOUNT,MOUNT} + * @flags: not used now + * + * This function infors 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 than 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 = mnt_context_disable_helpers(cxt, TRUE); + + if (!rc) + rc = set_flag(cxt, MNT_FL_HELPER, 1); + if (!rc) + cxt->action = action; + + DBG(CXT, mnt_debug_h(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 [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 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; + int rc; + + if (!cxt || !fs || !mounted) + return -EINVAL; + + rc = mnt_context_get_mtab(cxt, &mtab); + if (rc) + return rc; + + *mounted = mnt_table_is_fs_mounted(mtab, fs); + 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, mnt_debug_h(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; + + if (!mnt_context_is_parent(cxt)) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "forking context")); + + DBG_FLUSH; + + pid = fork(); + + switch (pid) { + case -1: /* error */ + DBG(CXT, mnt_debug_h(cxt, "fork failed %m")); + return -errno; + + case 0: /* child */ + cxt->pid = getpid(); + mnt_context_enable_fork(cxt, FALSE); + DBG(CXT, mnt_debug_h(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, mnt_debug_h(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; +} + + + +#ifdef TEST_PROGRAM + +struct libmnt_lock *lock; + +static void lock_fallback(void) +{ + if (lock) + mnt_unlock_file(lock); +} + +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 <mountpont>|<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 internaly + * create and manage the lock + */ + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_mount(cxt); + if (rc) + printf("failed to mount: %m\n"); + else + printf("successfully mounted\n"); + + lock = NULL; /* because we use atexit lock_fallback */ + mnt_free_context(cxt); + return rc; +} + +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 <mountpont>|<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; +} + +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 <mountpont>|<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; +} + +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 (!strcmp(argv[idx], "-O")) { + mnt_context_set_options_pattern(cxt, argv[idx + 1]); + idx += 2; + } + if (!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 */ |