]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/context_mount.c
libmount: fix mount.nfs segfault, rely on assert() rather than on nonnull
[thirdparty/util-linux.git] / libmount / src / context_mount.c
CommitLineData
1bb1d80b
KZ
1/*
2 * Copyright (C) 2010 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
63de90d4
KZ
8/**
9 * SECTION: context-mount
10 * @title: Mount context
11 * @short_description: high-level API to mount operation.
12 */
13
1bb1d80b
KZ
14#ifdef HAVE_LIBSELINUX
15#include <selinux/selinux.h>
16#include <selinux/context.h>
17#endif
18
19#include <sys/wait.h>
20#include <sys/mount.h>
21
de60d08e 22#include "linux_version.h"
1bb1d80b
KZ
23#include "mountP.h"
24
6498ece0
KZ
25/*
26 * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
58c41e15 27 * to bypass this restriction we call mount(2) per flag. It's really not a perfect
6498ece0
KZ
28 * solution, but it's the same like to execute multiple mount(8) commands.
29 *
30 * We use cxt->addmounts (additional mounts) list to keep order of the requested
31 * flags changes.
32 */
33struct libmnt_addmount *mnt_new_addmount(void)
34{
35 struct libmnt_addmount *ad = calloc(1, sizeof(*ad));
36 if (!ad)
37 return NULL;
38
39 INIT_LIST_HEAD(&ad->mounts);
40 return ad;
41}
42
43void mnt_free_addmount(struct libmnt_addmount *ad)
44{
45 if (!ad)
46 return;
47 list_del(&ad->mounts);
48 free(ad);
49}
50
51static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
52 struct libmnt_addmount *ad)
53{
54 assert(cxt);
55 assert(ad);
56
57 DBG(CXT, mnt_debug_h(cxt,
58 "mount: add additional flag: 0x%08lx",
59 ad->mountflags));
60
61 list_add_tail(&ad->mounts, &cxt->addmounts);
62 return 0;
63}
64
65static int init_propagation(struct libmnt_context *cxt)
66{
67 char *name;
68 char *opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
69 size_t namesz;
70 struct libmnt_optmap const *maps[1];
71
72 if (!opts)
73 return 0;
74
75 DBG(CXT, mnt_debug_h(cxt, "mount: initialize additional propagation mounts"));
76
77 maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
78
79 while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
80 const struct libmnt_optmap *ent;
81 struct libmnt_addmount *ad;
82 int rc;
83
84 if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent)
85 || !ent
86 || !(ent->id & MS_PROPAGATION))
87 continue;
88
89 ad = mnt_new_addmount();
90 if (!ad)
91 return -ENOMEM;
92
93 ad->mountflags = ent->id;
94 rc = mnt_context_append_additional_mount(cxt, ad);
95 if (rc)
96 return rc;
97
98 cxt->mountflags &= ~ent->id;
99 }
100
101 return 0;
102}
103
1bb1d80b
KZ
104/*
105 * this has to be called after mnt_context_evaluate_permissions()
106 */
68164f6c 107static int fix_optstr(struct libmnt_context *cxt)
1bb1d80b 108{
178537ed 109 int rc = 0;
76a06ca4 110 char *next;
1bb1d80b
KZ
111 char *name, *val;
112 size_t namesz, valsz;
68164f6c 113 struct libmnt_fs *fs;
178537ed
KZ
114#ifdef HAVE_LIBSELINUX
115 int se_fix = 0, se_rem = 0;
116#endif
1b56aae8
KZ
117 assert(cxt);
118 assert(cxt->fs);
119 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
120
1bb1d80b
KZ
121 if (!cxt)
122 return -EINVAL;
dc4dbbf1 123 if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
1bb1d80b
KZ
124 return 0;
125
1b56aae8 126 DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr"));
1bb1d80b 127
76a06ca4
KZ
128 fs = cxt->fs;
129
e90e7401
KZ
130 /*
131 * The "user" options is our business (so we can modify the option),
132 * but exception is command line for /sbin/mount.<type> helpers. Let's
133 * save the original user=<name> to call the helpers with unchanged
134 * "user" setting.
e90e7401 135 */
4be900c5
KZ
136 if (cxt->user_mountflags & MNT_MS_USER) {
137 if (!mnt_optstr_get_option(fs->user_optstr,
138 "user", &val, &valsz) && val) {
1bb1d80b
KZ
139 cxt->orig_user = strndup(val, valsz);
140 if (!cxt->orig_user) {
141 rc = -ENOMEM;
142 goto done;
143 }
144 }
145 cxt->flags |= MNT_FL_SAVED_USER;
146 }
147
148 /*
149 * Sync mount options with mount flags
150 */
6b741564 151 DBG(CXT, mnt_debug_h(cxt, "mount: fixing vfs optstr"));
76a06ca4 152 rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
1bb1d80b
KZ
153 mnt_get_builtin_optmap(MNT_LINUX_MAP));
154 if (rc)
155 goto done;
156
6b741564 157 DBG(CXT, mnt_debug_h(cxt, "mount: fixing user optstr"));
76a06ca4 158 rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
1bb1d80b
KZ
159 mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
160 if (rc)
161 goto done;
162
6b741564
KZ
163 if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
164 free(fs->vfs_optstr);
165 fs->vfs_optstr = NULL;
166 }
167 if (fs->user_optstr && *fs->user_optstr == '\0') {
168 free(fs->user_optstr);
169 fs->user_optstr = NULL;
170 }
171
6498ece0
KZ
172 if (cxt->mountflags & MS_PROPAGATION) {
173 rc = init_propagation(cxt);
174 if (rc)
175 return rc;
176 }
177
76a06ca4 178 next = fs->fs_optstr;
1bb1d80b
KZ
179
180#ifdef HAVE_LIBSELINUX
de60d08e
KZ
181 if (!is_selinux_enabled())
182 /* Always remove SELinux garbage if SELinux disabled */
183 se_rem = 1;
184 else if (cxt->mountflags & MS_REMOUNT)
185 /*
186 * Linux kernel < 2.6.39 does not allow to remount with any
187 * selinux specific mount options.
188 *
189 * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65
190 * 026eb167ae77244458fa4b4b9fc171209c079ba7
191 * fix this odd behavior, so we don't have to care about it in
192 * userspace.
193 */
194 se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39);
195 else
45683be5 196 /* For normal mount, contexts are translated */
de60d08e 197 se_fix = 1;
f1f9a46a
KZ
198
199 if (!se_rem) {
200 /* de-duplicate SELinux options */
201 mnt_optstr_deduplicate_option(&fs->fs_optstr, "context");
202 mnt_optstr_deduplicate_option(&fs->fs_optstr, "fscontext");
203 mnt_optstr_deduplicate_option(&fs->fs_optstr, "defcontext");
204 mnt_optstr_deduplicate_option(&fs->fs_optstr, "rootcontext");
205 mnt_optstr_deduplicate_option(&fs->fs_optstr, "seclabel");
206 }
1bb1d80b 207#endif
1bb1d80b
KZ
208 while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
209
210 if (namesz == 3 && !strncmp(name, "uid", 3))
76a06ca4 211 rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
1bb1d80b 212 else if (namesz == 3 && !strncmp(name, "gid", 3))
76a06ca4 213 rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
1bb1d80b 214#ifdef HAVE_LIBSELINUX
de60d08e
KZ
215 else if ((se_rem || se_fix) &&
216 namesz >= 7 && (!strncmp(name, "context", 7) ||
1bb1d80b
KZ
217 !strncmp(name, "fscontext", 9) ||
218 !strncmp(name, "defcontext", 10) ||
de60d08e
KZ
219 !strncmp(name, "rootcontext", 11) ||
220 !strncmp(name, "seclabel", 8))) {
221 if (se_rem) {
1bb1d80b
KZ
222 /* remove context= option */
223 next = name;
76a06ca4 224 rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
de60d08e
KZ
225 name,
226 val ? val + valsz :
227 name + namesz);
228 } else if (se_fix && val && valsz)
229 /* translate selinux contexts */
76a06ca4 230 rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
1bb1d80b
KZ
231 val, valsz, &next);
232 }
233#endif
1bb1d80b
KZ
234 if (rc)
235 goto done;
236 }
237
4be900c5 238 if (!rc && cxt->restricted && (cxt->user_mountflags & MNT_MS_USER))
5a669b12 239 rc = mnt_optstr_fix_user(&fs->user_optstr);
76a06ca4 240
f2b3a3a3
KZ
241 /* refresh merged optstr */
242 free(fs->optstr);
243 fs->optstr = NULL;
244 fs->optstr = mnt_fs_strdup_options(fs);
1bb1d80b 245done:
dc4dbbf1
KZ
246 cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
247
76a06ca4 248 DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: "
f2b3a3a3
KZ
249 "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
250 fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
61f5ff6c
KZ
251
252 if (rc)
253 rc = -MNT_ERR_MOUNTOPT;
1bb1d80b
KZ
254 return rc;
255}
256
257/*
3cbc5a95 258 * Converts already evaluated and fixed options to the form that is compatible
63de90d4 259 * with /sbin/mount.type helpers.
1bb1d80b 260 */
68164f6c 261static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
1bb1d80b 262{
6498ece0 263 struct libmnt_optmap const *maps[2];
5810d870
KZ
264 char *next, *name, *val;
265 size_t namesz, valsz;
1bb1d80b
KZ
266 int rc = 0;
267
268 assert(cxt);
269 assert(cxt->fs);
270 assert(optstr);
271
3cbc5a95 272 DBG(CXT, mnt_debug_h(cxt, "mount: generate helper mount options"));
5810d870 273
76a06ca4
KZ
274 *optstr = mnt_fs_strdup_options(cxt->fs);
275 if (!*optstr)
276 return -ENOMEM;
1bb1d80b 277
17206c05
KZ
278 if (cxt->user_mountflags & MNT_MS_USER) {
279 /*
280 * This is unnecessary for real user-mounts as mount.<type>
281 * helpers have to always follow fstab rather than mount
282 * options on command line.
283 *
284 * But if you call mount.<type> as root then the helper follows
285 * command line. If there is (for example) "user,exec" in fstab
286 * then we have to manually append the "exec" back to the options
287 * string, bacause there is nothing like MS_EXEC (we have only
288 * MS_NOEXEC in mount flags and we don't care about the original
289 * mount string in libmount for VFS options).
290 */
291 if (!(cxt->mountflags & MS_NOEXEC))
292 mnt_optstr_append_option(optstr, "exec", NULL);
293 if (!(cxt->mountflags & MS_NOSUID))
294 mnt_optstr_append_option(optstr, "suid", NULL);
295 if (!(cxt->mountflags & MS_NODEV))
296 mnt_optstr_append_option(optstr, "dev", NULL);
297 }
298
299
76a06ca4 300 if (cxt->flags & MNT_FL_SAVED_USER)
1bb1d80b 301 rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
5810d870
KZ
302 if (rc)
303 goto err;
304
305 /* remove userspace options with MNT_NOHLPS flag */
306 maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
6498ece0 307 maps[1] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
5810d870
KZ
308 next = *optstr;
309
310 while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
311 const struct libmnt_optmap *ent;
312
6498ece0 313 mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
5810d870
KZ
314 if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
315 next = name;
316 rc = mnt_optstr_remove_option_at(optstr, name,
317 val ? val + valsz : name + namesz);
318 if (rc)
319 goto err;
320 }
1bb1d80b 321 }
5810d870
KZ
322
323 return rc;
324err:
325 free(*optstr);
326 *optstr = NULL;
1bb1d80b
KZ
327 return rc;
328}
329
1bb1d80b 330/*
cf94e97f 331 * this has to be called before fix_optstr()
e90e7401
KZ
332 *
333 * Note that user=<name> maybe be used by some filesystems as filesystem
334 * specific option (e.g. cifs). Yes, developers of such filesystems have
335 * allocated pretty hot place in hell...
1bb1d80b 336 */
68164f6c 337static int evaluate_permissions(struct libmnt_context *cxt)
1bb1d80b 338{
766af80b 339 unsigned long u_flags = 0;
1bb1d80b 340
1b56aae8
KZ
341 assert(cxt);
342 assert(cxt->fs);
343 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
344
1bb1d80b
KZ
345 if (!cxt)
346 return -EINVAL;
347 if (!cxt->fs)
348 return 0;
349
1b56aae8
KZ
350 DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions"));
351
68164f6c 352 mnt_context_get_user_mflags(cxt, &u_flags);
1bb1d80b
KZ
353
354 if (!mnt_context_is_restricted(cxt)) {
355 /*
356 * superuser mount
357 */
358 cxt->user_mountflags &= ~MNT_MS_OWNER;
359 cxt->user_mountflags &= ~MNT_MS_GROUP;
1bb1d80b
KZ
360 } else {
361 /*
362 * user mount
363 */
30e43998
KZ
364 if (!(cxt->flags & MNT_FL_TAB_APPLIED))
365 {
be3df383 366 DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount"));
30e43998
KZ
367 return -EPERM;
368 }
369
ddfc6f28 370 /*
e90e7401
KZ
371 * MS_OWNERSECURE and MS_SECURE mount options are already
372 * applied by mnt_optstr_get_flags() in mnt_context_merge_mflags()
373 * if "user" (but no user=<name> !) options is set.
374 *
375 * Let's ignore all user=<name> (if <name> is set) requests.
ddfc6f28 376 */
e90e7401
KZ
377 if (cxt->user_mountflags & MNT_MS_USER) {
378 size_t valsz = 0;
1bb1d80b 379
e90e7401
KZ
380 if (!mnt_optstr_get_option(cxt->fs->user_optstr,
381 "user", NULL, &valsz) && valsz) {
382
383 DBG(CXT, mnt_debug_h(cxt, "perms: user=<name> detected, ignore"));
384 cxt->user_mountflags &= ~MNT_MS_USER;
385 }
386 }
1bb1d80b
KZ
387
388 /*
389 * MS_OWNER: Allow owners to mount when fstab contains the
390 * owner option. Note that this should never be used in a high
391 * security environment, but may be useful to give people at
392 * the console the possibility of mounting a floppy. MS_GROUP:
393 * Allow members of device group to mount. (Martin Dickopp)
394 */
395 if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
396 struct stat sb;
be3df383
KZ
397 struct libmnt_cache *cache = NULL;
398 char *xsrc = NULL;
399 const char *srcpath = mnt_fs_get_srcpath(cxt->fs);
400
401 if (!srcpath) { /* Ah... source is TAG */
402 cache = mnt_context_get_cache(cxt);
403 xsrc = mnt_resolve_spec(
404 mnt_context_get_source(cxt),
405 cache);
406 srcpath = xsrc;
407 }
408 if (!srcpath) {
409 DBG(CXT, mnt_debug_h(cxt, "perms: src undefined"));
410 return -EPERM;
411 }
1bb1d80b
KZ
412
413 if (strncmp(srcpath, "/dev/", 5) == 0 &&
414 stat(srcpath, &sb) == 0 &&
415 (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
416 ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))
417
418 cxt->user_mountflags |= MNT_MS_USER;
be3df383 419
0208ae2e 420 if (!cache)
be3df383 421 free(xsrc);
1bb1d80b
KZ
422 }
423
424 if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
425 DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS"));
426 return -EPERM;
427 }
428 }
429
430 return 0;
431}
432
b70785bc
KZ
433/*
434 * mnt_context_helper_setopt() backend
8c0797e7 435 *
63de90d4 436 * This function applies mount.type command line option (for example parsed
8c0797e7
KZ
437 * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
438 * then 1 is returned.
439 *
440 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
441 */
b70785bc 442int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
8c0797e7
KZ
443{
444 int rc = -EINVAL;
445
b70785bc
KZ
446 assert(cxt);
447 assert(cxt->action == MNT_ACT_MOUNT);
8c0797e7
KZ
448
449 switch(c) {
450 case 'f':
451 rc = mnt_context_enable_fake(cxt, TRUE);
452 break;
453 case 'n':
454 rc = mnt_context_disable_mtab(cxt, TRUE);
455 break;
456 case 'r':
457 rc = mnt_context_append_options(cxt, "ro");
458 break;
459 case 'v':
460 rc = mnt_context_enable_verbose(cxt, TRUE);
461 break;
462 case 'w':
463 rc = mnt_context_append_options(cxt, "rw");
464 break;
465 case 'o':
466 if (arg)
467 rc = mnt_context_append_options(cxt, arg);
468 break;
469 case 's':
470 rc = mnt_context_enable_sloppy(cxt, TRUE);
471 break;
472 case 't':
473 if (arg)
474 rc = mnt_context_set_fstype(cxt, arg);
475 break;
476 default:
477 return 1;
478 break;
479 }
480
481 return rc;
482}
483
68164f6c 484static int exec_helper(struct libmnt_context *cxt)
1bb1d80b
KZ
485{
486 char *o = NULL;
487 int rc;
488
489 assert(cxt);
490 assert(cxt->fs);
491 assert(cxt->helper);
1b56aae8
KZ
492 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
493
494 DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper));
1bb1d80b
KZ
495
496 rc = generate_helper_optstr(cxt, &o);
497 if (rc)
ea8f06f9 498 return -EINVAL;
1bb1d80b
KZ
499
500 DBG_FLUSH;
501
502 switch (fork()) {
503 case 0:
504 {
505 const char *args[12], *type;
506 int i = 0;
507
508 if (setgid(getgid()) < 0)
509 exit(EXIT_FAILURE);
510
511 if (setuid(getuid()) < 0)
512 exit(EXIT_FAILURE);
513
514 type = mnt_fs_get_fstype(cxt->fs);
515
97e23b5e
KZ
516 args[i++] = cxt->helper; /* 1 */
517 args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
1bb1d80b
KZ
518 args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */
519
9bf96901
KZ
520 /*
521 * TODO: remove the exception for "nfs", -s is documented
522 * for years should be usable everywhere.
523 */
524 if (mnt_context_is_sloppy(cxt) &&
525 type && startswith(type, "nfs"))
1bb1d80b 526 args[i++] = "-s"; /* 4 */
8c0797e7 527 if (mnt_context_is_fake(cxt))
1bb1d80b 528 args[i++] = "-f"; /* 5 */
8c0797e7 529 if (mnt_context_is_nomtab(cxt))
1bb1d80b 530 args[i++] = "-n"; /* 6 */
8c0797e7 531 if (mnt_context_is_verbose(cxt))
1bb1d80b
KZ
532 args[i++] = "-v"; /* 7 */
533 if (o) {
534 args[i++] = "-o"; /* 8 */
535 args[i++] = o; /* 9 */
536 }
537 if (type && !endswith(cxt->helper, type)) {
538 args[i++] = "-t"; /* 10 */
539 args[i++] = type; /* 11 */
540 }
541 args[i] = NULL; /* 12 */
542#ifdef CONFIG_LIBMOUNT_DEBUG
1bb1d80b
KZ
543 for (i = 0; args[i]; i++)
544 DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
545 i, args[i]));
546#endif
547 DBG_FLUSH;
548 execv(cxt->helper, (char * const *) args);
549 exit(EXIT_FAILURE);
550 }
551 default:
552 {
553 int st;
554 wait(&st);
555 cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
556
557 DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
558 cxt->helper, cxt->helper_status));
97e23b5e 559 cxt->helper_exec_status = rc = 0;
1bb1d80b
KZ
560 break;
561 }
562
563 case -1:
97e23b5e 564 cxt->helper_exec_status = rc = -errno;
1bb1d80b
KZ
565 DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
566 break;
567 }
568
31821efc 569 free(o);
1bb1d80b
KZ
570 return rc;
571}
572
6498ece0
KZ
573static int do_mount_additional(struct libmnt_context *cxt,
574 const char *target,
575 unsigned long flags,
576 int *syserr)
577{
578 struct list_head *p;
579
580 assert(cxt);
581 assert(target);
582
583 if (syserr)
584 *syserr = 0;
585
586 list_for_each(p, &cxt->addmounts) {
587 int rc;
588 struct libmnt_addmount *ad =
589 list_entry(p, struct libmnt_addmount, mounts);
590
591 DBG(CXT, mnt_debug_h(cxt, "mount(2) changing flag: 0x%08lx %s",
592 ad->mountflags,
593 ad->mountflags & MS_REC ? " (recursive)" : ""));
594
595 rc = mount("none", target, NULL,
596 ad->mountflags | (flags & MS_SILENT), NULL);
597 if (rc) {
598 if (syserr)
599 *syserr = -errno;
600 DBG(CXT, mnt_debug_h(cxt,
601 "mount(2) failed [errno=%d %m]",
602 errno));
603 return rc;
604 }
605 }
606
607 return 0;
608}
609
1bb1d80b
KZ
610/*
611 * The default is to use fstype from cxt->fs, this could be overwritten by
612 * @try_type argument.
9f7472b0
KZ
613 *
614 * Returns: 0 on success,
615 * >0 in case of mount(2) error (returns syscall errno),
616 * <0 in case of other errors.
1bb1d80b 617 */
68164f6c 618static int do_mount(struct libmnt_context *cxt, const char *try_type)
1bb1d80b 619{
97e23b5e 620 int rc = 0;
1bb1d80b
KZ
621 const char *src, *target, *type;
622 unsigned long flags;
623
624 assert(cxt);
625 assert(cxt->fs);
1b56aae8 626 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
1bb1d80b
KZ
627
628 if (try_type && !cxt->helper) {
629 rc = mnt_context_prepare_helper(cxt, "mount", try_type);
ae5afe07 630 if (rc)
1bb1d80b
KZ
631 return rc;
632 }
1bb1d80b 633
1bb1d80b
KZ
634 flags = cxt->mountflags;
635 src = mnt_fs_get_srcpath(cxt->fs);
636 target = mnt_fs_get_target(cxt->fs);
637
6498ece0
KZ
638 if (cxt->helper) {
639 rc = exec_helper(cxt);
640
641 if (mnt_context_helper_executed(cxt)
642 && mnt_context_get_helper_status(cxt) == 0
643 && !list_empty(&cxt->addmounts)
644 && do_mount_additional(cxt, target, flags, NULL))
645
646 return -MNT_ERR_APPLYFLAGS;
647 return rc;
648 }
649
772cce37 650 if (!target)
1bb1d80b 651 return -EINVAL;
41d6af28
KZ
652 if (!src) {
653 /* unnecessary, should be already resolved in
654 * mnt_context_prepare_srcpath(), but for sure... */
655 DBG(CXT, mnt_debug_h(cxt, "WARNING: source is NULL -- using \"none\"!"));
772cce37 656 src = "none";
41d6af28 657 }
97e23b5e
KZ
658 type = try_type ? : mnt_fs_get_fstype(cxt->fs);
659
1bb1d80b
KZ
660 if (!(flags & MS_MGC_MSK))
661 flags |= MS_MGC_VAL;
662
ea8f06f9 663 DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
1bb1d80b 664 "[source=%s, target=%s, type=%s, "
66bb8267 665 " mountflags=0x%08lx, mountdata=%s]",
379e8439 666 mnt_context_is_fake(cxt) ? "(FAKE) " : "",
1bb1d80b
KZ
667 src, target, type,
668 flags, cxt->mountdata ? "yes" : "<none>"));
669
6498ece0
KZ
670 if (mnt_context_is_fake(cxt)) {
671 /*
672 * fake
673 */
dbde1923 674 cxt->syscall_status = 0;
6498ece0
KZ
675
676 } else if (mnt_context_propagation_only(cxt)) {
677 /*
678 * propagation flags *only*
679 */
680 if (do_mount_additional(cxt, target, flags, &cxt->syscall_status))
681 return -MNT_ERR_APPLYFLAGS;
682 } else {
683 /*
684 * regular mount
685 */
ea8f06f9 686 if (mount(src, target, type, flags, cxt->mountdata)) {
97e23b5e 687 cxt->syscall_status = -errno;
dd369652 688 DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]",
97e23b5e 689 -cxt->syscall_status));
5982583a 690 return -cxt->syscall_status;
ea8f06f9
KZ
691 }
692 DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
97e23b5e 693 cxt->syscall_status = 0;
6498ece0
KZ
694
695 /*
696 * additional mounts for extra propagation flags
697 */
698 if (!list_empty(&cxt->addmounts)
699 && do_mount_additional(cxt, target, flags, NULL)) {
700
701 /* TODO: call umount? */
702 return -MNT_ERR_APPLYFLAGS;
703 }
1bb1d80b 704 }
97e23b5e
KZ
705
706 if (try_type && cxt->update) {
68164f6c 707 struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
97e23b5e
KZ
708 if (fs)
709 rc = mnt_fs_set_fstype(fs, try_type);
710 }
36bda5cb 711
97e23b5e
KZ
712 return rc;
713}
714
68164f6c 715static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
97e23b5e
KZ
716{
717 int neg = pattern && strncmp(pattern, "no", 2) == 0;
718 int rc = -EINVAL;
719 char **filesystems, **fp;
720
721 assert(cxt);
46cfd4a2 722 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
97e23b5e
KZ
723
724 if (!neg && pattern) {
725 /*
726 * try all types from the list
727 */
728 char *p, *p0;
729
26e42d81 730 DBG(CXT, mnt_debug_h(cxt, "trying to mount by FS pattern list"));
b0bb8fb6 731
97e23b5e
KZ
732 p0 = p = strdup(pattern);
733 if (!p)
734 return -ENOMEM;
735 do {
736 char *end = strchr(p, ',');
737 if (end)
738 *end = '\0';
739 rc = do_mount(cxt, p);
740 p = end ? end + 1 : NULL;
ae5afe07 741
97e23b5e
KZ
742 } while (!mnt_context_get_status(cxt) && p);
743
744 free(p0);
745
746 if (mnt_context_get_status(cxt))
747 return rc;
748 }
749
750 /*
751 * try /etc/filesystems and /proc/filesystems
752 */
26e42d81 753 DBG(CXT, mnt_debug_h(cxt, "trying to mount by filesystems lists"));
b0bb8fb6 754
97e23b5e
KZ
755 rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
756 if (rc)
757 return rc;
758
3de77c21
KZ
759 if (filesystems == NULL)
760 return -MNT_ERR_NOFSTYPE;
761
97e23b5e
KZ
762 for (fp = filesystems; *fp; fp++) {
763 rc = do_mount(cxt, *fp);
764 if (mnt_context_get_status(cxt))
765 break;
ec8121b1
KZ
766 if (mnt_context_get_syscall_errno(cxt) != EINVAL &&
767 mnt_context_get_syscall_errno(cxt) != ENODEV)
ae5afe07 768 break;
97e23b5e
KZ
769 }
770 mnt_free_filesystems(filesystems);
771 return rc;
1bb1d80b
KZ
772}
773
774/**
dbde1923
KZ
775 * mnt_context_prepare_mount:
776 * @cxt: context
1bb1d80b 777 *
dbde1923 778 * Prepare context for mounting, unnecessary for mnt_context_mount().
1bb1d80b 779 *
dbde1923 780 * Returns: negative number on error, zero on success
1bb1d80b 781 */
68164f6c 782int mnt_context_prepare_mount(struct libmnt_context *cxt)
1bb1d80b 783{
dbde1923 784 int rc = -EINVAL;
1bb1d80b 785
1d0cd73f
KZ
786 assert(cxt);
787 assert(cxt->fs);
97e23b5e
KZ
788 assert(cxt->helper_exec_status == 1);
789 assert(cxt->syscall_status == 1);
1bb1d80b 790
c70d9d76 791 if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
1bb1d80b 792 return -EINVAL;
1d0cd73f
KZ
793 if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
794 return -EINVAL;
dbde1923
KZ
795 if (cxt->flags & MNT_FL_PREPARED)
796 return 0;
797
1d0cd73f 798 cxt->action = MNT_ACT_MOUNT;
1bb1d80b 799
1b56aae8
KZ
800 DBG(CXT, mnt_debug_h(cxt, "mount: preparing"));
801
f63173b2 802 rc = mnt_context_apply_fstab(cxt);
1bb1d80b 803 if (!rc)
68164f6c 804 rc = mnt_context_merge_mflags(cxt);
1bb1d80b
KZ
805 if (!rc)
806 rc = evaluate_permissions(cxt);
807 if (!rc)
808 rc = fix_optstr(cxt);
809 if (!rc)
810 rc = mnt_context_prepare_srcpath(cxt);
c59cf20c 811 if (!rc)
f5017242 812 rc = mnt_context_prepare_target(cxt);
1bb1d80b
KZ
813 if (!rc)
814 rc = mnt_context_guess_fstype(cxt);
815 if (!rc)
816 rc = mnt_context_prepare_helper(cxt, "mount", NULL);
1d0cd73f
KZ
817 if (rc) {
818 DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed"));
819 return rc;
1bb1d80b 820 }
dbde1923
KZ
821 cxt->flags |= MNT_FL_PREPARED;
822 return rc;
823}
824
825/**
826 * mnt_context_do_mount
827 * @cxt: context
828 *
63de90d4 829 * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
dbde1923 830 *
8ab6accf
KZ
831 * Note that this function could be called only once. If you want to mount
832 * another source or target than you have to call mnt_reset_context().
833 *
871ffd09 834 * If you want to call mount(2) for the same source and target with a different
379e8439 835 * mount flags or fstype then call mnt_context_reset_status() and then try
8ab6accf
KZ
836 * again mnt_context_do_mount().
837 *
5982583a 838 * WARNING: non-zero return code does not mean that mount(2) syscall or
455fe9a0 839 * mount.type helper wasn't successfully called.
5982583a
KZ
840 *
841 * Check mnt_context_get_status() after error!
842*
843 * Returns: 0 on success;
844 * >0 in case of mount(2) error (returns syscall errno),
845 * <0 in case of other errors.
dbde1923 846 */
68164f6c 847int mnt_context_do_mount(struct libmnt_context *cxt)
dbde1923
KZ
848{
849 const char *type;
f9906424 850 int res;
dbde1923
KZ
851
852 assert(cxt);
853 assert(cxt->fs);
854 assert(cxt->helper_exec_status == 1);
855 assert(cxt->syscall_status == 1);
856 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
857 assert((cxt->flags & MNT_FL_PREPARED));
cfb9db30 858 assert((cxt->action == MNT_ACT_MOUNT));
1bb1d80b 859
1b56aae8
KZ
860 DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));
861
1bb1d80b 862 if (!(cxt->flags & MNT_FL_MOUNTDATA))
76a06ca4 863 cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);
1bb1d80b
KZ
864
865 type = mnt_fs_get_fstype(cxt->fs);
bbf9ce79
GFM
866 if (type) {
867 if (strchr(type, ','))
58c41e15 868 /* this only happens if fstab contains list of filesystems */
bbf9ce79
GFM
869 res = do_mount_by_pattern(cxt, type);
870 else
871 res = do_mount(cxt, NULL);
872 } else
f9906424
KZ
873 res = do_mount_by_pattern(cxt, cxt->fstype_pattern);
874
875 if (mnt_context_get_status(cxt)
379e8439 876 && !mnt_context_is_fake(cxt)
f9906424
KZ
877 && !cxt->helper) {
878 /*
879 * Mounted by mount(2), do some post-mount checks
880 *
881 * Kernel allows to use MS_RDONLY for bind mounts, but the
882 * read-only request could be silently ignored. Check it to
883 * avoid 'ro' in mtab and 'rw' in /proc/mounts.
884 */
885 if ((cxt->mountflags & MS_BIND)
886 && (cxt->mountflags & MS_RDONLY)
887 && !mnt_is_readonly(mnt_context_get_target(cxt)))
888
889 mnt_context_set_mflags(cxt,
890 cxt->mountflags & ~MS_RDONLY);
891
892
893 /* Kernel can silently add MS_RDONLY flag when mounting file
894 * system that does not have write support. Check this to avoid
895 * 'ro' in /proc/mounts and 'rw' in mtab.
896 */
6498ece0
KZ
897 if (!(cxt->mountflags & (MS_RDONLY | MS_MOVE))
898 && !mnt_context_propagation_only(cxt)
f9906424
KZ
899 && mnt_is_readonly(mnt_context_get_target(cxt)))
900
901 mnt_context_set_mflags(cxt,
902 cxt->mountflags | MS_RDONLY);
903 }
dbde1923 904
f9906424 905 return res;
dbde1923
KZ
906}
907
908/**
cfb9db30
KZ
909 * mnt_context_finalize_mount:
910 * @cxt: context
911 *
912 * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
913 * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
914 *
915 * Returns: negative number on error, 0 on success.
916 */
917int mnt_context_finalize_mount(struct libmnt_context *cxt)
918{
919 int rc;
920
921 assert(cxt);
922 assert(cxt->fs);
923 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
924 assert((cxt->flags & MNT_FL_PREPARED));
925
926 rc = mnt_context_prepare_update(cxt);
927 if (!rc)
928 rc = mnt_context_update_tabs(cxt);;
929 return rc;
930}
931
932/**
933 * mnt_context_mount:
dbde1923
KZ
934 * @cxt: mount context
935 *
cfb9db30 936 * High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type).
dbde1923 937 *
cfb9db30 938 * This is similar to:
dbde1923
KZ
939 *
940 * mnt_context_prepare_mount(cxt);
941 * mnt_context_do_mount(cxt);
942 * mnt_context_finalize_mount(cxt);
943 *
944 * See also mnt_context_disable_helpers().
945 *
8ab6accf
KZ
946 * Note that this function could be called only once. If you want to mount with
947 * different setting than you have to call mnt_reset_context(). It's NOT enough
ee314075 948 * to call mnt_context_reset_status() if you want call this function more than
8ab6accf
KZ
949 * once, whole context has to be reseted.
950 *
5982583a 951 * WARNING: non-zero return code does not mean that mount(2) syscall or
455fe9a0 952 * mount.type helper wasn't successfully called.
5982583a
KZ
953 *
954 * Check mnt_context_get_status() after error!
9f7472b0 955 *
5982583a
KZ
956 * Returns: 0 on success;
957 * >0 in case of mount(2) error (returns syscall errno),
958 * <0 in case of other errors.
dbde1923 959 */
cfb9db30 960int mnt_context_mount(struct libmnt_context *cxt)
dbde1923
KZ
961{
962 int rc;
963
964 assert(cxt);
965 assert(cxt->fs);
966 assert(cxt->helper_exec_status == 1);
967 assert(cxt->syscall_status == 1);
968
969 rc = mnt_context_prepare_mount(cxt);
970 if (!rc)
971 rc = mnt_context_prepare_update(cxt);
972 if (!rc)
973 rc = mnt_context_do_mount(cxt);
1bb1d80b 974
1d0cd73f
KZ
975 /* TODO: if mtab update is expected then check if the
976 * target is really mounted read-write to avoid 'ro' in
977 * mtab and 'rw' in /proc/mounts.
1bb1d80b 978 */
dbde1923
KZ
979 if (!rc)
980 rc = mnt_context_update_tabs(cxt);
981 return rc;
982}
983
9f7472b0
KZ
984/**
985 * mnt_context_next_mount:
986 * @cxt: context
987 * @itr: iterator
988 * @fs: returns the current filesystem
e6ecd606 989 * @mntrc: returns the return code from mnt_context_mount()
9f7472b0
KZ
990 * @ignored: returns 1 for not matching and 2 for already mounted filesystems
991 *
992 * This function tries to mount the next filesystem from fstab (as returned by
993 * mnt_context_get_fstab()). See also mnt_context_set_fstab().
994 *
995 * You can filter out filesystems by:
63de90d4
KZ
996 * mnt_context_set_options_pattern() to simulate mount -a -O pattern
997 * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern
9f7472b0
KZ
998 *
999 * If the filesystem is already mounted or does not match defined criteria,
1000 * then the mnt_context_next_mount() function returns zero, but the @ignored is
1001 * non-zero. Note that the root filesystem and filesystems with "noauto" option
1002 * are always ignored.
1003 *
63de90d4 1004 * If mount(2) syscall or mount.type helper failed, then the
9f7472b0
KZ
1005 * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
1006 * Use also mnt_context_get_status() to check if the filesystem was
1007 * successfully mounted.
1008 *
1009 * Returns: 0 on success,
1010 * <0 in case of error (!= mount(2) errors)
1011 * 1 at the end of the list.
1012 */
1013int mnt_context_next_mount(struct libmnt_context *cxt,
1014 struct libmnt_iter *itr,
1015 struct libmnt_fs **fs,
1016 int *mntrc,
1017 int *ignored)
1018{
1019 struct libmnt_table *fstab, *mtab;
1020 const char *o, *tgt;
1021 int rc, mounted = 0;
1022
1023 if (ignored)
1024 *ignored = 0;
1025 if (mntrc)
1026 *mntrc = 0;
1027
1028 if (!cxt || !fs || !itr)
1029 return -EINVAL;
1030
1031 mtab = cxt->mtab;
1032 cxt->mtab = NULL; /* do not reset mtab */
1033 mnt_reset_context(cxt);
1034 cxt->mtab = mtab;
1035
1036 rc = mnt_context_get_fstab(cxt, &fstab);
1037 if (rc)
1038 return rc;
1039
1040 rc = mnt_table_next_fs(fstab, itr, fs);
1041 if (rc != 0)
1042 return rc; /* more filesystems (or error) */
1043
1044 o = mnt_fs_get_user_options(*fs);
1045 tgt = mnt_fs_get_target(*fs);
1046
1047 DBG(CXT, mnt_debug_h(cxt, "next-mount: trying %s", tgt));
1048
1049 /* ignore swap */
c70d9d76 1050 if (mnt_fs_is_swaparea(*fs) ||
9f7472b0
KZ
1051
1052 /* ignore root filesystem */
c70d9d76 1053 (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
9f7472b0
KZ
1054
1055 /* ignore noauto filesystems */
1056 (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||
1057
1058 /* ignore filesystems not match with options patterns */
1059 (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
1060 cxt->fstype_pattern)) ||
1061
1062 /* ignore filesystems not match with type patterns */
1063 (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
1064 cxt->optstr_pattern))) {
1065 if (ignored)
1066 *ignored = 1;
1067 DBG(CXT, mnt_debug_h(cxt, "next-mount: not-match "
1068 "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
1069 mnt_fs_get_fstype(*fs),
1070 cxt->fstype_pattern,
1071 mnt_fs_get_options(*fs),
1072 cxt->optstr_pattern));
1073 return 0;
1074 }
1075
1076 /* ignore already mounted filesystems */
1077 rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
1078 if (rc)
1079 return rc;
1080 if (mounted) {
1081 if (ignored)
1082 *ignored = 2;
1083 return 0;
1084 }
1085
d2c97887
KZ
1086 if (mnt_context_is_fork(cxt)) {
1087 rc = mnt_fork_context(cxt);
1088 if (rc)
1089 return rc; /* fork error */
1090
1091 if (mnt_context_is_parent(cxt)) {
1092 return 0; /* parent */
1093 }
1094 }
1095
1096 /* child or non-forked */
1097
9f7472b0 1098 rc = mnt_context_set_fs(cxt, *fs);
d2c97887
KZ
1099 if (!rc) {
1100 rc = mnt_context_mount(cxt);
1101 if (mntrc)
1102 *mntrc = rc;
1103 }
1104
1105 if (mnt_context_is_child(cxt)) {
1106 DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc));
1107 DBG_FLUSH;
1108 exit(rc);
1109 }
9f7472b0
KZ
1110 return 0;
1111}
1112