]> err.no Git - util-linux/commitdiff
libmount: add optls (options container)
authorKarel Zak <kzak@redhat.com>
Mon, 11 Jan 2010 12:33:06 +0000 (13:33 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 3 Jun 2010 13:20:10 +0000 (15:20 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/Makefile.am
shlibs/mount/src/mount.h.in
shlibs/mount/src/mountP.h
shlibs/mount/src/optent.c [new file with mode: 0644]
shlibs/mount/src/optls.c [new file with mode: 0644]
shlibs/mount/src/optmap.c [new file with mode: 0644]

index 6f2b950232196a99ae5e71eb91835adf6a62ee11..8a265ecf6542854499ef5c83627d708e0805117e 100644 (file)
@@ -12,7 +12,7 @@ usrlib_exec_LTLIBRARIES = libmount.la
 libmount_la_SOURCES = $(mountinc_HEADERS)
 
 nodist_libmount_la_SOURCES = mount.h version.c utils.c test.c init.c cache.c \
-                       optstr.c iter.c list.h \
+                       optstr.c optmap.c optent.c optls.c iter.c list.h \
                        $(top_srcdir)/lib/canonicalize.c
 
 libmount_la_LIBADD = $(ul_libblkid_la)
@@ -22,7 +22,7 @@ libmount_la_DEPENDENCIES = $(libmount_la_LIBADD) mount.sym mount.h.in
 libmount_la_LDFLAGS = -Wl,--version-script=$(ul_libmount_srcdir)/mount.sym \
                       -version-info $(LIBMOUNT_VERSION_INFO)
 
-tests = test_version test_cache test_optstr
+tests = test_version test_cache test_optstr test_optls
 
 EXTRA_DIST = mount.sym mount.h.in
 CLEANFILES = $(tests)
index 696c294090a559ce4a2260c9314521dcd90c6f7a..ab644a23ef97fa4c134d923a3d9d14ae0047f69c 100644 (file)
 extern "C" {
 #endif
 
+#include <stdio.h>
+
+
 #define LIBMOUNT_VERSION   "@LIBMOUNT_VERSION@"
 
 /**
- * mnt_cache
+ * mnt_cache:
  *
  * Stores canonicalized paths and evaluated tags
  */
 typedef struct _mnt_cache mnt_cache;
 
 /**
- * mnt_iter
+ * mnt_iter:
  *
  * Generic iterator (stores state about lists)
  */
 typedef struct _mnt_iter mnt_iter;
 
+/**
+ * mnt_optls:
+ *
+ * Mount options list (stores parsed mount options)
+ */
+typedef struct _mnt_optls mnt_optls;
+
+/**
+ * mnt_optent:
+ *
+ * Parsed mount option - "mnt_optls" entry
+ */
+typedef struct _mnt_optent mnt_optent;
+
+/**
+ * struct mnt_optmap:
+ *
+ * Mount options description (map)
+ *
+ * The libmount supports mount options with values in %<type>:
+ * %s, %d, %u, %o, %x
+ */
+struct mnt_optmap
+{
+       const char      *name;   /* option name[=%<type>] (e.g. "loop[=%s]") */
+       int             id;      /* option ID or MS_* flags (e.g MS_RDONLY) */
+       int             mask;    /* MNT_{MFLAG,MDATA,INVMASK,...} mask */
+};
+
+/*
+ * mount options map masks
+ */
+#define MNT_MFLAG      (1 << 1) /* use the mask as mount(2) flag */
+#define MNT_MDATA      (1 << 2) /* use the option as mount(2) data */
+#define MNT_INVERT     (1 << 3) /* invert the mountflag */
+#define MNT_NOMTAB     (1 << 4) /* skip in the mtab option string */
+
 /* version.c */
 extern int mnt_parse_version_string(const char *ver_string);
 extern int mnt_get_library_version(const char **ver_string);
@@ -74,7 +114,6 @@ extern int mnt_optstr_set_option(char **optstr, const char *name,
                                const char *value);
 extern int mnt_optstr_remove_option(char **optstr, const char *name);
 
-
 /* iter.c */
 enum {
 
@@ -85,6 +124,161 @@ extern mnt_iter *mnt_new_iter(int direction);
 extern void mnt_free_iter(mnt_iter *mi);
 extern void mnt_reset_iter(mnt_iter *mi, int direction);
 
+/* optmap.c */
+enum {
+       MNT_LINUX_MAP = 1,
+       MNT_USERSPACE_MAP
+};
+extern const struct mnt_optmap *mnt_get_builtin_optmap(int id);
+
+/* optent.c */
+extern const struct mnt_optmap *mnt_optent_get_map(mnt_optent *op);
+extern const struct mnt_optmap *mnt_optent_get_mapent(mnt_optent *op);
+extern const char *mnt_optent_get_type(mnt_optent *op);
+extern int mnt_optent_set_value(mnt_optent *op, const char *data);
+extern int mnt_optent_has_value(mnt_optent *op);
+extern int mnt_optent_require_value(mnt_optent *op);
+extern int mnt_optent_is_inverted(mnt_optent *op);
+extern int mnt_optent_strtoul_value(mnt_optent *op, unsigned long int *number);
+extern int mnt_optent_strtol_value(mnt_optent *op, long int *number);
+extern int mnt_optent_strtoull_value(mnt_optent *op, unsigned long long int *number);
+extern const char *mnt_optent_get_value(mnt_optent *op);
+extern int mnt_optent_strlen_value(mnt_optent *op);
+extern int mnt_optent_snprintf_value(mnt_optent *op, char *str, size_t size);
+extern char *mnt_optent_dup_value(mnt_optent *op);
+extern const char *mnt_optent_get_name(mnt_optent *op);
+extern int mnt_optent_get_mask(mnt_optent *op);
+extern int mnt_optent_get_id(mnt_optent *op);
+extern int mnt_optent_get_flag(mnt_optent *op, int *flags);
+extern int mnt_optent_is_unknown(mnt_optent *op);
+extern int mnt_optent_print_debug(mnt_optent *op, FILE *file);
+
+/* optls.c */
+extern mnt_optls *mnt_new_optls(void);
+extern void mnt_free_optls(mnt_optls *ls);
+extern int mnt_optls_add_map(mnt_optls *ls, const struct mnt_optmap *map);
+extern int mnt_optls_add_builtin_map(mnt_optls *ls, int id);
+extern mnt_optent *mnt_optls_add_option(mnt_optls *ls,
+                        const char *name, const char *value);
+extern int mnt_optls_parse_optstr(mnt_optls *ls, const char *optstr);
+extern int mnt_optls_remove_option(mnt_optls *ls, const char *name);
+extern int mnt_optls_remove_option_by_flags(mnt_optls *ls,
+                const struct mnt_optmap *map, const int flags);
+extern int mnt_optls_remove_option_by_iflags(mnt_optls *ls,
+                const struct mnt_optmap *map, const int flags);
+extern int mnt_optls_iterate_options(mnt_iter *itr, mnt_optls *ls,
+                const struct mnt_optmap *map, mnt_optent **option);
+extern mnt_optent *mnt_optls_get_option(mnt_optls *ls, const char *name);
+extern int mnt_optls_get_ids(mnt_optls *ls, const struct mnt_optmap *map);
+extern int mnt_optls_create_mountflags(mnt_optls *ls);
+extern char *mnt_optls_create_mountdata(mnt_optls *ls);
+extern char *mnt_optls_create_mtab_optstr(mnt_optls *ls);
+extern char *mnt_optls_create_userspace_optstr(mnt_optls *ls);
+extern int mnt_optls_print_debug(mnt_optls *ls, FILE *file);
+
+
+/*
+ * mount(8) userspace options masks (MNT_MAP_USERSPACE map)
+ */
+#define MNT_MS_DFLTS   (1 << 1)
+#define MNT_MS_NOAUTO  (1 << 2)
+#define MNT_MS_USER    (1 << 3)
+#define MNT_MS_USERS   (1 << 4)
+#define MNT_MS_OWNER   (1 << 5)
+#define MNT_MS_GROUP   (1 << 6)
+#define MNT_MS_NETDEV  (1 << 7)
+#define MNT_MS_COMMENT  (1 << 8)
+#define MNT_MS_LOOP     (1 << 9)
+#define MNT_MS_NOFAIL   (1 << 10)
+
+/*
+ * mount(2) MS_* masks (MNT_MAP_LINUX map)
+ */
+#ifndef MS_RDONLY
+#define MS_RDONLY       1      /* Mount read-only */
+#endif
+#ifndef MS_NOSUID
+#define MS_NOSUID       2      /* Ignore suid and sgid bits */
+#endif
+#ifndef MS_NODEV
+#define MS_NODEV        4      /* Disallow access to device special files */
+#endif
+#ifndef MS_NOEXEC
+#define MS_NOEXEC       8      /* Disallow program execution */
+#endif
+#ifndef MS_SYNCHRONOUS
+#define MS_SYNCHRONOUS 16      /* Writes are synced at once */
+#endif
+#ifndef MS_REMOUNT
+#define MS_REMOUNT     32      /* Alter flags of a mounted FS */
+#endif
+#ifndef MS_MANDLOCK
+#define MS_MANDLOCK    64      /* Allow mandatory locks on an FS */
+#endif
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC     128     /* Directory modifications are synchronous */
+#endif
+#ifndef MS_NOATIME
+#define MS_NOATIME     0x400   /* 1024: Do not update access times. */
+#endif
+#ifndef MS_NODIRATIME
+#define MS_NODIRATIME   0x800  /* 2048: Don't update directory access times */
+#endif
+#ifndef MS_BIND
+#define        MS_BIND         0x1000  /* 4096: Mount existing tree also elsewhere */
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE                0x2000  /* 8192: Atomically move tree */
+#endif
+#ifndef MS_REC
+#define MS_REC         0x4000  /* 16384: Recursive loopback */
+#endif
+#ifndef MS_VERBOSE
+#define MS_VERBOSE     0x8000  /* 32768 */
+#endif
+#ifndef MS_RELATIME
+#define MS_RELATIME    0x200000 /* 200000: Update access times relative
+                                  to mtime/ctime */
+#endif
+#ifndef MS_UNBINDABLE
+#define MS_UNBINDABLE  (1<<17) /* 131072 unbindable*/
+#endif
+#ifndef MS_PRIVATE
+#define MS_PRIVATE     (1<<18) /* 262144 Private*/
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE       (1<<19) /* 524288 Slave*/
+#endif
+#ifndef MS_SHARED
+#define MS_SHARED      (1<<20) /* 1048576 Shared*/
+#endif
+#ifndef MS_I_VERSION
+#define MS_I_VERSION   (1<<23) /* update inode I_version field */
+#endif
+#ifndef MS_STRICTATIME
+#define MS_STRICTATIME (1<<24) /* strict atime semantics */
+#endif
+
+/*
+ * Magic mount flag number. Had to be or-ed to the flag values.
+ */
+#ifndef MS_MGC_VAL
+#define MS_MGC_VAL 0xC0ED0000  /* magic flag number to indicate "new" flags */
+#endif
+#ifndef MS_MGC_MSK
+#define MS_MGC_MSK 0xffff0000  /* magic flag number mask */
+#endif
+
+
+/* Shared-subtree options */
+#define MS_PROPAGATION  (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE)
+
+/* Options that we make ordinary users have by default.  */
+#define MS_SECURE      (MS_NOEXEC|MS_NOSUID|MS_NODEV)
+
+/* Options that we make owner-mounted devices have by default */
+#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV)
+
 #ifdef __cplusplus
 }
 #endif
index ef3c1a661e544f09e99de1393d4a1c49e969007c..62a5eca9efb8f77daee7a95f13ba7ff02432d663 100644 (file)
@@ -98,4 +98,55 @@ struct _mnt_iter {
                                (itr)->p->next : (itr)->p->prev; \
        } while(0)
 
-#endif
+
+/*
+ * mnt_optls entry
+ */
+struct _mnt_optent {
+       char                    *name;  /* option name (allcocated when mapent is NULL) */
+       char                    *value; /* option argument value */
+
+       int                     mask;   /* MNT_{INVMASK,MDATA,MFLAG,NOMTAB,NOSYS}
+                                        * modifiable flags (initial value comes from map->mask)
+                                        */
+       const struct mnt_optmap *mapent;/* the option description (msp entry) */
+       const struct mnt_optmap *map;   /* head of the map */
+
+       struct list_head        opts;   /* list of options */
+};
+
+/*
+ * Container (list) for mount options
+ */
+struct _mnt_optls {
+       struct mnt_optmap const **maps; /* array with option maps */
+       size_t                  nmaps;  /* number of maps */
+
+       struct list_head        opts;   /* list of options */
+};
+
+/* optmap.c */
+extern const struct mnt_optmap *mnt_optmap_get_entry(struct mnt_optmap const **maps,
+                             int nmaps, const char *name,
+                             size_t namelen, const struct mnt_optmap **mapent);
+extern int mnt_optmap_enum_to_number(const struct mnt_optmap *mapent,
+                        const char *rawdata, size_t len);
+extern const char *mnt_optmap_get_type(const struct mnt_optmap *mapent);
+extern int mnt_optmap_require_value(const struct mnt_optmap *mapent);
+
+/* optent.c */
+
+/* private option masks -- see mount.h.in for the publick masks */
+#define MNT_HASVAL     (1 << 10)
+
+extern mnt_optent *mnt_new_optent(const char *name, size_t namesz,
+                               const char *value, size_t valsz,
+                               struct mnt_optmap const **maps, int nmaps);
+extern void mnt_free_optent(mnt_optent *op);
+extern mnt_optent *mnt_new_optent_from_optstr(char **optstr,
+                               struct mnt_optmap const **maps, int nmaps);
+extern int mnt_optent_assign_map(mnt_optent *op,
+                               struct mnt_optmap const **maps, int nmaps);
+
+
+#endif /* _LIBMOUNT_PRIVATE_H */
diff --git a/shlibs/mount/src/optent.c b/shlibs/mount/src/optent.c
new file mode 100644 (file)
index 0000000..4bb7db1
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "nls.h"
+#include "mountP.h"
+
+static int mnt_init_optent(mnt_optent *op, const char *name, size_t namelen,
+                               struct mnt_optmap const **maps, int nmaps);
+static int __mnt_optent_set_value(mnt_optent *op, const char *data, size_t len);
+
+
+/*
+ * Returns a new optent.
+ */
+mnt_optent *mnt_new_optent(    const char *name, size_t namesz,
+                               const char *value, size_t valsz,
+                               struct mnt_optmap const **maps, int nmaps)
+{
+       mnt_optent *op;
+
+       op = calloc(1, sizeof(struct _mnt_optent));
+       if (!op)
+               return NULL;
+
+       INIT_LIST_HEAD(&op->opts);
+
+       if (mnt_init_optent(op, name, namesz, maps, nmaps))
+               goto err;
+
+       if (value) {
+               if (__mnt_optent_set_value(op, value, valsz))
+                       goto err;
+       } else if (mnt_optent_require_value(op))
+               goto err;
+
+       return op;
+err:
+       free(op);
+       return NULL;
+}
+
+/*
+ * Deallocates the optent.
+ */
+void mnt_free_optent(mnt_optent *op)
+{
+       if (!op)
+               return;
+
+       if (!op->mapent || op->mapent->name != op->name)
+               free(op->name);
+
+       free(op->value);
+
+       if (!list_empty(&op->opts))
+               list_del(&op->opts);
+
+       free(op);
+}
+
+/*
+ * initialize or reinitialize the option entry -- note that the option
+ * name is set to @name and the old name is not free()ed. If the @name
+ * is NULL the already existing option name is used.
+ */
+static int mnt_init_optent(mnt_optent *op, const char *name, size_t namelen,
+                       struct mnt_optmap const **maps, int nmaps)
+{
+       const struct mnt_optmap *mapent = NULL, *map = NULL;
+
+       assert(op);
+
+       if (!op)
+               return -1;
+
+       if (!name && op->name) {
+               name = op->name;
+               namelen = strlen(name);
+       }
+       if (!name)
+               return -1;
+
+       if (nmaps && maps)
+               map = mnt_optmap_get_entry(maps, nmaps, name, namelen, &mapent);
+
+       if (mapent == NULL || mnt_optmap_get_type(mapent) != NULL) {
+               /* we allocate the name for uknown options of for options with
+                * "=%<type>" argument. This is not perfect... */
+               if (op->name != name)
+                       op->name = strndup(name, namelen);
+       } else
+               op->name = (char *) mapent->name;
+
+       op->mapent = mapent;
+       op->map = map;
+       op->mask = mapent ? mapent->mask : 0;
+       if (op->value)
+               op->mask |= MNT_HASVAL;
+
+       if (!op->name)
+               return -1;      /* strdup() failed */
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s: initialized\n", op->name));
+
+       return 0;
+}
+
+static int mnt_optent_check_value(mnt_optent *op, const char *data, size_t len)
+{
+       const char *type;
+       char *end = NULL;
+
+       assert(op);
+       if (!op)
+               return -1;
+
+       type = mnt_optent_get_type(op);
+       if (!type)
+               goto err;                       /* value is unexpected */
+
+       if (!data) {
+               if (mnt_optent_require_value(op))
+                       goto err;
+       } else if (!strncmp(type, "%s", 2)) {
+               /* string type */
+               ;
+       } else if (*type == '{') {
+               /* enum type */
+               if (mnt_optmap_enum_to_number(op->mapent, data, len) < 0)
+                       goto err;
+       } else {
+               /* numbers */
+               int n;          /* happy gcc */
+
+               errno = 0;
+               if (!strncmp(type, "%d", 2) || !strncmp(type, "%ld", 3))
+                       n = strtol(data, &end, 10);
+               else if (!strncmp(type, "%u", 2) || !strncmp(type, "%lu", 3))
+                       n = strtoul(data, &end, 10);
+               else if (!strncmp(type, "%lld", 4))
+                       n = strtoll(data, &end, 10);
+               else if (!strncmp(type, "%llu", 4))
+                       n = strtoull(data, &end, 10);
+               else if (!strncmp(type, "%o", 2))
+                       n = strtoul(data, &end, 8);
+               else if (!strncmp(type, "%x", 2))
+                       n = strtoul(data, &end, 16);
+
+               if (errno == EINVAL || errno == ERANGE || end != data + len)
+                       goto err;
+       }
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s (type=%s): pass check\n",
+               op->name, type));
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s (type=%s): failed to check value %s\n",
+               op->name, type, data));
+       return -1;
+}
+
+/*
+ * Parses the first mount option from @optstr and move @optstr pointer
+ * to the next option.
+ *
+ * Returns: new optent (parsed option) or NULL in case of error.
+ */
+mnt_optent *mnt_new_optent_from_optstr(char **optstr,
+                       struct mnt_optmap const **maps, int nmaps)
+{
+       char *name, *value;
+       size_t nsz, vsz;
+
+       if (mnt_optstr_next_option(optstr, &name, &nsz, &value, &vsz) == 0)
+               return mnt_new_optent(name, nsz, value, vsz, maps, nmaps);
+
+       return NULL;
+}
+
+/*
+ * Lookups @maps and tries to found corresponding map entry for the @op option.
+ * If the map is found the option value is reverified.
+ *
+ * Returns 0 on success, 1 if map not found, -1 in case of error (revalidation
+ * failed or so).
+ */
+int mnt_optent_assign_map(mnt_optent *op,
+                       struct mnt_optmap const **maps, int nmaps)
+{
+       char *oldval, *oldname = NULL;
+       const char *type;
+
+       assert(op);
+       assert(op->name);
+
+       if (!op || !op->name)
+               return -1;
+
+       if (op->mapent && op->name != op->mapent->name)
+               oldname = op->name;     /* old name is allocated */
+
+       op->map = op->mapent = NULL;
+       oldval = op->value;
+
+       if (mnt_init_optent(op, NULL, 0, maps, nmaps))
+               return -1;
+
+       if (op->name != oldname)
+               free(oldname);
+
+       if (!op->map)
+               return 1;       /* uknown option, it's not error */
+
+       /* the new type */
+       type = mnt_optent_get_type(op);
+
+       if (type == NULL && oldval)
+               goto err;               /* value is unexpected */
+       if (mnt_optent_require_value(op) && !oldval)
+               goto err;               /* value is required */
+       if (oldval && mnt_optent_check_value(op, oldval, strlen(oldval)) != 0)
+               goto err;               /* bad value */
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s: assigned to \n",  op->name));
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s: assign failed\n", op->name));
+       return -1;
+}
+
+/**
+ * mnt_optent_get_map:
+ * @op: pointer to mnt_optent instance
+ *
+ * Note that the @op has to be associated with any option map (see
+ * mnt_optent_assign_map()) or NULL is returned.
+ *
+ * Returns pointer to the head of the map that is associated with the option or
+ * NULL (for "extra options").
+ */
+const struct mnt_optmap *mnt_optent_get_map(mnt_optent *op)
+{
+       assert(op);
+       return op ? op->map : NULL;
+}
+
+/**
+ * mnt_optent_get_map_entry:
+ * @op: pointer to mnt_optent instance
+ *
+ * Note that the @op has to be associated with any option map (see
+ * mnt_optent_assign_map()) or NULL is returned.
+
+ * Returns pointer to the map entry that describes the option or NULL (for
+ * "extra options").
+ */
+const struct mnt_optmap *mnt_optent_get_mapent(mnt_optent *op)
+{
+       assert(op);
+       return op ? op->mapent : NULL;
+}
+
+/**
+ * mnt_optent_get_type:
+ * @op: mnt_optent instance
+ *
+ * Note that the @op has to be associated with any option map (see
+ * mnt_optent_assign_map()) or the default "%s]" is returned.
+ *
+ * Returns pointer to the begin of type format string or NULL. For example:
+ *
+ *  "%s"  --> string, required argument (definition in the map is: "foo=%s")
+ *  "%s]" --> string, optional argument (definition in the map is: "foo[=%s]")
+ */
+const char *mnt_optent_get_type(mnt_optent *op)
+{
+       assert(op);
+       if (!op)
+               return NULL;
+       return op->mapent ? mnt_optmap_get_type(op->mapent) : "%s]";
+}
+
+
+
+/**
+ * mnt_optent_set_value:
+ * @op: mnt_optent instance
+ * @data: option argument data or NULL
+ *
+ * The function unset (zeroize) the option value if the @data pointer is NULL.
+ *
+ * Returns 0 on success or -1 in case of error.
+ */
+int mnt_optent_set_value(mnt_optent *op, const char *data)
+{
+       return __mnt_optent_set_value(op, data, data ? strlen(data) : 0);
+}
+
+static int __mnt_optent_set_value(mnt_optent *op, const char *data, size_t len)
+{
+       assert(op);
+       if (!op)
+               return -1;
+
+       free(op->value);
+       op->value = NULL;
+       op->mask &= ~MNT_HASVAL;
+
+       if (mnt_optent_check_value(op, data, len) != 0)
+               goto err;
+       if (data) {
+               op->value = strndup(data, len);
+               if (!op->value)
+                       goto err;
+               op->mask |= MNT_HASVAL;
+       }
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s: set argument value: %s\n",
+               op->name, op->value));
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s: set argument value failed\n",
+               op->name));
+       return -1;
+
+}
+
+/**
+ * mnt_optent_has_value:
+ * @option: pointer to mnt_optent instance
+ *
+ * Returns 1 if the option has actually set an argument value, or 0.
+ */
+int mnt_optent_has_value(mnt_optent *op)
+{
+       return op && (op->mask & MNT_HASVAL) ? 1 : 0;
+}
+
+/**
+ * mnt_optent_require_value:
+ * @op: pointer to mnt_optent instance
+ *
+ * Note that the @op has to be associated with any option map (see
+ * mnt_optent_assign_map()) or 0 is returned.
+ *
+ * Returns 1 if the option requires an argument (option=<arg>).
+ */
+int mnt_optent_require_value(mnt_optent *op)
+{
+       return op && op->mapent ? mnt_optmap_require_value(op->mapent) : 0;
+}
+
+/**
+ * mnt_optent_is_inverted:
+ * @op: pointer to mnt_optent instance
+ *
+ * Returns 1 if the option has MNT_INVERT mask or 0.
+ */
+int mnt_optent_is_inverted(mnt_optent *op)
+{
+       return (op && (op->mask & MNT_INVERT));
+}
+
+static int get_number_base(const char *type)
+{
+       int base = 10;          /* default */
+
+       if (!strncmp(type, "%o", 2))
+               base = 8;
+       else if (!strncmp(type, "%x", 16))
+               base = 16;
+       return base;
+}
+
+/**
+ * mnt_optent_strtoul_value:
+ * @op: pointer to mnt_optent instance
+ * @number: resulting number
+ *
+ * Converts an option value to number. The strtoul() base (decimal, octan or
+ * hex) is determined from (%u, %o or %x) option format type -- default is
+ * decimal (for unknown options).
+ *
+ * The whole option value has to be possible to convert to the number
+ * (e.g "123ABC" returns -1).
+ *
+ * This function also converts {enum0,enumN} type to number 0..N. For more
+ * details see info about options maps.
+ *
+ * Returns 0 on success, -1 in case of error.
+ */
+int mnt_optent_strtoul_value(mnt_optent *op, unsigned long int *number)
+{
+       const char *type = NULL;
+       char *end;
+       size_t len;
+
+       if (!mnt_optent_has_value(op) || !number)
+               goto err;;
+       type = mnt_optent_get_type(op);
+       if (!type)
+               goto err;
+
+       if (*type == '{') {
+               int n;
+
+               if (!op->mapent)
+                       goto err;
+               n = mnt_optmap_enum_to_number(op->mapent, op->value,
+                               strlen(op->value));
+               if (n < 0)
+                       goto err;
+               *number = n;
+       } else {
+               errno = 0;
+               *number = strtoul(op->value, &end, get_number_base(type));
+
+               if (errno == EINVAL || errno == ERANGE)
+                       goto err;
+               len = strlen(op->value);
+               if (end != op->value + len)
+                       goto err;
+       }
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s (type=%s): strtoul failed\n",
+               op->name, type));
+       return -1;
+}
+
+/**
+ * mnt_optent_strtol_value:
+ * @op: pointer to mnt_optent instance
+ * @number: resulting number
+ *
+ * Converts an option value to number. The strtol() base (decimal, octan or
+ * hex) is determined from (%u, %o or %x) option format type -- default is
+ * decimal.
+ *
+ * The whole option value has to be possible to convert to the number
+ * (e.g "123ABC" returns -1).
+ *
+ * Returns 0 on success, -1 in case of error.
+ */
+int mnt_optent_strtol_value(mnt_optent *op, long int *number)
+{
+       const char *type;
+       char *end;
+       size_t len;
+
+       if (!mnt_optent_has_value(op) || !number)
+               return -1;
+
+       type = mnt_optent_get_type(op);
+       if (!type)
+               goto err;
+
+       errno = 0;
+       *number = strtol(op->value, &end, get_number_base(type));
+
+       if (errno == EINVAL || errno == ERANGE)
+               goto err;
+       len = strlen(op->value);
+       if (end != op->value + len)
+               goto err;
+
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s (type=%s): strtol failed\n",
+               op->name, type));
+       return -1;
+}
+
+/**
+ * mnt_optent_strtoull_value:
+ * @op: pointer to mnt_optent instance
+ * @number: resulting number
+ *
+ * Converts an option value to number. The strtoull() base (decimal, octan or
+ * hex) is determined from (%u, %o or %x) option format type -- default is
+ * decimal.
+ *
+ * The whole option value has to be possible to convert to the number
+ * (e.g "123ABC" returns -1).
+ *
+ * Returns 0 on success, -1 in case of error.
+ */
+int mnt_optent_strtoull_value(mnt_optent *op, unsigned long long int *number)
+{
+       const char *type;
+       char *end;
+       size_t len;
+
+       if (!mnt_optent_has_value(op) || !number)
+               return -1;
+
+       type = mnt_optent_get_type(op);
+       if (!type)
+               goto err;
+
+       errno = 0;
+       *number = strtoull(op->value, &end, get_number_base(type));
+
+       if (errno == EINVAL || errno == ERANGE)
+               goto err;
+       len = strlen(op->value);
+       if (end != op->value + len)
+               goto err;
+       return 0;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: option %s (type=%s): strtoull failed\n",
+               op->name, type));
+       return -1;
+
+}
+
+/**
+ * mnt_optent_get_value:
+ * @op: pointer to mnt_optent instance
+ * @data: resulting string
+ *
+ * See also mnt_optent_has_value().
+ *
+ * Returns pointer to value or NULL.
+ */
+const char  *mnt_optent_get_value(mnt_optent *op)
+{
+       return op? op->value : NULL;
+}
+
+/**
+ * mnt_optent_strlen_value:
+ * @op: pointer to mnt_optent instance
+ *
+ * Returns length of string that is necessary to print option value or -1 in
+ * case of error.
+ */
+int mnt_optent_strlen_value(mnt_optent *op)
+{
+       assert(op);
+
+       if (!op)
+               return -1;
+       if (!mnt_optent_has_value(op))
+               return 0;
+       return strlen(op->value);
+}
+
+/**
+ * mnt_optent_snprintf_value:
+ * @op: pointer to mnt_optent instance
+ * @str: resulting string
+ * @size: size of string
+ *
+ * Returns number of printed characters or negative number in case of error.
+ */
+int mnt_optent_snprintf_value(mnt_optent *op, char *str, size_t size)
+{
+       assert(op);
+       assert(str);
+
+       if (!op || !str || !size)
+               return -1;
+       if (!mnt_optent_has_value(op))
+               return -1;
+
+       /* TODO: use extra quotes for SELinux contexts */
+       return snprintf(str, size, "%s", op->value);
+}
+
+/**
+ * mnt_optent_dup_value:
+ * @op: pointer to mnt_optent instance
+ *
+ * Returns duplicate a option value.
+ */
+char *mnt_optent_dup_value(mnt_optent *op)
+{
+       assert(op);
+
+       if (mnt_optent_has_value(op))
+               return strdup(op->value);
+       return NULL;
+}
+
+/**
+ * mnt_optent_get_name:
+ * @op: pointer to mnt_optent instance
+ *
+ * Returns option name or NULL in case of error.
+ */
+const char *mnt_optent_get_name(mnt_optent *op)
+{
+       assert(op);
+       return op ? op->name : NULL;
+}
+
+/**
+ * mnt_optent_get_mask:
+ * @op: pointer to mnt_optent instance
+ *
+ * The initial value of the option mask is a copy from map->mask.
+ * Note that the mask is NOT a mountflag/ID.
+ *
+ * Returns option mask or 0.
+ */
+int mnt_optent_get_mask(mnt_optent *op)
+{
+       assert(op);
+       return op ? op->mask : 0;
+}
+
+/**
+ * mnt_optent_get_id:
+ * @op: pointer to mnt_optent instance
+ *
+ * Note that the ID is also mountflag for all options with MNT_MFLAG mask.
+ *
+ * WARNING: the ID is usually shared between "option" (e.g. exec) and
+ * "nooption" (e.g. noexec) -- you have to carefully check for MNT_INVERT in
+ * the option mask. See mnt_optent_get_flag().
+ *
+ * Returns option ID/mountflag or 0 for extra options (options with undefined
+ * options map).
+ */
+int mnt_optent_get_id(mnt_optent *op)
+{
+       assert(op);
+       return op && op->mapent ? op->mapent->id : 0;
+}
+
+/**
+ * mnt_optent_get_flag:
+ * @op: pointer to mnt_optent instance
+ * @flags: resulting flags
+ *
+ * Adds option ID to @flags or removes option ID from @flags when the option
+ * is inverted option (e.g. "norelatime")
+ *
+ * Example:
+ *     int flags = 0;
+ *
+ *     while(mnt_iterate_options(&itr, opts, map, &op) == 0)
+ *             mnt_optent_get_flag(op, &flags);
+ *
+ *     if (flags & MS_RELATIME)
+ *             printf("relatime is set\n");
+ *
+ * Returns 0 on success, -1 in case of error.
+ */
+int mnt_optent_get_flag(mnt_optent *op, int *flags)
+{
+       int id;
+
+       assert(op);
+       if (!op || !flags)
+               return -1;
+
+       id = mnt_optent_get_id(op);
+       if (op->mask & MNT_INVERT)
+               *flags &= ~id;
+       else
+               *flags |= id;
+       return 0;
+}
+
+/**
+ * mnt_optent_is_unknown:
+ * @op: pointer to mnt_optent instance
+ *
+ * The "extra option" is unknown option (undefined in any option map)
+ *
+ * Return 1 or 0.
+ */
+int mnt_optent_is_unknown(mnt_optent *op)
+{
+       assert(op);
+       return op && op->mapent ? 0 : 1;
+}
+
+/**
+ * mnt_optent_print_debug:
+ * @file: output
+ * @op: pointer to mnt_optent instance
+ *
+ * Prints details about the option.
+ *
+ * Returns 0 on success, -1 in case of error.
+ */
+int mnt_optent_print_debug(mnt_optent *op, FILE *file)
+{
+       const struct mnt_optmap *map;
+       const char *type;
+
+       if (!op)
+               return -1;
+
+       fprintf(file, "------ option %p (%s):\n", op, mnt_optent_get_name(op));
+
+       fprintf(file, "\tID=0x%x\n", mnt_optent_get_id(op));
+       fprintf(file, "\tMASK=%d\n", mnt_optent_get_mask(op));
+
+       map = mnt_optent_get_map(op);
+       fprintf(file, "\tMAP=%p\n", map ? map : NULL);
+
+       map = mnt_optent_get_mapent(op);
+       fprintf(file, "\tMAPENT=%s\n", map ? map->name : NULL);
+
+       fprintf(file, "\tHAS_VALUE=%s\n",
+                       mnt_optent_has_value(op) ? "yes" : "not");
+
+       type = mnt_optent_get_type(op);
+       fprintf(file, "\tTYPE=%s\n", type ? : "<none>");
+       fprintf(file, "\tVALUE=%s\n", op->value);
+       return 0;
+}
diff --git a/shlibs/mount/src/optls.c b/shlibs/mount/src/optls.c
new file mode 100644 (file)
index 0000000..4c1bea6
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "nls.h"
+#include "mountP.h"
+
+/**
+ * mnt_new_optls:
+ *
+ * Returns: newly allocated and initialized optls instance. The library
+ * uses this object as a container for mount options.
+ */
+mnt_optls *mnt_new_optls(void)
+{
+       mnt_optls *ls = calloc(1, sizeof(struct _mnt_optls));
+       if (!ls)
+               return NULL;
+       INIT_LIST_HEAD(&ls->opts);
+       return ls;
+}
+
+/**
+ * mnt_free_optls:
+ * @ls: pointer to mnt_optls instance.
+ *
+ * Deallocates mnt_optls and all stored options.
+ */
+void mnt_free_optls(mnt_optls *ls)
+{
+       if (!ls)
+               return;
+       while (!list_empty(&ls->opts)) {
+               mnt_optent *o = list_entry(ls->opts.next, mnt_optent, opts);
+               mnt_free_optent(o);
+       }
+
+       free(ls->maps);
+       free(ls);
+}
+
+/**
+ * mnt_optls_add_map:
+ * @ls: pointer to mnt_optls instance
+ * @map: pointer to the custom map
+ *
+ * Stores pointer to the custom options map (options description). The map has
+ * to be accessible all time when the libmount works with options.
+ *
+ * All already stored unknown mount options are reverified against the new map.
+ * Note, it's recommented to add all maps to the @optls container before options
+ * parsing.
+ *
+ * Example (add new options "foo" and "bar=<data>"):
+ *
+ *     #define MY_MS_FOO   (1 << 1)
+ *     #define MY_MS_BAR   (1 << 2)
+ *
+ *     mnt_optmap myoptions[] = {
+ *       { "foo",   MY_MS_FOO, MNT_MFLAG },
+ *       { "nofoo", MY_MS_FOO, MNT_MFLAG | MNT_INVERT },
+ *       { "bar=%s",MY_MS_BAR, MNT_MDATA },
+ *       { NULL }
+ *     };
+ *
+ *     mnt_optls_add_map(ls, myoptions);
+ *
+ * Returns: 0 on success, 1 on failed verification, or -1 in case of error.
+ */
+int mnt_optls_add_map(mnt_optls *ls, const struct mnt_optmap *map)
+{
+       mnt_optent *op;
+       mnt_iter itr;
+
+       assert(ls);
+       assert(map || ls->maps == NULL);
+
+       ls->maps = realloc(ls->maps,
+                       sizeof(struct mnt_optmap *) * (ls->nmaps + 1));
+       if (!ls->maps)
+               return -1;
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: optls %p: add map[%zd]", ls, ls->nmaps));
+       ls->maps[ls->nmaps] = map;
+       ls->nmaps++;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               if (!mnt_optent_is_unknown(op))
+                       continue;
+               if (mnt_optent_assign_map(op, &map, 1) == -1)
+                       return 1;
+       }
+       return 0;
+}
+
+/**
+ * mnt_optls_add_builtin_map:
+ * @ls: pointer to mnt_optls instance
+ * @map_id: built-in map id (see mnt_get_builtin_map())
+ *
+ * Returns 0 on success or -1 in case of error.
+ */
+int mnt_optls_add_builtin_map(mnt_optls *ls, int id)
+{
+       const struct mnt_optmap *m = mnt_get_builtin_optmap(id);
+
+       assert(ls);
+       assert(id);
+
+       return m ? mnt_optls_add_map(ls, m) : -1;
+}
+
+
+/*
+ * Append the option to "ls" container.
+ */
+static void mnt_optls_add_optent(mnt_optls *ls, mnt_optent *op)
+{
+       assert(ls);
+       assert(op);
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: add option %s",
+               ls, mnt_optent_get_name(op)));
+
+       list_add_tail(&op->opts, &ls->opts);
+}
+
+/**
+ * mnt_optls_add_option:
+ * @ls: pointer to mnt_optls instance
+ * @name: option name
+ * @value: option value
+ *
+ * Returns: new option or NULL in case of error.
+ */
+mnt_optent *mnt_optls_add_option(mnt_optls *ls,
+                       const char *name, const char *value)
+{
+       mnt_optent *op;
+
+       if (!ls || !name)
+               return NULL;
+
+       op = mnt_new_optent(name, strlen(name),
+                               value, value ? strlen(value) : 0,
+                                ls->maps, ls->nmaps);
+       if (op)
+               mnt_optls_add_optent(ls, op);
+       return op;
+}
+
+/**
+ * mnt_optls_parse_optstr:
+ * @ls: pointer to mnt_optls instance.
+ * @optstr: zero terminated string with mount options (comma separaed list)
+ *
+ * Parses @optstr and all options from @optstr are added to @optls.  It's
+ * possible to call this function more than once. The new options from @optstr
+ * will be appended to the container.
+*
+ * The options are accessible by mnt_optls_iterate_options().
+ *
+ * If the @optls container is associated with any options map(s), all new
+ * options are verified according to the descriptions from the map(s).
+ *
+ * For example:
+ *
+ *     mnt_optls_parse_optstr(ls, "user=snake,noexec");
+ *
+ * is same like:
+ *
+ *      mnt_optls_add_option(ls, "user", "snake");
+ *      mnt_optls_add_option(ls, "noexec", NULL);
+ *
+ * Returns 0 on success or -1 in case of error.
+ */
+int mnt_optls_parse_optstr(mnt_optls *ls, const char *optstr)
+{
+       char *p = (char *) optstr;
+
+       assert(ls);
+       assert(optstr);
+
+       if (!ls || !optstr)
+               return -1;
+
+       while(p && *p) {
+               mnt_optent *op = mnt_new_optent_from_optstr(&p,
+                                       ls->maps, ls->nmaps);
+               if (!op)
+                       return -1;
+               mnt_optls_add_optent(ls, op);
+       }
+       return 0;
+}
+
+/**
+ * mnt_optls_remove_option:
+ * @ls: pointer to mnt_optls instance
+ * @name: option name
+ *
+ * Returns 0 on success, 1 if @name not found and -1 in case of error.
+ */
+int mnt_optls_remove_option(mnt_optls *ls, const char *name)
+{
+       struct list_head *p, *pnext;
+
+       if (!ls || !name)
+               return -1;
+
+       list_for_each_safe(p, pnext, &ls->opts) {
+               mnt_optent *op;
+               const char *n;
+
+               if (!p)
+                       break;
+               op = list_entry(p, mnt_optent, opts);
+               n = mnt_optent_get_name(op);
+               if (n && strcmp(name, n) == 0) {
+                       mnt_free_optent(op);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+
+/**
+ * mnt_optls_remove_option_by_flags:
+ * @ls: pointer to mnt_optls instance
+ * @map: pointer to the map with wanted options or NULL for all options
+ * @flags: option flags
+ *
+ * Removes options which match with @flags. The set of options could
+ * be restricted by @map. For exmaple:
+ *
+ *     mnt_optls_remove_option_by_flags(ls, NULL, MS_NOEXEC);
+ *
+ * removes "noexec" option from "ls".
+ *
+ * Note that this function is useles for options with MNT_INVERT mask (e.g.
+ * "exec" is inverting MS_NOEXEC flag).
+ *
+ * See also mnt_optent_get_flag() and mnt_optls_remove_option_by_iflags().
+ *
+ * Returns number of removed options or -1 in case of error.
+ */
+int mnt_optls_remove_option_by_flags(mnt_optls *ls,
+               const struct mnt_optmap *map, const int flags)
+{
+       struct list_head *p, *pnext;
+       int ct = 0;
+
+       if (!ls)
+               return -1;
+
+       list_for_each_safe(p, pnext, &ls->opts) {
+               mnt_optent *op;
+               int fl = 0;
+
+               if (!p)
+                       break;
+               op = list_entry(p, mnt_optent, opts);
+
+               if (!map || mnt_optent_get_map(op) == map) {
+                       mnt_optent_get_flag(op, &fl);
+                       if (fl & flags) {
+                               mnt_free_optent(op);
+                               ct++;
+                       }
+               }
+       }
+       return ct;
+}
+
+/**
+ * mnt_optls_remove_option_by_iflags:
+ * @ls: pointer to mnt_optls instance
+ * @map: pointer to the map with wanted options or NULL for all options
+ * @flags: option flags
+ *
+ * Removes options which inverting any id from @flags. The set of options could
+ * be restricted by @map. For exmaple:
+ *
+ *     mnt_optls_remove_option_by_iflags(ls, NULL, MS_NOEXEC);
+ *
+ * removes "exec" option from "ls".
+ *
+ * Note that this function is useles for options without MNT_INVERT mask (e.g.
+ * "noexec").
+ *
+ * See also mnt_optent_get_flag() and mnt_optls_remove_option_by_flags().
+ *
+ * Returns number of removed options or -1 in case of error.
+ */
+int mnt_optls_remove_option_by_iflags(mnt_optls *ls,
+               const struct mnt_optmap *map, const int flags)
+{
+       struct list_head *p, *pnext;
+       int ct = 0;
+
+       if (!ls)
+               return -1;
+
+       list_for_each_safe(p, pnext, &ls->opts) {
+               mnt_optent *op;
+               int fl = flags;
+
+               if (!p)
+                       break;
+               op = list_entry(p, mnt_optent, opts);
+
+               if (!map || mnt_optent_get_map(op) == map) {
+                       int id = mnt_optent_get_id(op);
+
+                       if (!(id & fl))
+                               continue;
+
+                       mnt_optent_get_flag(op, &fl);
+
+                       if (!(id & fl)) {
+                               mnt_free_optent(op);
+                               ct++;
+                       }
+               }
+       }
+       return ct;
+}
+
+/**
+ * mnt_optls_iterate_options:
+ * @ls: pointer to mnt_optls instance
+ * @map: pointer to the map of wanted options or NULL for all options
+ * @option: returns pointer to the option object
+ *
+ * Example (print all options):
+ *
+ *     mnt_optent *option;
+ *     mnt_optls *ls = mnt_optls_new();
+ *
+ *     mnt_optls_parse_optstr(ls, "noexec,nodev");
+ *
+ *     while(mnt_optls_iterate_options(itr, ls, NULL, &option ))
+ *         printf("%s\n", mnt_optent_get_name(option)));
+ *
+ * Returns 0 on succes, -1 in case of error or 1 at end of list.
+ */
+int mnt_optls_iterate_options(mnt_iter *itr, mnt_optls *ls,
+               const struct mnt_optmap *map, mnt_optent **option)
+{
+       assert(itr);
+       assert(ls);
+       assert(option);
+
+       if (!itr || !ls || !option)
+               return -1;
+       if (!itr->head)
+               MNT_ITER_INIT(itr, &ls->opts);
+       while (itr->p != itr->head) {
+               MNT_ITER_ITERATE(itr, *option, struct _mnt_optent, opts);
+               if (map == NULL || (*option)->map == map)
+                       return 0;
+       }
+
+       return 1;
+}
+
+/**
+ * mnt_optls_get_option:
+ * @ls: pointer to mnt_optls instance
+ * @name: options name
+ *
+ * Returns the option or NULL.
+ */
+mnt_optent *mnt_optls_get_option(mnt_optls *ls, const char *name)
+{
+       mnt_optent *op;
+       mnt_iter itr;
+
+       assert(ls);
+       assert(name);
+
+       if (!ls || !name)
+               return NULL;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               const char *n = mnt_optent_get_name(op);
+
+               if (n && !strcmp(n, name))
+                       return op;
+       }
+       return NULL;
+}
+
+/**
+ * mnt_optls_get_ids:
+ * @ls: pointer to mnt_optls instance
+ * @map: pointer to the map of wanted options or NULL for all options
+ *
+ * Note that ID has to be unique in all maps when the @map is NULL.
+ *
+ * Note also that this function works with ALL options -- see also
+ * mnt_optls_create_mountflags() that returns MNT_MFLAG options
+ * (mount(2) flags) only.
+ *
+ * Return IDs from all options.
+ */
+int mnt_optls_get_ids(mnt_optls *ls, const struct mnt_optmap *map)
+{
+       int flags = 0;
+       mnt_iter itr;
+       mnt_optent *op;
+
+       assert(ls);
+       if (!ls)
+               return 0;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, map, &op) == 0)
+               mnt_optent_get_flag(op, &flags);
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generated IDs 0x%08x", ls, flags));
+       return flags;
+}
+
+/**
+ * mnt_optls_create_mountflags:
+ * @ls: pointer to mnt_optls instance
+ *
+ * The mountflags are IDs from all MNT_MFLAG options. See "struct mnt_optmap".
+ * For more details about mountflags see mount(2) syscall.
+ *
+ * Returns mount flags or 0.
+ */
+int mnt_optls_create_mountflags(mnt_optls *ls)
+{
+       int flags = 0;
+       mnt_iter itr;
+       mnt_optent *op;
+
+       assert(ls);
+       if (!ls)
+               return 0;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               if (!(op->mask & MNT_MFLAG))
+                       continue;
+               mnt_optent_get_flag(op, &flags);
+       }
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generated mountflags 0x%08x", ls, flags));
+       return flags;
+}
+
+/**
+ * mnt_optls_create_mountdata:
+ * @ls: pointer to mnt_optls instance
+ *
+ * For more details about mountdata see mount(2) syscall.
+ *
+ * Returns newly allocated string with mount options or NULL in case of error.
+ */
+char *mnt_optls_create_mountdata(mnt_optls *ls)
+{
+       mnt_iter itr;
+       mnt_optent *op;
+       char *optstr = NULL;
+
+       assert(ls);
+       if (!ls)
+               return NULL;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               if (!(op->mask & MNT_MDATA) && !mnt_optent_is_unknown(op))
+                       continue;
+               if (mnt_optstr_append_option(&optstr,
+                               mnt_optent_get_name(op),
+                               mnt_optent_get_value(op)))
+                       goto err;
+       }
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generated mountdata: %s", ls, optstr));
+       return optstr;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: ls %p: generate mountdata failed", ls));
+       free(optstr);
+       return NULL;
+}
+
+/**
+ * mnt_optls_create_mtab_optstr:
+ * @ls: pointer to mnt_optls instance
+ *
+ * Returns newly allocated string with mount options for mtab.
+ */
+char *mnt_optls_create_mtab_optstr(mnt_optls *ls)
+{
+       mnt_iter itr;
+       mnt_optent *op;
+       char *optstr = NULL;
+
+       assert(ls);
+       if (!ls)
+               return NULL;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               if (op->mask & MNT_NOMTAB)
+                       continue;
+               if (mnt_optstr_append_option(&optstr,
+                               mnt_optent_get_name(op),
+                               mnt_optent_get_value(op)))
+                       goto err;
+       }
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generated mtab options: %s", ls, optstr));
+       return optstr;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generate mtab optstr failed", ls));
+       free(optstr);
+       return NULL;
+}
+
+/**
+ * mnt_optls_create_userspace_optstr:
+ * @ls: pointer to mnt_optls instance
+ *
+ * Returns newly allocated string with mount options that are
+ * userspace specific (e.g. uhelper=).
+ */
+char *mnt_optls_create_userspace_optstr(mnt_optls *ls)
+{
+       mnt_iter itr;
+       mnt_optent *op;
+       char *optstr = NULL;
+
+       assert(ls);
+       if (!ls)
+               return NULL;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0) {
+               if (mnt_optent_is_unknown(op))
+                       continue;
+               if (op->mask & (MNT_MDATA | MNT_MFLAG | MNT_NOMTAB))
+                       continue;
+               if (mnt_optstr_append_option(&optstr,
+                               mnt_optent_get_name(op),
+                               mnt_optent_get_value(op)))
+                       goto err;
+       }
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generated userspace-only options: %s",
+               ls, optstr));
+       return optstr;
+err:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+               "libmount: opts %p: generate userspace optstr failed", ls));
+       free(optstr);
+       return NULL;
+}
+
+/**
+ * mnt_optls_print_debug:
+ * @file: output
+ * @ls: pointer to mnt_optls instance
+ *
+ * Prints details about options container.
+ */
+int mnt_optls_print_debug(mnt_optls *ls, FILE *file)
+{
+       mnt_iter itr;
+       mnt_optent *op;
+
+       if (!ls)
+               return -1;
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+       fprintf(file, "--- opts: %p\n", ls);
+       while(mnt_optls_iterate_options(&itr, ls, NULL, &op) == 0)
+               mnt_optent_print_debug(op, file);
+
+       return 0;
+}
+
+#ifdef TEST_PROGRAM
+mnt_optls *mk_optls(const char *optstr)
+{
+       mnt_optls *ls = mnt_new_optls();
+       if (!ls)
+               goto err;
+
+       mnt_optls_add_builtin_map(ls, MNT_LINUX_MAP);
+       mnt_optls_add_builtin_map(ls, MNT_USERSPACE_MAP);
+
+       if (mnt_optls_parse_optstr(ls, optstr) != 0) {
+               fprintf(stderr, "\tfailed to parse: %s\n", optstr);
+               goto err;
+       }
+       return ls;
+err:
+       mnt_free_optls(ls);
+       return NULL;
+}
+
+int test_parse(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_optls *ls = NULL;
+       int rc = -1;
+
+       if (argc < 1)
+               goto done;
+       ls = mk_optls(argv[1]);
+       if (!ls)
+               goto done;
+
+       mnt_optls_print_debug(ls, stdout);
+       rc = 0;
+done:
+       mnt_free_optls(ls);
+       return rc;
+}
+
+int test_flags(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_optls *ls = NULL;
+       int rc = -1;
+       int flags;
+       const struct mnt_optmap *map;
+
+       if (argc < 1)
+               goto done;
+       ls = mk_optls(argv[1]);
+       if (!ls)
+               goto done;
+
+       flags = mnt_optls_create_mountflags(ls);
+       printf("\tmount(2) flags:        0x%08x\n", flags);
+
+       map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+       flags = mnt_optls_get_ids(ls, map);
+       printf("\tMNT_MAP_LINUX IDs:     0x%08x  (map %p)\n", flags, map);
+
+       map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+       flags = mnt_optls_get_ids(ls, map);
+       printf("\tMNT_USERSPACE_MAP IDs: 0x%08x  (map %p)\n", flags, map);
+
+       rc = 0;
+done:
+       mnt_free_optls(ls);
+       return rc;
+}
+
+int test_data(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_optls *ls = NULL;
+       char *optstr;
+       int rc = -1;
+
+       if (argc < 1)
+               goto done;
+       ls = mk_optls(argv[1]);
+       if (!ls)
+               goto done;
+
+       optstr = mnt_optls_create_mountdata(ls);
+       printf("\tmount(2) data: '%s'\n", optstr);
+       free(optstr);
+       rc = 0;
+done:
+       mnt_free_optls(ls);
+       return rc;
+}
+
+int test_mtabstr(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_optls *ls = NULL;
+       char *optstr;
+       int rc = -1;
+
+       if (argc < 1)
+               goto done;
+       ls = mk_optls(argv[1]);
+       if (!ls)
+               goto done;
+
+       optstr = mnt_optls_create_mtab_optstr(ls);
+       printf("\tmtab options: '%s'\n", optstr);
+       free(optstr);
+       rc = 0;
+done:
+       mnt_free_optls(ls);
+       return rc;
+}
+
+int test_reparse(struct mtest *ts, int argc, char *argv[])
+{
+       const struct mnt_optmap *map;
+       mnt_optls *ls = NULL;
+       char *optstr;
+       int rc = -1;
+
+       if (argc < 1)
+               goto done;
+       optstr = argv[1];
+       ls = mnt_new_optls();
+       if (!ls)
+               goto done;
+
+       /* add description for kernel options */
+       mnt_optls_add_builtin_map(ls, MNT_LINUX_MAP);
+
+       if (mnt_optls_parse_optstr(ls, optstr) != 0) {
+               fprintf(stderr, "\tfailed to parse: %s\n", optstr);
+               goto done;
+       }
+
+       fprintf(stdout, "------ parse\n");
+       mnt_optls_print_debug(ls, stdout);
+
+       /* add description for userspace options */
+       map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+       mnt_optls_add_map(ls, map);
+
+       fprintf(stdout, "------ re-parse\n");
+       mnt_optls_print_debug(ls, stdout);
+
+       rc = 0;
+done:
+       mnt_free_optls(ls);
+       return rc;
+}
+
+int main(int argc, char *argv[])
+{
+       struct mtest tss[] = {
+               { "--parse",    test_parse, "<optstr>  parse mount options string" },
+               { "--ls-data",  test_data,  "<optstr>  parse and generate mountdata" },
+               { "--ls-flags", test_flags, "<optstr>  parse and generate mountflags" },
+               { "--ls-mtabstr",test_mtabstr,"<optstr>  parse and generate mtab options" },
+               { "--reparse",  test_reparse, "<optstr>  test extra options reparsing" },
+               { NULL }
+       };
+       return  mnt_run_test(tss, argc, argv);
+}
+#endif /* TEST_PROGRAM */
diff --git a/shlibs/mount/src/optmap.c b/shlibs/mount/src/optmap.c
new file mode 100644 (file)
index 0000000..873c545
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Option-maps
+ * -----------
+ * The mount(2) linux syscall uses two arguments for mount options:
+ *
+ *    1) mountflags (see MS_* macros in linux/fs.h)
+ *    2) mountdata  (usully a comma separated string of options)
+ *
+ * The libmount uses options-map(s) to describe mount options. The number of
+ * maps is unlimited. The libmount options parser could be easily extended
+ * (e.g. by mnt_optls_add_map()) to work with new options.
+ *
+ * The option description (map entry) includes:
+ *
+ *    - option name and argument type (e.g. "loop[=%s]")
+ *    - option ID (in the map unique identifier or a mountflags, e.g MS_RDONLY)
+ *    - mask (MNT_INVERT, MNT_MDATA, MNT_MFLAG, MNT_NOMTAB)
+ *
+ * The option argument type is defined by:
+ *
+ *     "=<type>"          -- required argument
+ *     "[=<type>]"        -- optional argument
+ *
+ * where the <type> is sscanf() format string or
+ *
+ *     {item0,item1,...}  -- enum (mnt_option_get_number() converts the value
+ *                           to 0..N number)
+ *
+ * The options argument format is used for parsing only. The library internally
+ * stores the option argument as a string. The conversion to the data type is
+ * on-demant by mnt_option_get_value_*() functions.
+ *
+ * The library checks options argument according to <type> format for simple
+ * formats only:
+ *
+ *     %s, %d, %ld, %lld, %u, %lu, %llu, %x, %o and {enum}
+ *
+ * The libmount defines two basic built-in options maps:
+ *
+ *    - MNT_LINUX_MAP     -- fs-independent kernel mount options (usually MS_* flags)
+ *    - MNT_USERSPACE_MAP -- userspace specific mount options (e.g. "user", "loop")
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "nls.h"
+#include "mountP.h"
+
+/*
+ * fs-independent mount flags (built-in MNT_LINUX_MAP)
+ */
+static const struct mnt_optmap linux_flags_map[] =
+{
+   { "ro",       MS_RDONLY, MNT_MFLAG },               /* read-only */
+   { "rw",       MS_RDONLY, MNT_MFLAG | MNT_INVERT },  /* read-write */
+   { "exec",     MS_NOEXEC, MNT_MFLAG | MNT_INVERT },  /* permit execution of binaries */
+   { "noexec",   MS_NOEXEC, MNT_MFLAG },               /* don't execute binaries */
+   { "suid",     MS_NOSUID, MNT_MFLAG | MNT_INVERT },  /* honor suid executables */
+   { "nosuid",   MS_NOSUID, MNT_MFLAG },               /* don't honor suid executables */
+   { "dev",      MS_NODEV,  MNT_MFLAG | MNT_INVERT },  /* interpret device files  */
+   { "nodev",    MS_NODEV,  MNT_MFLAG },               /* don't interpret devices */
+
+   { "sync",     MS_SYNCHRONOUS, MNT_MFLAG },          /* synchronous I/O */
+   { "async",    MS_SYNCHRONOUS, MNT_MFLAG | MNT_INVERT }, /* asynchronous I/O */
+
+   { "dirsync",  MS_DIRSYNC, MNT_MFLAG },              /* synchronous directory modifications */
+   { "remount",  MS_REMOUNT, MNT_MFLAG },              /* Alter flags of mounted FS */
+   { "bind",     MS_BIND,    MNT_MFLAG },              /* Remount part of tree elsewhere */
+   { "rbind",    MS_BIND|MS_REC, MNT_MFLAG },          /* Idem, plus mounted subtrees */
+#ifdef MS_NOSUB
+   { "sub",      MS_NOSUB,  MNT_MFLAG | MNT_INVERT },  /* allow submounts */
+   { "nosub",    MS_NOSUB,  MNT_MFLAG },               /* don't allow submounts */
+#endif
+#ifdef MS_SILENT
+   { "quiet",   MS_SILENT, MNT_MFLAG },               /* be quiet  */
+   { "loud",     MS_SILENT, MNT_MFLAG | MNT_INVERT },  /* print out messages. */
+#endif
+#ifdef MS_MANDLOCK
+   { "mand",     MS_MANDLOCK, MNT_MFLAG },             /* Allow mandatory locks on this FS */
+   { "nomand",   MS_MANDLOCK, MNT_MFLAG | MNT_INVERT },/* Forbid mandatory locks on this FS */
+#endif
+#ifdef MS_NOATIME
+   { "atime",    MS_NOATIME, MNT_MFLAG | MNT_INVERT }, /* Update access time */
+   { "noatime",         MS_NOATIME, MNT_MFLAG },              /* Do not update access time */
+#endif
+#ifdef MS_I_VERSION
+   { "iversion", MS_I_VERSION,   MNT_MFLAG },          /* Update inode I_version time */
+   { "noiversion", MS_I_VERSION, MNT_MFLAG | MNT_INVERT}, /* Don't update inode I_version time */
+#endif
+#ifdef MS_NODIRATIME
+   { "diratime", MS_NODIRATIME,   MNT_MFLAG | MNT_INVERT }, /* Update dir access times */
+   { "nodiratime", MS_NODIRATIME, MNT_MFLAG },         /* Do not update dir access times */
+#endif
+#ifdef MS_RELATIME
+   { "relatime", MS_RELATIME,   MNT_MFLAG },           /* Update access times relative to mtime/ctime */
+   { "norelatime", MS_RELATIME, MNT_MFLAG | MNT_INVERT }, /* Update access time without regard to mtime/ctime */
+#endif
+#ifdef MS_STRICTATIME
+   { "strictatime", MS_STRICTATIME, MNT_MFLAG },       /* Strict atime semantics */
+   { "nostrictatime", MS_STRICTATIME, MNT_MFLAG | MNT_INVERT }, /* kernel default atime */
+#endif
+   { NULL, 0, 0 }
+};
+
+/*
+ * userspace mount option (built-in MNT_USERSPACE_MAP)
+ */
+static const struct mnt_optmap userspace_opts_map[] =
+{
+   { "defaults", MNT_MS_DFLTS, MNT_NOMTAB },               /* default options */
+
+   { "auto",    MNT_MS_NOAUTO, MNT_INVERT | MNT_NOMTAB },  /* Can be mounted using -a */
+   { "noauto",  MNT_MS_NOAUTO, MNT_NOMTAB },               /* Can  only be mounted explicitly */
+
+   { "user[=%s]", MNT_MS_USER },                           /* Allow ordinary user to mount (mtab) */
+   { "nouser",  MNT_MS_USER, MNT_INVERT | MNT_NOMTAB },    /* Forbid ordinary user to mount */
+
+   { "users",   MNT_MS_USERS, MNT_NOMTAB },                /* Allow ordinary users to mount */
+   { "nousers", MNT_MS_USERS, MNT_INVERT | MNT_NOMTAB },   /* Forbid ordinary users to mount */
+
+   { "owner",   MNT_MS_OWNER, MNT_NOMTAB },                /* Let the owner of the device mount */
+   { "noowner", MNT_MS_OWNER, MNT_INVERT | MNT_NOMTAB },   /* Device owner has no special privs */
+
+   { "group",   MNT_MS_GROUP, MNT_NOMTAB },                /* Let the group of the device mount */
+   { "nogroup", MNT_MS_GROUP, MNT_INVERT | MNT_NOMTAB },   /* Device group has no special privs */
+
+   { "_netdev", MNT_MS_NETDEV },                           /* Device requires network */
+
+   { "comment=%s", MNT_MS_COMMENT, MNT_NOMTAB },           /* fstab comment only */
+
+   { "loop[=%s]", MNT_MS_LOOP },                           /* use the loop device */
+
+   { "nofail",  MNT_MS_NOFAIL, MNT_NOMTAB },               /* Do not fail if ENOENT on dev */
+
+   { NULL, 0, 0 }
+};
+
+/**
+ * mnt_get_builtin_map:
+ * @id: map id -- MNT_LINUX_MAP or MNT_USERSPACE_MAP
+ *
+ * MNT_LINUX_MAP - Linux kernel fs-independent mount options
+ *                 (usually MS_* flags, see linux/fs.h)
+ *
+ * MNT_USERSPACE_MAP - userpace mount(8) specific mount options
+ *                     (e.g user=, _netdev, ...)
+ *
+ * Returns internal (static) libmount map.
+ */
+const struct mnt_optmap *mnt_get_builtin_optmap(int id)
+{
+       assert(id);
+
+       if (id == MNT_LINUX_MAP)
+               return linux_flags_map;
+       else if (id == MNT_USERSPACE_MAP)
+               return userspace_opts_map;
+       return NULL;
+}
+
+/*
+ * Lookups for the @name in @maps and returns a map and in @mapent
+ * returns the map entry
+ */
+const struct mnt_optmap *mnt_optmap_get_entry(
+                               struct mnt_optmap const **maps,
+                               int nmaps,
+                               const char *name,
+                               size_t namelen,
+                               const struct mnt_optmap **mapent)
+{
+       int i;
+
+       assert(maps);
+       assert(nmaps);
+       assert(name);
+       assert(namelen);
+       assert(mapent);
+
+       *mapent = NULL;
+
+       for (i = 0; i < nmaps; i++) {
+               const struct mnt_optmap *map = maps[i];
+               const struct mnt_optmap *ent;
+               const char *p;
+
+               for (ent = map; ent && ent->name; ent++) {
+                       if (strncmp(ent->name, name, namelen))
+                               continue;
+                       p = ent->name + namelen;
+                       if (*p == '\0' || *p == '=' || *p == '[') {
+                               *mapent = ent;
+                               return map;
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+/*
+ * Converts @rawdata to number according to enum definition in the @mapent.
+ */
+int mnt_optmap_enum_to_number(const struct mnt_optmap *mapent,
+                       const char *rawdata, size_t len)
+{
+       const char *p, *end = NULL, *begin = NULL;
+       int n = -1;
+
+       if (!rawdata || !*rawdata || !mapent || !len)
+               return -1;
+
+       p = strrchr(mapent->name, '=');
+       if (!p || *(p + 1) == '{')
+               return -1;      /* value unexpected or not "enum" */
+       p += 2;
+       if (!*p || *(p + 1) == '}')
+               return -1;      /* hmm... option <type> is "={" or "={}" */
+
+       /* we cannot use strstr(), @rawdata is not terminated */
+       for (; p && *p; p++) {
+               if (!begin)
+                       begin = p;              /* begin of the item */
+               if (*p == ',')
+                       end = p;                /* terminate the item */
+               if (*(p + 1) == '}')
+                       end = p + 1;            /* end of enum definition */
+               if (!begin || !end)
+                       continue;
+               if (end <= begin)
+                       return -1;
+               n++;
+               if (len == end - begin && strncasecmp(begin, rawdata, len) == 0)
+                       return n;
+               p = end;
+       }
+
+       return -1;
+}
+
+/*
+ * Returns data type defined in the @mapent.
+ */
+const char *mnt_optmap_get_type(const struct mnt_optmap *mapent)
+{
+       char *type;
+
+       assert(mapent);
+       assert(mapent->name);
+
+       type = strrchr(mapent->name, '=');
+       if (!type)
+               return NULL;                    /* value is unexpected */
+       if (type == mapent->name)
+               return NULL;                    /* wrong format of type definition */
+       type++;
+       if (*type != '%' && *type != '{')
+               return NULL;                    /* wrong format of type definition */
+       return type ? : NULL;
+}
+
+/*
+ * Does the option (that is described by @mntent) require any value? (e.g.
+ * uid=<foo>)
+ */
+int mnt_optmap_require_value(const struct mnt_optmap *mapent)
+{
+       char *type;
+
+       assert(mapent);
+       assert(mapent->name);
+
+       type = strchr(mapent->name, '=');
+       if (!type)
+               return 0;                       /* value is unexpected */
+       if (type == mapent->name)
+               return 0;                       /* wrong format of type definition */
+       if (*(type - 1) == '[')
+               return 0;                       /* optional */
+       return 1;
+}