2 * umount(8) -- mount a filesystem
4 * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
5 * Written by Karel Zak <kzak@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <sys/types.h>
36 #include "exitcodes.h"
37 #include "closestream.h"
38 #include "pathnames.h"
39 #include "canonicalize.h"
42 static int table_parser_errcb(struct libmnt_table
*tb
__attribute__((__unused__
)),
43 const char *filename
, int line
)
46 warnx(_("%s: parse error: ignore entry at line %d."),
52 static void __attribute__((__noreturn__
)) print_version(void)
54 const char *ver
= NULL
;
55 const char **features
= NULL
, **p
;
57 mnt_get_library_version(&ver
);
58 mnt_get_library_features(&features
);
60 printf(_("%s from %s (libmount %s"),
61 program_invocation_short_name
,
66 fputs(p
== features
? ": " : ", ", stdout
);
70 exit(MOUNT_EX_SUCCESS
);
72 static void __attribute__((__noreturn__
)) usage(FILE *out
)
74 fputs(USAGE_HEADER
, out
);
77 " %1$s -a [options]\n"
78 " %1$s [options] <source> | <directory>\n"),
79 program_invocation_short_name
);
81 fputs(USAGE_OPTIONS
, out
);
82 fputs(_(" -a, --all unmount all filesystems\n"), out
);
83 fputs(_(" -A, --all-targets unmount all mountpoints for the given device in the\n"
84 " current namespace\n"), out
);
85 fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out
);
86 fputs(_(" -d, --detach-loop if mounted loop device, also free this loop device\n"), out
);
87 fputs(_(" --fake dry run; skip the umount(2) syscall\n"), out
);
88 fputs(_(" -f, --force force unmount (in case of an unreachable NFS system)\n"), out
);
89 fputs(_(" -i, --internal-only don't call the umount.<type> helpers\n"), out
);
90 fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out
);
91 fputs(_(" -l, --lazy detach the filesystem now, clean up things later\n"), out
);
92 fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out
);
93 fputs(_(" -R, --recursive recursively unmount a target with all its children\n"), out
);
94 fputs(_(" -r, --read-only in case unmounting fails, try to remount read-only\n"), out
);
95 fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out
);
96 fputs(_(" -v, --verbose say what is being done\n"), out
);
98 fputs(USAGE_SEPARATOR
, out
);
99 fputs(USAGE_HELP
, out
);
100 fputs(USAGE_VERSION
, out
);
101 fprintf(out
, USAGE_MAN_TAIL("umount(8)"));
103 exit(out
== stderr
? MOUNT_EX_USAGE
: MOUNT_EX_SUCCESS
);
106 static void __attribute__((__noreturn__
)) exit_non_root(const char *option
)
108 const uid_t ruid
= getuid();
109 const uid_t euid
= geteuid();
111 if (ruid
== 0 && euid
!= 0) {
112 /* user is root, but setuid to non-root */
115 _("only root can use \"--%s\" option "
116 "(effective UID is %u)"),
118 errx(MOUNT_EX_USAGE
, _("only root can do that "
119 "(effective UID is %u)"), euid
);
122 errx(MOUNT_EX_USAGE
, _("only root can use \"--%s\" option"), option
);
123 errx(MOUNT_EX_USAGE
, _("only root can do that"));
126 static void success_message(struct libmnt_context
*cxt
)
128 const char *tgt
, *src
;
130 if (mnt_context_helper_executed(cxt
)
131 || mnt_context_get_status(cxt
) != 1)
134 tgt
= mnt_context_get_target(cxt
);
138 src
= mnt_context_get_source(cxt
);
140 warnx(_("%s (%s) unmounted"), tgt
, src
);
142 warnx(_("%s unmounted"), tgt
);
146 * Handles generic errors like ENOMEM, ...
149 * <0 error (usually -errno)
151 * Returns exit status (MOUNT_EX_*) and prints error message.
153 static int handle_generic_errors(int rc
, const char *msg
, ...)
168 rc
= MOUNT_EX_SYSERR
;
179 static int mk_exit_code(struct libmnt_context
*cxt
, int rc
)
182 const char *tgt
= mnt_context_get_target(cxt
);
184 if (mnt_context_helper_executed(cxt
))
186 * /sbin/umount.<type> called, return status
188 return mnt_context_get_helper_status(cxt
);
190 if (rc
== 0 && mnt_context_get_status(cxt
) == 1)
192 * Libmount success && syscall success.
194 return MOUNT_EX_SUCCESS
;
197 if (!mnt_context_syscall_called(cxt
)) {
199 * libmount errors (extra library checks)
201 if (rc
== -EPERM
&& !mnt_context_tab_applied(cxt
)) {
202 /* failed to evaluate permissions because not found
203 * relevant entry in mtab */
204 warnx(_("%s: not mounted"), tgt
);
205 return MOUNT_EX_USAGE
;
207 return handle_generic_errors(rc
, _("%s: umount failed"), tgt
);
209 } else if (mnt_context_get_syscall_errno(cxt
) == 0) {
211 * umount(2) syscall success, but something else failed
212 * (probably error in mtab processing).
215 return handle_generic_errors(rc
,
216 _("%s: filesystem was unmounted, but mount(8) failed"),
219 return MOUNT_EX_SOFTWARE
; /* internal error */
226 syserr
= mnt_context_get_syscall_errno(cxt
);
230 warnx(_("%s: invalid block device"), tgt
); /* ??? */
233 warnx(_("%s: not mounted"), tgt
);
236 warnx(_("%s: can't write superblock"), tgt
);
239 warnx(_("%s: target is busy\n"
240 " (In some cases useful info about processes that\n"
241 " use the device is found by lsof(8) or fuser(1).)"),
246 warnx(_("%s: mountpoint not found"), tgt
);
248 warnx(_("undefined mountpoint"));
251 warnx(_("%s: must be superuser to unmount"), tgt
);
254 warnx(_("%s: block devices are not permitted on filesystem"), tgt
);
261 return MOUNT_EX_FAIL
;
264 static int umount_all(struct libmnt_context
*cxt
)
266 struct libmnt_iter
*itr
;
267 struct libmnt_fs
*fs
;
268 int mntrc
, ignored
, rc
= 0;
270 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
272 warn(_("failed to initialize libmount iterator"));
273 return MOUNT_EX_SYSERR
;
276 while (mnt_context_next_umount(cxt
, itr
, &fs
, &mntrc
, &ignored
) == 0) {
278 const char *tgt
= mnt_fs_get_target(fs
);
281 if (mnt_context_is_verbose(cxt
))
282 printf(_("%-25s: ignored\n"), tgt
);
284 int xrc
= mk_exit_code(cxt
, mntrc
);
286 if (xrc
== MOUNT_EX_SUCCESS
287 && mnt_context_is_verbose(cxt
))
288 printf("%-25s: successfully unmounted\n", tgt
);
297 static int umount_one(struct libmnt_context
*cxt
, const char *spec
)
302 return MOUNT_EX_SOFTWARE
;
304 if (mnt_context_set_target(cxt
, spec
))
305 err(MOUNT_EX_SYSERR
, _("failed to set umount target"));
307 rc
= mnt_context_umount(cxt
);
308 rc
= mk_exit_code(cxt
, rc
);
310 if (rc
== MOUNT_EX_SUCCESS
&& mnt_context_is_verbose(cxt
))
311 success_message(cxt
);
313 mnt_reset_context(cxt
);
317 static struct libmnt_table
*new_mountinfo(struct libmnt_context
*cxt
)
319 struct libmnt_table
*tb
= mnt_new_table();
321 err(MOUNT_EX_SYSERR
, _("libmount table allocation failed"));
323 mnt_table_set_parser_errcb(tb
, table_parser_errcb
);
324 mnt_table_set_cache(tb
, mnt_context_get_cache(cxt
));
326 if (mnt_table_parse_file(tb
, _PATH_PROC_MOUNTINFO
)) {
327 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
336 * like umount_one() but does not return error is @spec not mounted
338 static int umount_one_if_mounted(struct libmnt_context
*cxt
, const char *spec
)
341 struct libmnt_fs
*fs
;
343 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
345 rc
= MOUNT_EX_SUCCESS
; /* alredy unmounted */
346 mnt_reset_context(cxt
);
348 rc
= mk_exit_code(cxt
, rc
); /* error */
349 mnt_reset_context(cxt
);
351 rc
= umount_one(cxt
, mnt_fs_get_target(fs
));
356 static int umount_do_recurse(struct libmnt_context
*cxt
,
357 struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
359 struct libmnt_fs
*child
;
360 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
364 err(MOUNT_EX_SYSERR
, _("libmount iterator allocation failed"));
366 /* umount all childern */
368 rc
= mnt_table_next_child_fs(tb
, itr
, fs
, &child
);
370 warnx(_("failed to get child fs of %s"),
371 mnt_fs_get_target(fs
));
372 rc
= MOUNT_EX_SOFTWARE
;
375 break; /* no more children */
377 rc
= umount_do_recurse(cxt
, tb
, child
);
378 if (rc
!= MOUNT_EX_SUCCESS
)
382 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
388 static int umount_recursive(struct libmnt_context
*cxt
, const char *spec
)
390 struct libmnt_table
*tb
;
391 struct libmnt_fs
*fs
;
394 tb
= new_mountinfo(cxt
);
396 return MOUNT_EX_SOFTWARE
;
398 /* it's always real mountpoint, don't assume that the target maybe a device */
399 mnt_context_disable_swapmatch(cxt
, 1);
401 fs
= mnt_table_find_target(tb
, spec
, MNT_ITER_BACKWARD
);
403 rc
= umount_do_recurse(cxt
, tb
, fs
);
406 warnx(access(spec
, F_OK
) == 0 ?
407 _("%s: not mounted") :
408 _("%s: not found"), spec
);
415 static int umount_alltargets(struct libmnt_context
*cxt
, const char *spec
, int rec
)
417 struct libmnt_fs
*fs
;
418 struct libmnt_table
*tb
;
419 struct libmnt_iter
*itr
= NULL
;
423 /* Convert @spec to device name, Use the same logic like regular
426 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
429 warnx(access(spec
, F_OK
) == 0 ?
430 _("%s: not mounted") :
431 _("%s: not found"), spec
);
435 return mk_exit_code(cxt
, rc
); /* error */
437 if (!mnt_fs_get_srcpath(fs
) || !mnt_fs_get_devno(fs
))
438 errx(MOUNT_EX_USAGE
, _("%s: failed to determine source "
439 "(--all-targets is unsupported on systems with "
440 "regular mtab file)."), spec
);
442 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
444 err(MOUNT_EX_SYSERR
, _("libmount iterator allocation failed"));
446 /* get on @cxt independent mountinfo */
447 tb
= new_mountinfo(cxt
);
449 return MOUNT_EX_SOFTWARE
;
451 /* Note that @fs is from mount context and the context will be reseted
452 * after each umount() call */
453 devno
= mnt_fs_get_devno(fs
);
456 mnt_reset_context(cxt
);
458 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
459 if (mnt_fs_get_devno(fs
) != devno
)
461 mnt_context_disable_swapmatch(cxt
, 1);
463 rc
= umount_do_recurse(cxt
, tb
, fs
);
465 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
467 if (rc
!= MOUNT_EX_SUCCESS
)
478 * Check path -- non-root user should not be able to resolve path which is
479 * unreadable for him.
481 static char *sanitize_path(const char *path
)
488 p
= canonicalize_path_restricted(path
);
490 err(MOUNT_EX_USAGE
, "%s", path
);
495 int main(int argc
, char **argv
)
497 int c
, rc
= 0, all
= 0, recursive
= 0, alltargets
= 0;
498 struct libmnt_context
*cxt
;
502 UMOUNT_OPT_FAKE
= CHAR_MAX
+ 1,
505 static const struct option longopts
[] = {
506 { "all", 0, 0, 'a' },
507 { "all-targets", 0, 0, 'A' },
508 { "detach-loop", 0, 0, 'd' },
509 { "fake", 0, 0, UMOUNT_OPT_FAKE
},
510 { "force", 0, 0, 'f' },
511 { "help", 0, 0, 'h' },
512 { "internal-only", 0, 0, 'i' },
513 { "lazy", 0, 0, 'l' },
514 { "no-canonicalize", 0, 0, 'c' },
515 { "no-mtab", 0, 0, 'n' },
516 { "read-only", 0, 0, 'r' },
517 { "recursive", 0, 0, 'R' },
518 { "test-opts", 1, 0, 'O' },
519 { "types", 1, 0, 't' },
520 { "verbose", 0, 0, 'v' },
521 { "version", 0, 0, 'V' },
525 static const ul_excl_t excl
[] = { /* rows and cols in in ASCII order */
526 { 'A','a' }, /* all-targets,all */
527 { 'R','a' }, /* recursive,all */
528 { 'O','R','t'}, /* options,recursive,types */
529 { 'R','r' }, /* recursive,read-only */
532 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
535 setlocale(LC_ALL
, "");
536 bindtextdomain(PACKAGE
, LOCALEDIR
);
538 atexit(close_stdout
);
541 cxt
= mnt_new_context();
543 err(MOUNT_EX_SYSERR
, _("libmount context allocation failed"));
545 mnt_context_set_tables_errcb(cxt
, table_parser_errcb
);
547 while ((c
= getopt_long(argc
, argv
, "aAcdfhilnRrO:t:vV",
548 longopts
, NULL
)) != -1) {
551 /* only few options are allowed for non-root users */
552 if (mnt_context_is_restricted(cxt
) && !strchr("hdilVv", c
))
553 exit_non_root(option_to_longopt(c
, longopts
));
555 err_exclusive_options(c
, longopts
, excl
, excl_st
);
565 mnt_context_disable_canonicalize(cxt
, TRUE
);
568 mnt_context_enable_loopdel(cxt
, TRUE
);
570 case UMOUNT_OPT_FAKE
:
571 mnt_context_enable_fake(cxt
, TRUE
);
574 mnt_context_enable_force(cxt
, TRUE
);
580 mnt_context_disable_helpers(cxt
, TRUE
);
583 mnt_context_enable_lazy(cxt
, TRUE
);
586 mnt_context_disable_mtab(cxt
, TRUE
);
589 mnt_context_enable_rdonly_umount(cxt
, TRUE
);
595 if (mnt_context_set_options_pattern(cxt
, optarg
))
596 err(MOUNT_EX_SYSERR
, _("failed to set options pattern"));
602 mnt_context_enable_verbose(cxt
, TRUE
);
618 types
= "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";
620 mnt_context_set_fstype_pattern(cxt
, types
);
621 rc
= umount_all(cxt
);
623 } else if (argc
< 1) {
626 } else if (alltargets
) {
628 rc
+= umount_alltargets(cxt
, *argv
++, recursive
);
629 } else if (recursive
) {
631 rc
+= umount_recursive(cxt
, *argv
++);
636 if (mnt_context_is_restricted(cxt
)
637 && !mnt_tag_is_valid(path
))
638 path
= sanitize_path(path
);
640 rc
+= umount_one(cxt
, path
);
648 mnt_free_context(cxt
);
649 return (rc
< 256) ? rc
: 255;