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__
)) umount_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 suid_drop(struct libmnt_context
*cxt
)
117 const uid_t ruid
= getuid();
118 const uid_t euid
= geteuid();
120 if (ruid
!= 0 && euid
== 0) {
121 if (setgid(getgid()) < 0)
122 err(MNT_EX_FAIL
, _("setgid() failed"));
124 if (setuid(getuid()) < 0)
125 err(MNT_EX_FAIL
, _("setuid() failed"));
128 /* be paranoid and check it, setuid(0) has to fail */
129 if (ruid
!= 0 && setuid(0) == 0)
130 errx(MNT_EX_FAIL
, _("drop permissions failed."));
132 mnt_context_force_unrestricted(cxt
);
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
);
225 && mnt_context_is_restricted(cxt
)
226 && !mnt_context_syscall_called(cxt
)) {
227 /* Failed somewhere in libmount, drop perms and try it again */
229 rc
= mnt_context_umount(cxt
);
232 rc
= mk_exit_code(cxt
, rc
);
234 if (rc
== MNT_EX_SUCCESS
&& mnt_context_is_verbose(cxt
))
235 success_message(cxt
);
237 mnt_reset_context(cxt
);
241 static struct libmnt_table
*new_mountinfo(struct libmnt_context
*cxt
)
243 struct libmnt_table
*tb
;
244 struct libmnt_ns
*ns_old
= mnt_context_switch_target_ns(cxt
);
247 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
249 tb
= mnt_new_table();
251 err(MNT_EX_SYSERR
, _("libmount table allocation failed"));
253 mnt_table_set_parser_errcb(tb
, table_parser_errcb
);
254 mnt_table_set_cache(tb
, mnt_context_get_cache(cxt
));
256 if (mnt_table_parse_file(tb
, _PATH_PROC_MOUNTINFO
)) {
257 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
262 if (!mnt_context_switch_ns(cxt
, ns_old
))
263 err(MNT_EX_SYSERR
, _("failed to switch namespace"));
269 * like umount_one() but does not return error is @spec not mounted
271 static int umount_one_if_mounted(struct libmnt_context
*cxt
, const char *spec
)
274 struct libmnt_fs
*fs
;
276 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
278 rc
= MNT_EX_SUCCESS
; /* already unmounted */
279 mnt_reset_context(cxt
);
281 rc
= mk_exit_code(cxt
, rc
); /* error */
282 mnt_reset_context(cxt
);
284 rc
= umount_one(cxt
, mnt_fs_get_target(fs
));
289 static int umount_do_recurse(struct libmnt_context
*cxt
,
290 struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
292 struct libmnt_fs
*child
;
293 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
297 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
299 /* umount all children */
301 rc
= mnt_table_next_child_fs(tb
, itr
, fs
, &child
);
303 warnx(_("failed to get child fs of %s"),
304 mnt_fs_get_target(fs
));
305 rc
= MNT_EX_SOFTWARE
;
308 break; /* no more children */
310 rc
= umount_do_recurse(cxt
, tb
, child
);
311 if (rc
!= MNT_EX_SUCCESS
)
315 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
321 static int umount_recursive(struct libmnt_context
*cxt
, const char *spec
)
323 struct libmnt_table
*tb
;
324 struct libmnt_fs
*fs
;
327 tb
= new_mountinfo(cxt
);
329 return MNT_EX_SOFTWARE
;
331 /* it's always real mountpoint, don't assume that the target maybe a device */
332 mnt_context_disable_swapmatch(cxt
, 1);
334 fs
= mnt_table_find_target(tb
, spec
, MNT_ITER_BACKWARD
);
336 rc
= umount_do_recurse(cxt
, tb
, fs
);
340 warnx(access(spec
, F_OK
) == 0 ?
341 _("%s: not mounted") :
342 _("%s: not found"), spec
);
349 static int umount_alltargets(struct libmnt_context
*cxt
, const char *spec
, int rec
)
351 struct libmnt_fs
*fs
;
352 struct libmnt_table
*tb
;
353 struct libmnt_iter
*itr
= NULL
;
357 /* Convert @spec to device name, Use the same logic like regular
360 rc
= mnt_context_find_umount_fs(cxt
, spec
, &fs
);
364 warnx(access(spec
, F_OK
) == 0 ?
365 _("%s: not mounted") :
366 _("%s: not found"), spec
);
370 return mk_exit_code(cxt
, rc
); /* error */
372 if (!mnt_fs_get_srcpath(fs
) || !mnt_fs_get_devno(fs
))
373 errx(MNT_EX_USAGE
, _("%s: failed to determine source "
374 "(--all-targets is unsupported on systems with "
375 "regular mtab file)."), spec
);
377 itr
= mnt_new_iter(MNT_ITER_BACKWARD
);
379 err(MNT_EX_SYSERR
, _("libmount iterator allocation failed"));
381 /* get on @cxt independent mountinfo */
382 tb
= new_mountinfo(cxt
);
384 rc
= MNT_EX_SOFTWARE
;
388 /* Note that @fs is from mount context and the context will be reset
389 * after each umount() call */
390 devno
= mnt_fs_get_devno(fs
);
393 mnt_reset_context(cxt
);
395 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
396 if (mnt_fs_get_devno(fs
) != devno
)
398 mnt_context_disable_swapmatch(cxt
, 1);
400 rc
= umount_do_recurse(cxt
, tb
, fs
);
402 rc
= umount_one_if_mounted(cxt
, mnt_fs_get_target(fs
));
404 if (rc
!= MNT_EX_SUCCESS
)
416 * Check path -- non-root user should not be able to resolve path which is
417 * unreadable for him.
419 static char *sanitize_path(const char *path
)
426 p
= canonicalize_path_restricted(path
);
428 err(MNT_EX_USAGE
, "%s", path
);
433 static pid_t
parse_pid(const char *str
)
439 ret
= strtoul(str
, &end
, 10);
441 if (ret
< 0 || errno
|| end
== str
|| (end
&& *end
))
446 int main(int argc
, char **argv
)
448 int c
, rc
= 0, all
= 0, recursive
= 0, alltargets
= 0;
449 struct libmnt_context
*cxt
;
453 UMOUNT_OPT_FAKE
= CHAR_MAX
+ 1,
456 static const struct option longopts
[] = {
457 { "all", no_argument
, NULL
, 'a' },
458 { "all-targets", no_argument
, NULL
, 'A' },
459 { "detach-loop", no_argument
, NULL
, 'd' },
460 { "fake", no_argument
, NULL
, UMOUNT_OPT_FAKE
},
461 { "force", no_argument
, NULL
, 'f' },
462 { "help", no_argument
, NULL
, 'h' },
463 { "internal-only", no_argument
, NULL
, 'i' },
464 { "lazy", no_argument
, NULL
, 'l' },
465 { "no-canonicalize", no_argument
, NULL
, 'c' },
466 { "no-mtab", no_argument
, NULL
, 'n' },
467 { "quiet", no_argument
, NULL
, 'q' },
468 { "read-only", no_argument
, NULL
, 'r' },
469 { "recursive", no_argument
, NULL
, 'R' },
470 { "test-opts", required_argument
, NULL
, 'O' },
471 { "types", required_argument
, NULL
, 't' },
472 { "verbose", no_argument
, NULL
, 'v' },
473 { "version", no_argument
, NULL
, 'V' },
474 { "namespace", required_argument
, NULL
, 'N' },
478 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
479 { 'A','a' }, /* all-targets,all */
480 { 'R','a' }, /* recursive,all */
481 { 'O','R','t'}, /* options,recursive,types */
482 { 'R','r' }, /* recursive,read-only */
485 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
488 setlocale(LC_ALL
, "");
489 bindtextdomain(PACKAGE
, LOCALEDIR
);
491 close_stdout_atexit();
494 cxt
= mnt_new_context();
496 err(MNT_EX_SYSERR
, _("libmount context allocation failed"));
498 mnt_context_set_tables_errcb(cxt
, table_parser_errcb
);
500 while ((c
= getopt_long(argc
, argv
, "aAcdfhilnqRrO:t:vVN:",
501 longopts
, NULL
)) != -1) {
504 /* only few options are allowed for non-root users */
505 if (mnt_context_is_restricted(cxt
) && !strchr("hdilqVv", c
))
508 err_exclusive_options(c
, longopts
, excl
, excl_st
);
518 mnt_context_disable_canonicalize(cxt
, TRUE
);
521 mnt_context_enable_loopdel(cxt
, TRUE
);
523 case UMOUNT_OPT_FAKE
:
524 mnt_context_enable_fake(cxt
, TRUE
);
527 mnt_context_enable_force(cxt
, TRUE
);
530 mnt_context_disable_helpers(cxt
, TRUE
);
533 mnt_context_enable_lazy(cxt
, TRUE
);
536 mnt_context_disable_mtab(cxt
, TRUE
);
542 mnt_context_enable_rdonly_umount(cxt
, TRUE
);
548 if (mnt_context_set_options_pattern(cxt
, optarg
))
549 err(MNT_EX_SYSERR
, _("failed to set options pattern"));
555 mnt_context_enable_verbose(cxt
, TRUE
);
560 pid_t pid
= parse_pid(optarg
);
563 snprintf(path
, sizeof(path
), "/proc/%i/ns/mnt", pid
);
565 if (mnt_context_set_target_ns(cxt
, pid
? path
: optarg
))
566 err(MNT_EX_SYSERR
, _("failed to set target namespace to %s"), pid
? path
: optarg
);
571 mnt_free_context(cxt
);
574 mnt_free_context(cxt
);
575 umount_print_version();
577 errtryhelp(MNT_EX_USAGE
);
586 warnx(_("unexpected number of arguments"));
587 errtryhelp(MNT_EX_USAGE
);
590 types
= "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd,noselinuxfs";
592 mnt_context_set_fstype_pattern(cxt
, types
);
593 rc
= umount_all(cxt
);
595 } else if (argc
< 1) {
596 warnx(_("bad usage"));
597 errtryhelp(MNT_EX_USAGE
);
599 } else if (alltargets
) {
601 rc
+= umount_alltargets(cxt
, *argv
++, recursive
);
602 } else if (recursive
) {
604 rc
+= umount_recursive(cxt
, *argv
++);
609 if (mnt_context_is_restricted(cxt
)
610 && !mnt_tag_is_valid(path
))
611 path
= sanitize_path(path
);
613 rc
+= umount_one(cxt
, path
);
621 mnt_free_context(cxt
);
622 return (rc
< 256) ? rc
: 255;