/*
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_LIBSELINUX
#include <selinux/selinux.h>
#include <selinux/context.h>
#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 */
};
/* 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_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 */
/* 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:
*
* Returns: newly allocated mount context
*/
mnt_context *mnt_new_context()
{
mnt_context *cxt;
uid_t ruid, euid;
cxt = calloc(1, sizeof(*cxt));
if (!cxt)
return NULL;
ruid = getuid();
euid = geteuid();
/* 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]" : ""));
return cxt;
}
/**
* mnt_free_context:
* @cxt: mount context
*
* Deallocates context struct.
*/
void mnt_free_context(mnt_context *cxt)
{
if (!cxt)
return;
DBG(CXT, mnt_debug_h(cxt, "free"));
free(cxt->fstype_pattern);
free(cxt->optstr_pattern);
free(cxt->spec);
if (!(cxt->flags & MNT_FL_EXTERN_FS))
mnt_free_fs(cxt->fs);
if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
mnt_free_tab(cxt->fstab);
if (!(cxt->flags & MNT_FL_EXTERN_CACHE))
mnt_free_cache(cxt->cache);
mnt_free_update(cxt->update);
mnt_free_tab(cxt->mtab);
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_optstr_pattern(cxt, NULL);
*
* to reset these stuff.
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_reset_context(mnt_context *cxt)
{
int fl;
if (!cxt)
return -EINVAL;
fl = cxt->flags;
if (cxt->update)
mnt_update_set_fs(cxt->update, NULL);
if (!(cxt->flags & MNT_FL_EXTERN_FS))
mnt_free_fs(cxt->fs);
mnt_free_tab(cxt->mtab);
cxt->fs = NULL;
cxt->mtab = NULL;
free(cxt->spec);
cxt->spec = NULL;
cxt->mountflags = 0;
cxt->user_mountflags = 0;
cxt->mountdata = NULL;
cxt->flags = MNT_FL_DEFAULT;
/* restore non-resetable flags */
cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB);
cxt->flags |= (fl & MNT_FL_EXTERN_CACHE);
return 0;
}
static int mnt_context_set_flag(mnt_context *cxt, int flag, int enable)
{
if (!cxt)
return -EINVAL;
if (enable)
cxt->flags |= flag;
else
cxt->flags &= ~flag;
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
*
* Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts
*/
int mnt_context_is_restricted(mnt_context *cxt)
{
assert(cxt);
return cxt->restricted;
}
/**
* mnt_context_set_optsmode
* @cxt: mount context
* @mode: MNT_OPTSMODE_{AUTO,FORCE,IGNORE,MTABFORCE}
*
* Defines a mode how libmount uses fstab mount options:
*
* auto - use options from fstab if source or target are not
* defined (this is mount(8) default).
*
* - For remount operation it reads options from mtab if
* the target is not found in fstab.
*
* ignore - never use mount options from fstab
*
* force - always use mount options from fstab
*
* mtab-force - for remount operation always use options from mtab (mountinfo)
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_optsmode(mnt_context *cxt, int mode)
{
if (!cxt)
return -EINVAL;
cxt->optsmode = mode;
return 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 fstab instance only (see
* mnt_context_set_fstab()). If you want to use an external fstab then you need
* manage your private mnt_cache (see mnt_tab_set_cache(fstab, NULL).
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_disable_canonicalize(mnt_context *cxt, int disable)
{
return mnt_context_set_flag(cxt, MNT_FL_NOCANONICALIZE, disable);
}
/**
* 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(mnt_context *cxt, int enable)
{
return mnt_context_set_flag(cxt, MNT_FL_LAZY, enable);
}
/**
* 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(mnt_context *cxt, int disable)
{
return mnt_context_set_flag(cxt, MNT_FL_NOHELPERS, disable);
}
/**
* 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(mnt_context *cxt, int enable)
{
return mnt_context_set_flag(cxt, MNT_FL_SLOPPY, enable);
}
/**
* 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(mnt_context *cxt, int enable)
{
return mnt_context_set_flag(cxt, MNT_FL_FAKE, enable);
}
/**
* 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_nomtab(mnt_context *cxt, int disable)
{
return mnt_context_set_flag(cxt, MNT_FL_NOMTAB, disable);
}
/**
* 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(mnt_context *cxt, int enable)
{
return mnt_context_set_flag(cxt, MNT_FL_FORCE, enable);
}
/**
* mnt_context_enable_verbose:
* @cxt: mount context
* @enable: TRUE or FALSE
*
* Enable/disable verbose output.
*
* 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);
}
/**
* 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(mnt_context *cxt, int enable)
{
return mnt_context_set_flag(cxt, MNT_FL_LOOPDEL, enable);
}
/**
* mnt_context_set_spec:
* @cxt: mount context
* @spec: unresolved source (device, label, uuid, ...) or target (mountpoint)
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_spec(mnt_context *cxt, const char *spec)
{
char *p;
if (!cxt)
return -EINVAL;
p = strdup(spec);
if (!p)
return -ENOMEM;
free(cxt->spec);
cxt->spec = p;
return 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,optstr,fstype}
* functions, Ft 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(mnt_context *cxt, mnt_fs *fs)
{
if (!cxt)
return -EINVAL;
if (!(cxt->flags & MNT_FL_EXTERN_FS))
mnt_free_fs(cxt->fs);
mnt_context_set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL);
cxt->fs = fs;
return 0;
}
/**
* 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(mnt_context *cxt, const char *source)
{
return mnt_fs_set_source(mnt_context_get_fs(cxt), source);
}
/**
* mnt_context_set_target:
* @cxt: mount context
* @target: mountpoint
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_target(mnt_context *cxt, const char *target)
{
return mnt_fs_set_target(mnt_context_get_fs(cxt), target);
}
/**
* 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 "no<fs>" notation use mnt_context_set_fstype_pattern().
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_fstype(mnt_context *cxt, const char *fstype)
{
return mnt_fs_set_fstype(mnt_context_get_fs(cxt), fstype);
}
/**
* mnt_context_set_optstr:
* @cxt: mount context
* @optstr: comma delimited mount options
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_optstr(mnt_context *cxt, const char *optstr)
{
return mnt_fs_set_optstr(mnt_context_get_fs(cxt), optstr);
}
/**
* mnt_context_append_optstr:
* @cxt: mount context
* @optstr: comma delimited mount options
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_append_optstr(mnt_context *cxt, const char *optstr)
{
return mnt_fs_append_optstr(mnt_context_get_fs(cxt), optstr);
}
/**
* 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(mnt_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_optstr_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_optstr_pattern(mnt_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 mnt_tab 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(mnt_context *cxt, mnt_tab *tb)
{
if (!cxt)
return -EINVAL;
if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
mnt_free_tab(cxt->fstab);
mnt_context_set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL);
cxt->fstab = tb;
return 0;
}
/**
* mnt_context_set_cache:
* @cxt: mount context
* @cache: cache instance or nULL
*
* The mount context maintains a private mnt_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(mnt_context *cxt, mnt_cache *cache)
{
if (!cxt)
return -EINVAL;
if (!(cxt->flags & MNT_FL_EXTERN_CACHE))
mnt_free_cache(cxt->cache);
mnt_context_set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL);
cxt->cache = cache;
return 0;
}
static mnt_cache *mnt_context_get_cache(mnt_context *cxt)
{
if (!cxt || (cxt->flags & MNT_FL_NOCANONICALIZE))
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_mountflags:
* @cxt: mount context
* @flags: mount(2) flags (MS_* flags)
*
* Note that mount context allows to define mount options by mount flags. It
* means you can for example use
*
* mnt_context_set_mountflags(cxt, MS_NOEXEC | MS_NOSUID);
*
* rather than
*
* mnt_context_set_optstr(cxt, "noexec,nosuid");
*
* these both calls have the same effect.
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_mountflags(mnt_context *cxt, unsigned long flags)
{
if (!cxt)
return -EINVAL;
cxt->mountflags = flags;
return 0;
}
/**
* mnt_context_get_mountflags:
* @cxt: mount context
* @flags: returns mount flags
*
* Converts mount options string to MS_* flags and bitewise-OR the result with
* already defined flags (see mnt_context_set_mountflags()).
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_get_mountflags(mnt_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_optstr(cxt->fs);
if (o)
rc = mnt_optstr_get_mountflags(o, flags);
}
if (!rc)
*flags |= cxt->mountflags;
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
* @flags: mount(2) flags (MNT_MS_* flags, e.g. MNT_MS_LOOP)
*
* See also notest for mnt_context_set_mountflags().
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_set_userspace_mountflags(mnt_context *cxt, unsigned long flags)
{
if (!cxt)
return -EINVAL;
cxt->user_mountflags = flags;
return 0;
}
/**
* mnt_context_get_userspace_mountflags:
* @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_userspace_mountflags()).
*
* Returns: 0 on success, negative number in case of error.
*/
int mnt_context_get_userspace_mountflags(mnt_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_optstr(cxt->fs);
if (o)
rc = mnt_optstr_get_userspace_mountflags(o, flags);
}
if (!rc)
*flags |= cxt->user_mountflags;
return rc;
}
static int mnt_context_is_loop(mnt_context *cxt)
{
unsigned long fl = 0;
if (cxt->user_mountflags & MNT_MS_LOOP)
return 1;
if (!mnt_context_get_mountflags(cxt, &fl) && (fl & MNT_MS_LOOP))
return 1;
/* TODO:
* - support MNT_MS_{OFFSET,SIZELIMIT,ENCRYPTION}
*/
return 0;
}
/**
* 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(mnt_context *cxt, void *data)
{
if (!cxt)
return -EINVAL;
cxt->mountdata = data;
cxt->flags |= MNT_FL_MOUNTDATA;
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;
/*
* 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:
DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: '%s'", rc, *optstr));
return rc;
}
/*
* this has to be called before mnt_context_fix_optstr()
*/
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)
{
const char *path;
mnt_cache *cache;
const char *t, *v, *src;
int rc = 0;
if (!cxt || !cxt->fs)
return -EINVAL;
src = mnt_fs_get_source(cxt->fs);
/* ignore filesystems without a real source */
if (!src || (cxt->fs->flags & (MNT_FS_PSEUDO | MNT_FS_NET)))
return 0;
DBG(CXT, mnt_debug_h(cxt, "preparing 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) : -EINVAL;
} else {
/*
* Source is PATH (canonicalize)
*/
if (cache) {
path = mnt_resolve_path(src, cache);
if (strcmp(path, src))
rc = mnt_fs_set_source(cxt->fs, path);
} else
path = src;
}
if (rc) {
DBG(CXT, mnt_debug_h(cxt, "failed to prepare srcpath"));
return rc;
}
/*
* Initialize loop device
*/
if (mnt_context_is_loop(cxt) &&
!(cxt->mountflags & (MS_BIND | MS_MOVE |
MS_PROPAGATION | MS_REMOUNT))) {
; /* TODO */
}
DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'", path));
return 0;
}
static int mnt_context_detect_fstype(mnt_context *cxt)
{
return 0; /* TODO */
}
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;
}
/**
* 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)
goto err;
rc = mnt_context_merge_mountflags(cxt);
if (rc)
return rc;
rc = mnt_context_evaluate_permissions(cxt);
if (rc)
goto err;
rc = mnt_context_fix_optstr(cxt);
if (rc)
goto err;
rc = mnt_context_prepare_srcpath(cxt);
if (rc)
goto err;
rc = mnt_context_detect_fstype(cxt);
if (rc)
goto err;
/* TODO: prepare mtab update */
/* TODO: replace generic optstr with fs_optstr */
DBG(CXT, mnt_debug_h(cxt, "sucessfully prepared"));
return 0;
err:
DBG(CXT, mnt_debug_h(cxt, "prepare failed"));
return rc;
}
int mnt_context_mount_fs(mnt_context *cxt)
{
if (!cxt)
return -EINVAL;
DBG(CXT, mnt_debug_h(cxt, "mounting:"));
DBG(CXT, mnt_fs_print_debug(cxt->fs, stderr));
DBG(CXT, mnt_debug_h(cxt, "mountflags: 0x%lx", cxt->mountflags));
return 0;
}
#ifdef TEST_PROGRAM
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 <spec> */
mnt_context_set_spec(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++]);
}
rc = mnt_context_prepare_mount(cxt);
if (rc)
fprintf(stderr, "failed to prepare mount\n");
else {
rc = mnt_context_mount_fs(cxt);
if (rc)
fprintf(stderr, "failed to mount\n");
}
mnt_free_context(cxt);
return rc;
}
int main(int argc, char *argv[])
{
struct mtest tss[] = {
{ "--mount", test_mount, "[-o <opts>] [-t <type>] <spec> | <src> <target>" },
{ NULL }
};
return mnt_run_test(tss, argc, argv);
}
#endif /* TEST_PROGRAM */