]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/context_umount.c
libmount: cleanup API and docs
[thirdparty/util-linux.git] / shlibs / mount / 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
8#include <stdio.h>
9#include <stdlib.h>
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <unistd.h>
13#include <string.h>
14#include <errno.h>
15
16#include <sys/wait.h>
17#include <sys/mount.h>
18
19#include "c.h"
20#include "pathnames.h"
21#include "strutils.h"
22#include "mountP.h"
23
24static int lookup_umount_fs(mnt_context *cxt)
25{
26 int rc;
27 const char *tgt;
28 mnt_tab *mtab;
29 mnt_fs *fs;
30
1b56aae8
KZ
31 assert(cxt);
32 assert(cxt->fs);
33
21193a48
KZ
34 DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS"));
35
ea8f06f9
KZ
36 tgt = mnt_fs_get_target(cxt->fs);
37 if (!tgt) {
38 DBG(CXT, mnt_debug_h(cxt, "umount: undefined target"));
39 return -EINVAL;
40 }
41 rc = mnt_context_get_mtab(cxt, &mtab);
42 if (rc) {
43 DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab"));
44 return rc;
45 }
46 fs = mnt_tab_find_target(mtab, tgt, MNT_ITER_BACKWARD);
47 if (!fs) {
48 /* maybe the option is source rather than target (mountpoint) */
49 fs = mnt_tab_find_source(mtab, tgt, MNT_ITER_BACKWARD);
50
51 if (fs) {
52 mnt_fs *fs1 = mnt_tab_find_target(mtab,
53 mnt_fs_get_target(fs),
54 MNT_ITER_BACKWARD);
55 if (!fs1) {
56 DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!"));
57 return -EINVAL;
58 }
59 if (fs != fs1) {
60 /* Something was stacked over `file' on the
61 * same mount point. */
62 DBG(CXT, mnt_debug_h(cxt,
63 "umount: %s: %s is mounted "
64 "over it on the same point",
65 tgt, mnt_fs_get_source(fs1)));
66 return -EINVAL;
67 }
68 }
69 }
70
71 if (!fs) {
72 DBG(CXT, mnt_debug_h(cxt, "cannot found %s in mtab", tgt));
73 return 0;
74 }
75
76 /* copy from mtab/fstab to our FS description
77 */
78 rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs));
79 if (!rc)
80 rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs));
81
82 if (!rc && !mnt_fs_get_fstype(cxt->fs))
83 rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
76a06ca4
KZ
84
85 if (!rc)
86 rc = mnt_fs_set_vfs_options(cxt->fs, mnt_fs_get_vfs_options(fs));
ea8f06f9 87 if (!rc)
76a06ca4
KZ
88 rc = mnt_fs_set_fs_options(cxt->fs, mnt_fs_get_fs_options(fs));
89 if (!rc)
90 rc = mnt_fs_set_userspace_options(cxt->fs, mnt_fs_get_userspace_options(fs));
91
dd369652
KZ
92 if (!rc && mnt_fs_get_bindsrc(fs))
93 rc = mnt_fs_set_bindsrc(cxt->fs, mnt_fs_get_bindsrc(fs));
ea8f06f9 94
dd369652 95 DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied"));
ea8f06f9
KZ
96 cxt->flags |= MNT_FL_TAB_APPLIED;
97 return rc;
98}
99
100/* check if @devname is loopdev and if the device is associated
101 * with a source from @fstab_fs
102 *
103 * TODO : move this to loopdev.c
104 */
105static int mnt_loopdev_associated_fs(const char *devname, mnt_fs *fs)
106{
107 uintmax_t offset = 0;
108 const char *src;
109 char *val, *optstr;
110 size_t valsz;
111
112 /* check if it begins with /dev/loop */
113 if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP)))
114 return 0;
115
116 src = mnt_fs_get_srcpath(fs);
117 if (!src)
118 return 0;
119
120 /* check for offset option in @fs */
76a06ca4 121 optstr = (char *) mnt_fs_get_userspace_options(fs);
ea8f06f9
KZ
122 if (optstr && !mnt_optstr_get_option(optstr, "offset=", &val, &valsz)) {
123 int rc;
124
125 val = strndup(val, valsz);
126 if (!val)
127 return 0;
128 rc = strtosize(val, &offset);
129 free(val);
130 if (rc)
131 return 0;
132 }
133
134 /* TODO:
135 * if (mnt_loopdev_associated_file(devname, src, offset))
136 * return 1;
137 */
138 return 0;
139}
140
141/*
142 * Note that cxt->fs contains relevant mtab entry!
143 */
144static int evaluate_permissions(mnt_context *cxt)
145{
146 mnt_tab *fstab;
766af80b 147 unsigned long u_flags = 0;
ea8f06f9
KZ
148 const char *tgt, *src, *optstr;
149 int rc, ok = 0;
150 mnt_fs *fs;
151
1b56aae8
KZ
152 assert(cxt);
153 assert(cxt->fs);
154 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
155
ea8f06f9
KZ
156 if (!cxt || !cxt->fs)
157 return -EINVAL;
158
159 if (!mnt_context_is_restricted(cxt))
160 return 0; /* superuser mount */
161
dd369652
KZ
162 DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions"));
163
ea8f06f9
KZ
164 if (!(cxt->flags & MNT_FL_TAB_APPLIED)) {
165 DBG(CXT, mnt_debug_h(cxt,
166 "cannot found %s in mtab and you are not root",
167 mnt_fs_get_target(cxt->fs)));
168 goto eperm;
169 }
170
1b56aae8
KZ
171 if (!(cxt->flags & MNT_FL_NOHELPERS) &&
172 (cxt->user_mountflags & MNT_MS_UHELPER)) {
ea8f06f9 173
ea8f06f9 174 char *suffix = NULL;
76a06ca4 175 char *o = (char *) mnt_fs_get_userspace_options(cxt->fs);
ea8f06f9
KZ
176 size_t valsz;
177
178 rc = mnt_optstr_get_option(o, "uhelper", &suffix, &valsz);
179 if (!rc) {
180 suffix = strndup(suffix, valsz);
181 if (!suffix)
182 return -ENOMEM;
183 rc = mnt_context_prepare_helper(cxt, "umount", suffix);
184 }
185 if (rc < 0)
186 return rc;
187 if (cxt->helper)
188 return 0; /* we'll call /sbin/umount.<uhelper> */
189 }
190
191 /*
192 * User mounts has to be in /etc/fstab
193 */
194 rc = mnt_context_get_fstab(cxt, &fstab);
195 if (rc)
196 return rc;
197
198 tgt = mnt_fs_get_target(cxt->fs);
199 src = mnt_fs_get_source(cxt->fs);
200
dd369652
KZ
201 if (mnt_fs_get_bindsrc(cxt->fs)) {
202 src = mnt_fs_get_bindsrc(cxt->fs);
203 DBG(CXT, mnt_debug_h(cxt,
204 "umount: using bind source: %s", src));
205 }
206
ea8f06f9
KZ
207 /* If fstab contains the two lines
208 * /dev/sda1 /mnt/zip auto user,noauto 0 0
209 * /dev/sda4 /mnt/zip auto user,noauto 0 0
210 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
211 * So, we must not look for file, but for the pair (dev,file) in fstab.
212 */
213 fs = mnt_tab_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
214 if (!fs) {
215 /*
216 * It's possible that there is /path/file.img in fstab and
217 * /dev/loop0 in mtab -- then we have to check releation
218 * between loopdev and the file.
219 */
220 fs = mnt_tab_find_target(fstab, tgt, MNT_ITER_FORWARD);
221 if (fs) {
222 const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */
223
224 if (!dev || !mnt_loopdev_associated_fs(dev, fs))
225 fs = NULL;
226 }
227 if (!fs) {
228 DBG(CXT, mnt_debug_h(cxt,
229 "umount %s: mtab disagrees with fstab",
230 tgt));
231 goto eperm;
232 }
233 }
234
235 /*
236 * User mounting and unmounting is allowed only if fstab contains one
237 * of the options `user', `users' or `owner' or `group'.
238 *
239 * The option `users' allows arbitrary users to mount and unmount -
240 * this may be a security risk.
241 *
242 * The options `user', `owner' and `group' only allow unmounting by the
243 * user that mounted (visible in mtab).
244 */
76a06ca4 245 optstr = mnt_fs_get_userspace_options(fs); /* FSTAB mount options! */
ea8f06f9
KZ
246 if (!optstr)
247 goto eperm;
248
76a06ca4
KZ
249 if (mnt_optstr_get_flags(optstr, &u_flags,
250 mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
ea8f06f9
KZ
251 goto eperm;
252
766af80b
KZ
253 if (u_flags & MNT_MS_USERS) {
254 DBG(CXT, mnt_debug_h(cxt,
255 "umount: promiscuous setting ('users') in fstab"));
ea8f06f9 256 return 0;
766af80b 257 }
ea8f06f9
KZ
258 /*
259 * Check user=<username> setting from mtab if there is user, owner or
260 * group option in /etc/fstab
261 */
262 if ((u_flags & MNT_MS_USER) || (u_flags & MNT_MS_OWNER) ||
263 (u_flags & MNT_MS_GROUP)) {
264
766af80b 265 char *curr_user = NULL;
ea8f06f9
KZ
266 char *mtab_user = NULL;
267 size_t sz;
268
766af80b
KZ
269 DBG(CXT, mnt_debug_h(cxt,
270 "umount: checking user=<username> from mtab"));
271
272 curr_user = mnt_get_username(getuid());
273
ea8f06f9
KZ
274 if (!curr_user) {
275 DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot "
766af80b 276 "convert %d to username", tgt, getuid()));
ea8f06f9
KZ
277 goto eperm;
278 }
279
280 /* get options from mtab */
76a06ca4 281 optstr = mnt_fs_get_userspace_options(cxt->fs);
ea8f06f9
KZ
282 if (optstr && !mnt_optstr_get_option((char *) optstr,
283 "user", &mtab_user, &sz) && sz)
284 ok = !strncmp(curr_user, mtab_user, sz);
285 }
286
287 if (ok) {
288 DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt));
289 return 0;
290 }
291eperm:
292 DBG(CXT, mnt_debug_h(cxt, "umount %s is not allowed for you", tgt));
293 return -EPERM;
294}
295
296static int exec_helper(mnt_context *cxt)
297{
298 int rc;
299
300 assert(cxt);
301 assert(cxt->fs);
302 assert(cxt->helper);
1b56aae8 303 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
97e23b5e 304 assert(cxt->helper_exec_status == 1);
ea8f06f9
KZ
305
306 DBG_FLUSH;
307
308 switch (fork()) {
309 case 0:
310 {
311 const char *args[10], *type;
312 int i = 0;
313
314 if (setgid(getgid()) < 0)
315 exit(EXIT_FAILURE);
316
317 if (setuid(getuid()) < 0)
318 exit(EXIT_FAILURE);
319
320 type = mnt_fs_get_fstype(cxt->fs);
321
322 args[i++] = cxt->helper; /* 1 */
323 args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */
324
325 if (cxt->flags & MNT_FL_NOMTAB)
326 args[i++] = "-n"; /* 3 */
327 if (cxt->flags & MNT_FL_LAZY)
328 args[i++] = "-l"; /* 4 */
329 if (cxt->flags & MNT_FL_FORCE)
330 args[i++] = "-f"; /* 5 */
331 if (cxt->flags & MNT_FL_VERBOSE)
332 args[i++] = "-v"; /* 6 */
333 if (cxt->flags & MNT_FL_RDONLY_UMOUNT)
334 args[i++] = "-r"; /* 7 */
335 if (type && !endswith(cxt->helper, type)) {
336 args[i++] = "-t"; /* 8 */
337 args[i++] = (char *) type; /* 9 */
338 }
339
340 args[i] = NULL; /* 10 */
341#ifdef CONFIG_LIBMOUNT_DEBUG
342 i = 0;
343 for (i = 0; args[i]; i++)
344 DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
345 i, args[i]));
346#endif
347 DBG_FLUSH;
348 execv(cxt->helper, (char * const *) args);
349 exit(EXIT_FAILURE);
350 }
351 default:
352 {
353 int st;
354 wait(&st);
355 cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
356
357 DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
358 cxt->helper, cxt->helper_status));
97e23b5e 359 cxt->helper_exec_status = rc = 0;
ea8f06f9
KZ
360 break;
361 }
362
363 case -1:
97e23b5e 364 cxt->helper_exec_status = rc = -errno;
ea8f06f9
KZ
365 DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
366 break;
367 }
368
369 return rc;
370}
371
372static int do_umount(mnt_context *cxt)
373{
374 int rc = 0;
375 const char *src, *target;
376
377 assert(cxt);
378 assert(cxt->fs);
1b56aae8 379 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
97e23b5e 380 assert(cxt->syscall_status == 1);
ea8f06f9
KZ
381
382 if (cxt->helper)
383 return exec_helper(cxt);
384
385 src = mnt_fs_get_srcpath(cxt->fs);
386 target = mnt_fs_get_target(cxt->fs);
387
388 if (!target)
389 return -EINVAL;
390
391 if (cxt->flags & MNT_FL_FAKE)
392 return 0;
393
394 if (cxt->flags & MNT_FL_LAZY)
395 rc = umount2(target, MNT_DETACH);
396
397 else if (cxt->flags & MNT_FL_FORCE) {
398 rc = umount2(target, MNT_FORCE);
399
400 if (rc < 0 && errno == ENOSYS)
401 rc = umount(target);
402 } else
403 rc = umount(target);
404
405 if (rc < 0)
97e23b5e 406 cxt->syscall_status = -errno;
ea8f06f9
KZ
407 /*
408 * try remount read-only
409 */
97e23b5e 410 if (rc < 0 && cxt->syscall_status == -EBUSY &&
ea8f06f9
KZ
411 (cxt->flags & MNT_FL_RDONLY_UMOUNT) && src) {
412
413 cxt->mountflags |= MS_REMOUNT | MS_RDONLY;
414 cxt->flags &= ~MNT_FL_LOOPDEL;
415 DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d] -- "
416 "tring remount read-only",
97e23b5e 417 -cxt->syscall_status));
ea8f06f9
KZ
418
419 rc = mount(src, target, NULL,
420 MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
421 if (rc < 0) {
97e23b5e 422 cxt->syscall_status = -errno;
ea8f06f9
KZ
423 DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) failed "
424 "[errno=%d]",
97e23b5e
KZ
425 -cxt->syscall_status));
426 return cxt->syscall_status;
ea8f06f9 427 }
97e23b5e 428 cxt->syscall_status = 0;
ea8f06f9
KZ
429 DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success"));
430 return 0;
431 }
432
433 if (rc < 0) {
434 DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]",
97e23b5e
KZ
435 -cxt->syscall_status));
436 return -cxt->syscall_status;
ea8f06f9 437 }
21193a48 438 cxt->syscall_status = 0;
ea8f06f9
KZ
439 DBG(CXT, mnt_debug_h(cxt, "umount(2) success"));
440 return 0;
441}
442
443/**
1d0cd73f 444 * mnt_context_do_umount:
ea8f06f9
KZ
445 * @cxt: mount context
446 *
0f32f1e2 447 * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
ea8f06f9 448 *
1d0cd73f 449 * See also mnt_context_disable_helpers().
ea8f06f9
KZ
450 *
451 * Returns: 0 on success, and negative number in case of error.
452 */
1d0cd73f 453int mnt_context_do_umount(mnt_context *cxt)
ea8f06f9 454{
1d0cd73f 455 int rc;
ea8f06f9 456
1d0cd73f 457 if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
ea8f06f9 458 return -EINVAL;
1d0cd73f 459 if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
ea8f06f9
KZ
460 return -EINVAL;
461
462 free(cxt->helper); /* be paranoid */
463 cxt->helper = NULL;
464
1d0cd73f
KZ
465 cxt->action = MNT_ACT_UMOUNT;
466
ea8f06f9 467 rc = lookup_umount_fs(cxt);
1b56aae8
KZ
468 if (!rc)
469 rc = mnt_context_merge_mountflags(cxt);
ea8f06f9
KZ
470 if (!rc)
471 rc = evaluate_permissions(cxt);
472 if (!rc && !cxt->helper)
473 rc = mnt_context_prepare_helper(cxt, "umount", NULL);
474/* TODO
475 if ((cxt->flags & MNT_FL_LOOPDEL) &&
476 (!mnt_is_loopdev(src) || mnt_loopdev_is_autoclear(src)))
477 cxt->flags &= ~MNT_FL_LOOPDEL;
478*/
479 if (!rc)
1d0cd73f
KZ
480 rc = mnt_context_prepare_update(cxt);
481 if (rc) {
482 DBG(CXT, mnt_debug_h(cxt, "prepared umount failed"));
483 return rc;
ea8f06f9
KZ
484 }
485
1d0cd73f
KZ
486 rc = do_umount(cxt);
487 if (rc)
488 return rc;
ea8f06f9
KZ
489/* TODO
490 if (cxt->flags & MNT_FL_LOOPDEL)
491 rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs));
492*/
493 if (cxt->flags & MNT_FL_NOMTAB)
494 return rc;
495
496 if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) &&
497 (cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) {
498 /*
36bda5cb 499 * remount --> read-only mount
ea8f06f9 500 */
76a06ca4 501 const char *o = mnt_fs_get_vfs_options(cxt->fs);
1d0cd73f 502 char *n = o ? strdup(o) : NULL;
ea8f06f9 503
21193a48
KZ
504 DBG(CXT, mnt_debug_h(cxt, "fix remount-on-umount update"));
505
1d0cd73f
KZ
506 if (n)
507 mnt_optstr_remove_option(&n, "rw");
508 rc = mnt_optstr_prepend_option(&n, "ro", NULL);
ea8f06f9 509 if (!rc)
76a06ca4 510 rc = mnt_fs_set_vfs_options(cxt->fs, n);
1d0cd73f 511
36bda5cb 512 /* use "remount" instead of "umount" in /etc/mtab */
77417bc0 513 if (!rc && cxt->update && cxt->mtab_writable)
1d0cd73f
KZ
514 rc = mnt_update_set_fs(cxt->update,
515 cxt->mountflags, NULL, cxt->fs);
ea8f06f9 516 }
ea8f06f9 517
1d0cd73f 518 return rc ? : mnt_context_update_tabs(cxt);
ea8f06f9 519}