From 1bb1d80b3e3512a4664cb9895c95f29a900f8aba Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 4 Oct 2010 13:37:33 +0200 Subject: [PATCH] libmount: split context.c Signed-off-by: Karel Zak --- shlibs/mount/src/Makefile.am | 3 +- shlibs/mount/src/context.c | 906 +++---------------------------- shlibs/mount/src/context_mount.c | 716 ++++++++++++++++++++++++ shlibs/mount/src/mount.h.in | 49 ++ shlibs/mount/src/mountP.h | 69 +++ 5 files changed, 909 insertions(+), 834 deletions(-) create mode 100644 shlibs/mount/src/context_mount.c diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am index 298a8754..f1db1ad1 100644 --- a/shlibs/mount/src/Makefile.am +++ b/shlibs/mount/src/Makefile.am @@ -11,7 +11,8 @@ nodist_mountinc_HEADERS = mount.h usrlib_exec_LTLIBRARIES = libmount.la libmount_la_SOURCES = mountP.h version.c utils.c test.c init.c cache.c \ optstr.c optmap.c iter.c lock.c \ - fs.c tab.c tab_parse.c tab_update.c context.c \ + fs.c tab.c tab_parse.c tab_update.c \ + context.c context_mount.c \ $(mountinc_HEADERS) \ $(top_srcdir)/lib/at.c \ $(top_srcdir)/include/list.h \ diff --git a/shlibs/mount/src/context.c b/shlibs/mount/src/context.c index 2102be53..07a72c2d 100644 --- a/shlibs/mount/src/context.c +++ b/shlibs/mount/src/context.c @@ -13,87 +13,9 @@ #include #include -#include -#include - -#ifdef HAVE_LIBSELINUX -#include -#include -#endif - #include "c.h" #include "mountP.h" -/* - * Mount context -- high-level API - */ -struct _mnt_context -{ - int action; /* MNT_ACT_{MOUNT,UMOUNT} */ - - int restricted; /* root or not? */ - - char *fstype_pattern; /* for mnt_match_fstype() */ - char *optstr_pattern; /* for mnt_match_options() */ - - char *spec; /* unresolved source OR target */ - mnt_fs *fs; /* filesystem description (type, mountpopint, device, ...) */ - - mnt_tab *fstab; /* fstab (or mtab for some remounts) entires */ - mnt_tab *mtab; /* mtab entries */ - int optsmode; /* fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE} */ - - unsigned long mountflags; /* final mount(2) flags */ - const void *mountdata; /* final mount(2) data, string or binary data */ - - unsigned long user_mountflags; /* MNT_MS_* (loop=, user=, ...) */ - - mnt_update *update; /* mtab update */ - mnt_cache *cache; /* paths cache */ - - int flags; /* private context flags */ - int ambi; /* libblkid returns ambivalent result */ - - char *helper; /* name of the used /sbin/[u]mount. helper */ - int helper_status; /* helper wait(2) status */ - - char *orig_user; /* original (non-fixed) user= option */ - - int syscall_errno; /* mount(2) or umount(2) error */ -}; - -/* flags */ -#define MNT_FL_NOMTAB (1 << 1) -#define MNT_FL_FAKE (1 << 2) -#define MNT_FL_SLOPPY (1 << 3) -#define MNT_FL_VERBOSE (1 << 4) -#define MNT_FL_NOHELPERS (1 << 5) -#define MNT_FL_LOOPDEL (1 << 6) -#define MNT_FL_LAZY (1 << 7) -#define MNT_FL_FORCE (1 << 8) -#define MNT_FL_NOCANONICALIZE (1 << 9) -#define MNT_FL_NOLOCK (1 << 10) /* don't lock mtab file */ - -#define MNT_FL_EXTERN_FS (1 << 15) /* cxt->fs is not private */ -#define MNT_FL_EXTERN_FSTAB (1 << 16) /* cxt->fstab is not private */ -#define MNT_FL_EXTERN_CACHE (1 << 17) /* cxt->cache is not private */ - -#define MNT_FL_MOUNTDATA (1 << 20) -#define MNT_FL_FSTAB_APPLIED (1 << 21) -#define MNT_FL_MOUNTFLAGS_MERGED (1 << 22) /* MS_* flags was read from optstr */ -#define MNT_FL_SAVED_USER (1 << 23) - -/* default flags */ -#define MNT_FL_DEFAULT 0 - -/* mode for mount options from fstab */ -enum { - MNT_OPTSMODE_AUTO = 0, /* use options if source or target are not defined */ - MNT_OPTSMODE_IGNORE, /* never use mount options from fstab */ - MNT_OPTSMODE_FORCE, /* always use mount options from fstab */ - MNT_OPTSMODE_MTABFORCE, /* for MS_REMOUNT use always options mountinfo/mtab */ -}; - /** * mnt_new_context: * @@ -212,7 +134,7 @@ int mnt_reset_context(mnt_context *cxt) return 0; } -static int mnt_context_set_flag(mnt_context *cxt, int flag, int enable) +static int set_flag(mnt_context *cxt, int flag, int enable) { if (!cxt) return -EINVAL; @@ -223,17 +145,6 @@ static int mnt_context_set_flag(mnt_context *cxt, int flag, int enable) return 0; } -static mnt_fs *mnt_context_get_fs(mnt_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_is_restricted: * @cxt: mount context @@ -292,7 +203,7 @@ int mnt_context_set_optsmode(mnt_context *cxt, int mode) */ int mnt_context_disable_canonicalize(mnt_context *cxt, int disable) { - return mnt_context_set_flag(cxt, MNT_FL_NOCANONICALIZE, disable); + return set_flag(cxt, MNT_FL_NOCANONICALIZE, disable); } /** @@ -306,7 +217,7 @@ int mnt_context_disable_canonicalize(mnt_context *cxt, int disable) */ int mnt_context_enable_lazy(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_LAZY, enable); + return set_flag(cxt, MNT_FL_LAZY, enable); } /** @@ -320,7 +231,7 @@ int mnt_context_enable_lazy(mnt_context *cxt, int enable) */ int mnt_context_disable_helpers(mnt_context *cxt, int disable) { - return mnt_context_set_flag(cxt, MNT_FL_NOHELPERS, disable); + return set_flag(cxt, MNT_FL_NOHELPERS, disable); } /** @@ -334,7 +245,7 @@ int mnt_context_disable_helpers(mnt_context *cxt, int disable) */ int mnt_context_enable_sloppy(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_SLOPPY, enable); + return set_flag(cxt, MNT_FL_SLOPPY, enable); } /** @@ -348,7 +259,7 @@ int mnt_context_enable_sloppy(mnt_context *cxt, int enable) */ int mnt_context_enable_fake(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_FAKE, enable); + return set_flag(cxt, MNT_FL_FAKE, enable); } /** @@ -362,7 +273,7 @@ int mnt_context_enable_fake(mnt_context *cxt, int enable) */ int mnt_context_disable_mtab(mnt_context *cxt, int disable) { - return mnt_context_set_flag(cxt, MNT_FL_NOMTAB, disable); + return set_flag(cxt, MNT_FL_NOMTAB, disable); } /** @@ -376,7 +287,7 @@ int mnt_context_disable_mtab(mnt_context *cxt, int disable) */ int mnt_context_disable_lock(mnt_context *cxt, int disable) { - return mnt_context_set_flag(cxt, MNT_FL_NOLOCK, disable); + return set_flag(cxt, MNT_FL_NOLOCK, disable); } /** @@ -390,7 +301,7 @@ int mnt_context_disable_lock(mnt_context *cxt, int disable) */ int mnt_context_enable_force(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_FORCE, enable); + return set_flag(cxt, MNT_FL_FORCE, enable); } /** @@ -398,13 +309,13 @@ int mnt_context_enable_force(mnt_context *cxt, int enable) * @cxt: mount context * @enable: TRUE or FALSE * - * Enable/disable verbose output. + * Enable/disable verbose output (see also mnt_context_mount_strerror()). * * Returns: 0 on success, negative number in case of error. */ int mnt_context_enable_verbose(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_VERBOSE, enable); + return set_flag(cxt, MNT_FL_VERBOSE, enable); } /** @@ -418,7 +329,7 @@ int mnt_context_enable_verbose(mnt_context *cxt, int enable) */ int mnt_context_enable_loopdel(mnt_context *cxt, int enable) { - return mnt_context_set_flag(cxt, MNT_FL_LOOPDEL, enable); + return set_flag(cxt, MNT_FL_LOOPDEL, enable); } /** @@ -464,11 +375,22 @@ int mnt_context_set_fs(mnt_context *cxt, mnt_fs *fs) if (!(cxt->flags & MNT_FL_EXTERN_FS)) mnt_free_fs(cxt->fs); - mnt_context_set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL); + set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL); cxt->fs = fs; return 0; } +mnt_fs *mnt_context_get_fs(mnt_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 @@ -606,7 +528,7 @@ int mnt_context_set_fstab(mnt_context *cxt, mnt_tab *tb) if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) mnt_free_tab(cxt->fstab); - mnt_context_set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL); + set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL); cxt->fstab = tb; return 0; } @@ -632,12 +554,20 @@ int mnt_context_set_cache(mnt_context *cxt, mnt_cache *cache) if (!(cxt->flags & MNT_FL_EXTERN_CACHE)) mnt_free_cache(cxt->cache); - mnt_context_set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL); + set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL); cxt->cache = cache; return 0; } -static mnt_cache *mnt_context_get_cache(mnt_context *cxt) +/** + * mnt_context_get_cache + * @cxt: mount context + * + * See also mnt_context_set_cache(). + * + * Returns: pointer to cache or NULL if canonicalization is disabled. + */ +mnt_cache *mnt_context_get_cache(mnt_context *cxt) { if (!cxt || (cxt->flags & MNT_FL_NOCANONICALIZE)) return NULL; @@ -651,6 +581,33 @@ static mnt_cache *mnt_context_get_cache(mnt_context *cxt) return cxt->cache; } +/** + * mnt_context_get_lock: + * @cxt: mount context + * + * The lock is available after mnt_context_prepare_mount() or + * mnt_context_prepare_umount(). + * + * The application that uses libmount context does not have to care about + * mtab locking, but with a small exceptions: the application has to be able to + * remove the lock file when interrupted by signal. It means that properly written + * mount(8)-like application has to call mnt_unlock_file() from a signal handler. + * + * See also mnt_unlock_file(), mnt_context_disable_lock() and + * mnt_context_disable_mtab(). + * + * It's not error if this function returns NULL (it usually means that the + * context is not prepared yet, or mtab update is unnecessary). + * + * Returns: pointer to lock struct. + */ +mnt_lock *mnt_context_get_lock(mnt_context *cxt) +{ + if (!cxt || !cxt->update || (cxt->flags & (MNT_FL_NOMTAB | MNT_FL_NOLOCK))) + return NULL; + return mnt_update_get_lock(cxt->update); +} + /** * mnt_context_set_mountflags: * @cxt: mount context @@ -704,17 +661,6 @@ int mnt_context_get_mountflags(mnt_context *cxt, unsigned long *flags) return rc; } -static int mnt_context_is_remount(mnt_context *cxt) -{ - unsigned long fl = 0; - - if (cxt->mountflags & MS_REMOUNT) - return 1; - if (!mnt_context_get_mountflags(cxt, &fl) && (fl & MS_REMOUNT)) - return 1; - return 0; -} - /** * mnt_context_set_userspace_mountflags: * @cxt: mount context @@ -759,7 +705,7 @@ int mnt_context_get_userspace_mountflags(mnt_context *cxt, unsigned long *flags) return rc; } -static int mnt_context_is_loop(mnt_context *cxt) +static int is_loop(mnt_context *cxt) { unsigned long fl = 0; @@ -774,7 +720,6 @@ static int mnt_context_is_loop(mnt_context *cxt) return 0; } - /** * mnt_context_set_mountdata: * @cxt: mount context @@ -798,322 +743,10 @@ int mnt_context_set_mountdata(mnt_context *cxt, void *data) return 0; } -static int mnt_context_apply_tab(mnt_context *cxt, mnt_tab *tb) -{ - mnt_fs *fs = NULL; - const char *src = NULL, *tgt = NULL; - int rc; - - if (!cxt->fs) - return -EINVAL; - - src = mnt_fs_get_source(cxt->fs); - tgt = mnt_fs_get_target(cxt->fs); - - if (tgt && src) - ; /* TODO: search pair for MNT_OPTSMODE_FORCE */ - else if (src) - fs = mnt_tab_find_source(tb, src, MNT_ITER_FORWARD); - else if (tgt) - fs = mnt_tab_find_target(tb, tgt, MNT_ITER_FORWARD); - else if (cxt->spec) { - fs = mnt_tab_find_source(tb, cxt->spec, MNT_ITER_FORWARD); - - if (!fs && (strncmp(cxt->spec, "LABEL=", 6) || - strncmp(cxt->spec, "UUID=", 5))) - fs = mnt_tab_find_target(tb, cxt->spec, MNT_ITER_FORWARD); - } - - if (!fs) - return -EINVAL; - - DBG(CXT, mnt_debug_h(cxt, "apply entry:")); - DBG(CXT, mnt_fs_print_debug(fs, stderr)); - - /* copy from fstab 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 && cxt->optsmode != MNT_OPTSMODE_IGNORE) - rc = mnt_fs_prepend_optstr(cxt->fs, mnt_fs_get_optstr(fs)); - - if (!rc) - cxt->flags |= MNT_FL_FSTAB_APPLIED; - - return rc; -} - -int mnt_context_apply_fstab(mnt_context *cxt) -{ - int rc; - mnt_cache *cache; - const char *src = NULL, *tgt = NULL; - - if (!cxt || (!cxt->spec && !cxt->fs)) - return -EINVAL; - - if (cxt->flags & MNT_FL_FSTAB_APPLIED) - return 0; - - if (cxt->fs) { - src = mnt_fs_get_source(cxt->fs); - tgt = mnt_fs_get_target(cxt->fs); - } - - /* fstab is not required if source and target are specified */ - if (src && tgt && !(cxt->optsmode == MNT_OPTSMODE_FORCE || - cxt->optsmode == MNT_OPTSMODE_MTABFORCE)) - return 0; - - DBG(CXT, mnt_debug_h(cxt, - "trying to apply fstab (src=%s, target=%s, spec=%s)", - src, tgt, cxt->spec)); - - /* initialize fstab */ - if (!cxt->fstab) { - cxt->fstab = mnt_new_tab(); - if (!cxt->fstab) - goto errnomem; - cxt->flags &= ~MNT_FL_EXTERN_FSTAB; - rc = mnt_tab_parse_fstab(cxt->fstab); - if (rc) - goto err; - } - - cache = mnt_context_get_cache(cxt); /* NULL if MNT_FL_NOCANONICALIZE is enabled */ - - /* never touch an external fstab */ - if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) - mnt_tab_set_cache(cxt->fstab, cache); - - /* let's initialize cxt->fs */ - mnt_context_get_fs(cxt); - - /* try fstab */ - rc = mnt_context_apply_tab(cxt, cxt->fstab); - - /* try mtab */ - if (rc || (cxt->optsmode == MNT_OPTSMODE_MTABFORCE && - mnt_context_is_remount(cxt))) { - - cxt->mtab = mnt_new_tab(); - if (!cxt->mtab) - goto errnomem; - rc = mnt_tab_parse_mtab(cxt->mtab); - if (rc) - goto err; - - mnt_tab_set_cache(cxt->mtab, cache); - rc = mnt_context_apply_tab(cxt, cxt->mtab); - if (rc) - goto err; - } - return 0; - -errnomem: - rc = ENOMEM; -err: - DBG(CXT, mnt_debug_h(cxt, "failed to found entry in fstab/mtab")); - return rc; -} - -/* - * this has to be called after mnt_context_evaluate_permissions() - */ -static int mnt_context_fix_optstr(mnt_context *cxt) -{ - int rc = 0, rem_se = 0; - char *next, **optstr; - char *name, *val; - size_t namesz, valsz; - - if (!cxt) - return -EINVAL; - if (!cxt->fs) - return 0; - - assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); - - /* - * we directly work with optstr pointer here - */ - optstr = &cxt->fs->optstr; - if (!optstr) - return 0; - - /* The propagation flags should not be used together with any other flags */ - if (cxt->mountflags & MS_PROPAGATION) - cxt->mountflags &= MS_PROPAGATION; - - if (!mnt_optstr_get_option(*optstr, "user", &val, &valsz)) { - if (val) { - cxt->orig_user = strndup(val, valsz); - if (!cxt->orig_user) { - rc = -ENOMEM; - goto done; - } - } - cxt->flags |= MNT_FL_SAVED_USER; - } - - /* - * Sync mount options with mount flags - */ - rc = mnt_optstr_apply_flags(optstr, cxt->mountflags, - mnt_get_builtin_optmap(MNT_LINUX_MAP)); - if (rc) - goto done; - - rc = mnt_optstr_apply_flags(optstr, cxt->user_mountflags, - mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); - if (rc) - goto done; - - next = *optstr; - -#ifdef HAVE_LIBSELINUX - rem_se = (cxt->mountflags & MS_REMOUNT) || !is_selinux_enabled(); -#endif - DBG(CXT, mnt_debug_h(cxt, "fixing mount options: '%s'", *optstr)); - - while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { - - if (namesz == 3 && !strncmp(name, "uid", 3)) - rc = mnt_optstr_fix_uid(optstr, val, valsz, &next); - else if (namesz == 3 && !strncmp(name, "gid", 3)) - rc = mnt_optstr_fix_gid(optstr, val, valsz, &next); -#ifdef HAVE_LIBSELINUX - else if (namesz >= 7 && (!strncmp(name, "context", 7) || - !strncmp(name, "fscontext", 9) || - !strncmp(name, "defcontext", 10) || - !strncmp(name, "rootcontext", 11))) { - if (rem_se) { - /* remove context= option */ - next = name; - rc = mnt_optstr_remove_option_at(optstr, - name, val + valsz); - } else - rc = mnt_optstr_fix_secontext(optstr, - val, valsz, &next); - } -#endif - else if (namesz == 4 && (cxt->user_mountflags && MNT_MS_USER) && - !strncmp(name, "user", 4)) { - - rc = mnt_optstr_fix_user(optstr, - val ? val : name + namesz, - valsz, &next); - } - if (rc) - goto done; - } - -done: - __mnt_fs_set_optstr_ptr(cxt->fs, *optstr, TRUE); - DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: '%s'", rc, *optstr)); - return rc; -} - -/* - * Converts already evalulated and fixed options to the form that is comaptible - * with /sbin/mount. helpers. - * - * Retursn newly allocated string. - */ -static int mnt_context_get_helper_optstr(mnt_context *cxt, char **optstr) -{ - const char *o; - int rc = 0; - - assert(cxt); - assert(cxt->fs); - assert(optstr); - - *optstr = NULL; - o = mnt_fs_get_optstr(cxt->fs); - - if (o) - rc = mnt_optstr_append_option(optstr, o, NULL); - if (!rc && (cxt->flags & MNT_FL_SAVED_USER)) - rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user); - if (rc) { - free(*optstr); - *optstr = NULL; - } - return rc; -} - - /* - * this has to be called before mnt_context_fix_optstr() + * Translates LABEL/UUID/path to mountable path */ -static int mnt_context_evaluate_permissions(mnt_context *cxt) -{ - unsigned long u_flags; - const char *srcpath; - - if (!cxt) - return -EINVAL; - if (!cxt->fs) - return 0; - - mnt_context_get_userspace_mountflags(cxt, &u_flags); - - if (!mnt_context_is_restricted(cxt)) { - /* - * superuser mount - */ - cxt->user_mountflags &= ~MNT_MS_OWNER; - cxt->user_mountflags &= ~MNT_MS_GROUP; - cxt->user_mountflags &= ~MNT_MS_USER; - cxt->user_mountflags &= ~MNT_MS_USERS; - } else { - /* - * user mount - */ - if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) - cxt->mountflags |= MS_OWNERSECURE; - - if (u_flags & (MNT_MS_USER | MNT_MS_USERS)) - cxt->mountflags |= MS_SECURE; - - srcpath = mnt_fs_get_srcpath(cxt->fs); - if (!srcpath) - return -EINVAL; - - /* - * MS_OWNER: Allow owners to mount when fstab contains the - * owner option. Note that this should never be used in a high - * security environment, but may be useful to give people at - * the console the possibility of mounting a floppy. MS_GROUP: - * Allow members of device group to mount. (Martin Dickopp) - */ - if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) { - struct stat sb; - - if (strncmp(srcpath, "/dev/", 5) == 0 && - stat(srcpath, &sb) == 0 && - (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) || - ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid)))) - - cxt->user_mountflags |= MNT_MS_USER; - } - - if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) { - DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS")); - return -EPERM; - } - } - - return 0; -} - -static int mnt_context_prepare_srcpath(mnt_context *cxt) +int mnt_context_prepare_srcpath(mnt_context *cxt) { const char *path = NULL, *type; mnt_cache *cache; @@ -1168,7 +801,7 @@ static int mnt_context_prepare_srcpath(mnt_context *cxt) /* * Initialize loop device */ - if (mnt_context_is_loop(cxt) && + if (is_loop(cxt) && !(cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION | MS_REMOUNT))) { ; /* TODO */ @@ -1178,7 +811,7 @@ static int mnt_context_prepare_srcpath(mnt_context *cxt) return 0; } -static int mnt_context_guess_fstype(mnt_context *cxt) +int mnt_context_guess_fstype(mnt_context *cxt) { char *type; const char *dev; @@ -1232,26 +865,6 @@ err: return rc; } -static int mnt_context_merge_mountflags(mnt_context *cxt) -{ - unsigned long fl = 0; - int rc; - - rc = mnt_context_get_mountflags(cxt, &fl); - if (rc) - return rc; - cxt->mountflags = fl; - - fl = 0; - rc = mnt_context_get_userspace_mountflags(cxt, &fl); - if (rc) - return rc; - cxt->user_mountflags = fl; - - cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED; - return 0; -} - /* * The default is to use fstype from cxt->fs, this could be overwritten by * @type. The @act is MNT_ACT_{MOUNT,UMOUNT}. @@ -1259,17 +872,15 @@ static int mnt_context_merge_mountflags(mnt_context *cxt) * 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. */ -static int mnt_context_prepare_helper(mnt_context *cxt, int act, const char *type) +int mnt_context_prepare_helper(mnt_context *cxt, const char *name, + const char *type) { char search_path[] = FS_SEARCH_PATH; /* from config.h */ char *p = NULL, *path; - const char *name; assert(cxt); assert(cxt->fs); - name = MNT_ACT_MOUNT ? "mount" : "umount"; - if (!type) type = mnt_fs_get_fstype(cxt->fs); @@ -1313,7 +924,10 @@ static int mnt_context_prepare_helper(mnt_context *cxt, int act, const char *typ return 0; } -static int mnt_context_prepare_update(mnt_context *cxt, int act) +/* + * Prepare /etc/mtab or /var/run/mount/mountinfo update + */ +int mnt_context_prepare_update(mnt_context *cxt, int act) { int rc; @@ -1346,377 +960,3 @@ static int mnt_context_prepare_update(mnt_context *cxt, int act) return rc; } -/** - * mnt_context_get_lock: - * @cxt: mount context - * - * The lock is available after mnt_context_prepare_mount() or - * mnt_context_prepare_umount(). - * - * The application that uses libmount context does not have to care about - * mtab locking, but with a small exceptions: the application has to be able to - * remove the lock file when interrupted by signal. It means that properly written - * mount(8)-like application has to call mnt_unlock_file() from a signal handler. - * - * See also mnt_unlock_file(), mnt_context_disable_lock() and - * mnt_context_disable_mtab(). - * - * It's not error if this function returns NULL (it usually means that the - * context is not prepared yet, or mtab update is unnecessary). - * - * Returns: pointer to lock struct. - */ -mnt_lock *mnt_context_get_lock(mnt_context *cxt) -{ - if (!cxt || !cxt->update || (cxt->flags & (MNT_FL_NOMTAB | MNT_FL_NOLOCK))) - return NULL; - return mnt_update_get_lock(cxt->update); -} - -/** - * mnt_context_prepare_mount: - * @cxt: mount context - * - * This function: - * - read information from fstab (if necessary) - * - cleanup mount options - * - check premissions - * - prepare device (e.g. loop device) - * - detect FS type (if necessary) - * - generate mount flags and mount data (if not set yet) - * - prepare for mtab update (if necessary) - * - * It's strongly recommended to use this function before mnt_context_mount_fs(). - * - * Returns: 0 on success, and negative number in case of error. - */ -int mnt_context_prepare_mount(mnt_context *cxt) -{ - int rc = 0; - - if (!cxt) - return -EINVAL; - - if (!cxt->spec && !(cxt->fs && (mnt_fs_get_source(cxt->fs) || - mnt_fs_get_target(cxt->fs)))) - return -EINVAL; - - rc = mnt_context_apply_fstab(cxt); - if (!rc) - rc = mnt_context_merge_mountflags(cxt); - if (!rc) - rc = mnt_context_evaluate_permissions(cxt); - if (!rc) - rc = mnt_context_fix_optstr(cxt); - if (!rc) - rc = mnt_context_prepare_srcpath(cxt); - if (!rc) - rc = mnt_context_guess_fstype(cxt); - if (!rc) - rc = mnt_context_prepare_helper(cxt, MNT_ACT_MOUNT, NULL); - if (!rc) - rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT); - - if (!rc) { - DBG(CXT, mnt_debug_h(cxt, "sucessfully prepared")); - return 0; - } - - DBG(CXT, mnt_debug_h(cxt, "prepare failed")); - return rc; -} - -static int exec_mount_helper(mnt_context *cxt) -{ - char *o = NULL; - int rc; - - assert(cxt); - assert(cxt->fs); - assert(cxt->helper); - - rc = mnt_context_get_helper_optstr(cxt, &o); - if (rc) - goto done; - - DBG_FLUSH; - - switch (fork()) { - case 0: - { - const char *args[12], *type; - int i = 0; - - if (setgid(getgid()) < 0) - exit(EXIT_FAILURE); - - if (setuid(getuid()) < 0) - exit(EXIT_FAILURE); - - type = mnt_fs_get_fstype(cxt->fs); - - args[i++] = cxt->helper; /* 1 */ - args[i++] = mnt_fs_get_srcpath(cxt->fs); /* 2 */ - args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */ - - if (cxt->flags & MNT_FL_SLOPPY) - args[i++] = "-s"; /* 4 */ - if (cxt->flags & MNT_FL_FAKE) - args[i++] = "-f"; /* 5 */ - if (cxt->flags & MNT_FL_NOMTAB) - args[i++] = "-n"; /* 6 */ - if (cxt->flags & MNT_FL_VERBOSE) - args[i++] = "-v"; /* 7 */ - if (o) { - args[i++] = "-o"; /* 8 */ - args[i++] = o; /* 9 */ - } - if (type && !endswith(cxt->helper, type)) { - args[i++] = "-t"; /* 10 */ - args[i++] = type; /* 11 */ - } - args[i] = NULL; /* 12 */ -#ifdef CONFIG_LIBMOUNT_DEBUG - i = 0; - for (i = 0; args[i]; i++) - DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", - i, args[i])); -#endif - DBG_FLUSH; - execv(cxt->helper, (char * const *) args); - exit(EXIT_FAILURE); - } - default: - { - int st; - wait(&st); - cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; - - DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", - cxt->helper, cxt->helper_status)); - rc = 0; - break; - } - - case -1: - rc = -errno; - DBG(CXT, mnt_debug_h(cxt, "fork() failed")); - break; - } - -done: - free(o); - return rc; -} - -/* - * The default is to use fstype from cxt->fs, this could be overwritten by - * @try_type argument. - */ -static int mnt_context_do_mount(mnt_context *cxt, const char *try_type) -{ - int rc; - const char *src, *target, *type; - unsigned long flags; - - assert(cxt); - assert(cxt->fs); - - if (try_type && !cxt->helper) { - rc = mnt_context_prepare_helper(cxt, MNT_ACT_MOUNT, try_type); - if (!rc) - return rc; - } - if (cxt->helper) - return exec_mount_helper(cxt); - - type = try_type ? : mnt_fs_get_fstype(cxt->fs); - - flags = cxt->mountflags; - src = mnt_fs_get_srcpath(cxt->fs); - target = mnt_fs_get_target(cxt->fs); - - if (!src || !target) - return -EINVAL; - - if (!(flags & MS_MGC_MSK)) - flags |= MS_MGC_VAL; - - DBG(CXT, mnt_debug_h(cxt, "calling mount(2) " - "[source=%s, target=%s, type=%s, " - " mountflags=%08lx, mountdata=%s]", - src, target, type, - flags, cxt->mountdata ? "yes" : "")); - - if (mount(src, target, type, flags, cxt->mountdata)) { - cxt->syscall_errno = errno; - DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d]", - cxt->syscall_errno)); - return -cxt->syscall_errno; - } - - DBG(CXT, mnt_debug_h(cxt, "mount(2) success")); - return 0; -} - -/** - * mnt_context_mount_fs: - * @cxt: mount context - * - * Mount filesystem by mount(2) or fork()+exec(/sbin/mount.). - * - * See also mnt_context_disable_helpers(). - * - * Returns: 0 on success, and negative number in case of error. - */ -int mnt_context_mount_fs(mnt_context *cxt) -{ - int rc = -EINVAL; - const char *type; - - if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP)) - return -EINVAL; - - if (!(cxt->flags & MNT_FL_MOUNTDATA)) - cxt->mountdata = (char *) mnt_fs_get_fs_optstr(cxt->fs); - - type = mnt_fs_get_fstype(cxt->fs); - - if (type && !strchr(type, ',')) { - rc = mnt_context_do_mount(cxt, NULL); - if (rc) - return rc; - } - - /* TODO: try all filesystems from comma separated list of types */ - - /* TODO: try all filesystems from /proc/filesystems and /etc/filesystems */ - - return rc; -} - -/** - * mnt_context_post_mount: - * @cxt: mount context - * - * Updates mtab, etc. This function should be always called after - * mnt_context_do_mount(). - * - * Returns: 0 on success, and negative number in case of error. - */ -int mnt_context_post_mount(mnt_context *cxt) -{ - int rc = 0; - - if (!cxt) - return -EINVAL; - /* - * Update /etc/mtab or /var/run/mount/mountinfo - */ - if (!cxt->syscall_errno && !cxt->helper && - !(cxt->flags & MNT_FL_NOMTAB) && - cxt->update && !mnt_update_is_pointless(cxt->update)) { - - /* TODO: if mtab update is expected then checkif the target is really - * mounted read-write to avoid 'ro' in mtab and 'rw' in /proc/mounts. - */ - rc = mnt_update_file(cxt->update); - } - - return rc; -} - -/** - * mnt_context_get_mount_error: - * @cxt: mount context - * @buf: buffer for error message - * @bufsiz: size of buffer - * - * Generates human readable error message for failed mnt_context_mount_fs(). - * - * Returns: 0 on success, and negative number in case of error. - */ -int mnt_context_get_mount_error(mnt_context *cxt, char *buf, size_t bufsiz) -{ - /* TODO: based on cxt->syscall_errno or cxt->helper_status */ - return 0; -} - -#ifdef TEST_PROGRAM - -mnt_lock *lock; - -static void lock_fallback(void) -{ - if (lock) { - mnt_unlock_file(lock); - mnt_free_lock(lock); - } -} - -int test_mount(struct mtest *ts, int argc, char *argv[]) -{ - int idx = 1, rc = 0; - mnt_context *cxt; - - if (argc < 2) - return -EINVAL; - - cxt = mnt_new_context(); - if (!cxt) - return -ENOMEM; - - if (!strcmp(argv[idx], "-o")) { - mnt_context_set_optstr(cxt, argv[idx + 1]); - idx += 2; - } - if (!strcmp(argv[idx], "-t")) { - /* TODO: use mnt_context_set_fstype_pattern() */ - mnt_context_set_fstype(cxt, argv[idx + 1]); - idx += 2; - } - - if (argc == idx + 1) - /* mount */ - mnt_context_set_spec(cxt, argv[idx++]); - - else if (argc == idx + 2) { - /* mount */ - mnt_context_set_source(cxt, argv[idx++]); - mnt_context_set_target(cxt, argv[idx++]); - } - - rc = mnt_context_prepare_mount(cxt); - if (rc) - printf("failed to prepare mount\n"); - else { - lock = mnt_context_get_lock(cxt); - if (lock) - atexit(lock_fallback); - - rc = mnt_context_mount_fs(cxt); - if (rc) - printf("failed to mount\n"); - else { - printf("successfully mounted"); - rc = mnt_context_post_mount(cxt); - if (rc) - printf("mtab update failed\n"); - } - } - - mnt_free_context(cxt); - return rc; -} - -int main(int argc, char *argv[]) -{ - struct mtest tss[] = { - { "--mount", test_mount, "[-o ] [-t ] | " }, - { NULL } - }; - - return mnt_run_test(tss, argc, argv); -} - -#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/context_mount.c b/shlibs/mount/src/context_mount.c new file mode 100644 index 00000000..d5f361a8 --- /dev/null +++ b/shlibs/mount/src/context_mount.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2010 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBSELINUX +#include +#include +#endif + +#include +#include + +#include "c.h" +#include "mountP.h" + +static int is_remount(mnt_context *cxt) +{ + unsigned long fl = 0; + + if (cxt->mountflags & MS_REMOUNT) + return 1; + if (!mnt_context_get_mountflags(cxt, &fl) && (fl & MS_REMOUNT)) + return 1; + return 0; +} + +static int apply_tab(mnt_context *cxt, mnt_tab *tb) +{ + mnt_fs *fs = NULL; + const char *src = NULL, *tgt = NULL; + int rc; + + if (!cxt->fs) + return -EINVAL; + + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + + if (tgt && src) + ; /* TODO: search pair for MNT_OPTSMODE_FORCE */ + else if (src) + fs = mnt_tab_find_source(tb, src, MNT_ITER_FORWARD); + else if (tgt) + fs = mnt_tab_find_target(tb, tgt, MNT_ITER_FORWARD); + else if (cxt->spec) { + fs = mnt_tab_find_source(tb, cxt->spec, MNT_ITER_FORWARD); + + if (!fs && (strncmp(cxt->spec, "LABEL=", 6) || + strncmp(cxt->spec, "UUID=", 5))) + fs = mnt_tab_find_target(tb, cxt->spec, MNT_ITER_FORWARD); + } + + if (!fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "apply entry:")); + DBG(CXT, mnt_fs_print_debug(fs, stderr)); + + /* copy from fstab 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 && cxt->optsmode != MNT_OPTSMODE_IGNORE) + rc = mnt_fs_prepend_optstr(cxt->fs, mnt_fs_get_optstr(fs)); + + if (!rc) + cxt->flags |= MNT_FL_FSTAB_APPLIED; + + return rc; +} + +static int apply_fstab(mnt_context *cxt) +{ + int rc; + mnt_cache *cache; + const char *src = NULL, *tgt = NULL; + + if (!cxt || (!cxt->spec && !cxt->fs)) + return -EINVAL; + + if (cxt->flags & MNT_FL_FSTAB_APPLIED) + return 0; + + if (cxt->fs) { + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + } + + /* fstab is not required if source and target are specified */ + if (src && tgt && !(cxt->optsmode == MNT_OPTSMODE_FORCE || + cxt->optsmode == MNT_OPTSMODE_MTABFORCE)) + return 0; + + DBG(CXT, mnt_debug_h(cxt, + "trying to apply fstab (src=%s, target=%s, spec=%s)", + src, tgt, cxt->spec)); + + /* initialize fstab */ + if (!cxt->fstab) { + cxt->fstab = mnt_new_tab(); + if (!cxt->fstab) + goto errnomem; + cxt->flags &= ~MNT_FL_EXTERN_FSTAB; + rc = mnt_tab_parse_fstab(cxt->fstab); + if (rc) + goto err; + } + + cache = mnt_context_get_cache(cxt); /* NULL if MNT_FL_NOCANONICALIZE is enabled */ + + /* never touch an external fstab */ + if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) + mnt_tab_set_cache(cxt->fstab, cache); + + /* let's initialize cxt->fs */ + mnt_context_get_fs(cxt); + + /* try fstab */ + rc = apply_tab(cxt, cxt->fstab); + + /* try mtab */ + if (rc || (cxt->optsmode == MNT_OPTSMODE_MTABFORCE && is_remount(cxt))) { + + cxt->mtab = mnt_new_tab(); + if (!cxt->mtab) + goto errnomem; + rc = mnt_tab_parse_mtab(cxt->mtab); + if (rc) + goto err; + + mnt_tab_set_cache(cxt->mtab, cache); + rc = apply_tab(cxt, cxt->mtab); + if (rc) + goto err; + } + return 0; + +errnomem: + rc = ENOMEM; +err: + DBG(CXT, mnt_debug_h(cxt, "failed to found entry in fstab/mtab")); + return rc; +} + +/* + * this has to be called after mnt_context_evaluate_permissions() + */ +static int fix_optstr(mnt_context *cxt) +{ + int rc = 0, rem_se = 0; + char *next, **optstr; + char *name, *val; + size_t namesz, valsz; + + if (!cxt) + return -EINVAL; + if (!cxt->fs) + return 0; + + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + /* + * we directly work with optstr pointer here + */ + optstr = &cxt->fs->optstr; + if (!optstr) + return 0; + + /* The propagation flags should not be used together with any other flags */ + if (cxt->mountflags & MS_PROPAGATION) + cxt->mountflags &= MS_PROPAGATION; + + if (!mnt_optstr_get_option(*optstr, "user", &val, &valsz)) { + if (val) { + cxt->orig_user = strndup(val, valsz); + if (!cxt->orig_user) { + rc = -ENOMEM; + goto done; + } + } + cxt->flags |= MNT_FL_SAVED_USER; + } + + /* + * Sync mount options with mount flags + */ + rc = mnt_optstr_apply_flags(optstr, cxt->mountflags, + mnt_get_builtin_optmap(MNT_LINUX_MAP)); + if (rc) + goto done; + + rc = mnt_optstr_apply_flags(optstr, cxt->user_mountflags, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); + if (rc) + goto done; + + next = *optstr; + +#ifdef HAVE_LIBSELINUX + rem_se = (cxt->mountflags & MS_REMOUNT) || !is_selinux_enabled(); +#endif + DBG(CXT, mnt_debug_h(cxt, "fixing mount options: '%s'", *optstr)); + + while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { + + if (namesz == 3 && !strncmp(name, "uid", 3)) + rc = mnt_optstr_fix_uid(optstr, val, valsz, &next); + else if (namesz == 3 && !strncmp(name, "gid", 3)) + rc = mnt_optstr_fix_gid(optstr, val, valsz, &next); +#ifdef HAVE_LIBSELINUX + else if (namesz >= 7 && (!strncmp(name, "context", 7) || + !strncmp(name, "fscontext", 9) || + !strncmp(name, "defcontext", 10) || + !strncmp(name, "rootcontext", 11))) { + if (rem_se) { + /* remove context= option */ + next = name; + rc = mnt_optstr_remove_option_at(optstr, + name, val + valsz); + } else + rc = mnt_optstr_fix_secontext(optstr, + val, valsz, &next); + } +#endif + else if (namesz == 4 && (cxt->user_mountflags && MNT_MS_USER) && + !strncmp(name, "user", 4)) { + + rc = mnt_optstr_fix_user(optstr, + val ? val : name + namesz, + valsz, &next); + } + if (rc) + goto done; + } + +done: + __mnt_fs_set_optstr_ptr(cxt->fs, *optstr, TRUE); + DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: '%s'", rc, *optstr)); + return rc; +} + +/* + * Converts already evalulated and fixed options to the form that is comaptible + * with /sbin/mount. helpers. + * + * Retursn newly allocated string. + */ +static int generate_helper_optstr(mnt_context *cxt, char **optstr) +{ + const char *o; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert(optstr); + + *optstr = NULL; + o = mnt_fs_get_optstr(cxt->fs); + + if (o) + rc = mnt_optstr_append_option(optstr, o, NULL); + if (!rc && (cxt->flags & MNT_FL_SAVED_USER)) + rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user); + if (rc) { + free(*optstr); + *optstr = NULL; + } + return rc; +} + + +/* + * this has to be called before mnt_context_fix_optstr() + */ +static int evaluate_permissions(mnt_context *cxt) +{ + unsigned long u_flags; + const char *srcpath; + + if (!cxt) + return -EINVAL; + if (!cxt->fs) + return 0; + + mnt_context_get_userspace_mountflags(cxt, &u_flags); + + if (!mnt_context_is_restricted(cxt)) { + /* + * superuser mount + */ + cxt->user_mountflags &= ~MNT_MS_OWNER; + cxt->user_mountflags &= ~MNT_MS_GROUP; + cxt->user_mountflags &= ~MNT_MS_USER; + cxt->user_mountflags &= ~MNT_MS_USERS; + } else { + /* + * user mount + */ + if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) + cxt->mountflags |= MS_OWNERSECURE; + + if (u_flags & (MNT_MS_USER | MNT_MS_USERS)) + cxt->mountflags |= MS_SECURE; + + srcpath = mnt_fs_get_srcpath(cxt->fs); + if (!srcpath) + return -EINVAL; + + /* + * MS_OWNER: Allow owners to mount when fstab contains the + * owner option. Note that this should never be used in a high + * security environment, but may be useful to give people at + * the console the possibility of mounting a floppy. MS_GROUP: + * Allow members of device group to mount. (Martin Dickopp) + */ + if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) { + struct stat sb; + + if (strncmp(srcpath, "/dev/", 5) == 0 && + stat(srcpath, &sb) == 0 && + (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) || + ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid)))) + + cxt->user_mountflags |= MNT_MS_USER; + } + + if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) { + DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS")); + return -EPERM; + } + } + + return 0; +} + +static int merge_mountflags(mnt_context *cxt) +{ + unsigned long fl = 0; + int rc; + + rc = mnt_context_get_mountflags(cxt, &fl); + if (rc) + return rc; + cxt->mountflags = fl; + + fl = 0; + rc = mnt_context_get_userspace_mountflags(cxt, &fl); + if (rc) + return rc; + cxt->user_mountflags = fl; + + cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED; + return 0; +} + +static int exec_helper(mnt_context *cxt) +{ + char *o = NULL; + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper); + + rc = generate_helper_optstr(cxt, &o); + if (rc) + goto done; + + DBG_FLUSH; + + switch (fork()) { + case 0: + { + const char *args[12], *type; + int i = 0; + + if (setgid(getgid()) < 0) + exit(EXIT_FAILURE); + + if (setuid(getuid()) < 0) + exit(EXIT_FAILURE); + + type = mnt_fs_get_fstype(cxt->fs); + + args[i++] = cxt->helper; /* 1 */ + args[i++] = mnt_fs_get_srcpath(cxt->fs); /* 2 */ + args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */ + + if (cxt->flags & MNT_FL_SLOPPY) + args[i++] = "-s"; /* 4 */ + if (cxt->flags & MNT_FL_FAKE) + args[i++] = "-f"; /* 5 */ + if (cxt->flags & MNT_FL_NOMTAB) + args[i++] = "-n"; /* 6 */ + if (cxt->flags & MNT_FL_VERBOSE) + args[i++] = "-v"; /* 7 */ + if (o) { + args[i++] = "-o"; /* 8 */ + args[i++] = o; /* 9 */ + } + if (type && !endswith(cxt->helper, type)) { + args[i++] = "-t"; /* 10 */ + args[i++] = type; /* 11 */ + } + args[i] = NULL; /* 12 */ +#ifdef CONFIG_LIBMOUNT_DEBUG + i = 0; + for (i = 0; args[i]; i++) + DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", + i, args[i])); +#endif + DBG_FLUSH; + execv(cxt->helper, (char * const *) args); + exit(EXIT_FAILURE); + } + default: + { + int st; + wait(&st); + cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; + + DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", + cxt->helper, cxt->helper_status)); + rc = 0; + break; + } + + case -1: + rc = -errno; + DBG(CXT, mnt_debug_h(cxt, "fork() failed")); + break; + } + +done: + free(o); + return rc; +} + +/* + * The default is to use fstype from cxt->fs, this could be overwritten by + * @try_type argument. + */ +static int do_mount(mnt_context *cxt, const char *try_type) +{ + int rc; + const char *src, *target, *type; + unsigned long flags; + + assert(cxt); + assert(cxt->fs); + + if (try_type && !cxt->helper) { + rc = mnt_context_prepare_helper(cxt, "mount", try_type); + if (!rc) + return rc; + } + if (cxt->helper) + return exec_helper(cxt); + + type = try_type ? : mnt_fs_get_fstype(cxt->fs); + + flags = cxt->mountflags; + src = mnt_fs_get_srcpath(cxt->fs); + target = mnt_fs_get_target(cxt->fs); + + if (!src || !target) + return -EINVAL; + + if (!(flags & MS_MGC_MSK)) + flags |= MS_MGC_VAL; + + DBG(CXT, mnt_debug_h(cxt, "calling mount(2) " + "[source=%s, target=%s, type=%s, " + " mountflags=%08lx, mountdata=%s]", + src, target, type, + flags, cxt->mountdata ? "yes" : "")); + + if (mount(src, target, type, flags, cxt->mountdata)) { + cxt->syscall_errno = errno; + DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d]", + cxt->syscall_errno)); + return -cxt->syscall_errno; + } + + DBG(CXT, mnt_debug_h(cxt, "mount(2) success")); + return 0; +} + +/** + * mnt_context_prepare_mount: + * @cxt: mount context + * + * This function: + * - read information from fstab (if necessary) + * - cleanup mount options + * - check premissions + * - prepare device (e.g. loop device) + * - detect FS type (if necessary) + * - generate mount flags and mount data (if not set yet) + * - prepare for mtab update (if necessary) + * + * It's strongly recommended to use this function before mnt_context_mount_fs(). + * + * Returns: 0 on success, and negative number in case of error. + */ +int mnt_context_prepare_mount(mnt_context *cxt) +{ + int rc = 0; + + if (!cxt) + return -EINVAL; + + if (!cxt->spec && !(cxt->fs && (mnt_fs_get_source(cxt->fs) || + mnt_fs_get_target(cxt->fs)))) + return -EINVAL; + + rc = apply_fstab(cxt); + if (!rc) + rc = merge_mountflags(cxt); + if (!rc) + rc = evaluate_permissions(cxt); + if (!rc) + rc = fix_optstr(cxt); + if (!rc) + rc = mnt_context_prepare_srcpath(cxt); + if (!rc) + rc = mnt_context_guess_fstype(cxt); + if (!rc) + rc = mnt_context_prepare_helper(cxt, "mount", NULL); + if (!rc) + rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT); + + if (!rc) { + DBG(CXT, mnt_debug_h(cxt, "sucessfully prepared")); + return 0; + } + + DBG(CXT, mnt_debug_h(cxt, "prepare failed")); + return rc; +} + +/** + * mnt_context_do_mount: + * @cxt: mount context + * + * Mount filesystem by mount(2) or fork()+exec(/sbin/mount.). + * + * See also mnt_context_disable_helpers(). + * + * Returns: 0 on success, and negative number in case of error. + */ +int mnt_context_do_mount(mnt_context *cxt) +{ + int rc = -EINVAL; + const char *type; + + if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP)) + return -EINVAL; + + if (!(cxt->flags & MNT_FL_MOUNTDATA)) + cxt->mountdata = (char *) mnt_fs_get_fs_optstr(cxt->fs); + + type = mnt_fs_get_fstype(cxt->fs); + + if (type && !strchr(type, ',')) { + rc = do_mount(cxt, NULL); + if (rc) + return rc; + } + + /* TODO: try all filesystems from comma separated list of types */ + + /* TODO: try all filesystems from /proc/filesystems and /etc/filesystems */ + + return rc; +} + +/** + * mnt_context_post_mount: + * @cxt: mount context + * + * Updates mtab, etc. This function should be always called after + * mnt_context_do_mount(). + * + * Returns: 0 on success, and negative number in case of error. + */ +int mnt_context_post_mount(mnt_context *cxt) +{ + int rc = 0; + + if (!cxt) + return -EINVAL; + /* + * Update /etc/mtab or /var/run/mount/mountinfo + */ + if (!cxt->syscall_errno && !cxt->helper && + !(cxt->flags & MNT_FL_NOMTAB) && + cxt->update && !mnt_update_is_pointless(cxt->update)) { + + /* TODO: if mtab update is expected then checkif the target is really + * mounted read-write to avoid 'ro' in mtab and 'rw' in /proc/mounts. + */ + rc = mnt_update_file(cxt->update); + } + + return rc; +} + +/** + * mnt_context_mount_strerror + * @cxt: mount context + * @buf: buffer + * @bufsiz: size of the buffer + * + * Returns: 0 or negative number in case of error. + */ +int mnt_context_mount_strerror(mnt_context *cxt, char *buf, size_t bufsiz) +{ + /* TODO: based on cxt->syscall_errno or cxt->helper_status */ + return 0; +} + +#ifdef TEST_PROGRAM + +mnt_lock *lock; + +static void lock_fallback(void) +{ + if (lock) { + mnt_unlock_file(lock); + mnt_free_lock(lock); + } +} + +int test_mount(struct mtest *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + mnt_context *cxt; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-o")) { + mnt_context_set_optstr(cxt, argv[idx + 1]); + idx += 2; + } + if (!strcmp(argv[idx], "-t")) { + /* TODO: use mnt_context_set_fstype_pattern() */ + mnt_context_set_fstype(cxt, argv[idx + 1]); + idx += 2; + } + + if (argc == idx + 1) + /* mount */ + mnt_context_set_spec(cxt, argv[idx++]); + + else if (argc == idx + 2) { + /* mount */ + mnt_context_set_source(cxt, argv[idx++]); + mnt_context_set_target(cxt, argv[idx++]); + } + + rc = mnt_context_prepare_mount(cxt); + if (rc) + printf("failed to prepare mount\n"); + else { + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_do_mount(cxt); + if (rc) + printf("failed to mount\n"); + else { + printf("successfully mounted"); + rc = mnt_context_post_mount(cxt); + if (rc) + printf("mtab update failed\n"); + } + } + + mnt_free_context(cxt); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--mount", test_mount, "[-o ] [-t ] | " }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in index 12f4b34c..99969727 100644 --- a/shlibs/mount/src/mount.h.in +++ b/shlibs/mount/src/mount.h.in @@ -299,6 +299,55 @@ extern int mnt_prepare_update(mnt_update *upd); extern int mnt_update_file(mnt_update *upd); extern int mnt_update_is_pointless(mnt_update *upd); +/* context.c */ + +/* mode for mount options from fstab */ +enum { + MNT_OPTSMODE_AUTO = 0, /* use options if source or target are not defined */ + MNT_OPTSMODE_IGNORE, /* never use mount options from fstab */ + MNT_OPTSMODE_FORCE, /* always use mount options from fstab */ + MNT_OPTSMODE_MTABFORCE, /* for MS_REMOUNT use always options from mountinfo/mtab */ +}; + +mnt_context *mnt_new_context(void); +void mnt_free_context(mnt_context *cxt); +int mnt_reset_context(mnt_context *cxt); +int mnt_context_is_restricted(mnt_context *cxt); +int mnt_context_set_optsmode(mnt_context *cxt, int mode); +int mnt_context_disable_canonicalize(mnt_context *cxt, int disable); +int mnt_context_enable_lazy(mnt_context *cxt, int enable); +int mnt_context_disable_helpers(mnt_context *cxt, int disable); +int mnt_context_enable_sloppy(mnt_context *cxt, int enable); +int mnt_context_enable_fake(mnt_context *cxt, int enable); +int mnt_context_disable_mtab(mnt_context *cxt, int disable); +int mnt_context_disable_lock(mnt_context *cxt, int disable); +int mnt_context_enable_force(mnt_context *cxt, int enable); +int mnt_context_enable_verbose(mnt_context *cxt, int enable); +int mnt_context_enable_loopdel(mnt_context *cxt, int enable); +int mnt_context_set_fs(mnt_context *cxt, mnt_fs *fs); +int mnt_context_set_source(mnt_context *cxt, const char *source); +int mnt_context_set_target(mnt_context *cxt, const char *target); +int mnt_context_set_fstype(mnt_context *cxt, const char *fstype); +int mnt_context_set_optstr(mnt_context *cxt, const char *optstr); +int mnt_context_append_optstr(mnt_context *cxt, const char *optstr); +int mnt_context_set_fstype_pattern(mnt_context *cxt, const char *pattern); +int mnt_context_set_optstr_pattern(mnt_context *cxt, const char *pattern); +int mnt_context_set_fstab(mnt_context *cxt, mnt_tab *tb); +int mnt_context_set_cache(mnt_context *cxt, mnt_cache *cache); +mnt_cache *mnt_context_get_cache(mnt_context *cxt); +mnt_lock *mnt_context_get_lock(mnt_context *cxt); +int mnt_context_set_mountflags(mnt_context *cxt, unsigned long flags); +int mnt_context_get_mountflags(mnt_context *cxt, unsigned long *flags); +int mnt_context_set_userspace_mountflags(mnt_context *cxt, unsigned long flags); +int mnt_context_get_userspace_mountflags(mnt_context *cxt, unsigned long *flags); +int mnt_context_set_mountdata(mnt_context *cxt, void *data); + + + + + + + /* * mount(8) userspace options masks (MNT_MAP_USERSPACE map) */ diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index 468e77a0..51230c67 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -202,6 +202,68 @@ struct _mnt_tab { }; +/* + * Mount context -- high-level API + */ +struct _mnt_context +{ + int action; /* MNT_ACT_{MOUNT,UMOUNT} */ + + int restricted; /* root or not? */ + + char *fstype_pattern; /* for mnt_match_fstype() */ + char *optstr_pattern; /* for mnt_match_options() */ + + char *spec; /* unresolved source OR target */ + mnt_fs *fs; /* filesystem description (type, mountpopint, device, ...) */ + + mnt_tab *fstab; /* fstab (or mtab for some remounts) entires */ + mnt_tab *mtab; /* mtab entries */ + int optsmode; /* fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE} */ + + unsigned long mountflags; /* final mount(2) flags */ + const void *mountdata; /* final mount(2) data, string or binary data */ + + unsigned long user_mountflags; /* MNT_MS_* (loop=, user=, ...) */ + + mnt_update *update; /* mtab update */ + mnt_cache *cache; /* paths cache */ + + int flags; /* private context flags */ + int ambi; /* libblkid returns ambivalent result */ + + char *helper; /* name of the used /sbin/[u]mount. helper */ + int helper_status; /* helper wait(2) status */ + + char *orig_user; /* original (non-fixed) user= option */ + + int syscall_errno; /* mount(2) or umount(2) error */ +}; + +/* flags */ +#define MNT_FL_NOMTAB (1 << 1) +#define MNT_FL_FAKE (1 << 2) +#define MNT_FL_SLOPPY (1 << 3) +#define MNT_FL_VERBOSE (1 << 4) +#define MNT_FL_NOHELPERS (1 << 5) +#define MNT_FL_LOOPDEL (1 << 6) +#define MNT_FL_LAZY (1 << 7) +#define MNT_FL_FORCE (1 << 8) +#define MNT_FL_NOCANONICALIZE (1 << 9) +#define MNT_FL_NOLOCK (1 << 10) /* don't lock mtab file */ + +#define MNT_FL_EXTERN_FS (1 << 15) /* cxt->fs is not private */ +#define MNT_FL_EXTERN_FSTAB (1 << 16) /* cxt->fstab is not private */ +#define MNT_FL_EXTERN_CACHE (1 << 17) /* cxt->cache is not private */ + +#define MNT_FL_MOUNTDATA (1 << 20) +#define MNT_FL_FSTAB_APPLIED (1 << 21) +#define MNT_FL_MOUNTFLAGS_MERGED (1 << 22) /* MS_* flags was read from optstr */ +#define MNT_FL_SAVED_USER (1 << 23) + +/* default flags */ +#define MNT_FL_DEFAULT 0 + /* optmap.c */ extern const struct mnt_optmap *mnt_optmap_get_entry(struct mnt_optmap const **maps, int nmaps, const char *name, @@ -220,4 +282,11 @@ extern int __mnt_fs_set_fstype_ptr(mnt_fs *fs, char *fstype); extern int __mnt_fs_set_optstr_ptr(mnt_fs *fs, char *optstr, int split); extern int __mnt_fs_set_optstr(mnt_fs *fs, const char *optstr, int split); +/* context.c */ +extern int mnt_context_prepare_srcpath(mnt_context *cxt); +extern int mnt_context_guess_fstype(mnt_context *cxt); +extern int mnt_context_prepare_helper(mnt_context *cxt, const char *name, const char *type); +extern int mnt_context_prepare_update(mnt_context *cxt, int act); +extern mnt_fs *mnt_context_get_fs(mnt_context *cxt); + #endif /* _LIBMOUNT_PRIVATE_H */ -- 2.39.5