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>
35 #include "closestream.h"
36 #include "pathnames.h"
37 #include "canonicalize.h"
39 #define XALLOC_EXIT_CODE MNT_EX_SYSERR
42 #define OPTUTILS_EXIT_CODE MNT_EX_USAGE
47 static int table_parser_errcb(struct libmnt_table
*tb
__attribute__((__unused__
)),
48 const char *filename
, int line
)
51 warnx(_("%s: parse error at line %d -- ignored"), filename
, line
);
56 static void __attribute__((__noreturn__
)) print_version(void)
58 const char *ver
= NULL
;
59 const char **features
= NULL
, **p
;
61 mnt_get_library_version(&ver
);
62 mnt_get_library_features(&features
);
64 printf(_("%s from %s (libmount %s"),
65 program_invocation_short_name
,
70 fputs(p
== features
? ": " : ", ", stdout
);
76 static void __attribute__((__noreturn__
)) usage(void)
79 fputs(USAGE_HEADER
, out
);
82 " %1$s -a [options]\n"
83 " %1$s [options] <source> | <directory>\n"),
84 program_invocation_short_name
);
86 fputs(USAGE_SEPARATOR
, out
);
87 fputs(_("Unmount filesystems.\n"), out
);
89 fputs(USAGE_OPTIONS
, out
);
90 fputs(_(" -a, --all unmount all filesystems\n"), out
);
91 fputs(_(" -A, --all-targets unmount all mountpoints for the given device in the\n"
92 " current namespace\n"), out
);
93 fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out
);
94 fputs(_(" -d, --detach-loop if mounted loop device, also free this loop device\n"), out
);
95 fputs(_(" --fake dry run; skip the umount(2) syscall\n"), out
);
96 fputs(_(" -f, --force force unmount (in case of an unreachable NFS system)\n"), out
);
97 fputs(_(" -i, --internal-only don't call the umount.<type> helpers\n"), out
);
98 fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out
);
99 fputs(_(" -l, --lazy detach the filesystem now, clean up things later\n"), out
);
100 fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out
);
101 fputs(_(" -R, --recursive recursively unmount a target with all its children\n"), out
);
102 fputs(_(" -r, --read-only in case unmounting fails, try to remount read-only\n"), out
);
103 fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out
);
104 fputs(_(" -v, --verbose say what is being done\n"), out
);
105 fputs(_(" -q, --quiet suppress 'not mounted' error messages\n"), out
);
106 fputs(_(" -N, --namespace <ns> perform umount in another namespace\n"), out
);
108 fputs(USAGE_SEPARATOR
, out
);
109 printf(USAGE_HELP_OPTIONS(25));
110 printf(USAGE_MAN_TAIL("umount(8)"));
112 exit(MNT_EX_SUCCESS
);
115 static void __attribute__((__noreturn__
)) exit_non_root(const char *option
)
117 const uid_t ruid
= getuid();
118 const uid_t euid
= geteuid();
120 if (ruid
== 0 && euid
!= 0) {
121 /* user is root, but setuid to non-root */
124 _("only root can use \"--%s\" option "
125 "(effective UID is %u)"),
127 errx(MNT_EX_USAGE
, _("only root can do that "
128 "(effective UID is %u)"), euid
);
131 errx(MNT_EX_USAGE
, _("only root can use \"--%s\" option"), option
);
132 errx(MNT_EX_USAGE
, _("only root can do that"));
135 static void success_message(struct libmnt_context
*cxt
)
137 const char *tgt
, *src
;
139 if (mnt_context_helper_executed(cxt
)
140 || mnt_context_get_status(cxt
) != 1)
143 tgt
= mnt_context_get_target(cxt
);
147 src
= mnt_context_get_source(cxt
);
149 warnx(_("%s (%s) unmounted"), tgt
, src
);
151 warnx(_("%s unmounted"), tgt
);
154 static int mk_exit_code(struct libmnt_context
*cxt
, int rc
)
156 char buf
[BUFSIZ
] = { 0 };
158 rc
= mnt_context_get_excode(cxt
, rc
, buf
, sizeof(buf
));
160 /* suppress "not mounted" error message */
163 mnt_context_syscall_called(cxt
) &&
164 mnt_context_get_syscall_errno(cxt
) == EINVAL
)
167 /* print errors/warnings */
169 const char *spec
= mnt_context_get_target(cxt
);
171 spec
= mnt_context_get_source(cxt
);
174 warnx("%s: %s.", spec
, buf
);
179 static int umount_all(struct libmnt_context
*cxt
)
181 struct libmnt_iter
*itr
;
182 struct libmnt_fs
*fs
;
183 int mntrc
, ignored
, rc
= 0;
185 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
187 warn(_("failed to initialize libmount iterator"));
188 return MNT_EX_SYSERR
;
191 while (mnt_context_next_umount(cxt
, itr
, &fs
, &mntrc
, &ignored
) == 0) {
193 const char *tgt
= mnt_fs_get_target(fs
);
196 if (mnt_context_is_verbose(cxt
))
197 printf(_("%-25s: ignored\n"), tgt
);
199 int xrc
= mk_exit_code(cxt
, mntrc
);
201 if (xrc
== MNT_EX_SUCCESS
202 && mnt_context_is_verbose(cxt
))
203 printf("%-25s: successfully unmounted\n", tgt
);
212 static int umount_one(struct libmnt_context
*cxt
, const char *spec
)
217 return MNT_EX_SOFTWARE
;
219 if (mnt_context_set_target(cxt
, spec
))
220 err(MNT_EX_SYSERR
, _("failed to set umount target"));
222 rc
= mnt_context_umount(cxt
);
223 rc
= mk_exit_code(cxt
, rc
);
225 if (rc
== MNT_EX_SUCCESS
&& mnt_context_is_verbose(cxt
))
226 success_message(cxt
);
228 mnt_reset_context(cxt
);
232 static struct libmnt_table
*new_mountinfo(struct libmnt_context
*cxt
)
234 struct libmnt_table
*tb
;
235 struct libmnt_ns
*ns_old
= mnt_context_switch_target_ns(cxt
);
238 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
240 tb
= mnt_new_table();
242 err(MNT_EX_SYSERR
, _("libmount table allocation failed"));
244 mnt_table_set_parser_errcb(tb
, table_parser_errcb
);
245 mnt_table_set_cache(tb
, mnt_context_get_cache(cxt
));
247 if (mnt_table_parse_file(tb
, _PATH_PROC_MOUNTINFO
)) {
248 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
253 if (!mnt_context_switch_ns(cxt
, ns_old
))
254 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
260 * like umount_one() but does not return error is @spec not mounted
262 static int umount_one_if_mounted(struct libmnt_context
*cxt
, const char *spec
)
265 struct libmnt_fs
*fs
;
267 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
269 rc
= MNT_EX_SUCCESS
; /* already unmounted */
270 mnt_reset_context(cxt
);
272 rc
= mk_exit_code(cxt
, rc
); /* error */
273 mnt_reset_context(cxt
);
275 rc
= umount_one(cxt
, mnt_fs_get_target(fs
));
280 static int umount_do_recurse(struct libmnt_context
*cxt
,
281 struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
283 struct libmnt_fs
*child
;
284 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
288 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
290 /* umount all children */
292 rc
= mnt_table_next_child_fs(tb
, itr
, fs
, &child
);
294 warnx(_("failed to get child fs of %s"),
295 mnt_fs_get_target(fs
));
296 rc
= MNT_EX_SOFTWARE
;
299 break; /* no more children */
301 rc
= umount_do_recurse(cxt
, tb
, child
);
302 if (rc
!= MNT_EX_SUCCESS
)
306 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
312 static int umount_recursive(struct libmnt_context
*cxt
, const char *spec
)
314 struct libmnt_table
*tb
;
315 struct libmnt_fs
*fs
;
318 tb
= new_mountinfo(cxt
);
320 return MNT_EX_SOFTWARE
;
322 /* it's always real mountpoint, don't assume that the target maybe a device */
323 mnt_context_disable_swapmatch(cxt
, 1);
325 fs
= mnt_table_find_target(tb
, spec
, MNT_ITER_BACKWARD
);
327 rc
= umount_do_recurse(cxt
, tb
, fs
);
331 warnx(access(spec
, F_OK
) == 0 ?
332 _("%s: not mounted") :
333 _("%s: not found"), spec
);
340 static int umount_alltargets(struct libmnt_context
*cxt
, const char *spec
, int rec
)
342 struct libmnt_fs
*fs
;
343 struct libmnt_table
*tb
;
344 struct libmnt_iter
*itr
= NULL
;
348 /* Convert @spec to device name, Use the same logic like regular
351 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
355 warnx(access(spec
, F_OK
) == 0 ?
356 _("%s: not mounted") :
357 _("%s: not found"), spec
);
361 return mk_exit_code(cxt
, rc
); /* error */
363 if (!mnt_fs_get_srcpath(fs
) || !mnt_fs_get_devno(fs
))
364 errx(MNT_EX_USAGE
, _("%s: failed to determine source "
365 "(--all-targets is unsupported on systems with "
366 "regular mtab file)."), spec
);
368 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
370 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
372 /* get on @cxt independent mountinfo */
373 tb
= new_mountinfo(cxt
);
375 rc
= MNT_EX_SOFTWARE
;
379 /* Note that @fs is from mount context and the context will be reset
380 * after each umount() call */
381 devno
= mnt_fs_get_devno(fs
);
384 mnt_reset_context(cxt
);
386 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
387 if (mnt_fs_get_devno(fs
) != devno
)
389 mnt_context_disable_swapmatch(cxt
, 1);
391 rc
= umount_do_recurse(cxt
, tb
, fs
);
393 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
395 if (rc
!= MNT_EX_SUCCESS
)
407 * Check path -- non-root user should not be able to resolve path which is
408 * unreadable for him.
410 static char *sanitize_path(const char *path
)
417 p
= canonicalize_path_restricted(path
);
419 err(MNT_EX_USAGE
, "%s", path
);
424 static pid_t
parse_pid(const char *str
)
430 ret
= strtoul(str
, &end
, 10);
432 if (ret
< 0 || errno
|| end
== str
|| (end
&& *end
))
437 int main(int argc
, char **argv
)
439 int c
, rc
= 0, all
= 0, recursive
= 0, alltargets
= 0;
440 struct libmnt_context
*cxt
;
444 UMOUNT_OPT_FAKE
= CHAR_MAX
+ 1,
447 static const struct option longopts
[] = {
448 { "all", no_argument
, NULL
, 'a' },
449 { "all-targets", no_argument
, NULL
, 'A' },
450 { "detach-loop", no_argument
, NULL
, 'd' },
451 { "fake", no_argument
, NULL
, UMOUNT_OPT_FAKE
},
452 { "force", no_argument
, NULL
, 'f' },
453 { "help", no_argument
, NULL
, 'h' },
454 { "internal-only", no_argument
, NULL
, 'i' },
455 { "lazy", no_argument
, NULL
, 'l' },
456 { "no-canonicalize", no_argument
, NULL
, 'c' },
457 { "no-mtab", no_argument
, NULL
, 'n' },
458 { "quiet", no_argument
, NULL
, 'q' },
459 { "read-only", no_argument
, NULL
, 'r' },
460 { "recursive", no_argument
, NULL
, 'R' },
461 { "test-opts", required_argument
, NULL
, 'O' },
462 { "types", required_argument
, NULL
, 't' },
463 { "verbose", no_argument
, NULL
, 'v' },
464 { "version", no_argument
, NULL
, 'V' },
465 { "namespace", required_argument
, NULL
, 'N' },
469 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
470 { 'A','a' }, /* all-targets,all */
471 { 'R','a' }, /* recursive,all */
472 { 'O','R','t'}, /* options,recursive,types */
473 { 'R','r' }, /* recursive,read-only */
476 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
479 setlocale(LC_ALL
, "");
480 bindtextdomain(PACKAGE
, LOCALEDIR
);
482 atexit(close_stdout
);
485 cxt
= mnt_new_context();
487 err(MNT_EX_SYSERR
, _("libmount context allocation failed"));
489 mnt_context_set_tables_errcb(cxt
, table_parser_errcb
);
491 while ((c
= getopt_long(argc
, argv
, "aAcdfhilnqRrO:t:vVN:",
492 longopts
, NULL
)) != -1) {
495 /* only few options are allowed for non-root users */
496 if (mnt_context_is_restricted(cxt
) && !strchr("hdilqVv", c
))
497 exit_non_root(option_to_longopt(c
, longopts
));
499 err_exclusive_options(c
, longopts
, excl
, excl_st
);
509 mnt_context_disable_canonicalize(cxt
, TRUE
);
512 mnt_context_enable_loopdel(cxt
, TRUE
);
514 case UMOUNT_OPT_FAKE
:
515 mnt_context_enable_fake(cxt
, TRUE
);
518 mnt_context_enable_force(cxt
, TRUE
);
524 mnt_context_disable_helpers(cxt
, TRUE
);
527 mnt_context_enable_lazy(cxt
, TRUE
);
530 mnt_context_disable_mtab(cxt
, TRUE
);
536 mnt_context_enable_rdonly_umount(cxt
, TRUE
);
542 if (mnt_context_set_options_pattern(cxt
, optarg
))
543 err(MNT_EX_SYSERR
, _("failed to set options pattern"));
549 mnt_context_enable_verbose(cxt
, TRUE
);
557 pid_t pid
= parse_pid(optarg
);
560 snprintf(path
, sizeof(path
), "/proc/%i/ns/mnt", pid
);
562 if (mnt_context_set_target_ns(cxt
, pid
? path
: optarg
))
563 err(MNT_EX_SYSERR
, _("failed to set target namespace to %s"), pid
? path
: optarg
);
567 errtryhelp(MNT_EX_USAGE
);
576 types
= "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd,noselinuxfs";
578 mnt_context_set_fstype_pattern(cxt
, types
);
579 rc
= umount_all(cxt
);
581 } else if (argc
< 1) {
582 warnx(_("bad usage"));
583 errtryhelp(MNT_EX_USAGE
);
585 } else if (alltargets
) {
587 rc
+= umount_alltargets(cxt
, *argv
++, recursive
);
588 } else if (recursive
) {
590 rc
+= umount_recursive(cxt
, *argv
++);
595 if (mnt_context_is_restricted(cxt
)
596 && !mnt_tag_is_valid(path
))
597 path
= sanitize_path(path
);
599 rc
+= umount_one(cxt
, path
);
607 mnt_free_context(cxt
);
608 return (rc
< 256) ? rc
: 255;