From 337eb00a2c3a421999c39c94ce7e33545ee8baa7 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Tue, 12 May 2009 15:10:54 +0200 Subject: Push BKL down into ->remount_fs() [xfs, btrfs, capifs, shmem don't need BKL, exempt] Signed-off-by: Alessio Igor Bogani Signed-off-by: Al Viro --- fs/nfs/super.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d2d67781c579..26127b69a275 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1813,6 +1813,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data) if (data == NULL) return -ENOMEM; + lock_kernel(); /* fill out struct with values from existing mount */ data->flags = nfss->flags; data->rsize = nfss->rsize; @@ -1837,6 +1838,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data) error = nfs_compare_remount_data(nfss, data); out: kfree(data); + unlock_kernel(); return error; } -- cgit v1.2.3 From 3fd5be9e19921a89d9ed78d6a708a379a6c3c76a Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Wed, 1 Apr 2009 09:21:48 -0400 Subject: nfs41: add mount command option minorversion mount -t nfs4 -o minorversion=[0|1] specifies whether to use 4.0 or 4.1. By default, the minorversion is set to 0. Signed-off-by: Mike Sager [set default minorversion to 0 as per Trond and SteveD's request] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d2d67781c579..5a8fdc791cc1 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -90,6 +90,7 @@ enum { Opt_mountport, Opt_mountvers, Opt_nfsvers, + Opt_minorversion, /* Mount options that take string arguments */ Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost, @@ -155,6 +156,7 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_mountvers, "mountvers=%u" }, { Opt_nfsvers, "nfsvers=%u" }, { Opt_nfsvers, "vers=%u" }, + { Opt_minorversion, "minorversion=%u" }, { Opt_sec, "sec=%s" }, { Opt_proto, "proto=%s" }, @@ -1211,6 +1213,13 @@ static int nfs_parse_mount_options(char *raw, nfs_parse_invalid_value("nfsvers"); } break; + case Opt_minorversion: + if (match_int(args, &option)) + return 0; + if (option < 0 || option > NFS4_MAX_MINOR_VERSION) + return 0; + mnt->minorversion = option; + break; /* * options that take text values @@ -2261,6 +2270,7 @@ static int nfs4_validate_mount_data(void *options, args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */ args->auth_flavors[0] = RPC_AUTH_UNIX; args->auth_flavor_len = 0; + args->minorversion = 0; switch (data->version) { case 1: -- cgit v1.2.3 From 0f3e66c6a6cae479028d31198288e524fe5ff90d Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:34 -0400 Subject: nfs41: destroy_session operation Implement the destroy_session operation conforming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove extraneous rpc_clnt pointer] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41; NFS_CS_READY required for DESTROY_SESSION] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust [nfs41: fix encode_destroy_session's xdr Xcoding pointer type] Signed-off-by: Benny Halevy --- fs/nfs/super.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5a8fdc791cc1..73db6f8c145a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2485,12 +2485,14 @@ static void nfs4_kill_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); + dprintk("--> %s\n", __func__); nfs_super_return_all_delegations(sb); kill_anon_super(sb); nfs4_renewd_prepare_shutdown(server); nfs_fscache_release_super_cookie(sb); nfs_free_server(server); + dprintk("<-- %s\n", __func__); } /* -- cgit v1.2.3 From 01c3f05228ce7fc19baa103e4e4bf6c1b5062a53 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:22:58 -0700 Subject: NFSv4: Fix the 'nolock' option regression NFSv4 should just ignore the 'nolock' option. It is an NFSv2/v3 thing... This fixes the Oops in http://bugzilla.kernel.org/show_bug.cgi?id=13330 Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 26127b69a275..98b47d17740a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2240,6 +2240,11 @@ static void nfs4_fill_super(struct super_block *sb) nfs_initialise_sb(sb); } +static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) +{ + args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); +} + /* * Validate NFSv4 mount options */ @@ -2336,6 +2341,8 @@ static int nfs4_validate_mount_data(void *options, nfs_validate_transport_protocol(args); + nfs4_validate_mount_flags(args); + if (args->auth_flavor_len > 1) goto out_inval_auth; -- cgit v1.2.3 From 5a0ffe544c54f62be99751e369f4d0f44bd5ee19 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:18 -0400 Subject: nfs41: Release backchannel resources associated with session Frees the preallocated backchannel resources that are associated with this session when the session is destroyed. A backchannel is currently created once per session. Destroy the backchannel only when the session is destroyed. Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy --- fs/nfs/super.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 73db6f8c145a..69804a8245f6 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2488,7 +2488,6 @@ static void nfs4_kill_super(struct super_block *sb) dprintk("--> %s\n", __func__); nfs_super_return_all_delegations(sb); kill_anon_super(sb); - nfs4_renewd_prepare_shutdown(server); nfs_fscache_release_super_cookie(sb); nfs_free_server(server); -- cgit v1.2.3 From c381ad2cf2d5dcd3991bcc8a18fddd9d5c66ccaa Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:09 -0700 Subject: NFS: Do not display the setting of the "intr" mount option The "intr" mount option has been deprecated for a while, but /proc/mounts continues to display "nointr" whether "intr" or "nointr" has been specified for a mount point. Since these options do not have any effect, simply do not display them. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 98b47d17740a..539a61a0887b 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -514,7 +514,6 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, const char *nostr; } nfs_info[] = { { NFS_MOUNT_SOFT, ",soft", ",hard" }, - { NFS_MOUNT_INTR, ",intr", ",nointr" }, { NFS_MOUNT_POSIX, ",posix", "" }, { NFS_MOUNT_NOCTO, ",nocto", "" }, { NFS_MOUNT_NOAC, ",noac", "" }, -- cgit v1.2.3 From 8e02f6b9aae9b265064f929c6df15222b9baf256 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:13 -0700 Subject: NFS: Update MNT and MNT3 reply decoding functions Solder xdr_stream-based XDR decoding functions into the in-kernel mountd client that are more careful about checking data types and watching for buffer overflows. The new MNT3 decoder includes support for auth-flavor list decoding. The "_sz" macro for MNT3 replies was missing the size of the file handle. I've added this back, and included the size of the auth flavor array. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 539a61a0887b..0b72357cdc41 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1380,6 +1380,7 @@ out_security_failure: static int nfs_try_mount(struct nfs_parsed_mount_data *args, struct nfs_fh *root_fh) { + unsigned int auth_flavor_len = 0; struct nfs_mount_request request = { .sap = (struct sockaddr *) &args->mount_server.address, @@ -1387,6 +1388,7 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, .protocol = args->mount_server.protocol, .fh = root_fh, .noresvport = args->flags & NFS_MOUNT_NORESVPORT, + .auth_flav_len = &auth_flavor_len, }; int status; -- cgit v1.2.3 From d23c45fd84f79a3b84899dac053dcafe9d43ebc9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:13 -0700 Subject: NFS: Invalid mount option values should always fail, even with "sloppy" Ian Kent reports: "I've noticed a couple of other regressions with the options vers and proto option of mount.nfs(8). The commands: mount -t nfs -o vers= :/ / mount -t nfs -o proto= :/ / both immediately fail. But if the "-s" option is also used they both succeed with the mount falling back to defaults (by the look of it). In the past these failed even when the sloppy option was given, as I think they should. I believe the sloppy option is meant to allow the mount command to still function for mount options (for example in shared autofs maps) that exist on other Unix implementations but aren't present in the Linux mount.nfs(8). So, an invalid value specified for a known mount option is different to an unknown mount option and should fail appropriately." See RH bugzilla 486266. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 157 ++++++++++++++++++++++----------------------------------- 1 file changed, 61 insertions(+), 96 deletions(-) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 0b72357cdc41..a2b2805caf9d 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -942,11 +942,6 @@ static int nfs_parse_security_flavors(char *value, return 1; } -static void nfs_parse_invalid_value(const char *option) -{ - dfprintk(MOUNT, "NFS: bad value specified for %s option\n", 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 @@ -957,7 +952,7 @@ static int nfs_parse_mount_options(char *raw, struct nfs_parsed_mount_data *mnt) { char *p, *string, *secdata; - int rc, sloppy = 0, errors = 0; + int rc, sloppy = 0, invalid_option = 0; if (!raw) { dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); @@ -1091,113 +1086,82 @@ static int nfs_parse_mount_options(char *raw, */ case Opt_port: if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) { - errors++; - nfs_parse_invalid_value("port"); - } else - mnt->nfs_server.port = option; + option < 0 || option > USHORT_MAX) + goto out_invalid_value; + mnt->nfs_server.port = option; break; case Opt_rsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("rsize"); - } else - mnt->rsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->rsize = option; break; case Opt_wsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("wsize"); - } else - mnt->wsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->wsize = option; break; case Opt_bsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("bsize"); - } else - mnt->bsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->bsize = option; break; case Opt_timeo: - if (match_int(args, &option) || option <= 0) { - errors++; - nfs_parse_invalid_value("timeo"); - } else - mnt->timeo = option; + if (match_int(args, &option) || option <= 0) + goto out_invalid_value; + mnt->timeo = option; break; case Opt_retrans: - if (match_int(args, &option) || option <= 0) { - errors++; - nfs_parse_invalid_value("retrans"); - } else - mnt->retrans = option; + if (match_int(args, &option) || option <= 0) + goto out_invalid_value; + mnt->retrans = option; break; case Opt_acregmin: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acregmin"); - } else - mnt->acregmin = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmin = option; break; case Opt_acregmax: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acregmax"); - } else - mnt->acregmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmax = option; break; case Opt_acdirmin: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acdirmin"); - } else - mnt->acdirmin = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acdirmin = option; break; case Opt_acdirmax: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acdirmax"); - } else - mnt->acdirmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acdirmax = option; break; case Opt_actimeo: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("actimeo"); - } else - mnt->acregmin = mnt->acregmax = - mnt->acdirmin = mnt->acdirmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmin = mnt->acregmax = + mnt->acdirmin = mnt->acdirmax = option; break; case Opt_namelen: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("namlen"); - } else - mnt->namlen = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->namlen = option; break; case Opt_mountport: if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) { - errors++; - nfs_parse_invalid_value("mountport"); - } else - mnt->mount_server.port = option; + option < 0 || option > USHORT_MAX) + goto out_invalid_value; + mnt->mount_server.port = option; break; case Opt_mountvers: if (match_int(args, &option) || option < NFS_MNT_VERSION || - option > NFS_MNT3_VERSION) { - errors++; - nfs_parse_invalid_value("mountvers"); - } else - mnt->mount_server.version = option; + option > NFS_MNT3_VERSION) + goto out_invalid_value; + mnt->mount_server.version = option; break; case Opt_nfsvers: - if (match_int(args, &option)) { - errors++; - nfs_parse_invalid_value("nfsvers"); - break; - } + if (match_int(args, &option)) + goto out_invalid_value; switch (option) { case NFS2_VERSION: mnt->flags &= ~NFS_MOUNT_VER3; @@ -1206,8 +1170,7 @@ static int nfs_parse_mount_options(char *raw, mnt->flags |= NFS_MOUNT_VER3; break; default: - errors++; - nfs_parse_invalid_value("nfsvers"); + goto out_invalid_value; } break; @@ -1221,9 +1184,9 @@ static int nfs_parse_mount_options(char *raw, rc = nfs_parse_security_flavors(string, mnt); kfree(string); if (!rc) { - errors++; dfprintk(MOUNT, "NFS: unrecognized " "security flavor\n"); + return 0; } break; case Opt_proto: @@ -1237,23 +1200,25 @@ static int nfs_parse_mount_options(char *raw, case Opt_xprt_udp: mnt->flags &= ~NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; + kfree(string); break; case Opt_xprt_tcp: mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; + kfree(string); break; case Opt_xprt_rdma: /* vector side protocols to TCP */ mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; xprt_load_transport(string); + kfree(string); break; default: - errors++; dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); + return 0; } - kfree(string); break; case Opt_mountproto: string = match_strdup(args); @@ -1272,9 +1237,9 @@ static int nfs_parse_mount_options(char *raw, break; case Opt_xprt_rdma: /* not used for side protocols */ default: - errors++; dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); + return 0; } break; case Opt_addr: @@ -1330,9 +1295,9 @@ static int nfs_parse_mount_options(char *raw, mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE; break; default: - errors++; dfprintk(MOUNT, "NFS: invalid " "lookupcache argument\n"); + return 0; }; break; @@ -1350,20 +1315,20 @@ static int nfs_parse_mount_options(char *raw, break; default: - errors++; + invalid_option = 1; dfprintk(MOUNT, "NFS: unrecognized mount option " "'%s'\n", p); } } - if (errors > 0) { - dfprintk(MOUNT, "NFS: parsing encountered %d error%s\n", - errors, (errors == 1 ? "" : "s")); - if (!sloppy) - return 0; - } + if (!sloppy && invalid_option) + return 0; + return 1; +out_invalid_value: + printk(KERN_INFO "NFS: bad mount option value specified: %s \n", p); + return 0; out_nomem: printk(KERN_INFO "NFS: not enough memory to parse option\n"); return 0; -- cgit v1.2.3 From a5a16bae707cd5d2bc97d7bd1a30079f18113a77 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:14 -0700 Subject: NFS: More "sloppy" parsing problems Specifying "port=-5" with the kernel's current mount option parser generates "unrecognized mount option". If "sloppy" is set, this causes the mount to succeed and use the default values; the desired behavior is that, since this is a valid option with an invalid value, the mount should fail, even with "sloppy." To properly handle "sloppy" parsing, we need to distinguish between correct options with invalid values, and incorrect options. We will need to parse integer values by hand, therefore, and not rely on match_token(). For instance, these must all fail with "invalid value": port=12345678 port=-5 port=samuel and not with "unrecognized option," as they do currently. Thus, for the sake of match_token() we need to treat the values for these options as strings, and do the conversion to integers using strict_strtol(). This is basically the same solution we used for the earlier "retry=" fix (commit ecbb3845), except in this case the kernel actually has to parse the value, rather than ignore it. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 142 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 34 deletions(-) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a2b2805caf9d..b798ed1bd36d 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -139,22 +139,22 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_fscache_uniq, "fsc=%s" }, { Opt_nofscache, "nofsc" }, - { Opt_port, "port=%u" }, - { Opt_rsize, "rsize=%u" }, - { Opt_wsize, "wsize=%u" }, - { Opt_bsize, "bsize=%u" }, - { Opt_timeo, "timeo=%u" }, - { Opt_retrans, "retrans=%u" }, - { Opt_acregmin, "acregmin=%u" }, - { Opt_acregmax, "acregmax=%u" }, - { Opt_acdirmin, "acdirmin=%u" }, - { Opt_acdirmax, "acdirmax=%u" }, - { Opt_actimeo, "actimeo=%u" }, - { Opt_namelen, "namlen=%u" }, - { Opt_mountport, "mountport=%u" }, - { Opt_mountvers, "mountvers=%u" }, - { Opt_nfsvers, "nfsvers=%u" }, - { Opt_nfsvers, "vers=%u" }, + { Opt_port, "port=%s" }, + { Opt_rsize, "rsize=%s" }, + { Opt_wsize, "wsize=%s" }, + { Opt_bsize, "bsize=%s" }, + { Opt_timeo, "timeo=%s" }, + { Opt_retrans, "retrans=%s" }, + { Opt_acregmin, "acregmin=%s" }, + { Opt_acregmax, "acregmax=%s" }, + { Opt_acdirmin, "acdirmin=%s" }, + { Opt_acdirmax, "acdirmax=%s" }, + { Opt_actimeo, "actimeo=%s" }, + { Opt_namelen, "namlen=%s" }, + { Opt_mountport, "mountport=%s" }, + { Opt_mountvers, "mountvers=%s" }, + { Opt_nfsvers, "nfsvers=%s" }, + { Opt_nfsvers, "vers=%s" }, { Opt_sec, "sec=%s" }, { Opt_proto, "proto=%s" }, @@ -976,7 +976,8 @@ static int nfs_parse_mount_options(char *raw, while ((p = strsep(&raw, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; - int option, token; + unsigned long option; + int token; if (!*p) continue; @@ -1085,82 +1086,155 @@ static int nfs_parse_mount_options(char *raw, * options that take numeric values */ case Opt_port: - if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option > USHORT_MAX) goto out_invalid_value; mnt->nfs_server.port = option; break; case Opt_rsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->rsize = option; break; case Opt_wsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->wsize = option; break; case Opt_bsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->bsize = option; break; case Opt_timeo: - if (match_int(args, &option) || option <= 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option == 0) goto out_invalid_value; mnt->timeo = option; break; case Opt_retrans: - if (match_int(args, &option) || option <= 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option == 0) goto out_invalid_value; mnt->retrans = option; break; case Opt_acregmin: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmin = option; break; case Opt_acregmax: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmax = option; break; case Opt_acdirmin: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acdirmin = option; break; case Opt_acdirmax: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acdirmax = option; break; case Opt_actimeo: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmin = mnt->acregmax = mnt->acdirmin = mnt->acdirmax = option; break; case Opt_namelen: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->namlen = option; break; case Opt_mountport: - if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option > USHORT_MAX) goto out_invalid_value; mnt->mount_server.port = option; break; case Opt_mountvers: - if (match_int(args, &option) || + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option < NFS_MNT_VERSION || option > NFS_MNT3_VERSION) goto out_invalid_value; mnt->mount_server.version = option; break; case Opt_nfsvers: - if (match_int(args, &option)) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; switch (option) { case NFS2_VERSION: -- cgit v1.2.3 From c02d7adf8c5429727a98bad1d039bccad4c61c50 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 22 Jun 2009 15:09:14 -0400 Subject: NFSv4: Replace nfs4_path_walk() with VFS path lookup in a private namespace As noted in the previous patch, the NFSv4 client mount code currently has several limitations. If the mount path contains symlinks, or referrals, or even if it just contains a '..', then the client code in nfs4_path_walk() will fail with an error. This patch replaces the nfs4_path_walk()-based lookup with a helper function that sets up a private namespace to represent the namespace on the server, then uses the ordinary VFS and NFS path lookup code to walk down the mount path in that namespace. Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/super.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 157 insertions(+), 21 deletions(-) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 3d460527daab..8e0673a0d6aa 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -272,10 +274,14 @@ static const struct super_operations nfs_sops = { #ifdef CONFIG_NFS_V4 static int nfs4_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static int nfs4_remote_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); static void nfs4_kill_super(struct super_block *sb); static struct file_system_type nfs4_fs_type = { @@ -286,6 +292,14 @@ static struct file_system_type nfs4_fs_type = { .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; +static struct file_system_type nfs4_remote_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs4_remote_get_sb, + .kill_sb = nfs4_kill_super, + .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + struct file_system_type nfs4_xdev_fs_type = { .owner = THIS_MODULE, .name = "nfs4", @@ -294,6 +308,14 @@ struct file_system_type nfs4_xdev_fs_type = { .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; +static struct file_system_type nfs4_remote_referral_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs4_remote_referral_get_sb, + .kill_sb = nfs4_kill_super, + .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + struct file_system_type nfs4_referral_fs_type = { .owner = THIS_MODULE, .name = "nfs4", @@ -2433,12 +2455,12 @@ out_no_client_address: } /* - * Get the superblock for an NFS4 mountpoint + * Get the superblock for the NFS4 root partition */ -static int nfs4_get_sb(struct file_system_type *fs_type, +static int nfs4_remote_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) { - struct nfs_parsed_mount_data *data; + struct nfs_parsed_mount_data *data = raw_data; struct super_block *s; struct nfs_server *server; struct nfs_fh *mntfh; @@ -2449,18 +2471,12 @@ static int nfs4_get_sb(struct file_system_type *fs_type, }; int error = -ENOMEM; - data = kzalloc(sizeof(*data), GFP_KERNEL); mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL); if (data == NULL || mntfh == NULL) goto out_free_fh; security_init_mnt_opts(&data->lsm_opts); - /* Validate the mount data */ - error = nfs4_validate_mount_data(raw_data, data, dev_name); - if (error < 0) - goto out; - /* Get a volume representation */ server = nfs4_create_server(data, mntfh); if (IS_ERR(server)) { @@ -2473,7 +2489,7 @@ static int nfs4_get_sb(struct file_system_type *fs_type, compare_super = NULL; /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); + s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_free; @@ -2510,14 +2526,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type, error = 0; out: - kfree(data->client_address); - kfree(data->nfs_server.export_path); - kfree(data->nfs_server.hostname); - kfree(data->fscache_uniq); security_free_mnt_opts(&data->lsm_opts); out_free_fh: kfree(mntfh); - kfree(data); return error; out_free: @@ -2531,6 +2542,102 @@ error_splat_super: goto out; } +static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, + int flags, void *data, const char *hostname) +{ + struct vfsmount *root_mnt; + char *root_devname; + size_t len; + + len = strlen(hostname) + 3; + root_devname = kmalloc(len, GFP_KERNEL); + if (root_devname == NULL) + return ERR_PTR(-ENOMEM); + snprintf(root_devname, len, "%s:/", hostname); + root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data); + kfree(root_devname); + return root_mnt; +} + +static int nfs_follow_remote_path(struct vfsmount *root_mnt, + const char *export_path, struct vfsmount *mnt_target) +{ + struct mnt_namespace *ns_private; + struct nameidata nd; + struct super_block *s; + int ret; + + ns_private = create_mnt_ns(root_mnt); + ret = PTR_ERR(ns_private); + if (IS_ERR(ns_private)) + goto out_mntput; + + ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, + export_path, LOOKUP_FOLLOW, &nd); + + put_mnt_ns(ns_private); + + if (ret != 0) + goto out_err; + + s = nd.path.mnt->mnt_sb; + atomic_inc(&s->s_active); + mnt_target->mnt_sb = s; + mnt_target->mnt_root = dget(nd.path.dentry); + + path_put(&nd.path); + down_write(&s->s_umount); + return 0; +out_mntput: + mntput(root_mnt); +out_err: + return ret; +} + +/* + * Get the superblock for an NFS4 mountpoint + */ +static int nfs4_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + struct nfs_parsed_mount_data *data; + char *export_path; + struct vfsmount *root_mnt; + int error = -ENOMEM; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + goto out_free_data; + + /* Validate the mount data */ + error = nfs4_validate_mount_data(raw_data, data, dev_name); + if (error < 0) + goto out; + + export_path = data->nfs_server.export_path; + data->nfs_server.export_path = "/"; + root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data, + data->nfs_server.hostname); + data->nfs_server.export_path = export_path; + + error = PTR_ERR(root_mnt); + if (IS_ERR(root_mnt)) + goto out; + + error = nfs_follow_remote_path(root_mnt, export_path, mnt); + +out: + kfree(data->client_address); + kfree(data->nfs_server.export_path); + kfree(data->nfs_server.hostname); + kfree(data->fscache_uniq); +out_free_data: + kfree(data); + dprintk("<-- nfs4_get_sb() = %d%s\n", error, + error != 0 ? " [error]" : ""); + return error; +} + static void nfs4_kill_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); @@ -2627,12 +2734,9 @@ error_splat_super: return error; } -/* - * Create an NFS4 server record on referral traversal - */ -static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data, - struct vfsmount *mnt) +static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, + struct vfsmount *mnt) { struct nfs_clone_mount *data = raw_data; struct super_block *s; @@ -2711,4 +2815,36 @@ error_splat_super: return error; } +/* + * Create an NFS4 server record on referral traversal + */ +static int nfs4_referral_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, + struct vfsmount *mnt) +{ + struct nfs_clone_mount *data = raw_data; + char *export_path; + struct vfsmount *root_mnt; + int error; + + dprintk("--> nfs4_referral_get_sb()\n"); + + export_path = data->mnt_path; + data->mnt_path = "/"; + + root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type, + flags, data, data->hostname); + data->mnt_path = export_path; + + error = PTR_ERR(root_mnt); + if (IS_ERR(root_mnt)) + goto out; + + error = nfs_follow_remote_path(root_mnt, export_path, mnt); +out: + dprintk("<-- nfs4_referral_get_sb() = %d%s\n", error, + error != 0 ? " [error]" : ""); + return error; +} + #endif /* CONFIG_NFS_V4 */ -- cgit v1.2.3 From b88f8a546f5dba213938fdfc11e66bc5c2421623 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 22 Jun 2009 15:09:14 -0400 Subject: NFS: Correct the NFS mount path when following a referral Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/super.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'fs/nfs/super.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 8e0673a0d6aa..0b4cbdc60abd 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2559,6 +2559,27 @@ static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, return root_mnt; } +static void nfs_fix_devname(const struct path *path, struct vfsmount *mnt) +{ + char *page = (char *) __get_free_page(GFP_KERNEL); + char *devname, *tmp; + + if (page == NULL) + return; + devname = nfs_path(path->mnt->mnt_devname, + path->mnt->mnt_root, path->dentry, + page, PAGE_SIZE); + if (devname == NULL) + goto out_freepage; + tmp = kstrdup(devname, GFP_KERNEL); + if (tmp == NULL) + goto out_freepage; + kfree(mnt->mnt_devname); + mnt->mnt_devname = tmp; +out_freepage: + free_page((unsigned long)page); +} + static int nfs_follow_remote_path(struct vfsmount *root_mnt, const char *export_path, struct vfsmount *mnt_target) { @@ -2585,6 +2606,9 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt, mnt_target->mnt_sb = s; mnt_target->mnt_root = dget(nd.path.dentry); + /* Correct the device pathname */ + nfs_fix_devname(&nd.path, mnt_target); + path_put(&nd.path); down_write(&s->s_umount); return 0; -- cgit v1.2.3