Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68d87669 authored by Xiubo Li's avatar Xiubo Li Committed by Greg Kroah-Hartman
Browse files

ceph: remove the extra slashes in the server path

commit 4fbc0c711b2464ee1551850b85002faae0b775d5 upstream.

It's possible to pass the mount helper a server path that has more
than one contiguous slash character. For example:

  $ mount -t ceph 192.168.195.165:40176:/// /mnt/cephfs/

In the MDS server side the extra slashes of the server path will be
treated as snap dir, and then we can get the following debug logs:

  ceph:  mount opening path //
  ceph:  open_root_inode opening '//'
  ceph:  fill_trace 0000000059b8a3bc is_dentry 0 is_target 1
  ceph:  alloc_inode 00000000dc4ca00b
  ceph:  get_inode created new inode 00000000dc4ca00b 1.ffffffffffffffff ino 1
  ceph:  get_inode on 1=1.ffffffffffffffff got 00000000dc4ca00b

And then when creating any new file or directory under the mount
point, we can hit the following BUG_ON in ceph_fill_trace():

  BUG_ON(ceph_snap(dir) != dvino.snap);

Have the client ignore the extra slashes in the server path when
mounting. This will also canonicalize the path, so that identical mounts
can be consilidated.

1) "//mydir1///mydir//"
2) "/mydir1/mydir"
3) "/mydir1/mydir/"

Regardless of the internal treatment of these paths, the kernel still
stores the original string including the leading '/' for presentation
to userland.

URL: https://tracker.ceph.com/issues/42771


Signed-off-by: default avatarXiubo Li <xiubli@redhat.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
Signed-off-by: default avatarLuis Henriques <lhenriques@suse.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 792a15d4
Loading
Loading
Loading
Loading
+99 −19
Original line number Diff line number Diff line
@@ -85,7 +85,6 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
	return 0;
}


static int ceph_sync_fs(struct super_block *sb, int wait)
{
	struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
@@ -321,6 +320,73 @@ static int strcmp_null(const char *s1, const char *s2)
	return strcmp(s1, s2);
}

/**
 * path_remove_extra_slash - Remove the extra slashes in the server path
 * @server_path: the server path and could be NULL
 *
 * Return NULL if the path is NULL or only consists of "/", or a string
 * without any extra slashes including the leading slash(es) and the
 * slash(es) at the end of the server path, such as:
 * "//dir1////dir2///" --> "dir1/dir2"
 */
static char *path_remove_extra_slash(const char *server_path)
{
	const char *path = server_path;
	const char *cur, *end;
	char *buf, *p;
	int len;

	/* if the server path is omitted */
	if (!path)
		return NULL;

	/* remove all the leading slashes */
	while (*path == '/')
		path++;

	/* if the server path only consists of slashes */
	if (*path == '\0')
		return NULL;

	len = strlen(path);

	buf = kmalloc(len + 1, GFP_KERNEL);
	if (!buf)
		return ERR_PTR(-ENOMEM);

	end = path + len;
	p = buf;
	do {
		cur = strchr(path, '/');
		if (!cur)
			cur = end;

		len = cur - path;

		/* including one '/' */
		if (cur != end)
			len += 1;

		memcpy(p, path, len);
		p += len;

		while (cur <= end && *cur == '/')
			cur++;
		path = cur;
	} while (path < end);

	*p = '\0';

	/*
	 * remove the last slash if there has and just to make sure that
	 * we will get something like "dir1/dir2"
	 */
	if (*(--p) == '/')
		*p = '\0';

	return buf;
}

static int compare_mount_options(struct ceph_mount_options *new_fsopt,
				 struct ceph_options *new_opt,
				 struct ceph_fs_client *fsc)
@@ -328,6 +394,7 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
	struct ceph_mount_options *fsopt1 = new_fsopt;
	struct ceph_mount_options *fsopt2 = fsc->mount_options;
	int ofs = offsetof(struct ceph_mount_options, snapdir_name);
	char *p1, *p2;
	int ret;

	ret = memcmp(fsopt1, fsopt2, ofs);
@@ -341,7 +408,17 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
	if (ret)
		return ret;

	ret = strcmp_null(fsopt1->server_path, fsopt2->server_path);
	p1 = path_remove_extra_slash(fsopt1->server_path);
	if (IS_ERR(p1))
		return PTR_ERR(p1);
	p2 = path_remove_extra_slash(fsopt2->server_path);
	if (IS_ERR(p2)) {
		kfree(p1);
		return PTR_ERR(p2);
	}
	ret = strcmp_null(p1, p2);
	kfree(p1);
	kfree(p2);
	if (ret)
		return ret;

@@ -396,13 +473,15 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt,
	 */
	dev_name_end = strchr(dev_name, '/');
	if (dev_name_end) {
		if (strlen(dev_name_end) > 1) {
		/*
		 * The server_path will include the whole chars from userland
		 * including the leading '/'.
		 */
		fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
		if (!fsopt->server_path) {
			err = -ENOMEM;
			goto out;
		}
		}
	} else {
		dev_name_end = dev_name + strlen(dev_name);
	}
@@ -725,7 +804,6 @@ static void destroy_caches(void)
	ceph_fscache_unregister();
}


/*
 * ceph_umount_begin - initiate forced umount.  Tear down down the
 * mount, skipping steps that may hang while waiting for server(s).
@@ -812,9 +890,6 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
	return root;
}




/*
 * mount: join the ceph cluster, and open root directory.
 */
@@ -828,24 +903,29 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
	mutex_lock(&fsc->client->mount_mutex);

	if (!fsc->sb->s_root) {
		const char *path;
		const char *path, *p;
		err = __ceph_open_session(fsc->client, started);
		if (err < 0)
			goto out;

		if (!fsc->mount_options->server_path) {
			path = "";
			dout("mount opening path \\t\n");
		} else {
			path = fsc->mount_options->server_path + 1;
			dout("mount opening path %s\n", path);
		p = path_remove_extra_slash(fsc->mount_options->server_path);
		if (IS_ERR(p)) {
			err = PTR_ERR(p);
			goto out;
		}
		/* if the server path is omitted or just consists of '/' */
		if (!p)
			path = "";
		else
			path = p;
		dout("mount opening path '%s'\n", path);

		err = ceph_fs_debugfs_init(fsc);
		if (err < 0)
			goto out;

		root = open_root_dentry(fsc, path, started);
		kfree(p);
		if (IS_ERR(root)) {
			err = PTR_ERR(root);
			goto out;