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
46 static struct ul_env_list
*envs_removed
;
48 static int table_parser_errcb(struct libmnt_table
*tb
__attribute__((__unused__
)),
49 const char *filename
, int line
)
52 warnx(_("%s: parse error at line %d -- ignored"), filename
, line
);
57 static void __attribute__((__noreturn__
)) umount_print_version(void)
59 const char *ver
= NULL
;
60 const char **features
= NULL
, **p
;
62 mnt_get_library_version(&ver
);
63 mnt_get_library_features(&features
);
65 printf(_("%s from %s (libmount %s"),
66 program_invocation_short_name
,
71 fputs(p
== features
? ": " : ", ", stdout
);
77 static void __attribute__((__noreturn__
)) usage(void)
80 fputs(USAGE_HEADER
, out
);
83 " %1$s -a [options]\n"
84 " %1$s [options] <source> | <directory>\n"),
85 program_invocation_short_name
);
87 fputs(USAGE_SEPARATOR
, out
);
88 fputs(_("Unmount filesystems.\n"), out
);
90 fputs(USAGE_OPTIONS
, out
);
91 fputs(_(" -a, --all unmount all filesystems\n"), out
);
92 fputs(_(" -A, --all-targets unmount all mountpoints for the given device in the\n"
93 " current namespace\n"), out
);
94 fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out
);
95 fputs(_(" -d, --detach-loop if mounted loop device, also free this loop device\n"), out
);
96 fputs(_(" --fake dry run; skip the umount(2) syscall\n"), out
);
97 fputs(_(" -f, --force force unmount (in case of an unreachable NFS system)\n"), out
);
98 fputs(_(" -i, --internal-only don't call the umount.<type> helpers\n"), out
);
99 fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out
);
100 fputs(_(" -l, --lazy detach the filesystem now, clean up things later\n"), out
);
101 fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out
);
102 fputs(_(" -R, --recursive recursively unmount a target with all its children\n"), out
);
103 fputs(_(" -r, --read-only in case unmounting fails, try to remount read-only\n"), out
);
104 fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out
);
105 fputs(_(" -v, --verbose say what is being done\n"), out
);
106 fputs(_(" -q, --quiet suppress 'not mounted' error messages\n"), out
);
107 fputs(_(" -N, --namespace <ns> perform umount in another namespace\n"), out
);
109 fputs(USAGE_SEPARATOR
, out
);
110 fprintf(out
, USAGE_HELP_OPTIONS(25));
111 fprintf(out
, USAGE_MAN_TAIL("umount(8)"));
113 exit(MNT_EX_SUCCESS
);
116 static void suid_drop(struct libmnt_context
*cxt
)
118 const uid_t ruid
= getuid();
119 const uid_t euid
= geteuid();
121 if (ruid
!= 0 && euid
== 0 && drop_permissions() != 0)
122 err(MNT_EX_FAIL
, _("drop permissions failed"));
124 /* be paranoid and check it, setuid(0) has to fail */
125 if (ruid
!= 0 && setuid(0) == 0)
126 errx(MNT_EX_FAIL
, _("drop permissions failed."));
128 mnt_context_force_unrestricted(cxt
);
130 /* restore "bad" environment variables */
132 env_list_setenv(envs_removed
);
133 env_list_free(envs_removed
);
138 static void success_message(struct libmnt_context
*cxt
)
140 const char *tgt
, *src
;
142 if (mnt_context_helper_executed(cxt
)
143 || mnt_context_get_status(cxt
) != 1)
146 tgt
= mnt_context_get_target(cxt
);
150 src
= mnt_context_get_source(cxt
);
152 warnx(_("%s (%s) unmounted"), tgt
, src
);
154 warnx(_("%s unmounted"), tgt
);
157 static int mk_exit_code(struct libmnt_context
*cxt
, int api_rc
)
159 char buf
[BUFSIZ
] = { 0 };
162 rc
= mnt_context_get_excode(cxt
, api_rc
, buf
, sizeof(buf
));
164 /* suppress "not mounted" error message */
168 if (api_rc
== -EPERM
) /* non-root user */
172 if (mnt_context_syscall_called(cxt
) &&
173 mnt_context_get_syscall_errno(cxt
) == EINVAL
)
179 /* print errors/warnings */
181 const char *spec
= mnt_context_get_target(cxt
);
183 spec
= mnt_context_get_source(cxt
);
186 warnx("%s: %s.", spec
, buf
);
191 static int umount_all(struct libmnt_context
*cxt
)
193 struct libmnt_iter
*itr
;
194 struct libmnt_fs
*fs
;
195 int mntrc
, ignored
, rc
= 0;
197 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
199 warn(_("failed to initialize libmount iterator"));
200 return MNT_EX_SYSERR
;
203 while (mnt_context_next_umount(cxt
, itr
, &fs
, &mntrc
, &ignored
) == 0) {
205 const char *tgt
= mnt_fs_get_target(fs
);
208 if (mnt_context_is_verbose(cxt
))
209 printf(_("%-25s: ignored\n"), tgt
);
211 int xrc
= mk_exit_code(cxt
, mntrc
);
213 if (xrc
== MNT_EX_SUCCESS
214 && mnt_context_is_verbose(cxt
))
215 printf("%-25s: successfully unmounted\n", tgt
);
224 static int umount_one(struct libmnt_context
*cxt
, const char *spec
)
229 return MNT_EX_SOFTWARE
;
231 if (mnt_context_set_target(cxt
, spec
))
232 err(MNT_EX_SYSERR
, _("failed to set umount target"));
234 rc
= mnt_context_umount(cxt
);
237 && mnt_context_is_restricted(cxt
)
238 && mnt_context_tab_applied(cxt
)
239 && !mnt_context_syscall_called(cxt
)) {
240 /* Mountpoint exists, but failed something else in libmount,
241 * drop perms and try it again */
243 rc
= mnt_context_umount(cxt
);
246 rc
= mk_exit_code(cxt
, rc
);
248 if (rc
== MNT_EX_SUCCESS
&& mnt_context_is_verbose(cxt
))
249 success_message(cxt
);
251 mnt_reset_context(cxt
);
255 static struct libmnt_table
*new_mountinfo(struct libmnt_context
*cxt
)
257 struct libmnt_table
*tb
;
258 struct libmnt_ns
*ns_old
= mnt_context_switch_target_ns(cxt
);
261 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
263 tb
= mnt_new_table();
265 err(MNT_EX_SYSERR
, _("libmount table allocation failed"));
267 mnt_table_set_parser_errcb(tb
, table_parser_errcb
);
268 mnt_table_set_cache(tb
, mnt_context_get_cache(cxt
));
270 if (mnt_table_parse_file(tb
, _PATH_PROC_MOUNTINFO
)) {
271 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
276 if (!mnt_context_switch_ns(cxt
, ns_old
))
277 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
283 * like umount_one() but does not return error is @spec not mounted
285 static int umount_one_if_mounted(struct libmnt_context
*cxt
, const char *spec
)
288 struct libmnt_fs
*fs
;
290 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
292 rc
= MNT_EX_SUCCESS
; /* already unmounted */
293 mnt_reset_context(cxt
);
295 rc
= mk_exit_code(cxt
, rc
); /* error */
296 mnt_reset_context(cxt
);
298 rc
= umount_one(cxt
, mnt_fs_get_target(fs
));
303 static int umount_do_recurse(struct libmnt_context
*cxt
,
304 struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
306 struct libmnt_fs
*child
, *over
= NULL
;
307 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
311 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
313 /* first try overmount */
314 if (mnt_table_over_fs(tb
, fs
, &over
) == 0 && over
) {
315 rc
= umount_do_recurse(cxt
, tb
, over
);
316 if (rc
!= MNT_EX_SUCCESS
)
320 /* umount all children */
322 rc
= mnt_table_next_child_fs(tb
, itr
, fs
, &child
);
324 warnx(_("failed to get child fs of %s"),
325 mnt_fs_get_target(fs
));
326 rc
= MNT_EX_SOFTWARE
;
329 break; /* no more children */
331 if (over
&& child
== over
)
334 rc
= umount_do_recurse(cxt
, tb
, child
);
335 if (rc
!= MNT_EX_SUCCESS
)
339 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
345 static int umount_recursive(struct libmnt_context
*cxt
, const char *spec
)
347 struct libmnt_table
*tb
;
348 struct libmnt_fs
*fs
;
351 tb
= new_mountinfo(cxt
);
353 return MNT_EX_SOFTWARE
;
355 /* it's always real mountpoint, don't assume that the target maybe a device */
356 mnt_context_disable_swapmatch(cxt
, 1);
358 fs
= mnt_table_find_target(tb
, spec
, MNT_ITER_BACKWARD
);
360 rc
= umount_do_recurse(cxt
, tb
, fs
);
364 warnx(access(spec
, F_OK
) == 0 ?
365 _("%s: not mounted") :
366 _("%s: not found"), spec
);
373 static int umount_alltargets(struct libmnt_context
*cxt
, const char *spec
, int rec
)
375 struct libmnt_fs
*fs
;
376 struct libmnt_table
*tb
;
377 struct libmnt_iter
*itr
= NULL
;
381 /* Convert @spec to device name, Use the same logic like regular
384 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
388 warnx(access(spec
, F_OK
) == 0 ?
389 _("%s: not mounted") :
390 _("%s: not found"), spec
);
394 return mk_exit_code(cxt
, rc
); /* error */
396 if (!mnt_fs_get_srcpath(fs
) || !mnt_fs_get_devno(fs
))
397 errx(MNT_EX_USAGE
, _("%s: failed to determine source "
398 "(--all-targets is unsupported on systems with "
399 "regular mtab file)."), spec
);
401 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
403 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
405 /* get on @cxt independent mountinfo */
406 tb
= new_mountinfo(cxt
);
408 rc
= MNT_EX_SOFTWARE
;
412 /* Note that @fs is from mount context and the context will be reset
413 * after each umount() call */
414 devno
= mnt_fs_get_devno(fs
);
417 mnt_reset_context(cxt
);
419 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
420 if (mnt_fs_get_devno(fs
) != devno
)
422 mnt_context_disable_swapmatch(cxt
, 1);
424 rc
= umount_do_recurse(cxt
, tb
, fs
);
426 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
428 if (rc
!= MNT_EX_SUCCESS
)
440 * Check path -- non-root user should not be able to resolve path which is
441 * unreadable for them.
443 static char *sanitize_path(const char *path
)
450 p
= canonicalize_path_restricted(path
);
452 err(MNT_EX_USAGE
, "%s", path
);
457 static pid_t
parse_pid(const char *str
)
463 ret
= strtoul(str
, &end
, 10);
465 if (ret
< 0 || errno
|| end
== str
|| (end
&& *end
))
470 int main(int argc
, char **argv
)
472 int c
, rc
= 0, all
= 0, recursive
= 0, alltargets
= 0;
473 struct libmnt_context
*cxt
;
477 UMOUNT_OPT_FAKE
= CHAR_MAX
+ 1,
480 static const struct option longopts
[] = {
481 { "all", no_argument
, NULL
, 'a' },
482 { "all-targets", no_argument
, NULL
, 'A' },
483 { "detach-loop", no_argument
, NULL
, 'd' },
484 { "fake", no_argument
, NULL
, UMOUNT_OPT_FAKE
},
485 { "force", no_argument
, NULL
, 'f' },
486 { "help", no_argument
, NULL
, 'h' },
487 { "internal-only", no_argument
, NULL
, 'i' },
488 { "lazy", no_argument
, NULL
, 'l' },
489 { "no-canonicalize", no_argument
, NULL
, 'c' },
490 { "no-mtab", no_argument
, NULL
, 'n' },
491 { "quiet", no_argument
, NULL
, 'q' },
492 { "read-only", no_argument
, NULL
, 'r' },
493 { "recursive", no_argument
, NULL
, 'R' },
494 { "test-opts", required_argument
, NULL
, 'O' },
495 { "types", required_argument
, NULL
, 't' },
496 { "verbose", no_argument
, NULL
, 'v' },
497 { "version", no_argument
, NULL
, 'V' },
498 { "namespace", required_argument
, NULL
, 'N' },
502 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
503 { 'A','a' }, /* all-targets,all */
504 { 'R','a' }, /* recursive,all */
505 { 'O','R','t'}, /* options,recursive,types */
506 { 'R','r' }, /* recursive,read-only */
509 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
511 __sanitize_env(&envs_removed
);
512 setlocale(LC_ALL
, "");
513 bindtextdomain(PACKAGE
, LOCALEDIR
);
515 close_stdout_atexit();
518 cxt
= mnt_new_context();
520 err(MNT_EX_SYSERR
, _("libmount context allocation failed"));
522 mnt_context_set_tables_errcb(cxt
, table_parser_errcb
);
524 while ((c
= getopt_long(argc
, argv
, "aAcdfhilnqRrO:t:vVN:",
525 longopts
, NULL
)) != -1) {
528 /* only few options are allowed for non-root users */
529 if (mnt_context_is_restricted(cxt
) && !strchr("hdilqVv", c
)) {
531 /* Silently ignore options without direct impact to the
532 * umount operation, but with security sensitive
535 continue; /* ignore */
537 /* drop permissions, continue as regular user */
541 err_exclusive_options(c
, longopts
, excl
, excl_st
);
551 mnt_context_disable_canonicalize(cxt
, TRUE
);
554 mnt_context_enable_loopdel(cxt
, TRUE
);
556 case UMOUNT_OPT_FAKE
:
557 mnt_context_enable_fake(cxt
, TRUE
);
560 mnt_context_enable_force(cxt
, TRUE
);
563 mnt_context_disable_helpers(cxt
, TRUE
);
566 mnt_context_enable_lazy(cxt
, TRUE
);
569 mnt_context_disable_mtab(cxt
, TRUE
);
575 mnt_context_enable_rdonly_umount(cxt
, TRUE
);
581 if (mnt_context_set_options_pattern(cxt
, optarg
))
582 err(MNT_EX_SYSERR
, _("failed to set options pattern"));
588 mnt_context_enable_verbose(cxt
, TRUE
);
593 pid_t pid
= parse_pid(optarg
);
596 snprintf(path
, sizeof(path
), "/proc/%i/ns/mnt", pid
);
598 if (mnt_context_set_target_ns(cxt
, pid
? path
: optarg
))
599 err(MNT_EX_SYSERR
, _("failed to set target namespace to %s"), pid
? path
: optarg
);
604 mnt_free_context(cxt
);
607 mnt_free_context(cxt
);
608 umount_print_version();
610 errtryhelp(MNT_EX_USAGE
);
619 warnx(_("unexpected number of arguments"));
620 errtryhelp(MNT_EX_USAGE
);
623 types
= "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd,noselinuxfs";
625 mnt_context_set_fstype_pattern(cxt
, types
);
626 rc
= umount_all(cxt
);
628 } else if (argc
< 1) {
629 warnx(_("bad usage"));
630 errtryhelp(MNT_EX_USAGE
);
632 } else if (alltargets
) {
634 rc
+= umount_alltargets(cxt
, *argv
++, recursive
);
635 } else if (recursive
) {
637 rc
+= umount_recursive(cxt
, *argv
++);
642 if (mnt_context_is_restricted(cxt
)
643 && !mnt_tag_is_valid(path
))
644 path
= sanitize_path(path
);
646 rc
+= umount_one(cxt
, path
);
654 mnt_free_context(cxt
);
655 env_list_free(envs_removed
);
657 return (rc
< 256) ? rc
: 255;