summaryrefslogtreecommitdiffstats
path: root/utils/mount/parse_opt.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mount/parse_opt.c')
-rw-r--r--utils/mount/parse_opt.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
new file mode 100644
index 0000000..d2d0b65
--- /dev/null
+++ b/utils/mount/parse_opt.c
@@ -0,0 +1,610 @@
+/*
+ * parse_opt.c -- mount option string parsing helpers
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+/*
+ * Converting a C string containing mount options to a data object
+ * and manipulating that object is cleaner in C than manipulating
+ * the C string itself. This is similar to the way Python handles
+ * string manipulation.
+ *
+ * The current implementation uses a linked list as the data object
+ * since lists are simple, and we don't need to worry about more
+ * than ten or twenty options at a time.
+ *
+ * Hopefully the interface is abstract enough that the underlying
+ * data structure can be replaced if needed without changing the API.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "parse_opt.h"
+#include "token.h"
+
+
+struct mount_option {
+ struct mount_option *next, *prev;
+ char *keyword;
+ char *value;
+};
+
+struct mount_options {
+ struct mount_option *head, *tail;
+ unsigned int count;
+};
+
+static struct mount_option *option_create(char *str)
+{
+ struct mount_option *option;
+ char *opteq;
+
+ if (!str)
+ return NULL;
+
+ option = malloc(sizeof(*option));
+ if (!option)
+ return NULL;
+
+ option->next = NULL;
+ option->prev = NULL;
+
+ opteq = strchr(str, '=');
+ if (opteq) {
+ option->keyword = strndup(str, opteq - str);
+ if (!option->keyword)
+ goto fail;
+ option->value = strdup(opteq + 1);
+ if (!option->value) {
+ free(option->keyword);
+ goto fail;
+ }
+ } else {
+ option->keyword = strdup(str);
+ if (!option->keyword)
+ goto fail;
+ option->value = NULL;
+ }
+
+ return option;
+
+fail:
+ free(option);
+ return NULL;
+}
+
+static struct mount_option *option_dup(const struct mount_option *option)
+{
+ struct mount_option *new;
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return NULL;
+
+ new->next = NULL;
+ new->prev = NULL;
+
+ new->keyword = strdup(option->keyword);
+ if (!new->keyword)
+ goto fail;
+
+ new->value = NULL;
+ if (option->value) {
+ new->value = strdup(option->value);
+ if (!new->value) {
+ free(new->keyword);
+ goto fail;
+ }
+ }
+
+ return new;
+
+fail:
+ free(new);
+ return NULL;
+}
+
+static void option_destroy(struct mount_option *option)
+{
+ free(option->keyword);
+ free(option->value);
+ free(option);
+}
+
+static void options_init(struct mount_options *options)
+{
+ options->head = options->tail = NULL;
+ options->count = 0;
+}
+
+static struct mount_options *options_create(void)
+{
+ struct mount_options *options;
+
+ options = malloc(sizeof(*options));
+ if (options)
+ options_init(options);
+
+ return options;
+}
+
+static int options_empty(struct mount_options *options)
+{
+ return options->count == 0;
+}
+
+static void options_tail_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = options->tail;
+
+ option->next = NULL;
+ option->prev = prev;
+
+ if (prev)
+ prev->next = option;
+ else
+ options->head = option;
+ options->tail = option;
+
+ options->count++;
+}
+
+static void options_head_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *ohead = options->head;
+
+ option->prev = NULL;
+ option->next = ohead;
+ if (ohead)
+ ohead->prev = option;
+ else
+ options->tail = option;
+ options->head = option;
+
+ options->count++;
+}
+
+static void options_delete(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = option->prev;
+ struct mount_option *next = option->next;
+
+ if (!options_empty(options)) {
+ if (prev)
+ prev->next = option->next;
+ if (next)
+ next->prev = option->prev;
+
+ if (options->head == option)
+ options->head = option->next;
+ if (options->tail == option)
+ options->tail = prev;
+
+ options->count--;
+
+ option_destroy(option);
+ }
+}
+
+
+/**
+ * po_destroy - deallocate a group of mount options
+ * @options: pointer to mount options to free
+ *
+ */
+void po_destroy(struct mount_options *options)
+{
+ if (options) {
+ while (!options_empty(options))
+ options_delete(options, options->head);
+ free(options);
+ }
+}
+
+/**
+ * po_split - split options string into group of options
+ * @options: pointer to C string containing zero or more comma-delimited options
+ *
+ * Convert our mount options string to a list to make it easier
+ * to adjust the options as we go. This is just an exercise in
+ * lexical parsing -- this function doesn't pay attention to the
+ * meaning of the options themselves.
+ *
+ * Returns a new group of mount options if successful; otherwise NULL
+ * is returned if some failure occurred.
+ */
+struct mount_options *po_split(char *str)
+{
+ struct mount_options *options;
+ struct tokenizer_state *tstate;
+ char *opt;
+
+ if (!str)
+ return options_create();
+
+ options = options_create();
+ if (options) {
+ tstate = init_tokenizer(str, ',');
+ for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
+ struct mount_option *option = option_create(opt);
+ free(opt);
+ if (!option)
+ goto fail;
+ options_tail_insert(options, option);
+ }
+ if (tokenizer_error(tstate))
+ goto fail;
+ end_tokenizer(tstate);
+ }
+ return options;
+
+fail:
+ end_tokenizer(tstate);
+ po_destroy(options);
+ return NULL;
+}
+
+/**
+ * po_dup - duplicate an existing list of options
+ * @options: pointer to mount options
+ *
+ */
+struct mount_options *po_dup(struct mount_options *source)
+{
+ struct mount_options *target;
+ struct mount_option *current;
+
+ if (!source)
+ return NULL;
+
+ target = options_create();
+ if (options_empty(source) || target == NULL)
+ return target;
+
+ current = source->head;
+ while (target->count < source->count) {
+ struct mount_option *option;
+
+ option = option_dup(current);
+ if (!option) {
+ po_destroy(target);
+ return NULL;
+ }
+
+ options_tail_insert(target, option);
+ current = current->next;
+ }
+
+ return target;
+}
+
+/**
+ * po_replace - replace mount options in one mount_options object with another
+ * @target: pointer to previously instantiated object to replace
+ * @source: pointer to object containing source mount options
+ *
+ * Side effect: the object referred to by source is emptied.
+ */
+void po_replace(struct mount_options *target, struct mount_options *source)
+{
+ if (target) {
+ while (!options_empty(target))
+ options_delete(target, target->head);
+
+ if (source) {
+ target->head = source->head;
+ target->tail = source->tail;
+ target->count = source->count;
+
+ options_init(source);
+ }
+ }
+}
+
+/**
+ * po_join - recombine group of mount options into a C string
+ * @options: pointer to mount options to recombine
+ * @str: handle on string to replace (input and output)
+ *
+ * Convert our mount options object back into a string that the
+ * rest of the world can use.
+ *
+ * Upon return, @string contains the address of a replacement
+ * C string containing a comma-delimited list of mount options
+ * and values; or the passed-in string is freed and NULL is
+ * returned if some failure occurred.
+ */
+po_return_t po_join(struct mount_options *options, char **str)
+{
+ size_t len = 0;
+ struct mount_option *option;
+
+ if (!str || !options)
+ return PO_FAILED;
+
+ free(*str);
+ *str = NULL;
+
+ if (options_empty(options)) {
+ *str = strdup("");
+ return *str ? PO_SUCCEEDED : PO_FAILED;
+ }
+
+ for (option = options->head; option; option = option->next) {
+ len += strlen(option->keyword);
+ if (option->value)
+ len +=strlen(option->value) + 1; /* equals sign */
+ if (option->next)
+ len++; /* comma */
+ }
+
+ len++; /* NULL on the end */
+
+ *str = malloc(len);
+ if (!*str)
+ return PO_FAILED;
+ *str[0] = '\0';
+
+ for (option = options->head; option; option = option->next) {
+ strcat(*str, option->keyword);
+ if (option->value) {
+ strcat(*str, "=");
+ strcat(*str, option->value);
+ }
+ if (option->next)
+ strcat(*str, ",");
+ }
+
+ return PO_SUCCEEDED;
+}
+
+/**
+ * po_insert - insert an option into a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_insert(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_head_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_append - concatenate an option onto a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_append(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_tail_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_contains - check for presence of an option in a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ */
+po_found_t po_contains(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = option->next)
+ if (strcmp(option->keyword, keyword) == 0)
+ return PO_FOUND;
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_contains_prefix - check for presence of an option matching a prefix
+ * @options: pointer to mount options
+ * @prefix: pointer to prefix to match against a keyword
+ * @keyword: pointer to a C string containing the option keyword if found
+ * @n: number of instances to skip, so '0' returns the first.
+ *
+ * On success, *keyword contains the pointer of the matching option's keyword.
+ */
+po_found_t po_contains_prefix(struct mount_options *options,
+ const char *prefix, char **keyword, int n)
+{
+ struct mount_option *option;
+
+ if (options && prefix) {
+ for (option = options->head; option; option = option->next)
+ if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) {
+ if (n > 0) {
+ n -= 1;
+ } else {
+ if (keyword)
+ *keyword = option->keyword;
+ return PO_FOUND;
+ }
+ }
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_get - return the value of the rightmost instance of an option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ * If multiple instances of the same option are present in a mount option
+ * list, the rightmost instance is always the effective one.
+ *
+ * Returns pointer to C string containing the value of the option.
+ * Returns NULL if the option isn't found, or if the option doesn't
+ * have a value.
+ */
+char *po_get(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->tail; option; option = option->prev)
+ if (strcmp(option->keyword, keyword) == 0)
+ return option->value;
+ }
+
+ return NULL;
+}
+
+/**
+ * po_get_numeric - return numeric value of rightmost instance of keyword option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ * @value: OUT: set to the value of the keyword
+ *
+ * This is specifically for parsing keyword options that take only a numeric
+ * value. If multiple instances of the same option are present in a mount
+ * option list, the rightmost instance is always the effective one.
+ *
+ * Returns:
+ * * PO_FOUND if the keyword was found and the value is numeric; @value is
+ * set to the keyword's value
+ * * PO_NOT_FOUND if the keyword was not found
+ * * PO_BAD_VALUE if the keyword was found, but the value is not numeric
+ *
+ * These last two are separate in case the caller wants to warn about bad mount
+ * options instead of silently using a default.
+ */
+#ifdef HAVE_STRTOL
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option, *endptr;
+ long tmp;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ errno = 0;
+ tmp = strtol(option, &endptr, 10);
+ if (errno == 0 && endptr != option) {
+ *value = tmp;
+ return PO_FOUND;
+ }
+ return PO_BAD_VALUE;
+}
+#else /* HAVE_STRTOL */
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ *value = atoi(option);
+ return PO_FOUND;
+}
+#endif /* HAVE_STRTOL */
+
+/**
+ * po_rightmost - determine the relative position of several options
+ * @options: pointer to mount options
+ * @keys: pointer to an array of C strings containing option keywords
+ *
+ * This function can be used to determine which of several similar
+ * options will be the one to take effect.
+ *
+ * The kernel parses the mount option string from left to right.
+ * If an option is specified more than once (for example, "intr"
+ * and "nointr", the rightmost option is the last to be parsed,
+ * and it therefore takes precedence over previous similar options.
+ *
+ * This can also distinguish among multiple synonymous options, such
+ * as "proto=," "udp" and "tcp."
+ *
+ * Returns the index into @keys of the option that is rightmost.
+ * If none of the options listed in @keys is present in @options, or
+ * if @options is NULL, returns -1.
+ */
+int po_rightmost(struct mount_options *options, const char *keys[])
+{
+ struct mount_option *option;
+ int i;
+
+ if (options) {
+ for (option = options->tail; option; option = option->prev) {
+ for (i = 0; keys[i] != NULL; i++)
+ if (strcmp(option->keyword, keys[i]) == 0)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * po_remove_all - remove instances of an option from a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing an option keyword to remove
+ *
+ * Side-effect: the passed-in list is truncated on success.
+ */
+po_found_t po_remove_all(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option, *next;
+ int found = PO_NOT_FOUND;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = next) {
+ next = option->next;
+ if (strcmp(option->keyword, keyword) == 0) {
+ options_delete(options, option);
+ found = PO_FOUND;
+ }
+ }
+ }
+
+ return found;
+}