]> err.no Git - util-linux/commitdiff
libmount: add umount(2) support
authorKarel Zak <kzak@redhat.com>
Tue, 12 Oct 2010 13:04:28 +0000 (15:04 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 3 Jan 2011 11:28:45 +0000 (12:28 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/Makefile.am
shlibs/mount/src/context.c
shlibs/mount/src/context_mount.c
shlibs/mount/src/context_umount.c [new file with mode: 0644]
shlibs/mount/src/mount.h.in
shlibs/mount/src/mountP.h

index f1db1ad1b7a35d792d8c34ad7ceb3ca8a1d542e3..820ab8bc03456ba0cd945d81c062dc6c55691e90 100644 (file)
@@ -12,7 +12,7 @@ usrlib_exec_LTLIBRARIES = libmount.la
 libmount_la_SOURCES =  mountP.h version.c utils.c test.c init.c cache.c \
                        optstr.c optmap.c iter.c lock.c \
                        fs.c tab.c tab_parse.c tab_update.c \
-                       context.c context_mount.c \
+                       context.c context_mount.c context_umount.c \
                        $(mountinc_HEADERS) \
                        $(top_srcdir)/lib/at.c \
                        $(top_srcdir)/include/list.h \
index bf364a4986fdec6fb5039c18a9e815269ef1eda5..08d4e1e86af2dc7e1ba217b13a9bfba5c7a56074 100644 (file)
@@ -217,6 +217,21 @@ int mnt_context_enable_lazy(mnt_context *cxt, int enable)
        return set_flag(cxt, MNT_FL_LAZY, enable);
 }
 
+/**
+ * mnt_context_enable_rdonly_umount:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable read-only remount on failed umount(2)
+ * (see umount(8) man page, option -r).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_rdonly_umount(mnt_context *cxt, int enable)
+{
+       return set_flag(cxt, MNT_FL_RDONLY_UMOUNT, enable);
+}
+
 /**
  * mnt_context_disable_helpers:
  * @cxt: mount context
@@ -568,7 +583,7 @@ int mnt_context_get_mtab(mnt_context *cxt, mnt_tab **tb)
                int rc;
 
                cxt->mtab = mnt_new_tab();
-               if (!cxt->fstab)
+               if (!cxt->mtab)
                        return -ENOMEM;
                rc = mnt_tab_parse_mtab(cxt->mtab);
                if (rc)
@@ -980,6 +995,11 @@ int mnt_context_prepare_helper(mnt_context *cxt, const char *name,
 int mnt_context_prepare_update(mnt_context *cxt, int act)
 {
        int rc;
+       const char *tgt = cxt->fs ? mnt_fs_get_target(cxt->fs) : NULL;
+
+       if (act == MNT_ACT_UMOUNT && tgt && !strcmp(tgt, "/"))
+               /* Don't try to touch mtab if umounting root FS */
+               cxt->flags |= MNT_FL_NOMTAB;
 
        if ((cxt->flags & MNT_FL_NOMTAB) || cxt->helper)
                return 0;
@@ -1131,3 +1151,146 @@ err:
        DBG(CXT, mnt_debug_h(cxt, "failed to found entry in fstab/mtab"));
        return rc;
 }
