]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: use chdir() and NOFOLLOW umount flag for umount operation
authorKarel Zak <kzak@redhat.com>
Wed, 30 Mar 2011 11:00:03 +0000 (13:00 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 30 Mar 2011 11:00:03 +0000 (13:00 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/context_mount.c
shlibs/mount/src/context_umount.c
shlibs/mount/src/mountP.h
shlibs/mount/src/utils.c

index eaba33c051e56a473116b978174364b07af1f3a1..58c3ec10ed7073dae7f6597d0bca3cf8650e9a0b 100644 (file)
@@ -392,7 +392,7 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type)
 
        DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
                        "[source=%s, target=%s, type=%s, "
-                       " mountflags=%08lx, mountdata=%s]",
+                       " mountflags=0x%08lx, mountdata=%s]",
                        (cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "",
                        src, target, type,
                        flags, cxt->mountdata ? "yes" : "<none>"));
index cdf91985446d208c01006b3366c542ccc5c38f48..0e69fe5f9faa2a80e0e9a68852b1aee11d747767 100644 (file)
@@ -424,10 +424,25 @@ int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
        return rc;
 }
 
+/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
+static int umount_nofollow_support(void)
+{
+       int res = umount2("", UMOUNT_UNUSED);
+       if (res != -1 || errno != EINVAL)
+               return 0;
+
+       res = umount2("", UMOUNT_NOFOLLOW);
+       if (res != -1 || errno != ENOENT)
+               return 0;
+
+       return 1;
+}
+
 static int do_umount(struct libmnt_context *cxt)
 {
        int rc = 0, flags = 0;
        const char *src, *target;
+       char *tgtbuf = NULL;
 
        assert(cxt);
        assert(cxt->fs);
@@ -446,16 +461,38 @@ static int do_umount(struct libmnt_context *cxt)
        if (cxt->flags & MNT_FL_FAKE)
                return 0;
 
+       DBG(CXT, mnt_debug_h(cxt, "do umount"));
+
+       if (cxt->restricted) {
+               /*
+                * extra paranoa for non-root users
+                * -- chdir to the parent of the mountpoint and use NOFOLLOW
+                *    flag to avoid races and symlink attacks.
+                */
+               if (umount_nofollow_support())
+                       flags |= UMOUNT_NOFOLLOW;
+
+               rc = mnt_chdir_to_parent(target, &tgtbuf);
+               if (rc)
+                       return rc;
+               target = tgtbuf;
+       }
+
        if (cxt->flags & MNT_FL_LAZY)
                flags |= MNT_DETACH;
 
        else if (cxt->flags & MNT_FL_FORCE)
                flags |= MNT_FORCE;
 
-       rc = flasg ? umount2(target, flags) : umount(target);
+       DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]",
+                               target, flags));
+
+       rc = flags ? umount2(target, flags) : umount(target);
        if (rc < 0)
                cxt->syscall_status = -errno;
 
+       free(tgtbuf);
+
        /*
         * try remount read-only
         */
@@ -468,7 +505,7 @@ static int do_umount(struct libmnt_context *cxt)
                        "umount(2) failed [errno=%d] -- tring remount read-only",
                        -cxt->syscall_status));
 
-               rc = mount(src, target, NULL,
+               rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
                            MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
                if (rc < 0) {
                        cxt->syscall_status = -errno;
index 0d773ba0a2b58fecb0fa45aa75c2a7f15f6e8788..abaa6b2c667b73c38dfab7fc25b494793203b438 100644 (file)
@@ -111,6 +111,7 @@ extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]);
 extern int endswith(const char *s, const char *sx);
 extern int startswith(const char *s, const char *sx);
 
+extern int mnt_chdir_to_parent(const char *target, char **filename);
 extern char *mnt_get_username(const uid_t uid);
 extern int mnt_get_uid(const char *username, uid_t *uid);
 extern int mnt_get_gid(const char *groupname, gid_t *gid);
index b02b6fe4d2d0d85afb0d3fb263d90888099f2bc9..1261a2934a268a10fff29eeda707a17fa62765f5 100644 (file)
@@ -71,6 +71,58 @@ static char *stripoff_last_component(char *path)
        return ++p;
 }
 
+/* Note that the @target has to be absolute path (so at least "/")
+ */
+int mnt_chdir_to_parent(const char *target, char **filename)
+{
+       char *path, *last = NULL;
+       char cwd[PATH_MAX];
+       int rc = -EINVAL;
+
+       if (!target || *target != '/')
+               return -EINVAL;
+
+       path = strdup(target);
+       if (!path)
+               return -ENOMEM;
+
+       if (*(path + 1) != '\0') {
+               last = stripoff_last_component(path);
+               if (!last)
+                       goto err;
+       }
+       if (!*path)
+               *path = '/';    /* root */
+
+       if (chdir(path) == -1) {
+               DBG(UTILS, mnt_debug("failed to chdir to %s: %m", path));
+               rc = -errno;
+               goto err;
+       }
+       if (!getcwd(cwd, sizeof(cwd))) {
+               DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
+               rc = -errno;
+               goto err;
+       }
+       if (strcmp(cwd, path) != 0) {
+               DBG(UTILS, mnt_debug("path moved (%s -> %s)", path, cwd));
+               goto err;
+       }
+
+       DBG(CXT, mnt_debug("current directory moved to %s", path));
+
+       *filename = path;
+
+       if (!last || !*last)
+               memcpy(*filename, ".", 2);
+       else
+               memcpy(*filename, last, strlen(last) + 1);
+       return 0;
+err:
+       free(path);
+       return rc;
+}
+
 /**
  * mnt_mangle:
  * @str: string
@@ -800,6 +852,26 @@ int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
        return rc;
 }
 
+int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
+{
+       int rc;
+       char *path = canonicalize_path(argv[1]),
+            *last = NULL;
+
+       if (!path)
+               return -errno;
+
+       rc = mnt_chdir_to_parent(path, &last);
+       if (!rc) {
+               printf("path='%s', abs='%s', last='%s'\n",
+                               argv[1], path, last);
+       }
+       free(path);
+       free(last);
+       return rc;
+}
+
+
 int main(int argc, char *argv[])
 {
        struct libmnt_test tss[] = {
@@ -810,6 +882,7 @@ int main(int argc, char *argv[])
        { "--ends-with",     test_endswith,        "<string> <prefix>" },
        { "--mountpoint",    test_mountpoint,      "<path>" },
        { "--fs-root",       test_fsroot,          "<path>" },
+       { "--cd-parent",     test_chdir,           "<path>" },
        { NULL }
        };