From 4897093455a2bf08f3db3a1132cc2f6f5484d77c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 08:03:02 +0200 Subject: Adding upstream version 1:2.6.4. Signed-off-by: Daniel Baumann --- utils/mount/parse_opt.c | 610 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 utils/mount/parse_opt.c (limited to 'utils/mount/parse_opt.c') 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 + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3