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