]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/context_mount.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2010-2018 Karel Zak <kzak@redhat.com>
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.
14 * SECTION: context-mount
15 * @title: Mount context
16 * @short_description: high-level API to mount operation.
19 #include <sys/mount.h>
24 #if defined(HAVE_SMACK)
25 static int is_option(const char *name
, const char *const *names
)
29 for (p
= names
; p
&& *p
; p
++) {
30 if (strcmp(name
, *p
) == 0)
35 #endif /* HAVE_SMACK */
38 * this has to be called after mnt_context_evaluate_permissions()
40 static int fix_optstr(struct libmnt_context
*cxt
)
42 struct libmnt_optlist
*ol
;
43 struct libmnt_opt
*opt
;
44 struct libmnt_ns
*ns_old
;
49 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
51 if (cxt
->flags
& MNT_FL_MOUNTOPTS_FIXED
)
54 DBG(CXT
, ul_debugobj(cxt
, "--> preparing options"));
56 ol
= mnt_context_get_optlist(cxt
);
60 ns_old
= mnt_context_switch_origin_ns(cxt
);
62 return -MNT_ERR_NAMESPACE
;
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
);
68 char *name
= mnt_get_username(getuid());
73 rc
= mnt_opt_set_value(opt
, name
);
82 opt
= mnt_optlist_get_named(ol
, "uid", NULL
);
83 if (opt
&& (val
= mnt_opt_get_value(opt
)) && !isdigit_string(val
)) {
86 if (strcmp(val
, "useruid") == 0) /* UID of the current user */
89 rc
= mnt_get_uid(val
, &id
); /* UID for the username */
91 rc
= mnt_opt_set_u64value(opt
, id
);
97 opt
= mnt_optlist_get_named(ol
, "gid", NULL
);
98 if (opt
&& (val
= mnt_opt_get_value(opt
)) && !isdigit_string(val
)) {
101 if (strcmp(val
, "usergid") == 0) /* UID of the current user */
104 rc
= mnt_get_gid(val
, &id
); /* UID for the groupname */
106 rc
= mnt_opt_set_u64value(opt
, id
);
111 if (!mnt_context_switch_ns(cxt
, ns_old
))
112 return -MNT_ERR_NAMESPACE
;
116 if (access("/sys/fs/smackfs", F_OK
) != 0) {
117 struct libmnt_iter itr
;
119 static const char *const smack_options
[] = {
127 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
129 while (mnt_optlist_next_opt(ol
, &itr
, &opt
) == 0) {
130 if (!is_option(mnt_opt_get_name(opt
), smack_options
))
132 rc
= mnt_optlist_remove_opt(ol
, opt
);
138 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_PREP_OPTIONS
);
140 DBG(CXT
, ul_debugobj(cxt
, "<-- preparing options done [rc=%d]", rc
));
141 cxt
->flags
|= MNT_FL_MOUNTOPTS_FIXED
;
144 rc
= -MNT_ERR_MOUNTOPT
;
149 * this has to be called before fix_optstr()
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...
155 static int evaluate_permissions(struct libmnt_context
*cxt
)
157 struct libmnt_optlist
*ol
;
158 struct libmnt_opt
*opt
= NULL
;
160 unsigned long user_flags
= 0; /* userspace mount flags */
164 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
169 DBG(CXT
, ul_debugobj(cxt
, "mount: evaluating permissions"));
171 ol
= mnt_context_get_optlist(cxt
);
175 /* get userspace mount flags (user[=<name>] etc.*/
176 rc
= mnt_optlist_get_flags(ol
, &user_flags
, cxt
->map_userspace
, 0);
180 if (!mnt_context_is_restricted(cxt
)) {
184 * Let's convert user, users, owenr and groups to MS_* flags
185 * to be compatible with non-root execution.
187 * The old deprecated way is to use mnt_optstr_get_flags().
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
);
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
);
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
);
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
);
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
);
213 DBG(CXT
, ul_debugobj(cxt
, "perms: superuser [rc=%d]", rc
));
217 if (user_flags
& (MNT_MS_OWNER
| MNT_MS_GROUP
|
218 MNT_MS_USER
| MNT_MS_USERS
))
219 mnt_optlist_merge_opts(ol
);
225 if (!mnt_context_tab_applied(cxt
))
227 DBG(CXT
, ul_debugobj(cxt
, "perms: fstab not applied, ignore user mount"));
232 * Ignore user=<name> (if <name> is set). Let's keep it hidden
233 * for normal library operations, but visible for /sbin/mount.<type>
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"));
241 cxt
->flags
|= MNT_FL_SAVED_USER
;
243 mnt_opt_set_external(opt
, 1);
244 user_flags
&= ~MNT_MS_USER
;
248 * Insert MS_SECURE between system flags on position where is MNT_MS_USER
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
)))
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
)))
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)
267 if (user_flags
& (MNT_MS_OWNER
| MNT_MS_GROUP
)) {
269 struct libmnt_cache
*cache
= NULL
;
271 const char *srcpath
= mnt_fs_get_srcpath(cxt
->fs
);
273 DBG(CXT
, ul_debugobj(cxt
, "perms: owner/group"));
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
),
283 DBG(CXT
, ul_debugobj(cxt
, "perms: src undefined"));
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
)))) {
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
);
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
);
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
));
316 /* we have modified some flags (noexec, ...), let's cleanup the
317 * options to remove duplicate stuff etc.*/
318 mnt_optlist_merge_opts(ol
);
325 * mnt_context_helper_setopt() backend
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.
331 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
333 int mnt_context_mount_setopt(struct libmnt_context
*cxt
, int c
, char *arg
)
338 assert(cxt
->action
== MNT_ACT_MOUNT
);
342 rc
= mnt_context_enable_fake(cxt
, TRUE
);
345 rc
= mnt_context_disable_mtab(cxt
, TRUE
);
348 rc
= mnt_context_append_options(cxt
, "ro");
351 rc
= mnt_context_enable_verbose(cxt
, TRUE
);
354 rc
= mnt_context_append_options(cxt
, "rw");
358 rc
= mnt_context_append_options(cxt
, arg
);
361 rc
= mnt_context_enable_sloppy(cxt
, TRUE
);
365 rc
= mnt_context_set_fstype(cxt
, arg
);
369 rc
= mnt_context_set_target_ns(cxt
, arg
);
378 static int exec_helper(struct libmnt_context
*cxt
)
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
;
390 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
392 DBG(CXT
, ul_debugobj(cxt
, "mount: executing helper %s", cxt
->helper
));
394 ol
= mnt_context_get_optlist(cxt
);
398 rc
= mnt_optlist_get_optstr(ol
, &o
, NULL
, MNT_OL_FLTR_HELPERS
);
403 && asprintf(&namespace, "/proc/%i/fd/%i",
404 getpid(), ns_tgt
->fd
) == -1) {
414 const char *args
[14], *type
;
417 if (drop_permissions() != 0)
420 if (!mnt_context_switch_origin_ns(cxt
))
423 type
= mnt_fs_get_fstype(cxt
->fs
);
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 */
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 */
438 args
[i
++] = "-o"; /* 8 */
439 args
[i
++] = o
; /* 9 */
443 && !endswith(cxt
->helper
, type
)) {
444 args
[i
++] = "-t"; /* 10 */
445 args
[i
++] = type
; /* 11 */
448 args
[i
++] = "-N"; /* 11 */
449 args
[i
++] = namespace; /* 12 */
451 args
[i
] = NULL
; /* 13 */
452 for (i
= 0; args
[i
]; i
++)
453 DBG(CXT
, ul_debugobj(cxt
, "argv[%d] = \"%s\"",
456 execv(cxt
->helper
, (char * const *) args
);
463 if (waitpid(pid
, &st
, 0) == (pid_t
) -1) {
464 cxt
->helper_status
= -1;
467 cxt
->helper_status
= WIFEXITED(st
) ? WEXITSTATUS(st
) : -1;
468 cxt
->helper_exec_status
= rc
= 0;
470 DBG(CXT
, ul_debugobj(cxt
, "%s executed [status=%d, rc=%d%s]",
472 cxt
->helper_status
, rc
,
473 rc
? " waitpid failed" : ""));
478 cxt
->helper_exec_status
= rc
= -errno
;
479 DBG(CXT
, ul_debugobj(cxt
, "fork() failed"));
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.
491 * Returns: 0 on success,
492 * >0 in case of mount(2) error (returns syscall errno),
493 * <0 in case of other errors.
495 static int do_mount(struct libmnt_context
*cxt
, const char *try_type
)
498 char *org_type
= NULL
;
499 struct libmnt_optlist
*ol
= NULL
;
503 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
506 rc
= mnt_context_prepare_helper(cxt
, "mount", try_type
);
512 return exec_helper(cxt
);
515 ol
= mnt_context_get_optlist(cxt
);
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
));
526 mnt_fs_set_fstype(cxt
->fs
, try_type
);
531 * mount(2) or others syscalls
534 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_MOUNT
);
536 if (org_type
&& rc
!= 0)
537 __mnt_fs_set_fstype_ptr(cxt
->fs
, org_type
);
540 if (rc
== 0 && try_type
&& cxt
->update
) {
541 struct libmnt_fs
*fs
= mnt_update_get_fs(cxt
->update
);
543 rc
= mnt_fs_set_fstype(fs
, try_type
);
548 mnt_optlist_remove_flags(ol
, MS_SILENT
, cxt
->map_linux
);
553 static int is_success_status(struct libmnt_context
*cxt
)
555 if (mnt_context_helper_executed(cxt
))
556 return mnt_context_get_helper_status(cxt
) == 0;
558 if (mnt_context_syscall_called(cxt
))
559 return mnt_context_get_status(cxt
) == 1;
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
)
571 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
573 DBG(CXT
, ul_debugobj(cxt
, "trying to mount by FS list '%s'", types
));
575 p0
= p
= strdup(types
);
579 char *autotype
= NULL
;
580 char *end
= strchr(p
, ',');
585 DBG(CXT
, ul_debugobj(cxt
, "-->trying '%s'", p
));
587 /* Let's support things like "udf,iso9660,auto" */
588 if (strcmp(p
, "auto") == 0) {
589 rc
= mnt_context_guess_srcpath_fstype(cxt
, &autotype
);
591 DBG(CXT
, ul_debugobj(cxt
, "failed to guess FS type [rc=%d]", rc
));
597 DBG(CXT
, ul_debugobj(cxt
, " --> '%s'", p
));
601 rc
= do_mount(cxt
, p
);
602 p
= end
? end
+ 1 : NULL
;
604 } while (!is_success_status(cxt
) && p
);
611 static int do_mount_by_pattern(struct libmnt_context
*cxt
, const char *pattern
)
613 int neg
= pattern
&& strncmp(pattern
, "no", 2) == 0;
615 char **filesystems
, **fp
;
616 struct libmnt_ns
*ns_old
;
619 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
622 * Use the pattern as list of the filesystems
624 if (!neg
&& pattern
) {
625 DBG(CXT
, ul_debugobj(cxt
, "use FS pattern as FS list"));
626 return do_mount_by_types(cxt
, pattern
);
629 DBG(CXT
, ul_debugobj(cxt
, "trying to mount by FS pattern '%s'", pattern
));
632 * Apply pattern to /etc/filesystems and /proc/filesystems
634 ns_old
= mnt_context_switch_origin_ns(cxt
);
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
;
643 if (filesystems
== NULL
)
644 return -MNT_ERR_NOFSTYPE
;
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
))
651 if (mnt_context_get_syscall_errno(cxt
) != EINVAL
&&
652 mnt_context_get_syscall_errno(cxt
) != ENODEV
)
655 mnt_free_filesystems(filesystems
);
659 static int prepare_target(struct libmnt_context
*cxt
)
661 const char *tgt
, *prefix
;
663 struct libmnt_ns
*ns_old
;
667 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
669 DBG(CXT
, ul_debugobj(cxt
, "--> preparing target path"));
671 tgt
= mnt_fs_get_target(cxt
->fs
);
676 prefix
= mnt_context_get_target_prefix(cxt
);
678 const char *p
= *tgt
== '/' ? tgt
+ 1 : tgt
;
681 /* target is "/", use "/prefix" */
682 rc
= mnt_fs_set_target(cxt
->fs
, prefix
);
686 if (asprintf(&path
, "%s/%s", prefix
, p
) <= 0)
689 rc
= mnt_fs_set_target(cxt
->fs
, path
);
695 tgt
= mnt_fs_get_target(cxt
->fs
);
698 ns_old
= mnt_context_switch_target_ns(cxt
);
700 return -MNT_ERR_NAMESPACE
;
702 /* canonicalize the path */
704 struct libmnt_cache
*cache
= mnt_context_get_cache(cxt
);
707 char *path
= mnt_resolve_path(tgt
, cache
);
708 if (path
&& strcmp(path
, tgt
) != 0)
709 rc
= mnt_fs_set_target(cxt
->fs
, path
);
714 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_PREP_TARGET
);
716 if (!mnt_context_switch_ns(cxt
, ns_old
))
717 return -MNT_ERR_NAMESPACE
;
719 DBG(CXT
, ul_debugobj(cxt
, "final target '%s' [rc=%d]",
720 mnt_fs_get_target(cxt
->fs
), rc
));
725 * mnt_context_prepare_mount:
728 * Prepare context for mounting, unnecessary for mnt_context_mount().
730 * Returns: negative number on error, zero on success
732 int mnt_context_prepare_mount(struct libmnt_context
*cxt
)
735 struct libmnt_ns
*ns_old
;
737 if (!cxt
|| !cxt
->fs
|| mnt_fs_is_swaparea(cxt
->fs
))
739 if (!mnt_fs_get_source(cxt
->fs
) && !mnt_fs_get_target(cxt
->fs
))
741 if (cxt
->flags
& MNT_FL_PREPARED
)
744 assert(cxt
->helper_exec_status
== 1);
745 assert(cxt
->syscall_status
== 1);
747 cxt
->action
= MNT_ACT_MOUNT
;
749 ns_old
= mnt_context_switch_target_ns(cxt
);
751 return -MNT_ERR_NAMESPACE
;
753 DBG(CXT
, ul_debugobj(cxt
, "mount: preparing"));
755 rc
= mnt_context_apply_fstab(cxt
);
757 rc
= mnt_context_merge_mflags(cxt
);
758 if (!rc
&& cxt
->fs
&& cxt
->optlist
)
759 rc
= mnt_fs_follow_optlist(cxt
->fs
, cxt
->optlist
);
761 rc
= evaluate_permissions(cxt
);
763 rc
= fix_optstr(cxt
);
765 rc
= mnt_context_prepare_srcpath(cxt
);
767 rc
= mnt_context_guess_fstype(cxt
);
769 rc
= prepare_target(cxt
);
771 rc
= mnt_context_prepare_helper(cxt
, "mount", NULL
);
773 if (!rc
&& mnt_context_is_onlyonce(cxt
)) {
775 rc
= mnt_context_is_fs_mounted(cxt
, cxt
->fs
, &mounted
);
776 if (rc
== 0 && mounted
== 1) {
777 rc
= -MNT_ERR_ONLYONCE
;
783 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_PREP
);
786 DBG(CXT
, ul_debugobj(cxt
, "mount: preparing failed"));
790 cxt
->flags
|= MNT_FL_PREPARED
;
793 if (!mnt_context_switch_ns(cxt
, ns_old
))
794 return -MNT_ERR_NAMESPACE
;
800 * mnt_context_do_mount
803 * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
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().
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().
812 * WARNING: non-zero return code does not mean that mount(2) syscall or
813 * mount.type helper wasn't successfully called.
815 * Check mnt_context_get_status() after error! See mnt_context_mount() for more
816 * details about errors and warnings.
818 * Returns: 0 on success;
819 * >0 in case of mount(2) error (returns syscall errno),
820 * <0 in case of other errors.
822 int mnt_context_do_mount(struct libmnt_context
*cxt
)
826 struct libmnt_ns
*ns_old
;
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
));
835 DBG(CXT
, ul_debugobj(cxt
, "mount: do mount"));
837 ns_old
= mnt_context_switch_target_ns(cxt
);
839 return -MNT_ERR_NAMESPACE
;
841 /* before mount stage */
842 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_MOUNT_PRE
);
847 type
= mnt_fs_get_fstype(cxt
->fs
);
849 if (strchr(type
, ','))
850 /* this only happens if fstab contains a list of filesystems */
851 res
= do_mount_by_types(cxt
, type
);
853 res
= do_mount(cxt
, NULL
);
855 res
= do_mount_by_pattern(cxt
, cxt
->fstype_pattern
);
857 /* after mount stage */
859 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_MOUNT_POST
);
864 if (!mnt_context_switch_ns(cxt
, ns_old
))
865 return -MNT_ERR_NAMESPACE
;
867 DBG(CXT
, ul_debugobj(cxt
, "mnt_context_do_mount() done [rc=%d]", res
));
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.
876 static struct libmnt_fs
*get_already_mounted_source(struct libmnt_context
*cxt
)
879 struct libmnt_table
*tb
;
883 src
= mnt_fs_get_srcpath(cxt
->fs
);
885 if (src
&& mnt_context_get_mountinfo(cxt
, &tb
) == 0) {
886 struct libmnt_iter itr
;
887 struct libmnt_fs
*fs
;
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
);
894 if (t
&& s
&& mnt_fs_streq_srcpath(fs
, src
))
902 * Checks if source filesystem superblock is already ro-mounted. Note that we
903 * care about FS superblock as VFS node is irrelevant here.
905 static int is_source_already_rdonly(struct libmnt_context
*cxt
)
907 struct libmnt_fs
*fs
= get_already_mounted_source(cxt
);
908 const char *opts
= fs
? mnt_fs_get_fs_options(fs
) : NULL
;
910 return opts
&& mnt_optstr_get_option(opts
, "ro", NULL
, NULL
) == 0;
914 * mnt_context_finalize_mount:
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().
920 * Returns: negative number on error, 0 on success.
922 int mnt_context_finalize_mount(struct libmnt_context
*cxt
)
928 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
929 assert((cxt
->flags
& MNT_FL_PREPARED
));
931 rc
= mnt_context_prepare_update(cxt
);
933 rc
= mnt_context_update_tabs(cxt
);
939 * @cxt: mount context
941 * High-level, mounts the filesystem by mount(2) or fork()+exec(/sbin/mount.type).
943 * This is similar to:
945 * mnt_context_prepare_mount(cxt);
946 * mnt_context_do_mount(cxt);
947 * mnt_context_finalize_mount(cxt);
949 * See also mnt_context_disable_helpers().
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.
956 * WARNING: non-zero return code does not mean that mount(2) syscall or
957 * mount.type helper wasn't successfully called.
959 * Always use mnt_context_get_status():
963 * rc = mnt_context_mount(cxt);
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;
973 * or mnt_context_get_excode() to generate mount(8) compatible error
974 * or warning message:
978 * rc = mnt_context_mount(cxt);
979 * rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf));
981 * warnx(_("%s: %s"), mnt_context_get_target(cxt), buf);
982 * return rc; // MNT_EX_*
986 * Returns: 0 on success;
987 * >0 in case of mount(2) error (returns syscall errno),
988 * <0 in case of other errors.
990 int mnt_context_mount(struct libmnt_context
*cxt
)
993 struct libmnt_ns
*ns_old
;
997 assert(cxt
->helper_exec_status
== 1);
999 ns_old
= mnt_context_switch_target_ns(cxt
);
1001 return -MNT_ERR_NAMESPACE
;
1004 rc
= mnt_context_prepare_mount(cxt
);
1006 rc
= mnt_context_prepare_update(cxt
);
1008 rc
= mnt_context_do_mount(cxt
);
1010 rc
= mnt_context_update_tabs(cxt
);
1013 * Read-only device or already read-only mounted FS.
1014 * Try mount the filesystem read-only.
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
)))
1022 unsigned long mflags
= 0;
1024 mnt_context_get_mflags(cxt
, &mflags
);
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 */
1031 assert(!(cxt
->flags
& MNT_FL_FORCED_RDONLY
));
1032 DBG(CXT
, ul_debugobj(cxt
, "write-protected source, trying RDONLY."));
1034 mnt_context_reset_status(cxt
);
1035 mnt_context_set_mflags(cxt
, mflags
| MS_RDONLY
);
1036 cxt
->flags
|= MNT_FL_FORCED_RDONLY
;
1042 rc
= mnt_context_call_hooks(cxt
, MNT_STAGE_POST
);
1044 mnt_context_deinit_hooksets(cxt
);
1046 if (!mnt_context_switch_ns(cxt
, ns_old
))
1047 rc
= -MNT_ERR_NAMESPACE
;
1049 DBG(CXT
, ul_debugobj(cxt
, "mnt_context_mount() done [rc=%d]", rc
));
1054 * mnt_context_next_mount:
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
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().
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
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.
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.
1078 * See mnt_context_mount() for more details about errors and warnings.
1080 * Returns: 0 on success,
1081 * <0 in case of error (!= mount(2) errors)
1082 * 1 at the end of the list.
1084 int mnt_context_next_mount(struct libmnt_context
*cxt
,
1085 struct libmnt_iter
*itr
,
1086 struct libmnt_fs
**fs
,
1090 struct libmnt_table
*fstab
, *mountinfo
;
1091 const char *o
, *tgt
;
1092 int rc
, mounted
= 0;
1099 if (!cxt
|| !fs
|| !itr
)
1102 /* ingore --onlyonce, it's default behavior for --all */
1103 mnt_context_enable_onlyonce(cxt
, 0);
1105 rc
= mnt_context_get_fstab(cxt
, &fstab
);
1109 rc
= mnt_table_next_fs(fstab
, itr
, fs
);
1111 return rc
; /* more filesystems (or error) */
1113 o
= mnt_fs_get_user_options(*fs
);
1114 tgt
= mnt_fs_get_target(*fs
);
1116 DBG(CXT
, ul_debugobj(cxt
, "next-mount: trying %s", tgt
));
1119 if (mnt_fs_is_swaparea(*fs
) ||
1121 /* ignore root filesystem */
1122 (tgt
&& (strcmp(tgt
, "/") == 0 || strcmp(tgt
, "root") == 0)) ||
1124 /* ignore noauto filesystems */
1125 (o
&& mnt_optstr_get_option(o
, "noauto", NULL
, NULL
) == 0) ||
1127 /* ignore filesystems which don't match options patterns */
1128 (cxt
->fstype_pattern
&& !mnt_fs_match_fstype(*fs
,
1129 cxt
->fstype_pattern
)) ||
1131 /* ignore filesystems which don't match type patterns */
1132 (cxt
->optstr_pattern
&& !mnt_fs_match_options(*fs
,
1133 cxt
->optstr_pattern
))) {
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
));
1145 /* ignore already mounted filesystems */
1146 rc
= mnt_context_is_fs_mounted(cxt
, *fs
, &mounted
);
1148 if (mnt_table_is_empty(cxt
->mountinfo
)) {
1149 DBG(CXT
, ul_debugobj(cxt
, "next-mount: no mount table [rc=%d], ignore", rc
));
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.
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
);
1173 /* reset context, but protect mountinfo */
1174 mountinfo
= cxt
->mountinfo
;
1175 cxt
->mountinfo
= NULL
;
1176 mnt_reset_context(cxt
);
1177 cxt
->mountinfo
= mountinfo
;
1179 if (mnt_context_is_fork(cxt
)) {
1180 rc
= mnt_fork_context(cxt
);
1182 return rc
; /* fork error */
1184 if (mnt_context_is_parent(cxt
)) {
1185 return 0; /* parent */
1190 * child or non-forked
1193 /* copy stuff from fstab to context */
1194 rc
= mnt_context_apply_fs(cxt
, *fs
);
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.
1201 char *pattern
= cxt
->fstype_pattern
;
1202 cxt
->fstype_pattern
= NULL
;
1204 rc
= mnt_context_mount(cxt
);
1206 cxt
->fstype_pattern
= pattern
;
1212 if (mnt_context_is_child(cxt
)) {
1213 DBG(CXT
, ul_debugobj(cxt
, "next-mount: child exit [rc=%d]", rc
));
1222 * mnt_context_next_remount:
1225 * @fs: returns the current filesystem
1226 * @mntrc: returns the return code from mnt_context_mount()
1227 * @ignored: returns 1 for non-matching
1229 * This function tries to remount the next mounted filesystem (as returned by
1230 * mnt_context_get_mtab()).
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
1236 * If the filesystem does not match defined criteria, then the
1237 * mnt_context_next_remount() function returns zero, but the @ignored is
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.
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.
1250 * See mnt_context_mount() for more details about errors and warnings.
1252 * Returns: 0 on success,
1253 * <0 in case of error (!= mount(2) errors)
1254 * 1 at the end of the list.
1258 int mnt_context_next_remount(struct libmnt_context
*cxt
,
1259 struct libmnt_iter
*itr
,
1260 struct libmnt_fs
**fs
,
1264 struct libmnt_table
*mountinfo
;
1273 if (!cxt
|| !fs
|| !itr
)
1276 rc
= mnt_context_get_mountinfo(cxt
, &mountinfo
);
1280 rc
= mnt_table_next_fs(mountinfo
, itr
, fs
);
1282 return rc
; /* more filesystems (or error) */
1284 tgt
= mnt_fs_get_target(*fs
);
1286 DBG(CXT
, ul_debugobj(cxt
, "next-remount: trying %s", tgt
));
1288 /* ignore filesystems which don't match options patterns */
1289 if ((cxt
->fstype_pattern
&& !mnt_fs_match_fstype(*fs
,
1290 cxt
->fstype_pattern
)) ||
1292 /* ignore filesystems which don't match type patterns */
1293 (cxt
->optstr_pattern
&& !mnt_fs_match_options(*fs
,
1294 cxt
->optstr_pattern
))) {
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
));
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.
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
);
1317 /* restore original, but protect mountinfo */
1318 cxt
->mountinfo
= NULL
;
1319 mnt_reset_context(cxt
);
1320 cxt
->mountinfo
= mountinfo
;
1322 rc
= mnt_context_set_target(cxt
, tgt
);
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.
1329 char *pattern
= cxt
->fstype_pattern
;
1330 cxt
->fstype_pattern
= NULL
;
1332 rc
= mnt_context_mount(cxt
);
1334 cxt
->fstype_pattern
= pattern
;
1345 * Returns 1 if @dir parent is shared
1347 static int is_shared_tree(struct libmnt_context
*cxt
, const char *dir
)
1349 struct libmnt_table
*tb
= NULL
;
1350 struct libmnt_fs
*fs
;
1351 unsigned long mflags
= 0;
1352 char *mnt
= NULL
, *p
;
1354 struct libmnt_ns
*ns_old
;
1356 ns_old
= mnt_context_switch_target_ns(cxt
);
1358 return -MNT_ERR_NAMESPACE
;
1362 if (mnt_context_get_mountinfo(cxt
, &tb
) || !tb
)
1368 p
= strrchr(mnt
, '/');
1373 fs
= mnt_table_find_mountpoint(tb
, mnt
, MNT_ITER_BACKWARD
);
1375 rc
= fs
&& mnt_fs_is_kernel(fs
)
1376 && mnt_fs_get_propagation(fs
, &mflags
) == 0
1377 && (mflags
& MS_SHARED
);
1380 if (!mnt_context_switch_ns(cxt
, ns_old
))
1381 return -MNT_ERR_NAMESPACE
;
1385 int mnt_context_get_mount_excode(
1386 struct libmnt_context
*cxt
,
1393 unsigned long uflags
= 0, mflags
= 0;
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
);
1399 if (mnt_context_helper_executed(cxt
)) {
1401 * /sbin/mount.<type> called, return status
1403 if (rc
== -MNT_ERR_APPLYFLAGS
&& buf
)
1404 snprintf(buf
, bufsz
, _("WARNING: failed to apply propagation flags"));
1406 return mnt_context_get_helper_status(cxt
);
1409 if (rc
== 0 && mnt_context_get_status(cxt
) == 1) {
1411 * Libmount success && syscall success.
1413 if (buf
&& mnt_context_forced_rdonly(cxt
))
1414 snprintf(buf
, bufsz
, _("WARNING: source write-protected, mounted read-only"));
1415 return MNT_EX_SUCCESS
;
1418 mnt_context_get_mflags(cxt
, &mflags
); /* mount(2) flags */
1419 mnt_context_get_user_mflags(cxt
, &uflags
); /* userspace flags */
1421 if (!mnt_context_syscall_called(cxt
)) {
1423 * libmount errors (extra library checks)
1428 snprintf(buf
, bufsz
, _("operation permitted for root only"));
1429 return MNT_EX_USAGE
;
1432 snprintf(buf
, bufsz
, _("%s is already mounted"), src
);
1433 return MNT_EX_USAGE
;
1434 case -MNT_ERR_NOFSTAB
:
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());
1441 snprintf(buf
, bufsz
, _("can't find mount point in %s"),
1442 mnt_get_fstab_path());
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
:
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
:
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
;
1462 snprintf(buf
, bufsz
, _("can't find %s"), src
);
1464 snprintf(buf
, bufsz
, _("no mount source specified"));
1466 return MNT_EX_USAGE
;
1467 case -MNT_ERR_MOUNTOPT
:
1469 const char *opts
= mnt_context_get_options(cxt
);
1474 snprintf(buf
, bufsz
, errno
?
1475 _("failed to parse mount options '%s': %m") :
1476 _("failed to parse mount options '%s'"), opts
);
1478 snprintf(buf
, bufsz
, errno
?
1479 _("failed to parse mount options: %m") :
1480 _("failed to parse mount options"));
1482 return MNT_EX_USAGE
;
1483 case -MNT_ERR_LOOPDEV
:
1485 snprintf(buf
, bufsz
, _("failed to setup loop device for %s"), src
);
1487 case -MNT_ERR_LOOPOVERLAP
:
1489 snprintf(buf
, bufsz
, _("overlapping loop device exists for %s"), src
);
1493 snprintf(buf
, bufsz
, _("locking failed"));
1494 return MNT_EX_FILEIO
;
1495 case -MNT_ERR_NAMESPACE
:
1497 snprintf(buf
, bufsz
, _("failed to switch namespace"));
1498 return MNT_EX_SYSERR
;
1499 case -MNT_ERR_ONLYONCE
:
1501 snprintf(buf
, bufsz
, _("filesystem already mounted"));
1504 return mnt_context_get_generic_excode(rc
, buf
, bufsz
, _("mount failed: %m"));
1507 } else if (mnt_context_get_syscall_errno(cxt
) == 0) {
1509 * mount(2) syscall success, but something else failed
1510 * (probably error in utab processing).
1512 if (rc
== -MNT_ERR_LOCK
) {
1514 snprintf(buf
, bufsz
, _("filesystem was mounted, but failed to update userspace mount table"));
1515 return MNT_EX_FILEIO
;
1518 if (rc
== -MNT_ERR_NAMESPACE
) {
1520 snprintf(buf
, bufsz
, _("filesystem was mounted, but failed to switch namespace back"));
1521 return MNT_EX_SYSERR
;
1524 if (rc
== -MNT_ERR_CHOWN
) {
1526 snprintf(buf
, bufsz
, _("filesystem was mounted, but failed to change ownership: %m"));
1527 return MNT_EX_SYSERR
;
1530 if (rc
== -MNT_ERR_CHMOD
) {
1532 snprintf(buf
, bufsz
, _("filesystem was mounted, but failed to change mode: %m"));
1533 return MNT_EX_SYSERR
;
1536 if (rc
== -MNT_ERR_IDMAP
) {
1538 snprintf(buf
, bufsz
, _("filesystem was mounted, but failed to attach idmapping"));
1539 return MNT_EX_SYSERR
;
1543 return mnt_context_get_generic_excode(rc
, buf
, bufsz
,
1544 _("filesystem was mounted, but any subsequent operation failed: %m"));
1546 return MNT_EX_SOFTWARE
; /* internal error */
1551 * mount(2) and other mount related syscalls errors
1553 syserr
= mnt_context_get_syscall_errno(cxt
);
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"));
1564 snprintf(buf
, bufsz
, _("permission denied"));
1566 snprintf(buf
, bufsz
, _("must be superuser to use mount"));
1572 if (mflags
& MS_REMOUNT
) {
1573 snprintf(buf
, bufsz
, _("mount point is busy"));
1577 struct libmnt_fs
*fs
= get_already_mounted_source(cxt
);
1579 if (fs
&& mnt_fs_get_target(fs
))
1580 snprintf(buf
, bufsz
, _("%s already mounted on %s"),
1581 src
, mnt_fs_get_target(fs
));
1584 snprintf(buf
, bufsz
, _("%s already mounted or mount point busy"), src
);
1587 if (tgt
&& mnt_safe_lstat(tgt
, &st
)) {
1589 snprintf(buf
, bufsz
, _("mount point does not exist"));
1590 } else if (tgt
&& mnt_safe_stat(tgt
, &st
)) {
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
;
1597 snprintf(buf
, bufsz
, _("special device %s does not exist"), src
);
1600 snprintf(buf
, bufsz
, _("mount(2) system call failed: %m"));
1605 if (mnt_safe_stat(tgt
, &st
) || ! S_ISDIR(st
.st_mode
)) {
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
;
1612 snprintf(buf
, bufsz
, _("special device %s does not exist "
1613 "(a path prefix is not a directory)"), src
);
1616 snprintf(buf
, bufsz
, _("mount(2) system call failed: %m"));
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"));
1636 snprintf(buf
, bufsz
,
1637 _("wrong fs type, bad option, bad superblock on %s, "
1638 "missing codepage or helper program, or other error"),
1644 snprintf(buf
, bufsz
, _("mount table full"));
1649 snprintf(buf
, bufsz
, _("can't read superblock on %s"), src
);
1655 if (mnt_context_get_fstype(cxt
))
1656 snprintf(buf
, bufsz
, _("unknown filesystem type '%s'"),
1657 mnt_context_get_fstype(cxt
));
1659 snprintf(buf
, bufsz
, _("unknown filesystem type"));
1663 if (uflags
& MNT_MS_NOFAIL
)
1664 return MNT_EX_SUCCESS
;
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
);
1676 snprintf(buf
, bufsz
, _("%s is not a block device"), src
);
1680 if (uflags
& MNT_MS_NOFAIL
)
1681 return MNT_EX_SUCCESS
;
1683 snprintf(buf
, bufsz
, _("%s is not a valid block device"), src
);
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
);
1700 snprintf(buf
, bufsz
, _("mount(2) system call failed: %m"));
1705 if (uflags
& MNT_MS_NOFAIL
)
1706 return MNT_EX_SUCCESS
;
1708 snprintf(buf
, bufsz
, _("no medium found on %s"), src
);
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
);
1723 snprintf(buf
, bufsz
, _("mount(2) system call failed: %m"));
1733 static int test_perms(struct libmnt_test
*ts
, int argc
, char *argv
[])
1735 struct libmnt_context
*cxt
;
1736 struct libmnt_optlist
*ls
;
1739 cxt
= mnt_new_context();
1743 cxt
->restricted
= 1; /* emulate suid mount(8) */
1744 mnt_context_get_fs(cxt
); /* due to assert() in evaluate_permissions() */
1747 warn("missing fstab options");
1750 if (argc
== 3 && strcmp(argv
[2], "--root") == 0)
1751 cxt
->restricted
= 0;
1753 ls
= mnt_context_get_optlist(cxt
);
1756 rc
= mnt_optlist_set_optstr(ls
, argv
[1], NULL
);
1758 warn("cannot apply fstab options");
1761 cxt
->flags
|= MNT_FL_TAB_APPLIED
; /* emulate mnt_context_apply_fstab() */
1763 mnt_context_merge_mflags(cxt
);
1765 rc
= evaluate_permissions(cxt
);
1767 warn("evaluate permission failed [rc=%d]", rc
);
1770 printf("user can mount\n");
1772 mnt_free_context(cxt
);
1776 static int test_fixopts(struct libmnt_test
*ts
, int argc
, char *argv
[])
1778 struct libmnt_context
*cxt
;
1779 struct libmnt_optlist
*ls
;
1780 unsigned long flags
= 0;
1784 cxt
= mnt_new_context();
1788 cxt
->restricted
= 1; /* emulate suid mount(8) */
1789 mnt_context_get_fs(cxt
); /* to fill fs->*_optstr */
1792 warn("missing fstab options");
1795 if (argc
== 3 && strcmp(argv
[2], "--root") == 0)
1796 cxt
->restricted
= 0;
1798 ls
= mnt_context_get_optlist(cxt
);
1801 rc
= mnt_optlist_set_optstr(ls
, argv
[1], NULL
);
1803 warn("cannot apply fstab options");
1806 cxt
->flags
|= MNT_FL_TAB_APPLIED
; /* emulate mnt_context_apply_fstab() */
1808 mnt_context_merge_mflags(cxt
);
1810 rc
= evaluate_permissions(cxt
);
1812 warn("evaluate permission failed [rc=%d]", rc
);
1815 rc
= fix_optstr(cxt
);
1817 warn("fix options failed [rc=%d]", rc
);
1821 mnt_optlist_get_optstr(ls
, &p
, NULL
, 0);
1823 mnt_optlist_get_flags(ls
, &flags
, cxt
->map_linux
, 0);
1824 printf("options (dfl): '%s' [mount flags: %08lx]\n", p
, flags
);
1826 mnt_optlist_get_optstr(ls
, &p
, NULL
, MNT_OL_FLTR_ALL
);
1827 printf("options (ex.): '%s'\n", p
);
1829 mnt_free_context(cxt
);
1833 int main(int argc
, char *argv
[])
1835 struct libmnt_test tss
[] = {
1836 { "--perms", test_perms
, "<fstab-options> [--root]" },
1837 { "--fix-options", test_fixopts
, "<fstab-options> [--root]" },
1841 return mnt_run_test(tss
, argc
, argv
);
1844 #endif /* TEST_PROGRAM */