]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/context_umount.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-umount
15 * @title: Umount context
16 * @short_description: high-level API to umount operation.
20 #include <sys/mount.h>
22 #include "pathnames.h"
31 # define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */
35 # define MNT_DETACH 0x00000002 /* Just detach from the tree */
38 #ifndef UMOUNT_NOFOLLOW
39 # define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
43 # define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
46 /* search in mountinfo */
47 static int __mountinfo_find_umount_fs(struct libmnt_context
*cxt
,
49 struct libmnt_fs
**pfs
)
52 struct libmnt_ns
*ns_old
;
53 struct libmnt_table
*mountinfo
= NULL
;
62 DBG(CXT
, ul_debugobj(cxt
, " search %s in mountinfo", tgt
));
65 * The mount table may be huge, and on systems with utab we have to
66 * merge userspace mount options into /proc/self/mountinfo. This all is
67 * expensive. The tab filter can be used to filter out entries, then a mount
68 * table and utab are very tiny files.
70 * The filter uses mnt_fs_streq_{target,srcpath} function where all
71 * paths should be absolute and canonicalized. This is done within
72 * mnt_context_get_mountinfo_for_target() where LABEL, UUID or symlinks are
73 * canonicalized. If --no-canonicalize is enabled than the target path
74 * is expected already canonical.
76 * Anyway it's better to read huge mount table than canonicalize target
77 * paths. It means we use the filter only if --no-canonicalize enabled.
79 * It also means that we have to read mount table from kernel.
81 if (mnt_context_is_nocanonicalize(cxt
) && *tgt
== '/')
82 rc
= mnt_context_get_mountinfo_for_target(cxt
, &mountinfo
, tgt
);
84 rc
= mnt_context_get_mountinfo(cxt
, &mountinfo
);
87 DBG(CXT
, ul_debugobj(cxt
, "umount: failed to read mountinfo"));
91 if (mnt_table_get_nents(mountinfo
) == 0) {
92 DBG(CXT
, ul_debugobj(cxt
, "umount: mountinfo empty"));
96 ns_old
= mnt_context_switch_target_ns(cxt
);
98 return -MNT_ERR_NAMESPACE
;
101 fs
= mnt_table_find_target(mountinfo
, tgt
, MNT_ITER_BACKWARD
);
102 if (!fs
&& mnt_context_is_swapmatch(cxt
)) {
104 * Maybe the option is source rather than target (sometimes
105 * people use e.g. "umount /dev/sda1")
107 fs
= mnt_table_find_source(mountinfo
, tgt
, MNT_ITER_BACKWARD
);
110 struct libmnt_fs
*fs1
= mnt_table_find_target(mountinfo
,
111 mnt_fs_get_target(fs
),
114 DBG(CXT
, ul_debugobj(cxt
, "mountinfo is broken?!?!"));
119 /* Something was stacked over `file' on the
120 * same mount point. */
121 DBG(CXT
, ul_debugobj(cxt
,
122 "umount: %s: %s is mounted "
123 "over it on the same point",
124 tgt
, mnt_fs_get_source(fs1
)));
131 if (!fs
&& !loopdev
&& mnt_context_is_swapmatch(cxt
)) {
133 * Maybe the option is /path/file.img, try to convert to /dev/loopN
137 if (mnt_safe_stat(tgt
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
139 struct libmnt_cache
*cache
= mnt_context_get_cache(cxt
);
140 const char *bf
= cache
? mnt_resolve_path(tgt
, cache
) : tgt
;
142 count
= loopdev_count_by_backing_file(bf
, &loopdev
);
144 DBG(CXT
, ul_debugobj(cxt
,
145 "umount: %s --> %s (retry)", tgt
, loopdev
));
149 } else if (count
> 1)
150 DBG(CXT
, ul_debugobj(cxt
,
151 "umount: warning: %s is associated "
152 "with more than one loopdev", tgt
));
158 if (!mnt_context_switch_ns(cxt
, ns_old
))
159 return -MNT_ERR_NAMESPACE
;
161 DBG(CXT
, ul_debugobj(cxt
, "umount fs: %s", fs
? mnt_fs_get_target(fs
) :
166 if (!mnt_context_switch_ns(cxt
, ns_old
))
167 return -MNT_ERR_NAMESPACE
;
172 * mnt_context_find_umount_fs:
173 * @cxt: mount context
174 * @tgt: mountpoint, device, ...
175 * @pfs: returns point to filesystem
177 * Returns: 0 on success, <0 on error, 1 if target filesystem not found
179 int mnt_context_find_umount_fs(struct libmnt_context
*cxt
,
181 struct libmnt_fs
**pfs
)
186 if (!cxt
|| !tgt
|| !pfs
)
189 DBG(CXT
, ul_debugobj(cxt
, "umount: lookup FS for '%s'", tgt
));
192 return 1; /* empty string is not an error */
194 /* In future this function should be extended to support for example
195 * fsinfo() (or another cheap way kernel will support), for now the
196 * default is expensive mountinfo.
198 return __mountinfo_find_umount_fs(cxt
, tgt
, pfs
);
201 /* Check if there is something important in the utab file. The parsed utab is
202 * stored in context->utab and deallocated by mnt_free_context().
204 * This function exists to avoid (if possible) /proc/self/mountinfo usage, so
205 * don't use things like mnt_resolve_target(), mnt_context_get_mountinfo() etc here.
206 * See lookup_umount_fs() for more details.
208 static int has_utab_entry(struct libmnt_context
*cxt
, const char *target
)
210 struct libmnt_cache
*cache
= NULL
;
211 struct libmnt_fs
*fs
;
212 struct libmnt_iter itr
;
219 const char *path
= mnt_get_utab_path();
221 if (!path
|| is_file_empty(path
))
223 cxt
->utab
= mnt_new_table();
226 cxt
->utab
->fmt
= MNT_FMT_UTAB
;
227 if (mnt_table_parse_file(cxt
->utab
, path
))
231 /* paths in utab are canonicalized */
232 cache
= mnt_context_get_cache(cxt
);
233 cn
= mnt_resolve_path(target
, cache
);
234 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
236 while (mnt_table_next_fs(cxt
->utab
, &itr
, &fs
) == 0) {
237 if (mnt_fs_streq_target(fs
, cn
)) {
248 /* returns: 1 not found; <0 on error; 1 success */
249 static int lookup_umount_fs_by_statfs(struct libmnt_context
*cxt
, const char *tgt
)
257 DBG(CXT
, ul_debugobj(cxt
, " lookup by statfs"));
260 * Let's try to avoid mountinfo usage at all to minimize performance
261 * degradation. Don't forget that kernel has to compose *whole*
262 * mountinfo about all mountpoints although we look for only one entry.
264 * All we need is fstype and to check if there is no userspace mount
265 * options for the target (e.g. helper=udisks to call /sbin/umount.udisks).
267 * So, let's use statfs() if possible (it's bad idea for --lazy/--force
268 * umounts as target is probably unreachable NFS, also for --detach-loop
269 * as this additionally needs to know the name of the loop device).
271 if (mnt_context_is_restricted(cxt
)
273 || mnt_context_within_helper(cxt
)
274 || mnt_context_is_force(cxt
)
275 || mnt_context_is_lazy(cxt
)
276 || mnt_context_is_nocanonicalize(cxt
)
277 || mnt_context_is_loopdel(cxt
)
278 || mnt_safe_stat(tgt
, &st
) != 0 || !S_ISDIR(st
.st_mode
)
279 || has_utab_entry(cxt
, tgt
))
280 return 1; /* not found */
282 type
= mnt_fs_get_fstype(cxt
->fs
);
287 DBG(CXT
, ul_debugobj(cxt
, " trying fstatfs()"));
289 /* O_PATH avoids triggering automount points. */
290 fd
= open(tgt
, O_PATH
);
292 if (fstatfs(fd
, &vfs
) == 0)
293 type
= mnt_statfs_get_fstype(&vfs
);
297 int rc
= mnt_fs_set_fstype(cxt
->fs
, type
);
303 DBG(CXT
, ul_debugobj(cxt
, " umount: disabling mountinfo"));
304 mnt_context_disable_mtab(cxt
, TRUE
);
306 DBG(CXT
, ul_debugobj(cxt
,
307 " mountinfo unnecessary [type=%s]", type
));
311 return 1; /* not found */
314 /* returns: 1 not found; <0 on error; 1 success */
315 static int lookup_umount_fs_by_mountinfo(struct libmnt_context
*cxt
, const char *tgt
)
317 struct libmnt_fs
*fs
= NULL
;
323 DBG(CXT
, ul_debugobj(cxt
, " lookup by mountinfo"));
326 rc
= __mountinfo_find_umount_fs(cxt
, tgt
, &fs
);
332 mnt_fs_set_source(cxt
->fs
, NULL
);
333 mnt_fs_set_target(cxt
->fs
, NULL
);
335 if (!mnt_copy_fs(cxt
->fs
, fs
)) {
336 DBG(CXT
, ul_debugobj(cxt
, " failed to copy FS"));
339 DBG(CXT
, ul_debugobj(cxt
, " mountinfo applied"));
342 cxt
->flags
|= MNT_FL_TAB_APPLIED
;
346 /* This function searchs for FS according to cxt->fs->target,
347 * apply result to cxt->fs and it's umount replacement to
348 * mnt_context_apply_fstab(), use mnt_context_tab_applied()
351 * The goal is to minimize situations when we need to parse
352 * /proc/self/mountinfo.
354 static int lookup_umount_fs(struct libmnt_context
*cxt
)
362 DBG(CXT
, ul_debugobj(cxt
, "umount: lookup FS"));
364 tgt
= mnt_fs_get_target(cxt
->fs
);
366 DBG(CXT
, ul_debugobj(cxt
, " undefined target"));
370 /* try get fs type by statfs() */
371 rc
= lookup_umount_fs_by_statfs(cxt
, tgt
);
375 /* get complete fs from fs entry from mountinfo */
376 rc
= lookup_umount_fs_by_mountinfo(cxt
, tgt
);
380 DBG(CXT
, ul_debugobj(cxt
, " cannot find '%s'", tgt
));
381 return 0; /* this is correct! */
384 if (rc
== 0 && cxt
->fs
) {
385 struct libmnt_optlist
*ol
= mnt_context_get_optlist(cxt
);
390 rc
= mnt_optlist_set_optstr(ol
, mnt_fs_get_options(cxt
->fs
), NULL
);
392 DBG(CXT
, ul_debugobj(cxt
, " lookup done [rc=%d]", rc
));
396 /* check if @devname is loopdev and if the device is associated
397 * with a source from @fstab_fs
399 static int is_associated_fs(const char *devname
, struct libmnt_fs
*fs
)
401 uintmax_t offset
= 0;
402 const char *src
, *optstr
;
407 /* check if it begins with /dev/loop */
408 if (strncmp(devname
, _PATH_DEV_LOOP
, sizeof(_PATH_DEV_LOOP
) - 1) != 0)
411 src
= mnt_fs_get_srcpath(fs
);
415 /* check for the offset option in @fs */
416 optstr
= mnt_fs_get_user_options(fs
);
419 mnt_optstr_get_option(optstr
, "offset", &val
, &valsz
) == 0) {
420 flags
|= LOOPDEV_FL_OFFSET
;
422 if (mnt_parse_offset(val
, valsz
, &offset
) != 0)
426 return loopdev_is_used(devname
, src
, offset
, 0, flags
);
429 /* returns: <0 on error; 1 not found (not wanted) */
430 static int prepare_helper_from_option(struct libmnt_context
*cxt
,
433 struct libmnt_optlist
*ol
;
434 struct libmnt_opt
*opt
;
437 ol
= mnt_context_get_optlist(cxt
);
441 opt
= mnt_optlist_get_named(ol
, name
, cxt
->map_userspace
);
442 if (!opt
|| !mnt_opt_has_value(opt
))
445 suffix
= mnt_opt_get_value(opt
);
446 DBG(CXT
, ul_debugobj(cxt
, "umount: umount.%s %s requested", suffix
, name
));
448 return mnt_context_prepare_helper(cxt
, "umount", suffix
);
451 static int is_fuse_usermount(struct libmnt_context
*cxt
, int *errsv
)
453 struct libmnt_ns
*ns_old
;
454 struct libmnt_optlist
*ol
;
455 struct libmnt_opt
*opt
;
456 const char *type
= mnt_fs_get_fstype(cxt
->fs
);
457 const char *val
= NULL
;;
458 uid_t uid
, entry_uid
;
465 if (strcmp(type
, "fuse") != 0 &&
466 strcmp(type
, "fuseblk") != 0 &&
467 strncmp(type
, "fuse.", 5) != 0 &&
468 strncmp(type
, "fuseblk.", 8) != 0)
471 ol
= mnt_context_get_optlist(cxt
);
475 opt
= mnt_optlist_get_named(ol
, "user_id", NULL
);
477 val
= mnt_opt_get_value(opt
);
478 if (!val
|| mnt_opt_get_map(opt
))
481 if (mnt_parse_uid(val
, strlen(val
), &entry_uid
) != 0)
484 /* get current user */
485 ns_old
= mnt_context_switch_origin_ns(cxt
);
487 *errsv
= -MNT_ERR_NAMESPACE
;
493 if (!mnt_context_switch_ns(cxt
, ns_old
)) {
494 *errsv
= -MNT_ERR_NAMESPACE
;
498 return uid
== entry_uid
;
502 * Note that cxt->fs contains relevant mountinfo entry!
504 static int evaluate_permissions(struct libmnt_context
*cxt
)
506 unsigned long fstab_flags
= 0;
507 struct libmnt_table
*fstab
;
508 const char *tgt
, *src
, *optstr
;
510 struct libmnt_fs
*fs
;
514 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
516 if (!mnt_context_is_restricted(cxt
))
517 return 0; /* superuser mount */
519 DBG(CXT
, ul_debugobj(cxt
, "umount: evaluating permissions"));
521 if (!mnt_context_tab_applied(cxt
)) {
522 DBG(CXT
, ul_debugobj(cxt
,
523 "cannot find %s in mountinfo and you are not root",
524 mnt_fs_get_target(cxt
->fs
)));
528 if (!mnt_context_is_nohelpers(cxt
)) {
529 rc
= prepare_helper_from_option(cxt
, "uhelper");
531 return rc
; /* error */
532 if (rc
== 0 && cxt
->helper
)
533 return 0; /* we'll call /sbin/umount.<uhelper> */
537 * Check if this is a fuse mount for the current user,
538 * if so then unmounting is allowed
540 if (is_fuse_usermount(cxt
, &rc
)) {
541 DBG(CXT
, ul_debugobj(cxt
, "fuse user mount, umount is allowed"));
548 * User mounts have to be in /etc/fstab
550 rc
= mnt_context_get_fstab(cxt
, &fstab
);
554 tgt
= mnt_fs_get_target(cxt
->fs
);
555 src
= mnt_fs_get_source(cxt
->fs
);
557 if (mnt_fs_get_bindsrc(cxt
->fs
)) {
558 src
= mnt_fs_get_bindsrc(cxt
->fs
);
559 DBG(CXT
, ul_debugobj(cxt
,
560 "umount: using bind source: %s", src
));
563 /* If fstab contains the two lines
564 * /dev/sda1 /mnt/zip auto user,noauto 0 0
565 * /dev/sda4 /mnt/zip auto user,noauto 0 0
566 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
567 * So, we must not look for the file, but for the pair (dev,file) in fstab.
569 fs
= mnt_table_find_pair(fstab
, src
, tgt
, MNT_ITER_FORWARD
);
572 * It's possible that there is /path/file.img in fstab and
573 * /dev/loop0 in mountinfo -- then we have to check the relation
574 * between loopdev and the file.
576 fs
= mnt_table_find_target(fstab
, tgt
, MNT_ITER_FORWARD
);
578 struct libmnt_cache
*cache
= mnt_context_get_cache(cxt
);
579 const char *sp
= mnt_fs_get_srcpath(cxt
->fs
); /* devname from mountinfo */
580 const char *dev
= sp
&& cache
? mnt_resolve_path(sp
, cache
) : sp
;
582 if (!dev
|| !is_associated_fs(dev
, fs
))
586 DBG(CXT
, ul_debugobj(cxt
,
587 "umount %s: mountinfo disagrees with fstab",
594 * User mounting and unmounting is allowed only if fstab contains one
595 * of the options `user', `users' or `owner' or `group'.
597 * The option `users' allows arbitrary users to mount and unmount -
598 * this may be a security risk.
600 * The options `user', `owner' and `group' only allow unmounting by the
601 * user that mounted (visible in mountinfo).
603 optstr
= mnt_fs_get_user_options(fs
); /* FSTAB mount options! */
607 if (mnt_optstr_get_flags(optstr
, &fstab_flags
,
608 mnt_get_builtin_optmap(MNT_USERSPACE_MAP
)))
611 if (fstab_flags
& MNT_MS_USERS
) {
612 DBG(CXT
, ul_debugobj(cxt
,
613 "umount: promiscuous setting ('users') in fstab"));
617 * Check user=<username> setting from utab if there is a user, owner or
618 * group option in /etc/fstab
620 if (fstab_flags
& (MNT_MS_USER
| MNT_MS_OWNER
| MNT_MS_GROUP
)) {
622 struct libmnt_optlist
*ol
;
623 struct libmnt_opt
*opt
;
624 char *curr_user
= NULL
;
625 struct libmnt_ns
*ns_old
;
627 DBG(CXT
, ul_debugobj(cxt
,
628 "umount: checking user=<username> from mountinfo"));
630 ns_old
= mnt_context_switch_origin_ns(cxt
);
632 return -MNT_ERR_NAMESPACE
;
634 curr_user
= mnt_get_username(getuid());
636 if (!mnt_context_switch_ns(cxt
, ns_old
)) {
638 return -MNT_ERR_NAMESPACE
;
641 DBG(CXT
, ul_debugobj(cxt
, "umount %s: cannot "
642 "convert %d to username", tgt
, getuid()));
646 /* get "user=" from utab */
647 ol
= mnt_context_get_optlist(cxt
);
652 opt
= mnt_optlist_get_named(ol
, "user", cxt
->map_userspace
);
653 if (opt
&& mnt_opt_has_value(opt
))
654 ok
= !strcmp(curr_user
, mnt_opt_get_value(opt
));
660 DBG(CXT
, ul_debugobj(cxt
, "umount %s is allowed", tgt
));
664 DBG(CXT
, ul_debugobj(cxt
, "umount is not allowed for you"));
668 static int exec_helper(struct libmnt_context
*cxt
)
670 char *namespace = NULL
;
671 struct libmnt_ns
*ns_tgt
= mnt_context_get_target_ns(cxt
);
678 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
679 assert(cxt
->helper_exec_status
== 1);
681 if (mnt_context_is_fake(cxt
)) {
682 DBG(CXT
, ul_debugobj(cxt
, "fake mode: does not execute helper"));
683 cxt
->helper_exec_status
= rc
= 0;
688 && asprintf(&namespace, "/proc/%i/fd/%i",
689 getpid(), ns_tgt
->fd
) == -1) {
699 const char *args
[12], *type
;
702 if (drop_permissions() != 0)
705 if (!mnt_context_switch_origin_ns(cxt
))
708 type
= mnt_fs_get_fstype(cxt
->fs
);
710 args
[i
++] = cxt
->helper
; /* 1 */
711 args
[i
++] = mnt_fs_get_target(cxt
->fs
); /* 2 */
713 if (mnt_context_is_nomtab(cxt
))
714 args
[i
++] = "-n"; /* 3 */
715 if (mnt_context_is_lazy(cxt
))
716 args
[i
++] = "-l"; /* 4 */
717 if (mnt_context_is_force(cxt
))
718 args
[i
++] = "-f"; /* 5 */
719 if (mnt_context_is_verbose(cxt
))
720 args
[i
++] = "-v"; /* 6 */
721 if (mnt_context_is_rdonly_umount(cxt
))
722 args
[i
++] = "-r"; /* 7 */
725 && !endswith(cxt
->helper
, type
)) {
726 args
[i
++] = "-t"; /* 8 */
727 args
[i
++] = type
; /* 9 */
730 args
[i
++] = "-N"; /* 10 */
731 args
[i
++] = namespace; /* 11 */
734 args
[i
] = NULL
; /* 12 */
735 for (i
= 0; args
[i
]; i
++)
736 DBG(CXT
, ul_debugobj(cxt
, "argv[%d] = \"%s\"",
739 execv(cxt
->helper
, (char * const *) args
);
746 if (waitpid(pid
, &st
, 0) == (pid_t
) -1) {
747 cxt
->helper_status
= -1;
750 cxt
->helper_status
= WIFEXITED(st
) ? WEXITSTATUS(st
) : -1;
751 cxt
->helper_exec_status
= rc
= 0;
753 DBG(CXT
, ul_debugobj(cxt
, "%s executed [status=%d, rc=%d%s]",
755 cxt
->helper_status
, rc
,
756 rc
? " waitpid failed" : ""));
761 cxt
->helper_exec_status
= rc
= -errno
;
762 DBG(CXT
, ul_debugobj(cxt
, "fork() failed"));
771 * mnt_context_helper_setopt() backend.
773 * This function applies umount.type command line option (for example parsed
774 * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
775 * then 1 is returned.
777 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
779 int mnt_context_umount_setopt(struct libmnt_context
*cxt
, int c
, char *arg
)
784 assert(cxt
->action
== MNT_ACT_UMOUNT
);
788 rc
= mnt_context_disable_mtab(cxt
, TRUE
);
791 rc
= mnt_context_enable_lazy(cxt
, TRUE
);
794 rc
= mnt_context_enable_force(cxt
, TRUE
);
797 rc
= mnt_context_enable_verbose(cxt
, TRUE
);
800 rc
= mnt_context_enable_rdonly_umount(cxt
, TRUE
);
804 rc
= mnt_context_set_fstype(cxt
, arg
);
808 rc
= mnt_context_set_target_ns(cxt
, arg
);
817 /* Check whether the kernel supports the UMOUNT_NOFOLLOW flag */
818 static int umount_nofollow_support(void)
820 int res
= umount2("", UMOUNT_UNUSED
);
821 if (res
!= -1 || errno
!= EINVAL
)
824 res
= umount2("", UMOUNT_NOFOLLOW
);
825 if (res
!= -1 || errno
!= ENOENT
)
831 static int do_umount(struct libmnt_context
*cxt
)
833 int rc
= 0, flags
= 0;
834 const char *src
, *target
;
839 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
840 assert(cxt
->syscall_status
== 1);
843 return exec_helper(cxt
);
845 src
= mnt_fs_get_srcpath(cxt
->fs
);
846 target
= mnt_fs_get_target(cxt
->fs
);
851 DBG(CXT
, ul_debugobj(cxt
, "do umount"));
853 if (mnt_context_is_restricted(cxt
) && !mnt_context_is_fake(cxt
)) {
855 * extra paranoia for non-root users
856 * -- chdir to the parent of the mountpoint and use NOFOLLOW
857 * flag to avoid races and symlink attacks.
859 if (umount_nofollow_support())
860 flags
|= UMOUNT_NOFOLLOW
;
862 rc
= mnt_chdir_to_parent(target
, &tgtbuf
);
868 if (mnt_context_is_lazy(cxt
))
871 if (mnt_context_is_force(cxt
))
874 DBG(CXT
, ul_debugobj(cxt
, "umount(2) [target='%s', flags=0x%08x]%s",
876 mnt_context_is_fake(cxt
) ? " (FAKE)" : ""));
878 if (mnt_context_is_fake(cxt
))
881 rc
= flags
? umount2(target
, flags
) : umount(target
);
883 cxt
->syscall_status
= -errno
;
888 * try remount read-only
891 && cxt
->syscall_status
== -EBUSY
892 && mnt_context_is_rdonly_umount(cxt
)
894 struct libmnt_optlist
*ol
= mnt_context_get_optlist(cxt
);
896 /* keep info about remount in mount flags */
898 mnt_optlist_append_flags(ol
, MS_REMOUNT
| MS_RDONLY
, cxt
->map_linux
);
900 mnt_context_enable_loopdel(cxt
, FALSE
);
902 DBG(CXT
, ul_debugobj(cxt
,
903 "umount(2) failed [errno=%d] -- trying to remount read-only",
904 -cxt
->syscall_status
));
906 rc
= mount(src
, mnt_fs_get_target(cxt
->fs
), NULL
,
907 MS_REMOUNT
| MS_RDONLY
, NULL
);
909 cxt
->syscall_status
= -errno
;
910 DBG(CXT
, ul_debugobj(cxt
,
911 "read-only re-mount(2) failed [errno=%d]",
912 -cxt
->syscall_status
));
914 return -cxt
->syscall_status
;
916 cxt
->syscall_status
= 0;
917 DBG(CXT
, ul_debugobj(cxt
, "read-only re-mount(2) success"));
922 DBG(CXT
, ul_debugobj(cxt
, "umount(2) failed [errno=%d]",
923 -cxt
->syscall_status
));
924 return -cxt
->syscall_status
;
927 cxt
->syscall_status
= 0;
928 DBG(CXT
, ul_debugobj(cxt
, "umount(2) success"));
933 * mnt_context_prepare_umount:
934 * @cxt: mount context
936 * Prepare context for umounting, unnecessary for mnt_context_umount().
938 * Returns: 0 on success, and negative number in case of error.
940 int mnt_context_prepare_umount(struct libmnt_context
*cxt
)
943 unsigned long flags
= 0;
944 struct libmnt_ns
*ns_old
;
946 if (!cxt
|| !cxt
->fs
|| mnt_fs_is_swaparea(cxt
->fs
))
948 if (!mnt_context_get_source(cxt
) && !mnt_context_get_target(cxt
))
950 if (cxt
->flags
& MNT_FL_PREPARED
)
953 assert(cxt
->helper_exec_status
== 1);
954 assert(cxt
->syscall_status
== 1);
956 free(cxt
->helper
); /* be paranoid */
958 cxt
->action
= MNT_ACT_UMOUNT
;
960 ns_old
= mnt_context_switch_target_ns(cxt
);
962 return -MNT_ERR_NAMESPACE
;
964 rc
= lookup_umount_fs(cxt
);
966 rc
= mnt_context_merge_mflags(cxt
);
968 rc
= evaluate_permissions(cxt
);
970 if (!rc
&& !mnt_context_is_nohelpers(cxt
) && !cxt
->helper
) {
971 /* on helper= mount option based helper */
972 rc
= prepare_helper_from_option(cxt
, "helper");
976 /* on fstype based helper */
977 rc
= mnt_context_prepare_helper(cxt
, "umount", NULL
);
981 rc
= mnt_context_get_user_mflags(cxt
, &flags
);
983 if (!rc
&& (flags
& MNT_MS_LOOP
))
984 /* loop option explicitly specified in utab, detach this loop */
985 mnt_context_enable_loopdel(cxt
, TRUE
);
987 if (!rc
&& mnt_context_is_loopdel(cxt
) && cxt
->fs
) {
988 const char *src
= mnt_fs_get_srcpath(cxt
->fs
);
990 if (src
&& (!is_loopdev(src
) || loopdev_is_autoclear(src
)))
991 mnt_context_enable_loopdel(cxt
, FALSE
);
995 DBG(CXT
, ul_debugobj(cxt
, "umount: preparing failed"));
998 cxt
->flags
|= MNT_FL_PREPARED
;
1000 if (!mnt_context_switch_ns(cxt
, ns_old
))
1001 return -MNT_ERR_NAMESPACE
;
1007 * mnt_context_do_umount:
1008 * @cxt: mount context
1010 * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
1011 * Unnecessary for mnt_context_umount().
1013 * See also mnt_context_disable_helpers().
1015 * WARNING: non-zero return code does not mean that umount(2) syscall or
1016 * umount.type helper wasn't successfully called.
1018 * Check mnt_context_get_status() after error!
1020 * Returns: 0 on success;
1021 * >0 in case of umount(2) error (returns syscall errno),
1022 * <0 in case of other errors.
1024 int mnt_context_do_umount(struct libmnt_context
*cxt
)
1027 struct libmnt_ns
*ns_old
;
1031 assert(cxt
->helper_exec_status
== 1);
1032 assert(cxt
->syscall_status
== 1);
1033 assert((cxt
->flags
& MNT_FL_PREPARED
));
1034 assert((cxt
->action
== MNT_ACT_UMOUNT
));
1035 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
1037 ns_old
= mnt_context_switch_target_ns(cxt
);
1039 return -MNT_ERR_NAMESPACE
;
1041 rc
= do_umount(cxt
);
1045 if (mnt_context_get_status(cxt
) && !mnt_context_is_fake(cxt
)) {
1047 * Umounted, do some post-umount operations
1049 * - refresh in-memory utab stuff if remount rather than
1050 * umount has been performed
1052 if (mnt_context_is_loopdel(cxt
)
1053 && !mnt_optlist_is_remount(cxt
->optlist
))
1054 rc
= mnt_context_delete_loopdev(cxt
);
1057 if (!mnt_context_switch_ns(cxt
, ns_old
))
1058 return -MNT_ERR_NAMESPACE
;
1064 * mnt_context_finalize_umount:
1067 * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called
1068 * after mnt_context_do_umount(). See also mnt_context_set_syscall_status().
1070 * Returns: negative number on error, 0 on success.
1072 int mnt_context_finalize_umount(struct libmnt_context
*cxt
)
1078 assert((cxt
->flags
& MNT_FL_PREPARED
));
1079 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
1081 rc
= mnt_context_prepare_update(cxt
);
1083 rc
= mnt_context_update_tabs(cxt
);
1089 * mnt_context_umount:
1090 * @cxt: umount context
1092 * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
1094 * This is similar to:
1096 * mnt_context_prepare_umount(cxt);
1097 * mnt_context_do_umount(cxt);
1098 * mnt_context_finalize_umount(cxt);
1100 * See also mnt_context_disable_helpers().
1102 * WARNING: non-zero return code does not mean that umount(2) syscall or
1103 * umount.type helper wasn't successfully called.
1105 * Check mnt_context_get_status() after error!
1107 * Returns: 0 on success;
1108 * >0 in case of umount(2) error (returns syscall errno),
1109 * <0 in case of other errors.
1111 int mnt_context_umount(struct libmnt_context
*cxt
)
1114 struct libmnt_ns
*ns_old
;
1118 assert(cxt
->helper_exec_status
== 1);
1119 assert(cxt
->syscall_status
== 1);
1121 DBG(CXT
, ul_debugobj(cxt
, "umount: %s", mnt_context_get_target(cxt
)));
1123 ns_old
= mnt_context_switch_target_ns(cxt
);
1125 return -MNT_ERR_NAMESPACE
;
1127 rc
= mnt_context_prepare_umount(cxt
);
1129 rc
= mnt_context_prepare_update(cxt
);
1131 rc
= mnt_context_do_umount(cxt
);
1133 rc
= mnt_context_update_tabs(cxt
);
1135 if (!mnt_context_switch_ns(cxt
, ns_old
))
1136 return -MNT_ERR_NAMESPACE
;
1143 * mnt_context_next_umount:
1146 * @fs: returns the current filesystem
1147 * @mntrc: returns the return code from mnt_context_umount()
1148 * @ignored: returns 1 for not matching
1150 * This function tries to umount the next filesystem from mountinfo file.
1152 * You can filter out filesystems by:
1153 * mnt_context_set_options_pattern() to simulate umount -a -O pattern
1154 * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern
1156 * If the filesystem is not mounted or does not match the defined criteria,
1157 * then the function mnt_context_next_umount() returns zero, but the @ignored is
1158 * non-zero. Note that the root filesystem is always ignored.
1160 * If umount(2) syscall or umount.type helper failed, then the
1161 * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero.
1162 * Use also mnt_context_get_status() to check if the filesystem was
1163 * successfully umounted.
1165 * Returns: 0 on success,
1166 * <0 in case of error (!= umount(2) errors)
1167 * 1 at the end of the list.
1169 int mnt_context_next_umount(struct libmnt_context
*cxt
,
1170 struct libmnt_iter
*itr
,
1171 struct libmnt_fs
**fs
,
1175 struct libmnt_table
*mountinfo
;
1184 if (!cxt
|| !fs
|| !itr
)
1187 rc
= mnt_context_get_mountinfo(cxt
, &mountinfo
);
1188 cxt
->mountinfo
= NULL
; /* do not reset mountinfo */
1189 mnt_reset_context(cxt
);
1194 cxt
->mountinfo
= mountinfo
;
1197 rc
= mnt_table_next_fs(mountinfo
, itr
, fs
);
1199 return rc
; /* no more filesystems (or error) */
1201 tgt
= mnt_fs_get_target(*fs
);
1204 DBG(CXT
, ul_debugobj(cxt
, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt
,
1205 mnt_fs_get_fstype(*fs
), cxt
->fstype_pattern
, mnt_fs_get_options(*fs
), cxt
->optstr_pattern
));
1207 /* ignore filesystems which don't match options patterns */
1208 if ((cxt
->fstype_pattern
&& !mnt_fs_match_fstype(*fs
,
1209 cxt
->fstype_pattern
)) ||
1211 /* ignore filesystems which don't match type patterns */
1212 (cxt
->optstr_pattern
&& !mnt_fs_match_options(*fs
,
1213 cxt
->optstr_pattern
))) {
1217 DBG(CXT
, ul_debugobj(cxt
, "next-umount: not-match"));
1221 rc
= mnt_context_set_fs(cxt
, *fs
);
1224 rc
= mnt_context_umount(cxt
);
1231 int mnt_context_get_umount_excode(
1232 struct libmnt_context
*cxt
,
1237 if (mnt_context_helper_executed(cxt
))
1239 * /sbin/umount.<type> called, return status
1241 return mnt_context_get_helper_status(cxt
);
1243 if (rc
== 0 && mnt_context_get_status(cxt
) == 1)
1245 * Libmount success && syscall success.
1247 return MNT_EX_SUCCESS
;
1249 if (!mnt_context_syscall_called(cxt
)) {
1251 * libmount errors (extra library checks)
1253 if (rc
== -EPERM
&& !mnt_context_tab_applied(cxt
)) {
1254 /* failed to evaluate permissions because not found
1255 * relevant entry in mountinfo */
1257 snprintf(buf
, bufsz
, _("not mounted"));
1258 return MNT_EX_USAGE
;
1261 if (rc
== -MNT_ERR_LOCK
) {
1263 snprintf(buf
, bufsz
, _("locking failed"));
1264 return MNT_EX_FILEIO
;
1267 if (rc
== -MNT_ERR_NAMESPACE
) {
1269 snprintf(buf
, bufsz
, _("failed to switch namespace"));
1270 return MNT_EX_SYSERR
;
1272 return mnt_context_get_generic_excode(rc
, buf
, bufsz
,
1273 _("umount failed: %m"));
1275 } if (mnt_context_get_syscall_errno(cxt
) == 0) {
1277 * umount(2) syscall success, but something else failed
1278 * (probably error in utab processing).
1280 if (rc
== -MNT_ERR_LOCK
) {
1282 snprintf(buf
, bufsz
, _("filesystem was unmounted, but failed to update userspace mount table"));
1283 return MNT_EX_FILEIO
;
1286 if (rc
== -MNT_ERR_NAMESPACE
) {
1288 snprintf(buf
, bufsz
, _("filesystem was unmounted, but failed to switch namespace back"));
1289 return MNT_EX_SYSERR
;
1294 return mnt_context_get_generic_excode(rc
, buf
, bufsz
,
1295 _("filesystem was unmounted, but any subsequent operation failed: %m"));
1297 return MNT_EX_SOFTWARE
; /* internal error */
1304 int syserr
= mnt_context_get_syscall_errno(cxt
);
1308 snprintf(buf
, bufsz
, _("invalid block device")); /* ??? */
1311 snprintf(buf
, bufsz
, _("not mounted"));
1314 snprintf(buf
, bufsz
, _("can't write superblock"));
1317 snprintf(buf
, bufsz
, _("target is busy"));
1320 snprintf(buf
, bufsz
, _("no mount point specified"));
1323 snprintf(buf
, bufsz
, _("must be superuser to unmount"));
1326 snprintf(buf
, bufsz
, _("block devices are not permitted on filesystem"));
1329 return mnt_context_get_generic_excode(syserr
, buf
, bufsz
,_("umount(2) system call failed: %m"));