mount.nfs: Eliminate compiler warning in utils/mount/parse_opt.c
[nfs-utils.git] / utils / mount / parse_opt.c
1 /*
2  * parse_opt.c -- mount option string parsing helpers
3  *
4  * Copyright (C) 2007 Oracle.  All rights reserved.
5  * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6  *
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.
11  *
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.
16  *
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.
21  *
22  */
23
24 /*
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.
29  *
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.
33  *
34  * Hopefully the interface is abstract enough that the underlying
35  * data structure can be replaced if needed without changing the API.
36  */
37
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <ctype.h>
44 #include <unistd.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <errno.h>
49
50 #include "parse_opt.h"
51 #include "token.h"
52
53
54 struct mount_option {
55         struct mount_option *next, *prev;
56         char *keyword;
57         char *value;
58 };
59
60 struct mount_options {
61         struct mount_option *head, *tail;
62         unsigned int count;
63 };
64
65 static struct mount_option *option_create(char *str)
66 {
67         struct mount_option *option;
68         char *opteq;
69
70         if (!str)
71                 return NULL;
72
73         option = malloc(sizeof(*option));
74         if (!option)
75                 return NULL;
76         
77         option->next = NULL;
78         option->prev = NULL;
79
80         opteq = strchr(str, '=');
81         if (opteq) {
82                 option->keyword = strndup(str, opteq - str);
83                 if (!option->keyword)
84                         goto fail;
85                 option->value = strdup(opteq + 1);
86                 if (!option->value) {
87                         free(option->keyword);
88                         goto fail;
89                 }
90         } else {
91                 option->keyword = strdup(str);
92                 if (!option->keyword)
93                         goto fail;
94                 option->value = NULL;
95         }
96
97         return option;
98
99 fail:
100         free(option);
101         return NULL;
102 }
103
104 static struct mount_option *option_dup(const struct mount_option *option)
105 {
106         struct mount_option *new;
107
108         new = malloc(sizeof(*new));
109         if (!new)
110                 return NULL;
111         
112         new->next = NULL;
113         new->prev = NULL;
114
115         new->keyword = strdup(option->keyword);
116         if (!new->keyword)
117                 goto fail;
118
119         new->value = NULL;
120         if (option->value) {
121                 new->value = strdup(option->value);
122                 if (!new->value) {
123                         free(new->keyword);
124                         goto fail;
125                 }
126         }
127
128         return new;
129
130 fail:
131         free(new);
132         return NULL;
133 }
134
135 static void option_destroy(struct mount_option *option)
136 {
137         free(option->keyword);
138         free(option->value);
139         free(option);
140 }
141
142 static void options_init(struct mount_options *options)
143 {
144         options->head = options->tail = NULL;
145         options->count = 0;
146 }
147
148 static struct mount_options *options_create(void)
149 {
150         struct mount_options *options;
151
152         options = malloc(sizeof(*options));
153         if (options)
154                 options_init(options);
155
156         return options;
157 }
158
159 static int options_empty(struct mount_options *options)
160 {
161         return options->count == 0;
162 }
163
164 static void options_tail_insert(struct mount_options *options,
165                                 struct mount_option *option)
166 {
167         struct mount_option *prev = options->tail;
168
169         option->next = NULL;
170         option->prev = prev;
171
172         if (prev)
173                 prev->next = option;
174         else
175                 options->head = option;
176         options->tail = option;
177
178         options->count++;
179 }
180
181 static void options_delete(struct mount_options *options,
182                            struct mount_option *option)
183 {
184         struct mount_option *prev = option->prev;
185         struct mount_option *next = option->next;
186
187         if (!options_empty(options)) {
188                 if (prev)
189                         prev->next = option->next;
190                 if (next)
191                         next->prev = option->prev;
192
193                 if (options->head == option)
194                         options->head = option->next;
195                 if (options->tail == option)
196                         options->tail = prev;
197
198                 options->count--;
199
200                 option_destroy(option);
201         }
202 }
203
204
205 /**
206  * po_destroy - deallocate a group of mount options
207  * @options: pointer to mount options to free
208  *
209  */
210 void po_destroy(struct mount_options *options)
211 {
212         if (options) {
213                 while (!options_empty(options))
214                         options_delete(options, options->head);
215                 free(options);
216         }
217 }
218
219 /**
220  * po_split - split options string into group of options
221  * @options: pointer to C string containing zero or more comma-delimited options
222  *
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.
227  *
228  * Returns a new group of mount options if successful; otherwise NULL
229  * is returned if some failure occurred.
230  */
231 struct mount_options *po_split(char *str)
232 {
233         struct mount_options *options;
234         struct tokenizer_state *tstate;
235         char *opt;
236
237         if (!str)
238                 return options_create();
239
240         options = options_create();
241         if (options) {
242                 tstate = init_tokenizer(str, ',');
243                 for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
244                         struct mount_option *option = option_create(opt);
245                         free(opt);
246                         if (!option)
247                                 goto fail;
248                         options_tail_insert(options, option);
249                 }
250                 if (tokenizer_error(tstate))
251                         goto fail;
252                 end_tokenizer(tstate);
253         }
254         return options;
255
256 fail:
257         end_tokenizer(tstate);
258         po_destroy(options);
259         return NULL;
260 }
261
262 /**
263  * po_dup - duplicate an existing list of options
264  * @options: pointer to mount options
265  *
266  */
267 struct mount_options *po_dup(struct mount_options *source)
268 {
269         struct mount_options *target;
270         struct mount_option *current;
271
272         if (!source)
273                 return NULL;
274
275         target = options_create();
276         if (options_empty(source) || target == NULL)
277                 return target;
278
279         current = source->head;
280         while (target->count < source->count) {
281                 struct mount_option *option;
282
283                 option = option_dup(current);
284                 if (!option) {
285                         po_destroy(target);
286                         return NULL;
287                 }
288
289                 options_tail_insert(target, option);
290                 current = current->next;
291         }
292
293         return target;
294 }
295
296 /**
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
300  *
301  * Side effect: the object referred to by source is emptied.
302  */
303 void po_replace(struct mount_options *target, struct mount_options *source)
304 {
305         if (target) {
306                 while (!options_empty(target))
307                         options_delete(target, target->head);
308
309                 if (source) {
310                         target->head = source->head;
311                         target->tail = source->tail;
312                         target->count = source->count;
313
314                         options_init(source);
315                 }
316         }
317 }
318
319 /**
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)
323  *
324  * Convert our mount options object back into a string that the
325  * rest of the world can use.
326  *
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.
331  */
332 po_return_t po_join(struct mount_options *options, char **str)
333 {
334         size_t len = 0;
335         struct mount_option *option;
336
337         if (!str || !options)
338                 return PO_FAILED;
339                 
340         free(*str);
341         *str = NULL;
342
343         if (options_empty(options)) {
344                 *str = strdup("");
345                 return *str ? PO_SUCCEEDED : PO_FAILED;
346         }
347
348         for (option = options->head; option; option = option->next) {
349                 len += strlen(option->keyword);
350                 if (option->value)
351                         len +=strlen(option->value) + 1;  /* equals sign */
352                 if (option->next)
353                         len++;  /* comma */
354         }
355
356         len++;  /* NULL on the end */
357
358         *str = malloc(len);
359         if (!*str)
360                 return PO_FAILED;
361         *str[0] = '\0';
362
363         for (option = options->head; option; option = option->next) {
364                 strcat(*str, option->keyword);
365                 if (option->value) {
366                         strcat(*str, "=");
367                         strcat(*str, option->value);
368                 }
369                 if (option->next)
370                         strcat(*str, ",");
371         }
372
373         return PO_SUCCEEDED;
374 }
375
376 /**
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
380  *
381  */
382 po_return_t po_append(struct mount_options *options, char *str)
383 {
384         struct mount_option *option = option_create(str);
385
386         if (option) {
387                 options_tail_insert(options, option);
388                 return PO_SUCCEEDED;
389         }
390         return PO_FAILED;
391 }
392
393 /**
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
397  *
398  */
399 po_found_t po_contains(struct mount_options *options, char *keyword)
400 {
401         struct mount_option *option;
402
403         if (options && keyword) {
404                 for (option = options->head; option; option = option->next)
405                         if (strcmp(option->keyword, keyword) == 0)
406                                 return PO_FOUND;
407         }
408
409         return PO_NOT_FOUND;
410 }
411
412 /**
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
416  *
417  * If multiple instances of the same option are present in a mount option
418  * list, the rightmost instance is always the effective one.
419  *
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
422  * have a value.
423  */
424 char *po_get(struct mount_options *options, char *keyword)
425 {
426         struct mount_option *option;
427
428         if (options && keyword) {
429                 for (option = options->tail; option; option = option->prev)
430                         if (strcmp(option->keyword, keyword) == 0)
431                                 return option->value;
432         }
433
434         return NULL;
435 }
436
437 /**
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
442  *
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.
446  *
447  * Returns:
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
452  *
453  * These last two are separate in case the caller wants to warn about bad mount
454  * options instead of silently using a default.
455  */
456 #ifdef HAVE_STRTOL
457 po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
458 {
459         char *option, *endptr;
460         long tmp;
461
462         option = po_get(options, keyword);
463         if (option == NULL)
464                 return PO_NOT_FOUND;
465
466         errno = 0;
467         tmp = strtol(option, &endptr, 10);
468         if (errno == 0 && endptr != option) {
469                 *value = tmp;
470                 return PO_FOUND;
471         }
472         return PO_BAD_VALUE;
473 }
474 #else   /* HAVE_STRTOL */
475 po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
476 {
477         char *option;
478
479         option = po_get(options, keyword);
480         if (option == NULL)
481                 return PO_NOT_FOUND;
482
483         *value = atoi(option);
484         return PO_FOUND;
485 }
486 #endif  /* HAVE_STRTOL */
487
488 /**
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
492  *
493  * This function can be used to determine which of several similar
494  * options will be the one to take effect.
495  *
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.
500  *
501  * This can also distinguish among multiple synonymous options, such
502  * as "proto=," "udp" and "tcp."
503  *
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.
507  */
508 int po_rightmost(struct mount_options *options, const char *keys[])
509 {
510         struct mount_option *option;
511         int i;
512
513         if (options) {
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)
517                                         return i;
518                 }
519         }
520
521         return -1;
522 }
523
524 /**
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
528  *
529  * Side-effect: the passed-in list is truncated on success.
530  */
531 po_found_t po_remove_all(struct mount_options *options, char *keyword)
532 {
533         struct mount_option *option, *next;
534         int found = PO_NOT_FOUND;
535
536         if (options && keyword) {
537                 for (option = options->head; option; option = next) {
538                         next = option->next;
539                         if (strcmp(option->keyword, keyword) == 0) {
540                                 options_delete(options, option);
541                                 found = PO_FOUND;
542                         }
543                 }
544         }
545
546         return found;
547 }