Split nfs_parse_mount_options() to move the prologue, list-splitting and epilogue into one function and the per-option processing into another. --- fs/nfs/internal.h | 3 + fs/nfs/mount.c | 143 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 83 insertions(+), 63 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index af6ca28da48d..ed255241baa7 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -100,7 +100,10 @@ struct nfs_sb_config { unsigned int version; unsigned int minorversion; char *fscache_uniq; + unsigned short protofamily; + unsigned short mountfamily; bool need_mount; + bool sloppy; struct { struct sockaddr_storage address; diff --git a/fs/nfs/mount.c b/fs/nfs/mount.c index ef9683deb2a8..26153b471e48 100644 --- a/fs/nfs/mount.c +++ b/fs/nfs/mount.c @@ -494,46 +494,18 @@ static int nfs_get_option_ul_bound(substring_t args[], unsigned long *option, } /* - * Error-check and convert a string of mount options from user space into - * a data structure. The whole mount string is processed; bad options are - * skipped as they are encountered. If there were no errors, return 1; - * otherwise return 0 (zero). + * Parse a single mount option in "key[=val]" form. */ -int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) +static int nfs_sb_config_parse_option(struct nfs_sb_config *cfg, char *p) { - char *p, *string, *secdata; - int rc, sloppy = 0, invalid_option = 0; - unsigned short protofamily = AF_UNSPEC; - unsigned short mountfamily = AF_UNSPEC; - - if (!raw) { - dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); - return 1; - } - dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw); - - secdata = alloc_secdata(); - if (!secdata) - goto out_nomem; - - rc = security_sb_copy_data(raw, secdata); - if (rc) - goto out_security_failure; - - rc = security_sb_parse_opts_str(secdata, &cfg->lsm_opts); - if (rc) - goto out_security_failure; - - free_secdata(secdata); + char *string; + int rc; - while ((p = strsep(&raw, ",")) != NULL) { + { substring_t args[MAX_OPT_ARGS]; unsigned long option; int token; - if (!*p) - continue; - dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", p); token = match_token(p, nfs_mount_option_tokens, args); @@ -737,7 +709,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) if (!rc) { dfprintk(MOUNT, "NFS: unrecognized " "security flavor\n"); - return 0; + return -EINVAL; } break; case Opt_proto: @@ -747,22 +719,22 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) token = match_token(string, nfs_xprt_protocol_tokens, args); - protofamily = AF_INET; + cfg->protofamily = AF_INET; switch (token) { case Opt_xprt_udp6: - protofamily = AF_INET6; + cfg->protofamily = AF_INET6; case Opt_xprt_udp: cfg->flags &= ~NFS_MOUNT_TCP; cfg->nfs_server.protocol = XPRT_TRANSPORT_UDP; break; case Opt_xprt_tcp6: - protofamily = AF_INET6; + cfg->protofamily = AF_INET6; case Opt_xprt_tcp: cfg->flags |= NFS_MOUNT_TCP; cfg->nfs_server.protocol = XPRT_TRANSPORT_TCP; break; case Opt_xprt_rdma6: - protofamily = AF_INET6; + cfg->protofamily = AF_INET6; case Opt_xprt_rdma: /* vector side protocols to TCP */ cfg->flags |= NFS_MOUNT_TCP; @@ -773,7 +745,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); kfree(string); - return 0; + return -EINVAL; } kfree(string); break; @@ -785,15 +757,15 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) nfs_xprt_protocol_tokens, args); kfree(string); - mountfamily = AF_INET; + cfg->mountfamily = AF_INET; switch (token) { case Opt_xprt_udp6: - mountfamily = AF_INET6; + cfg->mountfamily = AF_INET6; case Opt_xprt_udp: cfg->mount_server.protocol = XPRT_TRANSPORT_UDP; break; case Opt_xprt_tcp6: - mountfamily = AF_INET6; + cfg->mountfamily = AF_INET6; case Opt_xprt_tcp: cfg->mount_server.protocol = XPRT_TRANSPORT_TCP; break; @@ -801,7 +773,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) default: dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); - return 0; + return -EINVAL; } break; case Opt_addr: @@ -860,7 +832,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) default: dfprintk(MOUNT, "NFS: invalid " "lookupcache argument\n"); - return 0; + return -EINVAL; }; break; case Opt_fscache_uniq: @@ -893,7 +865,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) default: dfprintk(MOUNT, "NFS: invalid " "local_lock argument\n"); - return 0; + return -EINVAL; }; break; @@ -901,7 +873,7 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) * Special options */ case Opt_sloppy: - sloppy = 1; + cfg->sloppy = 1; dfprintk(MOUNT, "NFS: relaxing parsing rules\n"); break; case Opt_userspace: @@ -911,12 +883,63 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) break; default: - invalid_option = 1; dfprintk(MOUNT, "NFS: unrecognized mount option " "'%s'\n", p); + return -EINVAL; } } + return 0; + +out_invalid_address: + printk(KERN_INFO "NFS: bad IP address specified: %s\n", p); + return -EINVAL; +out_invalid_value: + printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p); + return -EINVAL; +out_nomem: + printk(KERN_INFO "NFS: not enough memory to parse option\n"); + return -ENOMEM; +} + +/* + * Error-check and convert a string of mount options from user space into + * a data structure. The whole mount string is processed; bad options are + * skipped as they are encountered. If there were no errors, return 1; + * otherwise return 0 (zero). + */ +int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) +{ + char *p, *secdata; + int rc, sloppy = 0, invalid_option = 0; + + if (!raw) { + dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); + return 1; + } + dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw); + + secdata = alloc_secdata(); + if (!secdata) + goto out_nomem; + + rc = security_sb_copy_data(raw, secdata); + if (rc) + goto out_security_failure; + + rc = security_sb_parse_opts_str(secdata, &cfg->lsm_opts); + if (rc) + goto out_security_failure; + + free_secdata(secdata); + + while ((p = strsep(&raw, ",")) != NULL) { + if (!*p) + continue; + if (nfs_sb_config_parse_option(cfg, p) < 0) + invalid_option = true; + } + if (!sloppy && invalid_option) return 0; @@ -931,22 +954,26 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) * verify that any proto=/mountproto= options match the address * families in the addr=/mountaddr= options. */ - if (protofamily != AF_UNSPEC && - protofamily != cfg->nfs_server.address.ss_family) + if (cfg->protofamily != AF_UNSPEC && + cfg->protofamily != cfg->nfs_server.address.ss_family) goto out_proto_mismatch; - if (mountfamily != AF_UNSPEC) { + if (cfg->mountfamily != AF_UNSPEC) { if (cfg->mount_server.addrlen) { - if (mountfamily != cfg->mount_server.address.ss_family) + if (cfg->mountfamily != cfg->mount_server.address.ss_family) goto out_mountproto_mismatch; } else { - if (mountfamily != cfg->nfs_server.address.ss_family) + if (cfg->mountfamily != cfg->nfs_server.address.ss_family) goto out_mountproto_mismatch; } } return 1; +out_minorversion_mismatch: + printk(KERN_INFO "NFS: mount option vers=%u does not support " + "minorversion=%u\n", cfg->version, cfg->minorversion); + return 0; out_mountproto_mismatch: printk(KERN_INFO "NFS: mount server address does not match mountproto= " "option\n"); @@ -954,20 +981,10 @@ int nfs_parse_mount_options(char *raw, struct nfs_sb_config *cfg) out_proto_mismatch: printk(KERN_INFO "NFS: server address does not match proto= option\n"); return 0; -out_invalid_address: - printk(KERN_INFO "NFS: bad IP address specified: %s\n", p); - return 0; -out_invalid_value: - printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p); - return 0; -out_minorversion_mismatch: - printk(KERN_INFO "NFS: mount option vers=%u does not support " - "minorversion=%u\n", cfg->version, cfg->minorversion); - return 0; out_migration_misuse: printk(KERN_INFO "NFS: 'migration' not supported for this NFS version\n"); - return 0; + return -EINVAL; out_nomem: printk(KERN_INFO "NFS: not enough memory to parse option\n"); return 0;