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