]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add libmnt_optlist
authorKarel Zak <kzak@redhat.com>
Fri, 24 Jun 2022 16:15:25 +0000 (18:15 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 3 Jan 2023 11:53:12 +0000 (12:53 +0100)
Now libmount internally keeps mount options as strings or flags. This
commit adds a new container to keep parsed mount options in memory.
We need:

 * the same mount options could be mapped to the different mount options
   maps (for example "nosuid" to classic MS_NOSUID or new MOUNT_ATTR_NOSUID)

 * easy to sync flags and strings (e.g. conversion from MS_NOSUID to
   "nosuid" and vice-versa).

 * double linked list is easy to use in while() when browse all options

 * easy to filter options by specific map (MS_*, userspace, etc.)

 * don't hardcode options maps in options parser, support 3th-party
   maps (for example for extensions like dm-verity, etc.)

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/Makemodule.am
libmount/src/mountP.h
libmount/src/optlist.c [new file with mode: 0644]

index 2881ae576292c2f0560db22f9327fae99eda0cbf..7a220f8fd1b5b47dc487db87f0dca558754273a8 100644 (file)
@@ -16,6 +16,7 @@ libmount_la_SOURCES = \
        libmount/src/iter.c \
        libmount/src/lock.c \
        libmount/src/optmap.c \
+       libmount/src/optlist.c \
        libmount/src/optstr.c \
        libmount/src/tab.c \
        libmount/src/tab_diff.c \
index 49bcdd0b7288cc8ab710d2251fb4796392d1ee82..0fe7bd4adfc850a2ae663ddd514e1415f5108e5c 100644 (file)
@@ -49,6 +49,7 @@
 #define MNT_DEBUG_LOOP         (1 << 13)
 #define MNT_DEBUG_VERITY       (1 << 14)
 #define MNT_DEBUG_HOOK         (1 << 15)
+#define MNT_DEBUG_OPTLIST      (1 << 16)
 
 #define MNT_DEBUG_ALL          0xFFFF
 
@@ -475,6 +476,44 @@ extern int mnt_buffer_append_option(struct ul_buffer *buf,
                         const char *name, size_t namesz,
                         const char *val, size_t valsz);
 
+/* optlist.h */
+struct libmnt_opt;
+struct libmnt_optlist;
+
+extern struct libmnt_optlist *mnt_new_optlist(void);
+extern void mnt_ref_optlist(struct libmnt_optlist *ls);
+extern void mnt_unref_optlist(struct libmnt_optlist *ls);
+extern int mnt_optlist_register_map(struct libmnt_optlist *ls, const struct libmnt_optmap *map);
+extern int mnt_optlist_remove_opt(struct libmnt_optlist *ls, struct libmnt_opt *opt);
+extern int mnt_optlist_next_opt(struct libmnt_optlist *ls,
+                        struct libmnt_iter *itr, struct libmnt_opt **opt);
+extern struct libmnt_opt *mnt_optlist_get_opt(struct libmnt_optlist *ls,
+                        unsigned long id, const struct libmnt_optmap *map);
+extern struct libmnt_opt *mnt_optlist_get_named(struct libmnt_optlist *ls,
+                          char *name, const struct libmnt_optmap *map);
+extern int mnt_optlist_set_optstr(struct libmnt_optlist *ls, const char *optstr,
+                          const struct libmnt_optmap *map);
+extern int mnt_optlist_append_optstr(struct libmnt_optlist *ls, const char *optstr,
+                        const struct libmnt_optmap *map);
+extern int mnt_optlist_prepend_optstr(struct libmnt_optlist *ls, const char *optstr,
+                        const struct libmnt_optmap *map);
+extern int mnt_optlist_append_flags(struct libmnt_optlist *ls, unsigned long flags,
+                          const struct libmnt_optmap *map);
+extern int mnt_optlist_set_flags(struct libmnt_optlist *ls, unsigned long flags,
+                          const struct libmnt_optmap *map);
+extern int mnt_optlist_insert_flags(struct libmnt_optlist *ls, unsigned long flags,
+                        const struct libmnt_optmap *map,
+                        unsigned long after,
+                        const struct libmnt_optmap *after_map);
+extern int mnt_optlist_get_flags(struct libmnt_optlist *ls, unsigned long *flags,
+                          const struct libmnt_optmap *map);
+extern int mnt_optlist_get_optstr(struct libmnt_optlist *ol, char **optstr,
+                        const struct libmnt_optmap *map);
+extern int mnt_optlist_get_propagation(struct libmnt_optlist *ls);
+extern int mnt_optlist_is_propagation_only(struct libmnt_optlist *ls);
+extern int mnt_opt_has_value(struct libmnt_opt *opt);
+
+
 /* fs.c */
 extern struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs);
 extern int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c