+
+
+#ifdef TEST_PROGRAM
+
+mnt_lock *lock;
+
+static void lock_fallback(void)
+{
+       if (lock) {
+               mnt_unlock_file(lock);
+               mnt_free_lock(lock);
+       }
+}
+
+int test_mount(struct mtest *ts, int argc, char *argv[])
+{
+       int idx = 1, rc = 0;
+       mnt_context *cxt;
+
+       if (argc < 2)
+               return -EINVAL;
+
+       cxt = mnt_new_context();
+       if (!cxt)
+               return -ENOMEM;
+
+       if (!strcmp(argv[idx], "-o")) {
+               mnt_context_set_optstr(cxt, argv[idx + 1]);
+               idx += 2;
+       }
+       if (!strcmp(argv[idx], "-t")) {
+               /* TODO: use mnt_context_set_fstype_pattern() */
+               mnt_context_set_fstype(cxt, argv[idx + 1]);
+               idx += 2;
+       }
+
+       if (argc == idx + 1)
+               /* mount <mountpont>|<device> */
+               mnt_context_set_target(cxt, argv[idx++]);
+
+       else if (argc == idx + 2) {
+               /* mount <device> <mountpoint> */
+               mnt_context_set_source(cxt, argv[idx++]);
+               mnt_context_set_target(cxt, argv[idx++]);
+       }
+
+       rc = mnt_context_prepare_mount(cxt);
+       if (rc)
+               printf("failed to prepare mount\n");
+       else {
+               lock = mnt_context_get_lock(cxt);
+               if (lock)
+                       atexit(lock_fallback);
+
+               rc = mnt_context_do_mount(cxt);
+               if (rc)
+                       printf("failed to mount\n");
+               else {
+                       printf("successfully mounted");
+                       rc = mnt_context_post_mount(cxt);
+                       if (rc)
+                               printf("mtab update failed\n");
+               }
+       }
+
+       mnt_free_context(cxt);
+       return rc;
+}
+
+int test_umount(struct mtest *ts, int argc, char *argv[])
+{
+       int idx = 1, rc = 0;
+       mnt_context *cxt;
+
+       if (argc < 2)
+               return -EINVAL;
+
+       cxt = mnt_new_context();
+       if (!cxt)
+               return -ENOMEM;
+
+       if (!strcmp(argv[idx], "-t")) {
+               mnt_context_set_fstype(cxt, argv[idx + 1]);
+               idx += 2;
+       }
+
+       if (!strcmp(argv[idx], "-f")) {
+               mnt_context_enable_force(cxt, TRUE);
+               idx++;
+       }
+
+       if (!strcmp(argv[idx], "-l")) {
+               mnt_context_enable_lazy(cxt, TRUE);
+               idx++;
+       }
+
+       if (!strcmp(argv[idx], "-r")) {
+               mnt_context_enable_rdonly_umount(cxt, TRUE);
+               idx++;
+       }
+
+       if (argc == idx + 1) {
+               /* mount <mountpont>|<device> */
+               mnt_context_set_target(cxt, argv[idx++]);
+       } else {
+               rc = -EINVAL;
+               goto err;
+       }
+
+       rc = mnt_context_prepare_umount(cxt);
+       if (rc)
+               printf("failed to prepare umount\n");
+       else {
+               lock = mnt_context_get_lock(cxt);
+               if (lock)
+                       atexit(lock_fallback);
+
+               rc = mnt_context_do_umount(cxt);
+               if (rc)
+                       printf("failed to umount\n");
+               else {
+                       printf("successfully umounted");
+                       rc = mnt_context_post_umount(cxt);
+                       if (rc)
+                               printf("mtab update failed\n");
+               }
+       }
+err:
+       mnt_free_context(cxt);
+       return rc;
+}
+
+int main(int argc, char *argv[])
+{
+       struct mtest tss[] = {
+       { "--mount",  test_mount,  "[-o <opts>] [-t <type>] <spec>|<src> <target>" },
+       { "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" },
+       { NULL }};
+
+       return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
index a09eee936ddae4a039044f3e9a2fd4c74f6fc347..4bfdb267c397246907c1782c03d4015cd907a14c 100644 (file)
@@ -246,7 +246,7 @@ static int exec_helper(mnt_context *cxt)
 
        rc = generate_helper_optstr(cxt, &o);
        if (rc)
-               goto done;
+               return -EINVAL;
 
        DBG_FLUSH;
 
@@ -313,8 +313,6 @@ static int exec_helper(mnt_context *cxt)
                break;
        }
 
-done:
-       free(o);
        return rc;
 }
 
@@ -351,20 +349,22 @@ static int do_mount(mnt_context *cxt, const char *try_type)
        if (!(flags & MS_MGC_MSK))
                flags |= MS_MGC_VAL;
 
-       DBG(CXT, mnt_debug_h(cxt, "calling mount(2) "
+       DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
                        "[source=%s, target=%s, type=%s, "
                        " mountflags=%08lx, mountdata=%s]",
+                       (cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "",
                        src, target, type,
                        flags, cxt->mountdata ? "yes" : "<none>"));
 
-       if (mount(src, target, type, flags, cxt->mountdata)) {
-               cxt->syscall_errno = errno;
-               DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d]",
-                                               cxt->syscall_errno));
-               return -cxt->syscall_errno;
+       if (!(cxt->flags & MNT_FL_FAKE)) {
+               if (mount(src, target, type, flags, cxt->mountdata)) {
+                       cxt->syscall_errno = errno;
+                       DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d]",
+                                                       cxt->syscall_errno));
+                       return -cxt->syscall_errno;
+               }
+               DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
        }
