~ubuntu-branches/ubuntu/precise/util-linux/precise

« back to all changes in this revision

Viewing changes to libmount/src/optstr.c

  • Committer: Package Import Robot
  • Author(s): Steve Langasek
  • Date: 2011-12-16 22:53:42 UTC
  • mfrom: (1.6.4) (4.5.5 sid)
  • Revision ID: package-import@ubuntu.com-20111216225342-206wz4bhvutyvx0d
Tags: 2.20.1-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Build for multiarch.
  - Add pre-depends on multiarch-support.
  - configure.ac: don't try to be clever about extracting a path name from
    $libdir to append to /usr in a way that's not overridable; instead,
    reuse the built-in configurable libexecdir.
  - Fix up the .pc.in files to know about libexecdir, so our substitutions
    don't leave us with unusable pkg-config files.
  - Install custom blkid.conf to use /dev/.blkid.tab since we don't
    expect device names to survive a reboot
  - Mention mountall(8) in fstab(5) manpages, along with its special
    options.
  - Since upstart is required in Ubuntu, the hwclock.sh init script is not
    called on startup and the hwclockfirst.sh init script is removed.
  - Drop depends on initscripts for the above.
  - Replace hwclock udev rule with an Upstart job.
  - For the case where mount is called with a directory to mount, look
    that directory up in mountall's /lib/init/fstab if we couldn't find
    it mentioned anywhere else.  This means "mount /proc", "mount /sys",
    etc. work.
  - mount.8 points to the cifs-utils package, not the obsolete smbfs one. 
  - Use canonicalize_spec in getmntdevbackward. proc should not be
    interpreted as a non-canonical-path.
* Dropped changes, superseded upstream:
  - shlibs/mount/src/tab_update.c: don't refuse to update mtab when source
    is 'none'.

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