]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/umount.c
Merge branch 'travis-functions' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / sys-utils / umount.c
1 /*
2 * umount(8) -- mount a filesystem
3 *
4 * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
5 * Written by Karel Zak <kzak@redhat.com>
6 *
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.
11 *
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.
16 *
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.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29
30 #include <libmount.h>
31
32 #include "nls.h"
33 #include "c.h"
34 #include "env.h"
35 #include "optutils.h"
36 #include "exitcodes.h"
37 #include "closestream.h"
38 #include "pathnames.h"
39 #include "canonicalize.h"
40 #include "xalloc.h"
41
42 static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
43 const char *filename, int line)
44 {
45 if (filename)
46 warnx(_("%s: parse error: ignore entry at line %d."),
47 filename, line);
48 return 0;
49 }
50
51
52 static void __attribute__((__noreturn__)) print_version(void)
53 {
54 const char *ver = NULL;
55 const char **features = NULL, **p;
56
57 mnt_get_library_version(&ver);
58 mnt_get_library_features(&features);
59
60 printf(_("%s from %s (libmount %s"),
61 program_invocation_short_name,
62 PACKAGE_STRING,
63 ver);
64 p = features;
65 while (p && *p) {
66 fputs(p == features ? ": " : ", ", stdout);
67 fputs(*p++, stdout);
68 }
69 fputs(")\n", stdout);
70 exit(MOUNT_EX_SUCCESS);
71 }
72 static void __attribute__((__noreturn__)) usage(FILE *out)
73 {
74 fputs(USAGE_HEADER, out);
75 fprintf(out, _(
76 " %1$s [-hV]\n"
77 " %1$s -a [options]\n"
78 " %1$s [options] <source> | <directory>\n"),
79 program_invocation_short_name);
80
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);
97
98 fputs(USAGE_SEPARATOR, out);
99 fputs(USAGE_HELP, out);
100 fputs(USAGE_VERSION, out);
101 fprintf(out, USAGE_MAN_TAIL("umount(8)"));
102
103 exit(out == stderr ? MOUNT_EX_USAGE : MOUNT_EX_SUCCESS);
104 }
105
106 static void __attribute__((__noreturn__)) exit_non_root(const char *option)
107 {
108 const uid_t ruid = getuid();
109 const uid_t euid = geteuid();
110
111 if (ruid == 0 && euid != 0) {
112 /* user is root, but setuid to non-root */
113 if (option)
114 errx(MOUNT_EX_USAGE,
115 _("only root can use \"--%s\" option "
116 "(effective UID is %u)"),
117 option, euid);
118 errx(MOUNT_EX_USAGE, _("only root can do that "
119 "(effective UID is %u)"), euid);
120 }
121 if (option)
122 errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
123 errx(MOUNT_EX_USAGE, _("only root can do that"));
124 }
125
126 static void success_message(struct libmnt_context *cxt)
127 {
128 const char *tgt, *src;
129
130 if (mnt_context_helper_executed(cxt)
131 || mnt_context_get_status(cxt) != 1)
132 return;
133
134 tgt = mnt_context_get_target(cxt);
135 if (!tgt)
136 return;
137
138 src = mnt_context_get_source(cxt);
139 if (src)
140 warnx(_("%s (%s) unmounted"), tgt, src);
141 else
142 warnx(_("%s unmounted"), tgt);
143 }
144
145 /*
146 * Handles generic errors like ENOMEM, ...
147 *
148 * rc = 0 success
149 * <0 error (usually -errno)
150 *
151 * Returns exit status (MOUNT_EX_*) and prints error message.
152 */
153 static int handle_generic_errors(int rc, const char *msg, ...)
154 {
155 va_list va;
156
157 va_start(va, msg);
158 errno = -rc;
159
160 switch(errno) {
161 case EINVAL:
162 case EPERM:
163 vwarn(msg, va);
164 rc = MOUNT_EX_USAGE;
165 break;
166 case ENOMEM:
167 vwarn(msg, va);
168 rc = MOUNT_EX_SYSERR;
169 break;
170 default:
171 vwarn(msg, va);
172 rc = MOUNT_EX_FAIL;
173 break;
174 }
175 va_end(va);
176 return rc;
177 }
178
179 static int mk_exit_code(struct libmnt_context *cxt, int rc)
180 {
181 int syserr;
182 const char *tgt = mnt_context_get_target(cxt);
183
184 if (mnt_context_helper_executed(cxt))
185 /*
186 * /sbin/umount.<type> called, return status
187 */
188 return mnt_context_get_helper_status(cxt);
189
190 if (rc == 0 && mnt_context_get_status(cxt) == 1)
191 /*
192 * Libmount success && syscall success.
193 */
194 return MOUNT_EX_SUCCESS;
195
196
197 if (!mnt_context_syscall_called(cxt)) {
198 /*
199 * libmount errors (extra library checks)
200 */
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;
206 }
207 return handle_generic_errors(rc, _("%s: umount failed"), tgt);
208
209 } else if (mnt_context_get_syscall_errno(cxt) == 0) {
210 /*
211 * umount(2) syscall success, but something else failed
212 * (probably error in mtab processing).
213 */
214 if (rc < 0)
215 return handle_generic_errors(rc,
216 _("%s: filesystem was unmounted, but mount(8) failed"),
217 tgt);
218
219 return MOUNT_EX_SOFTWARE; /* internal error */
220
221 }
222
223 /*
224 * umount(2) errors
225 */
226 syserr = mnt_context_get_syscall_errno(cxt);
227
228 switch(syserr) {
229 case ENXIO:
230 warnx(_("%s: invalid block device"), tgt); /* ??? */
231 break;
232 case EINVAL:
233 warnx(_("%s: not mounted"), tgt);
234 break;
235 case EIO:
236 warnx(_("%s: can't write superblock"), tgt);
237 break;
238 case EBUSY:
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).)"),
242 tgt);
243 break;
244 case ENOENT:
245 if (tgt && *tgt)
246 warnx(_("%s: mountpoint not found"), tgt);
247 else
248 warnx(_("undefined mountpoint"));
249 break;
250 case EPERM:
251 warnx(_("%s: must be superuser to unmount"), tgt);
252 break;
253 case EACCES:
254 warnx(_("%s: block devices are not permitted on filesystem"), tgt);
255 break;
256 default:
257 errno = syserr;
258 warn("%s", tgt);
259 break;
260 }
261 return MOUNT_EX_FAIL;
262 }
263
264 static int umount_all(struct libmnt_context *cxt)
265 {
266 struct libmnt_iter *itr;
267 struct libmnt_fs *fs;
268 int mntrc, ignored, rc = 0;
269
270 itr = mnt_new_iter(MNT_ITER_BACKWARD);
271 if (!itr) {
272 warn(_("failed to initialize libmount iterator"));
273 return MOUNT_EX_SYSERR;
274 }
275
276 while (mnt_context_next_umount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
277
278 const char *tgt = mnt_fs_get_target(fs);
279
280 if (ignored) {
281 if (mnt_context_is_verbose(cxt))
282 printf(_("%-25s: ignored\n"), tgt);
283 } else {
284 int xrc = mk_exit_code(cxt, mntrc);
285
286 if (xrc == MOUNT_EX_SUCCESS
287 && mnt_context_is_verbose(cxt))
288 printf("%-25s: successfully unmounted\n", tgt);
289 rc |= xrc;
290 }
291 }
292
293 mnt_free_iter(itr);
294 return rc;
295 }
296
297 static int umount_one(struct libmnt_context *cxt, const char *spec)
298 {
299 int rc;
300
301 if (!spec)
302 return MOUNT_EX_SOFTWARE;
303
304 if (mnt_context_set_target(cxt, spec))
305 err(MOUNT_EX_SYSERR, _("failed to set umount target"));
306
307 rc = mnt_context_umount(cxt);
308 rc = mk_exit_code(cxt, rc);
309
310 if (rc == MOUNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
311 success_message(cxt);
312
313 mnt_reset_context(cxt);
314 return rc;
315 }
316
317 static struct libmnt_table *new_mountinfo(struct libmnt_context *cxt)
318 {
319 struct libmnt_table *tb = mnt_new_table();
320 if (!tb)
321 err(MOUNT_EX_SYSERR, _("libmount table allocation failed"));
322
323 mnt_table_set_parser_errcb(tb, table_parser_errcb);
324 mnt_table_set_cache(tb, mnt_context_get_cache(cxt));
325
326 if (mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO)) {
327 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO);
328 mnt_unref_table(tb);
329 tb = NULL;
330 }
331
332 return tb;
333 }
334
335 /*
336 * like umount_one() but does not return error is @spec not mounted
337 */
338 static int umount_one_if_mounted(struct libmnt_context *cxt, const char *spec)
339 {
340 int rc;
341 struct libmnt_fs *fs;
342
343 rc = mnt_context_find_umount_fs(cxt, spec, &fs);
344 if (rc == 1) {
345 rc = MOUNT_EX_SUCCESS; /* alredy unmounted */
346 mnt_reset_context(cxt);
347 } else if (rc < 0) {
348 rc = mk_exit_code(cxt, rc); /* error */
349 mnt_reset_context(cxt);
350 } else
351 rc = umount_one(cxt, mnt_fs_get_target(fs));
352
353 return rc;
354 }
355
356 static int umount_do_recurse(struct libmnt_context *cxt,
357 struct libmnt_table *tb, struct libmnt_fs *fs)
358 {
359 struct libmnt_fs *child;
360 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
361 int rc;
362
363 if (!itr)
364 err(MOUNT_EX_SYSERR, _("libmount iterator allocation failed"));
365
366 /* umount all childern */
367 for (;;) {
368 rc = mnt_table_next_child_fs(tb, itr, fs, &child);
369 if (rc < 0) {
370 warnx(_("failed to get child fs of %s"),
371 mnt_fs_get_target(fs));
372 rc = MOUNT_EX_SOFTWARE;
373 goto done;
374 } else if (rc == 1)
375 break; /* no more children */
376
377 rc = umount_do_recurse(cxt, tb, child);
378 if (rc != MOUNT_EX_SUCCESS)
379 goto done;
380 }
381
382 rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
383 done:
384 mnt_free_iter(itr);
385 return rc;
386 }
387
388 static int umount_recursive(struct libmnt_context *cxt, const char *spec)
389 {
390 struct libmnt_table *tb;
391 struct libmnt_fs *fs;
392 int rc;
393
394 tb = new_mountinfo(cxt);
395 if (!tb)
396 return MOUNT_EX_SOFTWARE;
397
398 /* it's always real mountpoint, don't assume that the target maybe a device */
399 mnt_context_disable_swapmatch(cxt, 1);
400
401 fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD);
402 if (fs)
403 rc = umount_do_recurse(cxt, tb, fs);
404 else {
405 rc = MOUNT_EX_USAGE;
406 warnx(access(spec, F_OK) == 0 ?
407 _("%s: not mounted") :
408 _("%s: not found"), spec);
409 }
410
411 mnt_unref_table(tb);
412 return rc;
413 }
414
415 static int umount_alltargets(struct libmnt_context *cxt, const char *spec, int rec)
416 {
417 struct libmnt_fs *fs;
418 struct libmnt_table *tb;
419 struct libmnt_iter *itr = NULL;
420 dev_t devno = 0;
421 int rc;
422
423 /* Convert @spec to device name, Use the same logic like regular
424 * "umount <spec>".
425 */
426 rc = mnt_context_find_umount_fs(cxt, spec, &fs);
427 if (rc == 1) {
428 rc = MOUNT_EX_USAGE;
429 warnx(access(spec, F_OK) == 0 ?
430 _("%s: not mounted") :
431 _("%s: not found"), spec);
432 return rc;
433 }
434 if (rc < 0)
435 return mk_exit_code(cxt, rc); /* error */
436
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);
441
442 itr = mnt_new_iter(MNT_ITER_BACKWARD);
443 if (!itr)
444 err(MOUNT_EX_SYSERR, _("libmount iterator allocation failed"));
445
446 /* get on @cxt independent mountinfo */
447 tb = new_mountinfo(cxt);
448 if (!tb)
449 return MOUNT_EX_SOFTWARE;
450
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);
454 fs = NULL;
455
456 mnt_reset_context(cxt);
457
458 while (mnt_table_next_fs(tb, itr, &fs) == 0) {
459 if (mnt_fs_get_devno(fs) != devno)
460 continue;
461 mnt_context_disable_swapmatch(cxt, 1);
462 if (rec)
463 rc = umount_do_recurse(cxt, tb, fs);
464 else
465 rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
466
467 if (rc != MOUNT_EX_SUCCESS)
468 break;
469 }
470
471 mnt_free_iter(itr);
472 mnt_unref_table(tb);
473
474 return rc;
475 }
476
477 /*
478 * Check path -- non-root user should not be able to resolve path which is
479 * unreadable for him.
480 */
481 static char *sanitize_path(const char *path)
482 {
483 char *p;
484
485 if (!path)
486 return NULL;
487
488 p = canonicalize_path_restricted(path);
489 if (!p)
490 err(MOUNT_EX_USAGE, "%s", path);
491
492 return p;
493 }
494
495 int main(int argc, char **argv)
496 {
497 int c, rc = 0, all = 0, recursive = 0, alltargets = 0;
498 struct libmnt_context *cxt;
499 char *types = NULL;
500
501 enum {
502 UMOUNT_OPT_FAKE = CHAR_MAX + 1,
503 };
504
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' },
522 { NULL, 0, 0, 0 }
523 };
524
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 */
530 { 0 }
531 };
532 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
533
534 sanitize_env();
535 setlocale(LC_ALL, "");
536 bindtextdomain(PACKAGE, LOCALEDIR);
537 textdomain(PACKAGE);
538 atexit(close_stdout);
539
540 mnt_init_debug(0);
541 cxt = mnt_new_context();
542 if (!cxt)
543 err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));
544
545 mnt_context_set_tables_errcb(cxt, table_parser_errcb);
546
547 while ((c = getopt_long(argc, argv, "aAcdfhilnRrO:t:vV",
548 longopts, NULL)) != -1) {
549
550
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));
554
555 err_exclusive_options(c, longopts, excl, excl_st);
556
557 switch(c) {
558 case 'a':
559 all = 1;
560 break;
561 case 'A':
562 alltargets = 1;
563 break;
564 case 'c':
565 mnt_context_disable_canonicalize(cxt, TRUE);
566 break;
567 case 'd':
568 mnt_context_enable_loopdel(cxt, TRUE);
569 break;
570 case UMOUNT_OPT_FAKE:
571 mnt_context_enable_fake(cxt, TRUE);
572 break;
573 case 'f':
574 mnt_context_enable_force(cxt, TRUE);
575 break;
576 case 'h':
577 usage(stdout);
578 break;
579 case 'i':
580 mnt_context_disable_helpers(cxt, TRUE);
581 break;
582 case 'l':
583 mnt_context_enable_lazy(cxt, TRUE);
584 break;
585 case 'n':
586 mnt_context_disable_mtab(cxt, TRUE);
587 break;
588 case 'r':
589 mnt_context_enable_rdonly_umount(cxt, TRUE);
590 break;
591 case 'R':
592 recursive = TRUE;
593 break;
594 case 'O':
595 if (mnt_context_set_options_pattern(cxt, optarg))
596 err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
597 break;
598 case 't':
599 types = optarg;
600 break;
601 case 'v':
602 mnt_context_enable_verbose(cxt, TRUE);
603 break;
604 case 'V':
605 print_version();
606 break;
607 default:
608 usage(stderr);
609 break;
610 }
611 }
612
613 argc -= optind;
614 argv += optind;
615
616 if (all) {
617 if (!types)
618 types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";
619
620 mnt_context_set_fstype_pattern(cxt, types);
621 rc = umount_all(cxt);
622
623 } else if (argc < 1) {
624 usage(stderr);
625
626 } else if (alltargets) {
627 while (argc--)
628 rc += umount_alltargets(cxt, *argv++, recursive);
629 } else if (recursive) {
630 while (argc--)
631 rc += umount_recursive(cxt, *argv++);
632 } else {
633 while (argc--) {
634 char *path = *argv;
635
636 if (mnt_context_is_restricted(cxt)
637 && !mnt_tag_is_valid(path))
638 path = sanitize_path(path);
639
640 rc += umount_one(cxt, path);
641
642 if (path != *argv)
643 free(path);
644 argv++;
645 }
646 }
647
648 mnt_free_context(cxt);
649 return (rc < 256) ? rc : 255;
650 }
651