#include "strutils.h"
#if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK)
-struct libmnt_optname {
- const char *name;
- size_t namesz;
-};
-
-#define DEF_OPTNAME(n) { .name = n, .namesz = sizeof(n) - 1 }
-#define DEF_OPTNAME_LAST { .name = NULL }
-
-static int is_option(const char *name, size_t namesz,
- const struct libmnt_optname *names)
+static int is_option(const char *name, const char **names)
{
- const struct libmnt_optname *p;
+ const char **p;
- for (p = names; p && p->name; p++) {
- if (p->namesz == namesz
- && strncmp(name, p->name, namesz) == 0)
+ for (p = names; p && *p; p++) {
+ if (strcmp(name, *p) == 0)
return 1;
}
-
return 0;
}
#endif /* HAVE_LIBSELINUX || HAVE_SMACK */
*/
static int fix_optstr(struct libmnt_context *cxt)
{
- int rc = 0;
+ struct libmnt_optlist *ol;
+ struct libmnt_opt *opt;
struct libmnt_ns *ns_old;
- char *next;
- char *name, *val;
- size_t namesz, valsz;
struct libmnt_fs *fs;
+ const char *val;
+ int rc = 0;
#ifdef HAVE_LIBSELINUX
int se_fix = 0, se_rem = 0;
- static const struct libmnt_optname selinux_options[] = {
- DEF_OPTNAME("context"),
- DEF_OPTNAME("fscontext"),
- DEF_OPTNAME("defcontext"),
- DEF_OPTNAME("rootcontext"),
- DEF_OPTNAME("seclabel"),
- DEF_OPTNAME_LAST
- };
-#endif
-#ifdef HAVE_SMACK
- int sm_rem = 0;
- static const struct libmnt_optname smack_options[] = {
- DEF_OPTNAME("smackfsdef"),
- DEF_OPTNAME("smackfsfloor"),
- DEF_OPTNAME("smackfshat"),
- DEF_OPTNAME("smackfsroot"),
- DEF_OPTNAME("smackfstransmute"),
- DEF_OPTNAME_LAST
- };
#endif
assert(cxt);
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
- if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
+ if (cxt->flags & MNT_FL_MOUNTOPTS_FIXED)
return 0;
- DBG(CXT, ul_debugobj(cxt, "-->: preparing mount options"));
-
fs = cxt->fs;
- DBG(CXT, ul_debugobj(cxt, " current options"
- "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'",
- fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
+ DBG(CXT, ul_debugobj(cxt, "--> preparing options"));
- /*
- * The "user" options is our business (so we can modify the option),
- * the exception is command line for /sbin/mount.<type> helpers. Let's
- * save the original user=<name> to call the helpers with an unchanged
- * "user" setting.
- */
- if (cxt->user_mountflags & MNT_MS_USER) {
- if (!mnt_optstr_get_option(fs->user_optstr,
- "user", &val, &valsz) && val) {
- cxt->orig_user = strndup(val, valsz);
- if (!cxt->orig_user) {
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -EINVAL;
+
+ ns_old = mnt_context_switch_origin_ns(cxt);
+ if (!ns_old)
+ return -MNT_ERR_NAMESPACE;
+
+ /* Fix user (convert "user" to "user=username") */
+ if (mnt_context_is_restricted(cxt)) {
+ const struct libmnt_optmap *map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+ assert(map);
+
+ opt = mnt_optlist_get_opt(ol, MNT_MS_USER, map);
+ if (opt) {
+ char *name = mnt_get_username(getuid());
+
+ if (!name)
rc = -ENOMEM;
+ else
+ rc = mnt_opt_set_value(opt, name);
+ if (rc)
goto done;
- }
}
- cxt->flags |= MNT_FL_SAVED_USER;
}
- /*
- * Sync mount options with mount flags
- */
- DBG(CXT, ul_debugobj(cxt, " fixing vfs optstr"));
- rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
- mnt_get_builtin_optmap(MNT_LINUX_MAP));
- if (rc)
- goto done;
-
- DBG(CXT, ul_debugobj(cxt, " fixing user optstr"));
- rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
- mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
- if (rc)
- goto done;
+ /* Fix UID */
+ opt = mnt_optlist_get_named(ol, "uid", NULL);
+ if (opt && (val = mnt_opt_get_value(opt)) && !isdigit_string(val)) {
+ uid_t id;
- if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
- free(fs->vfs_optstr);
- fs->vfs_optstr = NULL;
- }
- if (fs->user_optstr && *fs->user_optstr == '\0') {
- free(fs->user_optstr);
- fs->user_optstr = NULL;
+ if (strcmp(val, "useruid") == 0) /* UID of the current user */
+ id = getuid();
+ else
+ rc = mnt_get_uid(val, &id); /* UID for the username */
+ if (!rc)
+ rc = mnt_opt_set_u64value(opt, id);
+ if (rc)
+ goto done;
}
+ /* Fix GID */
+ opt = mnt_optlist_get_named(ol, "gid", NULL);
+ if (opt && (val = mnt_opt_get_value(opt)) && !isdigit_string(val)) {
+ gid_t id;
- next = fs->fs_optstr;
+ if (strcmp(val, "usergid") == 0) /* UID of the current user */
+ id = getgid();
+ else
+ rc = mnt_get_gid(val, &id); /* UID for the groupname */
+ if (!rc)
+ rc = mnt_opt_set_u64value(opt, id);
+ if (rc)
+ goto done;
+ }
+
+ if (!mnt_context_switch_ns(cxt, ns_old))
+ return -MNT_ERR_NAMESPACE;
#ifdef HAVE_LIBSELINUX
if (!is_selinux_enabled())
/* Always remove SELinux garbage if SELinux disabled */
se_rem = 1;
- else if (cxt->mountflags & MS_REMOUNT)
+ else if (mnt_optlist_is_remount(ol))
/*
* Linux kernel < 2.6.39 does not support remount operation
* with any selinux specific mount options.
/* For normal mount, contexts are translated */
se_fix = 1;
- if (!se_rem) {
- /* de-duplicate SELinux options */
- const struct libmnt_optname *p;
- for (p = selinux_options; p && p->name; p++)
- mnt_optstr_deduplicate_option(&fs->fs_optstr, p->name);
- }
-#endif
-#ifdef HAVE_SMACK
- if (access("/sys/fs/smackfs", F_OK) != 0)
- sm_rem = 1;
-#endif
- while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+ /* Fix SELinux contexts */
+ if (se_rem || se_fix) {
+ static const char *selinux_options[] = {
+ "context",
+ "fscontext",
+ "defcontext",
+ "rootcontext",
+ "seclabel",
+ NULL
+ };
+ struct libmnt_iter itr;
- if (namesz == 3 && !strncmp(name, "uid", 3))
- rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
- else if (namesz == 3 && !strncmp(name, "gid", 3))
- rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
-#ifdef HAVE_LIBSELINUX
- else if ((se_rem || se_fix)
- && is_option(name, namesz, selinux_options)) {
-
- if (se_rem) {
- /* remove context= option */
- next = name;
- rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
- name,
- val ? val + valsz :
- name + namesz);
- } else if (se_fix && val && valsz)
- /* translate selinux contexts */
- rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
- val, valsz, &next);
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ if (!is_option(mnt_opt_get_name(opt), selinux_options))
+ continue;
+ if (se_rem)
+ rc = mnt_optlist_remove_opt(ol, opt);
+ else if (se_fix && mnt_opt_has_value(opt)) {
+ const char *val = mnt_opt_get_value(opt);
+ char *raw = NULL;
+
+ rc = selinux_trans_to_raw_context(val, &raw);
+ if (rc == -1 || !raw)
+ rc = -EINVAL;
+ if (!rc)
+ rc = mnt_opt_set_quoted_value(opt, raw);
+ if (raw)
+ freecon(raw);
+ }
+ if (rc)
+ goto done;
}
+ }
#endif
+
#ifdef HAVE_SMACK
- else if (sm_rem && is_option(name, namesz, smack_options)) {
+ /* Fix Smack */
+ if (access("/sys/fs/smackfs", F_OK) != 0) {
+ struct libmnt_iter itr;
- next = name;
- rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
- name,
- val ? val + valsz : name + namesz);
+ static const char *smack_options[] = {
+ "smackfsdef",
+ "smackfsfloor",
+ "smackfshat",
+ "smackfsroot",
+ "smackfstransmute",
+ NULL,
+ };
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ if (!is_option(mnt_opt_get_name(opt), smack_options))
+ continue;
+ rc = mnt_optlist_remove_opt(ol, opt);
+ if (rc)
+ goto done;
}
-#endif
- if (rc)
- goto done;
}
+#endif
+ mnt_context_call_hooks(cxt, MNT_STAGE_PREP_OPTIONS);
- if (!rc && mnt_context_is_restricted(cxt) && (cxt->user_mountflags & MNT_MS_USER)) {
- ns_old = mnt_context_switch_origin_ns(cxt);
- if (!ns_old)
- return -MNT_ERR_NAMESPACE;
+ /* For backward compatinility update context fs mount options */
+ if (fs) {
+ const struct libmnt_optmap *map;
+ const char *p;
- rc = mnt_optstr_fix_user(&fs->user_optstr);
+ /* All options */
+ mnt_optlist_get_optstr(ol, &p, NULL, 0);
+ strdup_to_struct_member(fs, optstr, p);
- if (!mnt_context_switch_ns(cxt, ns_old))
- return -MNT_ERR_NAMESPACE;
- }
+ /* FS options */
+ mnt_optlist_get_optstr(ol, &p, NULL, MNT_OPTLIST_UNKNOWN);
+ strdup_to_struct_member(fs, fs_optstr, p);
- mnt_context_call_hooks(cxt, MNT_STAGE_PREP_OPTIONS);
+ /* VFS options */
+ map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+ mnt_optlist_get_optstr(ol, &p, map, 0);
+ strdup_to_struct_member(fs, vfs_optstr, p);
+
+ /* Userspace options */
+ map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+ mnt_optlist_get_optstr(ol, &p, map, 0);
+ strdup_to_struct_member(fs, user_optstr, p);
+
+ DBG(CXT, ul_debugobj(cxt, " fixed options: "
+ "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'",
+ fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
+ }
- /* refresh merged optstr */
- free(fs->optstr);
- fs->optstr = NULL;
- fs->optstr = mnt_fs_strdup_options(fs);
done:
+ DBG(CXT, ul_debugobj(cxt, "<-- preparing options done [rc=%d]", rc));
cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
- DBG(CXT, ul_debugobj(cxt, " fixed options [rc=%d]: "
- "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
- fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
-
if (rc)
rc = -MNT_ERR_MOUNTOPT;
return rc;
assert(user_map);
/* get userspace mount flags (user[=<name>] etc.*/
- rc = mnt_optlist_get_flags(ol, &user_flags, user_map);
+ rc = mnt_optlist_get_flags(ol, &user_flags, user_map, 0);
if (rc)
return rc;
&& mnt_opt_has_value(opt)) {
DBG(CXT, ul_debugobj(cxt, "perms: user=<name> detected, ignore"));
+ cxt->flags |= MNT_FL_SAVED_USER;
+
mnt_opt_set_external(opt, 1);
user_flags &= ~MNT_MS_USER;
}
int rc;
cxt = mnt_new_context();
-
if (!cxt)
return -ENOMEM;
cxt->restricted = 1; /* emulate suid mount(8) */
mnt_context_get_fs(cxt); /* due to assert() in evaluate_permissions() */
- if (argc != 2) {
+ if (argc < 2) {
warn("missing fstab options");
return -EPERM;
}
+ if (argc == 3 && strcmp(argv[2], "--root") == 0)
+ cxt->restricted = 0;
ls = mnt_context_get_optlist(cxt);
if (!ls)
return 0;
}
+static int test_fixopts(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_context *cxt;
+ struct libmnt_optlist *ls;
+ const char *p;
+ int rc;
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ return -ENOMEM;
+
+ cxt->restricted = 1; /* emulate suid mount(8) */
+ mnt_context_get_fs(cxt); /* to fill fs->*_optstr */
+
+ if (argc < 2) {
+ warn("missing fstab options");
+ return -EPERM;
+ }
+ if (argc == 3 && strcmp(argv[2], "--root") == 0)
+ cxt->restricted = 0;
+
+ ls = mnt_context_get_optlist(cxt);
+ if (!ls)
+ return -ENOMEM;
+ rc = mnt_optlist_set_optstr(ls, argv[1], NULL);
+ if (rc) {
+ warn("cannot apply fstab options");
+ return rc;
+ }
+ cxt->flags |= MNT_FL_TAB_APPLIED; /* emulate mnt_context_apply_fstab() */
+
+ mnt_context_merge_mflags(cxt);
+
+ rc = evaluate_permissions(cxt);
+ if (rc) {
+ warn("evaluate permission failed [rc=%d]", rc);
+ return rc;
+ }
+ rc = fix_optstr(cxt);
+ if (rc) {
+ warn("fix options failed [rc=%d]", rc);
+ return rc;
+ }
+ mnt_optlist_get_optstr(ls, &p, NULL, 0);
+ printf("options (dfl): '%s'\n", p);
+
+ mnt_optlist_get_optstr(ls, &p, NULL, MNT_OPTLIST_EXTERNAL);
+ printf("options (ex.): '%s'\n", p);
+
+ mnt_free_context(cxt);
+ return 0;
+}
+
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
- { "--perms", test_perms, "<fstab-options>" },
+ { "--perms", test_perms, "<fstab-options> [--root]" },
+ { "--fix-options", test_fixopts, "<fstab-options> [--root]" },
+
{ NULL }};
return mnt_run_test(tss, argc, argv);