]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/context_mount.c
libmount: fix docs typo
[thirdparty/util-linux.git] / libmount / src / context_mount.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2010-2018 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 */
12
13 /**
14 * SECTION: context-mount
15 * @title: Mount context
16 * @short_description: high-level API to mount operation.
17 */
18
19 #ifdef HAVE_LIBSELINUX
20 #include <selinux/selinux.h>
21 #include <selinux/context.h>
22 #endif
23
24 #include <sys/wait.h>
25 #include <sys/mount.h>
26
27 #include "linux_version.h"
28 #include "mountP.h"
29 #include "strutils.h"
30
31 /*
32 * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
33 * to bypass this restriction we call mount(2) per flag. It's really not a perfect
34 * solution, but it's the same like to execute multiple mount(8) commands.
35 *
36 * We use cxt->addmounts (additional mounts) list to keep order of the requested
37 * flags changes.
38 */
39 struct libmnt_addmount *mnt_new_addmount(void)
40 {
41 struct libmnt_addmount *ad = calloc(1, sizeof(*ad));
42 if (!ad)
43 return NULL;
44
45 INIT_LIST_HEAD(&ad->mounts);
46 return ad;
47 }
48
49 void mnt_free_addmount(struct libmnt_addmount *ad)
50 {
51 if (!ad)
52 return;
53 list_del(&ad->mounts);
54 free(ad);
55 }
56
57 static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
58 struct libmnt_addmount *ad)
59 {
60 assert(cxt);
61 assert(ad);
62
63 if (!list_empty(&ad->mounts))
64 return -EINVAL;
65
66 DBG(CXT, ul_debugobj(cxt,
67 "mount: add additional flag: 0x%08lx",
68 ad->mountflags));
69
70 list_add_tail(&ad->mounts, &cxt->addmounts);
71 return 0;
72 }
73
74 /*
75 * add additional mount(2) syscall requests when necessary to set propagation flags
76 * after regular mount(2).
77 */
78 static int init_propagation(struct libmnt_context *cxt)
79 {
80 char *name;
81 char *opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
82 size_t namesz;
83 struct libmnt_optmap const *maps[1];
84 int rec_count = 0;
85
86 if (!opts)
87 return 0;
88
89 DBG(CXT, ul_debugobj(cxt, "mount: initialize additional propagation mounts"));
90
91 maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
92
93 while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
94 const struct libmnt_optmap *ent;
95 struct libmnt_addmount *ad;
96 int rc;
97
98 if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent) || !ent)
99 continue;
100
101 DBG(CXT, ul_debugobj(cxt, " checking %s", ent->name));
102
103 /* Note that MS_REC may be used for more flags, so we have to keep
104 * track about number of recursive options to keep the MS_REC in the
105 * mountflags if necessary.
106 */
107 if (ent->id & MS_REC)
108 rec_count++;
109
110 if (!(ent->id & MS_PROPAGATION))
111 continue;
112
113 ad = mnt_new_addmount();
114 if (!ad)
115 return -ENOMEM;
116
117 ad->mountflags = ent->id;
118 DBG(CXT, ul_debugobj(cxt, " adding extra mount(2) call for %s", ent->name));
119 rc = mnt_context_append_additional_mount(cxt, ad);
120 if (rc)
121 return rc;
122
123 DBG(CXT, ul_debugobj(cxt, " removing %s from primary mount(2) call", ent->name));
124 cxt->mountflags &= ~ent->id;
125
126 if (ent->id & MS_REC)
127 rec_count--;
128 }
129
130 if (rec_count)
131 cxt->mountflags |= MS_REC;
132
133 return 0;
134 }
135
136 /*
137 * add additional mount(2) syscall request to implement "bind,<flags>", the first regular
138 * mount(2) is the "bind" operation, the second is "remount,bind,<flags>" call.
139 */
140 static int init_bind_remount(struct libmnt_context *cxt)
141 {
142 struct libmnt_addmount *ad;
143 int rc;
144
145 assert(cxt);
146 assert(cxt->mountflags & MS_BIND);
147 assert(!(cxt->mountflags & MS_REMOUNT));
148
149 DBG(CXT, ul_debugobj(cxt, "mount: initialize additional ro,bind mount"));
150
151 ad = mnt_new_addmount();
152 if (!ad)
153 return -ENOMEM;
154
155 ad->mountflags = cxt->mountflags;
156 ad->mountflags |= (MS_REMOUNT | MS_BIND);
157
158 rc = mnt_context_append_additional_mount(cxt, ad);
159 if (rc)
160 return rc;
161
162 return 0;
163 }
164
165 #if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK)
166 struct libmnt_optname {
167 const char *name;
168 size_t namesz;
169 };
170
171 #define DEF_OPTNAME(n) { .name = n, .namesz = sizeof(n) - 1 }
172 #define DEF_OPTNAME_LAST { .name = NULL }
173
174 static int is_option(const char *name, size_t namesz,
175 const struct libmnt_optname *names)
176 {
177 const struct libmnt_optname *p;
178
179 for (p = names; p && p->name; p++) {
180 if (p->namesz == namesz
181 && strncmp(name, p->name, namesz) == 0)
182 return 1;
183 }
184
185 return 0;
186 }
187 #endif /* HAVE_LIBSELINUX || HAVE_SMACK */
188
189 /*
190 * this has to be called after mnt_context_evaluate_permissions()
191 */
192 static int fix_optstr(struct libmnt_context *cxt)
193 {
194 int rc = 0;
195 struct libmnt_ns *ns_old;
196 char *next;
197 char *name, *val;
198 size_t namesz, valsz;
199 struct libmnt_fs *fs;
200 #ifdef HAVE_LIBSELINUX
201 int se_fix = 0, se_rem = 0;
202 static const struct libmnt_optname selinux_options[] = {
203 DEF_OPTNAME("context"),
204 DEF_OPTNAME("fscontext"),
205 DEF_OPTNAME("defcontext"),
206 DEF_OPTNAME("rootcontext"),
207 DEF_OPTNAME("seclabel"),
208 DEF_OPTNAME_LAST
209 };
210 #endif
211 #ifdef HAVE_SMACK
212 int sm_rem = 0;
213 static const struct libmnt_optname smack_options[] = {
214 DEF_OPTNAME("smackfsdef"),
215 DEF_OPTNAME("smackfsfloor"),
216 DEF_OPTNAME("smackfshat"),
217 DEF_OPTNAME("smackfsroot"),
218 DEF_OPTNAME("smackfstransmute"),
219 DEF_OPTNAME_LAST
220 };
221 #endif
222 assert(cxt);
223 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
224
225 if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
226 return 0;
227
228 DBG(CXT, ul_debugobj(cxt, "mount: fixing optstr"));
229
230 fs = cxt->fs;
231
232 /*
233 * The "user" options is our business (so we can modify the option),
234 * the exception is command line for /sbin/mount.<type> helpers. Let's
235 * save the original user=<name> to call the helpers with an unchanged
236 * "user" setting.
237 */
238 if (cxt->user_mountflags & MNT_MS_USER) {
239 if (!mnt_optstr_get_option(fs->user_optstr,
240 "user", &val, &valsz) && val) {
241 cxt->orig_user = strndup(val, valsz);
242 if (!cxt->orig_user) {
243 rc = -ENOMEM;
244 goto done;
245 }
246 }
247 cxt->flags |= MNT_FL_SAVED_USER;
248 }
249
250 /*
251 * Sync mount options with mount flags
252 */
253 DBG(CXT, ul_debugobj(cxt, "mount: fixing vfs optstr"));
254 rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
255 mnt_get_builtin_optmap(MNT_LINUX_MAP));
256 if (rc)
257 goto done;
258
259 DBG(CXT, ul_debugobj(cxt, "mount: fixing user optstr"));
260 rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
261 mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
262 if (rc)
263 goto done;
264
265 if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
266 free(fs->vfs_optstr);
267 fs->vfs_optstr = NULL;
268 }
269 if (fs->user_optstr && *fs->user_optstr == '\0') {
270 free(fs->user_optstr);
271 fs->user_optstr = NULL;
272 }
273 if (cxt->mountflags & MS_PROPAGATION) {
274 rc = init_propagation(cxt);
275 if (rc)
276 return rc;
277 }
278 if ((cxt->mountflags & MS_BIND)
279 && (cxt->mountflags & MNT_BIND_SETTABLE)
280 && !(cxt->mountflags & MS_REMOUNT)) {
281 rc = init_bind_remount(cxt);
282 if (rc)
283 return rc;
284 }
285
286 next = fs->fs_optstr;
287
288 #ifdef HAVE_LIBSELINUX
289 if (!is_selinux_enabled())
290 /* Always remove SELinux garbage if SELinux disabled */
291 se_rem = 1;
292 else if (cxt->mountflags & MS_REMOUNT)
293 /*
294 * Linux kernel < 2.6.39 does not allow to remount with any
295 * selinux specific mount options.
296 *
297 * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65
298 * 026eb167ae77244458fa4b4b9fc171209c079ba7
299 * fix this odd behavior, so we don't have to care about it in
300 * userspace.
301 */
302 se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39);
303 else
304 /* For normal mount, contexts are translated */
305 se_fix = 1;
306
307 if (!se_rem) {
308 /* de-duplicate SELinux options */
309 const struct libmnt_optname *p;
310 for (p = selinux_options; p && p->name; p++)
311 mnt_optstr_deduplicate_option(&fs->fs_optstr, p->name);
312 }
313 #endif
314 #ifdef HAVE_SMACK
315 if (access("/sys/fs/smackfs", F_OK) != 0)
316 sm_rem = 1;
317 #endif
318 while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
319
320 if (namesz == 3 && !strncmp(name, "uid", 3))
321 rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
322 else if (namesz == 3 && !strncmp(name, "gid", 3))
323 rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
324 #ifdef HAVE_LIBSELINUX
325 else if ((se_rem || se_fix)
326 && is_option(name, namesz, selinux_options)) {
327
328 if (se_rem) {
329 /* remove context= option */
330 next = name;
331 rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
332 name,
333 val ? val + valsz :
334 name + namesz);
335 } else if (se_fix && val && valsz)
336 /* translate selinux contexts */
337 rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
338 val, valsz, &next);
339 }
340 #endif
341 #ifdef HAVE_SMACK
342 else if (sm_rem && is_option(name, namesz, smack_options)) {
343
344 next = name;
345 rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
346 name,
347 val ? val + valsz : name + namesz);
348 }
349 #endif
350 if (rc)
351 goto done;
352 }
353
354
355 if (!rc && cxt->restricted && (cxt->user_mountflags & MNT_MS_USER)) {
356 ns_old = mnt_context_switch_origin_ns(cxt);
357 if (!ns_old)
358 return -MNT_ERR_NAMESPACE;
359
360 rc = mnt_optstr_fix_user(&fs->user_optstr);
361
362 if (!mnt_context_switch_ns(cxt, ns_old))
363 return -MNT_ERR_NAMESPACE;
364 }
365
366 /* refresh merged optstr */
367 free(fs->optstr);
368 fs->optstr = NULL;
369 fs->optstr = mnt_fs_strdup_options(fs);
370 done:
371 cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
372
373 DBG(CXT, ul_debugobj(cxt, "fixed options [rc=%d]: "
374 "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
375 fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
376
377 if (rc)
378 rc = -MNT_ERR_MOUNTOPT;
379 return rc;
380 }
381
382 /*
383 * Converts the already evaluated and fixed options to the form that is compatible
384 * with /sbin/mount.type helpers.
385 */
386 static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
387 {
388 struct libmnt_optmap const *maps[2];
389 char *next, *name, *val;
390 size_t namesz, valsz;
391 int rc = 0;
392
393 assert(cxt);
394 assert(cxt->fs);
395 assert(optstr);
396
397 DBG(CXT, ul_debugobj(cxt, "mount: generate helper mount options"));
398
399 *optstr = mnt_fs_strdup_options(cxt->fs);
400 if (!*optstr)
401 return -ENOMEM;
402
403 if ((cxt->user_mountflags & MNT_MS_USER) ||
404 (cxt->user_mountflags & MNT_MS_USERS)) {
405 /*
406 * This is unnecessary for real user-mounts as mount.<type>
407 * helpers always have to follow fstab rather than mount
408 * options on the command line.
409 *
410 * However, if you call mount.<type> as root, then the helper follows
411 * the command line. If there is (for example) "user,exec" in fstab,
412 * then we have to manually append the "exec" back to the options
413 * string, because there is nothing like MS_EXEC (we only have
414 * MS_NOEXEC in mount flags and we don't care about the original
415 * mount string in libmount for VFS options).
416 */
417 if (!(cxt->mountflags & MS_NOEXEC))
418 mnt_optstr_append_option(optstr, "exec", NULL);
419 if (!(cxt->mountflags & MS_NOSUID))
420 mnt_optstr_append_option(optstr, "suid", NULL);
421 if (!(cxt->mountflags & MS_NODEV))
422 mnt_optstr_append_option(optstr, "dev", NULL);
423 }
424
425
426 if (cxt->flags & MNT_FL_SAVED_USER)
427 rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
428 if (rc)
429 goto err;
430
431 /* remove userspace options with MNT_NOHLPS flag */
432 maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
433 maps[1] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
434 next = *optstr;
435
436 while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
437 const struct libmnt_optmap *ent;
438
439 mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
440 if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
441 next = name;
442 rc = mnt_optstr_remove_option_at(optstr, name,
443 val ? val + valsz : name + namesz);
444 if (rc)
445 goto err;
446 }
447 }
448
449 return rc;
450 err:
451 free(*optstr);
452 *optstr = NULL;
453 return rc;
454 }
455
456 /*
457 * this has to be called before fix_optstr()
458 *
459 * Note that user=<name> may be used by some filesystems as a filesystem
460 * specific option (e.g. cifs). Yes, developers of such filesystems have
461 * allocated pretty hot place in hell...
462 */
463 static int evaluate_permissions(struct libmnt_context *cxt)
464 {
465 unsigned long u_flags = 0;
466
467 assert(cxt);
468 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
469
470 if (!cxt->fs)
471 return 0;
472
473 DBG(CXT, ul_debugobj(cxt, "mount: evaluating permissions"));
474
475 mnt_context_get_user_mflags(cxt, &u_flags);
476
477 if (!mnt_context_is_restricted(cxt)) {
478 /*
479 * superuser mount
480 */
481 cxt->user_mountflags &= ~MNT_MS_OWNER;
482 cxt->user_mountflags &= ~MNT_MS_GROUP;
483 } else {
484 /*
485 * user mount
486 */
487 if (!mnt_context_tab_applied(cxt))
488 {
489 DBG(CXT, ul_debugobj(cxt, "perms: fstab not applied, ignore user mount"));
490 return -EPERM;
491 }
492
493 /*
494 * MS_OWNERSECURE and MS_SECURE mount options are already
495 * applied by mnt_optstr_get_flags() in mnt_context_merge_mflags()
496 * if "user" (but no user=<name> !) options is set.
497 *
498 * Let's ignore all user=<name> (if <name> is set) requests.
499 */
500 if (cxt->user_mountflags & MNT_MS_USER) {
501 size_t valsz = 0;
502
503 if (!mnt_optstr_get_option(cxt->fs->user_optstr,
504 "user", NULL, &valsz) && valsz) {
505
506 DBG(CXT, ul_debugobj(cxt, "perms: user=<name> detected, ignore"));
507 cxt->user_mountflags &= ~MNT_MS_USER;
508 }
509 }
510
511 /*
512 * MS_OWNER: Allow owners to mount when fstab contains the
513 * owner option. Note that this should never be used in a high
514 * security environment, but may be useful to give people at
515 * the console the possibility of mounting a floppy. MS_GROUP:
516 * Allow members of device group to mount. (Martin Dickopp)
517 */
518 if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
519 struct stat sb;
520 struct libmnt_cache *cache = NULL;
521 char *xsrc = NULL;
522 const char *srcpath = mnt_fs_get_srcpath(cxt->fs);
523
524 if (!srcpath) { /* Ah... source is TAG */
525 cache = mnt_context_get_cache(cxt);
526 xsrc = mnt_resolve_spec(
527 mnt_context_get_source(cxt),
528 cache);
529 srcpath = xsrc;
530 }
531 if (!srcpath) {
532 DBG(CXT, ul_debugobj(cxt, "perms: src undefined"));
533 return -EPERM;
534 }
535
536 if (strncmp(srcpath, "/dev/", 5) == 0 &&
537 stat(srcpath, &sb) == 0 &&
538 (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
539 ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))
540
541 cxt->user_mountflags |= MNT_MS_USER;
542
543 if (!cache)
544 free(xsrc);
545 }
546
547 if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
548 DBG(CXT, ul_debugobj(cxt, "permissions evaluation ends with -EPERMS"));
549 return -EPERM;
550 }
551 }
552
553 return 0;
554 }
555
556 /*
557 * mnt_context_helper_setopt() backend
558 *
559 * This function applies the mount.type command line option (for example parsed
560 * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
561 * then 1 is returned.
562 *
563 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
564 */
565 int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
566 {
567 int rc = -EINVAL;
568
569 assert(cxt);
570 assert(cxt->action == MNT_ACT_MOUNT);
571
572 switch(c) {
573 case 'f':
574 rc = mnt_context_enable_fake(cxt, TRUE);
575 break;
576 case 'n':
577 rc = mnt_context_disable_mtab(cxt, TRUE);
578 break;
579 case 'r':
580 rc = mnt_context_append_options(cxt, "ro");
581 break;
582 case 'v':
583 rc = mnt_context_enable_verbose(cxt, TRUE);
584 break;
585 case 'w':
586 rc = mnt_context_append_options(cxt, "rw");
587 break;
588 case 'o':
589 if (arg)
590 rc = mnt_context_append_options(cxt, arg);
591 break;
592 case 's':
593 rc = mnt_context_enable_sloppy(cxt, TRUE);
594 break;
595 case 't':
596 if (arg)
597 rc = mnt_context_set_fstype(cxt, arg);
598 break;
599 case 'N':
600 if (arg)
601 rc = mnt_context_set_target_ns(cxt, arg);
602 break;
603 default:
604 return 1;
605 }
606
607 return rc;
608 }
609
610 static int exec_helper(struct libmnt_context *cxt)
611 {
612 char *o = NULL, *namespace = NULL;
613 struct libmnt_ns *ns_tgt = mnt_context_get_target_ns(cxt);
614 int rc;
615 pid_t pid;
616
617 assert(cxt);
618 assert(cxt->fs);
619 assert(cxt->helper);
620 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
621
622 DBG(CXT, ul_debugobj(cxt, "mount: executing helper %s", cxt->helper));
623
624 rc = generate_helper_optstr(cxt, &o);
625 if (rc)
626 return -EINVAL;
627
628 if (ns_tgt->fd != -1
629 && asprintf(&namespace, "/proc/%i/fd/%i",
630 getpid(), ns_tgt->fd) == -1) {
631 free(o);
632 return -ENOMEM;
633 }
634
635 DBG_FLUSH;
636
637 pid = fork();
638 switch (pid) {
639 case 0:
640 {
641 const char *args[14], *type;
642 int i = 0;
643
644 if (setgid(getgid()) < 0)
645 _exit(EXIT_FAILURE);
646
647 if (setuid(getuid()) < 0)
648 _exit(EXIT_FAILURE);
649
650 if (!mnt_context_switch_origin_ns(cxt))
651 _exit(EXIT_FAILURE);
652
653 type = mnt_fs_get_fstype(cxt->fs);
654
655 args[i++] = cxt->helper; /* 1 */
656 args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
657 args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */
658
659 if (mnt_context_is_sloppy(cxt))
660 args[i++] = "-s"; /* 4 */
661 if (mnt_context_is_fake(cxt))
662 args[i++] = "-f"; /* 5 */
663 if (mnt_context_is_nomtab(cxt))
664 args[i++] = "-n"; /* 6 */
665 if (mnt_context_is_verbose(cxt))
666 args[i++] = "-v"; /* 7 */
667 if (o) {
668 args[i++] = "-o"; /* 8 */
669 args[i++] = o; /* 9 */
670 }
671 if (type
672 && strchr(type, '.')
673 && !endswith(cxt->helper, type)) {
674 args[i++] = "-t"; /* 10 */
675 args[i++] = type; /* 11 */
676 }
677 if (namespace) {
678 args[i++] = "-N"; /* 11 */
679 args[i++] = namespace; /* 12 */
680 }
681 args[i] = NULL; /* 13 */
682 for (i = 0; args[i]; i++)
683 DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"",
684 i, args[i]));
685 DBG_FLUSH;
686 execv(cxt->helper, (char * const *) args);
687 _exit(EXIT_FAILURE);
688 }
689 default:
690 {
691 int st;
692
693 if (waitpid(pid, &st, 0) == (pid_t) -1) {
694 cxt->helper_status = -1;
695 rc = -errno;
696 } else {
697 cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
698 cxt->helper_exec_status = rc = 0;
699 }
700 DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d, rc=%d%s]",
701 cxt->helper,
702 cxt->helper_status, rc,
703 rc ? " waitpid failed" : ""));
704 break;
705 }
706
707 case -1:
708 cxt->helper_exec_status = rc = -errno;
709 DBG(CXT, ul_debugobj(cxt, "fork() failed"));
710 break;
711 }
712
713 free(o);
714 return rc;
715 }
716
717 static int do_mount_additional(struct libmnt_context *cxt,
718 const char *target,
719 unsigned long flags,
720 int *syserr)
721 {
722 struct list_head *p;
723
724 assert(cxt);
725 assert(target);
726
727 if (syserr)
728 *syserr = 0;
729
730 list_for_each(p, &cxt->addmounts) {
731 int rc;
732 struct libmnt_addmount *ad =
733 list_entry(p, struct libmnt_addmount, mounts);
734
735 DBG(CXT, ul_debugobj(cxt, "mount(2) changing flag: 0x%08lx %s",
736 ad->mountflags,
737 ad->mountflags & MS_REC ? " (recursive)" : ""));
738
739 rc = mount("none", target, NULL,
740 ad->mountflags | (flags & MS_SILENT), NULL);
741 if (rc) {
742 if (syserr)
743 *syserr = -errno;
744 DBG(CXT, ul_debugobj(cxt,
745 "mount(2) failed [errno=%d %m]",
746 errno));
747 return rc;
748 }
749 }
750
751 return 0;
752 }
753
754 /*
755 * The default is to use fstype from cxt->fs, this could be overwritten by
756 * @try_type argument. If @try_type is specified then mount with MS_SILENT.
757 *
758 * Returns: 0 on success,
759 * >0 in case of mount(2) error (returns syscall errno),
760 * <0 in case of other errors.
761 */
762 static int do_mount(struct libmnt_context *cxt, const char *try_type)
763 {
764 int rc = 0;
765 const char *src, *target, *type;
766 unsigned long flags;
767
768 assert(cxt);
769 assert(cxt->fs);
770 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
771
772 if (try_type && !cxt->helper) {
773 rc = mnt_context_prepare_helper(cxt, "mount", try_type);
774 if (rc)
775 return rc;
776 }
777
778 flags = cxt->mountflags;
779 src = mnt_fs_get_srcpath(cxt->fs);
780 target = mnt_fs_get_target(cxt->fs);
781
782 if (cxt->helper) {
783 rc = exec_helper(cxt);
784
785 if (mnt_context_helper_executed(cxt)
786 && mnt_context_get_helper_status(cxt) == 0
787 && !list_empty(&cxt->addmounts)
788 && do_mount_additional(cxt, target, flags, NULL))
789
790 return -MNT_ERR_APPLYFLAGS;
791 return rc;
792 }
793
794 if (!target)
795 return -EINVAL;
796 if (!src) {
797 /* unnecessary, should be already resolved in
798 * mnt_context_prepare_srcpath(), but to be sure... */
799 DBG(CXT, ul_debugobj(cxt, "WARNING: source is NULL -- using \"none\"!"));
800 src = "none";
801 }
802 type = try_type ? : mnt_fs_get_fstype(cxt->fs);
803
804 if (try_type)
805 flags |= MS_SILENT;
806
807 DBG(CXT, ul_debugobj(cxt, "%smount(2) "
808 "[source=%s, target=%s, type=%s, "
809 " mountflags=0x%08lx, mountdata=%s]",
810 mnt_context_is_fake(cxt) ? "(FAKE) " : "",
811 src, target, type,
812 flags, cxt->mountdata ? "yes" : "<none>"));
813
814 if (mnt_context_is_fake(cxt)) {
815 /*
816 * fake
817 */
818 cxt->syscall_status = 0;
819
820 } else if (mnt_context_propagation_only(cxt)) {
821 /*
822 * propagation flags *only*
823 */
824 if (do_mount_additional(cxt, target, flags, &cxt->syscall_status))
825 return -MNT_ERR_APPLYFLAGS;
826 } else {
827 /*
828 * regular mount
829 */
830 if (mount(src, target, type, flags, cxt->mountdata)) {
831 cxt->syscall_status = -errno;
832 DBG(CXT, ul_debugobj(cxt, "mount(2) failed [errno=%d %m]",
833 -cxt->syscall_status));
834 return -cxt->syscall_status;
835 }
836 DBG(CXT, ul_debugobj(cxt, " success"));
837 cxt->syscall_status = 0;
838
839 /*
840 * additional mounts for extra propagation flags
841 */
842 if (!list_empty(&cxt->addmounts)
843 && do_mount_additional(cxt, target, flags, NULL)) {
844
845 /* TODO: call umount? */
846 return -MNT_ERR_APPLYFLAGS;
847 }
848 }
849
850 if (try_type && cxt->update) {
851 struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
852 if (fs)
853 rc = mnt_fs_set_fstype(fs, try_type);
854 }
855
856 return rc;
857 }
858
859 /* try mount(2) for all items in comma separated list of the filesystem @types */
860 static int do_mount_by_types(struct libmnt_context *cxt, const char *types)
861 {
862 int rc = -EINVAL;
863 char *p, *p0;
864
865 assert(cxt);
866 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
867
868 DBG(CXT, ul_debugobj(cxt, "trying to mount by FS list '%s'", types));
869
870 p0 = p = strdup(types);
871 if (!p)
872 return -ENOMEM;
873 do {
874 char *autotype = NULL;
875 char *end = strchr(p, ',');
876
877 if (end)
878 *end = '\0';
879
880 DBG(CXT, ul_debugobj(cxt, "-->trying '%s'", p));
881
882 /* Let's support things like "udf,iso9660,auto" */
883 if (strcmp(p, "auto") == 0) {
884 rc = mnt_context_guess_srcpath_fstype(cxt, &autotype);
885 if (rc) {
886 DBG(CXT, ul_debugobj(cxt, "failed to guess FS type [rc=%d]", rc));
887 free(p0);
888 free(autotype);
889 return rc;
890 }
891 p = autotype;
892 DBG(CXT, ul_debugobj(cxt, " --> '%s'", p));
893 }
894
895 if (p)
896 rc = do_mount(cxt, p);
897 p = end ? end + 1 : NULL;
898 free(autotype);
899 } while (!mnt_context_get_status(cxt) && p);
900
901 free(p0);
902 return rc;
903 }
904
905
906 static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
907 {
908 int neg = pattern && strncmp(pattern, "no", 2) == 0;
909 int rc = -EINVAL;
910 char **filesystems, **fp;
911 struct libmnt_ns *ns_old;
912
913 assert(cxt);
914 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
915
916 /*
917 * Use the pattern as list of the filesystems
918 */
919 if (!neg && pattern) {
920 DBG(CXT, ul_debugobj(cxt, "use FS pattern as FS list"));
921 return do_mount_by_types(cxt, pattern);
922 }
923
924 DBG(CXT, ul_debugobj(cxt, "trying to mount by FS pattern '%s'", pattern));
925
926 /*
927 * Apply pattern to /etc/filesystems and /proc/filesystems
928 */
929 ns_old = mnt_context_switch_origin_ns(cxt);
930 if (!ns_old)
931 return -MNT_ERR_NAMESPACE;
932 rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
933 if (!mnt_context_switch_ns(cxt, ns_old))
934 return -MNT_ERR_NAMESPACE;
935 if (rc)
936 return rc;
937
938 if (filesystems == NULL)
939 return -MNT_ERR_NOFSTYPE;
940
941 for (fp = filesystems; *fp; fp++) {
942 rc = do_mount(cxt, *fp);
943 if (mnt_context_get_status(cxt))
944 break;
945 if (mnt_context_get_syscall_errno(cxt) != EINVAL &&
946 mnt_context_get_syscall_errno(cxt) != ENODEV)
947 break;
948 }
949 mnt_free_filesystems(filesystems);
950 return rc;
951 }
952
953 /**
954 * mnt_context_prepare_mount:
955 * @cxt: context
956 *
957 * Prepare context for mounting, unnecessary for mnt_context_mount().
958 *
959 * Returns: negative number on error, zero on success
960 */
961 int mnt_context_prepare_mount(struct libmnt_context *cxt)
962 {
963 int rc = -EINVAL;
964 struct libmnt_ns *ns_old;
965
966 if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
967 return -EINVAL;
968 if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
969 return -EINVAL;
970 if (cxt->flags & MNT_FL_PREPARED)
971 return 0;
972
973 assert(cxt->helper_exec_status == 1);
974 assert(cxt->syscall_status == 1);
975
976 cxt->action = MNT_ACT_MOUNT;
977
978 ns_old = mnt_context_switch_target_ns(cxt);
979 if (!ns_old)
980 return -MNT_ERR_NAMESPACE;
981
982 DBG(CXT, ul_debugobj(cxt, "mount: preparing"));
983
984 rc = mnt_context_apply_fstab(cxt);
985 if (!rc)
986 rc = mnt_context_merge_mflags(cxt);
987 if (!rc)
988 rc = evaluate_permissions(cxt);
989 if (!rc)
990 rc = fix_optstr(cxt);
991 if (!rc)
992 rc = mnt_context_prepare_srcpath(cxt);
993 if (!rc)
994 rc = mnt_context_prepare_target(cxt);
995 if (!rc)
996 rc = mnt_context_guess_fstype(cxt);
997 if (!rc)
998 rc = mnt_context_prepare_helper(cxt, "mount", NULL);
999 if (rc) {
1000 DBG(CXT, ul_debugobj(cxt, "mount: preparing failed"));
1001 goto end;
1002 }
1003 cxt->flags |= MNT_FL_PREPARED;
1004
1005 end:
1006 if (!mnt_context_switch_ns(cxt, ns_old))
1007 return -MNT_ERR_NAMESPACE;
1008
1009 return rc;
1010 }
1011
1012 /**
1013 * mnt_context_do_mount
1014 * @cxt: context
1015 *
1016 * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
1017 *
1018 * Note that this function could be called only once. If you want to mount
1019 * another source or target, then you have to call mnt_reset_context().
1020 *
1021 * If you want to call mount(2) for the same source and target with different
1022 * mount flags or fstype, then call mnt_context_reset_status() and then try
1023 * again mnt_context_do_mount().
1024 *
1025 * WARNING: non-zero return code does not mean that mount(2) syscall or
1026 * mount.type helper wasn't successfully called.
1027 *
1028 * Check mnt_context_get_status() after error! See mnt_context_mount() for more
1029 * details about errors and warnings.
1030 *
1031 * Returns: 0 on success;
1032 * >0 in case of mount(2) error (returns syscall errno),
1033 * <0 in case of other errors.
1034 */
1035 int mnt_context_do_mount(struct libmnt_context *cxt)
1036 {
1037 const char *type;
1038 int res;
1039 struct libmnt_ns *ns_old;
1040
1041 assert(cxt);
1042 assert(cxt->fs);
1043 assert(cxt->helper_exec_status == 1);
1044 assert(cxt->syscall_status == 1);
1045 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
1046 assert((cxt->flags & MNT_FL_PREPARED));
1047 assert((cxt->action == MNT_ACT_MOUNT));
1048
1049 DBG(CXT, ul_debugobj(cxt, "mount: do mount"));
1050
1051 if (!(cxt->flags & MNT_FL_MOUNTDATA))
1052 cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);
1053
1054 ns_old = mnt_context_switch_target_ns(cxt);
1055 if (!ns_old)
1056 return -MNT_ERR_NAMESPACE;
1057
1058 type = mnt_fs_get_fstype(cxt->fs);
1059 if (type) {
1060 if (strchr(type, ','))
1061 /* this only happens if fstab contains a list of filesystems */
1062 res = do_mount_by_types(cxt, type);
1063 else
1064 res = do_mount(cxt, NULL);
1065 } else
1066 res = do_mount_by_pattern(cxt, cxt->fstype_pattern);
1067
1068 #ifdef USE_LIBMOUNT_SUPPORT_MTAB
1069 if (mnt_context_get_status(cxt)
1070 && !mnt_context_is_fake(cxt)
1071 && !cxt->helper
1072 && mnt_context_mtab_writable(cxt)) {
1073
1074 int is_rdonly = -1;
1075
1076 DBG(CXT, ul_debugobj(cxt, "checking for RDONLY mismatch"));
1077
1078 /*
1079 * Mounted by mount(2), do some post-mount checks
1080 *
1081 * Kernel allows to use MS_RDONLY for bind mounts, but the
1082 * read-only request could be silently ignored. Check it to
1083 * avoid 'ro' in mtab and 'rw' in /proc/mounts.
1084 */
1085 if ((cxt->mountflags & MS_BIND)
1086 && (cxt->mountflags & MS_RDONLY)) {
1087
1088 if (is_rdonly < 0)
1089 is_rdonly = mnt_is_readonly(mnt_context_get_target(cxt));
1090 if (!is_rdonly)
1091 mnt_context_set_mflags(cxt, cxt->mountflags & ~MS_RDONLY);
1092 }
1093
1094
1095 /* Kernel can silently add MS_RDONLY flag when mounting file
1096 * system that does not have write support. Check this to avoid
1097 * 'ro' in /proc/mounts and 'rw' in mtab.
1098 */
1099 if (!(cxt->mountflags & (MS_RDONLY | MS_MOVE))
1100 && !mnt_context_propagation_only(cxt)) {
1101
1102 if (is_rdonly < 0)
1103 is_rdonly = mnt_is_readonly(mnt_context_get_target(cxt));
1104 if (is_rdonly)
1105 mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY);
1106 }
1107 }
1108 #endif
1109 if (!mnt_context_switch_ns(cxt, ns_old))
1110 return -MNT_ERR_NAMESPACE;
1111
1112 return res;
1113 }
1114
1115 /**
1116 * mnt_context_finalize_mount:
1117 * @cxt: context
1118 *
1119 * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
1120 * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
1121 *
1122 * Returns: negative number on error, 0 on success.
1123 */
1124 int mnt_context_finalize_mount(struct libmnt_context *cxt)
1125 {
1126 int rc;
1127
1128 assert(cxt);
1129 assert(cxt->fs);
1130 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
1131 assert((cxt->flags & MNT_FL_PREPARED));
1132
1133 rc = mnt_context_prepare_update(cxt);
1134 if (!rc)
1135 rc = mnt_context_update_tabs(cxt);
1136 return rc;
1137 }
1138
1139 /**
1140 * mnt_context_mount:
1141 * @cxt: mount context
1142 *
1143 * High-level, mounts the filesystem by mount(2) or fork()+exec(/sbin/mount.type).
1144 *
1145 * This is similar to:
1146 *
1147 * mnt_context_prepare_mount(cxt);
1148 * mnt_context_do_mount(cxt);
1149 * mnt_context_finalize_mount(cxt);
1150 *
1151 * See also mnt_context_disable_helpers().
1152 *
1153 * Note that this function should be called only once. If you want to mount with
1154 * different settings, then you have to call mnt_reset_context(). It's NOT enough
1155 * to call mnt_context_reset_status(). If you want to call this function more than
1156 * once, the whole context has to be reset.
1157 *
1158 * WARNING: non-zero return code does not mean that mount(2) syscall or
1159 * mount.type helper wasn't successfully called.
1160 *
1161 * Always use mnt_context_get_status():
1162 *
1163 * <informalexample>
1164 * <programlisting>
1165 * rc = mnt_context_mount(cxt);
1166 *
1167 * if (mnt_context_helper_executed(cxt))
1168 * return mnt_context_get_helper_status(cxt);
1169 * if (rc == 0 && mnt_context_get_status(cxt) == 1)
1170 * return MNT_EX_SUCCESS;
1171 * return MNT_EX_FAIL;
1172 * </programlisting>
1173 * </informalexample>
1174 *
1175 * or mnt_context_get_excode() to generate mount(8) compatible error
1176 * or warning message:
1177 *
1178 * <informalexample>
1179 * <programlisting>
1180 * rc = mnt_context_mount(cxt);
1181 * rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf));
1182 * if (buf)
1183 * warnx(_("%s: %s"), mnt_context_get_target(cxt), buf);
1184 * return rc; // MNT_EX_*
1185 * </programlisting>
1186 * </informalexample>
1187 *
1188 * Returns: 0 on success;
1189 * >0 in case of mount(2) error (returns syscall errno),
1190 * <0 in case of other errors.
1191 */
1192 int mnt_context_mount(struct libmnt_context *cxt)
1193 {
1194 int rc;
1195 struct libmnt_ns *ns_old;
1196
1197 assert(cxt);
1198 assert(cxt->fs);
1199 assert(cxt->helper_exec_status == 1);
1200 assert(cxt->syscall_status == 1);
1201
1202 ns_old = mnt_context_switch_target_ns(cxt);
1203 if (!ns_old)
1204 return -MNT_ERR_NAMESPACE;
1205
1206 again:
1207 rc = mnt_context_prepare_mount(cxt);
1208 if (!rc)
1209 rc = mnt_context_prepare_update(cxt);
1210 if (!rc)
1211 rc = mnt_context_do_mount(cxt);
1212 if (!rc)
1213 rc = mnt_context_update_tabs(cxt);
1214
1215 /*
1216 * Read-only device; try mount filesystem read-only
1217 */
1218 if ((rc == -EROFS && !mnt_context_syscall_called(cxt)) /* before syscall; rdonly loopdev */
1219 || mnt_context_get_syscall_errno(cxt) == EROFS /* syscall failed with EROFS */
1220 || mnt_context_get_syscall_errno(cxt) == EACCES) /* syscall failed with EACCES */
1221 {
1222 unsigned long mflags = 0;
1223
1224 mnt_context_get_mflags(cxt, &mflags);
1225
1226 if (!(mflags & MS_RDONLY) /* not yet RDONLY */
1227 && !(mflags & MS_REMOUNT) /* not remount */
1228 && !(mflags & MS_BIND) /* not bin mount */
1229 && !mnt_context_is_rwonly_mount(cxt)) { /* no explicit read-write */
1230
1231 assert(!(cxt->flags & MNT_FL_FORCED_RDONLY));
1232 DBG(CXT, ul_debugobj(cxt, "write-protected source, trying RDONLY."));
1233
1234 mnt_context_reset_status(cxt);
1235 mnt_context_set_mflags(cxt, mflags | MS_RDONLY);
1236 cxt->flags |= MNT_FL_FORCED_RDONLY;
1237 goto again;
1238 }
1239 }
1240 if (!mnt_context_switch_ns(cxt, ns_old))
1241 return -MNT_ERR_NAMESPACE;
1242 return rc;
1243 }
1244
1245 /**
1246 * mnt_context_next_mount:
1247 * @cxt: context
1248 * @itr: iterator
1249 * @fs: returns the current filesystem
1250 * @mntrc: returns the return code from mnt_context_mount()
1251 * @ignored: returns 1 for non-matching and 2 for already mounted filesystems
1252 *
1253 * This function tries to mount the next filesystem from fstab (as returned by
1254 * mnt_context_get_fstab()). See also mnt_context_set_fstab().
1255 *
1256 * You can filter out filesystems by:
1257 * mnt_context_set_options_pattern() to simulate mount -a -O pattern
1258 * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern
1259 *
1260 * If the filesystem is already mounted or does not match defined criteria,
1261 * then the mnt_context_next_mount() function returns zero, but the @ignored is
1262 * non-zero. Note that the root filesystem and filesystems with "noauto" option
1263 * are always ignored.
1264 *
1265 * If mount(2) syscall or mount.type helper failed, then the
1266 * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
1267 * Use also mnt_context_get_status() to check if the filesystem was
1268 * successfully mounted.
1269 *
1270 * See mnt_context_mount() for more details about errors and warnings.
1271 *
1272 * Returns: 0 on success,
1273 * <0 in case of error (!= mount(2) errors)
1274 * 1 at the end of the list.
1275 */
1276 int mnt_context_next_mount(struct libmnt_context *cxt,
1277 struct libmnt_iter *itr,
1278 struct libmnt_fs **fs,
1279 int *mntrc,
1280 int *ignored)
1281 {
1282 struct libmnt_table *fstab, *mtab;
1283 const char *o, *tgt;
1284 char *pattern;
1285 int rc, mounted = 0;
1286
1287 if (ignored)
1288 *ignored = 0;
1289 if (mntrc)
1290 *mntrc = 0;
1291
1292 if (!cxt || !fs || !itr)
1293 return -EINVAL;
1294
1295 mtab = cxt->mtab;
1296 cxt->mtab = NULL; /* do not reset mtab */
1297 mnt_reset_context(cxt);
1298 cxt->mtab = mtab;
1299
1300 rc = mnt_context_get_fstab(cxt, &fstab);
1301 if (rc)
1302 return rc;
1303
1304 rc = mnt_table_next_fs(fstab, itr, fs);
1305 if (rc != 0)
1306 return rc; /* more filesystems (or error) */
1307
1308 o = mnt_fs_get_user_options(*fs);
1309 tgt = mnt_fs_get_target(*fs);
1310
1311 DBG(CXT, ul_debugobj(cxt, "next-mount: trying %s", tgt));
1312
1313 /* ignore swap */
1314 if (mnt_fs_is_swaparea(*fs) ||
1315
1316 /* ignore root filesystem */
1317 (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
1318
1319 /* ignore noauto filesystems */
1320 (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||
1321
1322 /* ignore filesystems which don't match options patterns */
1323 (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
1324 cxt->fstype_pattern)) ||
1325
1326 /* ignore filesystems which don't match type patterns */
1327 (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
1328 cxt->optstr_pattern))) {
1329 if (ignored)
1330 *ignored = 1;
1331 DBG(CXT, ul_debugobj(cxt, "next-mount: not-match "
1332 "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
1333 mnt_fs_get_fstype(*fs),
1334 cxt->fstype_pattern,
1335 mnt_fs_get_options(*fs),
1336 cxt->optstr_pattern));
1337 return 0;
1338 }
1339
1340 /* ignore already mounted filesystems */
1341 rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
1342 if (rc)
1343 return rc;
1344 if (mounted) {
1345 if (ignored)
1346 *ignored = 2;
1347 return 0;
1348 }
1349
1350 if (mnt_context_is_fork(cxt)) {
1351 rc = mnt_fork_context(cxt);
1352 if (rc)
1353 return rc; /* fork error */
1354
1355 if (mnt_context_is_parent(cxt)) {
1356 return 0; /* parent */
1357 }
1358 }
1359
1360 /* child or non-forked */
1361
1362 rc = mnt_context_set_fs(cxt, *fs);
1363 if (!rc) {
1364 /*
1365 * "-t <pattern>" is used to filter out fstab entries, but for ordinary
1366 * mount operation -t means "-t <type>". We have to zeroize the pattern
1367 * to avoid misinterpretation.
1368 */
1369 pattern = cxt->fstype_pattern;
1370 cxt->fstype_pattern = NULL;
1371
1372 rc = mnt_context_mount(cxt);
1373
1374 cxt->fstype_pattern = pattern;
1375
1376 if (mntrc)
1377 *mntrc = rc;
1378 }
1379
1380 if (mnt_context_is_child(cxt)) {
1381 DBG(CXT, ul_debugobj(cxt, "next-mount: child exit [rc=%d]", rc));
1382 DBG_FLUSH;
1383 _exit(rc);
1384 }
1385 return 0;
1386 }
1387
1388
1389 /**
1390 * mnt_context_next_remount:
1391 * @cxt: context
1392 * @itr: iterator
1393 * @fs: returns the current filesystem
1394 * @mntrc: returns the return code from mnt_context_mount()
1395 * @ignored: returns 1 for non-matching
1396 *
1397 * This function tries to remount the next mounted filesystem (as returned by
1398 * mnt_context_get_mtab()).
1399 *
1400 * You can filter out filesystems by:
1401 * mnt_context_set_options_pattern() to simulate mount -a -O pattern
1402 * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern
1403 *
1404 * If the filesystem does not match defined criteria, then the
1405 * mnt_context_next_remount() function returns zero, but the @ignored is
1406 * non-zero.
1407 *
1408 * If mount(2) syscall or mount.type helper failed, then the
1409 * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
1410 * Use also mnt_context_get_status() to check if the filesystem was
1411 * successfully mounted.
1412 *
1413 * See mnt_context_mount() for more details about errors and warnings.
1414 *
1415 * Returns: 0 on success,
1416 * <0 in case of error (!= mount(2) errors)
1417 * 1 at the end of the list.
1418 *
1419 * Since: 2.34
1420 */
1421 int mnt_context_next_remount(struct libmnt_context *cxt,
1422 struct libmnt_iter *itr,
1423 struct libmnt_fs **fs,
1424 int *mntrc,
1425 int *ignored)
1426 {
1427 struct libmnt_context *remount_cxt = NULL;
1428 struct libmnt_table *mtab;
1429 const char *tgt;
1430 int rc;
1431
1432 if (ignored)
1433 *ignored = 0;
1434 if (mntrc)
1435 *mntrc = 0;
1436
1437 if (!cxt || !fs || !itr)
1438 return -EINVAL;
1439
1440 rc = mnt_context_get_mtab(cxt, &mtab);
1441 if (rc)
1442 return rc;
1443
1444 rc = mnt_table_next_fs(mtab, itr, fs);
1445 if (rc != 0)
1446 return rc; /* more filesystems (or error) */
1447
1448 tgt = mnt_fs_get_target(*fs);
1449
1450 DBG(CXT, ul_debugobj(cxt, "next-remount: trying %s", tgt));
1451
1452 /* ignore filesystems which don't match options patterns */
1453 if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
1454 cxt->fstype_pattern)) ||
1455
1456 /* ignore filesystems which don't match type patterns */
1457 (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
1458 cxt->optstr_pattern))) {
1459 if (ignored)
1460 *ignored = 1;
1461 DBG(CXT, ul_debugobj(cxt, "next-remount: not-match "
1462 "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
1463 mnt_fs_get_fstype(*fs),
1464 cxt->fstype_pattern,
1465 mnt_fs_get_options(*fs),
1466 cxt->optstr_pattern));
1467 return 0;
1468 }
1469
1470 /* make sure fstab is already read to avoid fstab parsing in cloned context */
1471 mnt_context_get_fstab(cxt, NULL);
1472
1473 /* clone context */
1474 remount_cxt = mnt_copy_context(cxt);
1475 if (!remount_cxt)
1476 return -ENOMEM;
1477
1478 rc = mnt_context_set_target(remount_cxt, tgt);
1479 if (!rc) {
1480 /*
1481 * "-t <pattern>" is used to filter out fstab entries, but for ordinary
1482 * mount operation -t means "-t <type>". We have to zeroize the pattern
1483 * to avoid misinterpretation.
1484 */
1485 remount_cxt->fstype_pattern = NULL;
1486 rc = mnt_context_mount(remount_cxt);
1487
1488 if (mntrc)
1489 *mntrc = rc;
1490 }
1491
1492 mnt_free_context(remount_cxt);
1493 return 0;
1494 }
1495
1496 /*
1497 * Returns 1 if @dir parent is shared
1498 */
1499 static int is_shared_tree(struct libmnt_context *cxt, const char *dir)
1500 {
1501 struct libmnt_table *tb = NULL;
1502 struct libmnt_fs *fs;
1503 unsigned long mflags = 0;
1504 char *mnt = NULL, *p;
1505 int rc = 0;
1506 struct libmnt_ns *ns_old;
1507
1508 ns_old = mnt_context_switch_target_ns(cxt);
1509 if (!ns_old)
1510 return -MNT_ERR_NAMESPACE;
1511
1512 if (!dir)
1513 return 0;
1514 if (mnt_context_get_mtab(cxt, &tb) || !tb)
1515 goto done;
1516
1517 mnt = strdup(dir);
1518 if (!mnt)
1519 goto done;
1520 p = strrchr(mnt, '/');
1521 if (!p)
1522 goto done;
1523 if (p > mnt)
1524 *p = '\0';
1525 fs = mnt_table_find_mountpoint(tb, mnt, MNT_ITER_BACKWARD);
1526
1527 rc = fs && mnt_fs_is_kernel(fs)
1528 && mnt_fs_get_propagation(fs, &mflags) == 0
1529 && (mflags & MS_SHARED);
1530 done:
1531 free(mnt);
1532 if (!mnt_context_switch_ns(cxt, ns_old))
1533 return -MNT_ERR_NAMESPACE;
1534 return rc;
1535 }
1536
1537 int mnt_context_get_mount_excode(
1538 struct libmnt_context *cxt,
1539 int rc,
1540 char *buf,
1541 size_t bufsz)
1542 {
1543 int syserr;
1544 struct stat st;
1545 unsigned long uflags = 0, mflags = 0;
1546
1547 int restricted = mnt_context_is_restricted(cxt);
1548 const char *tgt = mnt_context_get_target(cxt);
1549 const char *src = mnt_context_get_source(cxt);
1550
1551 if (mnt_context_helper_executed(cxt)) {
1552 /*
1553 * /sbin/mount.<type> called, return status
1554 */
1555 if (rc == -MNT_ERR_APPLYFLAGS && buf)
1556 snprintf(buf, bufsz, _("WARNING: failed to apply propagation flags"));
1557
1558 return mnt_context_get_helper_status(cxt);
1559 }
1560
1561 if (rc == 0 && mnt_context_get_status(cxt) == 1) {
1562 /*
1563 * Libmount success && syscall success.
1564 */
1565 if (buf && mnt_context_forced_rdonly(cxt))
1566 snprintf(buf, bufsz, _("WARNING: device write-protected, mounted read-only"));
1567 return MNT_EX_SUCCESS;
1568 }
1569
1570 mnt_context_get_mflags(cxt, &mflags); /* mount(2) flags */
1571 mnt_context_get_user_mflags(cxt, &uflags); /* userspace flags */
1572
1573 if (!mnt_context_syscall_called(cxt)) {
1574 /*
1575 * libmount errors (extra library checks)
1576 */
1577 switch (rc) {
1578 case -EPERM:
1579 if (buf)
1580 snprintf(buf, bufsz, _("operation permitted for root only"));
1581 return MNT_EX_USAGE;
1582 case -EBUSY:
1583 if (buf)
1584 snprintf(buf, bufsz, _("%s is already mounted"), src);
1585 return MNT_EX_USAGE;
1586 case -MNT_ERR_NOFSTAB:
1587 if (!buf)
1588 return MNT_EX_USAGE;
1589 if (mnt_context_is_swapmatch(cxt))
1590 snprintf(buf, bufsz, _("can't find in %s"),
1591 mnt_get_fstab_path());
1592 else if (tgt)
1593 snprintf(buf, bufsz, _("can't find mount point in %s"),
1594 mnt_get_fstab_path());
1595 else if (src)
1596 snprintf(buf, bufsz, _("can't find mount source %s in %s"),
1597 src, mnt_get_fstab_path());
1598 return MNT_EX_USAGE;
1599 case -MNT_ERR_AMBIFS:
1600 if (buf)
1601 snprintf(buf, bufsz, _("more filesystems detected on %s; use -t <type> or wipefs(8)"), src);
1602 return MNT_EX_USAGE;
1603 case -MNT_ERR_NOFSTYPE:
1604 if (buf)
1605 snprintf(buf, bufsz, restricted ?
1606 _("failed to determine filesystem type") :
1607 _("no filesystem type specified"));
1608 return MNT_EX_USAGE;
1609 case -MNT_ERR_NOSOURCE:
1610 if (uflags & MNT_MS_NOFAIL)
1611 return MNT_EX_SUCCESS;
1612 if (buf) {
1613 if (src)
1614 snprintf(buf, bufsz, _("can't find %s"), src);
1615 else
1616 snprintf(buf, bufsz, _("no mount source specified"));
1617 }
1618 return MNT_EX_USAGE;
1619 case -MNT_ERR_MOUNTOPT:
1620 if (buf)
1621 snprintf(buf, bufsz, errno ?
1622 _("failed to parse mount options: %m") :
1623 _("failed to parse mount options"));
1624 return MNT_EX_USAGE;
1625 case -MNT_ERR_LOOPDEV:
1626 if (buf)
1627 snprintf(buf, bufsz, _("failed to setup loop device for %s"), src);
1628 return MNT_EX_FAIL;
1629 case -MNT_ERR_LOOPOVERLAP:
1630 if (buf)
1631 snprintf(buf, bufsz, _("overlapping loop device exists for %s"), src);
1632 return MNT_EX_FAIL;
1633 case -MNT_ERR_LOCK:
1634 if (buf)
1635 snprintf(buf, bufsz, _("locking failed"));
1636 return MNT_EX_FILEIO;
1637 case -MNT_ERR_NAMESPACE:
1638 if (buf)
1639 snprintf(buf, bufsz, _("failed to switch namespace"));
1640 return MNT_EX_SYSERR;
1641 default:
1642 return mnt_context_get_generic_excode(rc, buf, bufsz, _("mount failed: %m"));
1643 }
1644
1645 } else if (mnt_context_get_syscall_errno(cxt) == 0) {
1646 /*
1647 * mount(2) syscall success, but something else failed
1648 * (probably error in mtab processing).
1649 */
1650 if (rc == -MNT_ERR_LOCK) {
1651 if (buf)
1652 snprintf(buf, bufsz, _("filesystem was mounted, but failed to update userspace mount table"));
1653 return MNT_EX_FILEIO;
1654 } else if (rc == -MNT_ERR_NAMESPACE) {
1655 if (buf)
1656 snprintf(buf, bufsz, _("filesystem was mounted, but failed to switch namespace back"));
1657 return MNT_EX_SYSERR;
1658
1659 } else if (rc < 0)
1660 return mnt_context_get_generic_excode(rc, buf, bufsz,
1661 _("filesystem was mounted, but any subsequent operation failed: %m"));
1662
1663 return MNT_EX_SOFTWARE; /* internal error */
1664
1665 }
1666
1667 /*
1668 * mount(2) errors
1669 */
1670 syserr = mnt_context_get_syscall_errno(cxt);
1671
1672
1673 switch(syserr) {
1674 case EPERM:
1675 if (!buf)
1676 break;
1677 if (geteuid() == 0) {
1678 if (stat(tgt, &st) || !S_ISDIR(st.st_mode))
1679 snprintf(buf, bufsz, _("mount point is not a directory"));
1680 else
1681 snprintf(buf, bufsz, _("permission denied"));
1682 } else
1683 snprintf(buf, bufsz, _("must be superuser to use mount"));
1684 break;
1685
1686 case EBUSY:
1687 {
1688 struct libmnt_table *tb;
1689
1690 if (!buf)
1691 break;
1692 if (mflags & MS_REMOUNT) {
1693 snprintf(buf, bufsz, _("mount point is busy"));
1694 break;
1695 }
1696 if (src && mnt_context_get_mtab(cxt, &tb) == 0) {
1697 struct libmnt_iter itr;
1698 struct libmnt_fs *fs;
1699
1700 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
1701 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1702 const char *s = mnt_fs_get_srcpath(fs),
1703 *t = mnt_fs_get_target(fs);
1704
1705 if (t && s && mnt_fs_streq_srcpath(fs, src)) {
1706 snprintf(buf, bufsz, _("%s already mounted on %s"), s, t);
1707 break;
1708 }
1709 }
1710 }
1711 if (!*buf)
1712 snprintf(buf, bufsz, _("%s already mounted or mount point busy"), src);
1713 break;
1714 }
1715 case ENOENT:
1716 if (tgt && lstat(tgt, &st)) {
1717 if (buf)
1718 snprintf(buf, bufsz, _("mount point does not exist"));
1719 } else if (tgt && stat(tgt, &st)) {
1720 if (buf)
1721 snprintf(buf, bufsz, _("mount point is a symbolic link to nowhere"));
1722 } else if (src && stat(src, &st)) {
1723 if (uflags & MNT_MS_NOFAIL)
1724 return MNT_EX_SUCCESS;
1725 if (buf)
1726 snprintf(buf, bufsz, _("special device %s does not exist"), src);
1727 } else if (buf) {
1728 errno = syserr;
1729 snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
1730 }
1731 break;
1732
1733 case ENOTDIR:
1734 if (stat(tgt, &st) || ! S_ISDIR(st.st_mode)) {
1735 if (buf)
1736 snprintf(buf, bufsz, _("mount point is not a directory"));
1737 } else if (src && stat(src, &st) && errno == ENOTDIR) {
1738 if (uflags & MNT_MS_NOFAIL)
1739 return MNT_EX_SUCCESS;
1740 if (buf)
1741 snprintf(buf, bufsz, _("special device %s does not exist "
1742 "(a path prefix is not a directory)"), src);
1743 } else if (buf) {
1744 errno = syserr;
1745 snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
1746 }
1747 break;
1748
1749 case EINVAL:
1750 if (!buf)
1751 break;
1752 if (mflags & MS_REMOUNT)
1753 snprintf(buf, bufsz, _("mount point not mounted or bad option"));
1754 else if (rc == -MNT_ERR_APPLYFLAGS)
1755 snprintf(buf, bufsz, _("not mount point or bad option"));
1756 else if ((mflags & MS_MOVE) && is_shared_tree(cxt, src))
1757 snprintf(buf, bufsz,
1758 _("bad option; moving a mount "
1759 "residing under a shared mount is unsupported"));
1760 else if (mnt_fs_is_netfs(mnt_context_get_fs(cxt)))
1761 snprintf(buf, bufsz,
1762 _("bad option; for several filesystems (e.g. nfs, cifs) "
1763 "you might need a /sbin/mount.<type> helper program"));
1764 else
1765 snprintf(buf, bufsz,
1766 _("wrong fs type, bad option, bad superblock on %s, "
1767 "missing codepage or helper program, or other error"),
1768 src);
1769 break;
1770
1771 case EMFILE:
1772 if (buf)
1773 snprintf(buf, bufsz, _("mount table full"));
1774 break;
1775
1776 case EIO:
1777 if (buf)
1778 snprintf(buf, bufsz, _("can't read superblock on %s"), src);
1779 break;
1780
1781 case ENODEV:
1782 if (!buf)
1783 break;
1784 if (mnt_context_get_fstype(cxt))
1785 snprintf(buf, bufsz, _("unknown filesystem type '%s'"),
1786 mnt_context_get_fstype(cxt));
1787 else
1788 snprintf(buf, bufsz, _("unknown filesystem type"));
1789 break;
1790
1791 case ENOTBLK:
1792 if (uflags & MNT_MS_NOFAIL)
1793 return MNT_EX_SUCCESS;
1794 if (!buf)
1795 break;
1796 if (src && stat(src, &st))
1797 snprintf(buf, bufsz, _("%s is not a block device, and stat(2) fails?"), src);
1798 else if (src && S_ISBLK(st.st_mode))
1799 snprintf(buf, bufsz,
1800 _("the kernel does not recognize %s as a block device; "
1801 "maybe \"modprobe driver\" is necessary"), src);
1802 else if (src && S_ISREG(st.st_mode))
1803 snprintf(buf, bufsz, _("%s is not a block device; try \"-o loop\""), src);
1804 else
1805 snprintf(buf, bufsz, _("%s is not a block device"), src);
1806 break;
1807
1808 case ENXIO:
1809 if (uflags & MNT_MS_NOFAIL)
1810 return MNT_EX_SUCCESS;
1811 if (buf)
1812 snprintf(buf, bufsz, _("%s is not a valid block device"), src);
1813 break;
1814
1815 case EACCES:
1816 case EROFS:
1817 if (!buf)
1818 break;
1819 if (mflags & MS_RDONLY)
1820 snprintf(buf, bufsz, _("cannot mount %s read-only"), src);
1821 else if (mnt_context_is_rwonly_mount(cxt))
1822 snprintf(buf, bufsz, _("%s is write-protected but explicit read-write mode requested"), src);
1823 else if (mflags & MS_REMOUNT)
1824 snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src);
1825 else if (mflags & MS_BIND)
1826 snprintf(buf, bufsz, _("bind %s failed"), src);
1827 else {
1828 errno = syserr;
1829 snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
1830 }
1831 break;
1832
1833 case ENOMEDIUM:
1834 if (uflags & MNT_MS_NOFAIL)
1835 return MNT_EX_SUCCESS;
1836 if (buf)
1837 snprintf(buf, bufsz, _("no medium found on %s"), src);
1838 break;
1839
1840 case EBADMSG:
1841 /* Bad CRC for classic filesystems (e.g. extN or XFS) */
1842 if (buf && src && stat(src, &st) == 0
1843 && (S_ISBLK(st.st_mode) || S_ISREG(st.st_mode))) {
1844 snprintf(buf, bufsz, _("cannot mount; probably corrupted filesystem on %s"), src);
1845 break;
1846 }
1847 /* fallthrough */
1848
1849 default:
1850 if (buf) {
1851 errno = syserr;
1852 snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
1853 }
1854 break;
1855 }
1856
1857 return MNT_EX_FAIL;
1858 }
1859