2
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
4
* This file may be redistributed under the terms of the
5
* GNU Lesser General Public License.
10
#include <sys/types.h>
16
#ifdef HAVE_LIBSELINUX
17
#include <selinux/selinux.h>
18
#include <selinux/context.h>
22
#include <sys/mount.h>
28
* this has to be called after mnt_context_evaluate_permissions()
30
static int fix_optstr(struct libmnt_context *cxt)
32
int rc = 0, rem_se = 0;
40
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
47
DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr"));
51
/* The propagation flags should not be used together with any other flags */
52
if (cxt->mountflags & MS_PROPAGATION)
53
cxt->mountflags &= MS_PROPAGATION;
55
if (!mnt_optstr_get_option(fs->user_optstr, "user", &val, &valsz)) {
57
cxt->orig_user = strndup(val, valsz);
58
if (!cxt->orig_user) {
63
cxt->flags |= MNT_FL_SAVED_USER;
67
* Sync mount options with mount flags
69
rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
70
mnt_get_builtin_optmap(MNT_LINUX_MAP));
74
rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
75
mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
81
#ifdef HAVE_LIBSELINUX
82
rem_se = (cxt->mountflags & MS_REMOUNT) || !is_selinux_enabled();
84
while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
86
if (namesz == 3 && !strncmp(name, "uid", 3))
87
rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
88
else if (namesz == 3 && !strncmp(name, "gid", 3))
89
rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
90
#ifdef HAVE_LIBSELINUX
91
else if (namesz >= 7 && (!strncmp(name, "context", 7) ||
92
!strncmp(name, "fscontext", 9) ||
93
!strncmp(name, "defcontext", 10) ||
94
!strncmp(name, "rootcontext", 11))) {
96
/* remove context= option */
98
rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
101
rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
109
if (!rc && cxt->user_mountflags && MNT_MS_USER)
110
rc = mnt_optstr_fix_user(&fs->user_optstr);
113
DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: "
114
"vfs: '%s' fs: '%s' user: '%s'", rc,
115
fs->vfs_optstr, fs->fs_optstr, fs->user_optstr));
120
* Converts already evalulated and fixed options to the form that is compatible
121
* with /sbin/mount.<type> helpers.
123
static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
131
*optstr = mnt_fs_strdup_options(cxt->fs);
135
if (cxt->flags & MNT_FL_SAVED_USER)
136
rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
146
* this has to be called before fix_optstr()
148
static int evaluate_permissions(struct libmnt_context *cxt)
150
unsigned long u_flags = 0;
155
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
162
DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions"));
164
mnt_context_get_user_mflags(cxt, &u_flags);
166
if (!mnt_context_is_restricted(cxt)) {
170
cxt->user_mountflags &= ~MNT_MS_OWNER;
171
cxt->user_mountflags &= ~MNT_MS_GROUP;
172
cxt->user_mountflags &= ~MNT_MS_USER;
173
cxt->user_mountflags &= ~MNT_MS_USERS;
178
if (!(cxt->flags & MNT_FL_TAB_APPLIED))
180
DBG(CXT, mnt_debug_h(cxt, "fstab not applied, ignore user mount"));
184
if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP))
185
cxt->mountflags |= MS_OWNERSECURE;
187
if (u_flags & (MNT_MS_USER | MNT_MS_USERS))
188
cxt->mountflags |= MS_SECURE;
190
srcpath = mnt_fs_get_srcpath(cxt->fs);
195
* MS_OWNER: Allow owners to mount when fstab contains the
196
* owner option. Note that this should never be used in a high
197
* security environment, but may be useful to give people at
198
* the console the possibility of mounting a floppy. MS_GROUP:
199
* Allow members of device group to mount. (Martin Dickopp)
201
if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
204
if (strncmp(srcpath, "/dev/", 5) == 0 &&
205
stat(srcpath, &sb) == 0 &&
206
(((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
207
((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))
209
cxt->user_mountflags |= MNT_MS_USER;
212
if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
213
DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS"));
222
* mnt_context_helper_setopt() backend
224
* This function applies mount.<type> command line option (for example parsed
225
* by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
226
* then 1 is returned.
228
* Returns: negative number on error, 1 if @c is unknown option, 0 on success.
230
int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
235
assert(cxt->action == MNT_ACT_MOUNT);
239
rc = mnt_context_enable_fake(cxt, TRUE);
242
rc = mnt_context_disable_mtab(cxt, TRUE);
245
rc = mnt_context_append_options(cxt, "ro");
248
rc = mnt_context_enable_verbose(cxt, TRUE);
251
rc = mnt_context_append_options(cxt, "rw");
255
rc = mnt_context_append_options(cxt, arg);
258
rc = mnt_context_enable_sloppy(cxt, TRUE);
262
rc = mnt_context_set_fstype(cxt, arg);
272
static int exec_helper(struct libmnt_context *cxt)
280
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
282
DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper));
284
rc = generate_helper_optstr(cxt, &o);
293
const char *args[12], *type;
296
if (setgid(getgid()) < 0)
299
if (setuid(getuid()) < 0)
302
type = mnt_fs_get_fstype(cxt->fs);
304
args[i++] = cxt->helper; /* 1 */
305
args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
306
args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */
308
if (mnt_context_is_sloppy(cxt))
309
args[i++] = "-s"; /* 4 */
310
if (mnt_context_is_fake(cxt))
311
args[i++] = "-f"; /* 5 */
312
if (mnt_context_is_nomtab(cxt))
313
args[i++] = "-n"; /* 6 */
314
if (mnt_context_is_verbose(cxt))
315
args[i++] = "-v"; /* 7 */
317
args[i++] = "-o"; /* 8 */
318
args[i++] = o; /* 9 */
320
if (type && !endswith(cxt->helper, type)) {
321
args[i++] = "-t"; /* 10 */
322
args[i++] = type; /* 11 */
324
args[i] = NULL; /* 12 */
325
#ifdef CONFIG_LIBMOUNT_DEBUG
327
for (i = 0; args[i]; i++)
328
DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
332
execv(cxt->helper, (char * const *) args);
339
cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
341
DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
342
cxt->helper, cxt->helper_status));
343
cxt->helper_exec_status = rc = 0;
348
cxt->helper_exec_status = rc = -errno;
349
DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
357
* The default is to use fstype from cxt->fs, this could be overwritten by
358
* @try_type argument.
360
static int do_mount(struct libmnt_context *cxt, const char *try_type)
363
const char *src, *target, *type;
368
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
370
if (try_type && !cxt->helper) {
371
rc = mnt_context_prepare_helper(cxt, "mount", try_type);
376
return exec_helper(cxt);
378
flags = cxt->mountflags;
379
src = mnt_fs_get_srcpath(cxt->fs);
380
target = mnt_fs_get_target(cxt->fs);
385
type = try_type ? : mnt_fs_get_fstype(cxt->fs);
387
if (!(flags & MS_MGC_MSK))
390
DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
391
"[source=%s, target=%s, type=%s, "
392
" mountflags=%08lx, mountdata=%s]",
393
(cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "",
395
flags, cxt->mountdata ? "yes" : "<none>"));
397
if (cxt->flags & MNT_FL_FAKE)
398
cxt->syscall_status = 0;
400
if (mount(src, target, type, flags, cxt->mountdata)) {
401
cxt->syscall_status = -errno;
402
DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]",
403
-cxt->syscall_status));
404
return -cxt->syscall_status;
406
DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
407
cxt->syscall_status = 0;
410
if (try_type && cxt->update) {
411
struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
413
rc = mnt_fs_set_fstype(fs, try_type);
416
/* TODO: check if the result is really read-only/read-write
417
* and if necessary update cxt->mountflags
423
static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
425
int neg = pattern && strncmp(pattern, "no", 2) == 0;
427
char **filesystems, **fp;
430
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
432
if (!neg && pattern) {
434
* try all types from the list
438
DBG(CXT, mnt_debug_h(cxt, "tring mount by FS pattern list"));
440
p0 = p = strdup(pattern);
444
char *end = strchr(p, ',');
447
rc = do_mount(cxt, p);
448
p = end ? end + 1 : NULL;
449
} while (!mnt_context_get_status(cxt) && p);
453
if (mnt_context_get_status(cxt))
458
* try /etc/filesystems and /proc/filesystems
460
DBG(CXT, mnt_debug_h(cxt, "tring mount by filesystems lists"));
462
rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
466
for (fp = filesystems; *fp; fp++) {
467
rc = do_mount(cxt, *fp);
468
if (mnt_context_get_status(cxt))
471
mnt_free_filesystems(filesystems);
476
* mnt_context_prepare_mount:
479
* Prepare context for mounting, unnecessary for mnt_context_mount().
481
* Returns: negative number on error, zero on success
483
int mnt_context_prepare_mount(struct libmnt_context *cxt)
489
assert(cxt->helper_exec_status == 1);
490
assert(cxt->syscall_status == 1);
492
if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
494
if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
496
if (cxt->flags & MNT_FL_PREPARED)
499
cxt->action = MNT_ACT_MOUNT;
501
DBG(CXT, mnt_debug_h(cxt, "mount: preparing"));
503
/* TODO: fstab is unnecessary for MS_{MOVE,BIND,STARED,...} */
504
rc = mnt_context_apply_fstab(cxt);
506
rc = mnt_context_merge_mflags(cxt);
508
rc = evaluate_permissions(cxt);
510
rc = fix_optstr(cxt);
512
rc = mnt_context_prepare_srcpath(cxt);
514
rc = mnt_context_prepare_target(cxt);
516
rc = mnt_context_guess_fstype(cxt);
518
rc = mnt_context_prepare_helper(cxt, "mount", NULL);
520
DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed"));
523
cxt->flags |= MNT_FL_PREPARED;
528
* mnt_context_do_mount
531
* Call mount(2) or mount.<type> helper. Unnecessary for mnt_context_mount().
533
* WARNING: non-zero return code does not mean that mount(2) syscall or
534
* umount.type helper wasn't sucessfully called.
536
* Check mnt_context_get_status() after error!
538
* Returns: 0 on success;
539
* >0 in case of mount(2) error (returns syscall errno),
540
* <0 in case of other errors.
542
int mnt_context_do_mount(struct libmnt_context *cxt)
548
assert(cxt->helper_exec_status == 1);
549
assert(cxt->syscall_status == 1);
550
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
551
assert((cxt->flags & MNT_FL_PREPARED));
552
assert((cxt->action == MNT_ACT_MOUNT));
554
DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));
556
if (!(cxt->flags & MNT_FL_MOUNTDATA))
557
cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);
559
type = mnt_fs_get_fstype(cxt->fs);
561
return do_mount(cxt, NULL);
563
return do_mount_by_pattern(cxt, cxt->fstype_pattern);
567
* mnt_context_finalize_mount:
570
* Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
571
* after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
573
* Returns: negative number on error, 0 on success.
575
int mnt_context_finalize_mount(struct libmnt_context *cxt)
581
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
582
assert((cxt->flags & MNT_FL_PREPARED));
584
rc = mnt_context_prepare_update(cxt);
586
rc = mnt_context_update_tabs(cxt);;
592
* @cxt: mount context
594
* High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type).
596
* This is similar to:
598
* mnt_context_prepare_mount(cxt);
599
* mnt_context_do_mount(cxt);
600
* mnt_context_finalize_mount(cxt);
602
* See also mnt_context_disable_helpers().
604
* WARNING: non-zero return code does not mean that mount(2) syscall or
605
* mount.type helper wasn't sucessfully called.
607
* Check mnt_context_get_status() after error!
609
* Returns: 0 on success;
610
* >0 in case of mount(2) error (returns syscall errno),
611
* <0 in case of other errors.
613
int mnt_context_mount(struct libmnt_context *cxt)
619
assert(cxt->helper_exec_status == 1);
620
assert(cxt->syscall_status == 1);
622
rc = mnt_context_prepare_mount(cxt);
624
rc = mnt_context_prepare_update(cxt);
626
rc = mnt_context_do_mount(cxt);
628
/* TODO: if mtab update is expected then check if the
629
* target is really mounted read-write to avoid 'ro' in
630
* mtab and 'rw' in /proc/mounts.
633
rc = mnt_context_update_tabs(cxt);