aboutsummaryrefslogtreecommitdiff
path: root/fs/nfsd/nfsctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd/nfsctl.c')
-rw-r--r--fs/nfsd/nfsctl.c723
1 files changed, 228 insertions, 495 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 4514ebbee4d..51844048937 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -8,29 +8,25 @@
#include <linux/namei.h>
#include <linux/ctype.h>
-#include <linux/nfsd_idmap.h>
#include <linux/sunrpc/svcsock.h>
-#include <linux/nfsd/syscall.h>
#include <linux/lockd/lockd.h>
-#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
+#include <linux/sunrpc/gss_api.h>
+#include <linux/sunrpc/gss_krb5_enctypes.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
+#include <linux/module.h>
+#include "idmap.h"
#include "nfsd.h"
#include "cache.h"
+#include "state.h"
+#include "netns.h"
/*
- * We have a single directory with 9 nodes in it.
+ * We have a single directory with several nodes in it.
*/
enum {
NFSD_Root = 1,
-#ifdef CONFIG_NFSD_DEPRECATED
- NFSD_Svc,
- NFSD_Add,
- NFSD_Del,
- NFSD_Export,
- NFSD_Unexport,
- NFSD_Getfd,
- NFSD_Getfs,
-#endif
NFSD_List,
NFSD_Export_features,
NFSD_Fh,
@@ -39,9 +35,11 @@ enum {
NFSD_Threads,
NFSD_Pool_Threads,
NFSD_Pool_Stats,
+ NFSD_Reply_Cache_Stats,
NFSD_Versions,
NFSD_Ports,
NFSD_MaxBlkSize,
+ NFSD_SupportedEnctypes,
/*
* The below MUST come last. Otherwise we leave a hole in nfsd_files[]
* with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
@@ -56,15 +54,6 @@ enum {
/*
* write() for these nodes.
*/
-#ifdef CONFIG_NFSD_DEPRECATED
-static ssize_t write_svc(struct file *file, char *buf, size_t size);
-static ssize_t write_add(struct file *file, char *buf, size_t size);
-static ssize_t write_del(struct file *file, char *buf, size_t size);
-static ssize_t write_export(struct file *file, char *buf, size_t size);
-static ssize_t write_unexport(struct file *file, char *buf, size_t size);
-static ssize_t write_getfd(struct file *file, char *buf, size_t size);
-static ssize_t write_getfs(struct file *file, char *buf, size_t size);
-#endif
static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
@@ -80,15 +69,6 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
#endif
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
-#ifdef CONFIG_NFSD_DEPRECATED
- [NFSD_Svc] = write_svc,
- [NFSD_Add] = write_add,
- [NFSD_Del] = write_del,
- [NFSD_Export] = write_export,
- [NFSD_Unexport] = write_unexport,
- [NFSD_Getfd] = write_getfd,
- [NFSD_Getfs] = write_getfs,
-#endif
[NFSD_Fh] = write_filehandle,
[NFSD_FO_UnlockIP] = write_unlock_ip,
[NFSD_FO_UnlockFS] = write_unlock_fs,
@@ -106,7 +86,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
{
- ino_t ino = file->f_path.dentry->d_inode->i_ino;
+ ino_t ino = file_inode(file)->i_ino;
char *data;
ssize_t rv;
@@ -127,14 +107,6 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu
static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
{
- static int warned;
- if (file->f_dentry->d_name.name[0] == '.' && !warned) {
- printk(KERN_INFO
- "Warning: \"%s\" uses deprecated NFSD interface: %s."
- " This will be removed in 2.6.40\n",
- current->comm, file->f_dentry->d_name.name);
- warned = 1;
- }
if (! file->private_data) {
/* An attempt to read a transaction file without writing
* causes a 0-byte write so that the file can return
@@ -154,13 +126,41 @@ static const struct file_operations transaction_ops = {
.llseek = default_llseek,
};
-static int exports_open(struct inode *inode, struct file *file)
+static int exports_net_open(struct net *net, struct file *file)
+{
+ int err;
+ struct seq_file *seq;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ err = seq_open(file, &nfs_exports_op);
+ if (err)
+ return err;
+
+ seq = file->private_data;
+ seq->private = nn->svc_export_cache;
+ return 0;
+}
+
+static int exports_proc_open(struct inode *inode, struct file *file)
+{
+ return exports_net_open(current->nsproxy->net_ns, file);
+}
+
+static const struct file_operations exports_proc_operations = {
+ .open = exports_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+ .owner = THIS_MODULE,
+};
+
+static int exports_nfsd_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &nfs_exports_op);
+ return exports_net_open(inode->i_sb->s_fs_info, file);
}
-static const struct file_operations exports_operations = {
- .open = exports_open,
+static const struct file_operations exports_nfsd_operations = {
+ .open = exports_nfsd_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
@@ -178,15 +178,32 @@ static int export_features_open(struct inode *inode, struct file *file)
return single_open(file, export_features_show, NULL);
}
-static struct file_operations export_features_operations = {
+static const struct file_operations export_features_operations = {
.open = export_features_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
-extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
-extern int nfsd_pool_stats_release(struct inode *inode, struct file *file);
+#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
+static int supported_enctypes_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, KRB5_SUPPORTED_ENCTYPES);
+ return 0;
+}
+
+static int supported_enctypes_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, supported_enctypes_show, NULL);
+}
+
+static const struct file_operations supported_enctypes_ops = {
+ .open = supported_enctypes_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
static const struct file_operations pool_stats_operations = {
.open = nfsd_pool_stats_open,
@@ -196,308 +213,18 @@ static const struct file_operations pool_stats_operations = {
.owner = THIS_MODULE,
};
+static struct file_operations reply_cache_stats_operations = {
+ .open = nfsd_reply_cache_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/*----------------------------------------------------------------------------*/
/*
* payload - write methods
*/
-#ifdef CONFIG_NFSD_DEPRECATED
-/**
- * write_svc - Start kernel's NFSD server
- *
- * Deprecated. /proc/fs/nfsd/threads is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_svc
- * svc_port: port number of this
- * server's listener
- * svc_nthreads: number of threads to start
- * size: size in bytes of passed in nfsctl_svc
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_svc(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_svc *data;
- int err;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_svc*) buf;
- err = nfsd_svc(data->svc_port, data->svc_nthreads);
- if (err < 0)
- return err;
- return 0;
-}
-
-/**
- * write_add - Add or modify client entry in auth unix cache
- *
- * Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_client
- * cl_ident: '\0'-terminated C string
- * containing domain name
- * of client
- * cl_naddr: no. of items in cl_addrlist
- * cl_addrlist: array of client addresses
- * cl_fhkeytype: ignored
- * cl_fhkeylen: ignored
- * cl_fhkey: ignored
- * size: size in bytes of passed in nfsctl_client
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since
- * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
- */
-static ssize_t write_add(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_client *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_client *)buf;
- return exp_addclient(data);
-}
-
-/**
- * write_del - Remove client from auth unix cache
- *
- * Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_client
- * cl_ident: '\0'-terminated C string
- * containing domain name
- * of client
- * cl_naddr: ignored
- * cl_addrlist: ignored
- * cl_fhkeytype: ignored
- * cl_fhkeylen: ignored
- * cl_fhkey: ignored
- * size: size in bytes of passed in nfsctl_client
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since
- * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
- */
-static ssize_t write_del(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_client *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_client *)buf;
- return exp_delclient(data);
-}
-
-/**
- * write_export - Export part or all of a local file system
- *
- * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_export
- * ex_client: '\0'-terminated C string
- * containing domain name
- * of client allowed to access
- * this export
- * ex_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * ex_dev: fsid to use for this export
- * ex_ino: ignored
- * ex_flags: export flags for this export
- * ex_anon_uid: UID to use for anonymous
- * requests
- * ex_anon_gid: GID to use for anonymous
- * requests
- * size: size in bytes of passed in nfsctl_export
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_export(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_export *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_export*)buf;
- return exp_export(data);
-}
-
-/**
- * write_unexport - Unexport a previously exported file system
- *
- * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_export
- * ex_client: '\0'-terminated C string
- * containing domain name
- * of client no longer allowed
- * to access this export
- * ex_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * ex_dev: ignored
- * ex_ino: ignored
- * ex_flags: ignored
- * ex_anon_uid: ignored
- * ex_anon_gid: ignored
- * size: size in bytes of passed in nfsctl_export
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_unexport(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_export *data;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_export*)buf;
- return exp_unexport(data);
-}
-
-/**
- * write_getfs - Get a variable-length NFS file handle by path
- *
- * Deprecated. /proc/fs/nfsd/filehandle is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_fsparm
- * gd_addr: socket address of client
- * gd_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * gd_maxlen: maximum size of returned file
- * handle
- * size: size in bytes of passed in nfsctl_fsparm
- * Output:
- * On success: passed-in buffer filled with a knfsd_fh structure
- * (a variable-length raw NFS file handle);
- * return code is the size in bytes of the file handle
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since gd_addr
- * is the same size as a struct sockaddr_in.
- */
-static ssize_t write_getfs(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_fsparm *data;
- struct sockaddr_in *sin;
- struct auth_domain *clp;
- int err = 0;
- struct knfsd_fh *res;
- struct in6_addr in6;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_fsparm*)buf;
- err = -EPROTONOSUPPORT;
- if (data->gd_addr.sa_family != AF_INET)
- goto out;
- sin = (struct sockaddr_in *)&data->gd_addr;
- if (data->gd_maxlen > NFS3_FHSIZE)
- data->gd_maxlen = NFS3_FHSIZE;
-
- res = (struct knfsd_fh*)buf;
-
- exp_readlock();
-
- ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
-
- clp = auth_unix_lookup(&init_net, &in6);
- if (!clp)
- err = -EPERM;
- else {
- err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
- auth_domain_put(clp);
- }
- exp_readunlock();
- if (err == 0)
- err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
- out:
- return err;
-}
-
-/**
- * write_getfd - Get a fixed-length NFS file handle by path (used by mountd)
- *
- * Deprecated. /proc/fs/nfsd/filehandle is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_fdparm
- * gd_addr: socket address of client
- * gd_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * gd_version: fdparm structure version
- * size: size in bytes of passed in nfsctl_fdparm
- * Output:
- * On success: passed-in buffer filled with nfsctl_res
- * (a fixed-length raw NFS file handle);
- * return code is the size in bytes of the file handle
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since gd_addr
- * is the same size as a struct sockaddr_in.
- */
-static ssize_t write_getfd(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_fdparm *data;
- struct sockaddr_in *sin;
- struct auth_domain *clp;
- int err = 0;
- struct knfsd_fh fh;
- char *res;
- struct in6_addr in6;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_fdparm*)buf;
- err = -EPROTONOSUPPORT;
- if (data->gd_addr.sa_family != AF_INET)
- goto out;
- err = -EINVAL;
- if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
- goto out;
-
- res = buf;
- sin = (struct sockaddr_in *)&data->gd_addr;
- exp_readlock();
-
- ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
-
- clp = auth_unix_lookup(&init_net, &in6);
- if (!clp)
- err = -EPERM;
- else {
- err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
- auth_domain_put(clp);
- }
- exp_readunlock();
-
- if (err == 0) {
- memset(res,0, NFS_FHSIZE);
- memcpy(res, &fh.fh_base, fh.fh_size);
- err = NFS_FHSIZE;
- }
- out:
- return err;
-}
-#endif /* CONFIG_NFSD_DEPRECATED */
/**
* write_unlock_ip - Release all locks used by a client
@@ -519,6 +246,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
struct sockaddr *sap = (struct sockaddr *)&address;
size_t salen = sizeof(address);
char *fo_path;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
/* sanity check */
if (size == 0)
@@ -531,7 +259,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
if (qword_get(&buf, fo_path, size) < 0)
return -EINVAL;
- if (rpc_pton(fo_path, size, sap, salen) == 0)
+ if (rpc_pton(net, fo_path, size, sap, salen) == 0)
return -EINVAL;
return nlmsvc_unlock_all_by_ip(sap);
@@ -581,7 +309,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
* 2. Is that directory a mount point, or
* 3. Is that directory the root of an exported file system?
*/
- error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
+ error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
path_put(&path);
return error;
@@ -616,6 +344,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
int len;
struct auth_domain *dom;
struct knfsd_fh fh;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
if (size == 0)
return -EINVAL;
@@ -651,7 +380,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
if (!dom)
return -ENOMEM;
- len = exp_rootfh(dom, path, &fh, maxsize);
+ len = exp_rootfh(net, dom, path, &fh, maxsize);
auth_domain_put(dom);
if (len)
return len;
@@ -695,6 +424,8 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
int rv;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+
if (size > 0) {
int newthreads;
rv = get_int(&mesg, &newthreads);
@@ -702,11 +433,11 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return rv;
if (newthreads < 0)
return -EINVAL;
- rv = nfsd_svc(NFS_PORT, newthreads);
+ rv = nfsd_svc(newthreads, net);
if (rv < 0)
return rv;
} else
- rv = nfsd_nrthreads();
+ rv = nfsd_nrthreads(net);
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv);
}
@@ -744,9 +475,10 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
int len;
int npools;
int *nthreads;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
mutex_lock(&nfsd_mutex);
- npools = nfsd_nrpools();
+ npools = nfsd_nrpools(net);
if (npools == 0) {
/*
* NFS is shut down. The admin can start it by
@@ -774,12 +506,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
if (nthreads[i] < 0)
goto out_free;
}
- rv = nfsd_set_nrthreads(i, nthreads);
+ rv = nfsd_set_nrthreads(i, nthreads, net);
if (rv)
goto out_free;
}
- rv = nfsd_get_nrthreads(npools, nthreads);
+ rv = nfsd_get_nrthreads(npools, nthreads, net);
if (rv)
goto out_free;
@@ -806,11 +538,13 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
unsigned minor;
ssize_t tlen = 0;
char *sep;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (size>0) {
- if (nfsd_serv)
+ if (nn->nfsd_serv)
/* Cannot change versions without updating
- * nfsd_serv->sv_xdrsize, and reallocing
+ * nn->nfsd_serv->sv_xdrsize, and reallocing
* rq_argp and rq_resp
*/
return -EBUSY;
@@ -828,7 +562,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
else
num = simple_strtol(vers, &minorp, 0);
if (*minorp == '.') {
- if (num < 4)
+ if (num != 4)
return -EINVAL;
minor = simple_strtoul(minorp+1, NULL, 0);
if (minor == 0)
@@ -941,11 +675,13 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
* Zero-length write. Return a list of NFSD's current listener
* transports.
*/
-static ssize_t __write_ports_names(char *buf)
+static ssize_t __write_ports_names(char *buf, struct net *net)
{
- if (nfsd_serv == NULL)
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ if (nn->nfsd_serv == NULL)
return 0;
- return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
+ return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
}
/*
@@ -953,134 +689,92 @@ static ssize_t __write_ports_names(char *buf)
* a socket of a supported family/protocol, and we use it as an
* nfsd listener.
*/
-static ssize_t __write_ports_addfd(char *buf)
+static ssize_t __write_ports_addfd(char *buf, struct net *net)
{
char *mesg = buf;
int fd, err;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
err = get_int(&mesg, &fd);
if (err != 0 || fd < 0)
return -EINVAL;
- err = nfsd_create_serv();
+ if (svc_alien_sock(net, fd)) {
+ printk(KERN_ERR "%s: socket net is different to NFSd's one\n", __func__);
+ return -EINVAL;
+ }
+
+ err = nfsd_create_serv(net);
if (err != 0)
return err;
- err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
+ err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
if (err < 0) {
- svc_destroy(nfsd_serv);
+ nfsd_destroy(net);
return err;
}
/* Decrease the count, but don't shut down the service */
- nfsd_serv->sv_nrthreads--;
+ nn->nfsd_serv->sv_nrthreads--;
return err;
}
/*
- * A '-' followed by the 'name' of a socket means we close the socket.
- */
-static ssize_t __write_ports_delfd(char *buf)
-{
- char *toclose;
- int len = 0;
-
- toclose = kstrdup(buf + 1, GFP_KERNEL);
- if (toclose == NULL)
- return -ENOMEM;
-
- if (nfsd_serv != NULL)
- len = svc_sock_names(nfsd_serv, buf,
- SIMPLE_TRANSACTION_LIMIT, toclose);
- kfree(toclose);
- return len;
-}
-
-/*
* A transport listener is added by writing it's transport name and
* a port number.
*/
-static ssize_t __write_ports_addxprt(char *buf)
+static ssize_t __write_ports_addxprt(char *buf, struct net *net)
{
char transport[16];
struct svc_xprt *xprt;
int port, err;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- if (sscanf(buf, "%15s %4u", transport, &port) != 2)
+ if (sscanf(buf, "%15s %5u", transport, &port) != 2)
return -EINVAL;
if (port < 1 || port > USHRT_MAX)
return -EINVAL;
- err = nfsd_create_serv();
+ err = nfsd_create_serv(net);
if (err != 0)
return err;
- err = svc_create_xprt(nfsd_serv, transport, &init_net,
+ err = svc_create_xprt(nn->nfsd_serv, transport, net,
PF_INET, port, SVC_SOCK_ANONYMOUS);
if (err < 0)
goto out_err;
- err = svc_create_xprt(nfsd_serv, transport, &init_net,
+ err = svc_create_xprt(nn->nfsd_serv, transport, net,
PF_INET6, port, SVC_SOCK_ANONYMOUS);
if (err < 0 && err != -EAFNOSUPPORT)
goto out_close;
/* Decrease the count, but don't shut down the service */
- nfsd_serv->sv_nrthreads--;
+ nn->nfsd_serv->sv_nrthreads--;
return 0;
out_close:
- xprt = svc_find_xprt(nfsd_serv, transport, PF_INET, port);
+ xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port);
if (xprt != NULL) {
svc_close_xprt(xprt);
svc_xprt_put(xprt);
}
out_err:
- svc_destroy(nfsd_serv);
+ nfsd_destroy(net);
return err;
}
-/*
- * A transport listener is removed by writing a "-", it's transport
- * name, and it's port number.
- */
-static ssize_t __write_ports_delxprt(char *buf)
-{
- struct svc_xprt *xprt;
- char transport[16];
- int port;
-
- if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
- return -EINVAL;
-
- if (port < 1 || port > USHRT_MAX || nfsd_serv == NULL)
- return -EINVAL;
-
- xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
- if (xprt == NULL)
- return -ENOTCONN;
-
- svc_close_xprt(xprt);
- svc_xprt_put(xprt);
- return 0;
-}
-
-static ssize_t __write_ports(struct file *file, char *buf, size_t size)
+static ssize_t __write_ports(struct file *file, char *buf, size_t size,
+ struct net *net)
{
if (size == 0)
- return __write_ports_names(buf);
+ return __write_ports_names(buf, net);
if (isdigit(buf[0]))
- return __write_ports_addfd(buf);
-
- if (buf[0] == '-' && isdigit(buf[1]))
- return __write_ports_delfd(buf);
+ return __write_ports_addfd(buf, net);
if (isalpha(buf[0]))
- return __write_ports_addxprt(buf);
-
- if (buf[0] == '-' && isalpha(buf[1]))
- return __write_ports_delxprt(buf);
+ return __write_ports_addxprt(buf, net);
return -EINVAL;
}
@@ -1119,21 +813,6 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
* OR
*
* Input:
- * buf: C string containing a "-" followed
- * by an integer value representing a
- * previously passed in socket file
- * descriptor
- * size: non-zero length of C string in @buf
- * Output:
- * On success: NFS service no longer listens on that socket;
- * passed-in buffer filled with a '\n'-terminated C
- * string containing a unique name of the listener;
- * return code is the size in bytes of the string
- * On error: return code is a negative errno value
- *
- * OR
- *
- * Input:
* buf: C string containing a transport
* name and an unsigned integer value
* representing the port to listen on,
@@ -1142,26 +821,14 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
* Output:
* On success: returns zero; NFS service is started
* On error: return code is a negative errno value
- *
- * OR
- *
- * Input:
- * buf: C string containing a "-" followed
- * by a transport name and an unsigned
- * integer value representing the port
- * to listen on, separated by whitespace
- * size: non-zero length of C string in @buf
- * Output:
- * On success: returns zero; NFS service no longer listens
- * on that transport
- * On error: return code is a negative errno value
*/
static ssize_t write_ports(struct file *file, char *buf, size_t size)
{
ssize_t rv;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
mutex_lock(&nfsd_mutex);
- rv = __write_ports(file, buf, size);
+ rv = __write_ports(file, buf, size, net);
mutex_unlock(&nfsd_mutex);
return rv;
}
@@ -1193,6 +860,9 @@ int nfsd_max_blksize;
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
if (size > 0) {
int bsize;
int rv = get_int(&mesg, &bsize);
@@ -1207,7 +877,7 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
bsize = NFSSVC_MAXBLKSIZE;
bsize &= ~(1024-1);
mutex_lock(&nfsd_mutex);
- if (nfsd_serv) {
+ if (nn->nfsd_serv) {
mutex_unlock(&nfsd_mutex);
return -EBUSY;
}
@@ -1220,13 +890,14 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
}
#ifdef CONFIG_NFSD_V4
-static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
+static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
+ time_t *time, struct nfsd_net *nn)
{
char *mesg = buf;
int rv, i;
if (size > 0) {
- if (nfsd_serv)
+ if (nn->nfsd_serv)
return -EBUSY;
rv = get_int(&mesg, &i);
if (rv)
@@ -1251,12 +922,13 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, tim
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time);
}
-static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
+static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
+ time_t *time, struct nfsd_net *nn)
{
ssize_t rv;
mutex_lock(&nfsd_mutex);
- rv = __nfsd4_write_time(file, buf, size, time);
+ rv = __nfsd4_write_time(file, buf, size, time, nn);
mutex_unlock(&nfsd_mutex);
return rv;
}
@@ -1284,7 +956,9 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_
*/
static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
{
- return nfsd4_write_time(file, buf, size, &nfsd4_lease);
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn);
}
/**
@@ -1299,19 +973,20 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
*/
static ssize_t write_gracetime(struct file *file, char *buf, size_t size)
{
- return nfsd4_write_time(file, buf, size, &nfsd4_grace);
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn);
}
-extern char *nfs4_recoverydir(void);
-
-static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
+static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size,
+ struct nfsd_net *nn)
{
char *mesg = buf;
char *recdir;
int len, status;
if (size > 0) {
- if (nfsd_serv)
+ if (nn->nfsd_serv)
return -EBUSY;
if (size > PATH_MAX || buf[size-1] != '\n')
return -EINVAL;
@@ -1355,9 +1030,11 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
{
ssize_t rv;
+ struct net *net = file->f_dentry->d_sb->s_fs_info;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
mutex_lock(&nfsd_mutex);
- rv = __write_recoverydir(file, buf, size);
+ rv = __write_recoverydir(file, buf, size, nn);
mutex_unlock(&nfsd_mutex);
return rv;
}
@@ -1372,16 +1049,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
{
static struct tree_descr nfsd_files[] = {
-#ifdef CONFIG_NFSD_DEPRECATED
- [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
- [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
- [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
- [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
- [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
- [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
- [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
-#endif
- [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
+ [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
[NFSD_Export_features] = {"export_features",
&export_features_operations, S_IRUGO},
[NFSD_FO_UnlockIP] = {"unlock_ip",
@@ -1392,9 +1060,13 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
+ [NFSD_Reply_Cache_Stats] = {"reply_cache_stats", &reply_cache_stats_operations, S_IRUGO},
[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
+#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
+ [NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO},
+#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
#ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR},
@@ -1402,21 +1074,37 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
#endif
/* last one */ {""}
};
- return simple_fill_super(sb, 0x6e667364, nfsd_files);
+ struct net *net = data;
+ int ret;
+
+ ret = simple_fill_super(sb, 0x6e667364, nfsd_files);
+ if (ret)
+ return ret;
+ sb->s_fs_info = get_net(net);
+ return 0;
}
static struct dentry *nfsd_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- return mount_single(fs_type, flags, data, nfsd_fill_super);
+ return mount_ns(fs_type, flags, current->nsproxy->net_ns, nfsd_fill_super);
+}
+
+static void nfsd_umount(struct super_block *sb)
+{
+ struct net *net = sb->s_fs_info;
+
+ kill_litter_super(sb);
+ put_net(net);
}
static struct file_system_type nfsd_fs_type = {
.owner = THIS_MODULE,
.name = "nfsd",
.mount = nfsd_mount,
- .kill_sb = kill_litter_super,
+ .kill_sb = nfsd_umount,
};
+MODULE_ALIAS_FS("nfsd");
#ifdef CONFIG_PROC_FS
static int create_proc_exports_entry(void)
@@ -1426,9 +1114,12 @@ static int create_proc_exports_entry(void)
entry = proc_mkdir("fs/nfs", NULL);
if (!entry)
return -ENOMEM;
- entry = proc_create("exports", 0, entry, &exports_operations);
- if (!entry)
+ entry = proc_create("exports", 0, entry,
+ &exports_proc_operations);
+ if (!entry) {
+ remove_proc_entry("fs/nfs", NULL);
return -ENOMEM;
+ }
return 0;
}
#else /* CONFIG_PROC_FS */
@@ -1438,28 +1129,67 @@ static int create_proc_exports_entry(void)
}
#endif
+int nfsd_net_id;
+
+static __net_init int nfsd_init_net(struct net *net)
+{
+ int retval;
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ retval = nfsd_export_init(net);
+ if (retval)
+ goto out_export_error;
+ retval = nfsd_idmap_init(net);
+ if (retval)
+ goto out_idmap_error;
+ nn->nfsd4_lease = 90; /* default lease time */
+ nn->nfsd4_grace = 90;
+ return 0;
+
+out_idmap_error:
+ nfsd_export_shutdown(net);
+out_export_error:
+ return retval;
+}
+
+static __net_exit void nfsd_exit_net(struct net *net)
+{
+ nfsd_idmap_shutdown(net);
+ nfsd_export_shutdown(net);
+}
+
+static struct pernet_operations nfsd_net_ops = {
+ .init = nfsd_init_net,
+ .exit = nfsd_exit_net,
+ .id = &nfsd_net_id,
+ .size = sizeof(struct nfsd_net),
+};
+
static int __init init_nfsd(void)
{
int retval;
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
- retval = nfs4_state_init(); /* nfs4 locking state */
+ retval = register_cld_notifier();
if (retval)
return retval;
+ retval = register_pernet_subsys(&nfsd_net_ops);
+ if (retval < 0)
+ goto out_unregister_notifier;
+ retval = nfsd4_init_slabs();
+ if (retval)
+ goto out_unregister_pernet;
+ retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */
+ if (retval)
+ goto out_free_slabs;
nfsd_stat_init(); /* Statistics */
retval = nfsd_reply_cache_init();
if (retval)
goto out_free_stat;
- retval = nfsd_export_init();
- if (retval)
- goto out_free_cache;
nfsd_lockd_init(); /* lockd->nfsd callbacks */
- retval = nfsd_idmap_init();
- if (retval)
- goto out_free_lockd;
retval = create_proc_exports_entry();
if (retval)
- goto out_free_idmap;
+ goto out_free_lockd;
retval = register_filesystem(&nfsd_fs_type);
if (retval)
goto out_free_all;
@@ -1467,30 +1197,33 @@ static int __init init_nfsd(void)
out_free_all:
remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL);
-out_free_idmap:
- nfsd_idmap_shutdown();
out_free_lockd:
nfsd_lockd_shutdown();
- nfsd_export_shutdown();
-out_free_cache:
nfsd_reply_cache_shutdown();
out_free_stat:
nfsd_stat_shutdown();
+ nfsd_fault_inject_cleanup();
+out_free_slabs:
nfsd4_free_slabs();
+out_unregister_pernet:
+ unregister_pernet_subsys(&nfsd_net_ops);
+out_unregister_notifier:
+ unregister_cld_notifier();
return retval;
}
static void __exit exit_nfsd(void)
{
- nfsd_export_shutdown();
nfsd_reply_cache_shutdown();
remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL);
nfsd_stat_shutdown();
nfsd_lockd_shutdown();
- nfsd_idmap_shutdown();
nfsd4_free_slabs();
+ nfsd_fault_inject_cleanup();
unregister_filesystem(&nfsd_fs_type);
+ unregister_pernet_subsys(&nfsd_net_ops);
+ unregister_cld_notifier();
}
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");