(root)/
util-linux-2.39/
libmount/
src/
optlist.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) 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 */