-
-       DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
        return 0;
 }
 
@@ -502,81 +502,3 @@ int mnt_context_mount_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
        return 0;
 }
 
-#ifdef TEST_PROGRAM
-
-mnt_lock *lock;
-
-static void lock_fallback(void)
-{
-       if (lock) {
-               mnt_unlock_file(lock);
-               mnt_free_lock(lock);
-       }
-}
-
-int test_mount(struct mtest *ts, int argc, char *argv[])
-{
-       int idx = 1, rc = 0;
-       mnt_context *cxt;
-
-       if (argc < 2)
-               return -EINVAL;
-
-       cxt = mnt_new_context();
-       if (!cxt)
-               return -ENOMEM;
-
-       if (!strcmp(argv[idx], "-o")) {
-               mnt_context_set_optstr(cxt, argv[idx + 1]);
-               idx += 2;
-       }
-       if (!strcmp(argv[idx], "-t")) {
-               /* TODO: use mnt_context_set_fstype_pattern() */
-               mnt_context_set_fstype(cxt, argv[idx + 1]);
-               idx += 2;
-       }
-
-       if (argc == idx + 1)
-               /* mount <mountpont>|<device> */
-               mnt_context_set_target(cxt, argv[idx++]);
-
-       else if (argc == idx + 2) {
-               /* mount <device> <mountpoint> */
-               mnt_context_set_source(cxt, argv[idx++]);
-               mnt_context_set_target(cxt, argv[idx++]);
-       }
-
-       rc = mnt_context_prepare_mount(cxt);
-       if (rc)
-               printf("failed to prepare mount\n");
-       else {
-               lock = mnt_context_get_lock(cxt);
-               if (lock)
-                       atexit(lock_fallback);
-
-               rc = mnt_context_do_mount(cxt);
-               if (rc)
-                       printf("failed to mount\n");
-               else {
-                       printf("successfully mounted");
-                       rc = mnt_context_post_mount(cxt);
-                       if (rc)
-                               printf("mtab update failed\n");
-               }
-       }
-
-       mnt_free_context(cxt);
-       return rc;
-}
-
-int main(int argc, char *argv[])
-{
-       struct mtest tss[] = {
-       { "--mount",    test_mount,        "[-o <opts>] [-t <type>] <spec> | <src> <target>" },
-       { NULL }
-       };
-
-       return mnt_run_test(tss, argc, argv);
-}
-
-#endif /* TEST_PROGRAM */
diff --git a/shlibs/mount/src/context_umount.c b/shlibs/mount/src/context_umount.c
new file mode 100644 (file)
index 0000000..1222fe6
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "c.h"
+#include "pathnames.h"
+#include "strutils.h"
+#include "mountP.h"
+
+static int lookup_umount_fs(mnt_context *cxt)
+{
+       int rc;
+       const char *tgt;
+       mnt_tab *mtab;
+       mnt_fs *fs;
+
+       tgt = mnt_fs_get_target(cxt->fs);
+       if (!tgt) {
+               DBG(CXT, mnt_debug_h(cxt, "umount: undefined target"));
+               return -EINVAL;
+       }
+       rc = mnt_context_get_mtab(cxt, &mtab);
+       if (rc) {
+               DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab"));
+               return rc;
+       }
+       fs = mnt_tab_find_target(mtab, tgt, MNT_ITER_BACKWARD);
+       if (!fs) {
+               /* maybe the option is source rather than target (mountpoint) */
+               fs = mnt_tab_find_source(mtab, tgt, MNT_ITER_BACKWARD);
+
+               if (fs) {
+                       mnt_fs *fs1 = mnt_tab_find_target(mtab,
+                                                       mnt_fs_get_target(fs),
+                                                       MNT_ITER_BACKWARD);
+                       if (!fs1) {
+                               DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!"));
+                               return -EINVAL;
+                       }
+                       if (fs != fs1) {
+                               /* Something was stacked over `file' on the
+                                * same mount point. */
+                               DBG(CXT, mnt_debug_h(cxt,
+                                               "umount: %s: %s is mounted "
+                                               "over it on the same point",
+                                               tgt, mnt_fs_get_source(fs1)));
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       if (!fs) {
+               DBG(CXT, mnt_debug_h(cxt, "cannot found %s in mtab", tgt));
+               return 0;
+       }
+
+       /* copy from mtab/fstab to our FS description
+        */
+       rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs));
+       if (!rc)
+               rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs));
+
+       if (!rc && !mnt_fs_get_fstype(cxt->fs))
+               rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
+       if (!rc)
+               rc = mnt_fs_set_optstr(cxt->fs, mnt_fs_get_optstr(fs));
+
+       cxt->flags |= MNT_FL_TAB_APPLIED;
+       return rc;
+}
+
+/* check if @devname is loopdev and if the device is associated
+ * with a source from @fstab_fs
+ *
+ * TODO : move this to loopdev.c
+ */
+static int mnt_loopdev_associated_fs(const char *devname, mnt_fs *fs)
+{
+       uintmax_t offset = 0;
+       const char *src;
+       char *val, *optstr;
+       size_t valsz;
+
+       /* check if it begins with /dev/loop */
+       if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP)))
+               return 0;
+
+       src = mnt_fs_get_srcpath(fs);
+       if (!src)
+               return 0;
+
+       /* check for offset option in @fs */
+       optstr = (char *) mnt_fs_get_optstr(fs);
+       if (optstr && !mnt_optstr_get_option(optstr, "offset=", &val, &valsz)) {
+               int rc;
+
+               val = strndup(val, valsz);
+               if (!val)
+                       return 0;
+               rc = strtosize(val, &offset);
+               free(val);
+               if (rc)
+                       return 0;
+       }
+
+       /* TODO:
+        * if (mnt_loopdev_associated_file(devname, src, offset))
+        *      return 1;
+        */
+       return 0;
+}
+
+/*
+ * Note that cxt->fs contains relevant mtab entry!
+ */
+static int evaluate_permissions(mnt_context *cxt)
+{
+       mnt_tab *fstab;
+       unsigned long u_flags;
+       const char *tgt, *src, *optstr;
+       int rc, ok = 0;
+       mnt_fs *fs;
+
+       if (!cxt || !cxt->fs)
+               return -EINVAL;
+
+       if (!mnt_context_is_restricted(cxt))
+                return 0;              /* superuser mount */
+
+       if (!(cxt->flags & MNT_FL_TAB_APPLIED)) {
+               DBG(CXT, mnt_debug_h(cxt,
+                               "cannot found %s in mtab and you are not root",
+                               mnt_fs_get_target(cxt->fs)));
+               goto eperm;
+       }
+
+       mnt_context_get_userspace_mountflags(cxt, &u_flags);
+
+       if (!(cxt->flags & MNT_FL_NOHELPERS) && (u_flags & MNT_MS_UHELPER)) {
+               char *suffix = NULL;
+               char *o = (char *) mnt_fs_get_optstr(cxt->fs);
+               size_t valsz;
+
+               rc = mnt_optstr_get_option(o, "uhelper", &suffix, &valsz);
+               if (!rc) {
+                       suffix = strndup(suffix, valsz);
+                       if (!suffix)
+                               return -ENOMEM;
+                       rc = mnt_context_prepare_helper(cxt, "umount", suffix);
+               }
+               if (rc < 0)
+                       return rc;
+               if (cxt->helper)
+                       return 0;       /* we'll call /sbin/umount.<uhelper> */
+       }
+
+       /*
+        * User mounts has to be in /etc/fstab
+        */
+       rc = mnt_context_get_fstab(cxt, &fstab);
+       if (rc)
+               return rc;
+
+       tgt = mnt_fs_get_target(cxt->fs);
+       src = mnt_fs_get_source(cxt->fs);
+
+       /* If fstab contains the two lines
+        *      /dev/sda1 /mnt/zip auto user,noauto  0 0
+        *      /dev/sda4 /mnt/zip auto user,noauto  0 0
+        * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
+        * So, we must not look for file, but for the pair (dev,file) in fstab.
+         */
+       fs = mnt_tab_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
+       if (!fs) {
+               /*
+                * It's possible that there is /path/file.img in fstab and
+                * /dev/loop0 in mtab -- then we have to check releation
+                * between loopdev and the file.
+                */
+               fs = mnt_tab_find_target(fstab, tgt, MNT_ITER_FORWARD);
+               if (fs) {
+                       const char *dev = mnt_fs_get_srcpath(cxt->fs);          /* devname from mtab */
+
+                       if (!dev || !mnt_loopdev_associated_fs(dev, fs))
+                               fs = NULL;
+               }
+               if (!fs) {
+                       DBG(CXT, mnt_debug_h(cxt,
+                                       "umount %s: mtab disagrees with fstab",
+                                       tgt));
+                       goto eperm;
+               }
+       }
+
+       /*
+        * User mounting and unmounting is allowed only if fstab contains one
+        * of the options `user', `users' or `owner' or `group'.
+        *
+        * The option `users' allows arbitrary users to mount and unmount -
+        * this may be a security risk.
+        *
+        * The options `user', `owner' and `group' only allow unmounting by the
+        * user that mounted (visible in mtab).
+        */
+       optstr = mnt_fs_get_optstr(fs);
+       if (!optstr)
+               goto eperm;
+
+       if (mnt_optstr_get_userspace_mountflags(optstr, &u_flags))
+               goto eperm;
+
+       if (u_flags & MNT_MS_USERS)
+               /* promiscuous setting in fstab */
+               return 0;
+       /*
+        * Check user=<username> setting from mtab if there is user, owner or
+        * group option in /etc/fstab
+        */
+       if ((u_flags & MNT_MS_USER) || (u_flags & MNT_MS_OWNER) ||
+           (u_flags & MNT_MS_GROUP)) {
+
+               char *curr_user = mnt_get_username(getuid());
+               char *mtab_user = NULL;
+               size_t sz;
+
+               if (!curr_user) {
+                       DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot "
+                                       "convert %d to username",
+                                       tgt, getuid()));
+                       goto eperm;
+               }
+
+               /* get options from mtab */
+               optstr = mnt_fs_get_optstr(cxt->fs);
+               if (optstr && !mnt_optstr_get_option((char *) optstr,
+                                       "user", &mtab_user, &sz) && sz)
+                       ok = !strncmp(curr_user, mtab_user, sz);
+       }
+
+       if (ok) {
+               DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt));
+               return 0;
+       }
+eperm:
+       DBG(CXT, mnt_debug_h(cxt, "umount %s is not allowed for you", tgt));
+       return -EPERM;
+}
+
+static int exec_helper(mnt_context *cxt)
+{
+       int rc;
+
+       assert(cxt);
+       assert(cxt->fs);
+       assert(cxt->helper);
+
+       DBG_FLUSH;
+
+       switch (fork()) {
+       case 0:
+       {
+               const char *args[10], *type;
+               int i = 0;
+
+               if (setgid(getgid()) < 0)
+                       exit(EXIT_FAILURE);
+
+               if (setuid(getuid()) < 0)
+                       exit(EXIT_FAILURE);
+
+               type = mnt_fs_get_fstype(cxt->fs);
+
+               args[i++] = cxt->helper;                        /* 1 */
+               args[i++] = mnt_fs_get_target(cxt->fs);         /* 2 */
+
+               if (cxt->flags & MNT_FL_NOMTAB)
+                       args[i++] = "-n";                       /* 3 */
+               if (cxt->flags & MNT_FL_LAZY)
+                       args[i++] = "-l";                       /* 4 */
+               if (cxt->flags & MNT_FL_FORCE)
+                       args[i++] = "-f";                       /* 5 */
+               if (cxt->flags & MNT_FL_VERBOSE)
+                       args[i++] = "-v";                       /* 6 */
+               if (cxt->flags & MNT_FL_RDONLY_UMOUNT)
+                       args[i++] = "-r";                       /* 7 */
+               if (type && !endswith(cxt->helper, type)) {
+                       args[i++] = "-t";                       /* 8 */
+                       args[i++] = (char *) type;      /* 9 */
+               }
+
+               args[i] = NULL;                                 /* 10 */
+#ifdef CONFIG_LIBMOUNT_DEBUG
+               i = 0;
+               for (i = 0; args[i]; i++)
+                       DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
+                                                       i, args[i]));
+#endif
+               DBG_FLUSH;
+               execv(cxt->helper, (char * const *) args);
+               exit(EXIT_FAILURE);
+       }
+       default:
+       {
+               int st;
+               wait(&st);
+               cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
+
+               DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
+                                       cxt->helper, cxt->helper_status));
+               rc = 0;
+               break;
+       }
+
+       case -1:
+               rc = -errno;
+               DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
+               break;
+       }
+
+       return rc;
+}
+
+static int do_umount(mnt_context *cxt)
+{
+       int rc = 0;
+       const char *src, *target;
+
+       assert(cxt);
+       assert(cxt->fs);
+
+       if (cxt->helper)
+               return exec_helper(cxt);
+
+       src = mnt_fs_get_srcpath(cxt->fs);
+       target = mnt_fs_get_target(cxt->fs);
+
+       if (!target)
+               return -EINVAL;
+
+       if (cxt->flags & MNT_FL_FAKE)
+               return 0;
+
+       if (cxt->flags & MNT_FL_LAZY)
+               rc = umount2(target, MNT_DETACH);
+
+       else if (cxt->flags & MNT_FL_FORCE) {
+               rc = umount2(target, MNT_FORCE);
+
+               if (rc < 0 && errno == ENOSYS)
+                       rc = umount(target);
+       } else
+               rc = umount(target);
+
+       if (rc < 0)
+               cxt->syscall_errno = errno;
+
+       /*
+        * try remount read-only
+        */
+       if (rc < 0 && cxt->syscall_errno == EBUSY &&
+           (cxt->flags & MNT_FL_RDONLY_UMOUNT) && src) {
+
+               cxt->mountflags |= MS_REMOUNT | MS_RDONLY;
+               cxt->flags &= ~MNT_FL_LOOPDEL;
+               DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d] -- "
+                                       "tring remount read-only",
+                                       cxt->syscall_errno));
+
+               rc = mount(src, target, NULL,
+                           MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+               if (rc < 0) {
+                       cxt->syscall_errno = errno;
+                       DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) failed "
+                                       "[errno=%d]",
+                                       cxt->syscall_errno));
+                       return -cxt->syscall_errno;
+               }
+               cxt->syscall_errno = 0;
+               DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success"));
+               return 0;
+       }
+
+       if (rc < 0) {
+               DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]",
+                                       cxt->syscall_errno));
+               return -cxt->syscall_errno;
+       }
+       DBG(CXT, mnt_debug_h(cxt, "umount(2) success"));
+       return 0;
+}
+
+/**
+ * mnt_context_prepare_umount:
+ * @cxt: mount context
+ *
+ * This function:
+ *     - read information from fstab/mtab (if necessary)
+ *     - check premissions
+ *     - prepare for mtab update (if necessary)
+ *
+ * It's strongly recommended to use this function before mnt_context_do_umount().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_prepare_umount(mnt_context *cxt)
+{
+       int rc = 0;
+
+       if (!cxt)
+               return -EINVAL;
+
+       if (!cxt->fs || (!mnt_fs_get_source(cxt->fs) &&
+                        !mnt_fs_get_target(cxt->fs)))
+               return -EINVAL;
+
+       free(cxt->helper);      /* be paranoid */
+       cxt->helper = NULL;
+
+       rc = lookup_umount_fs(cxt);
+       if (!rc)
+               rc = evaluate_permissions(cxt);
+       if (!rc && !cxt->helper)
+               rc = mnt_context_prepare_helper(cxt, "umount", NULL);
+/* TODO
+       if ((cxt->flags & MNT_FL_LOOPDEL) &&
+           (!mnt_is_loopdev(src) || mnt_loopdev_is_autoclear(src)))
+               cxt->flags &= ~MNT_FL_LOOPDEL;
+*/
+       if (!rc)
+               rc = mnt_context_prepare_update(cxt, MNT_ACT_UMOUNT);
+       if (!rc) {
+               DBG(CXT, mnt_debug_h(cxt, "umount sucessfully prepared"));
+               return 0;
+       }
+
+       DBG(CXT, mnt_debug_h(cxt, "umount prepare failed"));
+       return rc;
+}
+
+/**
+ * mnt_context_do_umount:
+ * @cxt: mount context
+ *
+ * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.<type>).
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_do_umount(mnt_context *cxt)
+{
+       if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
+               return -EINVAL;
+
+       return do_umount(cxt);
+}
+
+/**
+ * mnt_context_post_umount:
+ * @cxt: mount context
+ *
+ * Updates mtab and detroy loopdev etc. This function should be always called after
+ * mnt_context_do_umount().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_post_umount(mnt_context *cxt)
+{
+       int rc = 0;
+
+       if (!cxt)
+               return -EINVAL;
+       if (cxt->syscall_errno || cxt->helper)
+               return 0;
+/* TODO
+       if (cxt->flags & MNT_FL_LOOPDEL)
+               rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs));
+*/
+       if (cxt->flags & MNT_FL_NOMTAB)
+               return rc;
+
+       if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) &&
+           (cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) {
+               /*
+                * refresh update to handle remount to read-only
+                */
+               rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT);
+               if (rc)
+                       return rc;
+       }
+
+       /*
+        * Update /etc/mtab or /var/run/mount/mountinfo
+        */
+       if (cxt->update && !mnt_update_is_pointless(cxt->update)) {
+               rc = mnt_update_file(cxt->update);
+               if (!rc)
+                       return rc;
+       }
+       return rc;
+}
+
+/**
+ * mnt_context_umount_strerror
+ * @cxt: mount context
+ * @buf: buffer
+ * @bufsiz: size of the buffer
+ *
+ * Returns: 0 or negative number in case of error.
+ */
+int mnt_context_umount_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
+{
+       /* TODO: based on cxt->syscall_errno or cxt->helper_status */
+       return 0;
+}
index 43f81080d2677b15d93eecda7e606b2dd8ae63ab..f4b4e6ad4c85ff5cd64e75d74c43eb3d623ffb42 100644 (file)
@@ -326,6 +326,7 @@ extern int mnt_context_disable_lock(mnt_context *cxt, int disable);
 extern int mnt_context_enable_force(mnt_context *cxt, int enable);
 extern int mnt_context_enable_verbose(mnt_context *cxt, int enable);
 extern int mnt_context_enable_loopdel(mnt_context *cxt, int enable);