new file mode 100644 (file)
index 0000000..dd34c89
--- /dev/null
@@ -0,0 +1,478 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * The "optlist" is container for parsed mount options.
+ *
+ */
+#include "strutils.h"
+#include "list.h"
+#include "mountP.h"
+
+#define MNT_OPTLIST_MAXMAPS    64
+
+enum libmnt_optsrc {
+       MNT_OPTSRC_STRING,
+       MNT_OPTSRC_FLAG
+};
+
+struct libmnt_opt {
+       char *name;
+       char *value;
+
+       struct list_head opts;  /* libmnt_optlist->opts member */
+
+       const struct libmnt_optmap      *map;
+       const struct libmnt_optmap      *ent;   /* map entry */
+
+       enum libmnt_optsrc      src;
+
+       unsigned int external : 1;      /* visible for external helpers only */
+};
+
+struct libmnt_optlist {
+       int refcount;
+
+       const struct libmnt_optmap      *linux_map;     /* map with MS_ flags */
+       const struct libmnt_optmap      *maps[MNT_OPTLIST_MAXMAPS];
+       size_t nmaps;
+
+       unsigned long           propagation;    /* propagation MS_ flags */
+       struct list_head        opts;           /* parsed options */
+};
+
+struct libmnt_optlist *mnt_new_optlist(void)
+{
+       struct libmnt_optlist *ls = calloc(1, sizeof(*ls));
+       if (ls)
+               return NULL;
+
+       ls->refcount = 1;
+       INIT_LIST_HEAD(&ls->opts);
+
+       ls->linux_map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+
+       DBG(OPTLIST, ul_debugobj(ls, "alloc"));
+       return ls;
+}
+
+void mnt_ref_optlist(struct libmnt_optlist *ls)
+{
+       if (ls) {
+               ls->refcount++;
+               /*DBG(OPTLIST, ul_debugobj(ls, "ref=%d", ls->refcount));*/
+       }
+}
+
+void mnt_unref_optlist(struct libmnt_optlist *ls)
+{
+       if (!ls)
+               return;
+
+       ls->refcount--;
+       if (ls->refcount > 0)
+               return;
+
+       /*DBG(OPTLIST, ul_debugobj(ls, "unref=%d", ls->refcount));*/
+       while (!list_empty(&ls->opts)) {
+               struct libmnt_opt *opt = list_entry(ls->opts.next, struct libmnt_opt, opts);
+               mnt_optlist_remove_opt(ls, opt);
+       }
+
+       free(ls);
+}
+
+int mnt_optlist_register_map(struct libmnt_optlist *ls, const struct libmnt_optmap *map)
+{
+       size_t i;
+
+       if (!ls || !map)
+               return -EINVAL;
+
+       for (i = 0; i < ls->nmaps; i++) {
+               if (ls->maps[i] == map)
+                       return 0;               /* already registred, ignore */
+       }
+       if (ls->nmaps + 1 >= MNT_OPTLIST_MAXMAPS)
+               return -ERANGE;
+
+       DBG(OPTLIST, ul_debugobj(ls, "registr map %p", map));
+       ls->maps[ls->nmaps++] = map;
+       return 0;
+}
+
+int mnt_optlist_remove_opt(struct libmnt_optlist *ls, struct libmnt_opt *opt)
+{
+       if (!opt)
+               return -EINVAL;
+
+       if (opt->map && opt->ent
+           && opt->map == ls->linux_map && opt->ent->id & MS_PROPAGATION)
+               ls->propagation &= ~opt->ent->id;
+
+       list_del_init(&opt->opts);
+       free(opt->value);
+       free(opt->name);
+       free(opt);
+
+       return 0;
+}
+
+int mnt_optlist_next_opt(struct libmnt_optlist *ls,
+                       struct libmnt_iter *itr, struct libmnt_opt **opt)
+{
+       int rc = 1;
+
+       if (!ls || !itr || !opt)
+               return -EINVAL;
+       *opt = NULL;
+
+       if (!itr->head)
+               MNT_ITER_INIT(itr, &ls->opts);
+       if (itr->p != itr->head) {
+               MNT_ITER_ITERATE(itr, *opt, struct libmnt_opt, opts);
+               rc = 0;
+       }
+
+       return rc;
+}
+
+struct libmnt_opt *mnt_optlist_get_opt(struct libmnt_optlist *ls,
+                       unsigned long id, const struct libmnt_optmap *map)
+{
+       struct libmnt_iter itr;
+       struct libmnt_opt *opt;
+
+       if (!ls || !map)
+               return NULL;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
+               if (!opt->external && opt->map == map
+                   && opt->ent && opt->ent->id == (int) id)
+                       return opt;
+       }
+
+       return NULL;
+}
+
+struct libmnt_opt *mnt_optlist_get_named(struct libmnt_optlist *ls,
+                       char *name, const struct libmnt_optmap *map)
+{
+       struct libmnt_iter itr;
+       struct libmnt_opt *opt;
+
+       if (!ls || !name)
+               return NULL;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
+               if (!opt->external)
+                       continue;
+               if (map && map != opt->map)
+                       continue;
+               if (opt->name && strcmp(opt->name, name) == 0)
+                       return opt;
+       }
+
+       return NULL;
+}
+
+static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
+                       const char *name, size_t namesz,
+                       const char *value, size_t valsz,
+                       const struct libmnt_optmap *map,
+                       const struct libmnt_optmap *ent,
+                       struct list_head *where)
+
+{
+       struct libmnt_opt *opt;
+
+       opt = calloc(1, sizeof(*opt));
+       if (!opt)
+               return NULL;
+
+       INIT_LIST_HEAD(&opt->opts);
+       opt->map = map;
+       opt->ent = ent;
+
+       if (valsz) {
+               opt->value = strndup(value, valsz);
+               if (!opt->value)
+                       goto fail;
+       }
+       if (namesz) {
+               opt->name = strndup(name, namesz);
+               if (!opt->name)
+                       goto fail;
+       }
+
+       if (where)
+               list_add(&opt->opts, where);
+       else
+               list_add_tail(&opt->opts, &ls->opts);
+
+       if (map && ent && map == ls->linux_map && ent->id & MS_PROPAGATION)
+               ls->propagation |= ent->id;
+
+       DBG(OPTLIST, ul_debugobj(ls, " added %s", opt->name));
+       return opt;
+fail:
+       mnt_optlist_remove_opt(ls, opt);
+       return NULL;
+}
+
+static int optlist_add_optstr(struct libmnt_optlist *ls, const char *optstr,
+                       const struct libmnt_optmap *map, struct list_head *where)
+{
+       char *p = (char *) optstr, *name, *val;
+       size_t namesz, valsz;
+       int rc;
+
+       if (!ls || !optstr)
+               return -EINVAL;
+       if (map && (rc =  mnt_optlist_register_map(ls, map)))
+               return rc;
+
+       while (ul_optstr_next(&p, &name, &namesz, &val, &valsz) == 0) {
+
+               struct libmnt_opt *opt;
+               const struct libmnt_optmap *e = NULL, *m = NULL;
+
+               if (map)
+                       m = mnt_optmap_get_entry(&map, 1, name, namesz, &e);
+               if (!m && ls->nmaps)
+                       m = mnt_optmap_get_entry(ls->maps, ls->nmaps, name, namesz, &e);
+
+               /* TODO: add the option more than once if belongs to the more maps */
+
+               opt = optlist_new_opt(ls, name, namesz, val, valsz, m, e, where);
+               if (!opt)
+                       return -ENOMEM;
+               opt->src = MNT_OPTSRC_STRING;
+       }
+
+       return 0;
+}
+
+/* replaces all existing options with options from @optstr */
+int mnt_optlist_set_optstr(struct libmnt_optlist *ls, const char *optstr,
+                         const struct libmnt_optmap *map)
+{
+       struct list_head *p, *next;
+
+       DBG(OPTLIST, ul_debugobj(ls, "set %s", optstr));
+
+       /* remove all previous options set by optstr */
+       list_for_each_safe(p, next, &ls->opts) {
+               struct libmnt_opt *opt = list_entry(p, struct libmnt_opt, opts);
+
+               if (opt->external)
+                       continue;
+               if (opt->src == MNT_OPTSRC_STRING)
+                       mnt_optlist_remove_opt(ls, opt);
+       }
+
+       return optlist_add_optstr(ls, optstr, map, NULL);
+}
+
+int mnt_optlist_append_optstr(struct libmnt_optlist *ls, const char *optstr,
+                       const struct libmnt_optmap *map)
+{
+       DBG(OPTLIST, ul_debugobj(ls, "append %s", optstr));
+       return optlist_add_optstr(ls, optstr, map, NULL);
+}
+
+int mnt_optlist_prepend_optstr(struct libmnt_optlist *ls, const char *optstr,
+                       const struct libmnt_optmap *map)
+{
+       DBG(OPTLIST, ul_debugobj(ls, "prepend %s", optstr));
+       return optlist_add_optstr(ls, optstr, map, &ls->opts);
+}
+
+static int optlist_add_flags(struct libmnt_optlist *ls, unsigned long flags,
+                       const struct libmnt_optmap *map, struct list_head *where)
+{
+       const struct libmnt_optmap *ent;
+       int rc;
+
+       if (!ls || !map)
+               return -EINVAL;
+
+       if (map && (rc =  mnt_optlist_register_map(ls, map)))
+               return rc;
+
+       for (ent = map; ent && ent->name; ent++) {
+
+               char *p;
+               size_t sz;
+               struct libmnt_opt *opt;
+
+               if ((ent->mask & MNT_INVERT)
+                   || ent->id == 0
+                   || (flags & ent->id) != (unsigned long) ent->id)
+                       continue;
+
+               /* don't add options which require values (e.g. offset=%d) */
+               p = strchr(ent->name, '=');
+               if (p) {
+                       if (p > ent->name && *(p - 1) == '[')
+                               p--;             /* name[=] */
+                       else
+                               continue;       /* name=<value> */
+                       sz = p - ent->name;
+               } else
+                       sz = strlen(ent->name); /* alone "name" */
+
+               opt = optlist_new_opt(ls, p, sz, NULL, 0, map, ent, where);
+               if (!opt)
+                       return -ENOMEM;
+               opt->src = MNT_OPTSRC_FLAG;
+       }
+
+       return 0;
+}
+
+int mnt_optlist_append_flags(struct libmnt_optlist *ls, unsigned long flags,
+                         const struct libmnt_optmap *map)
+{
+       DBG(OPTLIST, ul_debugobj(ls, "append %lx", flags));
+       return optlist_add_flags(ls, flags, map, NULL);
+}
+
+
+int mnt_optlist_set_flags(struct libmnt_optlist *ls, unsigned long flags,
+                         const struct libmnt_optmap *map)
+{
+       struct list_head *p, *next;
+
+       if (!ls || !map)
+               return -EINVAL;
+
+       DBG(OPTLIST, ul_debugobj(ls, "set %lx", flags));
+
+       /* remove all previous options defined by flag */
+       list_for_each_safe(p, next, &ls->opts) {
+               struct libmnt_opt *opt = list_entry(p, struct libmnt_opt, opts);
+
+               if (opt->external)
+                       continue;
+               if (opt->src == MNT_OPTSRC_FLAG)
+                       mnt_optlist_remove_opt(ls, opt);
+       }
+
+       return mnt_optlist_append_flags(ls, flags, map);
+}
+
+int mnt_optlist_insert_flags(struct libmnt_optlist *ls, unsigned long flags,
+                       const struct libmnt_optmap *map,
+                       unsigned long after,
+                       const struct libmnt_optmap *after_map)
+{
+       struct libmnt_opt *opt;
+
+       if (!ls || !map || !after || !after_map)
+               return -EINVAL;
+
+       DBG(OPTLIST, ul_debugobj(ls, "insert %lx (after %s)",
+                               flags, opt->ent ? opt->ent->name : "???"));
+
+       opt = mnt_optlist_get_opt(ls, after, after_map);
+       return optlist_add_flags(ls, flags, map, &opt->opts);
+}
+
+/* like mnt_optstr_get_flags() */
+int mnt_optlist_get_flags(struct libmnt_optlist *ls, unsigned long *flags,
+                         const struct libmnt_optmap *map)
+{
+       struct libmnt_iter itr;
+       struct libmnt_opt *opt;
+
+       if (!ls || !map)
+               return -EINVAL;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
+               if (opt->map != map || !opt->ent || !opt->ent->id)
+                       continue;
+               if (opt->external)
+                       continue;
+               if (opt->ent->mask & MNT_INVERT)
+                       *flags &= ~opt->ent->id;
+               else
+                       *flags |= opt->ent->id;
+       }
+
+       DBG(OPTLIST, ul_debugobj(ls, "return flags %lx [map=%p]", *flags, map));
+       return 0;
+}
+
+int mnt_optlist_get_optstr(struct libmnt_optlist *ls, char **optstr,
+                       const struct libmnt_optmap *map)
+{
+       struct libmnt_iter itr;
+       struct libmnt_opt *opt;
+       struct ul_buffer buf = UL_INIT_BUFFER;
+
+       if (!ls || !optstr)
+               return -EINVAL;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
+               int rc;
+
+               if (map && opt->map != map)
+                       continue;
+               if (!opt->name)
+                       continue;
+
+               rc = mnt_buffer_append_option(&buf,
+                                       opt->name, strlen(opt->name),
+                                       opt->value,
+                                       opt->value ? strlen(opt->value) : 0);
+               if (rc) {
+                       ul_buffer_free_data(&buf);
+                       return rc;
+               }
+       }
+
+       *optstr = ul_buffer_get_data(&buf, NULL, NULL);
+       DBG(OPTLIST, ul_debugobj(ls, "return optstr %s", *optstr));
+       return 0;
+}
+
+int mnt_optlist_get_propagation(struct libmnt_optlist *ls)
+{
+       return ls ? ls->propagation : 0;
+}
+
+int mnt_optlist_is_propagation_only(struct libmnt_optlist *ls)
+{
+       unsigned long flags = 0, rest;
+
+       if (!ls || !ls->propagation || !ls->nmaps)
+               return 0;
+
+       if (mnt_optlist_get_flags(ls, &flags, ls->linux_map) != 0)
+               return 0;
+
+       rest = flags & ~MS_PROPAGATION;
+       return (rest == 0 || rest == MS_SILENT);
+}
+
+int mnt_opt_has_value(struct libmnt_opt *opt)
+{
+       return opt && opt->value;
+}