[v3] ceph: remove the extra slashes in the server path
diff mbox series

Message ID 20191218004009.3781-1-xiubli@redhat.com
State New
Headers show
Series
  • [v3] ceph: remove the extra slashes in the server path
Related show

Commit Message

Xiubo Li Dec. 18, 2019, 12:40 a.m. UTC
From: Xiubo Li <xiubli@redhat.com>

When mounting if the server path has more than one slash continuously,
such as:

'mount.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:

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

And then when creating any new file or directory under the mount
point, we can get the following crash core dump:

<4>[  ...] ------------[ cut here ]------------
<2>[  ...] kernel BUG at fs/ceph/inode.c:1347!
<4>[  ...] invalid opcode: 0000 [#1] SMP PTI
<4>[  ...] CPU: 0 PID: 7 Comm: kworker/0:1 Tainted: G            E     5.4.0-rc5+ #1
<4>[  ...] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
<4>[  ...] Workqueue: ceph-msgr ceph_con_workfn [libceph]
<4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
<4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 4d [...]
<4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
<4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
<4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
<4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
<4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
<4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
<4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
<4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
<4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
<4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
<4>[  ...] Call Trace:
<4>[  ...]  dispatch+0x2ac/0x12b0 [ceph]
<4>[  ...]  ceph_con_workfn+0xd40/0x27c0 [libceph]
<4>[  ...]  process_one_work+0x1b0/0x350
<4>[  ...]  worker_thread+0x50/0x3b0
<4>[  ...]  kthread+0xfb/0x130
<4>[  ...]  ? process_one_work+0x350/0x350
<4>[  ...]  ? kthread_park+0x90/0x90
<4>[  ...]  ret_from_fork+0x35/0x40
<4>[  ...] Modules linked in: ceph(E) libceph fscache [...]
<4>[  ...] ---[ end trace ba883d8ccf9afcb0 ]---
<4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
<4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 [...]
<4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
<4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
<4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
<4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
<4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
<4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
<4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
<4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
<4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
<4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400

And we should ignore the extra slashes in the server path when mount
opening in case users have added them by mistake.

This will also help us to get an existing superblock if all the
other options are the same, such as all the following server paths
are treated as the same:

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

The mount_options->server_path will always save the original string
including the leading '/'.

Signed-off-by: Xiubo Li <xiubli@redhat.com>
---

Changed in V3:
- switch strchr instead.

Changed in V2:
- rebase to new mount API.


 fs/ceph/super.c | 118 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 98 insertions(+), 20 deletions(-)

Comments

Jeff Layton Dec. 20, 2019, 11:46 a.m. UTC | #1
On Tue, 2019-12-17 at 19:40 -0500, xiubli@redhat.com wrote:
> From: Xiubo Li <xiubli@redhat.com>
> 
> When mounting if the server path has more than one slash continuously,
> such as:
> 
> 'mount.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:
> 
> <7>[  ...] ceph:  mount opening path //
> <7>[  ...] ceph:  open_root_inode opening '//'
> <7>[  ...] ceph:  fill_trace 0000000059b8a3bc is_dentry 0 is_target 1
> <7>[  ...] ceph:  alloc_inode 00000000dc4ca00b
> <7>[  ...] ceph:  get_inode created new inode 00000000dc4ca00b 1.ffffffffffffffff ino 1
> <7>[  ...] ceph:  get_inode on 1=1.ffffffffffffffff got 00000000dc4ca00b
> 
> And then when creating any new file or directory under the mount
> point, we can get the following crash core dump:
> 
> <4>[  ...] ------------[ cut here ]------------
> <2>[  ...] kernel BUG at fs/ceph/inode.c:1347!
> <4>[  ...] invalid opcode: 0000 [#1] SMP PTI
> <4>[  ...] CPU: 0 PID: 7 Comm: kworker/0:1 Tainted: G            E     5.4.0-rc5+ #1
> <4>[  ...] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
> <4>[  ...] Workqueue: ceph-msgr ceph_con_workfn [libceph]
> <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
> <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 4d [...]
> <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
> <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
> <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
> <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
> <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
> <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
> <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
> <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
> <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> <4>[  ...] Call Trace:
> <4>[  ...]  dispatch+0x2ac/0x12b0 [ceph]
> <4>[  ...]  ceph_con_workfn+0xd40/0x27c0 [libceph]
> <4>[  ...]  process_one_work+0x1b0/0x350
> <4>[  ...]  worker_thread+0x50/0x3b0
> <4>[  ...]  kthread+0xfb/0x130
> <4>[  ...]  ? process_one_work+0x350/0x350
> <4>[  ...]  ? kthread_park+0x90/0x90
> <4>[  ...]  ret_from_fork+0x35/0x40
> <4>[  ...] Modules linked in: ceph(E) libceph fscache [...]
> <4>[  ...] ---[ end trace ba883d8ccf9afcb0 ]---
> <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
> <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 [...]
> <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
> <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
> <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
> <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
> <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
> <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
> <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
> <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
> <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> 
> And we should ignore the extra slashes in the server path when mount
> opening in case users have added them by mistake.
> 
> This will also help us to get an existing superblock if all the
> other options are the same, such as all the following server paths
> are treated as the same:
> 
> 1) "//mydir1///mydir//"
> 2) "/mydir1/mydir"
> 3) "/mydir1/mydir/"
> 
> The mount_options->server_path will always save the original string
> including the leading '/'.
> 
> Signed-off-by: Xiubo Li <xiubli@redhat.com>
> ---
> 
> Changed in V3:
> - switch strchr instead.
> 
> Changed in V2:
> - rebase to new mount API.
> 
> 
>  fs/ceph/super.c | 118 ++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 98 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/ceph/super.c b/fs/ceph/super.c
> index 6f33a265ccf1..9c9af183ad45 100644
> --- a/fs/ceph/super.c
> +++ b/fs/ceph/super.c
> @@ -107,7 +107,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);
> @@ -211,7 +210,6 @@ struct ceph_parse_opts_ctx {
>  
>  /*
>   * Parse the source parameter.  Distinguish the server list from the path.
> - * Internally we do not include the leading '/' in the path.
>   *
>   * The source will look like:
>   *     <server_spec>[,<server_spec>...]:[<path>]
> @@ -232,12 +230,15 @@ static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
>  
>  	dev_name_end = strchr(dev_name, '/');
>  	if (dev_name_end) {
> -		if (strlen(dev_name_end) > 1) {
> -			kfree(fsopt->server_path);
> -			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> -			if (!fsopt->server_path)
> -				return -ENOMEM;
> -		}
> +		kfree(fsopt->server_path);
> +
> +		/*
> +		 * 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)
> +			return -ENOMEM;
>  	} else {
>  		dev_name_end = dev_name + strlen(dev_name);
>  	}
> @@ -459,6 +460,69 @@ 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;
> +
> +		/* including the last '/' */
> +		len = cur - path + 1;
> +		memcpy(p, path, len);
> +		p += len;
> +
> +		while (*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)
> @@ -466,6 +530,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);
> @@ -478,9 +543,21 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
>  	ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
>  	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;
> +
>  	ret = strcmp_null(fsopt1->fscache_uniq, fsopt2->fscache_uniq);
>  	if (ret)
>  		return ret;
> @@ -786,7 +863,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).
> @@ -866,9 +942,6 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
>  	return root;
>  }
>  
> -
> -
> -
>  /*
>   * mount: join the ceph cluster, and open root directory.
>   */
> @@ -883,7 +956,7 @@ 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;
> @@ -895,17 +968,22 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
>  				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);
>  
>  		ceph_fs_debugfs_init(fsc);
>  
>  		root = open_root_dentry(fsc, path, started);
> +		kfree(p);
>  		if (IS_ERR(root)) {
>  			err = PTR_ERR(root);
>  			goto out;


Ok. Given that the MDS treats "//" as special, I think we do have to fix
this in the kclient after all. This looks fine to me, so merged. Should
make 5.6.

Thanks,
Jeff Layton Dec. 20, 2019, 12:08 p.m. UTC | #2
On Fri, 2019-12-20 at 06:46 -0500, Jeff Layton wrote:
> On Tue, 2019-12-17 at 19:40 -0500, xiubli@redhat.com wrote:
> > From: Xiubo Li <xiubli@redhat.com>
> > 
> > When mounting if the server path has more than one slash continuously,
> > such as:
> > 
> > 'mount.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:
> > 
> > <7>[  ...] ceph:  mount opening path //
> > <7>[  ...] ceph:  open_root_inode opening '//'
> > <7>[  ...] ceph:  fill_trace 0000000059b8a3bc is_dentry 0 is_target 1
> > <7>[  ...] ceph:  alloc_inode 00000000dc4ca00b
> > <7>[  ...] ceph:  get_inode created new inode 00000000dc4ca00b 1.ffffffffffffffff ino 1
> > <7>[  ...] ceph:  get_inode on 1=1.ffffffffffffffff got 00000000dc4ca00b
> > 
> > And then when creating any new file or directory under the mount
> > point, we can get the following crash core dump:
> > 
> > <4>[  ...] ------------[ cut here ]------------
> > <2>[  ...] kernel BUG at fs/ceph/inode.c:1347!
> > <4>[  ...] invalid opcode: 0000 [#1] SMP PTI
> > <4>[  ...] CPU: 0 PID: 7 Comm: kworker/0:1 Tainted: G            E     5.4.0-rc5+ #1
> > <4>[  ...] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
> > <4>[  ...] Workqueue: ceph-msgr ceph_con_workfn [libceph]
> > <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
> > <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 4d [...]
> > <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
> > <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
> > <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
> > <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
> > <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
> > <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
> > <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
> > <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
> > <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> > <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> > <4>[  ...] Call Trace:
> > <4>[  ...]  dispatch+0x2ac/0x12b0 [ceph]
> > <4>[  ...]  ceph_con_workfn+0xd40/0x27c0 [libceph]
> > <4>[  ...]  process_one_work+0x1b0/0x350
> > <4>[  ...]  worker_thread+0x50/0x3b0
> > <4>[  ...]  kthread+0xfb/0x130
> > <4>[  ...]  ? process_one_work+0x350/0x350
> > <4>[  ...]  ? kthread_park+0x90/0x90
> > <4>[  ...]  ret_from_fork+0x35/0x40
> > <4>[  ...] Modules linked in: ceph(E) libceph fscache [...]
> > <4>[  ...] ---[ end trace ba883d8ccf9afcb0 ]---
> > <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
> > <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 [...]
> > <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
> > <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
> > <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
> > <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
> > <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
> > <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
> > <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
> > <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
> > <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> > <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> > 
> > And we should ignore the extra slashes in the server path when mount
> > opening in case users have added them by mistake.
> > 
> > This will also help us to get an existing superblock if all the
> > other options are the same, such as all the following server paths
> > are treated as the same:
> > 
> > 1) "//mydir1///mydir//"
> > 2) "/mydir1/mydir"
> > 3) "/mydir1/mydir/"
> > 
> > The mount_options->server_path will always save the original string
> > including the leading '/'.
> > 
> > Signed-off-by: Xiubo Li <xiubli@redhat.com>
> > ---
> > 
> > Changed in V3:
> > - switch strchr instead.
> > 
> > Changed in V2:
> > - rebase to new mount API.
> > 
> > 
> >  fs/ceph/super.c | 118 ++++++++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 98 insertions(+), 20 deletions(-)
> > 
> > diff --git a/fs/ceph/super.c b/fs/ceph/super.c
> > index 6f33a265ccf1..9c9af183ad45 100644
> > --- a/fs/ceph/super.c
> > +++ b/fs/ceph/super.c
> > @@ -107,7 +107,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);
> > @@ -211,7 +210,6 @@ struct ceph_parse_opts_ctx {
> >  
> >  /*
> >   * Parse the source parameter.  Distinguish the server list from the path.
> > - * Internally we do not include the leading '/' in the path.
> >   *
> >   * The source will look like:
> >   *     <server_spec>[,<server_spec>...]:[<path>]
> > @@ -232,12 +230,15 @@ static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
> >  
> >  	dev_name_end = strchr(dev_name, '/');
> >  	if (dev_name_end) {
> > -		if (strlen(dev_name_end) > 1) {
> > -			kfree(fsopt->server_path);
> > -			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> > -			if (!fsopt->server_path)
> > -				return -ENOMEM;
> > -		}
> > +		kfree(fsopt->server_path);
> > +
> > +		/*
> > +		 * 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)
> > +			return -ENOMEM;
> >  	} else {
> >  		dev_name_end = dev_name + strlen(dev_name);
> >  	}
> > @@ -459,6 +460,69 @@ 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;
> > +
> > +		/* including the last '/' */
> > +		len = cur - path + 1;
> > +		memcpy(p, path, len);
> > +		p += len;
> > +
> > +		while (*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)
> > @@ -466,6 +530,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);
> > @@ -478,9 +543,21 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
> >  	ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
> >  	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;
> > +
> >  	ret = strcmp_null(fsopt1->fscache_uniq, fsopt2->fscache_uniq);
> >  	if (ret)
> >  		return ret;
> > @@ -786,7 +863,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).
> > @@ -866,9 +942,6 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
> >  	return root;
> >  }
> >  
> > -
> > -
> > -
> >  /*
> >   * mount: join the ceph cluster, and open root directory.
> >   */
> > @@ -883,7 +956,7 @@ 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;
> > @@ -895,17 +968,22 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
> >  				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);
> >  
> >  		ceph_fs_debugfs_init(fsc);
> >  
> >  		root = open_root_dentry(fsc, path, started);
> > +		kfree(p);
> >  		if (IS_ERR(root)) {
> >  			err = PTR_ERR(root);
> >  			goto out;
> 
> Ok. Given that the MDS treats "//" as special, I think we do have to fix
> this in the kclient after all. This looks fine to me, so merged. Should
> make 5.6.
> 

Hmm, I'll take it back. I was doing a bit of testing with this, and I'm
seeing memory corruption issues when running xfstests on it against a
vstart cluster. Dropped for now.

Xiubo, you may want to try this patch with KASAN enabled and see if it
points out the issue.

Thanks,
Xiubo Li Dec. 20, 2019, 12:16 p.m. UTC | #3
On 2019/12/20 20:08, Jeff Layton wrote:
> On Fri, 2019-12-20 at 06:46 -0500, Jeff Layton wrote:
>> On Tue, 2019-12-17 at 19:40 -0500, xiubli@redhat.com wrote:
>>> From: Xiubo Li <xiubli@redhat.com>
>>>
>>> When mounting if the server path has more than one slash continuously,
>>> such as:
>>>
>>> 'mount.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:
>>>
>>> <7>[  ...] ceph:  mount opening path //
>>> <7>[  ...] ceph:  open_root_inode opening '//'
>>> <7>[  ...] ceph:  fill_trace 0000000059b8a3bc is_dentry 0 is_target 1
>>> <7>[  ...] ceph:  alloc_inode 00000000dc4ca00b
>>> <7>[  ...] ceph:  get_inode created new inode 00000000dc4ca00b 1.ffffffffffffffff ino 1
>>> <7>[  ...] ceph:  get_inode on 1=1.ffffffffffffffff got 00000000dc4ca00b
>>>
>>> And then when creating any new file or directory under the mount
>>> point, we can get the following crash core dump:
>>>
>>> <4>[  ...] ------------[ cut here ]------------
>>> <2>[  ...] kernel BUG at fs/ceph/inode.c:1347!
>>> <4>[  ...] invalid opcode: 0000 [#1] SMP PTI
>>> <4>[  ...] CPU: 0 PID: 7 Comm: kworker/0:1 Tainted: G            E     5.4.0-rc5+ #1
>>> <4>[  ...] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
>>> <4>[  ...] Workqueue: ceph-msgr ceph_con_workfn [libceph]
>>> <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
>>> <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 4d [...]
>>> <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
>>> <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
>>> <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
>>> <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
>>> <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
>>> <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
>>> <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
>>> <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>> <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
>>> <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>>> <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>>> <4>[  ...] Call Trace:
>>> <4>[  ...]  dispatch+0x2ac/0x12b0 [ceph]
>>> <4>[  ...]  ceph_con_workfn+0xd40/0x27c0 [libceph]
>>> <4>[  ...]  process_one_work+0x1b0/0x350
>>> <4>[  ...]  worker_thread+0x50/0x3b0
>>> <4>[  ...]  kthread+0xfb/0x130
>>> <4>[  ...]  ? process_one_work+0x350/0x350
>>> <4>[  ...]  ? kthread_park+0x90/0x90
>>> <4>[  ...]  ret_from_fork+0x35/0x40
>>> <4>[  ...] Modules linked in: ceph(E) libceph fscache [...]
>>> <4>[  ...] ---[ end trace ba883d8ccf9afcb0 ]---
>>> <4>[  ...] RIP: 0010:ceph_fill_trace+0x992/0xb30 [ceph]
>>> <4>[  ...] Code: ff 0f 0b 0f 0b 0f 0b 4c 89 fa 48 c7 c6 [...]
>>> <4>[  ...] RSP: 0018:ffffa23d40067c70 EFLAGS: 00010297
>>> <4>[  ...] RAX: fffffffffffffffe RBX: ffff8a229eb566c0 RCX: 0000000000000006
>>> <4>[  ...] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff8a23aec17900
>>> <4>[  ...] RBP: ffff8a226bd91eb0 R08: 0000000000000001 R09: 0000000000000885
>>> <4>[  ...] R10: 000000000002dfd8 R11: ffff8a226bd95b30 R12: ffff8a239347e000
>>> <4>[  ...] R13: 0000000000000000 R14: ffff8a22fabeb000 R15: ffff8a2338b0c900
>>> <4>[  ...] FS:  0000000000000000(0000) GS:ffff8a23aec00000(0000) knlGS:0000000000000000
>>> <4>[  ...] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>> <4>[  ...] CR2: 000055b479d92068 CR3: 00000003764f6004 CR4: 00000000003606f0
>>> <4>[  ...] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>>> <4>[  ...] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>>>
>>> And we should ignore the extra slashes in the server path when mount
>>> opening in case users have added them by mistake.
>>>
>>> This will also help us to get an existing superblock if all the
>>> other options are the same, such as all the following server paths
>>> are treated as the same:
>>>
>>> 1) "//mydir1///mydir//"
>>> 2) "/mydir1/mydir"
>>> 3) "/mydir1/mydir/"
>>>
>>> The mount_options->server_path will always save the original string
>>> including the leading '/'.
>>>
>>> Signed-off-by: Xiubo Li <xiubli@redhat.com>
>>> ---
>>>
>>> Changed in V3:
>>> - switch strchr instead.
>>>
>>> Changed in V2:
>>> - rebase to new mount API.
>>>
>>>
>>>   fs/ceph/super.c | 118 ++++++++++++++++++++++++++++++++++++++++--------
>>>   1 file changed, 98 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/fs/ceph/super.c b/fs/ceph/super.c
>>> index 6f33a265ccf1..9c9af183ad45 100644
>>> --- a/fs/ceph/super.c
>>> +++ b/fs/ceph/super.c
>>> @@ -107,7 +107,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);
>>> @@ -211,7 +210,6 @@ struct ceph_parse_opts_ctx {
>>>   
>>>   /*
>>>    * Parse the source parameter.  Distinguish the server list from the path.
>>> - * Internally we do not include the leading '/' in the path.
>>>    *
>>>    * The source will look like:
>>>    *     <server_spec>[,<server_spec>...]:[<path>]
>>> @@ -232,12 +230,15 @@ static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
>>>   
>>>   	dev_name_end = strchr(dev_name, '/');
>>>   	if (dev_name_end) {
>>> -		if (strlen(dev_name_end) > 1) {
>>> -			kfree(fsopt->server_path);
>>> -			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
>>> -			if (!fsopt->server_path)
>>> -				return -ENOMEM;
>>> -		}
>>> +		kfree(fsopt->server_path);
>>> +
>>> +		/*
>>> +		 * 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)
>>> +			return -ENOMEM;
>>>   	} else {
>>>   		dev_name_end = dev_name + strlen(dev_name);
>>>   	}
>>> @@ -459,6 +460,69 @@ 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;
>>> +
>>> +		/* including the last '/' */
>>> +		len = cur - path + 1;
>>> +		memcpy(p, path, len);
>>> +		p += len;
>>> +
>>> +		while (*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)
>>> @@ -466,6 +530,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);
>>> @@ -478,9 +543,21 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
>>>   	ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
>>>   	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;
>>> +
>>>   	ret = strcmp_null(fsopt1->fscache_uniq, fsopt2->fscache_uniq);
>>>   	if (ret)
>>>   		return ret;
>>> @@ -786,7 +863,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).
>>> @@ -866,9 +942,6 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
>>>   	return root;
>>>   }
>>>   
>>> -
>>> -
>>> -
>>>   /*
>>>    * mount: join the ceph cluster, and open root directory.
>>>    */
>>> @@ -883,7 +956,7 @@ 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;
>>> @@ -895,17 +968,22 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
>>>   				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);
>>>   
>>>   		ceph_fs_debugfs_init(fsc);
>>>   
>>>   		root = open_root_dentry(fsc, path, started);
>>> +		kfree(p);
>>>   		if (IS_ERR(root)) {
>>>   			err = PTR_ERR(root);
>>>   			goto out;
>> Ok. Given that the MDS treats "//" as special, I think we do have to fix
>> this in the kclient after all. This looks fine to me, so merged. Should
>> make 5.6.
>>
> Hmm, I'll take it back. I was doing a bit of testing with this, and I'm
> seeing memory corruption issues when running xfstests on it against a
> vstart cluster. Dropped for now.
>
> Xiubo, you may want to try this patch with KASAN enabled and see if it
> points out the issue.

I will check it right now.

Thanks.



> Thanks,

Patch
diff mbox series

diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 6f33a265ccf1..9c9af183ad45 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -107,7 +107,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);
@@ -211,7 +210,6 @@  struct ceph_parse_opts_ctx {
 
 /*
  * Parse the source parameter.  Distinguish the server list from the path.
- * Internally we do not include the leading '/' in the path.
  *
  * The source will look like:
  *     <server_spec>[,<server_spec>...]:[<path>]
@@ -232,12 +230,15 @@  static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
 
 	dev_name_end = strchr(dev_name, '/');
 	if (dev_name_end) {
-		if (strlen(dev_name_end) > 1) {
-			kfree(fsopt->server_path);
-			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
-			if (!fsopt->server_path)
-				return -ENOMEM;
-		}
+		kfree(fsopt->server_path);
+
+		/*
+		 * 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)
+			return -ENOMEM;
 	} else {
 		dev_name_end = dev_name + strlen(dev_name);
 	}
@@ -459,6 +460,69 @@  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;
+
+		/* including the last '/' */
+		len = cur - path + 1;
+		memcpy(p, path, len);
+		p += len;
+
+		while (*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)
@@ -466,6 +530,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);
@@ -478,9 +543,21 @@  static int compare_mount_options(struct ceph_mount_options *new_fsopt,
 	ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
 	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;
+
 	ret = strcmp_null(fsopt1->fscache_uniq, fsopt2->fscache_uniq);
 	if (ret)
 		return ret;
@@ -786,7 +863,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).
@@ -866,9 +942,6 @@  static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
 	return root;
 }
 
-
-
-
 /*
  * mount: join the ceph cluster, and open root directory.
  */
@@ -883,7 +956,7 @@  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;
@@ -895,17 +968,22 @@  static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
 				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);
 
 		ceph_fs_debugfs_init(fsc);
 
 		root = open_root_dentry(fsc, path, started);
+		kfree(p);
 		if (IS_ERR(root)) {
 			err = PTR_ERR(root);
 			goto out;