]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/context_umount.c
libmount: add mnt_context_within_helper() wrapper
[thirdparty/util-linux.git] / libmount / src / context_umount.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2010-2018 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 */
12
13 /**
14 * SECTION: context-umount
15 * @title: Umount context
16 * @short_description: high-level API to umount operation.
17 */
18
19 #include <sys/wait.h>
20 #include <sys/mount.h>
21
22 #include "pathnames.h"
23 #include "loopdev.h"
24 #include "strutils.h"
25 #include "mountP.h"
26
27 /*
28 * umount2 flags
29 */
30 #ifndef MNT_FORCE
31 # define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */
32 #endif
33
34 #ifndef MNT_DETACH
35 # define MNT_DETACH 0x00000002 /* Just detach from the tree */
36 #endif
37
38 #ifndef UMOUNT_NOFOLLOW
39 # define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
40 #endif
41
42 #ifndef UMOUNT_UNUSED
43 # define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
44 #endif
45
46 /* search in mountinfo */
47 static int __mountinfo_find_umount_fs(struct libmnt_context *cxt,
48 const char *tgt,
49 struct libmnt_fs **pfs)
50 {
51 int rc;
52 struct libmnt_ns *ns_old;
53 struct libmnt_table *mountinfo = NULL;
54 struct libmnt_fs *fs;
55 char *loopdev = NULL;
56
57 assert(cxt);
58 assert(tgt);
59 assert(pfs);
60
61 *pfs = NULL;
62 DBG(CXT, ul_debugobj(cxt, " search %s in mountinfo", tgt));
63
64 /*
65 * The mount table may be huge, and on systems with utab we have to
66 * merge userspace mount options into /proc/self/mountinfo. This all is
67 * expensive. The tab filter can be used to filter out entries, then a mount
68 * table and utab are very tiny files.
69 *
70 * The filter uses mnt_fs_streq_{target,srcpath} function where all
71 * paths should be absolute and canonicalized. This is done within
72 * mnt_context_get_mountinfo_for_target() where LABEL, UUID or symlinks are
73 * canonicalized. If --no-canonicalize is enabled than the target path
74 * is expected already canonical.
75 *
76 * Anyway it's better to read huge mount table than canonicalize target
77 * paths. It means we use the filter only if --no-canonicalize enabled.
78 *
79 * It also means that we have to read mount table from kernel.
80 */
81 if (mnt_context_is_nocanonicalize(cxt) && *tgt == '/')
82 rc = mnt_context_get_mountinfo_for_target(cxt, &mountinfo, tgt);
83 else
84 rc = mnt_context_get_mountinfo(cxt, &mountinfo);
85
86 if (rc) {
87 DBG(CXT, ul_debugobj(cxt, "umount: failed to read mountinfo"));
88 return rc;
89 }
90
91 if (mnt_table_get_nents(mountinfo) == 0) {
92 DBG(CXT, ul_debugobj(cxt, "umount: mountinfo empty"));
93 return 1;
94 }
95
96 ns_old = mnt_context_switch_target_ns(cxt);
97 if (!ns_old)
98 return -MNT_ERR_NAMESPACE;
99
100 try_loopdev:
101 fs = mnt_table_find_target(mountinfo, tgt, MNT_ITER_BACKWARD);
102 if (!fs && mnt_context_is_swapmatch(cxt)) {
103 /*
104 * Maybe the option is source rather than target (sometimes
105 * people use e.g. "umount /dev/sda1")
106 */
107 fs = mnt_table_find_source(mountinfo, tgt, MNT_ITER_BACKWARD);
108
109 if (fs) {
110 struct libmnt_fs *fs1 = mnt_table_find_target(mountinfo,
111 mnt_fs_get_target(fs),
112 MNT_ITER_BACKWARD);
113 if (!fs1) {
114 DBG(CXT, ul_debugobj(cxt, "mountinfo is broken?!?!"));
115 rc = -EINVAL;
116 goto err;
117 }
118 if (fs != fs1) {
119 /* Something was stacked over `file' on the
120 * same mount point. */
121 DBG(CXT, ul_debugobj(cxt,
122 "umount: %s: %s is mounted "
123 "over it on the same point",
124 tgt, mnt_fs_get_source(fs1)));
125 rc = -EINVAL;
126 goto err;
127 }
128 }
129 }
130
131 if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) {
132 /*
133 * Maybe the option is /path/file.img, try to convert to /dev/loopN
134 */
135 struct stat st;
136
137 if (mnt_safe_stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) {
138 int count;
139 struct libmnt_cache *cache = mnt_context_get_cache(cxt);
140 const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt;
141
142 count = loopdev_count_by_backing_file(bf, &loopdev);
143 if (count == 1) {
144 DBG(CXT, ul_debugobj(cxt,
145 "umount: %s --> %s (retry)", tgt, loopdev));
146 tgt = loopdev;
147 goto try_loopdev;
148
149 } else if (count > 1)
150 DBG(CXT, ul_debugobj(cxt,
151 "umount: warning: %s is associated "
152 "with more than one loopdev", tgt));
153 }
154 }
155
156 *pfs = fs;
157 free(loopdev);
158 if (!mnt_context_switch_ns(cxt, ns_old))
159 return -MNT_ERR_NAMESPACE;
160
161 DBG(CXT, ul_debugobj(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) :
162 "<not found>"));
163 return fs ? 0 : 1;
164 err:
165 free(loopdev);
166 if (!mnt_context_switch_ns(cxt, ns_old))
167 return -MNT_ERR_NAMESPACE;
168 return rc;
169 }
170
171 /**
172 * mnt_context_find_umount_fs:
173 * @cxt: mount context
174 * @tgt: mountpoint, device, ...
175 * @pfs: returns point to filesystem
176 *
177 * Returns: 0 on success, <0 on error, 1 if target filesystem not found
178 */
179 int mnt_context_find_umount_fs(struct libmnt_context *cxt,
180 const char *tgt,
181 struct libmnt_fs **pfs)
182 {
183 if (pfs)
184 *pfs = NULL;
185
186 if (!cxt || !tgt || !pfs)
187 return -EINVAL;
188
189 DBG(CXT, ul_debugobj(cxt, "umount: lookup FS for '%s'", tgt));
190
191 if (!*tgt)
192 return 1; /* empty string is not an error */
193
194 /* In future this function should be extended to support for example
195 * fsinfo() (or another cheap way kernel will support), for now the
196 * default is expensive mountinfo.
197 */
198 return __mountinfo_find_umount_fs(cxt, tgt, pfs);
199 }
200
201 /* Check if there is something important in the utab file. The parsed utab is
202 * stored in context->utab and deallocated by mnt_free_context().
203 *
204 * This function exists to avoid (if possible) /proc/self/mountinfo usage, so
205 * don't use things like mnt_resolve_target(), mnt_context_get_mountinfo() etc here.
206 * See lookup_umount_fs() for more details.
207 */
208 static int has_utab_entry(struct libmnt_context *cxt, const char *target)
209 {
210 struct libmnt_cache *cache = NULL;
211 struct libmnt_fs *fs;
212 struct libmnt_iter itr;
213 char *cn = NULL;
214 int rc = 0;
215
216 assert(cxt);
217
218 if (!cxt->utab) {
219 const char *path = mnt_get_utab_path();
220
221 if (!path || is_file_empty(path))
222 return 0;
223 cxt->utab = mnt_new_table();
224 if (!cxt->utab)
225 return 0;
226 cxt->utab->fmt = MNT_FMT_UTAB;
227 if (mnt_table_parse_file(cxt->utab, path))
228 return 0;
229 }
230
231 /* paths in utab are canonicalized */
232 cache = mnt_context_get_cache(cxt);
233 cn = mnt_resolve_path(target, cache);
234 mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
235
236 while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) {
237 if (mnt_fs_streq_target(fs, cn)) {
238 rc = 1;
239 break;
240 }
241 }
242
243 if (!cache)
244 free(cn);
245 return rc;
246 }
247
248 /* returns: 1 not found; <0 on error; 1 success */
249 static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tgt)
250 {
251 struct stat st;
252 const char *type;
253
254 assert(cxt);
255 assert(cxt->fs);
256
257 DBG(CXT, ul_debugobj(cxt, " lookup by statfs"));
258
259 /*
260 * Let's try to avoid mountinfo usage at all to minimize performance
261 * degradation. Don't forget that kernel has to compose *whole*
262 * mountinfo about all mountpoints although we look for only one entry.
263 *
264 * All we need is fstype and to check if there is no userspace mount
265 * options for the target (e.g. helper=udisks to call /sbin/umount.udisks).
266 *
267 * So, let's use statfs() if possible (it's bad idea for --lazy/--force
268 * umounts as target is probably unreachable NFS, also for --detach-loop
269 * as this additionally needs to know the name of the loop device).
270 */
271 if (mnt_context_is_restricted(cxt)
272 || *tgt != '/'
273 || mnt_context_within_helper(cxt)
274 || mnt_context_is_force(cxt)
275 || mnt_context_is_lazy(cxt)
276 || mnt_context_is_nocanonicalize(cxt)
277 || mnt_context_is_loopdel(cxt)
278 || mnt_safe_stat(tgt, &st) != 0 || !S_ISDIR(st.st_mode)
279 || has_utab_entry(cxt, tgt))
280 return 1; /* not found */
281
282 type = mnt_fs_get_fstype(cxt->fs);
283 if (!type) {
284 struct statfs vfs;
285 int fd;
286
287 DBG(CXT, ul_debugobj(cxt, " trying fstatfs()"));
288
289 /* O_PATH avoids triggering automount points. */
290 fd = open(tgt, O_PATH);
291 if (fd >= 0) {
292 if (fstatfs(fd, &vfs) == 0)
293 type = mnt_statfs_get_fstype(&vfs);
294 close(fd);
295 }
296 if (type) {
297 int rc = mnt_fs_set_fstype(cxt->fs, type);
298 if (rc)
299 return rc;
300 }
301 }
302 if (type) {
303 DBG(CXT, ul_debugobj(cxt, " umount: disabling mountinfo"));
304 mnt_context_disable_mtab(cxt, TRUE);
305
306 DBG(CXT, ul_debugobj(cxt,
307 " mountinfo unnecessary [type=%s]", type));
308 return 0;
309 }
310
311 return 1; /* not found */
312 }
313
314 /* returns: 1 not found; <0 on error; 1 success */
315 static int lookup_umount_fs_by_mountinfo(struct libmnt_context *cxt, const char *tgt)
316 {
317 struct libmnt_fs *fs = NULL;
318 int rc;
319
320 assert(cxt);
321 assert(cxt->fs);
322
323 DBG(CXT, ul_debugobj(cxt, " lookup by mountinfo"));
324
325 /* search */
326 rc = __mountinfo_find_umount_fs(cxt, tgt, &fs);
327 if (rc != 0)
328 return rc;
329
330 /* apply result */
331 if (fs != cxt->fs) {
332 mnt_fs_set_source(cxt->fs, NULL);
333 mnt_fs_set_target(cxt->fs, NULL);
334
335 if (!mnt_copy_fs(cxt->fs, fs)) {
336 DBG(CXT, ul_debugobj(cxt, " failed to copy FS"));
337 return -errno;
338 }
339 DBG(CXT, ul_debugobj(cxt, " mountinfo applied"));
340 }
341
342 cxt->flags |= MNT_FL_TAB_APPLIED;
343 return 0;
344 }
345
346 /* This function searchs for FS according to cxt->fs->target,
347 * apply result to cxt->fs and it's umount replacement to
348 * mnt_context_apply_fstab(), use mnt_context_tab_applied()
349 * to check result.
350 *
351 * The goal is to minimize situations when we need to parse
352 * /proc/self/mountinfo.
353 */
354 static int lookup_umount_fs(struct libmnt_context *cxt)
355 {
356 const char *tgt;
357 int rc = 0;
358
359 assert(cxt);
360 assert(cxt->fs);
361
362 DBG(CXT, ul_debugobj(cxt, "umount: lookup FS"));
363
364 tgt = mnt_fs_get_target(cxt->fs);
365 if (!tgt) {
366 DBG(CXT, ul_debugobj(cxt, " undefined target"));
367 return -EINVAL;
368 }
369
370 /* try get fs type by statfs() */
371 rc = lookup_umount_fs_by_statfs(cxt, tgt);
372 if (rc <= 0)
373 goto done;
374
375 /* get complete fs from fs entry from mountinfo */
376 rc = lookup_umount_fs_by_mountinfo(cxt, tgt);
377 if (rc <= 0)
378 goto done;
379
380 DBG(CXT, ul_debugobj(cxt, " cannot find '%s'", tgt));
381 return 0; /* this is correct! */
382
383 done:
384 if (rc == 0 && cxt->fs) {
385 struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
386
387 if (!ol)
388 return -ENOMEM;
389
390 rc = mnt_optlist_set_optstr(ol, mnt_fs_get_options(cxt->fs), NULL);
391 }
392 DBG(CXT, ul_debugobj(cxt, " lookup done [rc=%d]", rc));
393 return rc;
394 }
395
396 /* check if @devname is loopdev and if the device is associated
397 * with a source from @fstab_fs
398 */
399 static int is_associated_fs(const char *devname, struct libmnt_fs *fs)
400 {
401 uintmax_t offset = 0;
402 const char *src, *optstr;
403 char *val;
404 size_t valsz;
405 int flags = 0;
406
407 /* check if it begins with /dev/loop */
408 if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP) - 1) != 0)
409 return 0;
410
411 src = mnt_fs_get_srcpath(fs);
412 if (!src)
413 return 0;
414
415 /* check for the offset option in @fs */
416 optstr = mnt_fs_get_user_options(fs);
417
418 if (optstr &&
419 mnt_optstr_get_option(optstr, "offset", &val, &valsz) == 0) {
420 flags |= LOOPDEV_FL_OFFSET;
421
422 if (mnt_parse_offset(val, valsz, &offset) != 0)
423 return 0;
424 }
425
426 return loopdev_is_used(devname, src, offset, 0, flags);
427 }
428
429 /* returns: <0 on error; 1 not found (not wanted) */
430 static int prepare_helper_from_option(struct libmnt_context *cxt,
431 const char *name)
432 {
433 struct libmnt_optlist *ol;
434 struct libmnt_opt *opt;
435 const char *suffix;
436
437 ol = mnt_context_get_optlist(cxt);
438 if (!ol)
439 return -ENOMEM;
440
441 opt = mnt_optlist_get_named(ol, name, cxt->map_userspace);
442 if (!opt || !mnt_opt_has_value(opt))
443 return 1;
444
445 suffix = mnt_opt_get_value(opt);
446 DBG(CXT, ul_debugobj(cxt, "umount: umount.%s %s requested", suffix, name));
447
448 return mnt_context_prepare_helper(cxt, "umount", suffix);
449 }
450
451 static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv)
452 {
453 struct libmnt_ns *ns_old;
454 struct libmnt_optlist *ol;
455 struct libmnt_opt *opt;
456 const char *type = mnt_fs_get_fstype(cxt->fs);
457 const char *val = NULL;;
458 uid_t uid, entry_uid;
459
460 *errsv = 0;
461
462 if (!type)
463 return 0;
464
465 if (strcmp(type, "fuse") != 0 &&
466 strcmp(type, "fuseblk") != 0 &&
467 strncmp(type, "fuse.", 5) != 0 &&
468 strncmp(type, "fuseblk.", 8) != 0)
469 return 0;
470
471 ol = mnt_context_get_optlist(cxt);
472 if (!ol)
473 return 0;
474
475 opt = mnt_optlist_get_named(ol, "user_id", NULL);
476 if (opt)
477 val = mnt_opt_get_value(opt);
478 if (!val || mnt_opt_get_map(opt))
479 return 0;
480
481 if (mnt_parse_uid(val, strlen(val), &entry_uid) != 0)
482 return 0;
483
484 /* get current user */
485 ns_old = mnt_context_switch_origin_ns(cxt);
486 if (!ns_old) {
487 *errsv = -MNT_ERR_NAMESPACE;
488 return 0;
489 }
490
491 uid = getuid();
492
493 if (!mnt_context_switch_ns(cxt, ns_old)) {
494 *errsv = -MNT_ERR_NAMESPACE;
495 return 0;
496 }
497
498 return uid == entry_uid;
499 }
500
501 /*
502 * Note that cxt->fs contains relevant mountinfo entry!
503 */
504 static int evaluate_permissions(struct libmnt_context *cxt)
505 {
506 unsigned long fstab_flags = 0;
507 struct libmnt_table *fstab;
508 const char *tgt, *src, *optstr;
509 int rc = 0, ok = 0;
510 struct libmnt_fs *fs;
511
512 assert(cxt);
513 assert(cxt->fs);
514 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
515
516 if (!mnt_context_is_restricted(cxt))
517 return 0; /* superuser mount */
518
519 DBG(CXT, ul_debugobj(cxt, "umount: evaluating permissions"));
520
521 if (!mnt_context_tab_applied(cxt)) {
522 DBG(CXT, ul_debugobj(cxt,
523 "cannot find %s in mountinfo and you are not root",
524 mnt_fs_get_target(cxt->fs)));
525 goto eperm;
526 }
527
528 if (!mnt_context_is_nohelpers(cxt)) {
529 rc = prepare_helper_from_option(cxt, "uhelper");
530 if (rc < 0)
531 return rc; /* error */
532 if (rc == 0 && cxt->helper)
533 return 0; /* we'll call /sbin/umount.<uhelper> */
534 }
535
536 /*
537 * Check if this is a fuse mount for the current user,
538 * if so then unmounting is allowed
539 */
540 if (is_fuse_usermount(cxt, &rc)) {
541 DBG(CXT, ul_debugobj(cxt, "fuse user mount, umount is allowed"));
542 return 0;
543 }
544 if (rc)
545 return rc;
546
547 /*
548 * User mounts have to be in /etc/fstab
549 */
550 rc = mnt_context_get_fstab(cxt, &fstab);
551 if (rc)
552 return rc;
553
554 tgt = mnt_fs_get_target(cxt->fs);
555 src = mnt_fs_get_source(cxt->fs);
556
557 if (mnt_fs_get_bindsrc(cxt->fs)) {
558 src = mnt_fs_get_bindsrc(cxt->fs);
559 DBG(CXT, ul_debugobj(cxt,
560 "umount: using bind source: %s", src));
561 }
562
563 /* If fstab contains the two lines
564 * /dev/sda1 /mnt/zip auto user,noauto 0 0
565 * /dev/sda4 /mnt/zip auto user,noauto 0 0
566 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
567 * So, we must not look for the file, but for the pair (dev,file) in fstab.
568 */
569 fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
570 if (!fs) {
571 /*
572 * It's possible that there is /path/file.img in fstab and
573 * /dev/loop0 in mountinfo -- then we have to check the relation
574 * between loopdev and the file.
575 */
576 fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
577 if (fs) {
578 struct libmnt_cache *cache = mnt_context_get_cache(cxt);
579 const char *sp = mnt_fs_get_srcpath(cxt->fs); /* devname from mountinfo */
580 const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp;
581
582 if (!dev || !is_associated_fs(dev, fs))
583 fs = NULL;
584 }
585 if (!fs) {
586 DBG(CXT, ul_debugobj(cxt,
587 "umount %s: mountinfo disagrees with fstab",
588 tgt));
589 goto eperm;
590 }
591 }
592
593 /*
594 * User mounting and unmounting is allowed only if fstab contains one
595 * of the options `user', `users' or `owner' or `group'.
596 *
597 * The option `users' allows arbitrary users to mount and unmount -
598 * this may be a security risk.
599 *
600 * The options `user', `owner' and `group' only allow unmounting by the
601 * user that mounted (visible in mountinfo).
602 */
603 optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */
604 if (!optstr)
605 goto eperm;
606
607 if (mnt_optstr_get_flags(optstr, &fstab_flags,
608 mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
609 goto eperm;
610
611 if (fstab_flags & MNT_MS_USERS) {
612 DBG(CXT, ul_debugobj(cxt,
613 "umount: promiscuous setting ('users') in fstab"));
614 return 0;
615 }
616 /*
617 * Check user=<username> setting from utab if there is a user, owner or
618 * group option in /etc/fstab
619 */
620 if (fstab_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) {
621
622 struct libmnt_optlist *ol;
623 struct libmnt_opt *opt;
624 char *curr_user = NULL;
625 struct libmnt_ns *ns_old;
626
627 DBG(CXT, ul_debugobj(cxt,
628 "umount: checking user=<username> from mountinfo"));
629
630 ns_old = mnt_context_switch_origin_ns(cxt);
631 if (!ns_old)
632 return -MNT_ERR_NAMESPACE;
633
634 curr_user = mnt_get_username(getuid());
635
636 if (!mnt_context_switch_ns(cxt, ns_old)) {
637 free(curr_user);
638 return -MNT_ERR_NAMESPACE;
639 }
640 if (!curr_user) {
641 DBG(CXT, ul_debugobj(cxt, "umount %s: cannot "
642 "convert %d to username", tgt, getuid()));
643 goto eperm;
644 }
645
646 /* get "user=" from utab */
647 ol = mnt_context_get_optlist(cxt);
648 if (!ol) {
649 free(curr_user);
650 return -ENOMEM;
651 }
652 opt = mnt_optlist_get_named(ol, "user", cxt->map_userspace);
653 if (opt && mnt_opt_has_value(opt))
654 ok = !strcmp(curr_user, mnt_opt_get_value(opt));
655
656 free(curr_user);
657 }
658
659 if (ok) {
660 DBG(CXT, ul_debugobj(cxt, "umount %s is allowed", tgt));
661 return 0;
662 }
663 eperm:
664 DBG(CXT, ul_debugobj(cxt, "umount is not allowed for you"));
665 return -EPERM;
666 }
667
668 static int exec_helper(struct libmnt_context *cxt)
669 {
670 char *namespace = NULL;
671 struct libmnt_ns *ns_tgt = mnt_context_get_target_ns(cxt);
672 int rc;
673 pid_t pid;
674
675 assert(cxt);
676 assert(cxt->fs);
677 assert(cxt->helper);
678 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
679 assert(cxt->helper_exec_status == 1);
680
681 if (mnt_context_is_fake(cxt)) {
682 DBG(CXT, ul_debugobj(cxt, "fake mode: does not execute helper"));
683 cxt->helper_exec_status = rc = 0;
684 return rc;
685 }
686
687 if (ns_tgt->fd != -1
688 && asprintf(&namespace, "/proc/%i/fd/%i",
689 getpid(), ns_tgt->fd) == -1) {
690 return -ENOMEM;
691 }
692
693 DBG_FLUSH;
694
695 pid = fork();
696 switch (pid) {
697 case 0:
698 {
699 const char *args[12], *type;
700 int i = 0;
701
702 if (drop_permissions() != 0)
703 _exit(EXIT_FAILURE);
704
705 if (!mnt_context_switch_origin_ns(cxt))
706 _exit(EXIT_FAILURE);
707
708 type = mnt_fs_get_fstype(cxt->fs);
709
710 args[i++] = cxt->helper; /* 1 */
711 args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */
712
713 if (mnt_context_is_nomtab(cxt))
714 args[i++] = "-n"; /* 3 */
715 if (mnt_context_is_lazy(cxt))
716 args[i++] = "-l"; /* 4 */
717 if (mnt_context_is_force(cxt))
718 args[i++] = "-f"; /* 5 */
719 if (mnt_context_is_verbose(cxt))
720 args[i++] = "-v"; /* 6 */
721 if (mnt_context_is_rdonly_umount(cxt))
722 args[i++] = "-r"; /* 7 */
723 if (type
724 && strchr(type, '.')
725 && !endswith(cxt->helper, type)) {
726 args[i++] = "-t"; /* 8 */
727 args[i++] = type; /* 9 */
728 }
729 if (namespace) {
730 args[i++] = "-N"; /* 10 */
731 args[i++] = namespace; /* 11 */
732 }
733
734 args[i] = NULL; /* 12 */
735 for (i = 0; args[i]; i++)
736 DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"",
737 i, args[i]));
738 DBG_FLUSH;
739 execv(cxt->helper, (char * const *) args);
740 _exit(EXIT_FAILURE);
741 }
742 default:
743 {
744 int st;
745
746 if (waitpid(pid, &st, 0) == (pid_t) -1) {
747 cxt->helper_status = -1;
748 rc = -errno;
749 } else {
750 cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
751 cxt->helper_exec_status = rc = 0;
752 }
753 DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d, rc=%d%s]",
754 cxt->helper,
755 cxt->helper_status, rc,
756 rc ? " waitpid failed" : ""));
757 break;
758 }
759
760 case -1:
761 cxt->helper_exec_status = rc = -errno;
762 DBG(CXT, ul_debugobj(cxt, "fork() failed"));
763 break;
764 }
765
766 free(namespace);
767 return rc;
768 }
769
770 /*
771 * mnt_context_helper_setopt() backend.
772 *
773 * This function applies umount.type command line option (for example parsed
774 * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
775 * then 1 is returned.
776 *
777 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
778 */
779 int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
780 {
781 int rc = -EINVAL;
782
783 assert(cxt);
784 assert(cxt->action == MNT_ACT_UMOUNT);
785
786 switch(c) {
787 case 'n':
788 rc = mnt_context_disable_mtab(cxt, TRUE);
789 break;
790 case 'l':
791 rc = mnt_context_enable_lazy(cxt, TRUE);
792 break;
793 case 'f':
794 rc = mnt_context_enable_force(cxt, TRUE);
795 break;
796 case 'v':
797 rc = mnt_context_enable_verbose(cxt, TRUE);
798 break;
799 case 'r':
800 rc = mnt_context_enable_rdonly_umount(cxt, TRUE);
801 break;
802 case 't':
803 if (arg)
804 rc = mnt_context_set_fstype(cxt, arg);
805 break;
806 case 'N':
807 if (arg)
808 rc = mnt_context_set_target_ns(cxt, arg);
809 break;
810 default:
811 return 1;
812 }
813
814 return rc;
815 }
816
817 /* Check whether the kernel supports the UMOUNT_NOFOLLOW flag */
818 static int umount_nofollow_support(void)
819 {
820 int res = umount2("", UMOUNT_UNUSED);
821 if (res != -1 || errno != EINVAL)
822 return 0;
823
824 res = umount2("", UMOUNT_NOFOLLOW);
825 if (res != -1 || errno != ENOENT)
826 return 0;
827
828 return 1;
829 }
830
831 static int do_umount(struct libmnt_context *cxt)
832 {
833 int rc = 0, flags = 0;
834 const char *src, *target;
835 char *tgtbuf = NULL;
836
837 assert(cxt);
838 assert(cxt->fs);
839 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
840 assert(cxt->syscall_status == 1);
841
842 if (cxt->helper)
843 return exec_helper(cxt);
844
845 src = mnt_fs_get_srcpath(cxt->fs);
846 target = mnt_fs_get_target(cxt->fs);
847
848 if (!target)
849 return -EINVAL;
850
851 DBG(CXT, ul_debugobj(cxt, "do umount"));
852
853 if (mnt_context_is_restricted(cxt) && !mnt_context_is_fake(cxt)) {
854 /*
855 * extra paranoia for non-root users
856 * -- chdir to the parent of the mountpoint and use NOFOLLOW
857 * flag to avoid races and symlink attacks.
858 */
859 if (umount_nofollow_support())
860 flags |= UMOUNT_NOFOLLOW;
861
862 rc = mnt_chdir_to_parent(target, &tgtbuf);
863 if (rc)
864 return rc;
865 target = tgtbuf;
866 }
867
868 if (mnt_context_is_lazy(cxt))
869 flags |= MNT_DETACH;
870
871 if (mnt_context_is_force(cxt))
872 flags |= MNT_FORCE;
873
874 DBG(CXT, ul_debugobj(cxt, "umount(2) [target='%s', flags=0x%08x]%s",
875 target, flags,
876 mnt_context_is_fake(cxt) ? " (FAKE)" : ""));
877
878 if (mnt_context_is_fake(cxt))
879 rc = 0;
880 else {
881 rc = flags ? umount2(target, flags) : umount(target);
882 if (rc < 0)
883 cxt->syscall_status = -errno;
884 free(tgtbuf);
885 }
886
887 /*
888 * try remount read-only
889 */
890 if (rc < 0
891 && cxt->syscall_status == -EBUSY
892 && mnt_context_is_rdonly_umount(cxt)
893 && src) {
894 struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
895
896 /* keep info about remount in mount flags */
897 assert(ol);
898 mnt_optlist_append_flags(ol, MS_REMOUNT | MS_RDONLY, cxt->map_linux);
899
900 mnt_context_enable_loopdel(cxt, FALSE);
901
902 DBG(CXT, ul_debugobj(cxt,
903 "umount(2) failed [errno=%d] -- trying to remount read-only",
904 -cxt->syscall_status));
905
906 rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
907 MS_REMOUNT | MS_RDONLY, NULL);
908 if (rc < 0) {
909 cxt->syscall_status = -errno;
910 DBG(CXT, ul_debugobj(cxt,
911 "read-only re-mount(2) failed [errno=%d]",
912 -cxt->syscall_status));
913
914 return -cxt->syscall_status;
915 }
916 cxt->syscall_status = 0;
917 DBG(CXT, ul_debugobj(cxt, "read-only re-mount(2) success"));
918 return 0;
919 }
920
921 if (rc < 0) {
922 DBG(CXT, ul_debugobj(cxt, "umount(2) failed [errno=%d]",
923 -cxt->syscall_status));
924 return -cxt->syscall_status;
925 }
926
927 cxt->syscall_status = 0;
928 DBG(CXT, ul_debugobj(cxt, "umount(2) success"));
929 return 0;
930 }
931
932 /**
933 * mnt_context_prepare_umount:
934 * @cxt: mount context
935 *
936 * Prepare context for umounting, unnecessary for mnt_context_umount().
937 *
938 * Returns: 0 on success, and negative number in case of error.
939 */
940 int mnt_context_prepare_umount(struct libmnt_context *cxt)
941 {
942 int rc;
943 unsigned long flags = 0;
944 struct libmnt_ns *ns_old;
945
946 if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
947 return -EINVAL;
948 if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt))
949 return -EINVAL;
950 if (cxt->flags & MNT_FL_PREPARED)
951 return 0;
952
953 assert(cxt->helper_exec_status == 1);
954 assert(cxt->syscall_status == 1);
955
956 free(cxt->helper); /* be paranoid */
957 cxt->helper = NULL;
958 cxt->action = MNT_ACT_UMOUNT;
959
960 ns_old = mnt_context_switch_target_ns(cxt);
961 if (!ns_old)
962 return -MNT_ERR_NAMESPACE;
963
964 rc = lookup_umount_fs(cxt);
965 if (!rc)
966 rc = mnt_context_merge_mflags(cxt);
967 if (!rc)
968 rc = evaluate_permissions(cxt);
969
970 if (!rc && !mnt_context_is_nohelpers(cxt) && !cxt->helper) {
971 /* on helper= mount option based helper */
972 rc = prepare_helper_from_option(cxt, "helper");
973 if (rc < 0)
974 return rc;
975 if (!cxt->helper)
976 /* on fstype based helper */
977 rc = mnt_context_prepare_helper(cxt, "umount", NULL);
978 }
979
980 if (!rc)
981 rc = mnt_context_get_user_mflags(cxt, &flags);
982
983 if (!rc && (flags & MNT_MS_LOOP))
984 /* loop option explicitly specified in utab, detach this loop */
985 mnt_context_enable_loopdel(cxt, TRUE);
986
987 if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) {
988 const char *src = mnt_fs_get_srcpath(cxt->fs);
989
990 if (src && (!is_loopdev(src) || loopdev_is_autoclear(src)))
991 mnt_context_enable_loopdel(cxt, FALSE);
992 }
993
994 if (rc) {
995 DBG(CXT, ul_debugobj(cxt, "umount: preparing failed"));
996 return rc;
997 }
998 cxt->flags |= MNT_FL_PREPARED;
999
1000 if (!mnt_context_switch_ns(cxt, ns_old))
1001 return -MNT_ERR_NAMESPACE;
1002
1003 return rc;
1004 }
1005
1006 /**
1007 * mnt_context_do_umount:
1008 * @cxt: mount context
1009 *
1010 * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
1011 * Unnecessary for mnt_context_umount().
1012 *
1013 * See also mnt_context_disable_helpers().
1014 *
1015 * WARNING: non-zero return code does not mean that umount(2) syscall or
1016 * umount.type helper wasn't successfully called.
1017 *
1018 * Check mnt_context_get_status() after error!
1019 *
1020 * Returns: 0 on success;
1021 * >0 in case of umount(2) error (returns syscall errno),
1022 * <0 in case of other errors.
1023 */
1024 int mnt_context_do_umount(struct libmnt_context *cxt)
1025 {
1026 int rc;
1027 struct libmnt_ns *ns_old;
1028
1029 assert(cxt);
1030 assert(cxt->fs);
1031 assert(cxt->helper_exec_status == 1);
1032 assert(cxt->syscall_status == 1);
1033 assert((cxt->flags & MNT_FL_PREPARED));
1034 assert((cxt->action == MNT_ACT_UMOUNT));
1035 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
1036
1037 ns_old = mnt_context_switch_target_ns(cxt);
1038 if (!ns_old)
1039 return -MNT_ERR_NAMESPACE;
1040
1041 rc = do_umount(cxt);
1042 if (rc)
1043 goto end;
1044
1045 if (mnt_context_get_status(cxt) && !mnt_context_is_fake(cxt)) {
1046 /*
1047 * Umounted, do some post-umount operations
1048 * - remove loopdev
1049 * - refresh in-memory utab stuff if remount rather than
1050 * umount has been performed
1051 */
1052 if (mnt_context_is_loopdel(cxt)
1053 && !mnt_optlist_is_remount(cxt->optlist))
1054 rc = mnt_context_delete_loopdev(cxt);
1055 }
1056 end:
1057 if (!mnt_context_switch_ns(cxt, ns_old))
1058 return -MNT_ERR_NAMESPACE;
1059
1060 return rc;
1061 }
1062
1063 /**
1064 * mnt_context_finalize_umount:
1065 * @cxt: context
1066 *
1067 * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called
1068 * after mnt_context_do_umount(). See also mnt_context_set_syscall_status().
1069 *
1070 * Returns: negative number on error, 0 on success.
1071 */
1072 int mnt_context_finalize_umount(struct libmnt_context *cxt)
1073 {
1074 int rc;
1075
1076 assert(cxt);
1077 assert(cxt->fs);
1078 assert((cxt->flags & MNT_FL_PREPARED));
1079 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
1080
1081 rc = mnt_context_prepare_update(cxt);
1082 if (!rc)
1083 rc = mnt_context_update_tabs(cxt);
1084 return rc;
1085 }
1086
1087
1088 /**
1089 * mnt_context_umount:
1090 * @cxt: umount context
1091 *
1092 * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
1093 *
1094 * This is similar to:
1095 *
1096 * mnt_context_prepare_umount(cxt);
1097 * mnt_context_do_umount(cxt);
1098 * mnt_context_finalize_umount(cxt);
1099 *
1100 * See also mnt_context_disable_helpers().
1101 *
1102 * WARNING: non-zero return code does not mean that umount(2) syscall or
1103 * umount.type helper wasn't successfully called.
1104 *
1105 * Check mnt_context_get_status() after error!
1106 *
1107 * Returns: 0 on success;
1108 * >0 in case of umount(2) error (returns syscall errno),
1109 * <0 in case of other errors.
1110 */
1111 int mnt_context_umount(struct libmnt_context *cxt)
1112 {
1113 int rc;
1114 struct libmnt_ns *ns_old;
1115
1116 assert(cxt);
1117 assert(cxt->fs);
1118 assert(cxt->helper_exec_status == 1);
1119 assert(cxt->syscall_status == 1);
1120
1121 DBG(CXT, ul_debugobj(cxt, "umount: %s", mnt_context_get_target(cxt)));
1122
1123 ns_old = mnt_context_switch_target_ns(cxt);
1124 if (!ns_old)
1125 return -MNT_ERR_NAMESPACE;
1126
1127 rc = mnt_context_prepare_umount(cxt);
1128 if (!rc)
1129 rc = mnt_context_prepare_update(cxt);
1130 if (!rc)
1131 rc = mnt_context_do_umount(cxt);
1132 if (!rc)
1133 rc = mnt_context_update_tabs(cxt);
1134
1135 if (!mnt_context_switch_ns(cxt, ns_old))
1136 return -MNT_ERR_NAMESPACE;
1137
1138 return rc;
1139 }
1140
1141
1142 /**
1143 * mnt_context_next_umount:
1144 * @cxt: context
1145 * @itr: iterator
1146 * @fs: returns the current filesystem
1147 * @mntrc: returns the return code from mnt_context_umount()
1148 * @ignored: returns 1 for not matching
1149 *
1150 * This function tries to umount the next filesystem from mountinfo file.
1151 *
1152 * You can filter out filesystems by:
1153 * mnt_context_set_options_pattern() to simulate umount -a -O pattern
1154 * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern
1155 *
1156 * If the filesystem is not mounted or does not match the defined criteria,
1157 * then the function mnt_context_next_umount() returns zero, but the @ignored is
1158 * non-zero. Note that the root filesystem is always ignored.
1159 *
1160 * If umount(2) syscall or umount.type helper failed, then the
1161 * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero.
1162 * Use also mnt_context_get_status() to check if the filesystem was
1163 * successfully umounted.
1164 *
1165 * Returns: 0 on success,
1166 * <0 in case of error (!= umount(2) errors)
1167 * 1 at the end of the list.
1168 */
1169 int mnt_context_next_umount(struct libmnt_context *cxt,
1170 struct libmnt_iter *itr,
1171 struct libmnt_fs **fs,
1172 int *mntrc,
1173 int *ignored)
1174 {
1175 struct libmnt_table *mountinfo;
1176 const char *tgt;
1177 int rc;
1178
1179 if (ignored)
1180 *ignored = 0;
1181 if (mntrc)
1182 *mntrc = 0;
1183
1184 if (!cxt || !fs || !itr)
1185 return -EINVAL;
1186
1187 rc = mnt_context_get_mountinfo(cxt, &mountinfo);
1188 cxt->mountinfo = NULL; /* do not reset mountinfo */
1189 mnt_reset_context(cxt);
1190
1191 if (rc)
1192 return rc;
1193
1194 cxt->mountinfo = mountinfo;
1195
1196 do {
1197 rc = mnt_table_next_fs(mountinfo, itr, fs);
1198 if (rc != 0)
1199 return rc; /* no more filesystems (or error) */
1200
1201 tgt = mnt_fs_get_target(*fs);
1202 } while (!tgt);
1203
1204 DBG(CXT, ul_debugobj(cxt, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt,
1205 mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern));
1206
1207 /* ignore filesystems which don't match options patterns */
1208 if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
1209 cxt->fstype_pattern)) ||
1210
1211 /* ignore filesystems which don't match type patterns */
1212 (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
1213 cxt->optstr_pattern))) {
1214 if (ignored)
1215 *ignored = 1;
1216
1217 DBG(CXT, ul_debugobj(cxt, "next-umount: not-match"));
1218 return 0;
1219 }
1220
1221 rc = mnt_context_set_fs(cxt, *fs);
1222 if (rc)
1223 return rc;
1224 rc = mnt_context_umount(cxt);
1225 if (mntrc)
1226 *mntrc = rc;
1227 return 0;
1228 }
1229
1230
1231 int mnt_context_get_umount_excode(
1232 struct libmnt_context *cxt,
1233 int rc,
1234 char *buf,
1235 size_t bufsz)
1236 {
1237 if (mnt_context_helper_executed(cxt))
1238 /*
1239 * /sbin/umount.<type> called, return status
1240 */
1241 return mnt_context_get_helper_status(cxt);
1242
1243 if (rc == 0 && mnt_context_get_status(cxt) == 1)
1244 /*
1245 * Libmount success && syscall success.
1246 */
1247 return MNT_EX_SUCCESS;
1248
1249 if (!mnt_context_syscall_called(cxt)) {
1250 /*
1251 * libmount errors (extra library checks)
1252 */
1253 if (rc == -EPERM && !mnt_context_tab_applied(cxt)) {
1254 /* failed to evaluate permissions because not found
1255 * relevant entry in mountinfo */
1256 if (buf)
1257 snprintf(buf, bufsz, _("not mounted"));
1258 return MNT_EX_USAGE;
1259 }
1260
1261 if (rc == -MNT_ERR_LOCK) {
1262 if (buf)
1263 snprintf(buf, bufsz, _("locking failed"));
1264 return MNT_EX_FILEIO;
1265 }
1266
1267 if (rc == -MNT_ERR_NAMESPACE) {
1268 if (buf)
1269 snprintf(buf, bufsz, _("failed to switch namespace"));
1270 return MNT_EX_SYSERR;
1271 }
1272 return mnt_context_get_generic_excode(rc, buf, bufsz,
1273 _("umount failed: %m"));
1274
1275 } if (mnt_context_get_syscall_errno(cxt) == 0) {
1276 /*
1277 * umount(2) syscall success, but something else failed
1278 * (probably error in utab processing).
1279 */
1280 if (rc == -MNT_ERR_LOCK) {
1281 if (buf)
1282 snprintf(buf, bufsz, _("filesystem was unmounted, but failed to update userspace mount table"));
1283 return MNT_EX_FILEIO;
1284 }
1285
1286 if (rc == -MNT_ERR_NAMESPACE) {
1287 if (buf)
1288 snprintf(buf, bufsz, _("filesystem was unmounted, but failed to switch namespace back"));
1289 return MNT_EX_SYSERR;
1290
1291 }
1292
1293 if (rc < 0)
1294 return mnt_context_get_generic_excode(rc, buf, bufsz,
1295 _("filesystem was unmounted, but any subsequent operation failed: %m"));
1296
1297 return MNT_EX_SOFTWARE; /* internal error */
1298 }
1299
1300 /*
1301 * umount(2) errors
1302 */
1303 if (buf) {
1304 int syserr = mnt_context_get_syscall_errno(cxt);
1305
1306 switch (syserr) {
1307 case ENXIO:
1308 snprintf(buf, bufsz, _("invalid block device")); /* ??? */
1309 break;
1310 case EINVAL:
1311 snprintf(buf, bufsz, _("not mounted"));
1312 break;
1313 case EIO:
1314 snprintf(buf, bufsz, _("can't write superblock"));
1315 break;
1316 case EBUSY:
1317 snprintf(buf, bufsz, _("target is busy"));
1318 break;
1319 case ENOENT:
1320 snprintf(buf, bufsz, _("no mount point specified"));
1321 break;
1322 case EPERM:
1323 snprintf(buf, bufsz, _("must be superuser to unmount"));
1324 break;
1325 case EACCES:
1326 snprintf(buf, bufsz, _("block devices are not permitted on filesystem"));
1327 break;
1328 default:
1329 return mnt_context_get_generic_excode(syserr, buf, bufsz,_("umount(2) system call failed: %m"));
1330 }
1331 }
1332 return MNT_EX_FAIL;
1333 }