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