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., 59 Temple Place - Suite 330,
20 * Boston, MA 021110-1307, 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.
46 #include "parse_opt.h"
51 struct mount_option *next, *prev;
56 struct mount_options {
57 struct mount_option *head, *tail;
61 static struct mount_option *option_create(char *str)
63 struct mount_option *option;
69 option = malloc(sizeof(*option));
76 opteq = strchr(str, '=');
78 option->keyword = strndup(str, opteq - str);
81 option->value = strdup(opteq + 1);
83 free(option->keyword);
87 option->keyword = strdup(str);
100 static void option_destroy(struct mount_option *option)
102 free(option->keyword);
107 static void options_init(struct mount_options *options)
109 options->head = options->tail = NULL;
113 static struct mount_options *options_create(void)
115 struct mount_options *options;
117 options = malloc(sizeof(*options));
119 options_init(options);
124 static int options_empty(struct mount_options *options)
126 return options->count == 0;
129 static void options_tail_insert(struct mount_options *options,
130 struct mount_option *option)
132 struct mount_option *prev = options->tail;
140 options->head = option;
141 options->tail = option;
146 static void options_delete(struct mount_options *options,
147 struct mount_option *option)
149 struct mount_option *prev = option->prev;
150 struct mount_option *next = option->next;
152 if (!options_empty(options)) {
154 prev->next = option->next;
156 next->prev = option->prev;
158 if (options->head == option)
159 options->head = option->next;
160 if (options->tail == option)
161 options->tail = prev;
165 option_destroy(option);
171 * po_destroy - deallocate a group of mount options
172 * @options: pointer to mount options to free
175 void po_destroy(struct mount_options *options)
178 while (!options_empty(options))
179 options_delete(options, options->head);
185 * po_split - split options string into group of options
186 * @options: pointer to C string containing zero or more comma-delimited options
188 * Convert our mount options string to a list to make it easier
189 * to adjust the options as we go. This is just an exercise in
190 * lexical parsing -- this function doesn't pay attention to the
191 * meaning of the options themselves.
193 * Returns a new group of mount options if successful; otherwise NULL
194 * is returned if some failure occurred.
196 struct mount_options *po_split(char *str)
198 struct mount_options *options;
199 struct tokenizer_state *tstate;
203 return options_create();
205 options = options_create();
207 tstate = init_tokenizer(str, ',');
208 for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
209 struct mount_option *option = option_create(opt);
213 options_tail_insert(options, option);
215 if (tokenizer_error(tstate))
217 end_tokenizer(tstate);
222 end_tokenizer(tstate);
228 * po_replace - replace mount options in one mount_options object with another
229 * @target: pointer to previously instantiated object to replace
230 * @source: pointer to object containing source mount options
232 * Side effect: the object referred to by source is emptied.
234 void po_replace(struct mount_options *target, struct mount_options *source)
237 while (!options_empty(target))
238 options_delete(target, target->head);
241 target->head = source->head;
242 target->tail = source->tail;
243 target->count = source->count;
245 options_init(source);
251 * po_join - recombine group of mount options into a C string
252 * @options: pointer to mount options to recombine
253 * @str: handle on string to replace (input and output)
255 * Convert our mount options object back into a string that the
256 * rest of the world can use.
258 * Upon return, @string contains the address of a replacement
259 * C string containing a comma-delimited list of mount options
260 * and values; or the passed-in string is freed and NULL is
261 * returned if some failure occurred.
263 po_return_t po_join(struct mount_options *options, char **str)
266 struct mount_option *option;
268 if (!str || !options)
274 if (options_empty(options)) {
276 return *str ? PO_SUCCEEDED : PO_FAILED;
279 for (option = options->head; option; option = option->next) {
280 len += strlen(option->keyword);
282 len +=strlen(option->value) + 1; /* equals sign */
287 len++; /* NULL on the end */
294 for (option = options->head; option; option = option->next) {
295 strcat(*str, option->keyword);
298 strcat(*str, option->value);
308 * po_append - concatenate an option onto a group of options
309 * @options: pointer to mount options
310 * @option: pointer to a C string containing the option to add
313 po_return_t po_append(struct mount_options *options, char *str)
315 struct mount_option *option = option_create(str);
318 options_tail_insert(options, option);
325 * po_contains - check for presense of an option in a group
326 * @options: pointer to mount options
327 * @keyword: pointer to a C string containing option keyword for which to search
330 po_found_t po_contains(struct mount_options *options, char *keyword)
332 struct mount_option *option;
334 if (options && keyword) {
335 for (option = options->head; option; option = option->next)
336 if (strcmp(option->keyword, keyword) == 0)
344 * po_get - return the value of the rightmost instance of an option
345 * @options: pointer to mount options
346 * @keyword: pointer to a C string containing option keyword for which to search
348 * If multiple instances of the same option are present in a mount option
349 * list, the rightmost instance is always the effective one.
351 * Returns pointer to C string containing the value of the option.
352 * Returns NULL if the option isn't found, or if the option doesn't
355 char *po_get(struct mount_options *options, char *keyword)
357 struct mount_option *option;
359 if (options && keyword) {
360 for (option = options->tail; option; option = option->prev)
361 if (strcmp(option->keyword, keyword) == 0)
362 return option->value;
369 * po_rightmost - determine the relative position of two options
370 * @options: pointer to mount options
371 * @key1: pointer to a C string containing an option keyword
372 * @key2: pointer to a C string containing another option keyword
374 * The kernel parses the mount option string from left to right.
375 * If an option is specified more than once (for example, "intr"
376 * and "nointr", the rightmost option is the last to be parsed,
377 * and it therefore takes precedence over previous similar options.
379 * This function can be used to determine which of two similar
380 * options will be the one to take effect.
382 po_rightmost_t po_rightmost(struct mount_options *options,
383 char *key1, char *key2)
385 struct mount_option *option;
388 for (option = options->tail; option; option = option->prev) {
389 if (key2 && strcmp(option->keyword, key2) == 0)
390 return PO_KEY2_RIGHTMOST;
391 if (key1 && strcmp(option->keyword, key1) == 0)
392 return PO_KEY1_RIGHTMOST;
396 return PO_NEITHER_FOUND;
400 * po_remove_all - remove instances of an option from a group
401 * @options: pointer to mount options
402 * @keyword: pointer to a C string containing an option keyword to remove
404 * Side-effect: the passed-in list is truncated on success.
406 po_found_t po_remove_all(struct mount_options *options, char *keyword)
408 struct mount_option *option, *next;
409 int found = PO_NOT_FOUND;
411 if (options && keyword) {
412 for (option = options->head; option; option = next) {
414 if (strcmp(option->keyword, keyword) == 0) {
415 options_delete(options, option);