/* Mount options that take string arguments */
Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
Opt_addr, Opt_mountaddr, Opt_clientaddr,
+ Opt_lookupcache,
- /* Mount options that are ignored */
- Opt_userspace, Opt_deprecated,
+ /* Special mount options */
+ Opt_userspace, Opt_deprecated, Opt_sloppy,
Opt_err
};
{ Opt_userspace, "fg" },
{ Opt_userspace, "retry=%s" },
+ { Opt_sloppy, "sloppy" },
+
{ Opt_soft, "soft" },
{ Opt_hard, "hard" },
{ Opt_deprecated, "intr" },
{ Opt_mounthost, "mounthost=%s" },
{ Opt_mountaddr, "mountaddr=%s" },
+ { Opt_lookupcache, "lookupcache=%s" },
+
{ Opt_err, NULL }
};
{ Opt_sec_err, NULL }
};
+enum {
+ Opt_lookupcache_all, Opt_lookupcache_positive,
+ Opt_lookupcache_none,
+
+ Opt_lookupcache_err
+};
+
+static match_table_t nfs_lookupcache_tokens = {
+ { Opt_lookupcache_all, "all" },
+ { Opt_lookupcache_positive, "pos" },
+ { Opt_lookupcache_positive, "positive" },
+ { Opt_lookupcache_none, "none" },
+
+ { Opt_lookupcache_err, NULL }
+};
+
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *);
static int nfs_xdev_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
static void nfs_kill_super(struct super_block *);
-static void nfs_put_super(struct super_block *);
static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
static struct file_system_type nfs_fs_type = {
.alloc_inode = nfs_alloc_inode,
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
- .put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin,
unregister_filesystem(&nfs_fs_type);
}
-void nfs_sb_active(struct nfs_server *server)
+void nfs_sb_active(struct super_block *sb)
{
- atomic_inc(&server->active);
-}
+ struct nfs_server *server = NFS_SB(sb);
-void nfs_sb_deactive(struct nfs_server *server)
-{
- if (atomic_dec_and_test(&server->active))
- wake_up(&server->active_wq);
+ if (atomic_inc_return(&server->active) == 1)
+ atomic_inc(&sb->s_active);
}
-static void nfs_put_super(struct super_block *sb)
+void nfs_sb_deactive(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
- /*
- * Make sure there are no outstanding ops to this server.
- * If so, wait for them to finish before allowing the
- * unmount to continue.
- */
- wait_event(server->active_wq, atomic_read(&server->active) == 0);
+
+ if (atomic_dec_and_test(&server->active))
+ deactivate_super(sb);
}
/*
};
int error;
- lock_kernel();
-
error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
if (error < 0)
goto out_err;
buf->f_namelen = server->namelen;
- unlock_kernel();
return 0;
out_err:
dprintk("%s: statfs error = %d\n", __func__, -error);
- unlock_kernel();
return error;
}
}
/*
- * Set the port number in an address. Be agnostic about the address family.
- */
-static void nfs_set_port(struct sockaddr *sap, unsigned short port)
-{
- switch (sap->sa_family) {
- case AF_INET: {
- struct sockaddr_in *ap = (struct sockaddr_in *)sap;
- ap->sin_port = htons(port);
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
- ap->sin6_port = htons(port);
- break;
- }
- }
-}
-
-/*
* Sanity-check a server address provided by the mount command.
*
* Address family must be initialized, and address must not be
*addr_len = 0;
}
-#define IPV6_SCOPE_DELIMITER '%'
-
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
const char *delim,
* 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,
+void nfs_parse_ip_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
unsigned int i, colons;
/*
* Parse the value of the 'sec=' option.
*
- * The flags setting is for v2/v3. The flavor_len setting is for v4.
- * v2/v3 also need to know the difference between NULL and UNIX.
+ * The flavor_len setting is for v4 mounts.
*/
static int nfs_parse_security_flavors(char *value,
struct nfs_parsed_mount_data *mnt)
switch (match_token(value, nfs_secflavor_tokens, args)) {
case Opt_sec_none:
- mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_NULL;
break;
case Opt_sec_sys:
- mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_UNIX;
break;
case Opt_sec_krb5:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
break;
case Opt_sec_krb5i:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
break;
case Opt_sec_krb5p:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
break;
case Opt_sec_lkey:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
break;
case Opt_sec_lkeyi:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
break;
case Opt_sec_lkeyp:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
break;
case Opt_sec_spkm:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
break;
case Opt_sec_spkmi:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
break;
case Opt_sec_spkmp:
- mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
break;
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
+ * 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).
*/
static int nfs_parse_mount_options(char *raw,
struct nfs_parsed_mount_data *mnt)
{
char *p, *string, *secdata;
- int rc;
+ int rc, sloppy = 0, errors = 0;
if (!raw) {
dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
token = match_token(p, nfs_mount_option_tokens, args);
switch (token) {
+
+ /*
+ * boolean options: foo/nofoo
+ */
case Opt_soft:
mnt->flags |= NFS_MOUNT_SOFT;
break;
mnt->flags |= NFS_MOUNT_UNSHARED;
break;
+ /*
+ * options that take numeric values
+ */
case Opt_port:
- if (match_int(args, &option))
- return 0;
- if (option < 0 || option > 65535)
- return 0;
- mnt->nfs_server.port = option;
+ if (match_int(args, &option) ||
+ option < 0 || option > USHORT_MAX) {
+ errors++;
+ nfs_parse_invalid_value("port");
+ } else
+ mnt->nfs_server.port = option;
break;
case Opt_rsize:
- if (match_int(args, &mnt->rsize))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("rsize");
+ } else
+ mnt->rsize = option;
break;
case Opt_wsize:
- if (match_int(args, &mnt->wsize))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("wsize");
+ } else
+ mnt->wsize = option;
break;
case Opt_bsize:
- if (match_int(args, &option))
- return 0;
- if (option < 0)
- return 0;
- mnt->bsize = option;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("bsize");
+ } else
+ mnt->bsize = option;
break;
case Opt_timeo:
- if (match_int(args, &mnt->timeo))
- return 0;
+ if (match_int(args, &option) || option <= 0) {
+ errors++;
+ nfs_parse_invalid_value("timeo");
+ } else
+ mnt->timeo = option;
break;
case Opt_retrans:
- if (match_int(args, &mnt->retrans))
- return 0;
+ if (match_int(args, &option) || option <= 0) {
+ errors++;
+ nfs_parse_invalid_value("retrans");
+ } else
+ mnt->retrans = option;
break;
case Opt_acregmin:
- if (match_int(args, &mnt->acregmin))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("acregmin");
+ } else
+ mnt->acregmin = option;
break;
case Opt_acregmax:
- if (match_int(args, &mnt->acregmax))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("acregmax");
+ } else
+ mnt->acregmax = option;
break;
case Opt_acdirmin:
- if (match_int(args, &mnt->acdirmin))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("acdirmin");
+ } else
+ mnt->acdirmin = option;
break;
case Opt_acdirmax:
- if (match_int(args, &mnt->acdirmax))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("acdirmax");
+ } else
+ mnt->acdirmax = option;
break;
case Opt_actimeo:
- if (match_int(args, &option))
- return 0;
- if (option < 0)
- return 0;
- mnt->acregmin =
- mnt->acregmax =
- mnt->acdirmin =
- mnt->acdirmax = option;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("actimeo");
+ } else
+ mnt->acregmin = mnt->acregmax =
+ mnt->acdirmin = mnt->acdirmax = option;
break;
case Opt_namelen:
- if (match_int(args, &mnt->namlen))
- return 0;
+ if (match_int(args, &option) || option < 0) {
+ errors++;
+ nfs_parse_invalid_value("namlen");
+ } else
+ mnt->namlen = option;
break;
case Opt_mountport:
- if (match_int(args, &option))
- return 0;
- if (option < 0 || option > 65535)
- return 0;
- mnt->mount_server.port = option;
+ if (match_int(args, &option) ||
+ option < 0 || option > USHORT_MAX) {
+ errors++;
+ nfs_parse_invalid_value("mountport");
+ } else
+ mnt->mount_server.port = option;
break;
case Opt_mountvers:
- if (match_int(args, &option))
- return 0;
- if (option < 0)
- return 0;
- mnt->mount_server.version = option;
+ 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;
break;
case Opt_nfsvers:
- if (match_int(args, &option))
- return 0;
+ if (match_int(args, &option)) {
+ errors++;
+ nfs_parse_invalid_value("nfsvers");
+ break;
+ }
switch (option) {
- case 2:
+ case NFS2_VERSION:
mnt->flags &= ~NFS_MOUNT_VER3;
break;
- case 3:
+ case NFS3_VERSION:
mnt->flags |= NFS_MOUNT_VER3;
break;
default:
- goto out_unrec_vers;
+ errors++;
+ nfs_parse_invalid_value("nfsvers");
}
break;
+ /*
+ * options that take text values
+ */
case Opt_sec:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = nfs_parse_security_flavors(string, mnt);
kfree(string);
- if (!rc)
- goto out_unrec_sec;
+ if (!rc) {
+ errors++;
+ dfprintk(MOUNT, "NFS: unrecognized "
+ "security flavor\n");
+ }
break;
case Opt_proto:
string = match_strdup(args);
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
break;
default:
- goto out_unrec_xprt;
+ errors++;
+ dfprintk(MOUNT, "NFS: unrecognized "
+ "transport protocol\n");
}
break;
case Opt_mountproto:
break;
case Opt_xprt_rdma: /* not used for side protocols */
default:
- goto out_unrec_xprt;
+ errors++;
+ dfprintk(MOUNT, "NFS: unrecognized "
+ "transport protocol\n");
}
break;
case Opt_addr:
&mnt->mount_server.addrlen);
kfree(string);
break;
+ case Opt_lookupcache:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+ token = match_token(string,
+ nfs_lookupcache_tokens, args);
+ kfree(string);
+ switch (token) {
+ case Opt_lookupcache_all:
+ mnt->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
+ break;
+ case Opt_lookupcache_positive:
+ mnt->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
+ mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
+ break;
+ case Opt_lookupcache_none:
+ mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
+ break;
+ default:
+ errors++;
+ dfprintk(MOUNT, "NFS: invalid "
+ "lookupcache argument\n");
+ };
+ break;
+ /*
+ * Special options
+ */
+ case Opt_sloppy:
+ sloppy = 1;
+ dfprintk(MOUNT, "NFS: relaxing parsing rules\n");
+ break;
case Opt_userspace:
case Opt_deprecated:
dfprintk(MOUNT, "NFS: ignoring mount option "
break;
default:
- goto out_unknown;
+ errors++;
+ 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;
+ }
return 1;
out_nomem:
free_secdata(secdata);
printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
return 0;
-out_unrec_vers:
- printk(KERN_INFO "NFS: unrecognized NFS version number\n");
- return 0;
-
-out_unrec_xprt:
- printk(KERN_INFO "NFS: unrecognized transport protocol\n");
- return 0;
-
-out_unrec_sec:
- printk(KERN_INFO "NFS: unrecognized security flavor\n");
- return 0;
-
-out_unknown:
- printk(KERN_INFO "NFS: unknown mount option: %s\n", p);
- return 0;
}
/*
args->mount_server.port = 0; /* autobind unless user sets port */
args->nfs_server.port = 0; /* autobind unless user sets port */
args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+ args->auth_flavors[0] = RPC_AUTH_UNIX;
switch (data->version) {
case 1:
* Translate to nfs_parsed_mount_data, which nfs_fill_super
* can deal with.
*/
- args->flags = data->flags;
+ args->flags = data->flags & NFS_MOUNT_FLAGMASK;
args->rsize = data->rsize;
args->wsize = data->wsize;
args->timeo = data->timeo;
args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
args->namlen = data->namlen;
args->bsize = data->bsize;
- args->auth_flavors[0] = data->pseudoflavor;
+
+ if (data->flags & NFS_MOUNT_SECFLAVOUR)
+ args->auth_flavors[0] = data->pseudoflavor;
if (!args->nfs_server.hostname)
goto out_nomem;
}
}
- if (!(args->flags & NFS_MOUNT_SECFLAVOUR))
- args->auth_flavors[0] = RPC_AUTH_UNIX;
-
#ifndef CONFIG_NFS_V3
if (args->flags & NFS_MOUNT_VER3)
goto out_v3_not_compiled;
* ones were explicitly specified. Fall back to legacy behavior and
* just return success.
*/
- if ((nfsvers == 4 && options4->version == 1) ||
- (nfsvers <= 3 && options->version >= 1 &&
- options->version <= 6))
+ if ((nfsvers == 4 && (!options4 || options4->version == 1)) ||
+ (nfsvers <= 3 && (!options || (options->version >= 1 &&
+ options->version <= 6))))
return 0;
data = kzalloc(sizeof(*data), GFP_KERNEL);
args->acdirmin = NFS_DEF_ACDIRMIN;
args->acdirmax = NFS_DEF_ACDIRMAX;
args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */
+ args->auth_flavors[0] = RPC_AUTH_UNIX;
+ args->auth_flavor_len = 0;
switch (data->version) {
case 1:
&args->nfs_server.address))
goto out_no_address;
- switch (data->auth_flavourlen) {
- case 0:
- args->auth_flavors[0] = RPC_AUTH_UNIX;
- break;
- case 1:
+ if (data->auth_flavourlen) {
+ if (data->auth_flavourlen > 1)
+ goto out_inval_auth;
if (copy_from_user(&args->auth_flavors[0],
data->auth_flavours,
sizeof(args->auth_flavors[0])))
return -EFAULT;
- break;
- default:
- goto out_inval_auth;
}
c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
nfs_validate_transport_protocol(args);
- switch (args->auth_flavor_len) {
- case 0:
- args->auth_flavors[0] = RPC_AUTH_UNIX;
- break;
- case 1:
- break;
- default:
+ if (args->auth_flavor_len > 1)
goto out_inval_auth;
- }
if (args->client_address == NULL)
goto out_no_client_address;