|
Luis Henriques |
8d1ba8 |
From: Ilya Dryomov <idryomov@gmail.com>
|
|
Luis Henriques |
8d1ba8 |
Date: Mon, 10 Feb 2020 22:51:08 +0100
|
|
Luis Henriques |
8d1ba8 |
Subject: ceph: canonicalize server path in place
|
|
Luis Henriques |
8d1ba8 |
Git-commit: b27a939e8376a3f1ed09b9c33ef44d20f18ec3d0
|
|
Luis Henriques |
8d1ba8 |
Patch-mainline: v5.6-rc2
|
|
Luis Henriques |
8d1ba8 |
References: bsc#1168443
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
syzbot reported that 4fbc0c711b24 ("ceph: remove the extra slashes in
|
|
Luis Henriques |
8d1ba8 |
the server path") had caused a regression where an allocation could be
|
|
Luis Henriques |
8d1ba8 |
done under a spinlock -- compare_mount_options() is called by sget_fc()
|
|
Luis Henriques |
8d1ba8 |
with sb_lock held.
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
We don't really need the supplied server path, so canonicalize it
|
|
Luis Henriques |
8d1ba8 |
in place and compare it directly. To make this work, the leading
|
|
Luis Henriques |
8d1ba8 |
slash is kept around and the logic in ceph_real_mount() to skip it
|
|
Luis Henriques |
8d1ba8 |
is restored. CEPH_MSG_CLIENT_SESSION now reports the same (i.e.
|
|
Luis Henriques |
8d1ba8 |
canonicalized) path, with the leading slash of course.
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
Fixes: 4fbc0c711b24 ("ceph: remove the extra slashes in the server path")
|
|
Luis Henriques |
8d1ba8 |
Reported-by: syzbot+98704a51af8e3d9425a9@syzkaller.appspotmail.com
|
|
Luis Henriques |
8d1ba8 |
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
|
|
Luis Henriques |
8d1ba8 |
Reviewed-by: Jeff Layton <jlayton@kernel.org>
|
|
Luis Henriques |
8d1ba8 |
Acked-by: Luis Henriques <lhenriques@suse.com>
|
|
Luis Henriques |
8d1ba8 |
---
|
|
Luis Henriques |
8d1ba8 |
fs/ceph/super.c | 118 ++++++++++++--------------------------------------------
|
|
Luis Henriques |
8d1ba8 |
fs/ceph/super.h | 2
|
|
Luis Henriques |
8d1ba8 |
2 files changed, 28 insertions(+), 92 deletions(-)
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
--- a/fs/ceph/super.c
|
|
Luis Henriques |
8d1ba8 |
+++ b/fs/ceph/super.c
|
|
Luis Henriques |
8d1ba8 |
@@ -124,6 +124,26 @@ static int ceph_sync_fs(struct super_blo
|
|
Luis Henriques |
8d1ba8 |
}
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
/*
|
|
Luis Henriques |
8d1ba8 |
+ * Remove adjacent slashes and then the trailing slash, unless it is
|
|
Luis Henriques |
8d1ba8 |
+ * the only remaining character.
|
|
Luis Henriques |
8d1ba8 |
+ *
|
|
Luis Henriques |
8d1ba8 |
+ * E.g. "//dir1////dir2///" --> "/dir1/dir2", "///" --> "/".
|
|
Luis Henriques |
8d1ba8 |
+ */
|
|
Luis Henriques |
8d1ba8 |
+static void canonicalize_path(char *path)
|
|
Luis Henriques |
8d1ba8 |
+{
|
|
Luis Henriques |
8d1ba8 |
+ int i, j = 0;
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
+ for (i = 0; path[i] != '\0'; i++) {
|
|
Luis Henriques |
8d1ba8 |
+ if (path[i] != '/' || j < 1 || path[j - 1] != '/')
|
|
Luis Henriques |
8d1ba8 |
+ path[j++] = path[i];
|
|
Luis Henriques |
8d1ba8 |
+ }
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
+ if (j > 1 && path[j - 1] == '/')
|
|
Luis Henriques |
8d1ba8 |
+ j--;
|
|
Luis Henriques |
8d1ba8 |
+ path[j] = '\0';
|
|
Luis Henriques |
8d1ba8 |
+}
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
+/*
|
|
Luis Henriques |
8d1ba8 |
* mount options
|
|
Luis Henriques |
8d1ba8 |
*/
|
|
Luis Henriques |
8d1ba8 |
enum {
|
|
Luis Henriques |
8d1ba8 |
@@ -389,73 +409,6 @@ static int strcmp_null(const char *s1, c
|
|
Luis Henriques |
8d1ba8 |
return strcmp(s1, s2);
|
|
Luis Henriques |
8d1ba8 |
}
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
-/**
|
|
Luis Henriques |
8d1ba8 |
- * path_remove_extra_slash - Remove the extra slashes in the server path
|
|
Luis Henriques |
8d1ba8 |
- * @server_path: the server path and could be NULL
|
|
Luis Henriques |
8d1ba8 |
- *
|
|
Luis Henriques |
8d1ba8 |
- * Return NULL if the path is NULL or only consists of "/", or a string
|
|
Luis Henriques |
8d1ba8 |
- * without any extra slashes including the leading slash(es) and the
|
|
Luis Henriques |
8d1ba8 |
- * slash(es) at the end of the server path, such as:
|
|
Luis Henriques |
8d1ba8 |
- * "//dir1////dir2///" --> "dir1/dir2"
|
|
Luis Henriques |
8d1ba8 |
- */
|
|
Luis Henriques |
8d1ba8 |
-static char *path_remove_extra_slash(const char *server_path)
|
|
Luis Henriques |
8d1ba8 |
-{
|
|
Luis Henriques |
8d1ba8 |
- const char *path = server_path;
|
|
Luis Henriques |
8d1ba8 |
- const char *cur, *end;
|
|
Luis Henriques |
8d1ba8 |
- char *buf, *p;
|
|
Luis Henriques |
8d1ba8 |
- int len;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- /* if the server path is omitted */
|
|
Luis Henriques |
8d1ba8 |
- if (!path)
|
|
Luis Henriques |
8d1ba8 |
- return NULL;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- /* remove all the leading slashes */
|
|
Luis Henriques |
8d1ba8 |
- while (*path == '/')
|
|
Luis Henriques |
8d1ba8 |
- path++;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- /* if the server path only consists of slashes */
|
|
Luis Henriques |
8d1ba8 |
- if (*path == '\0')
|
|
Luis Henriques |
8d1ba8 |
- return NULL;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- len = strlen(path);
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- buf = kmalloc(len + 1, GFP_KERNEL);
|
|
Luis Henriques |
8d1ba8 |
- if (!buf)
|
|
Luis Henriques |
8d1ba8 |
- return ERR_PTR(-ENOMEM);
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- end = path + len;
|
|
Luis Henriques |
8d1ba8 |
- p = buf;
|
|
Luis Henriques |
8d1ba8 |
- do {
|
|
Luis Henriques |
8d1ba8 |
- cur = strchr(path, '/');
|
|
Luis Henriques |
8d1ba8 |
- if (!cur)
|
|
Luis Henriques |
8d1ba8 |
- cur = end;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- len = cur - path;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- /* including one '/' */
|
|
Luis Henriques |
8d1ba8 |
- if (cur != end)
|
|
Luis Henriques |
8d1ba8 |
- len += 1;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- memcpy(p, path, len);
|
|
Luis Henriques |
8d1ba8 |
- p += len;
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- while (cur <= end && *cur == '/')
|
|
Luis Henriques |
8d1ba8 |
- cur++;
|
|
Luis Henriques |
8d1ba8 |
- path = cur;
|
|
Luis Henriques |
8d1ba8 |
- } while (path < end);
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- *p = '\0';
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- /*
|
|
Luis Henriques |
8d1ba8 |
- * remove the last slash if there has and just to make sure that
|
|
Luis Henriques |
8d1ba8 |
- * we will get something like "dir1/dir2"
|
|
Luis Henriques |
8d1ba8 |
- */
|
|
Luis Henriques |
8d1ba8 |
- if (*(--p) == '/')
|
|
Luis Henriques |
8d1ba8 |
- *p = '\0';
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
- return buf;
|
|
Luis Henriques |
8d1ba8 |
-}
|
|
Luis Henriques |
8d1ba8 |
-
|
|
Luis Henriques |
8d1ba8 |
static int compare_mount_options(struct ceph_mount_options *new_fsopt,
|
|
Luis Henriques |
8d1ba8 |
struct ceph_options *new_opt,
|
|
Luis Henriques |
8d1ba8 |
struct ceph_fs_client *fsc)
|
|
Luis Henriques |
8d1ba8 |
@@ -463,7 +416,6 @@ static int compare_mount_options(struct
|
|
Luis Henriques |
8d1ba8 |
struct ceph_mount_options *fsopt1 = new_fsopt;
|
|
Luis Henriques |
8d1ba8 |
struct ceph_mount_options *fsopt2 = fsc->mount_options;
|
|
Luis Henriques |
8d1ba8 |
int ofs = offsetof(struct ceph_mount_options, snapdir_name);
|
|
Luis Henriques |
8d1ba8 |
- char *p1, *p2;
|
|
Luis Henriques |
8d1ba8 |
int ret;
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
ret = memcmp(fsopt1, fsopt2, ofs);
|
|
Luis Henriques |
8d1ba8 |
@@ -473,21 +425,12 @@ static int compare_mount_options(struct
|
|
Luis Henriques |
8d1ba8 |
ret = strcmp_null(fsopt1->snapdir_name, fsopt2->snapdir_name);
|
|
Luis Henriques |
8d1ba8 |
if (ret)
|
|
Luis Henriques |
8d1ba8 |
return ret;
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
|
|
Luis Henriques |
8d1ba8 |
if (ret)
|
|
Luis Henriques |
8d1ba8 |
return ret;
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
- p1 = path_remove_extra_slash(fsopt1->server_path);
|
|
Luis Henriques |
8d1ba8 |
- if (IS_ERR(p1))
|
|
Luis Henriques |
8d1ba8 |
- return PTR_ERR(p1);
|
|
Luis Henriques |
8d1ba8 |
- p2 = path_remove_extra_slash(fsopt2->server_path);
|
|
Luis Henriques |
8d1ba8 |
- if (IS_ERR(p2)) {
|
|
Luis Henriques |
8d1ba8 |
- kfree(p1);
|
|
Luis Henriques |
8d1ba8 |
- return PTR_ERR(p2);
|
|
Luis Henriques |
8d1ba8 |
- }
|
|
Luis Henriques |
8d1ba8 |
- ret = strcmp_null(p1, p2);
|
|
Luis Henriques |
8d1ba8 |
- kfree(p1);
|
|
Luis Henriques |
8d1ba8 |
- kfree(p2);
|
|
Luis Henriques |
8d1ba8 |
+ ret = strcmp_null(fsopt1->server_path, fsopt2->server_path);
|
|
Luis Henriques |
8d1ba8 |
if (ret)
|
|
Luis Henriques |
8d1ba8 |
return ret;
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
@@ -555,6 +498,8 @@ static int parse_mount_options(struct ce
|
|
Luis Henriques |
8d1ba8 |
err = -ENOMEM;
|
|
Luis Henriques |
8d1ba8 |
goto out;
|
|
Luis Henriques |
8d1ba8 |
}
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
+ canonicalize_path(fsopt->server_path);
|
|
Luis Henriques |
8d1ba8 |
} else {
|
|
Luis Henriques |
8d1ba8 |
dev_name_end = dev_name + strlen(dev_name);
|
|
Luis Henriques |
8d1ba8 |
}
|
|
Luis Henriques |
8d1ba8 |
@@ -981,7 +926,9 @@ static struct dentry *ceph_real_mount(st
|
|
Luis Henriques |
8d1ba8 |
mutex_lock(&fsc->client->mount_mutex);
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
if (!fsc->sb->s_root) {
|
|
Luis Henriques |
8d1ba8 |
- const char *path, *p;
|
|
Luis Henriques |
8d1ba8 |
+ const char *path = fsc->mount_options->server_path ?
|
|
Luis Henriques |
8d1ba8 |
+ fsc->mount_options->server_path + 1 : "";
|
|
Luis Henriques |
8d1ba8 |
+
|
|
Luis Henriques |
8d1ba8 |
err = __ceph_open_session(fsc->client, started);
|
|
Luis Henriques |
8d1ba8 |
if (err < 0)
|
|
Luis Henriques |
8d1ba8 |
goto out;
|
|
Luis Henriques |
8d1ba8 |
@@ -993,16 +940,6 @@ static struct dentry *ceph_real_mount(st
|
|
Luis Henriques |
8d1ba8 |
goto out;
|
|
Luis Henriques |
8d1ba8 |
}
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
- p = path_remove_extra_slash(fsc->mount_options->server_path);
|
|
Luis Henriques |
8d1ba8 |
- if (IS_ERR(p)) {
|
|
Luis Henriques |
8d1ba8 |
- err = PTR_ERR(p);
|
|
Luis Henriques |
8d1ba8 |
- goto out;
|
|
Luis Henriques |
8d1ba8 |
- }
|
|
Luis Henriques |
8d1ba8 |
- /* if the server path is omitted or just consists of '/' */
|
|
Luis Henriques |
8d1ba8 |
- if (!p)
|
|
Luis Henriques |
8d1ba8 |
- path = "";
|
|
Luis Henriques |
8d1ba8 |
- else
|
|
Luis Henriques |
8d1ba8 |
- path = p;
|
|
Luis Henriques |
8d1ba8 |
dout("mount opening path '%s'\n", path);
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
err = ceph_fs_debugfs_init(fsc);
|
|
Luis Henriques |
8d1ba8 |
@@ -1010,7 +947,6 @@ static struct dentry *ceph_real_mount(st
|
|
Luis Henriques |
8d1ba8 |
goto out;
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
root = open_root_dentry(fsc, path, started);
|
|
Luis Henriques |
8d1ba8 |
- kfree(p);
|
|
Luis Henriques |
8d1ba8 |
if (IS_ERR(root)) {
|
|
Luis Henriques |
8d1ba8 |
err = PTR_ERR(root);
|
|
Luis Henriques |
8d1ba8 |
goto out;
|
|
Luis Henriques |
8d1ba8 |
--- a/fs/ceph/super.h
|
|
Luis Henriques |
8d1ba8 |
+++ b/fs/ceph/super.h
|
|
Luis Henriques |
8d1ba8 |
@@ -85,7 +85,7 @@ struct ceph_mount_options {
|
|
Luis Henriques |
8d1ba8 |
|
|
Luis Henriques |
8d1ba8 |
char *snapdir_name; /* default ".snap" */
|
|
Luis Henriques |
8d1ba8 |
char *mds_namespace; /* default NULL */
|
|
Luis Henriques |
8d1ba8 |
- char *server_path; /* default "/" */
|
|
Luis Henriques |
8d1ba8 |
+ char *server_path; /* default NULL (means "/") */
|
|
Luis Henriques |
8d1ba8 |
char *fscache_uniq; /* default NULL */
|
|
Luis Henriques |
8d1ba8 |
};
|
|
Luis Henriques |
8d1ba8 |
|