~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to shlibs/mount/src/optstr.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

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