/* * Copyright (C) 2008-2009 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. */ /** * SECTION: optstr * @title: Options string * @short_description: low-level API for work with mount options * * This is simple and low-level API to work with mount options that are stored * in string. This API is independent on the high-level options container and * option maps. */ #include #include #include #include #include "nls.h" #include "mountP.h" /* * Parses the first option from @optstr. The @optstr pointer is set to begin of * the next option. * * Returns -1 on parse error, 1 at the end of optstr and 0 on success. */ static int mnt_optstr_parse_next(char **optstr, char **name, size_t *namesz, char **value, size_t *valsz) { int open_quote = 0; char *start = NULL, *stop = NULL, *p, *sep = NULL; char *optstr0; assert(optstr); assert(*optstr); optstr0 = *optstr; if (name) *name = NULL; if (namesz) *namesz = 0; if (value) *value = NULL; if (valsz) *valsz = 0; for (p = optstr0; p && *p; p++) { if (!start) start = p; /* begin of the option item */ if (*p == '"') open_quote ^= 1; /* reverse the status */ if (open_quote) continue; /* still in quoted block */ if (!sep && *p == '=') sep = p; /* name and value separator */ if (*p == ',') stop = p; /* terminate the option item */ else if (*(p + 1) == '\0') stop = p + 1; /* end of optstr */ if (!start || !stop) continue; if (stop <= start) goto error; if (name) *name = start; if (namesz) *namesz = sep ? sep - start : stop - start; *optstr = *stop ? stop + 1 : stop; if (sep) { if (value) *value = sep + 1; if (valsz) *valsz = stop - sep - 1; } return 0; } return 1; /* end of optstr */ error: DBG(DEBUG_OPTIONS, fprintf(stderr, "libmount: parse error: \"%s\"\n", optstr0)); return -1; } /* * Locates the first option that match with @name. The @end is set to * char behind the option (it means ',' or \0). * * Returns -1 on parse error, 1 when not found and 0 on success. */ static int mnt_optstr_locate_option(char *optstr, const char *name, char **begin, char **end, char **value, size_t *valsz) { char *n; size_t namesz, nsz; int rc; assert(name); assert(optstr); namesz = strlen(name); do { rc = mnt_optstr_parse_next(&optstr, &n, &nsz, value, valsz); if (rc) break; if (namesz == nsz && strncmp(n, name, nsz) == 0) { if (begin) *begin = n; if (end) *end = *(optstr - 1) == ',' ? optstr - 1 : optstr; return 0; } } while(1); DBG(DEBUG_OPTIONS, fprintf(stderr, "libmount: can't found '%s' option\n", name)); return rc; } /** * mnt_optstr_next_option: * @optstr: option string, returns position to next option * @name: returns option name * @namesz: returns option name length * @value: returns option value or NULL * @valuesz: returns option value length or zero * * Parses the first option in @optstr or -1 in case of error. * * Returns: 0 on success, 1 at the end of @optstr or -1 in case of error. */ int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz, char **value, size_t *valuesz) { if (!optstr || !*optstr) return -1; return mnt_optstr_parse_next(optstr, name, namesz, value, valuesz); } static int __mnt_optstr_append_option(char **optstr, const char *name, size_t nsz, const char *value, size_t vsz) { char *p; size_t sz, osz; assert(name); osz = *optstr ? strlen(*optstr) : 0; sz = osz + nsz + 1; /* 1: '\0' */ if (osz) sz++; /* ',' options separator */ if (vsz) sz += vsz + 1; /* 1: '=' */ p = realloc(*optstr, sz); if (!p) return -1; *optstr = p; if (osz) { p += osz; *p++ = ','; } memcpy(p, name, nsz); p += nsz; if (vsz) { *p++ = '='; memcpy(p, value, vsz); p += vsz; } *p = '\0'; return 0; } /** * mnt_optstr_append_option: * @optstr: option string or NULL * @name: value name * @value: value * * Returns: reallocated (or newly allocated) @optstr with ,name=value */ int mnt_optstr_append_option(char **optstr, const char *name, const char *value) { size_t vsz, nsz; if (!name) return -1; nsz = strlen(name); vsz = value ? strlen(value) : 0; return __mnt_optstr_append_option(optstr, name, nsz, value, vsz); } /** * mnt_optstr_get_option: * @optstr: string with comma separated list of options * @name: requested option name * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL * @valsz: returns size of the value or 0 * * Returns: 0 on success, 1 when not found the @name or -1 in case of error. */ int mnt_optstr_get_option(char *optstr, const char *name, char **value, size_t *valsz) { return mnt_optstr_locate_option(optstr, name, NULL, NULL, value, valsz); } /* Removes substring located between @begin and @end from @str * -- result never starts or ends with comma or contains two commas * (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,") */ static void remove_substring(char *str, char *begin, char *end) { size_t sz = strlen(end); if ((begin == str || *(begin - 1) == ',') && *end == ',') end++; memmove(begin, end, sz + 1); if (!*begin && *(begin - 1) == ',') *(begin - 1) = '\0'; } /* insert '=substr' to @str on position @pos */ static int insert_substring(char **str, char *pos, const char *substr) { char *p; size_t ssz = strlen(substr); /* substring size */ p = realloc(*str, strlen(*str) + 1 + ssz); if (!p) return -1; *str = p; memmove(pos + ssz + 1, pos, strlen(pos) + 1); *pos++ = '='; memcpy(pos, substr, ssz); return 0; } /** * mnt_optstr_set_option: * @optstr: string with comma separated list of options * @name: requested option * @value: new value or NULL * * Set or unset option @value. * * Returns: 0 on success, 1 when not found the @name or -1 in case of error. */ int mnt_optstr_set_option(char **optstr, const char *name, const char *value) { char *val = NULL, *begin, *end, *nameend; size_t valsz; int rc = 1; if (!optstr) return -1; if (*optstr) rc = mnt_optstr_locate_option(*optstr, name, &begin, &end, &val, &valsz); if (rc == -1) /* parse error */ return -1; if (rc == 1) /* not found */ return mnt_optstr_append_option(optstr, name, value); nameend = begin + strlen(name); if (value == NULL && val && valsz) /* remove unwanted "=value" */ remove_substring(*optstr, nameend, end); else if (value && val == NULL) /* insert "=value" */ rc = insert_substring(optstr, nameend, value); else if (value && val && strlen(value) == valsz) /* simply replace =value */ memcpy(val, value, valsz); else if (value && val) { remove_substring(*optstr, nameend, end); rc = insert_substring(optstr, nameend, value); } return 0; } /** * mnt_optstr_remove_option: * @optstr: string with comma separated list of options * @name: requested option name * * Returns: 0 on success, 1 when not found the @name or -1 in case of error. */ int mnt_optstr_remove_option(char **optstr, const char *name) { char *begin, *end; int rc; rc = mnt_optstr_locate_option(*optstr, name, &begin, &end, NULL, NULL); if (rc != 0) return rc; remove_substring(*optstr, begin, end); return 0; } /** * mnt_split_optstr: * @optstr: string with comma separated list of options * @user: returns newly allocated string with userspace options * @vfs: returns newly allocated string with VFS options * @fs: returns newly allocated string with FS options * @ignore_user: option mask for options that should be ignored * @ignore_vfs: option mask for options that should be ignored * * For example: * * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0); * * returns all userspace options, the options that does not belong to * mtab are ignored. * * Note that FS options are all options that are undefined in MNT_USERSPACE_MAP * or MNT_LINUX_MAP. * * Returns: 0 on success, or -1 in case of error. */ int mnt_split_optstr(const char *optstr, char **user, char **vfs, char **fs, int ignore_user, int ignore_vfs) { char *name, *val, *str = (char *) optstr; size_t namesz, valsz; struct mnt_optmap const *maps[2]; assert(optstr); if (!optstr) return -1; maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP); maps[1] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); if (vfs) *vfs = NULL; if (fs) *fs = NULL; if (user) *user = NULL; while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { int rc = 0; const struct mnt_optmap *ent; const struct mnt_optmap *m = mnt_optmap_get_entry(maps, 2, name, namesz, &ent); if (m && m == maps[0] && vfs) { if (ignore_vfs && (ent->mask & ignore_vfs)) continue; rc = __mnt_optstr_append_option(vfs, name, namesz, val, valsz); } else if (m && m == maps[1] && user) { if (ignore_user && (ent->mask & ignore_user)) continue; rc = __mnt_optstr_append_option(user, name, namesz, val, valsz); } else if (!m && fs) rc = __mnt_optstr_append_option(fs, name, namesz, val, valsz); if (rc) { if (vfs) free(*vfs); if (fs) free(*fs); if (user) free(*user); return rc; } } return 0; } #ifdef TEST_PROGRAM int test_append(struct mtest *ts, int argc, char *argv[]) { const char *value = NULL, *name; char *optstr; if (argc < 3) goto done; optstr = strdup(argv[1]); name = argv[2]; if (argc == 4) value = argv[3]; if (mnt_optstr_append_option(&optstr, name, value) == 0) { printf("result: >%s<\n", optstr); return 0; } done: return -1; } int test_split(struct mtest *ts, int argc, char *argv[]) { char *optstr, *user = NULL, *fs = NULL, *vfs = NULL; int rc = -1; if (argc < 2) return -1; optstr = strdup(argv[1]); if (mnt_split_optstr(optstr, &user, &vfs, &fs) == 0) { printf("user : %s\n", user); printf("vfs : %s\n", vfs); printf("fs : %s\n", fs); rc = 0; } free(user); free(vfs); free(fs); free(optstr); return rc; } int test_set(struct mtest *ts, int argc, char *argv[]) { const char *value = NULL, *name; char *optstr; if (argc < 3) goto done; optstr = strdup(argv[1]); name = argv[2]; if (argc == 4) value = argv[3]; if (mnt_optstr_set_option(&optstr, name, value) == 0) { printf("result: >%s<\n", optstr); return 0; } done: return -1; } int test_get(struct mtest *ts, int argc, char *argv[]) { char *optstr; const char *name; char *val = NULL; size_t sz = 0; int rc; if (argc < 2) goto done; optstr = argv[1]; name = argv[2]; rc = mnt_optstr_get_option(optstr, name, &val, &sz); if (rc == 0) { printf("found; name: %s", name); if (sz) { printf(", argument: size=%zd data=", sz); if (fwrite(val, 1, sz, stdout) != sz) goto done; } printf("\n"); return 0; } else if (rc == 1) printf("%s: not found\n", name); else printf("parse error: %s\n", optstr); done: return -1; } int test_remove(struct mtest *ts, int argc, char *argv[]) { const char *name; char *optstr; if (argc < 3) goto done; optstr = strdup(argv[1]); name = argv[2]; if (mnt_optstr_remove_option(&optstr, name) == 0) { printf("result: >%s<\n", optstr); return 0; } done: return -1; } int main(int argc, char *argv[]) { struct mtest tss[] = { { "--append", test_append, " [] append value to optstr" }, { "--set", test_set, " [] (un)set value" }, { "--get", test_get, " search name in optstr" }, { "--remove", test_remove, " remove name in optstr" }, { "--split", test_split, " split into FS, VFS and userspace" }, { NULL } }; return mnt_run_test(tss, argc, argv); } #endif /* TEST_PROGRAM */