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 it8 * under the terms of the GNU Lesser General Public License as published by9 * the Free Software Foundation; either version 2.1 of the License, or10 * (at your option) any later version.11 */1213#include <assert.h>14#include <stdlib.h>15#include <string.h>16#include <errno.h>1718#ifndef min19# define min(x, y) __extension__ ({ \20 __typeof__(x) _min1 = (x); \21 __typeof__(y) _min2 = (y); \22 (void) (&_min1 == &_min2); \23 _min1 < _min2 ? _min1 : _min2; })24#endif2526/*27 * Parses the first option from @optstr. The @optstr pointer is set to the beginning28 * of the next option. The options string looks like 'aaa,bbb=data,foo,bar="xxx"'.29 *30 * Note this function is used by libmount to parse mount options. Be careful when modify.31 *32 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.33 */34int ul_optstr_next(char **optstr, char **name, size_t *namesz,35 char **value, size_t *valsz)36{37 int open_quote = 0;38 char *start = NULL, *stop = NULL, *p, *sep = NULL;39 char *optstr0;4041 assert(optstr);42 assert(*optstr);4344 optstr0 = *optstr;4546 if (name)47 *name = NULL;48 if (namesz)49 *namesz = 0;50 if (value)51 *value = NULL;52 if (valsz)53 *valsz = 0;5455 /* trim leading commas as to not invalidate option56 * strings with multiple consecutive commas */57 while (optstr0 && *optstr0 == ',')58 optstr0++;5960 for (p = optstr0; p && *p; p++) {61 if (!start)62 start = p; /* beginning of the option item */63 if (*p == '"')64 open_quote ^= 1; /* reverse the status */65 if (open_quote)66 continue; /* still in quoted block */67 if (!sep && p > start && *p == '=')68 sep = p; /* name and value separator */69 if (*p == ',')70 stop = p; /* terminate the option item */71 else if (*(p + 1) == '\0')72 stop = p + 1; /* end of optstr */73 if (!start || !stop)74 continue;75 if (stop <= start)76 return -EINVAL;7778 if (name)79 *name = start;80 if (namesz)81 *namesz = sep ? sep - start : stop - start;82 *optstr = *stop ? stop + 1 : stop;8384 if (sep) {85 if (value)86 *value = sep + 1;87 if (valsz)88 *valsz = stop - sep - 1;89 }90 return 0;91 }9293 return 1; /* end of optstr */94}9596/*97 * Match string beginning.98 */99static inline const char *startswith(const char *s, const char *prefix)100{101 size_t sz = prefix ? strlen(prefix) : 0;102103 if (s && sz && strncmp(s, prefix, sz) == 0)104 return s + sz;105 return NULL;106}107108/* caller guarantees n > 0 */109static void xstrncpy(char *dest, const char *src, size_t n)110{111 size_t len = src ? strlen(src) : 0;112113 if (!len)114 return;115 len = min(len, n - 1);116 memcpy(dest, src, len);117 dest[len] = 0;118}119120/*121 * Option location122 */123struct libmnt_optloc {124 char *begin;125 char *end;126 char *value;127 size_t valsz;128 size_t namesz;129};130131#define MNT_INIT_OPTLOC { .begin = NULL }132133#define mnt_optmap_entry_novalue(e) \134 (e && (e)->name && !strchr((e)->name, '=') && !((e)->mask & MNT_PREFIX))135136/*137 * Locates the first option that matches @name. The @end is set to the138 * char behind the option (it means ',' or \0).139 *140 * Returns negative number on parse error, 1 when not found and 0 on success.141 */142static int mnt_optstr_locate_option(char *optstr, const char *name,143 struct libmnt_optloc *ol)144{145 char *n;146 size_t namesz, nsz;147 int rc;148149 if (!optstr)150 return 1;151152 assert(name);153154 namesz = strlen(name);155 if (!namesz)156 return 1;157158 do {159 rc = ul_optstr_next(&optstr, &n, &nsz,160 &ol->value, &ol->valsz);161 if (rc)162 break;163164 if (namesz == nsz && strncmp(n, name, nsz) == 0) {165 ol->begin = n;166 ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;167 ol->namesz = nsz;168 return 0;169 }170 } while(1);171172 return rc;173}174175/**176 * mnt_optstr_get_option:177 * @optstr: string with a comma separated list of options178 * @name: requested option name179 * @value: returns a pointer to the beginning of the value (e.g. name=VALUE) or NULL180 * @valsz: returns size of the value or 0181 *182 * Returns: 0 on success, 1 when not found the @name or negative number in case183 * of error.184 */185int mnt_optstr_get_option(const char *optstr, const char *name,186 char **value, size_t *valsz)187{188 struct libmnt_optloc ol = MNT_INIT_OPTLOC;189 int rc;190191 if (!optstr || !name)192 return -EINVAL;193194 rc = mnt_optstr_locate_option((char *) optstr, name, &ol);195 if (!rc) {196 if (value)197 *value = ol.value;198 if (valsz)199 *valsz = ol.valsz;200 }201 return rc;202}203204/**205 * mnt_optstr_next_option:206 * @optstr: option string, returns the position of the next option207 * @name: returns the option name208 * @namesz: returns the option name length209 * @value: returns the option value or NULL210 * @valuesz: returns the option value length or zero211 *212 * Parses the first option in @optstr.213 *214 * Returns: 0 on success, 1 at the end of @optstr or negative number in case of215 * error.216 */217static int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,218 char **value, size_t *valuesz)219{220 if (!optstr || !*optstr)221 return -EINVAL;222223 return ul_optstr_next(optstr, name, namesz, value, valuesz);224}225226int mnt_match_options(const char *optstr, const char *pattern)227{228 char *name, *pat = (char *) pattern;229 char *buf = NULL, *patval;230 size_t namesz = 0, patvalsz = 0;231 int match = 1;232233 if (!pattern && !optstr)234 return 1;235 if (pattern && optstr && !*pattern && !*optstr)236 return 1;237 if (!pattern)238 return 0;239240 /* walk on pattern string241 */242 while (match && !mnt_optstr_next_option(&pat, &name, &namesz,243 &patval, &patvalsz)) {244 char *val;245 size_t sz = 0;246 int no = 0, rc;247248 if (*name == '+' && namesz > 1)249 name++, namesz--;250 else if ((no = (startswith(name, "no") != NULL))) {251 name += 2, namesz -= 2;252 if (!*name || *name == ',') {253 match = 0;254 break; /* alone "no" keyword is error */255 }256 }257258 if (optstr && *optstr && *name) {259 if (!buf) {260 buf = malloc(strlen(pattern) + 1);261 if (!buf)262 return 0;263 }264265 xstrncpy(buf, name, namesz + 1);266 rc = mnt_optstr_get_option(optstr, buf, &val, &sz);267268 } else if (!*name) {269 rc = 0; /* empty pattern matches */270 } else {271 rc = 1; /* not found in empty string */272 }273274 /* check also value (if the pattern is "foo=value") */275 if (rc == 0 && patvalsz > 0 &&276 (patvalsz != sz || strncmp(patval, val, sz) != 0))277 rc = 1;278279 switch (rc) {280 case 0: /* found */281 match = no == 0 ? 1 : 0;282 break;283 case 1: /* not found */284 match = no == 1 ? 1 : 0;285 break;286 default: /* parse error */287 match = 0;288 break;289 }290 }291292 free(buf);293 return match;294}