2
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
4
* This file may be redistributed under the terms of the
5
* GNU Lesser General Public License.
9
* SECTION: context-umount
10
* @title: Umount context
11
* @short_description: high-level API to umount operation.
15
#include <sys/mount.h>
17
#include "pathnames.h"
25
# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
29
# define MNT_DETACH 0x00000002 /* Just detach from the tree */
32
#ifndef UMOUNT_NOFOLLOW
33
# define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
37
# define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
41
static int lookup_umount_fs(struct libmnt_context *cxt)
45
struct libmnt_table *mtab = NULL;
51
DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS"));
53
tgt = mnt_fs_get_target(cxt->fs);
55
DBG(CXT, mnt_debug_h(cxt, "umount: undefined target"));
58
rc = mnt_context_get_mtab(cxt, &mtab);
60
DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab"));
63
fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD);
65
/* maybe the option is source rather than target (mountpoint) */
66
fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD);
69
struct libmnt_fs *fs1 = mnt_table_find_target(mtab,
70
mnt_fs_get_target(fs),
73
DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!"));
77
/* Something was stacked over `file' on the
78
* same mount point. */
79
DBG(CXT, mnt_debug_h(cxt,
80
"umount: %s: %s is mounted "
81
"over it on the same point",
82
tgt, mnt_fs_get_source(fs1)));
89
DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt));
93
/* copy from mtab to our FS description
95
mnt_fs_set_source(cxt->fs, NULL);
96
mnt_fs_set_target(cxt->fs, NULL);
98
if (!mnt_copy_fs(cxt->fs, fs)) {
99
DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS"));
103
DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied"));
104
cxt->flags |= MNT_FL_TAB_APPLIED;
108
/* check if @devname is loopdev and if the device is associated
109
* with a source from @fstab_fs
111
* TODO : move this to loopdev.c
113
static int mnt_loopdev_associated_fs(const char *devname, struct libmnt_fs *fs)
115
uintmax_t offset = 0;
120
/* check if it begins with /dev/loop */
121
if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP)))
124
src = mnt_fs_get_srcpath(fs);
128
/* check for offset option in @fs */
129
optstr = (char *) mnt_fs_get_user_options(fs);
130
if (optstr && !mnt_optstr_get_option(optstr, "offset=", &val, &valsz)) {
133
val = strndup(val, valsz);
136
rc = strtosize(val, &offset);
143
* if (mnt_loopdev_associated_file(devname, src, offset))
149
static int prepare_helper_from_options(struct libmnt_context *cxt,
156
if (cxt->flags & MNT_FL_NOHELPERS)
159
opts = mnt_fs_get_user_options(cxt->fs);
163
if (mnt_optstr_get_option(opts, name, &suffix, &valsz))
166
suffix = strndup(suffix, valsz);
170
DBG(CXT, mnt_debug_h(cxt, "umount: umount.%s %s requested", suffix, name));
172
return mnt_context_prepare_helper(cxt, "umount", suffix);
176
* Note that cxt->fs contains relevant mtab entry!
178
static int evaluate_permissions(struct libmnt_context *cxt)
180
struct libmnt_table *fstab;
181
unsigned long u_flags = 0;
182
const char *tgt, *src, *optstr;
184
struct libmnt_fs *fs;
188
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
190
if (!cxt || !cxt->fs)
193
if (!mnt_context_is_restricted(cxt))
194
return 0; /* superuser mount */
196
DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions"));
198
if (!(cxt->flags & MNT_FL_TAB_APPLIED)) {
199
DBG(CXT, mnt_debug_h(cxt,
200
"cannot find %s in mtab and you are not root",
201
mnt_fs_get_target(cxt->fs)));
205
if (cxt->user_mountflags & MNT_MS_UHELPER) {
206
/* on uhelper= mount option based helper */
207
rc = prepare_helper_from_options(cxt, "uhelper");
211
return 0; /* we'll call /sbin/umount.<uhelper> */
215
* User mounts has to be in /etc/fstab
217
rc = mnt_context_get_fstab(cxt, &fstab);
221
tgt = mnt_fs_get_target(cxt->fs);
222
src = mnt_fs_get_source(cxt->fs);
224
if (mnt_fs_get_bindsrc(cxt->fs)) {
225
src = mnt_fs_get_bindsrc(cxt->fs);
226
DBG(CXT, mnt_debug_h(cxt,
227
"umount: using bind source: %s", src));
230
/* If fstab contains the two lines
231
* /dev/sda1 /mnt/zip auto user,noauto 0 0
232
* /dev/sda4 /mnt/zip auto user,noauto 0 0
233
* then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
234
* So, we must not look for file, but for the pair (dev,file) in fstab.
236
fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
239
* It's possible that there is /path/file.img in fstab and
240
* /dev/loop0 in mtab -- then we have to check releation
241
* between loopdev and the file.
243
fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
245
const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */
247
if (!dev || !mnt_loopdev_associated_fs(dev, fs))
251
DBG(CXT, mnt_debug_h(cxt,
252
"umount %s: mtab disagrees with fstab",
259
* User mounting and unmounting is allowed only if fstab contains one
260
* of the options `user', `users' or `owner' or `group'.
262
* The option `users' allows arbitrary users to mount and unmount -
263
* this may be a security risk.
265
* The options `user', `owner' and `group' only allow unmounting by the
266
* user that mounted (visible in mtab).
268
optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */
272
if (mnt_optstr_get_flags(optstr, &u_flags,
273
mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
276
if (u_flags & MNT_MS_USERS) {
277
DBG(CXT, mnt_debug_h(cxt,
278
"umount: promiscuous setting ('users') in fstab"));
282
* Check user=<username> setting from mtab if there is user, owner or
283
* group option in /etc/fstab
285
if ((u_flags & MNT_MS_USER) || (u_flags & MNT_MS_OWNER) ||
286
(u_flags & MNT_MS_GROUP)) {
288
char *curr_user = NULL;
289
char *mtab_user = NULL;
292
DBG(CXT, mnt_debug_h(cxt,
293
"umount: checking user=<username> from mtab"));
295
curr_user = mnt_get_username(getuid());
298
DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot "
299
"convert %d to username", tgt, getuid()));
303
/* get options from mtab */
304
optstr = mnt_fs_get_user_options(cxt->fs);
305
if (optstr && !mnt_optstr_get_option(optstr,
306
"user", &mtab_user, &sz) && sz)
307
ok = !strncmp(curr_user, mtab_user, sz);
311
DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt));
315
DBG(CXT, mnt_debug_h(cxt, "umount is not allowed for you"));
319
static int exec_helper(struct libmnt_context *cxt)
326
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
327
assert(cxt->helper_exec_status == 1);
334
const char *args[10], *type;
337
if (setgid(getgid()) < 0)
340
if (setuid(getuid()) < 0)
343
type = mnt_fs_get_fstype(cxt->fs);
345
args[i++] = cxt->helper; /* 1 */
346
args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */
348
if (cxt->flags & MNT_FL_NOMTAB)
349
args[i++] = "-n"; /* 3 */
350
if (cxt->flags & MNT_FL_LAZY)
351
args[i++] = "-l"; /* 4 */
352
if (cxt->flags & MNT_FL_FORCE)
353
args[i++] = "-f"; /* 5 */
354
if (cxt->flags & MNT_FL_VERBOSE)
355
args[i++] = "-v"; /* 6 */
356
if (cxt->flags & MNT_FL_RDONLY_UMOUNT)
357
args[i++] = "-r"; /* 7 */
358
if (type && !endswith(cxt->helper, type)) {
359
args[i++] = "-t"; /* 8 */
360
args[i++] = (char *) type; /* 9 */
363
args[i] = NULL; /* 10 */
364
#ifdef CONFIG_LIBMOUNT_DEBUG
366
for (i = 0; args[i]; i++)
367
DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
371
execv(cxt->helper, (char * const *) args);
378
cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
380
DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
381
cxt->helper, cxt->helper_status));
382
cxt->helper_exec_status = rc = 0;
387
cxt->helper_exec_status = rc = -errno;
388
DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
396
* mnt_context_helper_setopt() backend.
398
* This function applies umount.<type> command line option (for example parsed
399
* by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
400
* then 1 is returned.
402
* Returns: negative number on error, 1 if @c is unknown option, 0 on success.
404
int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
409
assert(cxt->action == MNT_ACT_UMOUNT);
413
rc = mnt_context_disable_mtab(cxt, TRUE);
416
rc = mnt_context_enable_lazy(cxt, TRUE);
419
rc = mnt_context_enable_force(cxt, TRUE);
422
rc = mnt_context_enable_verbose(cxt, TRUE);
425
rc = mnt_context_enable_rdonly_umount(cxt, TRUE);
429
rc = mnt_context_set_fstype(cxt, arg);
439
/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
440
static int umount_nofollow_support(void)
442
int res = umount2("", UMOUNT_UNUSED);
443
if (res != -1 || errno != EINVAL)
446
res = umount2("", UMOUNT_NOFOLLOW);
447
if (res != -1 || errno != ENOENT)
453
static int do_umount(struct libmnt_context *cxt)
455
int rc = 0, flags = 0;
456
const char *src, *target;
461
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
462
assert(cxt->syscall_status == 1);
465
return exec_helper(cxt);
467
src = mnt_fs_get_srcpath(cxt->fs);
468
target = mnt_fs_get_target(cxt->fs);
473
if (cxt->flags & MNT_FL_FAKE)
476
DBG(CXT, mnt_debug_h(cxt, "do umount"));
478
if (cxt->restricted) {
480
* extra paranoa for non-root users
481
* -- chdir to the parent of the mountpoint and use NOFOLLOW
482
* flag to avoid races and symlink attacks.
484
if (umount_nofollow_support())
485
flags |= UMOUNT_NOFOLLOW;
487
rc = mnt_chdir_to_parent(target, &tgtbuf);
493
if (cxt->flags & MNT_FL_LAZY)
496
else if (cxt->flags & MNT_FL_FORCE)
499
DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]",
502
rc = flags ? umount2(target, flags) : umount(target);
504
cxt->syscall_status = -errno;
509
* try remount read-only
511
if (rc < 0 && cxt->syscall_status == -EBUSY &&
512
(cxt->flags & MNT_FL_RDONLY_UMOUNT) && src) {
514
cxt->mountflags |= MS_REMOUNT | MS_RDONLY;
515
cxt->flags &= ~MNT_FL_LOOPDEL;
516
DBG(CXT, mnt_debug_h(cxt,
517
"umount(2) failed [errno=%d] -- tring remount read-only",
518
-cxt->syscall_status));
520
rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
521
MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
523
cxt->syscall_status = -errno;
524
DBG(CXT, mnt_debug_h(cxt,
525
"read-only re-mount(2) failed [errno=%d]",
526
-cxt->syscall_status));
528
return -cxt->syscall_status;
530
cxt->syscall_status = 0;
531
DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success"));
536
DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]",
537
-cxt->syscall_status));
538
return -cxt->syscall_status;
541
cxt->syscall_status = 0;
542
DBG(CXT, mnt_debug_h(cxt, "umount(2) success"));
547
* mnt_context_prepare_umount:
548
* @cxt: mount context
550
* Prepare context for umounting, unnecessary for mnt_context_umount().
552
* Returns: 0 on success, and negative number in case of error.
554
int mnt_context_prepare_umount(struct libmnt_context *cxt)
560
assert(cxt->helper_exec_status == 1);
561
assert(cxt->syscall_status == 1);
563
if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
565
if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
567
if (cxt->flags & MNT_FL_PREPARED)
570
free(cxt->helper); /* be paranoid */
572
cxt->action = MNT_ACT_UMOUNT;
574
rc = lookup_umount_fs(cxt);
576
rc = mnt_context_merge_mflags(cxt);
578
rc = evaluate_permissions(cxt);
580
rc = mnt_context_prepare_target(cxt);
582
if (!rc && !cxt->helper) {
584
if (cxt->user_mountflags & MNT_MS_HELPER)
585
/* on helper= mount option based helper */
586
rc = prepare_helper_from_options(cxt, "helper");
588
if (!rc && !cxt->helper)
589
/* on fstype based helper */
590
rc = mnt_context_prepare_helper(cxt, "umount", NULL);
594
if ((cxt->flags & MNT_FL_LOOPDEL) &&
595
(!mnt_is_loopdev(src) || mnt_loopdev_is_autoclear(src)))
596
cxt->flags &= ~MNT_FL_LOOPDEL;
599
DBG(CXT, mnt_debug_h(cxt, "umount: preparing failed"));
602
cxt->flags |= MNT_FL_PREPARED;
607
* mnt_context_do_umount:
608
* @cxt: mount context
610
* Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
611
* Unnecessary for mnt_context_umount().
613
* See also mnt_context_disable_helpers().
615
* WARNING: non-zero return code does not mean that umount(2) syscall or
616
* umount.type helper wasn't sucessfully called.
618
* Check mnt_context_get_status() after error!
620
* Returns: 0 on success;
621
* >0 in case of umount(2) error (returns syscall errno),
622
* <0 in case of other errors.
624
int mnt_context_do_umount(struct libmnt_context *cxt)
630
assert(cxt->helper_exec_status == 1);
631
assert(cxt->syscall_status == 1);
632
assert((cxt->flags & MNT_FL_PREPARED));
633
assert((cxt->action == MNT_ACT_UMOUNT));
634
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
640
if (cxt->flags & MNT_FL_LOOPDEL)
641
rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs));
643
if (cxt->flags & MNT_FL_NOMTAB)
646
if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) &&
647
(cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) {
649
* fix options, remount --> read-only mount
651
const char *o = mnt_fs_get_options(cxt->fs);
652
char *n = o ? strdup(o) : NULL;
654
DBG(CXT, mnt_debug_h(cxt, "fix remount-on-umount update"));
657
mnt_optstr_remove_option(&n, "rw");
658
rc = mnt_optstr_prepend_option(&n, "ro", NULL);
660
rc = mnt_fs_set_options(cxt->fs, n);
662
/* use "remount" instead of "umount" in /etc/mtab */
663
if (!rc && cxt->update && cxt->mtab_writable)
664
rc = mnt_update_set_fs(cxt->update,
665
cxt->mountflags, NULL, cxt->fs);
672
* mnt_context_finalize_umount:
675
* Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called
676
* after mnt_context_do_umount(). See also mnt_context_set_syscall_status().
678
* Returns: negative number on error, 0 on success.
680
int mnt_context_finalize_umount(struct libmnt_context *cxt)
686
assert((cxt->flags & MNT_FL_PREPARED));
687
assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
689
rc = mnt_context_prepare_update(cxt);
691
rc = mnt_context_update_tabs(cxt);;
697
* mnt_context_umount:
698
* @cxt: umount context
700
* High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
702
* This is similar to:
704
* mnt_context_prepare_umount(cxt);
705
* mnt_context_do_umount(cxt);
706
* mnt_context_finalize_umount(cxt);
708
* See also mnt_context_disable_helpers().
710
* WARNING: non-zero return code does not mean that umount(2) syscall or
711
* umount.type helper wasn't sucessfully called.
713
* Check mnt_context_get_status() after error!
715
* Returns: 0 on success;
716
* >0 in case of umount(2) error (returns syscall errno),
717
* <0 in case of other errors.
719
int mnt_context_umount(struct libmnt_context *cxt)
725
assert(cxt->helper_exec_status == 1);
726
assert(cxt->syscall_status == 1);
728
rc = mnt_context_prepare_umount(cxt);
730
rc = mnt_context_prepare_update(cxt);
732
rc = mnt_context_do_umount(cxt);
734
rc = mnt_context_update_tabs(cxt);