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