Skip to content

Commit 4fbc0c7

Browse files
lxbszidryomov
authored andcommitted
ceph: remove the extra slashes in the server path
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: Xiubo Li <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]>
1 parent b38c9eb commit 4fbc0c7

File tree

1 file changed

+102
-20
lines changed

1 file changed

+102
-20
lines changed

fs/ceph/super.c

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
107107
return 0;
108108
}
109109

110-
111110
static int ceph_sync_fs(struct super_block *sb, int wait)
112111
{
113112
struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
@@ -211,7 +210,6 @@ struct ceph_parse_opts_ctx {
211210

212211
/*
213212
* Parse the source parameter. Distinguish the server list from the path.
214-
* Internally we do not include the leading '/' in the path.
215213
*
216214
* The source will look like:
217215
* <server_spec>[,<server_spec>...]:[<path>]
@@ -232,12 +230,15 @@ static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
232230

233231
dev_name_end = strchr(dev_name, '/');
234232
if (dev_name_end) {
235-
if (strlen(dev_name_end) > 1) {
236-
kfree(fsopt->server_path);
237-
fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
238-
if (!fsopt->server_path)
239-
return -ENOMEM;
240-
}
233+
kfree(fsopt->server_path);
234+
235+
/*
236+
* The server_path will include the whole chars from userland
237+
* including the leading '/'.
238+
*/
239+
fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
240+
if (!fsopt->server_path)
241+
return -ENOMEM;
241242
} else {
242243
dev_name_end = dev_name + strlen(dev_name);
243244
}
@@ -461,13 +462,81 @@ static int strcmp_null(const char *s1, const char *s2)
461462
return strcmp(s1, s2);
462463
}
463464

465+
/**
466+
* path_remove_extra_slash - Remove the extra slashes in the server path
467+
* @server_path: the server path and could be NULL
468+
*
469+
* Return NULL if the path is NULL or only consists of "/", or a string
470+
* without any extra slashes including the leading slash(es) and the
471+
* slash(es) at the end of the server path, such as:
472+
* "//dir1////dir2///" --> "dir1/dir2"
473+
*/
474+
static char *path_remove_extra_slash(const char *server_path)
475+
{
476+
const char *path = server_path;
477+
const char *cur, *end;
478+
char *buf, *p;
479+
int len;
480+
481+
/* if the server path is omitted */
482+
if (!path)
483+
return NULL;
484+
485+
/* remove all the leading slashes */
486+
while (*path == '/')
487+
path++;
488+
489+
/* if the server path only consists of slashes */
490+
if (*path == '\0')
491+
return NULL;
492+
493+
len = strlen(path);
494+
495+
buf = kmalloc(len + 1, GFP_KERNEL);
496+
if (!buf)
497+
return ERR_PTR(-ENOMEM);
498+
499+
end = path + len;
500+
p = buf;
501+
do {
502+
cur = strchr(path, '/');
503+
if (!cur)
504+
cur = end;
505+
506+
len = cur - path;
507+
508+
/* including one '/' */
509+
if (cur != end)
510+
len += 1;
511+
512+
memcpy(p, path, len);
513+
p += len;
514+
515+
while (cur <= end && *cur == '/')
516+
cur++;
517+
path = cur;
518+
} while (path < end);
519+
520+
*p = '\0';
521+
522+
/*
523+
* remove the last slash if there has and just to make sure that
524+
* we will get something like "dir1/dir2"
525+
*/
526+
if (*(--p) == '/')
527+
*p = '\0';
528+
529+
return buf;
530+
}
531+
464532
static int compare_mount_options(struct ceph_mount_options *new_fsopt,
465533
struct ceph_options *new_opt,
466534
struct ceph_fs_client *fsc)
467535
{
468536
struct ceph_mount_options *fsopt1 = new_fsopt;
469537
struct ceph_mount_options *fsopt2 = fsc->mount_options;
470538
int ofs = offsetof(struct ceph_mount_options, snapdir_name);
539+
char *p1, *p2;
471540
int ret;
472541

473542
ret = memcmp(fsopt1, fsopt2, ofs);
@@ -480,9 +549,21 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
480549
ret = strcmp_null(fsopt1->mds_namespace, fsopt2->mds_namespace);
481550
if (ret)
482551
return ret;
483-
ret = strcmp_null(fsopt1->server_path, fsopt2->server_path);
552+
553+
p1 = path_remove_extra_slash(fsopt1->server_path);
554+
if (IS_ERR(p1))
555+
return PTR_ERR(p1);
556+
p2 = path_remove_extra_slash(fsopt2->server_path);
557+
if (IS_ERR(p2)) {
558+
kfree(p1);
559+
return PTR_ERR(p2);
560+
}
561+
ret = strcmp_null(p1, p2);
562+
kfree(p1);
563+
kfree(p2);
484564
if (ret)
485565
return ret;
566+
486567
ret = strcmp_null(fsopt1->fscache_uniq, fsopt2->fscache_uniq);
487568
if (ret)
488569
return ret;
@@ -788,7 +869,6 @@ static void destroy_caches(void)
788869
ceph_fscache_unregister();
789870
}
790871

791-
792872
/*
793873
* ceph_umount_begin - initiate forced umount. Tear down down the
794874
* mount, skipping steps that may hang while waiting for server(s).
@@ -868,9 +948,6 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
868948
return root;
869949
}
870950

871-
872-
873-
874951
/*
875952
* mount: join the ceph cluster, and open root directory.
876953
*/
@@ -885,7 +962,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
885962
mutex_lock(&fsc->client->mount_mutex);
886963

887964
if (!fsc->sb->s_root) {
888-
const char *path;
965+
const char *path, *p;
889966
err = __ceph_open_session(fsc->client, started);
890967
if (err < 0)
891968
goto out;
@@ -897,17 +974,22 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
897974
goto out;
898975
}
899976

900-
if (!fsc->mount_options->server_path) {
901-
path = "";
902-
dout("mount opening path \\t\n");
903-
} else {
904-
path = fsc->mount_options->server_path + 1;
905-
dout("mount opening path %s\n", path);
977+
p = path_remove_extra_slash(fsc->mount_options->server_path);
978+
if (IS_ERR(p)) {
979+
err = PTR_ERR(p);
980+
goto out;
906981
}
982+
/* if the server path is omitted or just consists of '/' */
983+
if (!p)
984+
path = "";
985+
else
986+
path = p;
987+
dout("mount opening path '%s'\n", path);
907988

908989
ceph_fs_debugfs_init(fsc);
909990

910991
root = open_root_dentry(fsc, path, started);
992+
kfree(p);
911993
if (IS_ERR(root)) {
912994
err = PTR_ERR(root);
913995
goto out;

0 commit comments

Comments
 (0)