+extern int mnt_context_enable_rdonly_umount(mnt_context *cxt, int enable);
 extern int mnt_context_set_fs(mnt_context *cxt, mnt_fs *fs);
 extern int mnt_context_set_source(mnt_context *cxt, const char *source);
 extern int mnt_context_set_target(mnt_context *cxt, const char *target);
@@ -335,6 +336,8 @@ extern int mnt_context_append_optstr(mnt_context *cxt, const char *optstr);
 extern int mnt_context_set_fstype_pattern(mnt_context *cxt, const char *pattern);
 extern int mnt_context_set_optstr_pattern(mnt_context *cxt, const char *pattern);
 extern int mnt_context_set_fstab(mnt_context *cxt, mnt_tab *tb);
+extern int mnt_context_get_fstab(mnt_context *cxt, mnt_tab **tb);
+extern int mnt_context_get_mtab(mnt_context *cxt, mnt_tab **tb);
 extern int mnt_context_set_cache(mnt_context *cxt, mnt_cache *cache);
 extern mnt_cache *mnt_context_get_cache(mnt_context *cxt);
 extern mnt_lock *mnt_context_get_lock(mnt_context *cxt);
index 96490b16056093da88b9e62672baafde29ed5d8d..9f05cf555feef4a62374b7ca912dc5e456d6a124 100644 (file)
@@ -250,6 +250,7 @@ struct _mnt_context
 #define MNT_FL_FORCE           (1 << 8)
 #define MNT_FL_NOCANONICALIZE  (1 << 9)
 #define MNT_FL_NOLOCK          (1 << 10)       /* don't lock mtab file */
+#define MNT_FL_RDONLY_UMOUNT   (1 << 11)       /* remount,ro after EBUSY umount(2) */
 
 #define MNT_FL_EXTERN_FS       (1 << 15)       /* cxt->fs is not private */
 #define MNT_FL_EXTERN_FSTAB    (1 << 16)       /* cxt->fstab is not private */