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) 2022 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 * The "optlist" is container for parsed mount options.
14 *
15 */
16 #include "strutils.h"
17 #include "list.h"
18 #include "mountP.h"
19 #include "mount-api-utils.h"
20
21 #define MNT_OL_MAXMAPS 8
22
23 enum libmnt_optsrc {
24 MNT_OPTSRC_STRING,
25 MNT_OPTSRC_FLAG
26 };
27
28 struct optlist_cache {
29 unsigned long flags;
30 char *optstr;
31
32 unsigned int flags_ready: 1,
33 optstr_ready : 1;
34 };
35
36 struct libmnt_opt {
37 char *name;
38 char *value;
39
40 struct list_head opts; /* libmnt_optlist->opts member */
41
42 const struct libmnt_optmap *map;
43 const struct libmnt_optmap *ent; /* map entry */
44
45 enum libmnt_optsrc src;
46
47 unsigned int external : 1, /* visible for external helpers only */
48 recursive : 1, /* recursive flag */
49 is_linux : 1, /* defined in ls->linux_map (VFS attr) */
50 quoted : 1; /* name="value" */
51 };
52
53 struct libmnt_optlist {
54 int refcount;
55 unsigned int age; /* incremented after each change */
56
57 const struct libmnt_optmap *linux_map; /* map with MS_ flags */
58 const struct libmnt_optmap *maps[MNT_OL_MAXMAPS];
59 size_t nmaps;
60
61 struct optlist_cache cache_mapped[MNT_OL_MAXMAPS]; /* cache by map */
62 struct optlist_cache cache_all[__MNT_OL_FLTR_COUNT]; /* from all maps, unknown, external, ... */
63
64 unsigned long propagation; /* propagation MS_ flags */
65 struct list_head opts; /* parsed options */
66
67 unsigned int merged : 1, /* don't care about MNT_OPTSRC_* */
68 is_remount : 1,
69 is_bind : 1,
70 is_rbind : 1,
71 is_rdonly : 1,
72 is_move : 1,
73 is_silent : 1,
74 is_recursive : 1;
75 };
76
77 struct libmnt_optlist *mnt_new_optlist(void)
78 {
79 struct libmnt_optlist *ls = calloc(1, sizeof(*ls));
80
81 if (!ls)
82 return NULL;
83
84 ls->refcount = 1;
85 INIT_LIST_HEAD(&ls->opts);
86
87 ls->linux_map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
88
89 DBG(OPTLIST, ul_debugobj(ls, "alloc"));
90 return ls;
91 }
92
93 void mnt_ref_optlist(struct libmnt_optlist *ls)
94 {
95 if (ls)
96 ls->refcount++;
97 }
98
99 static void reset_cache(struct optlist_cache *cache)
100 {
101 if (!cache || (cache->flags_ready == 0 && cache->optstr_ready == 0))
102 return;
103 free(cache->optstr);
104 memset(cache, 0, sizeof(*cache));
105 }
106
107 void mnt_unref_optlist(struct libmnt_optlist *ls)
108 {
109 size_t i;
110
111 if (!ls)
112 return;
113
114 ls->refcount--;
115 if (ls->refcount > 0)
116 return;
117
118 while (!list_empty(&ls->opts)) {
119 struct libmnt_opt *opt = list_entry(ls->opts.next, struct libmnt_opt, opts);
120 mnt_optlist_remove_opt(ls, opt);
121 }
122
123 for (i = 0; i < ls->nmaps; i++)
124 reset_cache(&ls->cache_mapped[i]);
125
126 for (i = 0; i < __MNT_OL_FLTR_COUNT; i++)
127 reset_cache(&ls->cache_all[i]);
128
129 free(ls);
130 }
131
132 int mnt_optlist_register_map(struct libmnt_optlist *ls, const struct libmnt_optmap *map)
133 {
134 size_t i;
135
136 if (!ls || !map)
137 return -EINVAL;
138
139 for (i = 0; i < ls->nmaps; i++) {
140 if (ls->maps[i] == map)
141 return 0; /* already registred, ignore */
142 }
143 if (ls->nmaps + 1 >= MNT_OL_MAXMAPS)
144 return -ERANGE;
145
146 DBG(OPTLIST, ul_debugobj(ls, "registr map %p", map));
147 ls->maps[ls->nmaps++] = map;
148 return 0;
149 }
150
151 static size_t optlist_get_mapidx(struct libmnt_optlist *ls, const struct libmnt_optmap *map)
152 {
153 size_t i;
154
155 assert(ls);
156 assert(map);
157
158 for (i = 0; i < ls->nmaps; i++)
159 if (map == ls->maps[i])
160 return i;
161
162 return (size_t) -1;
163 }
164
165 static void optlist_cleanup_cache(struct libmnt_optlist *ls)
166 {
167 size_t i;
168
169 ls->age++;
170
171 if (list_empty(&ls->opts))
172 return;
173
174 for (i = 0; i < ARRAY_SIZE(ls->cache_mapped); i++)
175 reset_cache(&ls->cache_mapped[i]);
176
177 for (i = 0; i < __MNT_OL_FLTR_COUNT; i++)
178 reset_cache(&ls->cache_all[i]);
179 }
180
181 int mnt_optlist_remove_opt(struct libmnt_optlist *ls, struct libmnt_opt *opt)
182 {
183 if (!opt)
184 return -EINVAL;
185
186 DBG(OPTLIST, ul_debugobj(ls, " remove %s", opt->name));
187
188 if (opt->map && opt->ent && opt->map == ls->linux_map) {
189 if (opt->ent->id & MS_PROPAGATION)
190 ls->propagation &= ~opt->ent->id;
191 else if (opt->ent->id == MS_REMOUNT)
192 ls->is_remount = 0;
193 else if (opt->ent->id == (MS_BIND|MS_REC))
194 ls->is_rbind = 0;
195 else if (opt->ent->id == MS_BIND)
196 ls->is_bind = 0;
197 else if (opt->ent->id == MS_RDONLY)
198 ls->is_rdonly = 0;
199 else if (opt->ent->id == MS_MOVE)
200 ls->is_move = 0;
201 else if (opt->ent->id == MS_SILENT)
202 ls->is_silent = 0;
203
204 if (opt->ent->id & MS_REC)
205 ls->is_recursive = 0;
206 }
207
208 optlist_cleanup_cache(ls);
209
210 list_del_init(&opt->opts);
211 free(opt->value);
212 free(opt->name);
213 free(opt);
214
215 return 0;
216 }
217
218 int mnt_optlist_remove_named(struct libmnt_optlist *ls, const char *name,
219 const struct libmnt_optmap *map)
220 {
221 struct libmnt_opt *opt = mnt_optlist_get_named(ls, name, map);
222
223 return opt ? mnt_optlist_remove_opt(ls, opt) : 0;
224 }
225
226 int mnt_optlist_next_opt(struct libmnt_optlist *ls,
227 struct libmnt_iter *itr, struct libmnt_opt **opt)
228 {
229 int rc = 1;
230
231 if (!ls || !itr)
232 return -EINVAL;
233 if (opt)
234 *opt = NULL;
235
236 if (!itr->head)
237 MNT_ITER_INIT(itr, &ls->opts);
238 if (itr->p != itr->head) {
239 if (opt)
240 *opt = MNT_ITER_GET_ENTRY(itr, struct libmnt_opt, opts);
241 MNT_ITER_ITERATE(itr);
242 rc = 0;
243 }
244
245 return rc;
246 }
247
248 struct libmnt_opt *mnt_optlist_get_opt(struct libmnt_optlist *ls,
249 unsigned long id, const struct libmnt_optmap *map)
250 {
251 struct libmnt_iter itr;
252 struct libmnt_opt *opt;
253
254 if (!ls || !map)
255 return NULL;
256
257 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
258
259 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
260 if (opt->external)
261 continue;
262 if (map && opt->map != map)
263 continue;
264 if (opt->ent && (unsigned long) opt->ent->id == id)
265 return opt;
266 }
267
268 return NULL;
269 }
270
271 struct libmnt_opt *mnt_optlist_get_named(struct libmnt_optlist *ls,
272 const char *name, const struct libmnt_optmap *map)
273 {
274 struct libmnt_iter itr;
275 struct libmnt_opt *opt;
276
277 if (!ls || !name)
278 return NULL;
279
280 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
281
282 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
283 if (opt->external)
284 continue;
285 if (map && map != opt->map)
286 continue;
287 if (opt->name && strcmp(opt->name, name) == 0)
288 return opt;
289 }
290
291 return NULL;
292 }
293
294 static int is_equal_opts(struct libmnt_opt *a, struct libmnt_opt *b)
295 {
296 if (a->map != b->map)
297 return 0;
298 if (a->ent && b->ent && a->ent != b->ent)
299 return 0;
300 if ((a->value && !b->value) || (!a->value && b->value))
301 return 0;
302 if (strcmp(a->name, b->name) != 0)
303 return 0;
304 if (a->value && b->value && strcmp(a->value, b->value) != 0)
305 return 0;
306
307 return 1;
308 }
309
310 int mnt_optlist_merge_opts(struct libmnt_optlist *ls)
311 {
312 struct libmnt_iter itr;
313 struct libmnt_opt *opt;
314
315 if (!ls)
316 return -EINVAL;
317
318 DBG(OPTLIST, ul_debugobj(ls, "merging"));
319 ls->merged = 1;
320
321 /* deduplicate, keep last instance of the option only */
322 mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
323
324 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
325 struct libmnt_iter xtr;
326 struct libmnt_opt *x;
327
328 mnt_reset_iter(&xtr, MNT_ITER_FORWARD);
329 while (mnt_optlist_next_opt(ls, &xtr, &x) == 0) {
330 int rem = 0;
331
332 if (opt == x)
333 break; /* no another instance */
334
335 /* remove duplicate option */
336 if (is_equal_opts(opt, x))
337 rem = 1;
338
339 /* remove inverted option */
340 else if (opt->ent && x->ent
341 && opt->ent->id == x->ent->id
342 && (opt->ent->mask & MNT_INVERT
343 || x->ent->mask & MNT_INVERT))
344 rem = 1;
345
346 if (rem) {
347 /* me sure @itr does not point to removed item */
348 if (itr.p == &x->opts)
349 itr.p = x->opts.prev;
350 mnt_optlist_remove_opt(ls, x);
351 }
352
353 }
354 }
355
356 return 0;
357 }
358
359 #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
360 static inline int flag_to_attr(unsigned long flag, uint64_t *attr)
361 {
362 uint64_t a = 0;
363
364 switch (flag) {
365 case MS_RDONLY:
366 a = MOUNT_ATTR_RDONLY;
367 break;
368 case MS_NOSUID:
369 a = MOUNT_ATTR_NOSUID;
370 break;
371 case MS_NODEV:
372 a = MOUNT_ATTR_NODEV;
373 break;
374 case MS_NOEXEC:
375 a = MOUNT_ATTR_NOEXEC;
376 break;
377 case MS_NODIRATIME:
378 a = MOUNT_ATTR_NODIRATIME;
379 break;
380 case MS_RELATIME:
381 a = MOUNT_ATTR_RELATIME;
382 break;
383 case MS_NOATIME:
384 a = MOUNT_ATTR_NOATIME;
385 break;
386 case MS_STRICTATIME:
387 a = MOUNT_ATTR_STRICTATIME;
388 break;
389 case MS_NOSYMFOLLOW:
390 a = MOUNT_ATTR_NOSYMFOLLOW;
391 break;
392 default:
393 return -1;
394 }
395
396 if (attr)
397 *attr = a;
398 return 0;
399 }
400
401 /*
402 * Is the @opt relevant for mount_setattr() ?
403 */
404 static inline int is_vfs_opt(struct libmnt_opt *opt)
405 {
406 if (!opt->map || !opt->ent || !opt->ent->id || !opt->is_linux)
407 return 0;
408
409 return flag_to_attr(opt->ent->id, NULL) < 0 ? 0 : 1;
410 }
411 #endif
412
413 static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
414 const char *name, size_t namesz,
415 const char *value, size_t valsz,
416 const struct libmnt_optmap *map,
417 const struct libmnt_optmap *ent,
418 struct list_head *where)
419
420 {
421 struct libmnt_opt *opt;
422
423 opt = calloc(1, sizeof(*opt));
424 if (!opt)
425 return NULL;
426
427 INIT_LIST_HEAD(&opt->opts);
428 opt->map = map;
429 opt->ent = ent;
430
431 if (valsz) {
432 if (*value == '"' && *(value + valsz - 1) == '"') {
433 opt->quoted = 1;
434 value++;
435 valsz -= 2;
436 }
437 opt->value = strndup(value, valsz);
438 if (!opt->value)
439 goto fail;
440 }
441 if (namesz) {
442 opt->name = strndup(name, namesz);
443 if (!opt->name)
444 goto fail;
445 }
446
447 if (where)
448 list_add(&opt->opts, where);
449 else
450 list_add_tail(&opt->opts, &ls->opts);
451
452 /* shortcuts */
453 if (map && ent && map == ls->linux_map) {
454 opt->is_linux = 1;
455
456 if (ent->id & MS_PROPAGATION)
457 ls->propagation |= ent->id;
458 else if (opt->ent->id == MS_REMOUNT)
459 ls->is_remount = 1;
460 else if (opt->ent->id == (MS_REC|MS_BIND))
461 ls->is_rbind = 1;
462 else if (opt->ent->id == MS_BIND)
463 ls->is_bind = 1;
464 else if (opt->ent->id == MS_RDONLY)
465 ls->is_rdonly = 1;
466 else if (opt->ent->id == MS_MOVE)
467 ls->is_move = 1;
468 else if (opt->ent->id == MS_SILENT)
469 ls->is_silent = 1;
470
471 if (opt->ent->id & MS_REC) {
472 ls->is_recursive = 1;
473 opt->recursive = 1;
474 }
475 }
476 #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
477 if (!opt->recursive && opt->value
478 && is_vfs_opt(opt) && strcmp(opt->value, "recursive") == 0)
479 opt->recursive = 1;
480 #endif
481 if (ent && map) {
482 DBG(OPTLIST, ul_debugobj(ls, " added %s [id=0x%08x map=%p]",
483 opt->name, ent->id, map));
484 } else {
485 DBG(OPTLIST, ul_debugobj(ls, " added %s", opt->name));
486 }
487 return opt;
488 fail:
489 mnt_optlist_remove_opt(ls, opt);
490 return NULL;
491 }
492
493 static int optlist_add_optstr(struct libmnt_optlist *ls, const char *optstr,
494 const struct libmnt_optmap *map, struct list_head *where)
495 {
496 char *p = (char *) optstr, *name, *val;
497 size_t namesz, valsz;
498 int rc;
499
500 if (!ls)
501 return -EINVAL;
502 if (map && (rc = mnt_optlist_register_map(ls, map)))
503 return rc;
504 if (!optstr)
505 return 0;
506
507 while (ul_optstr_next(&p, &name, &namesz, &val, &valsz) == 0) {
508
509 struct libmnt_opt *opt;
510 const struct libmnt_optmap *e = NULL, *m = NULL;
511
512 if (map)
513 m = mnt_optmap_get_entry(&map, 1, name, namesz, &e);
514 if (!m && ls->nmaps)
515 m = mnt_optmap_get_entry(ls->maps, ls->nmaps, name, namesz, &e);
516
517 /* TODO: add the option more than once if belongs to the more maps */
518
519 opt = optlist_new_opt(ls, name, namesz, val, valsz, m, e, where);
520 if (!opt)
521 return -ENOMEM;
522 opt->src = MNT_OPTSRC_STRING;
523 }
524
525 optlist_cleanup_cache(ls);
526
527 return 0;
528 }
529
530 /*
531 * The library differentiate between options specified by flags and strings by
532 * default. In this case mnt_optlist_set_optstr() replaces all options
533 * specified by strings for the @map or for all maps if @map is NULL
534 *
535 * If optlist is marked as merged by mnt_optlist_set_merged() than this
536 * function replaced all options for the @map or for all maps if @map is NULL.
537 */
538 int mnt_optlist_set_optstr(struct libmnt_optlist *ls, const char *optstr,
539 const struct libmnt_optmap *map)
540 {
541 struct list_head *p, *next;
542
543 if (!ls)
544 return -EINVAL;
545
546 DBG(OPTLIST, ul_debugobj(ls, "set %s", optstr));
547
548 /* remove all already set options */
549 list_for_each_safe(p, next, &ls->opts) {
550 struct libmnt_opt *opt = list_entry(p, struct libmnt_opt, opts);
551
552 if (opt->external)
553 continue;
554 if (map && opt->map != map)
555 continue;
556 if (ls->merged || opt->src == MNT_OPTSRC_STRING)
557 mnt_optlist_remove_opt(ls, opt);
558 }
559
560 return optlist_add_optstr(ls, optstr, map, NULL);
561 }
562
563 int mnt_optlist_append_optstr(struct libmnt_optlist *ls, const char *optstr,
564 const struct libmnt_optmap *map)
565 {
566 if (!ls)
567 return -EINVAL;
568
569 DBG(OPTLIST, ul_debugobj(ls, "append %s", optstr));
570 return optlist_add_optstr(ls, optstr, map, NULL);
571 }
572
573 int mnt_optlist_prepend_optstr(struct libmnt_optlist *ls, const char *optstr,
574 const struct libmnt_optmap *map)
575 {
576 if (!ls)
577 return -EINVAL;
578
579 DBG(OPTLIST, ul_debugobj(ls, "prepend %s", optstr));
580 return optlist_add_optstr(ls, optstr, map, &ls->opts);
581 }
582
583 static int optlist_add_flags(struct libmnt_optlist *ls, unsigned long flags,
584 const struct libmnt_optmap *map, struct list_head *where)
585 {
586 const struct libmnt_optmap *ent;
587 int rc;
588
589 if (!ls || !map)
590 return -EINVAL;
591
592 if (map && (rc = mnt_optlist_register_map(ls, map)))
593 return rc;
594
595 for (ent = map; ent && ent->name; ent++) {
596
597 char *p;
598 size_t sz;
599 struct libmnt_opt *opt;
600
601 if ((ent->mask & MNT_INVERT)
602 || ent->name == NULL
603 || ent->id == 0
604 || (flags & ent->id) != (unsigned long) ent->id)
605 continue;
606
607 /* don't add options which require values (e.g. offset=%d) */
608 p = strchr(ent->name, '=');
609 if (p) {
610 if (p > ent->name && *(p - 1) == '[')
611 p--; /* name[=] */
612 else
613 continue; /* name=<value> */
614 sz = p - ent->name;
615 p -= sz;
616 } else {
617 p = (char *) ent->name;
618 sz = strlen(ent->name); /* alone "name" */
619 }
620
621 opt = optlist_new_opt(ls, p, sz, NULL, 0, map, ent, where);
622 if (!opt)
623 return -ENOMEM;
624 opt->src = MNT_OPTSRC_FLAG;
625 }
626
627 optlist_cleanup_cache(ls);
628
629 return 0;
630 }
631
632 int mnt_optlist_append_flags(struct libmnt_optlist *ls, unsigned long flags,
633 const struct libmnt_optmap *map)
634 {
635 if (!ls || !map)
636 return -EINVAL;
637
638 DBG(OPTLIST, ul_debugobj(ls, "append 0x%08lx", flags));
639 return optlist_add_flags(ls, flags, map, NULL);
640 }
641
642
643 int mnt_optlist_set_flags(struct libmnt_optlist *ls, unsigned long flags,
644 const struct libmnt_optmap *map)
645 {
646 struct list_head *p, *next;
647
648 if (!ls || !map)
649 return -EINVAL;
650
651 DBG(OPTLIST, ul_debugobj(ls, "set 0x%08lx", flags));
652
653 /* remove all already set options */
654 list_for_each_safe(p, next, &ls->opts) {
655 struct libmnt_opt *opt = list_entry(p, struct libmnt_opt, opts);
656
657 if (opt->external)
658 continue;
659 if (map && opt->map != map)
660 continue;
661 if (ls->merged || opt->src == MNT_OPTSRC_FLAG)
662 mnt_optlist_remove_opt(ls, opt);
663 }
664
665 return mnt_optlist_append_flags(ls, flags, map);
666 }
667
668 int mnt_optlist_remove_flags(struct libmnt_optlist *ls, unsigned long flags,
669 const struct libmnt_optmap *map)
670 {
671 struct list_head *p, *next;
672
673 if (!ls || !map)
674 return -EINVAL;
675
676 DBG(OPTLIST, ul_debugobj(ls, "remove 0x%08lx", flags));
677
678 list_for_each_safe(p, next, &ls->opts) {
679 struct libmnt_opt *opt = list_entry(p, struct libmnt_opt, opts);
680
681 if (opt->external || !opt->ent)
682 continue;
683 if (map && opt->map != map)
684 continue;
685 if (opt->ent->id & flags)
686 mnt_optlist_remove_opt(ls, opt);
687 }
688 return 0;
689 }
690
691 int mnt_optlist_insert_flags(struct libmnt_optlist *ls, unsigned long flags,
692 const struct libmnt_optmap *map,
693 unsigned long after,
694 const struct libmnt_optmap *after_map)
695 {
696 struct libmnt_opt *opt;
697
698 if (!ls || !map || !after || !after_map)
699 return -EINVAL;
700
701 opt = mnt_optlist_get_opt(ls, after, after_map);
702 if (!opt)
703 return -EINVAL;
704
705 DBG(OPTLIST, ul_debugobj(ls, "insert 0x%08lx (after %s)",
706 flags, opt->ent ? opt->ent->name : "???"));
707
708 return optlist_add_flags(ls, flags, map, &opt->opts);
709 }
710
711 static int is_wanted_opt(struct libmnt_opt *opt, const struct libmnt_optmap *map,
712 unsigned int what)
713 {
714 switch (what) {
715 case MNT_OL_FLTR_DFLT:
716 if (opt->external)
717 return 0;
718 if (map && opt->map != map)
719 return 0;
720 break;
721 case MNT_OL_FLTR_ALL:
722 break;
723 case MNT_OL_FLTR_UNKNOWN:
724 if (opt->map || opt->external)
725 return 0;
726 break;
727 case MNT_OL_FLTR_HELPERS:
728 if (opt->ent && opt->ent->mask & MNT_NOHLPS)
729 return 0;
730 break;
731 case MNT_OL_FLTR_MTAB:
732 if (opt->ent && opt->ent->mask & MNT_NOMTAB)
733 return 0;
734 break;
735 }
736
737 return 1;
738 }
739
740 static struct optlist_cache *get_cache( struct libmnt_optlist *ls,
741 const struct libmnt_optmap *map,
742 unsigned int what)
743 {
744 switch (what) {
745 case MNT_OL_FLTR_DFLT:
746 if (map) {
747 const size_t idx = optlist_get_mapidx(ls, map);
748 if (idx == (size_t) -1)
749 return NULL;
750 return &ls->cache_mapped[idx];
751 }
752 return &ls->cache_all[MNT_OL_FLTR_DFLT];
753
754 case MNT_OL_FLTR_ALL:
755 case MNT_OL_FLTR_UNKNOWN:
756 case MNT_OL_FLTR_HELPERS:
757 case MNT_OL_FLTR_MTAB:
758 return &ls->cache_all[what];
759
760 default:
761 break;
762 }
763
764 return NULL;
765 }
766
767 /*
768 * Returns flags (bit mask from options map entries).
769 */
770 int mnt_optlist_get_flags(struct libmnt_optlist *ls, unsigned long *flags,
771 const struct libmnt_optmap *map, unsigned int what)
772 {
773 struct optlist_cache *cache;
774
775 if (!ls || !map || !flags)
776 return -EINVAL;
777
778 cache = get_cache(ls, map, what);
779 if (!cache)
780 return -EINVAL;
781
782 if (!cache->flags_ready) {
783 struct libmnt_iter itr;
784 struct libmnt_opt *opt;
785 unsigned long fl = 0;
786
787 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
788
789 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
790 if (map != opt->map)
791 continue;
792 if (!opt->ent || !opt->ent->id)
793 continue;
794 if (!is_wanted_opt(opt, map, what))
795 continue;
796
797 if (opt->ent->mask & MNT_INVERT)
798 fl &= ~opt->ent->id;
799 else
800 fl |= opt->ent->id;
801 }
802
803 cache->flags = fl;
804 cache->flags_ready = 1;
805 }
806
807 *flags = cache->flags;
808
809 DBG(OPTLIST, ul_debugobj(ls, "return flags 0x%08lx [map=%p]", *flags, map));
810 return 0;
811 }
812
813
814 /*
815 * Like mnt_optlist_get_flags() for VFS flags, but converts classic MS_* flags to
816 * new MOUNT_ATTR_*
817 */
818 #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
819
820 #define MNT_RESETABLE_ATTRS (MOUNT_ATTR_RDONLY| MOUNT_ATTR_NOSUID| \
821 MOUNT_ATTR_NODEV | MOUNT_ATTR_NOEXEC| \
822 MOUNT_ATTR_NOATIME| MOUNT_ATTR_NODIRATIME | \
823 MOUNT_ATTR_NOSYMFOLLOW)
824
825 int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *clr, int rec)
826 {
827 struct libmnt_iter itr;
828 struct libmnt_opt *opt;
829 uint64_t remount_reset = 0;
830
831 if (!ls || !ls->linux_map || !set || !clr)
832 return -EINVAL;
833
834 *set = 0, *clr = 0;
835 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
836
837 /* The classic mount(2) MS_REMOUNT resets all flags which are not
838 * specified (except atime stuff). For backward compatibility we need
839 * to emulate this semantic by mount_setattr(). The new
840 * mount_setattr() has simple set/unset sematinc and nothing is
841 * internally in kernel reseted.
842 */
843 if (mnt_optlist_is_remount(ls)
844 && !mnt_optlist_is_bind(ls)
845 && rec == MNT_OL_NOREC)
846 remount_reset = (MOUNT_ATTR_RDONLY| MOUNT_ATTR_NOSUID| \
847 MOUNT_ATTR_NODEV | MOUNT_ATTR_NOEXEC| \
848 MOUNT_ATTR_NOSYMFOLLOW);
849
850 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
851 uint64_t x = 0;
852
853 if (ls->linux_map != opt->map)
854 continue;
855 if (!opt->ent || !opt->ent->id)
856 continue;
857
858 if (rec == MNT_OL_REC && !opt->recursive)
859 continue;
860 if (rec == MNT_OL_NOREC && opt->recursive)
861 continue;
862
863 if (!is_wanted_opt(opt, ls->linux_map, MNT_OL_FLTR_DFLT))
864 continue;
865 if (flag_to_attr( opt->ent->id, &x) < 0)
866 continue;
867
868 if (x && remount_reset)
869 remount_reset &= ~x;
870
871 if (opt->ent->mask & MNT_INVERT) {
872 DBG(OPTLIST, ul_debugobj(ls, " clr: %s", opt->ent->name));
873 *clr |= x;
874 } else {
875 DBG(OPTLIST, ul_debugobj(ls, " set: %s", opt->ent->name));
876 *set |= x;
877
878 if (x == MOUNT_ATTR_RELATIME || x == MOUNT_ATTR_NOATIME ||
879 x == MOUNT_ATTR_STRICTATIME)
880 *clr |= MOUNT_ATTR__ATIME;
881 }
882 }
883
884 if (remount_reset)
885 *clr |= remount_reset;
886
887 DBG(OPTLIST, ul_debugobj(ls, "return attrs set=0x%08" PRIx64
888 ", clr=0x%08" PRIx64 " %s",
889 *set, *clr,
890 rec == MNT_OL_REC ? "[rec]" :
891 rec == MNT_OL_NOREC ? "[norec]" : ""));
892 return 0;
893 }
894
895 #else
896 int mnt_optlist_get_attrs(struct libmnt_optlist *ls __attribute__((__unused__)),
897 uint64_t *set __attribute__((__unused__)),
898 uint64_t *clr __attribute__((__unused__)),
899 int mask __attribute__((__unused__)))
900 {
901 return 0;
902 }
903 #endif /* USE_LIBMOUNT_MOUNTFD_SUPPORT */
904
905 int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr,
906 const struct libmnt_optmap *map, unsigned int what)
907 {
908 struct libmnt_iter itr;
909 struct libmnt_opt *opt;
910 struct ul_buffer buf = UL_INIT_BUFFER;
911 char *str = NULL;
912 int rc = 0, is_rdonly = 0, xx_wanted = 0;
913
914 if (!ls || !optstr)
915 return -EINVAL;
916
917 *optstr = NULL;
918
919 /* For generic options srings ro/rw is expected at the begining */
920 if ((!map || map == ls->linux_map)
921 && (what == MNT_OL_FLTR_DFLT ||
922 what == MNT_OL_FLTR_ALL ||
923 what == MNT_OL_FLTR_HELPERS)) {
924
925 rc = mnt_buffer_append_option(&buf, "rw", 2, NULL, 0, 0);
926 if (rc)
927 goto fail;
928 xx_wanted = 1;
929 }
930
931 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
932
933 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
934 if (!opt->name)
935 continue;
936 if (opt->map == ls->linux_map && opt->ent->id == MS_RDONLY) {
937 is_rdonly = opt->ent->mask & MNT_INVERT ? 0 : 1;
938 continue;
939 }
940 if (!is_wanted_opt(opt, map, what))
941 continue;
942 rc = mnt_buffer_append_option(&buf,
943 opt->name, strlen(opt->name),
944 opt->value,
945 opt->value ? strlen(opt->value) : 0,
946 opt->quoted);
947 if (rc)
948 goto fail;
949 }
950
951 str = ul_buffer_get_data(&buf, NULL, NULL);
952
953 /* convert 'rw' at the beginning to 'ro' if necessary */
954 if (str && is_rdonly && xx_wanted
955 && (what == MNT_OL_FLTR_DFLT ||
956 what == MNT_OL_FLTR_ALL ||
957 what == MNT_OL_FLTR_HELPERS)) {
958
959 str[0] = 'r';
960 str[1] = 'o';
961 }
962
963 if (optstr)
964 *optstr = str;
965 return 0;
966 fail:
967 ul_buffer_free_data(&buf);
968 return rc;
969 }
970
971 int mnt_optlist_get_optstr(struct libmnt_optlist *ls, const char **optstr,
972 const struct libmnt_optmap *map, unsigned int what)
973 {
974 struct optlist_cache *cache;
975
976 if (!ls || !optstr)
977 return -EINVAL;
978
979 *optstr = NULL;
980
981 cache = get_cache(ls, map, what);
982 if (!cache)
983 return -EINVAL;
984
985 if (!cache->optstr_ready) {
986 char *str = NULL;
987 int rc = mnt_optlist_strdup_optstr(ls, &str, map, what);
988
989 if (rc)
990 return rc;
991
992 cache->optstr = str;
993 cache->optstr_ready = 1;
994 }
995
996 *optstr = cache->optstr;
997 return 0;
998 }
999
1000 struct libmnt_optlist *mnt_copy_optlist(struct libmnt_optlist *ls)
1001 {
1002 struct libmnt_optlist *n = mnt_new_optlist();
1003 struct libmnt_iter itr;
1004 struct libmnt_opt *opt;
1005 size_t i;
1006
1007 if (!n)
1008 return NULL;
1009
1010 n->age = ls->age;
1011 n->linux_map = ls->linux_map;
1012
1013 for (i = 0; i < ls->nmaps; i++)
1014 n->maps[i] = ls->maps[i];
1015 n->nmaps = ls->nmaps;
1016
1017 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
1018
1019 while (mnt_optlist_next_opt(ls, &itr, &opt) == 0) {
1020 struct libmnt_opt *no;
1021
1022 no = optlist_new_opt(n,
1023 opt->name, opt->name ? strlen(opt->name) : 0,
1024 opt->value, opt->value ? strlen(opt->value) : 0,
1025 opt->map, opt->ent, NULL);
1026 if (no) {
1027 no->src = opt->src;
1028 no->external = opt->external;
1029 no->quoted = opt->quoted;
1030 }
1031 }
1032
1033 n->merged = ls->merged;
1034 return n;
1035 }
1036
1037
1038 int mnt_optlist_is_empty(struct libmnt_optlist *ls)
1039 {
1040 return ls == NULL || list_empty(&ls->opts);
1041 }
1042
1043 unsigned int mnt_optlist_get_age(struct libmnt_optlist *ls)
1044 {
1045 return ls ? ls->age : 0;
1046 }
1047
1048 int mnt_optlist_get_propagation(struct libmnt_optlist *ls)
1049 {
1050 return ls ? ls->propagation : 0;
1051 }
1052
1053 int mnt_optlist_is_propagation_only(struct libmnt_optlist *ls)
1054 {
1055 unsigned long flags = 0, rest;
1056
1057 if (!ls || !ls->propagation || !ls->nmaps)
1058 return 0;
1059
1060 if (mnt_optlist_get_flags(ls, &flags, ls->linux_map, 0) != 0)
1061 return 0;
1062
1063 rest = flags & ~MS_PROPAGATION;
1064 DBG(OPTLIST, ul_debugobj(ls, " propagation-only: %s",
1065 (rest == 0 || (rest & (MS_SILENT | MS_REC)) ? "y" : "n")));
1066
1067 return (rest == 0 || (rest & (MS_SILENT | MS_REC)));
1068 }
1069
1070 int mnt_optlist_is_remount(struct libmnt_optlist *ls)
1071 {
1072 return ls && ls->is_remount;
1073 }
1074
1075 int mnt_optlist_is_recursive(struct libmnt_optlist *ls)
1076 {
1077 return ls && ls->is_recursive;
1078 }
1079
1080 int mnt_optlist_is_move(struct libmnt_optlist *ls)
1081 {
1082 return ls && ls->is_move;
1083 }
1084
1085 int mnt_optlist_is_bind(struct libmnt_optlist *ls)
1086 {
1087 return ls && (ls->is_bind || ls->is_rbind);
1088 }
1089
1090 int mnt_optlist_is_rbind(struct libmnt_optlist *ls)
1091 {
1092 return ls && ls->is_rbind;
1093 }
1094
1095 int mnt_optlist_is_rdonly(struct libmnt_optlist *ls)
1096 {
1097 return ls && ls->is_rdonly;
1098 }
1099
1100 int mnt_optlist_is_silent(struct libmnt_optlist *ls)
1101 {
1102 return ls && ls->is_silent;
1103 }
1104
1105
1106 int mnt_opt_has_value(struct libmnt_opt *opt)
1107 {
1108 return opt && opt->value;
1109 }
1110
1111 const char *mnt_opt_get_value(struct libmnt_opt *opt)
1112 {
1113 return opt->value;
1114 }
1115
1116 const char *mnt_opt_get_name(struct libmnt_opt *opt)
1117 {
1118 return opt->name;
1119 }
1120
1121 const struct libmnt_optmap *mnt_opt_get_map(struct libmnt_opt *opt)
1122 {
1123 return opt->map;
1124 }
1125
1126 const struct libmnt_optmap *mnt_opt_get_mapent(struct libmnt_opt *opt)
1127 {
1128 return opt->ent;
1129 }
1130
1131 int mnt_opt_set_value(struct libmnt_opt *opt, const char *str)
1132 {
1133 int rc;
1134
1135 opt->recursive = 0;
1136 rc = strdup_to_struct_member(opt, value, str);
1137
1138 if (rc == 0 && str && strcmp(str, "recursive") == 0)
1139 opt->recursive = 1;
1140 return rc;
1141 }
1142
1143 int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num)
1144 {
1145 char buf[ sizeof(stringify_value(UINT64_MAX)) ];
1146
1147 snprintf(buf, sizeof(buf), "%"PRIu64, num);
1148
1149 return mnt_opt_set_value(opt, buf);
1150 }
1151
1152 int mnt_opt_set_quoted_value(struct libmnt_opt *opt, const char *str)
1153 {
1154 opt->quoted = 1;
1155 return mnt_opt_set_value(opt, str);
1156 }
1157
1158 int mnt_opt_set_external(struct libmnt_opt *opt, int enable)
1159 {
1160 if (!opt)
1161 return -EINVAL;
1162 opt->external = enable ? 1 : 0;
1163 return 0;
1164 }
1165
1166 int mnt_opt_is_external(struct libmnt_opt *opt)
1167 {
1168 return opt && opt->external ? 1 : 0;
1169 }
1170
1171
1172 #ifdef TEST_PROGRAM
1173
1174 static int mk_optlist(struct libmnt_optlist **ol, const char *optstr)
1175 {
1176 int rc = 0;
1177
1178 *ol = mnt_new_optlist();
1179 if (!*ol)
1180 rc = -ENOMEM;
1181
1182 if (!rc)
1183 rc = mnt_optlist_register_map(*ol, mnt_get_builtin_optmap(MNT_LINUX_MAP));
1184 if (!rc)
1185 rc = mnt_optlist_register_map(*ol, mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
1186 if (!rc && optstr)
1187 rc = mnt_optlist_append_optstr(*ol, optstr, NULL);
1188 if (rc) {
1189 mnt_unref_optlist(*ol);
1190 *ol = NULL;
1191 }
1192 return rc;
1193 }
1194
1195 static void dump_optlist(struct libmnt_optlist *ol)
1196 {
1197 struct libmnt_iter itr;
1198 struct libmnt_opt *opt;
1199 int i = 0;
1200
1201 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
1202
1203 while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
1204 if (opt->ent)
1205 printf("#%02d [%p:0x%08x] name:'%s',\tvalue:'%s'\n",
1206 ++i, opt->map, opt->ent->id, opt->name, opt->value);
1207 else
1208 printf("#%02d [ unknown ] name:'%s',\tvalue:'%s'\n",
1209 ++i, opt->name, opt->value);
1210
1211 }
1212 }
1213
1214 static const struct libmnt_optmap *get_map(const char *name)
1215 {
1216 if (name && strcmp(name, "linux") == 0)
1217 return mnt_get_builtin_optmap(MNT_LINUX_MAP);
1218 if (name && strcmp(name, "user") == 0)
1219 return mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
1220 return NULL;
1221 }
1222
1223 static inline unsigned long str2flg(const char *str)
1224 {
1225 return (unsigned long) strtox64_or_err(str, "connt convert string to flags");
1226 }
1227
1228 static int test_append_str(struct libmnt_test *ts, int argc, char *argv[])
1229 {
1230 struct libmnt_optlist *ol;
1231 int rc;
1232
1233 if (argc < 3)
1234 return -EINVAL;
1235 rc = mk_optlist(&ol, argv[1]);
1236 if (!rc)
1237 rc = mnt_optlist_append_optstr(ol, argv[2], get_map(argv[3]));
1238 if (!rc)
1239 dump_optlist(ol);
1240 mnt_unref_optlist(ol);
1241 return rc;
1242 }
1243
1244 static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[])
1245 {
1246 struct libmnt_optlist *ol;
1247 int rc;
1248
1249 if (argc < 3)
1250 return -EINVAL;
1251 rc = mk_optlist(&ol, argv[1]);
1252 if (!rc)
1253 rc = mnt_optlist_prepend_optstr(ol, argv[2], get_map(argv[3]));
1254 if (!rc)
1255 dump_optlist(ol);
1256 mnt_unref_optlist(ol);
1257 return rc;
1258 }
1259
1260 static int test_set_str(struct libmnt_test *ts, int argc, char *argv[])
1261 {
1262 struct libmnt_optlist *ol;
1263 int rc;
1264
1265 if (argc < 3)
1266 return -EINVAL;
1267 rc = mk_optlist(&ol, argv[1]);
1268 if (!rc)
1269 rc = mnt_optlist_set_optstr(ol, argv[2], get_map(argv[3]));
1270 if (!rc)
1271 dump_optlist(ol);
1272 mnt_unref_optlist(ol);
1273 return rc;
1274 }
1275
1276 static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[])
1277 {
1278 struct libmnt_optlist *ol;
1279 int rc;
1280
1281 if (argc < 4)
1282 return -EINVAL;
1283 rc = mk_optlist(&ol, argv[1]);
1284 if (!rc)
1285 rc = mnt_optlist_append_flags(ol, str2flg(argv[2]), get_map(argv[3]));
1286 if (!rc)
1287 dump_optlist(ol);
1288 mnt_unref_optlist(ol);
1289 return rc;
1290 }
1291
1292 static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[])
1293 {
1294 struct libmnt_optlist *ol;
1295 int rc;
1296
1297 if (argc < 4)
1298 return -EINVAL;
1299 rc = mk_optlist(&ol, argv[1]);
1300 if (!rc)
1301 rc = mnt_optlist_set_flags(ol, str2flg(argv[2]), get_map(argv[3]));
1302 if (!rc)
1303 dump_optlist(ol);
1304 mnt_unref_optlist(ol);
1305 return rc;
1306 }
1307
1308 static int test_get_str(struct libmnt_test *ts, int argc, char *argv[])
1309 {
1310 struct libmnt_optlist *ol;
1311 const struct libmnt_optmap *map;
1312 const char *str = NULL;
1313 int rc;
1314 unsigned long flags = 0;
1315
1316 if (argc < 2)
1317 return -EINVAL;
1318 rc = mk_optlist(&ol, argv[1]);
1319 if (rc)
1320 goto done;
1321
1322 map = get_map(argv[2]);
1323 mnt_optlist_merge_opts(ol);
1324
1325 /* We always call mnt_optlist_get_optstr() two times to test the cache */
1326 if (map) {
1327 rc = mnt_optlist_get_optstr(ol, &str, map, MNT_OL_FLTR_DFLT);
1328 if (!rc)
1329 rc = mnt_optlist_get_optstr(ol, &str, map, MNT_OL_FLTR_DFLT);
1330 if (!rc)
1331 rc = mnt_optlist_get_flags(ol, &flags, map, MNT_OL_FLTR_DFLT);
1332 if (!rc)
1333 rc = mnt_optlist_get_flags(ol, &flags, map, MNT_OL_FLTR_DFLT);
1334 if (!rc)
1335 printf("Default: %s [0x%08lx] (in %s map)\n", str, flags, argv[2]);
1336 }
1337
1338 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_DFLT);
1339 if (!rc)
1340 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_DFLT);
1341 if (!rc)
1342 printf("Default: %s\n", str);
1343
1344
1345 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_ALL);
1346 if (!rc)
1347 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_ALL);
1348 if (!rc)
1349 printf("All: %s\n", str);
1350
1351 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_UNKNOWN);
1352 if (!rc)
1353 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_UNKNOWN);
1354 if (!rc)
1355 printf("Unknown: %s\n", str);
1356
1357 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_HELPERS);
1358 if (!rc)
1359 rc = mnt_optlist_get_optstr(ol, &str, NULL, MNT_OL_FLTR_HELPERS);
1360 if (!rc)
1361 printf("Helpers: %s\n", str);
1362 done:
1363 mnt_unref_optlist(ol);
1364 return rc;
1365 }
1366
1367 static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[])
1368 {
1369 struct libmnt_optlist *ol;
1370 unsigned long flags = 0;
1371 int rc;
1372
1373 if (argc < 3)
1374 return -EINVAL;
1375 rc = mk_optlist(&ol, argv[1]);
1376 if (!rc)
1377 rc = mnt_optlist_get_flags(ol, &flags, get_map(argv[2]), 0);
1378 if (!rc)
1379 printf("0x%08lx\n", flags);
1380 mnt_unref_optlist(ol);
1381 return rc;
1382 }
1383
1384 int main(int argc, char *argv[])
1385 {
1386 struct libmnt_test tss[] = {
1387 { "--append-str", test_append_str, "<list> <str> [linux|user] append to the list" },
1388 { "--prepend-str", test_prepend_str, "<list> <str> [linux|user] prepend to the list" },
1389 { "--set-str", test_set_str, "<list> <str> [linux|user] set to the list" },
1390 { "--append-flg", test_append_flg, "<list> <flg> linux|user append to the list" },
1391 { "--set-flg", test_set_flg, "<list> <flg> linux|user set to the list" },
1392 { "--get-str", test_get_str, "<list> [linux|user] all options in string" },
1393 { "--get-flg", test_get_flg, "<list> linux|user all options by flags" },
1394
1395 { NULL }
1396 };
1397 return mnt_run_test(tss, argc, argv);
1398 }
1399 #endif /* TEST_PROGRAM */