From 078edb2d6e4188963938b7be076db5382f6f802b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 11 Jan 2010 13:33:06 +0100 Subject: [PATCH] libmount: add optls (options container) Signed-off-by: Karel Zak --- shlibs/mount/src/Makefile.am | 4 +- shlibs/mount/src/mount.h.in | 200 ++++++++- shlibs/mount/src/mountP.h | 53 ++- shlibs/mount/src/optent.c | 734 +++++++++++++++++++++++++++++++++ shlibs/mount/src/optls.c | 768 +++++++++++++++++++++++++++++++++++ shlibs/mount/src/optmap.c | 289 +++++++++++++ 6 files changed, 2042 insertions(+), 6 deletions(-) create mode 100644 shlibs/mount/src/optent.c create mode 100644 shlibs/mount/src/optls.c create mode 100644 shlibs/mount/src/optmap.c diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am index 6f2b9502..8a265ecf 100644 --- a/shlibs/mount/src/Makefile.am +++ b/shlibs/mount/src/Makefile.am @@ -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) diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in index 696c2940..ab644a23 100644 --- a/shlibs/mount/src/mount.h.in +++ b/shlibs/mount/src/mount.h.in @@ -25,22 +25,62 @@ extern "C" { #endif +#include + + #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 %: + * %s, %d, %u, %o, %x + */ +struct mnt_optmap +{ + const char *name; /* option name[=%] (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 diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index ef3c1a66..62a5eca9 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -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 index 00000000..4bb7db10 --- /dev/null +++ b/shlibs/mount/src/optent.c @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2010 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include +#include +#include +#include + +#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 + * "=%" 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=). + */ +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 ? : ""); + 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 index 00000000..4c1bea6e --- /dev/null +++ b/shlibs/mount/src/optls.c @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2010 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include + +#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="): + * + * #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, " parse mount options string" }, + { "--ls-data", test_data, " parse and generate mountdata" }, + { "--ls-flags", test_flags, " parse and generate mountflags" }, + { "--ls-mtabstr",test_mtabstr," parse and generate mtab options" }, + { "--reparse", test_reparse, " 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 index 00000000..873c545c --- /dev/null +++ b/shlibs/mount/src/optmap.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2010 Karel Zak + * + * 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: + * + * "=" -- required argument + * "[=]" -- optional argument + * + * where the 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 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 +#include +#include +#include + +#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 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=) + */ +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; +} -- 2.39.5