2 * parse_opt.c -- mount option string parsing helpers
4 * Copyright (C) 2007 Oracle. All rights reserved.
5 * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
25 * Converting a C string containing mount options to a data object
26 * and manipulating that object is cleaner in C than manipulating
27 * the C string itself. This is similar to the way Python handles
28 * string manipulation.
30 * The current implementation uses a linked list as the data object
31 * since lists are simple, and we don't need to worry about more
32 * than ten or twenty options at a time.
34 * Hopefully the interface is abstract enough that the underlying
35 * data structure can be replaced if needed without changing the API.
50 #include "parse_opt.h"
55 struct mount_option *next, *prev;
60 struct mount_options {
61 struct mount_option *head, *tail;
65 static struct mount_option *option_create(char *str)
67 struct mount_option *option;
73 option = malloc(sizeof(*option));
80 opteq = strchr(str, '=');
82 option->keyword = strndup(str, opteq - str);
85 option->value = strdup(opteq + 1);
87 free(option->keyword);
91 option->keyword = strdup(str);
104 static struct mount_option *option_dup(const struct mount_option *option)
106 struct mount_option *new;
108 new = malloc(sizeof(*new));
115 new->keyword = strdup(option->keyword);
121 new->value = strdup(option->value);
135 static void option_destroy(struct mount_option *option)
137 free(option->keyword);
142 static void options_init(struct mount_options *options)
144 options->head = options->tail = NULL;
148 static struct mount_options *options_create(void)
150 struct mount_options *options;
152 options = malloc(sizeof(*options));
154 options_init(options);
159 static int options_empty(struct mount_options *options)
161 return options->count == 0;
164 static void options_tail_insert(struct mount_options *options,
165 struct mount_option *option)
167 struct mount_option *prev = options->tail;
175 options->head = option;
176 options->tail = option;
181 static void options_delete(struct mount_options *options,
182 struct mount_option *option)
184 struct mount_option *prev = option->prev;
185 struct mount_option *next = option->next;
187 if (!options_empty(options)) {
189 prev->next = option->next;
191 next->prev = option->prev;
193 if (options->head == option)
194 options->head = option->next;
195 if (options->tail == option)
196 options->tail = prev;
200 option_destroy(option);
206 * po_destroy - deallocate a group of mount options
207 * @options: pointer to mount options to free
210 void po_destroy(struct mount_options *options)
213 while (!options_empty(options))
214 options_delete(options, options->head);
220 * po_split - split options string into group of options
221 * @options: pointer to C string containing zero or more comma-delimited options
223 * Convert our mount options string to a list to make it easier
224 * to adjust the options as we go. This is just an exercise in
225 * lexical parsing -- this function doesn't pay attention to the
226 * meaning of the options themselves.
228 * Returns a new group of mount options if successful; otherwise NULL
229 * is returned if some failure occurred.
231 struct mount_options *po_split(char *str)
233 struct mount_options *options;
234 struct tokenizer_state *tstate;
238 return options_create();
240 options = options_create();
242 tstate = init_tokenizer(str, ',');
243 for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
244 struct mount_option *option = option_create(opt);
248 options_tail_insert(options, option);
250 if (tokenizer_error(tstate))
252 end_tokenizer(tstate);
257 end_tokenizer(tstate);
263 * po_dup - duplicate an existing list of options
264 * @options: pointer to mount options
267 struct mount_options *po_dup(struct mount_options *source)
269 struct mount_options *target;
270 struct mount_option *current;
275 target = options_create();
276 if (options_empty(source) || target == NULL)
279 current = source->head;
280 while (target->count < source->count) {
281 struct mount_option *option;
283 option = option_dup(current);
289 options_tail_insert(target, option);
290 current = current->next;
297 * po_replace - replace mount options in one mount_options object with another
298 * @target: pointer to previously instantiated object to replace
299 * @source: pointer to object containing source mount options
301 * Side effect: the object referred to by source is emptied.
303 void po_replace(struct mount_options *target, struct mount_options *source)
306 while (!options_empty(target))
307 options_delete(target, target->head);
310 target->head = source->head;
311 target->tail = source->tail;
312 target->count = source->count;
314 options_init(source);
320 * po_join - recombine group of mount options into a C string
321 * @options: pointer to mount options to recombine
322 * @str: handle on string to replace (input and output)
324 * Convert our mount options object back into a string that the
325 * rest of the world can use.
327 * Upon return, @string contains the address of a replacement
328 * C string containing a comma-delimited list of mount options
329 * and values; or the passed-in string is freed and NULL is
330 * returned if some failure occurred.
332 po_return_t po_join(struct mount_options *options, char **str)
335 struct mount_option *option;
337 if (!str || !options)
343 if (options_empty(options)) {
345 return *str ? PO_SUCCEEDED : PO_FAILED;
348 for (option = options->head; option; option = option->next) {
349 len += strlen(option->keyword);
351 len +=strlen(option->value) + 1; /* equals sign */
356 len++; /* NULL on the end */
363 for (option = options->head; option; option = option->next) {
364 strcat(*str, option->keyword);
367 strcat(*str, option->value);
377 * po_append - concatenate an option onto a group of options
378 * @options: pointer to mount options
379 * @option: pointer to a C string containing the option to add
382 po_return_t po_append(struct mount_options *options, char *str)
384 struct mount_option *option = option_create(str);
387 options_tail_insert(options, option);
394 * po_contains - check for presense of an option in a group
395 * @options: pointer to mount options
396 * @keyword: pointer to a C string containing option keyword for which to search
399 po_found_t po_contains(struct mount_options *options, char *keyword)
401 struct mount_option *option;
403 if (options && keyword) {
404 for (option = options->head; option; option = option->next)
405 if (strcmp(option->keyword, keyword) == 0)
413 * po_get - return the value of the rightmost instance of an option
414 * @options: pointer to mount options
415 * @keyword: pointer to a C string containing option keyword for which to search
417 * If multiple instances of the same option are present in a mount option
418 * list, the rightmost instance is always the effective one.
420 * Returns pointer to C string containing the value of the option.
421 * Returns NULL if the option isn't found, or if the option doesn't
424 char *po_get(struct mount_options *options, char *keyword)
426 struct mount_option *option;
428 if (options && keyword) {
429 for (option = options->tail; option; option = option->prev)
430 if (strcmp(option->keyword, keyword) == 0)
431 return option->value;
438 * po_get_numeric - return numeric value of rightmost instance of keyword option
439 * @options: pointer to mount options
440 * @keyword: pointer to a C string containing option keyword for which to search
441 * @value: OUT: set to the value of the keyword
443 * This is specifically for parsing keyword options that take only a numeric
444 * value. If multiple instances of the same option are present in a mount
445 * option list, the rightmost instance is always the effective one.
448 * * PO_FOUND if the keyword was found and the value is numeric; @value is
449 * set to the keyword's value
450 * * PO_NOT_FOUND if the keyword was not found
451 * * PO_BAD_VALUE if the keyword was found, but the value is not numeric
453 * These last two are separate in case the caller wants to warn about bad mount
454 * options instead of silently using a default.
457 po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
459 char *option, *endptr;
462 option = po_get(options, keyword);
467 tmp = strtol(option, &endptr, 10);
468 if (errno == 0 && endptr != option) {
474 #else /* HAVE_STRTOL */
475 po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
479 option = po_get(options, keyword);
483 *value = atoi(option);
486 #endif /* HAVE_STRTOL */
489 * po_rightmost - determine the relative position of several options
490 * @options: pointer to mount options
491 * @keys: pointer to an array of C strings containing option keywords
493 * This function can be used to determine which of several similar
494 * options will be the one to take effect.
496 * The kernel parses the mount option string from left to right.
497 * If an option is specified more than once (for example, "intr"
498 * and "nointr", the rightmost option is the last to be parsed,
499 * and it therefore takes precedence over previous similar options.
501 * This can also distinguish among multiple synonymous options, such
502 * as "proto=," "udp" and "tcp."
504 * Returns the index into @keys of the option that is rightmost.
505 * If none of the options listed in @keys is present in @options, or
506 * if @options is NULL, returns -1.
508 int po_rightmost(struct mount_options *options, const char *keys[])
510 struct mount_option *option;
514 for (option = options->tail; option; option = option->prev) {
515 for (i = 0; keys[i] != NULL; i++)
516 if (strcmp(option->keyword, keys[i]) == 0)
525 * po_remove_all - remove instances of an option from a group
526 * @options: pointer to mount options
527 * @keyword: pointer to a C string containing an option keyword to remove
529 * Side-effect: the passed-in list is truncated on success.
531 po_found_t po_remove_all(struct mount_options *options, char *keyword)
533 struct mount_option *option, *next;
534 int found = PO_NOT_FOUND;
536 if (options && keyword) {
537 for (option = options->head; option; option = next) {
539 if (strcmp(option->keyword, keyword) == 0) {
540 options_delete(options, option);