]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/optstr.c
Merge branch 'lsns--Q' of https://github.com/masatake/util-linux
[thirdparty/util-linux.git] / libmount / src / optstr.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2009-2018 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 */
12
13 /**
14 * SECTION: optstr
15 * @title: Options string
16 * @short_description: low-level API for working with mount options
17 *
18 * This is a simple and low-level API to working with mount options that are stored
19 * in a string.
20 */
21 #include <ctype.h>
22
23 #include "strutils.h"
24 #include "mountP.h"
25
26 /*
27 * Option location
28 */
29 struct libmnt_optloc {
30 char *begin;
31 char *end;
32 char *value;
33 size_t valsz;
34 size_t namesz;
35 };
36
37 #define MNT_INIT_OPTLOC { .begin = NULL }
38
39 #define mnt_optmap_entry_novalue(e) \
40 (e && (e)->name && !strchr((e)->name, '=') && !((e)->mask & MNT_PREFIX))
41
42 /*
43 * Locates the first option that matches @name. The @end is set to the
44 * char behind the option (it means ',' or \0).
45 *
46 * @ol is optional.
47 *
48 * Returns negative number on parse error, 1 when not found and 0 on success.
49 */
50 static int mnt_optstr_locate_option(char *optstr,
51 const char *name, size_t namesz,
52 struct libmnt_optloc *ol)
53 {
54 char *n;
55 size_t nsz;
56 int rc;
57
58 if (!optstr)
59 return 1;
60
61 assert(name);
62
63 if (!namesz)
64 namesz = strlen(name);
65 if (!namesz)
66 return 1;
67
68 do {
69 rc = ul_optstr_next(&optstr, &n, &nsz,
70 ol ? &ol->value : NULL,
71 ol ? &ol->valsz : NULL);
72 if (rc)
73 break;
74
75 if (namesz == nsz && strncmp(n, name, nsz) == 0) {
76 if (ol) {
77 ol->begin = n;
78 ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
79 ol->namesz = nsz;
80 }
81 return 0;
82 }
83 } while(1);
84
85 return rc;
86 }
87 /**
88 * mnt_optstr_next_option:
89 * @optstr: option string, returns the position of the next option
90 * @name: returns the option name
91 * @namesz: returns the option name length
92 * @value: returns the option value or NULL
93 * @valuesz: returns the option value length or zero
94 *
95 * Parses the first option in @optstr.
96 *
97 * Returns: 0 on success, 1 at the end of @optstr or negative number in case of
98 * error.
99 */
100 int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,
101 char **value, size_t *valuesz)
102 {
103 if (!optstr || !*optstr)
104 return -EINVAL;
105
106 return ul_optstr_next(optstr, name, namesz, value, valuesz);
107 }
108
109 int mnt_buffer_append_option(struct ul_buffer *buf,
110 const char *name, size_t namesz,
111 const char *val, size_t valsz,
112 int quoted)
113 {
114 int rc = 0;
115
116 if (!ul_buffer_is_empty(buf))
117 rc = ul_buffer_append_data(buf, ",", 1);
118 if (!rc)
119 rc = ul_buffer_append_data(buf, name, namesz);
120 if (val && !rc) {
121 /* we need to append '=' is value is empty string, see
122 * 727c689908c5e68c92aa1dd65e0d3bdb6d91c1e5 */
123 rc = ul_buffer_append_data(buf, "=", 1);
124 if (!rc && valsz) {
125 if (quoted)
126 rc = ul_buffer_append_data(buf, "\"", 1);
127 if (!rc)
128 rc = ul_buffer_append_data(buf, val, valsz);
129 if (quoted)
130 rc = ul_buffer_append_data(buf, "\"", 1);
131 }
132 }
133 return rc;
134 }
135
136 /**
137 * mnt_optstr_append_option:
138 * @optstr: option string or NULL, returns a reallocated string
139 * @name: value name
140 * @value: value
141 *
142 * Returns: 0 on success or <0 in case of error. After an error the @optstr should
143 * be unmodified.
144 */
145 int mnt_optstr_append_option(char **optstr, const char *name, const char *value)
146 {
147 struct ul_buffer buf = UL_INIT_BUFFER;
148 int rc;
149 size_t nsz, vsz, osz;
150
151 if (!optstr)
152 return -EINVAL;
153 if (!name || !*name)
154 return 0;
155
156 nsz = strlen(name);
157 osz = *optstr ? strlen(*optstr) : 0;
158 vsz = value ? strlen(value) : 0;
159
160 ul_buffer_refer_string(&buf, *optstr);
161 ul_buffer_set_chunksize(&buf, osz + nsz + vsz + 3); /* to call realloc() only once */
162
163 rc = mnt_buffer_append_option(&buf, name, nsz, value, vsz, 0);
164 if (!rc)
165 *optstr = ul_buffer_get_data(&buf, NULL, NULL);
166 else if (osz == 0)
167 ul_buffer_free_data(&buf);
168
169 return rc;
170 }
171 /**
172 * mnt_optstr_prepend_option:
173 * @optstr: option string or NULL, returns a reallocated string
174 * @name: value name
175 * @value: value
176 *
177 * Returns: 0 on success or <0 in case of error. After an error the @optstr should
178 * be unmodified.
179 */
180 int mnt_optstr_prepend_option(char **optstr, const char *name, const char *value)
181 {
182 struct ul_buffer buf = UL_INIT_BUFFER;
183 size_t nsz, vsz, osz;
184 int rc;
185
186 if (!optstr)
187 return -EINVAL;
188 if (!name || !*name)
189 return 0;
190
191 nsz = strlen(name);
192 osz = *optstr ? strlen(*optstr) : 0;
193 vsz = value ? strlen(value) : 0;
194
195 ul_buffer_set_chunksize(&buf, osz + nsz + vsz + 3); /* to call realloc() only once */
196
197 rc = mnt_buffer_append_option(&buf, name, nsz, value, vsz, 0);
198 if (*optstr && !rc) {
199 rc = ul_buffer_append_data(&buf, ",", 1);
200 if (!rc)
201 rc = ul_buffer_append_data(&buf, *optstr, osz);
202 free(*optstr);
203 }
204
205 if (!rc)
206 *optstr = ul_buffer_get_data(&buf, NULL, NULL);
207 else
208 ul_buffer_free_data(&buf);
209
210 return rc;
211 }
212
213 /**
214 * mnt_optstr_get_option:
215 * @optstr: string with a comma separated list of options
216 * @name: requested option name
217 * @value: returns a pointer to the beginning of the value (e.g. name=VALUE) or NULL
218 * @valsz: returns size of the value or 0
219 *
220 * Returns: 0 on success, 1 when not found the @name or negative number in case
221 * of error.
222 */
223 int mnt_optstr_get_option(const char *optstr, const char *name,
224 char **value, size_t *valsz)
225 {
226 struct libmnt_optloc ol = MNT_INIT_OPTLOC;
227 int rc;
228
229 if (!optstr || !name)
230 return -EINVAL;
231
232 rc = mnt_optstr_locate_option((char *) optstr, name, 0, &ol);
233 if (!rc) {
234 if (value)
235 *value = ol.value;
236 if (valsz)
237 *valsz = ol.valsz;
238 }
239 return rc;
240 }
241
242 /**
243 * mnt_optstr_deduplicate_option:
244 * @optstr: string with a comma separated list of options
245 * @name: requested option name
246 *
247 * Removes all instances of @name except the last one.
248 *
249 * Returns: 0 on success, 1 when not found the @name or negative number in case
250 * of error.
251 */
252 int mnt_optstr_deduplicate_option(char **optstr, const char *name)
253 {
254 int rc;
255 char *begin = NULL, *end = NULL, *opt;
256
257 if (!optstr || !name)
258 return -EINVAL;
259
260 opt = *optstr;
261 do {
262 struct libmnt_optloc ol = MNT_INIT_OPTLOC;
263
264 rc = mnt_optstr_locate_option(opt, name, 0, &ol);
265 if (!rc) {
266 if (begin) {
267 /* remove the previous instance */
268 size_t shift = strlen(*optstr);
269
270 mnt_optstr_remove_option_at(optstr, begin, end);
271
272 /* now all the offsets are not valid anymore - recount */
273 shift -= strlen(*optstr);
274 ol.begin -= shift;
275 ol.end -= shift;
276 }
277 begin = ol.begin;
278 end = ol.end;
279 opt = end && *end ? end + 1 : NULL;
280 }
281 if (opt == NULL)
282 break;
283 } while (rc == 0 && *opt);
284
285 return rc < 0 ? rc : begin ? 0 : 1;
286 }
287
288 /*
289 * The result never starts or ends with a comma or contains two commas
290 * (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,")
291 */
292 int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end)
293 {
294 size_t sz;
295
296 if (!optstr || !begin || !end)
297 return -EINVAL;
298
299 if ((begin == *optstr || *(begin - 1) == ',') && *end == ',')
300 end++;
301
302 sz = strlen(end);
303
304 memmove(begin, end, sz + 1);
305 if (!*begin && (begin > *optstr) && *(begin - 1) == ',')
306 *(begin - 1) = '\0';
307
308 return 0;
309 }
310
311 /* insert 'substr' or '=substr' to @str on position @pos */
312 static int __attribute__((nonnull(1,2,3)))
313 insert_value(char **str, char *pos, const char *substr, char **next)
314 {
315 size_t subsz = strlen(substr); /* substring size */
316 size_t strsz = strlen(*str);
317 size_t possz = strlen(pos);
318 size_t posoff;
319 char *p;
320 int sep;
321
322 /* is it necessary to prepend '=' before the substring ? */
323 sep = !(pos > *str && *(pos - 1) == '=');
324
325 /* save an offset of the place where we need to add substr */
326 posoff = pos - *str;
327
328 p = realloc(*str, strsz + sep + subsz + 1);
329 if (!p)
330 return -ENOMEM;
331
332 /* zeroize the newly allocated memory -- valgrind loves us... */
333 memset(p + strsz, 0, sep + subsz + 1);
334
335 /* set pointers to the reallocated string */
336 *str = p;
337 pos = p + posoff;
338
339 if (possz)
340 /* create a room for the new substring */
341 memmove(pos + subsz + sep, pos, possz + 1);
342 if (sep)
343 *pos++ = '=';
344
345 memcpy(pos, substr, subsz);
346
347 if (next) {
348 /* set pointer to the next option */
349 *next = pos + subsz;
350 if (**next == ',')
351 (*next)++;
352 }
353 return 0;
354 }
355
356 /**
357 * mnt_optstr_set_option:
358 * @optstr: string with a comma separated list of options
359 * @name: requested option
360 * @value: new value or NULL
361 *
362 * Set or unset the option @value.
363 *
364 * Returns: 0 on success, 1 when not found the @name or negative number in case
365 * of error.
366 */
367 int mnt_optstr_set_option(char **optstr, const char *name, const char *value)
368 {
369 struct libmnt_optloc ol = MNT_INIT_OPTLOC;
370 char *nameend;
371 int rc = 1;
372
373 if (!optstr || !name)
374 return -EINVAL;
375
376 if (*optstr)
377 rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
378 if (rc < 0)
379 return rc; /* parse error */
380 if (rc == 1)
381 return mnt_optstr_append_option(optstr, name, value); /* not found */
382
383 nameend = ol.begin + ol.namesz;
384
385 if (value == NULL && ol.value && ol.valsz)
386 /* remove unwanted "=value" */
387 mnt_optstr_remove_option_at(optstr, nameend, ol.end);
388
389 else if (value && ol.value == NULL)
390 /* insert "=value" */
391 rc = insert_value(optstr, nameend, value, NULL);
392
393 else if (value && ol.value && strlen(value) == ol.valsz)
394 /* simply replace =value */
395 memcpy(ol.value, value, ol.valsz);
396
397 else if (value && ol.value) {
398 mnt_optstr_remove_option_at(optstr, nameend, ol.end);
399 rc = insert_value(optstr, nameend, value, NULL);
400 }
401 return rc;
402 }
403
404 /**
405 * mnt_optstr_remove_option:
406 * @optstr: string with a comma separated list of options
407 * @name: requested option name
408 *
409 * Returns: 0 on success, 1 when not found the @name or negative number in case
410 * of error.
411 */
412 int mnt_optstr_remove_option(char **optstr, const char *name)
413 {
414 struct libmnt_optloc ol = MNT_INIT_OPTLOC;
415 int rc;
416
417 if (!optstr || !name)
418 return -EINVAL;
419
420 rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
421 if (rc != 0)
422 return rc;
423
424 mnt_optstr_remove_option_at(optstr, ol.begin, ol.end);
425 return 0;
426 }
427
428 /**
429 * mnt_split_optstr:
430 * @optstr: string with comma separated list of options
431 * @user: returns newly allocated string with userspace options
432 * @vfs: returns newly allocated string with VFS options
433 * @fs: returns newly allocated string with FS options
434 * @ignore_user: option mask for options that should be ignored
435 * @ignore_vfs: option mask for options that should be ignored
436 *
437 * For example:
438 *
439 * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0);
440 *
441 * returns all userspace options, the options that do not belong to
442 * mtab are ignored.
443 *
444 * Note that FS options are all options that are undefined in MNT_USERSPACE_MAP
445 * or MNT_LINUX_MAP.
446 *
447 * Returns: 0 on success, or a negative number in case of error.
448 */
449 int mnt_split_optstr(const char *optstr, char **user, char **vfs,
450 char **fs, int ignore_user, int ignore_vfs)
451 {
452 int rc = 0;
453 char *name, *val, *str = (char *) optstr;
454 size_t namesz, valsz, chunsz;
455 struct libmnt_optmap const *maps[2];
456 struct ul_buffer xvfs = UL_INIT_BUFFER,
457 xfs = UL_INIT_BUFFER,
458 xuser = UL_INIT_BUFFER;
459
460 if (!optstr)
461 return -EINVAL;
462
463 maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
464 maps[1] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
465
466 chunsz = strlen(optstr) / 2;
467
468 while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
469 struct ul_buffer *buf = NULL;
470 const struct libmnt_optmap *ent = NULL;
471 const struct libmnt_optmap *m =
472 mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
473
474 if (ent && !ent->id)
475 continue; /* ignore undefined options (comments) */
476
477 /* ignore name=<value> if options map expects <name> only */
478 if (valsz && mnt_optmap_entry_novalue(ent))
479 m = NULL;
480
481 if (ent && m && m == maps[0] && vfs) {
482 if (ignore_vfs && (ent->mask & ignore_vfs))
483 continue;
484 if (vfs)
485 buf = &xvfs;
486 } else if (ent && m && m == maps[1] && user) {
487 if (ignore_user && (ent->mask & ignore_user))
488 continue;
489 if (user)
490 buf = &xuser;
491 } else if (!m && fs) {
492 if (fs)
493 buf = &xfs;
494 }
495
496 if (buf) {
497 if (ul_buffer_is_empty(buf))
498 ul_buffer_set_chunksize(buf, chunsz);
499 rc = mnt_buffer_append_option(buf, name, namesz, val, valsz, 0);
500 }
501 if (rc)
502 break;
503 }
504
505 if (vfs)
506 *vfs = rc ? NULL : ul_buffer_get_data(&xvfs, NULL, NULL);
507 if (fs)
508 *fs = rc ? NULL : ul_buffer_get_data(&xfs, NULL, NULL);
509 if (user)
510 *user = rc ? NULL : ul_buffer_get_data(&xuser, NULL, NULL);
511 if (rc) {
512 ul_buffer_free_data(&xvfs);
513 ul_buffer_free_data(&xfs);
514 ul_buffer_free_data(&xuser);
515 }
516
517 return rc;
518 }
519
520 /**
521 * mnt_optstr_get_options
522 * @optstr: string with a comma separated list of options
523 * @subset: returns newly allocated string with options
524 * @map: options map
525 * @ignore: mask of the options that should be ignored
526 *
527 * Extracts options from @optstr that belong to the @map, for example:
528 *
529 * mnt_optstr_get_options(optstr, &p,
530 * mnt_get_builtin_optmap(MNT_LINUX_MAP),
531 * MNT_NOMTAB);
532 *
533 * the 'p' returns all VFS options, the options that do not belong to mtab
534 * are ignored.
535 *
536 * Returns: 0 on success, or a negative number in case of error.
537 */
538 int mnt_optstr_get_options(const char *optstr, char **subset,
539 const struct libmnt_optmap *map, int ignore)
540 {
541 struct libmnt_optmap const *maps[1];
542 struct ul_buffer buf = UL_INIT_BUFFER;
543 char *name, *val, *str = (char *) optstr;
544 size_t namesz, valsz;
545 int rc = 0;
546
547 if (!optstr || !subset)
548 return -EINVAL;
549
550 maps[0] = map;
551
552 ul_buffer_set_chunksize(&buf, strlen(optstr)/2);
553
554 while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
555 const struct libmnt_optmap *ent;
556
557 mnt_optmap_get_entry(maps, 1, name, namesz, &ent);
558
559 if (!ent || !ent->id)
560 continue; /* ignore undefined options (comments) */
561
562 if (ignore && (ent->mask & ignore))
563 continue;
564
565 /* ignore name=<value> if options map expects <name> only */
566 if (valsz && mnt_optmap_entry_novalue(ent))
567 continue;
568
569 rc = mnt_buffer_append_option(&buf, name, namesz, val, valsz, 0);
570 if (rc)
571 break;
572 }
573
574 *subset = rc ? NULL : ul_buffer_get_data(&buf, NULL, NULL);
575 if (rc)
576 ul_buffer_free_data(&buf);
577 return rc;
578 }
579
580 /*
581 * @optstr: string with comma separated list of options
582 * @wanted: options expected in @optstr
583 * @missing: returns options from @wanted which missing in @optstr (optional)
584 *
585 * Retursn: <0 on error, 0 on missing options, 1 if nothing is missing
586 */
587 int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing)
588 {
589 char *name, *val, *str = (char *) wanted;
590 size_t namesz = 0, valsz = 0;
591 struct ul_buffer buf = UL_INIT_BUFFER;
592 int rc = 0;
593
594 if (!wanted)
595 return 1;
596 if (missing) {
597 /* caller wants data, prepare buffer */
598 ul_buffer_set_chunksize(&buf, strlen(wanted) + 3); /* to call realloc() only once */
599 *missing = NULL;
600 }
601
602 while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
603
604 rc = mnt_optstr_locate_option((char *) optstr, name, namesz, NULL);
605 if (rc == 1) { /* not found */
606 if (!missing)
607 return 0;
608 rc = mnt_buffer_append_option(&buf, name, namesz, val, valsz, 0);
609 }
610 if (rc < 0)
611 break;
612 rc = 0;
613 }
614
615 if (!rc && missing) {
616 if (ul_buffer_is_empty(&buf))
617 rc = 1;
618 else
619 *missing = ul_buffer_get_data(&buf, NULL, NULL);
620 } else
621 ul_buffer_free_data(&buf);
622
623 return rc;
624 }
625
626 /**
627 * mnt_optstr_get_flags:
628 * @optstr: string with comma separated list of options
629 * @flags: returns mount flags
630 * @map: options map
631 *
632 * Returns in @flags IDs of options from @optstr as defined in the @map.
633 *
634 * For example:
635 *
636 * "bind,exec,foo,bar" --returns-> MS_BIND
637 *
638 * "bind,noexec,foo,bar" --returns-> MS_BIND|MS_NOEXEC
639 *
640 * Note that @flags are not zeroized by this function! This function sets/unsets
641 * bits in the @flags only.
642 *
643 * Returns: 0 on success or negative number in case of error
644 */
645 int mnt_optstr_get_flags(const char *optstr, unsigned long *flags,
646 const struct libmnt_optmap *map)
647 {
648 struct libmnt_optmap const *maps[2];
649 char *name, *str = (char *) optstr;
650 size_t namesz = 0, valsz = 0;
651 int nmaps = 0;
652
653 if (!optstr || !flags || !map)
654 return -EINVAL;
655
656 maps[nmaps++] = map;
657
658 if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP))
659 /*
660 * Add userspace map -- the "user" is interpreted as
661 * MS_NO{EXEC,SUID,DEV}.
662 */
663 maps[nmaps++] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
664
665 while(!mnt_optstr_next_option(&str, &name, &namesz, NULL, &valsz)) {
666 const struct libmnt_optmap *ent;
667 const struct libmnt_optmap *m;
668
669 m = mnt_optmap_get_entry(maps, nmaps, name, namesz, &ent);
670 if (!m || !ent || !ent->id)
671 continue;
672
673 /* ignore name=<value> if options map expects <name> only */
674 if (valsz && mnt_optmap_entry_novalue(ent))
675 continue;
676
677 if (m == map) { /* requested map */
678 if (ent->mask & MNT_INVERT)
679 *flags &= ~ent->id;
680 else
681 *flags |= ent->id;
682
683 } else if (nmaps == 2 && m == maps[1] && valsz == 0) {
684 /*
685 * Special case -- translate "user" (but no user=) to
686 * MS_ options
687 */
688 if (ent->mask & MNT_INVERT)
689 continue;
690 if (ent->id & (MNT_MS_OWNER | MNT_MS_GROUP))
691 *flags |= MS_OWNERSECURE;
692 else if (ent->id & (MNT_MS_USER | MNT_MS_USERS))
693 *flags |= MS_SECURE;
694 }
695 }
696
697 return 0;
698 }
699
700 /**
701 * mnt_optstr_apply_flags:
702 * @optstr: string with comma separated list of options
703 * @flags: returns mount flags
704 * @map: options map
705 *
706 * Removes/adds options to the @optstr according to flags. For example:
707 *
708 * MS_NOATIME and "foo,bar,noexec" --returns-> "foo,bar,noatime"
709 *
710 * Returns: 0 on success or negative number in case of error.
711 *
712 * Deprecated: since v2.39.
713 */
714 int mnt_optstr_apply_flags(char **optstr, unsigned long flags,
715 const struct libmnt_optmap *map)
716 {
717 struct libmnt_optmap const *maps[1];
718 char *name, *next, *val;
719 size_t namesz = 0, valsz = 0, multi = 0;
720 unsigned long fl;
721 int rc = 0;
722
723 if (!optstr || !map)
724 return -EINVAL;
725
726 DBG(CXT, ul_debug("applying 0x%08lx flags to '%s'", flags, *optstr));
727
728 maps[0] = map;
729 next = *optstr;
730 fl = flags;
731
732 /*
733 * There is a convention that 'rw/ro' flags are always at the beginning of
734 * the string (although the 'rw' is unnecessary).
735 */
736 if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) {
737 const char *o = (fl & MS_RDONLY) ? "ro" : "rw";
738
739 if (next &&
740 (!strncmp(next, "rw", 2) || !strncmp(next, "ro", 2)) &&
741 (*(next + 2) == '\0' || *(next + 2) == ',')) {
742
743 /* already set, be paranoid and fix it */
744 memcpy(next, o, 2);
745 } else {
746 rc = mnt_optstr_prepend_option(optstr, o, NULL);
747 if (rc)
748 goto err;
749 next = *optstr; /* because realloc() */
750 }
751 fl &= ~MS_RDONLY;
752 next += 2;
753 if (*next == ',')
754 next++;
755 }
756
757 if (next && *next) {
758 /*
759 * scan @optstr and remove options that are missing in
760 * @flags
761 */
762 while(!mnt_optstr_next_option(&next, &name, &namesz,
763 &val, &valsz)) {
764 const struct libmnt_optmap *ent;
765
766 if (mnt_optmap_get_entry(maps, 1, name, namesz, &ent)) {
767 /*
768 * remove unwanted option (rw/ro is already set)
769 */
770 if (!ent || !ent->id)
771 continue;
772 /* ignore name=<value> if options map expects <name> only */
773 if (valsz && mnt_optmap_entry_novalue(ent))
774 continue;
775
776 if (ent->id == MS_RDONLY ||
777 (ent->mask & MNT_INVERT) ||
778 (fl & ent->id) != (unsigned long) ent->id) {
779
780 char *end = val ? val + valsz :
781 name + namesz;
782 next = name;
783 rc = mnt_optstr_remove_option_at(
784 optstr, name, end);
785 if (rc)
786 goto err;
787 }
788 if (!(ent->mask & MNT_INVERT)) {
789 /* allow options with prefix (X-mount.foo,X-mount.bar) more than once */
790 if (ent->mask & MNT_PREFIX)
791 multi |= ent->id;
792 else
793 fl &= ~ent->id;
794 if (ent->id & MS_REC)
795 fl |= MS_REC;
796 }
797 }
798 }
799 }
800
801 /* remove from flags options which are allowed more than once */
802 fl &= ~multi;
803
804 /* add missing options (but ignore fl if contains MS_REC only) */
805 if (fl && fl != MS_REC) {
806
807 const struct libmnt_optmap *ent;
808 struct ul_buffer buf = UL_INIT_BUFFER;
809 size_t sz;
810 char *p;
811
812 ul_buffer_refer_string(&buf, *optstr);
813
814 for (ent = map; ent && ent->name; ent++) {
815 if ((ent->mask & MNT_INVERT)
816 || ent->id == 0
817 || (fl & ent->id) != (unsigned long) ent->id)
818 continue;
819
820 /* don't add options which require values (e.g. offset=%d) */
821 p = strchr(ent->name, '=');
822 if (p) {
823 if (p > ent->name && *(p - 1) == '[')
824 p--; /* name[=] */
825 else
826 continue; /* name= */
827 sz = p - ent->name;
828 } else
829 sz = strlen(ent->name);
830
831 rc = mnt_buffer_append_option(&buf, ent->name, sz, NULL, 0, 0);
832 if (rc)
833 break;
834 }
835
836 if (rc) {
837 ul_buffer_free_data(&buf);
838 goto err;
839 } else
840 *optstr = ul_buffer_get_data(&buf, NULL, NULL);
841 }
842
843 DBG(CXT, ul_debug("new optstr '%s'", *optstr));
844 return rc;
845 err:
846 DBG(CXT, ul_debug("failed to apply flags [rc=%d]", rc));
847 return rc;
848 }
849
850 /**
851 * mnt_match_options:
852 * @optstr: options string
853 * @pattern: comma delimited list of options
854 *
855 * The "no" could be used for individual items in the @options list. The "no"
856 * prefix does not have a global meaning.
857 *
858 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
859 * DIFFERENT meanings; each option is matched explicitly as specified.
860 *
861 * The "no" prefix interpretation could be disabled by the "+" prefix, for example
862 * "+noauto" matches if @optstr literally contains the "noauto" string.
863 *
864 * The alone "no" is error and all matching ends with False.
865 *
866 * "xxx,yyy,zzz" : "nozzz" -> False
867 *
868 * "xxx,yyy,zzz" : "xxx,noeee" -> True
869 *
870 * "bar,zzz" : "nofoo" -> True (does not contain "foo")
871 *
872 * "nofoo,bar" : "nofoo" -> True (does not contain "foo")
873 *
874 * "nofoo,bar" : "+nofoo" -> True (contains "nofoo")
875 *
876 * "bar,zzz" : "+nofoo" -> False (does not contain "nofoo")
877 *
878 * "bar,zzz" : "" or "+" -> True (empty pattern is matching)
879 *
880 * "" : "" -> True
881 *
882 * "" : "foo" -> False
883 *
884 * "" : "nofoo" -> True
885 *
886 * "" : "no,foo" -> False (alone "no" is error)
887 *
888 * "no" : "+no" -> True ("no" is an option due to "+")
889 *
890 * Returns: 1 if pattern is matching, else 0. This function also returns 0
891 * if @pattern is NULL and @optstr is non-NULL.
892 */
893 int mnt_match_options(const char *optstr, const char *pattern)
894 {
895 char *name, *pat = (char *) pattern;
896 char *buf = NULL, *patval;
897 size_t namesz = 0, patvalsz = 0;
898 int match = 1;
899
900 if (!pattern && !optstr)
901 return 1;
902 if (pattern && optstr && !*pattern && !*optstr)
903 return 1;
904 if (!pattern)
905 return 0;
906
907 /* walk on pattern string
908 */
909 while (match && !mnt_optstr_next_option(&pat, &name, &namesz,
910 &patval, &patvalsz)) {
911 char *val;
912 size_t sz = 0;
913 int no = 0, rc;
914
915 if (*name == '+')
916 name++, namesz--;
917 else if ((no = (startswith(name, "no") != NULL))) {
918 name += 2, namesz -= 2;
919 if (!*name || *name == ',') {
920 match = 0;
921 break; /* alone "no" keyword is error */
922 }
923 }
924
925 if (optstr && *optstr && *name) {
926 if (!buf) {
927 buf = malloc(strlen(pattern) + 1);
928 if (!buf)
929 return 0;
930 }
931
932 xstrncpy(buf, name, namesz + 1);
933 rc = mnt_optstr_get_option(optstr, buf, &val, &sz);
934
935 } else if (!*name) {
936 rc = 0; /* empty pattern matches */
937 } else {
938 rc = 1; /* not found in empty string */
939 }
940
941 /* check also value (if the pattern is "foo=value") */
942 if (rc == 0 && patvalsz > 0 &&
943 (patvalsz != sz || strncmp(patval, val, sz) != 0))
944 rc = 1;
945
946 switch (rc) {
947 case 0: /* found */
948 match = no == 0 ? 1 : 0;
949 break;
950 case 1: /* not found */
951 match = no == 1 ? 1 : 0;
952 break;
953 default: /* parse error */
954 match = 0;
955 break;
956 }
957 }
958
959 free(buf);
960 return match;
961 }
962
963 #ifdef TEST_PROGRAM
964 static int test_append(struct libmnt_test *ts __attribute__((unused)),
965 int argc, char *argv[])
966 {
967 const char *value = NULL, *name;
968 char *optstr;
969 int rc;
970
971 if (argc < 3)
972 return -EINVAL;
973 optstr = strdup(argv[1]);
974 if (!optstr)
975 err_oom();
976 name = argv[2];
977
978 if (argc == 4)
979 value = argv[3];
980
981 rc = mnt_optstr_append_option(&optstr, name, value);
982 if (!rc)
983 printf("result: >%s<\n", optstr);
984 free(optstr);
985 return rc;
986 }
987
988 static int test_prepend(struct libmnt_test *ts __attribute__((unused)),
989 int argc, char *argv[])
990 {
991 const char *value = NULL, *name;
992 char *optstr;
993 int rc;
994
995 if (argc < 3)
996 return -EINVAL;
997 optstr = strdup(argv[1]);
998 if (!optstr)
999 err_oom();
1000 name = argv[2];
1001
1002 if (argc == 4)
1003 value = argv[3];
1004
1005 rc = mnt_optstr_prepend_option(&optstr, name, value);
1006 if (!rc)
1007 printf("result: >%s<\n", optstr);
1008 free(optstr);
1009 return rc;
1010 }
1011
1012 static int test_split(struct libmnt_test *ts __attribute__((unused)),
1013 int argc, char *argv[])
1014 {
1015 char *optstr, *user = NULL, *fs = NULL, *vfs = NULL;
1016 int rc;
1017
1018 if (argc < 2)
1019 return -EINVAL;
1020
1021 optstr = strdup(argv[1]);
1022 if (!optstr)
1023 err_oom();
1024
1025 rc = mnt_split_optstr(optstr, &user, &vfs, &fs, 0, 0);
1026 if (!rc) {
1027 printf("user : %s\n", user);
1028 printf("vfs : %s\n", vfs);
1029 printf("fs : %s\n", fs);
1030 }
1031
1032 free(user);
1033 free(vfs);
1034 free(fs);
1035 free(optstr);
1036 return rc;
1037 }
1038
1039 static int test_flags(struct libmnt_test *ts __attribute__((unused)),
1040 int argc, char *argv[])
1041 {
1042 char *optstr;
1043 int rc;
1044 unsigned long fl = 0;
1045
1046 if (argc < 2)
1047 return -EINVAL;
1048
1049 optstr = strdup(argv[1]);
1050 if (!optstr)
1051 err_oom();
1052
1053 rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_LINUX_MAP));
1054 if (rc)
1055 return rc;
1056 printf("mountflags: 0x%08lx\n", fl);
1057
1058 fl = 0;
1059 rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
1060 if (rc)
1061 return rc;
1062 printf("userspace-mountflags: 0x%08lx\n", fl);
1063
1064 free(optstr);
1065 return rc;
1066 }
1067
1068 static int test_apply(struct libmnt_test *ts __attribute__((unused)),
1069 int argc, char *argv[])
1070 {
1071 char *optstr;
1072 int rc, map;
1073 unsigned long flags;
1074
1075 if (argc < 4)
1076 return -EINVAL;
1077
1078 if (!strcmp(argv[1], "--user"))
1079 map = MNT_USERSPACE_MAP;
1080 else if (!strcmp(argv[1], "--linux"))
1081 map = MNT_LINUX_MAP;
1082 else {
1083 fprintf(stderr, "unknown option '%s'\n", argv[1]);
1084 return -EINVAL;
1085 }
1086
1087 optstr = strdup(argv[2]);
1088 if (!optstr)
1089 err_oom();
1090 flags = strtoul(argv[3], NULL, 16);
1091
1092 printf("flags: 0x%08lx\n", flags);
1093
1094 rc = mnt_optstr_apply_flags(&optstr, flags, mnt_get_builtin_optmap(map));
1095 printf("optstr: %s\n", optstr);
1096
1097 free(optstr);
1098 return rc;
1099 }
1100
1101 static int test_set(struct libmnt_test *ts __attribute__((unused)),
1102 int argc, char *argv[])
1103 {
1104 const char *value = NULL, *name;
1105 char *optstr;
1106 int rc;
1107
1108 if (argc < 3)
1109 return -EINVAL;
1110 optstr = strdup(argv[1]);
1111 if (!optstr)
1112 err_oom();
1113 name = argv[2];
1114
1115 if (argc == 4)
1116 value = argv[3];
1117
1118 rc = mnt_optstr_set_option(&optstr, name, value);
1119 if (!rc)
1120 printf("result: >%s<\n", optstr);
1121 free(optstr);
1122 return rc;
1123 }
1124
1125 static int test_get(struct libmnt_test *ts __attribute__((unused)),
1126 int argc, char *argv[])
1127 {
1128 char *optstr;
1129 const char *name;
1130 char *val = NULL;
1131 size_t sz = 0;
1132 int rc;
1133
1134 if (argc < 2)
1135 return -EINVAL;
1136 optstr = argv[1];
1137 name = argv[2];
1138
1139 rc = mnt_optstr_get_option(optstr, name, &val, &sz);
1140 if (rc == 0) {
1141 printf("found; name: %s", name);
1142 if (sz) {
1143 printf(", argument: size=%zd data=", sz);
1144 if (fwrite(val, 1, sz, stdout) != sz)
1145 return -1;
1146 }
1147 printf("\n");
1148 } else if (rc == 1)
1149 printf("%s: not found\n", name);
1150 else
1151 printf("parse error: %s\n", optstr);
1152 return rc;
1153 }
1154
1155 static int test_missing(struct libmnt_test *ts __attribute__((unused)),
1156 int argc, char *argv[])
1157 {
1158 const char *optstr;
1159 const char *wanted;
1160 char *missing = NULL;
1161 int rc;
1162
1163 if (argc < 2)
1164 return -EINVAL;
1165 optstr = argv[1];
1166 wanted = argv[2];
1167
1168 rc = mnt_optstr_get_missing(optstr, wanted, &missing);
1169 if (rc == 0)
1170 printf("missing: %s\n", missing);
1171 else if (rc == 1) {
1172 printf("nothing\n");
1173 rc = 0;
1174 } else
1175 printf("parse error: %s\n", optstr);
1176 return rc;
1177 }
1178
1179 static int test_remove(struct libmnt_test *ts __attribute__((unused)),
1180 int argc, char *argv[])
1181 {
1182 const char *name;
1183 char *optstr;
1184 int rc;
1185
1186 if (argc < 3)
1187 return -EINVAL;
1188 optstr = strdup(argv[1]);
1189 if (!optstr)
1190 err_oom();
1191 name = argv[2];
1192
1193 rc = mnt_optstr_remove_option(&optstr, name);
1194 if (!rc)
1195 printf("result: >%s<\n", optstr);
1196 free(optstr);
1197 return rc;
1198 }
1199
1200 static int test_dedup(struct libmnt_test *ts __attribute__((unused)),
1201 int argc, char *argv[])
1202 {
1203 const char *name;
1204 char *optstr;
1205 int rc;
1206
1207 if (argc < 3)
1208 return -EINVAL;
1209 optstr = strdup(argv[1]);
1210 if (!optstr)
1211 err_oom();
1212 name = argv[2];
1213
1214 rc = mnt_optstr_deduplicate_option(&optstr, name);
1215 if (!rc)
1216 printf("result: >%s<\n", optstr);
1217 free(optstr);
1218 return rc;
1219 }
1220
1221 static int test_match(struct libmnt_test *ts __attribute__((unused)),
1222 int argc, char *argv[])
1223 {
1224 char *optstr, *pattern;
1225
1226 if (argc < 3)
1227 return -EINVAL;
1228
1229 optstr = argv[1];
1230 pattern = argv[2];
1231 printf("%-6s: \"%s\"\t:\t\"%s\"\n",
1232 mnt_match_options(optstr, pattern) == 1 ? "true" : "false",
1233 optstr, pattern);
1234 return 0;
1235 }
1236
1237 int main(int argc, char *argv[])
1238 {
1239 struct libmnt_test tss[] = {
1240 { "--append", test_append, "<optstr> <name> [<value>] append value to optstr" },
1241 { "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" },
1242 { "--set", test_set, "<optstr> <name> [<value>] (un)set value" },
1243 { "--get", test_get, "<optstr> <name> search name in optstr" },
1244 { "--missing",test_missing,"<optstr> <wanted> what from wanted is missing" },
1245 { "--remove", test_remove, "<optstr> <name> remove name in optstr" },
1246 { "--dedup", test_dedup, "<optstr> <name> deduplicate name in optstr" },
1247 { "--match", test_match, "<optstr> <pattern> compare optstr with pattern" },
1248 { "--split", test_split, "<optstr> split into FS, VFS and userspace" },
1249 { "--flags", test_flags, "<optstr> convert options to MS_* flags" },
1250 { "--apply", test_apply, "--{linux,user} <optstr> <mask> apply mask to optstr" },
1251
1252 { NULL }
1253 };
1254 return mnt_run_test(tss, argc, argv);
1255 }
1256 #endif /* TEST_PROGRAM */