diff options
Diffstat (limited to 'fs/nfsd/nfsctl.c')
| -rw-r--r-- | fs/nfsd/nfsctl.c | 723 | 
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>");  | 
