diff options
author | Alex Elder <elder@inktank.com> | 2012-08-09 13:33:26 -0400 |
---|---|---|
committer | Alex Elder <elder@inktank.com> | 2012-10-01 15:30:49 -0400 |
commit | c98f533c9497e285109a047bfb955d683f33f7e4 (patch) | |
tree | 2f47418cd404a20351beedadb1b2712302bbd6fc /fs | |
parent | 4156d998409be065aa8141b6bd2c6f18be1b21e9 (diff) |
ceph: let path portion of mount "device" be optional
A recent change to /sbin/mountall causes any trailing '/' character
in the "device" (or fs_spec) field in /etc/fstab to be stripped. As
a result, an entry for a ceph mount that intends to mount the root
of the name space ends up with now path portion, and the ceph mount
option processing code rejects this.
That is, an entry in /etc/fstab like:
cephserver:port:/ /mnt ceph defaults 0 0
provides to the ceph code just "cephserver:port:" as the "device,"
and that gets rejected.
Although this is a bug in /sbin/mountall, we can have the ceph mount
code support an empty/nonexistent path, interpreting it to mean the
root of the name space.
RFC 5952 offers recommendations for how to express IPv6 addresses,
and recommends the usage found in RFC 3986 (which specifies the
format for URI's) for representing both IPv4 and IPv6 addresses that
include port numbers. (See in particular the definition of
"authority" found in the Appendix of RFC 3986.)
According to those standards, no host specification will ever
contain a '/' character. As a result, it is sufficient to scan a
provided "device" from an /etc/fstab entry for the first '/'
character, and if it's found, treat that as the beginning of the
path. If no '/' character is present, we can treat the entire
string as the monitor host specification(s), and assume the path
to be the root of the name space. We'll still require a ':' to
separate the host portion from the (possibly empty) path portion.
This means that we can more formally define how ceph will interpret
the "device" it's provided when processing a mount request:
"device" will look like:
<server_spec>[,<server_spec>...]:[<path>]
where
<server_spec> is <ip>[:<port>]
<path> is optional, but if present must begin with '/'
This addresses http://tracker.newdream.net/issues/2919
Signed-off-by: Alex Elder <elder@inktank.com>
Reviewed-by: Dan Mick <dan.mick@inktank.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ceph/super.c | 37 |
1 files changed, 26 insertions, 11 deletions
diff --git a/fs/ceph/super.c b/fs/ceph/super.c index b982239f38f9..2f586b0e5e0f 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c | |||
@@ -307,7 +307,10 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt, | |||
307 | { | 307 | { |
308 | struct ceph_mount_options *fsopt; | 308 | struct ceph_mount_options *fsopt; |
309 | const char *dev_name_end; | 309 | const char *dev_name_end; |
310 | int err = -ENOMEM; | 310 | int err; |
311 | |||
312 | if (!dev_name || !*dev_name) | ||
313 | return -EINVAL; | ||
311 | 314 | ||
312 | fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL); | 315 | fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL); |
313 | if (!fsopt) | 316 | if (!fsopt) |
@@ -328,21 +331,33 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt, | |||
328 | fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; | 331 | fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; |
329 | fsopt->congestion_kb = default_congestion_kb(); | 332 | fsopt->congestion_kb = default_congestion_kb(); |
330 | 333 | ||
331 | /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ | 334 | /* |
335 | * Distinguish the server list from the path in "dev_name". | ||
336 | * Internally we do not include the leading '/' in the path. | ||
337 | * | ||
338 | * "dev_name" will look like: | ||
339 | * <server_spec>[,<server_spec>...]:[<path>] | ||
340 | * where | ||
341 | * <server_spec> is <ip>[:<port>] | ||
342 | * <path> is optional, but if present must begin with '/' | ||
343 | */ | ||
344 | dev_name_end = strchr(dev_name, '/'); | ||
345 | if (dev_name_end) { | ||
346 | /* skip over leading '/' for path */ | ||
347 | *path = dev_name_end + 1; | ||
348 | } else { | ||
349 | /* path is empty */ | ||
350 | dev_name_end = dev_name + strlen(dev_name); | ||
351 | *path = dev_name_end; | ||
352 | } | ||
332 | err = -EINVAL; | 353 | err = -EINVAL; |
333 | if (!dev_name) | 354 | dev_name_end--; /* back up to ':' separator */ |
334 | goto out; | 355 | if (*dev_name_end != ':') { |
335 | *path = strstr(dev_name, ":/"); | 356 | pr_err("device name is missing path (no : separator in %s)\n", |
336 | if (*path == NULL) { | ||
337 | pr_err("device name is missing path (no :/ in %s)\n", | ||
338 | dev_name); | 357 | dev_name); |
339 | goto out; | 358 | goto out; |
340 | } | 359 | } |
341 | dev_name_end = *path; | ||
342 | dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name); | 360 | dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name); |
343 | |||
344 | /* path on server */ | ||
345 | *path += 2; | ||
346 | dout("server path '%s'\n", *path); | 361 | dout("server path '%s'\n", *path); |
347 | 362 | ||
348 | *popt = ceph_parse_options(options, dev_name, dev_name_end, | 363 | *popt = ceph_parse_options(options, dev_name, dev_name_end, |