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