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