]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: (mount) use optlist from options processing
authorKarel Zak <kzak@redhat.com>
Thu, 18 Aug 2022 07:51:57 +0000 (09:51 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 3 Jan 2023 11:58:42 +0000 (12:58 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/context.c
libmount/src/context_mount.c

index 8115ffc18fc69e238813cbbefaeaededce557e56..c1040021819aab47ae2d87784f14006b96f3724f 100644 (file)
@@ -1175,7 +1175,7 @@ const char *mnt_context_get_options(struct libmnt_context *cxt)
        if (!ls)
                return NULL;
 
-       mnt_optlist_get_optstr(ls, &str, NULL);
+       mnt_optlist_get_optstr(ls, &str, NULL, 0);
        return str;
 }
 
@@ -1709,7 +1709,7 @@ int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
                return -ENOMEM;
 
        return mnt_optlist_get_flags(ls, flags,
-                               mnt_get_builtin_optmap(MNT_LINUX_MAP));
+                               mnt_get_builtin_optmap(MNT_LINUX_MAP), 0);
 }
 
 /**
@@ -1752,7 +1752,7 @@ int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags
                return -ENOMEM;
 
        return mnt_optlist_get_flags(ls, flags,
-                               mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+                               mnt_get_builtin_optmap(MNT_USERSPACE_MAP), 0);
 }
 
 /**
index 7772d39e056bd6e18c5dbb448acb2257c95c7b62..1275862a2684077c06021edda5a1e3c864341579 100644 (file)
 #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 */
@@ -57,98 +46,89 @@ static int is_option(const char *name, size_t namesz,
  */
 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.
@@ -163,77 +143,101 @@ static int fix_optstr(struct libmnt_context *cxt)
                /* 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;
@@ -345,7 +349,7 @@ static int evaluate_permissions(struct libmnt_context *cxt)
        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;
 
@@ -383,6 +387,8 @@ static int evaluate_permissions(struct libmnt_context *cxt)
                    && 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;
                }
@@ -1874,17 +1880,18 @@ static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
        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)
@@ -1909,10 +1916,65 @@ static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
        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);