* [PATCH 0/4] NFS mounting with raw IPv6 server hostnames
@ 2008-06-17 18:17 Chuck Lever
[not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
0 siblings, 1 reply; 10+ messages in thread
From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Hi Trond-
Something meatier today.
These four patches introduce the ability to mount NFS servers using a raw
IPv6 address. This support requires the ability to handle colons in the
passed-in server hostname, and the ability to handle an IPv6 interface
identifier in the passed-in server hostname.
IPv6 addresses contain colons. A colon is also used to separate the server
name from the export path. By enclosing an IPv6 address inside square
brackets we effectively escape the colons in the IPv6 address and allow them
to pass unrecognized through the device name parser.
As an added bonus, I also include the patch that changes
nfs_parse_server_address() to take a name length. This allows the passed-in
address string to have no '\0'-termination, which will help us support IPv6
for NFSv4 referrals.
Please consider these for 2.6.27.
---
Chuck Lever (4):
NFS: handle interface identifiers in incoming IPv6 addresses
NFS: Add string length argument to nfs_parse_server_address
NFS: Support raw IPv6 address hostnames during NFS mount operation
NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3
fs/nfs/super.c | 336 ++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 261 insertions(+), 75 deletions(-)
--
Chuck Lever
chu ckd otl eve rat ora cle dot com
^ permalink raw reply [flat|nested] 10+ messages in thread[parent not found: <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>]
* [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 [not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> @ 2008-06-17 18:17 ` Chuck Lever [not found] ` <20080617181719.3215.5824.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> 2008-06-17 18:17 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever ` (3 subsequent siblings) 4 siblings, 1 reply; 10+ messages in thread From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw) To: trond.myklebust; +Cc: linux-nfs To support passing a raw IPv6 address as a server hostname, we need to expand the logic that handles splitting the passed-in device name into a server hostname and export path Start by pulling device name parsing out of the mount option validation functions and into separate helper functions. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfs/super.c | 124 ++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 79 insertions(+), 45 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d884f52..5e0eefa 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, } /* + * Split "dev_name" into "hostname:export_path". + * + * Note: caller frees hostname and export path, even on error. + */ +static int nfs_parse_devname(const char *dev_name, + char **hostname, size_t maxnamlen, + char **export_path, size_t maxpathlen) +{ + size_t len; + char *colon, *comma; + + colon = strchr(dev_name, ':'); + if (colon == NULL) + goto out_bad_devname; + + len = colon - dev_name; + if (len > maxnamlen) + goto out_hostname; + + /* N.B. caller will free nfs_server.hostname in all cases */ + *hostname = kstrndup(dev_name, len, GFP_KERNEL); + if (!*hostname) + goto out_nomem; + + /* kill possible hostname list: not supported */ + comma = strchr(*hostname, ','); + if (comma != NULL) { + if (comma == *hostname) + goto out_bad_devname; + *comma = '\0'; + } + + colon++; + len = strlen(colon); + if (len > maxpathlen) + goto out_path; + *export_path = kstrndup(colon, len, GFP_KERNEL); + if (!*export_path) + goto out_nomem; + + dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path); + return 0; + +out_bad_devname: + dfprintk(MOUNT, "NFS: device name not in host:path format\n"); + return -EINVAL; + +out_nomem: + dfprintk(MOUNT, "NFS: not enough memory to parse device name\n"); + return -ENOMEM; + +out_hostname: + dfprintk(MOUNT, "NFS: server hostname too long\n"); + return -ENAMETOOLONG; + +out_path: + dfprintk(MOUNT, "NFS: export pathname too long\n"); + return -ENAMETOOLONG; +} + +/* * Validate the NFS2/NFS3 mount data * - fills in the mount root filehandle * @@ -1341,8 +1402,6 @@ static int nfs_validate_mount_data(void *options, break; default: { - unsigned int len; - char *c; int status; if (nfs_parse_mount_options((char *)options, args) == 0) @@ -1357,21 +1416,17 @@ static int nfs_validate_mount_data(void *options, nfs_set_transport_defaults(args); - c = strchr(dev_name, ':'); - if (c == NULL) - return -EINVAL; - len = c - dev_name; - /* N.B. caller will free nfs_server.hostname in all cases */ - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); - if (!args->nfs_server.hostname) - goto out_nomem; + status = nfs_parse_devname(dev_name, + &args->nfs_server.hostname, + PAGE_SIZE, + &args->nfs_server.export_path, + NFS_MAXPATHLEN); + if (!status) + status = nfs_try_mount(args, mntfh); - c++; - if (strlen(c) > NFS_MAXPATHLEN) - return -ENAMETOOLONG; - args->nfs_server.export_path = c; + kfree(args->nfs_server.export_path); + args->nfs_server.export_path = NULL; - status = nfs_try_mount(args, mntfh); if (status) return status; @@ -1898,7 +1953,7 @@ static int nfs4_validate_mount_data(void *options, break; default: { - unsigned int len; + int status; if (nfs_parse_mount_options((char *)options, args) == 0) return -EINVAL; @@ -1922,34 +1977,17 @@ static int nfs4_validate_mount_data(void *options, goto out_inval_auth; } - /* - * Split "dev_name" into "hostname:mntpath". - */ - c = strchr(dev_name, ':'); - if (c == NULL) - return -EINVAL; - /* while calculating len, pretend ':' is '\0' */ - len = c - dev_name; - if (len > NFS4_MAXNAMLEN) - return -ENAMETOOLONG; - /* N.B. caller will free nfs_server.hostname in all cases */ - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); - if (!args->nfs_server.hostname) - goto out_nomem; - - c++; /* step over the ':' */ - len = strlen(c); - if (len > NFS4_MAXPATHLEN) - return -ENAMETOOLONG; - args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL); - if (!args->nfs_server.export_path) - goto out_nomem; - - dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path); - if (args->client_address == NULL) goto out_no_client_address; + status = nfs_parse_devname(dev_name, + &args->nfs_server.hostname, + NFS4_MAXNAMLEN, + &args->nfs_server.export_path, + NFS4_MAXPATHLEN); + if (status < 0) + return status; + break; } } @@ -1965,10 +2003,6 @@ out_inval_auth: data->auth_flavourlen); return -EINVAL; -out_nomem: - dfprintk(MOUNT, "NFS4: not enough memory to handle mount options\n"); - return -ENOMEM; - out_no_address: dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); return -EINVAL; ^ permalink raw reply related [flat|nested] 10+ messages in thread
[parent not found: <20080617181719.3215.5824.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>]
* Re: [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 [not found] ` <20080617181719.3215.5824.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> @ 2008-06-17 20:33 ` Trond Myklebust 2008-06-17 20:37 ` Trond Myklebust 2008-06-17 20:41 ` Chuck Lever 0 siblings, 2 replies; 10+ messages in thread From: Trond Myklebust @ 2008-06-17 20:33 UTC (permalink / raw) To: Chuck Lever; +Cc: linux-nfs On Tue, 2008-06-17 at 14:17 -0400, Chuck Lever wrote: > To support passing a raw IPv6 address as a server hostname, we need to > expand the logic that handles splitting the passed-in device name into > a server hostname and export path > > Start by pulling device name parsing out of the mount option validation > functions and into separate helper functions. > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com> > --- > > fs/nfs/super.c | 124 ++++++++++++++++++++++++++++++++++++-------------------- > 1 files changed, 79 insertions(+), 45 deletions(-) > > > diff --git a/fs/nfs/super.c b/fs/nfs/super.c > index d884f52..5e0eefa 100644 > --- a/fs/nfs/super.c > +++ b/fs/nfs/super.c > @@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, > } > > /* > + * Split "dev_name" into "hostname:export_path". > + * > + * Note: caller frees hostname and export path, even on error. > + */ > +static int nfs_parse_devname(const char *dev_name, > + char **hostname, size_t maxnamlen, > + char **export_path, size_t maxpathlen) > +{ > + size_t len; > + char *colon, *comma; > + > + colon = strchr(dev_name, ':'); > + if (colon == NULL) > + goto out_bad_devname; > + > + len = colon - dev_name; > + if (len > maxnamlen) > + goto out_hostname; > + > + /* N.B. caller will free nfs_server.hostname in all cases */ > + *hostname = kstrndup(dev_name, len, GFP_KERNEL); > + if (!*hostname) > + goto out_nomem; > + > + /* kill possible hostname list: not supported */ > + comma = strchr(*hostname, ','); > + if (comma != NULL) { > + if (comma == *hostname) > + goto out_bad_devname; Won't this and subsequent errors leak memory in *hostname? > + *comma = '\0'; > + } > + > + colon++; > + len = strlen(colon); > + if (len > maxpathlen) > + goto out_path; > + *export_path = kstrndup(colon, len, GFP_KERNEL); > + if (!*export_path) > + goto out_nomem; > + > + dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path); > + return 0; > + > +out_bad_devname: > + dfprintk(MOUNT, "NFS: device name not in host:path format\n"); > + return -EINVAL; > + > +out_nomem: > + dfprintk(MOUNT, "NFS: not enough memory to parse device name\n"); > + return -ENOMEM; > + > +out_hostname: > + dfprintk(MOUNT, "NFS: server hostname too long\n"); > + return -ENAMETOOLONG; > + > +out_path: > + dfprintk(MOUNT, "NFS: export pathname too long\n"); > + return -ENAMETOOLONG; > +} > + > +/* > * Validate the NFS2/NFS3 mount data > * - fills in the mount root filehandle > * > @@ -1341,8 +1402,6 @@ static int nfs_validate_mount_data(void *options, > > break; > default: { > - unsigned int len; > - char *c; > int status; > > if (nfs_parse_mount_options((char *)options, args) == 0) > @@ -1357,21 +1416,17 @@ static int nfs_validate_mount_data(void *options, > > nfs_set_transport_defaults(args); > > - c = strchr(dev_name, ':'); > - if (c == NULL) > - return -EINVAL; > - len = c - dev_name; > - /* N.B. caller will free nfs_server.hostname in all cases */ > - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); > - if (!args->nfs_server.hostname) > - goto out_nomem; > + status = nfs_parse_devname(dev_name, > + &args->nfs_server.hostname, > + PAGE_SIZE, > + &args->nfs_server.export_path, > + NFS_MAXPATHLEN); > + if (!status) > + status = nfs_try_mount(args, mntfh); > > - c++; > - if (strlen(c) > NFS_MAXPATHLEN) > - return -ENAMETOOLONG; > - args->nfs_server.export_path = c; > + kfree(args->nfs_server.export_path); > + args->nfs_server.export_path = NULL; > > - status = nfs_try_mount(args, mntfh); > if (status) > return status; > > @@ -1898,7 +1953,7 @@ static int nfs4_validate_mount_data(void *options, > > break; > default: { > - unsigned int len; > + int status; > > if (nfs_parse_mount_options((char *)options, args) == 0) > return -EINVAL; > @@ -1922,34 +1977,17 @@ static int nfs4_validate_mount_data(void *options, > goto out_inval_auth; > } > > - /* > - * Split "dev_name" into "hostname:mntpath". > - */ > - c = strchr(dev_name, ':'); > - if (c == NULL) > - return -EINVAL; > - /* while calculating len, pretend ':' is '\0' */ > - len = c - dev_name; > - if (len > NFS4_MAXNAMLEN) > - return -ENAMETOOLONG; > - /* N.B. caller will free nfs_server.hostname in all cases */ > - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); > - if (!args->nfs_server.hostname) > - goto out_nomem; > - > - c++; /* step over the ':' */ > - len = strlen(c); > - if (len > NFS4_MAXPATHLEN) > - return -ENAMETOOLONG; > - args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL); > - if (!args->nfs_server.export_path) > - goto out_nomem; > - > - dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path); > - > if (args->client_address == NULL) > goto out_no_client_address; > > + status = nfs_parse_devname(dev_name, > + &args->nfs_server.hostname, > + NFS4_MAXNAMLEN, > + &args->nfs_server.export_path, > + NFS4_MAXPATHLEN); > + if (status < 0) > + return status; > + > break; > } > } > @@ -1965,10 +2003,6 @@ out_inval_auth: > data->auth_flavourlen); > return -EINVAL; > > -out_nomem: > - dfprintk(MOUNT, "NFS4: not enough memory to handle mount options\n"); > - return -ENOMEM; > - > out_no_address: > dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); > return -EINVAL; > -- Trond Myklebust Linux NFS client maintainer NetApp Trond.Myklebust@netapp.com www.netapp.com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 2008-06-17 20:33 ` Trond Myklebust @ 2008-06-17 20:37 ` Trond Myklebust 2008-06-17 20:47 ` Chuck Lever 2008-06-17 20:41 ` Chuck Lever 1 sibling, 1 reply; 10+ messages in thread From: Trond Myklebust @ 2008-06-17 20:37 UTC (permalink / raw) To: Chuck Lever; +Cc: linux-nfs On Tue, 2008-06-17 at 16:33 -0400, Trond Myklebust wrote: > On Tue, 2008-06-17 at 14:17 -0400, Chuck Lever wrote: > > To support passing a raw IPv6 address as a server hostname, we need to > > expand the logic that handles splitting the passed-in device name into > > a server hostname and export path > > > > Start by pulling device name parsing out of the mount option validation > > functions and into separate helper functions. > > > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com> > > --- > > > > fs/nfs/super.c | 124 ++++++++++++++++++++++++++++++++++++-------------------- > > 1 files changed, 79 insertions(+), 45 deletions(-) > > > > > > diff --git a/fs/nfs/super.c b/fs/nfs/super.c > > index d884f52..5e0eefa 100644 > > --- a/fs/nfs/super.c > > +++ b/fs/nfs/super.c > > @@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, > > } > > > > /* > > + * Split "dev_name" into "hostname:export_path". > > + * > > + * Note: caller frees hostname and export path, even on error. > > + */ > > +static int nfs_parse_devname(const char *dev_name, > > + char **hostname, size_t maxnamlen, > > + char **export_path, size_t maxpathlen) > > +{ > > + size_t len; > > + char *colon, *comma; > > + > > + colon = strchr(dev_name, ':'); > > + if (colon == NULL) > > + goto out_bad_devname; > > + > > + len = colon - dev_name; > > + if (len > maxnamlen) > > + goto out_hostname; > > + > > + /* N.B. caller will free nfs_server.hostname in all cases */ > > + *hostname = kstrndup(dev_name, len, GFP_KERNEL); > > + if (!*hostname) > > + goto out_nomem; > > + > > + /* kill possible hostname list: not supported */ > > + comma = strchr(*hostname, ','); > > + if (comma != NULL) { > > + if (comma == *hostname) > > + goto out_bad_devname; > > Won't this and subsequent errors leak memory in *hostname? Sorry. I missed the fact that nfs_get_sb() and nfs4_get_sb() will do it for us... -- Trond Myklebust Linux NFS client maintainer NetApp Trond.Myklebust@netapp.com www.netapp.com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 2008-06-17 20:37 ` Trond Myklebust @ 2008-06-17 20:47 ` Chuck Lever 0 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2008-06-17 20:47 UTC (permalink / raw) To: Trond Myklebust; +Cc: linux-nfs On Jun 17, 2008, at 4:37 PM, Trond Myklebust wrote: > On Tue, 2008-06-17 at 16:33 -0400, Trond Myklebust wrote: >> On Tue, 2008-06-17 at 14:17 -0400, Chuck Lever wrote: >>> To support passing a raw IPv6 address as a server hostname, we >>> need to >>> expand the logic that handles splitting the passed-in device name >>> into >>> a server hostname and export path >>> >>> Start by pulling device name parsing out of the mount option >>> validation >>> functions and into separate helper functions. >>> >>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> >>> --- >>> >>> fs/nfs/super.c | 124 +++++++++++++++++++++++++++++++++++ >>> +-------------------- >>> 1 files changed, 79 insertions(+), 45 deletions(-) >>> >>> >>> diff --git a/fs/nfs/super.c b/fs/nfs/super.c >>> index d884f52..5e0eefa 100644 >>> --- a/fs/nfs/super.c >>> +++ b/fs/nfs/super.c >>> @@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct >>> nfs_parsed_mount_data *args, >>> } >>> >>> /* >>> + * Split "dev_name" into "hostname:export_path". >>> + * >>> + * Note: caller frees hostname and export path, even on error. >>> + */ >>> +static int nfs_parse_devname(const char *dev_name, >>> + char **hostname, size_t maxnamlen, >>> + char **export_path, size_t maxpathlen) >>> +{ >>> + size_t len; >>> + char *colon, *comma; >>> + >>> + colon = strchr(dev_name, ':'); >>> + if (colon == NULL) >>> + goto out_bad_devname; >>> + >>> + len = colon - dev_name; >>> + if (len > maxnamlen) >>> + goto out_hostname; >>> + >>> + /* N.B. caller will free nfs_server.hostname in all cases */ >>> + *hostname = kstrndup(dev_name, len, GFP_KERNEL); >>> + if (!*hostname) >>> + goto out_nomem; >>> + >>> + /* kill possible hostname list: not supported */ >>> + comma = strchr(*hostname, ','); >>> + if (comma != NULL) { >>> + if (comma == *hostname) >>> + goto out_bad_devname; >> >> Won't this and subsequent errors leak memory in *hostname? > > Sorry. I missed the fact that nfs_get_sb() and nfs4_get_sb() will do > it > for us... That's a useful mistake, actually. I architected it this way to eliminate a lot of indentation and gotos. If you think this is hard to read, maybe I should find a different way to organize this logic? -- Chuck Lever chuck[dot]lever[at]oracle[dot]com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 2008-06-17 20:33 ` Trond Myklebust 2008-06-17 20:37 ` Trond Myklebust @ 2008-06-17 20:41 ` Chuck Lever 1 sibling, 0 replies; 10+ messages in thread From: Chuck Lever @ 2008-06-17 20:41 UTC (permalink / raw) To: Trond Myklebust; +Cc: linux-nfs On Jun 17, 2008, at 4:33 PM, Trond Myklebust wrote: > On Tue, 2008-06-17 at 14:17 -0400, Chuck Lever wrote: >> To support passing a raw IPv6 address as a server hostname, we need >> to >> expand the logic that handles splitting the passed-in device name >> into >> a server hostname and export path >> >> Start by pulling device name parsing out of the mount option >> validation >> functions and into separate helper functions. >> >> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> >> --- >> >> fs/nfs/super.c | 124 +++++++++++++++++++++++++++++++++++ >> +-------------------- >> 1 files changed, 79 insertions(+), 45 deletions(-) >> >> >> diff --git a/fs/nfs/super.c b/fs/nfs/super.c >> index d884f52..5e0eefa 100644 >> --- a/fs/nfs/super.c >> +++ b/fs/nfs/super.c >> @@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct >> nfs_parsed_mount_data *args, >> } >> >> /* >> + * Split "dev_name" into "hostname:export_path". >> + * >> + * Note: caller frees hostname and export path, even on error. >> + */ >> +static int nfs_parse_devname(const char *dev_name, >> + char **hostname, size_t maxnamlen, >> + char **export_path, size_t maxpathlen) >> +{ >> + size_t len; >> + char *colon, *comma; >> + >> + colon = strchr(dev_name, ':'); >> + if (colon == NULL) >> + goto out_bad_devname; >> + >> + len = colon - dev_name; >> + if (len > maxnamlen) >> + goto out_hostname; >> + >> + /* N.B. caller will free nfs_server.hostname in all cases */ >> + *hostname = kstrndup(dev_name, len, GFP_KERNEL); >> + if (!*hostname) >> + goto out_nomem; >> + >> + /* kill possible hostname list: not supported */ >> + comma = strchr(*hostname, ','); >> + if (comma != NULL) { >> + if (comma == *hostname) >> + goto out_bad_devname; > > Won't this and subsequent errors leak memory in *hostname? See block comment above: "Caller frees hostname and export path, even on error." Is there a case that I missed? > > >> + *comma = '\0'; >> + } >> + >> + colon++; >> + len = strlen(colon); >> + if (len > maxpathlen) >> + goto out_path; >> + *export_path = kstrndup(colon, len, GFP_KERNEL); >> + if (!*export_path) >> + goto out_nomem; >> + >> + dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path); >> + return 0; >> + >> +out_bad_devname: >> + dfprintk(MOUNT, "NFS: device name not in host:path format\n"); >> + return -EINVAL; >> + >> +out_nomem: >> + dfprintk(MOUNT, "NFS: not enough memory to parse device name\n"); >> + return -ENOMEM; >> + >> +out_hostname: >> + dfprintk(MOUNT, "NFS: server hostname too long\n"); >> + return -ENAMETOOLONG; >> + >> +out_path: >> + dfprintk(MOUNT, "NFS: export pathname too long\n"); >> + return -ENAMETOOLONG; >> +} >> + >> +/* >> * Validate the NFS2/NFS3 mount data >> * - fills in the mount root filehandle >> * >> @@ -1341,8 +1402,6 @@ static int nfs_validate_mount_data(void >> *options, >> >> break; >> default: { >> - unsigned int len; >> - char *c; >> int status; >> >> if (nfs_parse_mount_options((char *)options, args) == 0) >> @@ -1357,21 +1416,17 @@ static int nfs_validate_mount_data(void >> *options, >> >> nfs_set_transport_defaults(args); >> >> - c = strchr(dev_name, ':'); >> - if (c == NULL) >> - return -EINVAL; >> - len = c - dev_name; >> - /* N.B. caller will free nfs_server.hostname in all cases */ >> - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); >> - if (!args->nfs_server.hostname) >> - goto out_nomem; >> + status = nfs_parse_devname(dev_name, >> + &args->nfs_server.hostname, >> + PAGE_SIZE, >> + &args->nfs_server.export_path, >> + NFS_MAXPATHLEN); >> + if (!status) >> + status = nfs_try_mount(args, mntfh); >> >> - c++; >> - if (strlen(c) > NFS_MAXPATHLEN) >> - return -ENAMETOOLONG; >> - args->nfs_server.export_path = c; >> + kfree(args->nfs_server.export_path); >> + args->nfs_server.export_path = NULL; >> >> - status = nfs_try_mount(args, mntfh); >> if (status) >> return status; >> >> @@ -1898,7 +1953,7 @@ static int nfs4_validate_mount_data(void >> *options, >> >> break; >> default: { >> - unsigned int len; >> + int status; >> >> if (nfs_parse_mount_options((char *)options, args) == 0) >> return -EINVAL; >> @@ -1922,34 +1977,17 @@ static int nfs4_validate_mount_data(void >> *options, >> goto out_inval_auth; >> } >> >> - /* >> - * Split "dev_name" into "hostname:mntpath". >> - */ >> - c = strchr(dev_name, ':'); >> - if (c == NULL) >> - return -EINVAL; >> - /* while calculating len, pretend ':' is '\0' */ >> - len = c - dev_name; >> - if (len > NFS4_MAXNAMLEN) >> - return -ENAMETOOLONG; >> - /* N.B. caller will free nfs_server.hostname in all cases */ >> - args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL); >> - if (!args->nfs_server.hostname) >> - goto out_nomem; >> - >> - c++; /* step over the ':' */ >> - len = strlen(c); >> - if (len > NFS4_MAXPATHLEN) >> - return -ENAMETOOLONG; >> - args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL); >> - if (!args->nfs_server.export_path) >> - goto out_nomem; >> - >> - dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path); >> - >> if (args->client_address == NULL) >> goto out_no_client_address; >> >> + status = nfs_parse_devname(dev_name, >> + &args->nfs_server.hostname, >> + NFS4_MAXNAMLEN, >> + &args->nfs_server.export_path, >> + NFS4_MAXPATHLEN); >> + if (status < 0) >> + return status; >> + >> break; >> } >> } >> @@ -1965,10 +2003,6 @@ out_inval_auth: >> data->auth_flavourlen); >> return -EINVAL; >> >> -out_nomem: >> - dfprintk(MOUNT, "NFS4: not enough memory to handle mount options >> \n"); >> - return -ENOMEM; >> - >> out_no_address: >> dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); >> return -EINVAL; >> > -- > Trond Myklebust > Linux NFS client maintainer > > NetApp > Trond.Myklebust@netapp.com > www.netapp.com -- Chuck Lever chuck[dot]lever[at]oracle[dot]com ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation [not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> 2008-06-17 18:17 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever @ 2008-06-17 18:17 ` Chuck Lever 2008-06-17 18:17 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever ` (2 subsequent siblings) 4 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw) To: trond.myklebust; +Cc: linux-nfs Traditionally the mount command has looked for a ":" to separate the server's hostname from the export path in the mounted on device name, like this: mount server:/export /mounted/on/dir The server's hostname is "server" and the export path is "/export". You can also substitute a specific IPv4 network address for the server hostname, like this: mount 192.168.0.55:/export /mounted/on/dir Raw IPv6 addresses present a problem, however, because they look something like this: fe80::200:5aff:fe00:30b Note the use of colons. To get around the presence of colons, copy the Solaris convention used for mounting IPv6 servers by address: wrap a raw IPv6 address with square brackets. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfs/super.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 82 insertions(+), 8 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5e0eefa..98c8110 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1215,14 +1215,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, return status; } -/* - * Split "dev_name" into "hostname:export_path". - * - * Note: caller frees hostname and export path, even on error. - */ -static int nfs_parse_devname(const char *dev_name, - char **hostname, size_t maxnamlen, - char **export_path, size_t maxpathlen) +static int nfs_parse_simple_hostname(const char *dev_name, + char **hostname, size_t maxnamlen, + char **export_path, size_t maxpathlen) { size_t len; char *colon, *comma; @@ -1277,6 +1272,85 @@ out_path: } /* + * Hostname has square brackets around it because it contains one or + * more colons. We look for the first closing square bracket, and a + * colon must follow it. + */ +static int nfs_parse_protected_hostname(const char *dev_name, + char **hostname, size_t maxnamlen, + char **export_path, size_t maxpathlen) +{ + size_t len; + char *start, *end; + + start = (char *)(dev_name + 1); + + end = strchr(start, ']'); + if (end == NULL) + goto out_bad_devname; + if (*(end + 1) != ':') + goto out_bad_devname; + + len = end - start; + if (len > maxnamlen) + goto out_hostname; + + /* N.B. caller will free nfs_server.hostname in all cases */ + *hostname = kstrndup(start, len, GFP_KERNEL); + if (*hostname == NULL) + goto out_nomem; + + end += 2; + len = strlen(end); + if (len > maxpathlen) + goto out_path; + *export_path = kstrndup(end, len, GFP_KERNEL); + if (!*export_path) + goto out_nomem; + + return 0; + +out_bad_devname: + dfprintk(MOUNT, "NFS: device name not in host:path format\n"); + return -EINVAL; + +out_nomem: + dfprintk(MOUNT, "NFS: not enough memory to parse device name\n"); + return -ENOMEM; + +out_hostname: + dfprintk(MOUNT, "NFS: server hostname too long\n"); + return -ENAMETOOLONG; + +out_path: + dfprintk(MOUNT, "NFS: export pathname too long\n"); + return -ENAMETOOLONG; +} + +/* + * Split "dev_name" into "hostname:export_path". + * + * The leftmost colon demarks the split between the server's hostname + * and the export path. If the hostname starts with a left square + * bracket, then it may contain colons. + * + * Note: caller frees hostname and export path, even on error. + */ +static int nfs_parse_devname(const char *dev_name, + char **hostname, size_t maxnamlen, + char **export_path, size_t maxpathlen) +{ + if (*dev_name == '[') + return nfs_parse_protected_hostname(dev_name, + hostname, maxnamlen, + export_path, maxpathlen); + + return nfs_parse_simple_hostname(dev_name, + hostname, maxnamlen, + export_path, maxpathlen); +} + +/* * Validate the NFS2/NFS3 mount data * - fills in the mount root filehandle * ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address [not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> 2008-06-17 18:17 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever 2008-06-17 18:17 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever @ 2008-06-17 18:17 ` Chuck Lever 2008-06-17 18:17 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever 2008-06-18 0:07 ` [PATCH 0/4] NFS mounting with raw IPv6 server hostnames Trond Myklebust 4 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw) To: trond.myklebust; +Cc: linux-nfs To make nfs_parse_server_address() more generally useful, allow it to accept input strings that are not terminated with '\0'. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfs/super.c | 98 +++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 68 insertions(+), 30 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 98c8110..a1fa5c9 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -62,6 +62,13 @@ #define NFSDBG_FACILITY NFSDBG_VFS +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN (16) +#endif +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN (48) +#endif + enum { /* Mount options that take no arguments */ Opt_soft, Opt_hard, @@ -702,38 +709,67 @@ static int nfs_verify_server_address(struct sockaddr *addr) return 0; } -/* - * Parse string addresses passed in via a mount option, - * and construct a sockaddr based on the result. - * - * If address parsing fails, set the sockaddr's address - * family to AF_UNSPEC to force nfs_verify_server_address() - * to punt the mount. - */ -static void nfs_parse_server_address(char *value, - struct sockaddr *sap, - size_t *len) +static void nfs_parse_ipv4_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) { - if (strchr(value, ':')) { - struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap; - u8 *addr = (u8 *)&ap->sin6_addr.in6_u; + struct sockaddr_in *sin = (struct sockaddr_in *)sap; + u8 *addr = (u8 *)&sin->sin_addr.s_addr; - ap->sin6_family = AF_INET6; - *len = sizeof(*ap); - if (in6_pton(value, -1, addr, '\0', NULL)) + dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n", + str_len, string); + + if (str_len <= INET_ADDRSTRLEN) { + sin->sin_family = AF_INET; + *addr_len = sizeof(*sin); + if (in4_pton(string, str_len, addr, '\0', NULL)) return; - } else { - struct sockaddr_in *ap = (struct sockaddr_in *)sap; - u8 *addr = (u8 *)&ap->sin_addr.s_addr; + } + + sap->sa_family = AF_UNSPEC; + *addr_len = 0; +} + +static void nfs_parse_ipv6_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; + + dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n", + str_len, string); - ap->sin_family = AF_INET; - *len = sizeof(*ap); - if (in4_pton(value, -1, addr, '\0', NULL)) + if (str_len <= INET6_ADDRSTRLEN) { + sin6->sin6_family = AF_INET6; + *addr_len = sizeof(*sin6); + if (in6_pton(string, str_len, addr, '\0', NULL)) return; } sap->sa_family = AF_UNSPEC; - *len = 0; + *addr_len = 0; +} + +/* + * Construct a sockaddr based on the contents of a string that contains + * an IP address in presentation format. + * + * If there is a problem constructing the new sockaddr, set the address + * family to AF_UNSPEC. + */ +static void nfs_parse_ip_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) +{ + unsigned int i, colons; + + colons = 0; + for (i = 0; i < str_len; i++) + if (string[i] == ':') + colons++; + + if (colons >= 2) + return nfs_parse_ipv6_address(string, str_len, sap, addr_len); + else + return nfs_parse_ipv4_address(string, str_len, sap, addr_len); } /* @@ -1093,9 +1129,10 @@ static int nfs_parse_mount_options(char *raw, string = match_strdup(args); if (string == NULL) goto out_nomem; - nfs_parse_server_address(string, (struct sockaddr *) - &mnt->nfs_server.address, - &mnt->nfs_server.addrlen); + nfs_parse_ip_address(string, strlen(string), + (struct sockaddr *) + &mnt->nfs_server.address, + &mnt->nfs_server.addrlen); kfree(string); break; case Opt_clientaddr: @@ -1116,9 +1153,10 @@ static int nfs_parse_mount_options(char *raw, string = match_strdup(args); if (string == NULL) goto out_nomem; - nfs_parse_server_address(string, (struct sockaddr *) - &mnt->mount_server.address, - &mnt->mount_server.addrlen); + nfs_parse_ip_address(string, strlen(string), + (struct sockaddr *) + &mnt->mount_server.address, + &mnt->mount_server.addrlen); kfree(string); break; ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses [not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> ` (2 preceding siblings ...) 2008-06-17 18:17 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever @ 2008-06-17 18:17 ` Chuck Lever 2008-06-18 0:07 ` [PATCH 0/4] NFS mounting with raw IPv6 server hostnames Trond Myklebust 4 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw) To: trond.myklebust; +Cc: linux-nfs Add support in the kernel NFS client's address parser for interface identifiers. IPv6 link-local addresses require an additional "interface identifier", which is a network device name or an integer that indexes the array of local network interfaces. They are suffixed to the address with a '%'. For example: fe80::215:c5ff:fe3b:e1b2%2 indicates an interface index of 2. Or fe80::215:c5ff:fe3b:e1b2%eth0 indicates that requests should be routed through the eth0 device. Without the interface ID, link-local addresses are not usable for NFS. Both the kernel NFS client mount option parser and the mount.nfs command can take either form. The mount.nfs command always passes the address through getnameinfo(3), which usually re-writes interface indices as device names. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfs/super.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 files changed, 41 insertions(+), 1 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a1fa5c9..f46b10f 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -47,6 +47,7 @@ #include <linux/inet.h> #include <linux/in6.h> #include <net/ipv6.h> +#include <linux/netdevice.h> #include <linux/nfs_xdr.h> #include <linux/magic.h> #include <linux/parser.h> @@ -68,6 +69,9 @@ #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN (48) #endif +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif enum { /* Mount options that take no arguments */ @@ -729,11 +733,45 @@ static void nfs_parse_ipv4_address(char *string, size_t str_len, *addr_len = 0; } +static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len, + const char *delim, + struct sockaddr_in6 *sin6) +{ + char *p; + size_t len; + + if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) + return ; + if (*delim != SCOPE_DELIMITER) + return; + + len = (string + str_len) - delim - 1; + p = kstrndup(delim + 1, len, GFP_KERNEL); + if (p) { + unsigned long scope_id = 0; + struct net_device *dev; + + dev = dev_get_by_name(&init_net, p); + if (dev != NULL) { + scope_id = dev->ifindex; + dev_put(dev); + } else { + /* scope_id is set to zero on error */ + strict_strtoul(p, 10, &scope_id); + } + + kfree(p); + sin6->sin6_scope_id = scope_id; + dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id); + } +} + static void nfs_parse_ipv6_address(char *string, size_t str_len, struct sockaddr *sap, size_t *addr_len) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; + const char *delim; dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n", str_len, string); @@ -741,8 +779,10 @@ static void nfs_parse_ipv6_address(char *string, size_t str_len, if (str_len <= INET6_ADDRSTRLEN) { sin6->sin6_family = AF_INET6; *addr_len = sizeof(*sin6); - if (in6_pton(string, str_len, addr, '\0', NULL)) + if (in6_pton(string, str_len, addr, SCOPE_DELIMITER, &delim)) { + nfs_parse_ipv6_scope_id(string, str_len, delim, sin6); return; + } } sap->sa_family = AF_UNSPEC; ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 0/4] NFS mounting with raw IPv6 server hostnames [not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org> ` (3 preceding siblings ...) 2008-06-17 18:17 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever @ 2008-06-18 0:07 ` Trond Myklebust 4 siblings, 0 replies; 10+ messages in thread From: Trond Myklebust @ 2008-06-18 0:07 UTC (permalink / raw) To: Chuck Lever; +Cc: linux-nfs On Tue, 2008-06-17 at 14:17 -0400, Chuck Lever wrote: > Hi Trond- > > Something meatier today. > > These four patches introduce the ability to mount NFS servers using a raw > IPv6 address. This support requires the ability to handle colons in the > passed-in server hostname, and the ability to handle an IPv6 interface > identifier in the passed-in server hostname. > > IPv6 addresses contain colons. A colon is also used to separate the server > name from the export path. By enclosing an IPv6 address inside square > brackets we effectively escape the colons in the IPv6 address and allow them > to pass unrecognized through the device name parser. > > As an added bonus, I also include the patch that changes > nfs_parse_server_address() to take a name length. This allows the passed-in > address string to have no '\0'-termination, which will help us support IPv6 > for NFSv4 referrals. > > Please consider these for 2.6.27. > > --- > > Chuck Lever (4): > NFS: handle interface identifiers in incoming IPv6 addresses > NFS: Add string length argument to nfs_parse_server_address > NFS: Support raw IPv6 address hostnames during NFS mount operation > NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 > > > fs/nfs/super.c | 336 ++++++++++++++++++++++++++++++++++++++++++++------------ > 1 files changed, 261 insertions(+), 75 deletions(-) > All 4 patches applied to the 'devel' branch. -- Trond Myklebust Linux NFS client maintainer NetApp Trond.Myklebust@netapp.com www.netapp.com ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2008-06-18 0:08 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-17 18:17 [PATCH 0/4] NFS mounting with raw IPv6 server hostnames Chuck Lever
[not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-17 18:17 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever
[not found] ` <20080617181719.3215.5824.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-17 20:33 ` Trond Myklebust
2008-06-17 20:37 ` Trond Myklebust
2008-06-17 20:47 ` Chuck Lever
2008-06-17 20:41 ` Chuck Lever
2008-06-17 18:17 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
2008-06-17 18:17 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever
2008-06-17 18:17 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever
2008-06-18 0:07 ` [PATCH 0/4] NFS mounting with raw IPv6 server hostnames Trond Myklebust
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox