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