xref: /openbmc/linux/fs/nfsd/nfsctl.c (revision 3f8206d4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfsd/nfsctl.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Syscall interface to knfsd.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/linkage.h>
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/fs.h>
153f8206d4SAl Viro #include <linux/namei.h>
161da177e4SLinus Torvalds #include <linux/fcntl.h>
171da177e4SLinus Torvalds #include <linux/net.h>
181da177e4SLinus Torvalds #include <linux/in.h>
191da177e4SLinus Torvalds #include <linux/syscalls.h>
201da177e4SLinus Torvalds #include <linux/unistd.h>
211da177e4SLinus Torvalds #include <linux/slab.h>
221da177e4SLinus Torvalds #include <linux/proc_fs.h>
231da177e4SLinus Torvalds #include <linux/seq_file.h>
241da177e4SLinus Torvalds #include <linux/pagemap.h>
251da177e4SLinus Torvalds #include <linux/init.h>
264373ea84SWendy Cheng #include <linux/inet.h>
2770c3b76cSNeilBrown #include <linux/string.h>
2880212d59SNeilBrown #include <linux/smp_lock.h>
29b41b66d6SNeilBrown #include <linux/ctype.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <linux/nfs.h>
321da177e4SLinus Torvalds #include <linux/nfsd_idmap.h>
33b41b66d6SNeilBrown #include <linux/lockd/bind.h>
341da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
3580212d59SNeilBrown #include <linux/sunrpc/svcsock.h>
361da177e4SLinus Torvalds #include <linux/nfsd/nfsd.h>
371da177e4SLinus Torvalds #include <linux/nfsd/cache.h>
381da177e4SLinus Torvalds #include <linux/nfsd/xdr.h>
391da177e4SLinus Torvalds #include <linux/nfsd/syscall.h>
404373ea84SWendy Cheng #include <linux/lockd/lockd.h>
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds #include <asm/uaccess.h>
43f15364bdSAurélien Charbon #include <net/ipv6.h>
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /*
461da177e4SLinus Torvalds  *	We have a single directory with 9 nodes in it.
471da177e4SLinus Torvalds  */
481da177e4SLinus Torvalds enum {
491da177e4SLinus Torvalds 	NFSD_Root = 1,
501da177e4SLinus Torvalds 	NFSD_Svc,
511da177e4SLinus Torvalds 	NFSD_Add,
521da177e4SLinus Torvalds 	NFSD_Del,
531da177e4SLinus Torvalds 	NFSD_Export,
541da177e4SLinus Torvalds 	NFSD_Unexport,
551da177e4SLinus Torvalds 	NFSD_Getfd,
561da177e4SLinus Torvalds 	NFSD_Getfs,
571da177e4SLinus Torvalds 	NFSD_List,
581da177e4SLinus Torvalds 	NFSD_Fh,
594373ea84SWendy Cheng 	NFSD_FO_UnlockIP,
6017efa372SWendy Cheng 	NFSD_FO_UnlockFS,
611da177e4SLinus Torvalds 	NFSD_Threads,
62eed2965aSGreg Banks 	NFSD_Pool_Threads,
6370c3b76cSNeilBrown 	NFSD_Versions,
6480212d59SNeilBrown 	NFSD_Ports,
65596bbe53SNeilBrown 	NFSD_MaxBlkSize,
6670c3b76cSNeilBrown 	/*
6770c3b76cSNeilBrown 	 * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
6870c3b76cSNeilBrown 	 * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
6970c3b76cSNeilBrown 	 */
7070c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4
711da177e4SLinus Torvalds 	NFSD_Leasetime,
720964a3d3SNeilBrown 	NFSD_RecoveryDir,
7370c3b76cSNeilBrown #endif
741da177e4SLinus Torvalds };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /*
771da177e4SLinus Torvalds  * write() for these nodes.
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds static ssize_t write_svc(struct file *file, char *buf, size_t size);
801da177e4SLinus Torvalds static ssize_t write_add(struct file *file, char *buf, size_t size);
811da177e4SLinus Torvalds static ssize_t write_del(struct file *file, char *buf, size_t size);
821da177e4SLinus Torvalds static ssize_t write_export(struct file *file, char *buf, size_t size);
831da177e4SLinus Torvalds static ssize_t write_unexport(struct file *file, char *buf, size_t size);
841da177e4SLinus Torvalds static ssize_t write_getfd(struct file *file, char *buf, size_t size);
851da177e4SLinus Torvalds static ssize_t write_getfs(struct file *file, char *buf, size_t size);
861da177e4SLinus Torvalds static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
871da177e4SLinus Torvalds static ssize_t write_threads(struct file *file, char *buf, size_t size);
88eed2965aSGreg Banks static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
8970c3b76cSNeilBrown static ssize_t write_versions(struct file *file, char *buf, size_t size);
9080212d59SNeilBrown static ssize_t write_ports(struct file *file, char *buf, size_t size);
91596bbe53SNeilBrown static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
9270c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4
931da177e4SLinus Torvalds static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
940964a3d3SNeilBrown static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
9570c3b76cSNeilBrown #endif
961da177e4SLinus Torvalds 
974373ea84SWendy Cheng static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size);
9817efa372SWendy Cheng static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size);
994373ea84SWendy Cheng 
1001da177e4SLinus Torvalds static ssize_t (*write_op[])(struct file *, char *, size_t) = {
1011da177e4SLinus Torvalds 	[NFSD_Svc] = write_svc,
1021da177e4SLinus Torvalds 	[NFSD_Add] = write_add,
1031da177e4SLinus Torvalds 	[NFSD_Del] = write_del,
1041da177e4SLinus Torvalds 	[NFSD_Export] = write_export,
1051da177e4SLinus Torvalds 	[NFSD_Unexport] = write_unexport,
1061da177e4SLinus Torvalds 	[NFSD_Getfd] = write_getfd,
1071da177e4SLinus Torvalds 	[NFSD_Getfs] = write_getfs,
1081da177e4SLinus Torvalds 	[NFSD_Fh] = write_filehandle,
1094373ea84SWendy Cheng 	[NFSD_FO_UnlockIP] = failover_unlock_ip,
11017efa372SWendy Cheng 	[NFSD_FO_UnlockFS] = failover_unlock_fs,
1111da177e4SLinus Torvalds 	[NFSD_Threads] = write_threads,
112eed2965aSGreg Banks 	[NFSD_Pool_Threads] = write_pool_threads,
11370c3b76cSNeilBrown 	[NFSD_Versions] = write_versions,
11480212d59SNeilBrown 	[NFSD_Ports] = write_ports,
115596bbe53SNeilBrown 	[NFSD_MaxBlkSize] = write_maxblksize,
11670c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4
1171da177e4SLinus Torvalds 	[NFSD_Leasetime] = write_leasetime,
1180964a3d3SNeilBrown 	[NFSD_RecoveryDir] = write_recoverydir,
11970c3b76cSNeilBrown #endif
1201da177e4SLinus Torvalds };
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
1231da177e4SLinus Torvalds {
1247eaa36e2SJosef "Jeff" Sipek 	ino_t ino =  file->f_path.dentry->d_inode->i_ino;
1251da177e4SLinus Torvalds 	char *data;
1261da177e4SLinus Torvalds 	ssize_t rv;
1271da177e4SLinus Torvalds 
128e8c96f8cSTobias Klauser 	if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
1291da177e4SLinus Torvalds 		return -EINVAL;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	data = simple_transaction_get(file, buf, size);
1321da177e4SLinus Torvalds 	if (IS_ERR(data))
1331da177e4SLinus Torvalds 		return PTR_ERR(data);
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	rv =  write_op[ino](file, data, size);
1368971a101SNeilBrown 	if (rv >= 0) {
1371da177e4SLinus Torvalds 		simple_transaction_set(file, rv);
1381da177e4SLinus Torvalds 		rv = size;
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 	return rv;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
1437390022dSNeilBrown static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
1447390022dSNeilBrown {
1457390022dSNeilBrown 	if (! file->private_data) {
1467390022dSNeilBrown 		/* An attempt to read a transaction file without writing
1477390022dSNeilBrown 		 * causes a 0-byte write so that the file can return
1487390022dSNeilBrown 		 * state information
1497390022dSNeilBrown 		 */
1507390022dSNeilBrown 		ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
1517390022dSNeilBrown 		if (rv < 0)
1527390022dSNeilBrown 			return rv;
1537390022dSNeilBrown 	}
1547390022dSNeilBrown 	return simple_transaction_read(file, buf, size, pos);
1557390022dSNeilBrown }
1567390022dSNeilBrown 
1574b6f5d20SArjan van de Ven static const struct file_operations transaction_ops = {
1581da177e4SLinus Torvalds 	.write		= nfsctl_transaction_write,
1597390022dSNeilBrown 	.read		= nfsctl_transaction_read,
1601da177e4SLinus Torvalds 	.release	= simple_transaction_release,
1611da177e4SLinus Torvalds };
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds static int exports_open(struct inode *inode, struct file *file)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	return seq_open(file, &nfs_exports_op);
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds 
1684b6f5d20SArjan van de Ven static const struct file_operations exports_operations = {
1691da177e4SLinus Torvalds 	.open		= exports_open,
1701da177e4SLinus Torvalds 	.read		= seq_read,
1711da177e4SLinus Torvalds 	.llseek		= seq_lseek,
1721da177e4SLinus Torvalds 	.release	= seq_release,
1739ef2db26SDenis V. Lunev 	.owner		= THIS_MODULE,
1741da177e4SLinus Torvalds };
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/
1771da177e4SLinus Torvalds /*
1781da177e4SLinus Torvalds  * payload - write methods
1791da177e4SLinus Torvalds  * If the method has a response, the response should be put in buf,
1801da177e4SLinus Torvalds  * and the length returned.  Otherwise return 0 or and -error.
1811da177e4SLinus Torvalds  */
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds static ssize_t write_svc(struct file *file, char *buf, size_t size)
1841da177e4SLinus Torvalds {
1851da177e4SLinus Torvalds 	struct nfsctl_svc *data;
1861da177e4SLinus Torvalds 	if (size < sizeof(*data))
1871da177e4SLinus Torvalds 		return -EINVAL;
1881da177e4SLinus Torvalds 	data = (struct nfsctl_svc*) buf;
1891da177e4SLinus Torvalds 	return nfsd_svc(data->svc_port, data->svc_nthreads);
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds static ssize_t write_add(struct file *file, char *buf, size_t size)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds 	struct nfsctl_client *data;
1951da177e4SLinus Torvalds 	if (size < sizeof(*data))
1961da177e4SLinus Torvalds 		return -EINVAL;
1971da177e4SLinus Torvalds 	data = (struct nfsctl_client *)buf;
1981da177e4SLinus Torvalds 	return exp_addclient(data);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static ssize_t write_del(struct file *file, char *buf, size_t size)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	struct nfsctl_client *data;
2041da177e4SLinus Torvalds 	if (size < sizeof(*data))
2051da177e4SLinus Torvalds 		return -EINVAL;
2061da177e4SLinus Torvalds 	data = (struct nfsctl_client *)buf;
2071da177e4SLinus Torvalds 	return exp_delclient(data);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds static ssize_t write_export(struct file *file, char *buf, size_t size)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	struct nfsctl_export *data;
2131da177e4SLinus Torvalds 	if (size < sizeof(*data))
2141da177e4SLinus Torvalds 		return -EINVAL;
2151da177e4SLinus Torvalds 	data = (struct nfsctl_export*)buf;
2161da177e4SLinus Torvalds 	return exp_export(data);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static ssize_t write_unexport(struct file *file, char *buf, size_t size)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds 	struct nfsctl_export *data;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	if (size < sizeof(*data))
2241da177e4SLinus Torvalds 		return -EINVAL;
2251da177e4SLinus Torvalds 	data = (struct nfsctl_export*)buf;
2261da177e4SLinus Torvalds 	return exp_unexport(data);
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds static ssize_t write_getfs(struct file *file, char *buf, size_t size)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	struct nfsctl_fsparm *data;
2321da177e4SLinus Torvalds 	struct sockaddr_in *sin;
2331da177e4SLinus Torvalds 	struct auth_domain *clp;
2341da177e4SLinus Torvalds 	int err = 0;
2351da177e4SLinus Torvalds 	struct knfsd_fh *res;
236f15364bdSAurélien Charbon 	struct in6_addr in6;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	if (size < sizeof(*data))
2391da177e4SLinus Torvalds 		return -EINVAL;
2401da177e4SLinus Torvalds 	data = (struct nfsctl_fsparm*)buf;
2411da177e4SLinus Torvalds 	err = -EPROTONOSUPPORT;
2421da177e4SLinus Torvalds 	if (data->gd_addr.sa_family != AF_INET)
2431da177e4SLinus Torvalds 		goto out;
2441da177e4SLinus Torvalds 	sin = (struct sockaddr_in *)&data->gd_addr;
2451da177e4SLinus Torvalds 	if (data->gd_maxlen > NFS3_FHSIZE)
2461da177e4SLinus Torvalds 		data->gd_maxlen = NFS3_FHSIZE;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	res = (struct knfsd_fh*)buf;
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	exp_readlock();
251f15364bdSAurélien Charbon 
252f15364bdSAurélien Charbon 	ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
253f15364bdSAurélien Charbon 
254f15364bdSAurélien Charbon 	clp = auth_unix_lookup(&in6);
255f15364bdSAurélien Charbon 	if (!clp)
2561da177e4SLinus Torvalds 		err = -EPERM;
2571da177e4SLinus Torvalds 	else {
2581da177e4SLinus Torvalds 		err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
2591da177e4SLinus Torvalds 		auth_domain_put(clp);
2601da177e4SLinus Torvalds 	}
2611da177e4SLinus Torvalds 	exp_readunlock();
2621da177e4SLinus Torvalds 	if (err == 0)
26312127498SAndrew Morton 		err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
2641da177e4SLinus Torvalds  out:
2651da177e4SLinus Torvalds 	return err;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds static ssize_t write_getfd(struct file *file, char *buf, size_t size)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	struct nfsctl_fdparm *data;
2711da177e4SLinus Torvalds 	struct sockaddr_in *sin;
2721da177e4SLinus Torvalds 	struct auth_domain *clp;
2731da177e4SLinus Torvalds 	int err = 0;
2741da177e4SLinus Torvalds 	struct knfsd_fh fh;
2751da177e4SLinus Torvalds 	char *res;
276f15364bdSAurélien Charbon 	struct in6_addr in6;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	if (size < sizeof(*data))
2791da177e4SLinus Torvalds 		return -EINVAL;
2801da177e4SLinus Torvalds 	data = (struct nfsctl_fdparm*)buf;
2811da177e4SLinus Torvalds 	err = -EPROTONOSUPPORT;
2821da177e4SLinus Torvalds 	if (data->gd_addr.sa_family != AF_INET)
2831da177e4SLinus Torvalds 		goto out;
2841da177e4SLinus Torvalds 	err = -EINVAL;
2851da177e4SLinus Torvalds 	if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
2861da177e4SLinus Torvalds 		goto out;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	res = buf;
2891da177e4SLinus Torvalds 	sin = (struct sockaddr_in *)&data->gd_addr;
2901da177e4SLinus Torvalds 	exp_readlock();
291f15364bdSAurélien Charbon 
292f15364bdSAurélien Charbon 	ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
293f15364bdSAurélien Charbon 
294f15364bdSAurélien Charbon 	clp = auth_unix_lookup(&in6);
295f15364bdSAurélien Charbon 	if (!clp)
2961da177e4SLinus Torvalds 		err = -EPERM;
2971da177e4SLinus Torvalds 	else {
2981da177e4SLinus Torvalds 		err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
2991da177e4SLinus Torvalds 		auth_domain_put(clp);
3001da177e4SLinus Torvalds 	}
3011da177e4SLinus Torvalds 	exp_readunlock();
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	if (err == 0) {
3041da177e4SLinus Torvalds 		memset(res,0, NFS_FHSIZE);
3051da177e4SLinus Torvalds 		memcpy(res, &fh.fh_base, fh.fh_size);
3061da177e4SLinus Torvalds 		err = NFS_FHSIZE;
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds  out:
3091da177e4SLinus Torvalds 	return err;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3124373ea84SWendy Cheng static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size)
3134373ea84SWendy Cheng {
314367c8c7bSChuck Lever 	struct sockaddr_in sin = {
315367c8c7bSChuck Lever 		.sin_family	= AF_INET,
316367c8c7bSChuck Lever 	};
3174373ea84SWendy Cheng 	int b1, b2, b3, b4;
318367c8c7bSChuck Lever 	char c;
319367c8c7bSChuck Lever 	char *fo_path;
3204373ea84SWendy Cheng 
3214373ea84SWendy Cheng 	/* sanity check */
3224373ea84SWendy Cheng 	if (size == 0)
3234373ea84SWendy Cheng 		return -EINVAL;
3244373ea84SWendy Cheng 
3254373ea84SWendy Cheng 	if (buf[size-1] != '\n')
3264373ea84SWendy Cheng 		return -EINVAL;
3274373ea84SWendy Cheng 
3284373ea84SWendy Cheng 	fo_path = buf;
3294373ea84SWendy Cheng 	if (qword_get(&buf, fo_path, size) < 0)
3304373ea84SWendy Cheng 		return -EINVAL;
3314373ea84SWendy Cheng 
3324373ea84SWendy Cheng 	/* get ipv4 address */
333367c8c7bSChuck Lever 	if (sscanf(fo_path, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) != 4)
3344373ea84SWendy Cheng 		return -EINVAL;
335367c8c7bSChuck Lever 	if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
336367c8c7bSChuck Lever 		return -EINVAL;
337367c8c7bSChuck Lever 	sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
3384373ea84SWendy Cheng 
339367c8c7bSChuck Lever 	return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
3404373ea84SWendy Cheng }
3414373ea84SWendy Cheng 
34217efa372SWendy Cheng static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size)
34317efa372SWendy Cheng {
34417efa372SWendy Cheng 	struct nameidata nd;
34517efa372SWendy Cheng 	char *fo_path;
34617efa372SWendy Cheng 	int error;
34717efa372SWendy Cheng 
34817efa372SWendy Cheng 	/* sanity check */
34917efa372SWendy Cheng 	if (size == 0)
35017efa372SWendy Cheng 		return -EINVAL;
35117efa372SWendy Cheng 
35217efa372SWendy Cheng 	if (buf[size-1] != '\n')
35317efa372SWendy Cheng 		return -EINVAL;
35417efa372SWendy Cheng 
35517efa372SWendy Cheng 	fo_path = buf;
35617efa372SWendy Cheng 	if (qword_get(&buf, fo_path, size) < 0)
35717efa372SWendy Cheng 		return -EINVAL;
35817efa372SWendy Cheng 
35917efa372SWendy Cheng 	error = path_lookup(fo_path, 0, &nd);
36017efa372SWendy Cheng 	if (error)
36117efa372SWendy Cheng 		return error;
36217efa372SWendy Cheng 
36317efa372SWendy Cheng 	error = nlmsvc_unlock_all_by_sb(nd.path.mnt->mnt_sb);
36417efa372SWendy Cheng 
36517efa372SWendy Cheng 	path_put(&nd.path);
36617efa372SWendy Cheng 	return error;
36717efa372SWendy Cheng }
36817efa372SWendy Cheng 
3691da177e4SLinus Torvalds static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds 	/* request is:
3721da177e4SLinus Torvalds 	 *   domain path maxsize
3731da177e4SLinus Torvalds 	 * response is
3741da177e4SLinus Torvalds 	 *   filehandle
3751da177e4SLinus Torvalds 	 *
3761da177e4SLinus Torvalds 	 * qword quoting is used, so filehandle will be \x....
3771da177e4SLinus Torvalds 	 */
3781da177e4SLinus Torvalds 	char *dname, *path;
379246d95baSAndrew Morton 	int uninitialized_var(maxsize);
3801da177e4SLinus Torvalds 	char *mesg = buf;
3811da177e4SLinus Torvalds 	int len;
3821da177e4SLinus Torvalds 	struct auth_domain *dom;
3831da177e4SLinus Torvalds 	struct knfsd_fh fh;
3841da177e4SLinus Torvalds 
38587d26ea7SJ. Bruce Fields 	if (size == 0)
38687d26ea7SJ. Bruce Fields 		return -EINVAL;
38787d26ea7SJ. Bruce Fields 
3881da177e4SLinus Torvalds 	if (buf[size-1] != '\n')
3891da177e4SLinus Torvalds 		return -EINVAL;
3901da177e4SLinus Torvalds 	buf[size-1] = 0;
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	dname = mesg;
3931da177e4SLinus Torvalds 	len = qword_get(&mesg, dname, size);
3941da177e4SLinus Torvalds 	if (len <= 0) return -EINVAL;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	path = dname+len+1;
3971da177e4SLinus Torvalds 	len = qword_get(&mesg, path, size);
3981da177e4SLinus Torvalds 	if (len <= 0) return -EINVAL;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	len = get_int(&mesg, &maxsize);
4011da177e4SLinus Torvalds 	if (len)
4021da177e4SLinus Torvalds 		return len;
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	if (maxsize < NFS_FHSIZE)
4051da177e4SLinus Torvalds 		return -EINVAL;
4061da177e4SLinus Torvalds 	if (maxsize > NFS3_FHSIZE)
4071da177e4SLinus Torvalds 		maxsize = NFS3_FHSIZE;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	if (qword_get(&mesg, mesg, size)>0)
4101da177e4SLinus Torvalds 		return -EINVAL;
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	/* we have all the words, they are in buf.. */
4131da177e4SLinus Torvalds 	dom = unix_domain_find(dname);
4141da177e4SLinus Torvalds 	if (!dom)
4151da177e4SLinus Torvalds 		return -ENOMEM;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	len = exp_rootfh(dom, path, &fh,  maxsize);
4181da177e4SLinus Torvalds 	auth_domain_put(dom);
4191da177e4SLinus Torvalds 	if (len)
4201da177e4SLinus Torvalds 		return len;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	mesg = buf; len = SIMPLE_TRANSACTION_LIMIT;
4231da177e4SLinus Torvalds 	qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
4241da177e4SLinus Torvalds 	mesg[-1] = '\n';
4251da177e4SLinus Torvalds 	return mesg - buf;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds static ssize_t write_threads(struct file *file, char *buf, size_t size)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	/* if size > 0, look for a number of threads and call nfsd_svc
4311da177e4SLinus Torvalds 	 * then write out number of threads as reply
4321da177e4SLinus Torvalds 	 */
4331da177e4SLinus Torvalds 	char *mesg = buf;
4341da177e4SLinus Torvalds 	int rv;
4351da177e4SLinus Torvalds 	if (size > 0) {
4361da177e4SLinus Torvalds 		int newthreads;
4371da177e4SLinus Torvalds 		rv = get_int(&mesg, &newthreads);
4381da177e4SLinus Torvalds 		if (rv)
4391da177e4SLinus Torvalds 			return rv;
4401da177e4SLinus Torvalds 		if (newthreads <0)
4411da177e4SLinus Torvalds 			return -EINVAL;
4421da177e4SLinus Torvalds 		rv = nfsd_svc(2049, newthreads);
4431da177e4SLinus Torvalds 		if (rv)
4441da177e4SLinus Torvalds 			return rv;
4451da177e4SLinus Torvalds 	}
4461da177e4SLinus Torvalds 	sprintf(buf, "%d\n", nfsd_nrthreads());
4471da177e4SLinus Torvalds 	return strlen(buf);
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
450eed2965aSGreg Banks static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
451eed2965aSGreg Banks {
452eed2965aSGreg Banks 	/* if size > 0, look for an array of number of threads per node
453eed2965aSGreg Banks 	 * and apply them  then write out number of threads per node as reply
454eed2965aSGreg Banks 	 */
455eed2965aSGreg Banks 	char *mesg = buf;
456eed2965aSGreg Banks 	int i;
457eed2965aSGreg Banks 	int rv;
458eed2965aSGreg Banks 	int len;
459bedbdd8bSNeil Brown 	int npools;
460eed2965aSGreg Banks 	int *nthreads;
461eed2965aSGreg Banks 
462bedbdd8bSNeil Brown 	mutex_lock(&nfsd_mutex);
463bedbdd8bSNeil Brown 	npools = nfsd_nrpools();
464eed2965aSGreg Banks 	if (npools == 0) {
465eed2965aSGreg Banks 		/*
466eed2965aSGreg Banks 		 * NFS is shut down.  The admin can start it by
467eed2965aSGreg Banks 		 * writing to the threads file but NOT the pool_threads
468eed2965aSGreg Banks 		 * file, sorry.  Report zero threads.
469eed2965aSGreg Banks 		 */
470bedbdd8bSNeil Brown 		mutex_unlock(&nfsd_mutex);
471eed2965aSGreg Banks 		strcpy(buf, "0\n");
472eed2965aSGreg Banks 		return strlen(buf);
473eed2965aSGreg Banks 	}
474eed2965aSGreg Banks 
475eed2965aSGreg Banks 	nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
476bedbdd8bSNeil Brown 	rv = -ENOMEM;
477eed2965aSGreg Banks 	if (nthreads == NULL)
478bedbdd8bSNeil Brown 		goto out_free;
479eed2965aSGreg Banks 
480eed2965aSGreg Banks 	if (size > 0) {
481eed2965aSGreg Banks 		for (i = 0; i < npools; i++) {
482eed2965aSGreg Banks 			rv = get_int(&mesg, &nthreads[i]);
483eed2965aSGreg Banks 			if (rv == -ENOENT)
484eed2965aSGreg Banks 				break;		/* fewer numbers than pools */
485eed2965aSGreg Banks 			if (rv)
486eed2965aSGreg Banks 				goto out_free;	/* syntax error */
487eed2965aSGreg Banks 			rv = -EINVAL;
488eed2965aSGreg Banks 			if (nthreads[i] < 0)
489eed2965aSGreg Banks 				goto out_free;
490eed2965aSGreg Banks 		}
491eed2965aSGreg Banks 		rv = nfsd_set_nrthreads(i, nthreads);
492eed2965aSGreg Banks 		if (rv)
493eed2965aSGreg Banks 			goto out_free;
494eed2965aSGreg Banks 	}
495eed2965aSGreg Banks 
496eed2965aSGreg Banks 	rv = nfsd_get_nrthreads(npools, nthreads);
497eed2965aSGreg Banks 	if (rv)
498eed2965aSGreg Banks 		goto out_free;
499eed2965aSGreg Banks 
500eed2965aSGreg Banks 	mesg = buf;
501eed2965aSGreg Banks 	size = SIMPLE_TRANSACTION_LIMIT;
502eed2965aSGreg Banks 	for (i = 0; i < npools && size > 0; i++) {
503eed2965aSGreg Banks 		snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
504eed2965aSGreg Banks 		len = strlen(mesg);
505eed2965aSGreg Banks 		size -= len;
506eed2965aSGreg Banks 		mesg += len;
507eed2965aSGreg Banks 	}
508eed2965aSGreg Banks 
509bedbdd8bSNeil Brown 	mutex_unlock(&nfsd_mutex);
510eed2965aSGreg Banks 	return (mesg-buf);
511eed2965aSGreg Banks 
512eed2965aSGreg Banks out_free:
513eed2965aSGreg Banks 	kfree(nthreads);
514bedbdd8bSNeil Brown 	mutex_unlock(&nfsd_mutex);
515eed2965aSGreg Banks 	return rv;
516eed2965aSGreg Banks }
517eed2965aSGreg Banks 
5183dd98a3bSJeff Layton static ssize_t __write_versions(struct file *file, char *buf, size_t size)
51970c3b76cSNeilBrown {
52070c3b76cSNeilBrown 	/*
52170c3b76cSNeilBrown 	 * Format:
52270c3b76cSNeilBrown 	 *   [-/+]vers [-/+]vers ...
52370c3b76cSNeilBrown 	 */
52470c3b76cSNeilBrown 	char *mesg = buf;
52570c3b76cSNeilBrown 	char *vers, sign;
52670c3b76cSNeilBrown 	int len, num;
52770c3b76cSNeilBrown 	ssize_t tlen = 0;
52870c3b76cSNeilBrown 	char *sep;
52970c3b76cSNeilBrown 
53070c3b76cSNeilBrown 	if (size>0) {
53170c3b76cSNeilBrown 		if (nfsd_serv)
5326658d3a7SNeilBrown 			/* Cannot change versions without updating
5336658d3a7SNeilBrown 			 * nfsd_serv->sv_xdrsize, and reallocing
5346658d3a7SNeilBrown 			 * rq_argp and rq_resp
5356658d3a7SNeilBrown 			 */
53670c3b76cSNeilBrown 			return -EBUSY;
53770c3b76cSNeilBrown 		if (buf[size-1] != '\n')
53870c3b76cSNeilBrown 			return -EINVAL;
53970c3b76cSNeilBrown 		buf[size-1] = 0;
54070c3b76cSNeilBrown 
54170c3b76cSNeilBrown 		vers = mesg;
54270c3b76cSNeilBrown 		len = qword_get(&mesg, vers, size);
54370c3b76cSNeilBrown 		if (len <= 0) return -EINVAL;
54470c3b76cSNeilBrown 		do {
54570c3b76cSNeilBrown 			sign = *vers;
54670c3b76cSNeilBrown 			if (sign == '+' || sign == '-')
54770c3b76cSNeilBrown 				num = simple_strtol((vers+1), NULL, 0);
54870c3b76cSNeilBrown 			else
54970c3b76cSNeilBrown 				num = simple_strtol(vers, NULL, 0);
55070c3b76cSNeilBrown 			switch(num) {
55170c3b76cSNeilBrown 			case 2:
55270c3b76cSNeilBrown 			case 3:
55370c3b76cSNeilBrown 			case 4:
5546658d3a7SNeilBrown 				nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
55570c3b76cSNeilBrown 				break;
55670c3b76cSNeilBrown 			default:
55770c3b76cSNeilBrown 				return -EINVAL;
55870c3b76cSNeilBrown 			}
55970c3b76cSNeilBrown 			vers += len + 1;
56070c3b76cSNeilBrown 			tlen += len;
56170c3b76cSNeilBrown 		} while ((len = qword_get(&mesg, vers, size)) > 0);
56270c3b76cSNeilBrown 		/* If all get turned off, turn them back on, as
56370c3b76cSNeilBrown 		 * having no versions is BAD
56470c3b76cSNeilBrown 		 */
5656658d3a7SNeilBrown 		nfsd_reset_versions();
56670c3b76cSNeilBrown 	}
56770c3b76cSNeilBrown 	/* Now write current state into reply buffer */
56870c3b76cSNeilBrown 	len = 0;
56970c3b76cSNeilBrown 	sep = "";
57070c3b76cSNeilBrown 	for (num=2 ; num <= 4 ; num++)
5716658d3a7SNeilBrown 		if (nfsd_vers(num, NFSD_AVAIL)) {
57270c3b76cSNeilBrown 			len += sprintf(buf+len, "%s%c%d", sep,
5736658d3a7SNeilBrown 				       nfsd_vers(num, NFSD_TEST)?'+':'-',
57470c3b76cSNeilBrown 				       num);
57570c3b76cSNeilBrown 			sep = " ";
57670c3b76cSNeilBrown 		}
57770c3b76cSNeilBrown 	len += sprintf(buf+len, "\n");
57870c3b76cSNeilBrown 	return len;
57970c3b76cSNeilBrown }
58070c3b76cSNeilBrown 
5813dd98a3bSJeff Layton static ssize_t write_versions(struct file *file, char *buf, size_t size)
5823dd98a3bSJeff Layton {
5833dd98a3bSJeff Layton 	ssize_t rv;
5843dd98a3bSJeff Layton 
5853dd98a3bSJeff Layton 	mutex_lock(&nfsd_mutex);
5863dd98a3bSJeff Layton 	rv = __write_versions(file, buf, size);
5873dd98a3bSJeff Layton 	mutex_unlock(&nfsd_mutex);
5883dd98a3bSJeff Layton 	return rv;
5893dd98a3bSJeff Layton }
5903dd98a3bSJeff Layton 
591bedbdd8bSNeil Brown static ssize_t __write_ports(struct file *file, char *buf, size_t size)
59280212d59SNeilBrown {
593b41b66d6SNeilBrown 	if (size == 0) {
59480212d59SNeilBrown 		int len = 0;
595bedbdd8bSNeil Brown 
59680212d59SNeilBrown 		if (nfsd_serv)
5979571af18STom Tucker 			len = svc_xprt_names(nfsd_serv, buf, 0);
59880212d59SNeilBrown 		return len;
59980212d59SNeilBrown 	}
600b41b66d6SNeilBrown 	/* Either a single 'fd' number is written, in which
601b41b66d6SNeilBrown 	 * case it must be for a socket of a supported family/protocol,
602b41b66d6SNeilBrown 	 * and we use it as an nfsd socket, or
603b41b66d6SNeilBrown 	 * A '-' followed by the 'name' of a socket in which case
604b41b66d6SNeilBrown 	 * we close the socket.
605b41b66d6SNeilBrown 	 */
606b41b66d6SNeilBrown 	if (isdigit(buf[0])) {
607b41b66d6SNeilBrown 		char *mesg = buf;
608b41b66d6SNeilBrown 		int fd;
609b41b66d6SNeilBrown 		int err;
610b41b66d6SNeilBrown 		err = get_int(&mesg, &fd);
611b41b66d6SNeilBrown 		if (err)
612b41b66d6SNeilBrown 			return -EINVAL;
613b41b66d6SNeilBrown 		if (fd < 0)
614b41b66d6SNeilBrown 			return -EINVAL;
615b41b66d6SNeilBrown 		err = nfsd_create_serv();
616b41b66d6SNeilBrown 		if (!err) {
617b41b66d6SNeilBrown 			int proto = 0;
618b41b66d6SNeilBrown 			err = svc_addsock(nfsd_serv, fd, buf, &proto);
6195680c446SNeilBrown 			if (err >= 0) {
6205680c446SNeilBrown 				err = lockd_up(proto);
6215680c446SNeilBrown 				if (err < 0)
6225680c446SNeilBrown 					svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
6233dfb4210SNeilBrown 			}
624b41b66d6SNeilBrown 			/* Decrease the count, but don't shutdown the
625b41b66d6SNeilBrown 			 * the service
626b41b66d6SNeilBrown 			 */
627b41b66d6SNeilBrown 			nfsd_serv->sv_nrthreads--;
628b41b66d6SNeilBrown 		}
6295680c446SNeilBrown 		return err < 0 ? err : 0;
630b41b66d6SNeilBrown 	}
631a217813fSTom Tucker 	if (buf[0] == '-' && isdigit(buf[1])) {
632b41b66d6SNeilBrown 		char *toclose = kstrdup(buf+1, GFP_KERNEL);
633b41b66d6SNeilBrown 		int len = 0;
634b41b66d6SNeilBrown 		if (!toclose)
635b41b66d6SNeilBrown 			return -ENOMEM;
636b41b66d6SNeilBrown 		if (nfsd_serv)
637b41b66d6SNeilBrown 			len = svc_sock_names(buf, nfsd_serv, toclose);
63837a03472SNeilBrown 		if (len >= 0)
63937a03472SNeilBrown 			lockd_down();
640b41b66d6SNeilBrown 		kfree(toclose);
641b41b66d6SNeilBrown 		return len;
642b41b66d6SNeilBrown 	}
643a217813fSTom Tucker 	/*
644a217813fSTom Tucker 	 * Add a transport listener by writing it's transport name
645a217813fSTom Tucker 	 */
646a217813fSTom Tucker 	if (isalpha(buf[0])) {
647a217813fSTom Tucker 		int err;
648a217813fSTom Tucker 		char transport[16];
649a217813fSTom Tucker 		int port;
650a217813fSTom Tucker 		if (sscanf(buf, "%15s %4d", transport, &port) == 2) {
651a217813fSTom Tucker 			err = nfsd_create_serv();
652a217813fSTom Tucker 			if (!err) {
653a217813fSTom Tucker 				err = svc_create_xprt(nfsd_serv,
654a217813fSTom Tucker 						      transport, port,
655a217813fSTom Tucker 						      SVC_SOCK_ANONYMOUS);
656a217813fSTom Tucker 				if (err == -ENOENT)
657a217813fSTom Tucker 					/* Give a reasonable perror msg for
658a217813fSTom Tucker 					 * bad transport string */
659a217813fSTom Tucker 					err = -EPROTONOSUPPORT;
660a217813fSTom Tucker 			}
661a217813fSTom Tucker 			return err < 0 ? err : 0;
662a217813fSTom Tucker 		}
663a217813fSTom Tucker 	}
664a217813fSTom Tucker 	/*
665a217813fSTom Tucker 	 * Remove a transport by writing it's transport name and port number
666a217813fSTom Tucker 	 */
667a217813fSTom Tucker 	if (buf[0] == '-' && isalpha(buf[1])) {
668a217813fSTom Tucker 		struct svc_xprt *xprt;
669a217813fSTom Tucker 		int err = -EINVAL;
670a217813fSTom Tucker 		char transport[16];
671a217813fSTom Tucker 		int port;
672a217813fSTom Tucker 		if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) {
673a217813fSTom Tucker 			if (port == 0)
674a217813fSTom Tucker 				return -EINVAL;
675a217813fSTom Tucker 			if (nfsd_serv) {
676a217813fSTom Tucker 				xprt = svc_find_xprt(nfsd_serv, transport,
677a217813fSTom Tucker 						     AF_UNSPEC, port);
678a217813fSTom Tucker 				if (xprt) {
679a217813fSTom Tucker 					svc_close_xprt(xprt);
680a217813fSTom Tucker 					svc_xprt_put(xprt);
681a217813fSTom Tucker 					err = 0;
682a217813fSTom Tucker 				} else
683a217813fSTom Tucker 					err = -ENOTCONN;
684a217813fSTom Tucker 			}
685a217813fSTom Tucker 			return err < 0 ? err : 0;
686a217813fSTom Tucker 		}
687a217813fSTom Tucker 	}
688b41b66d6SNeilBrown 	return -EINVAL;
689b41b66d6SNeilBrown }
69080212d59SNeilBrown 
691bedbdd8bSNeil Brown static ssize_t write_ports(struct file *file, char *buf, size_t size)
692bedbdd8bSNeil Brown {
693bedbdd8bSNeil Brown 	ssize_t rv;
6943dd98a3bSJeff Layton 
695bedbdd8bSNeil Brown 	mutex_lock(&nfsd_mutex);
696bedbdd8bSNeil Brown 	rv = __write_ports(file, buf, size);
697bedbdd8bSNeil Brown 	mutex_unlock(&nfsd_mutex);
698bedbdd8bSNeil Brown 	return rv;
699bedbdd8bSNeil Brown }
700bedbdd8bSNeil Brown 
701bedbdd8bSNeil Brown 
702596bbe53SNeilBrown int nfsd_max_blksize;
703596bbe53SNeilBrown 
704596bbe53SNeilBrown static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
705596bbe53SNeilBrown {
706596bbe53SNeilBrown 	char *mesg = buf;
707596bbe53SNeilBrown 	if (size > 0) {
708596bbe53SNeilBrown 		int bsize;
709596bbe53SNeilBrown 		int rv = get_int(&mesg, &bsize);
710596bbe53SNeilBrown 		if (rv)
711596bbe53SNeilBrown 			return rv;
712596bbe53SNeilBrown 		/* force bsize into allowed range and
713596bbe53SNeilBrown 		 * required alignment.
714596bbe53SNeilBrown 		 */
715596bbe53SNeilBrown 		if (bsize < 1024)
716596bbe53SNeilBrown 			bsize = 1024;
717596bbe53SNeilBrown 		if (bsize > NFSSVC_MAXBLKSIZE)
718596bbe53SNeilBrown 			bsize = NFSSVC_MAXBLKSIZE;
719596bbe53SNeilBrown 		bsize &= ~(1024-1);
720bedbdd8bSNeil Brown 		mutex_lock(&nfsd_mutex);
721596bbe53SNeilBrown 		if (nfsd_serv && nfsd_serv->sv_nrthreads) {
722bedbdd8bSNeil Brown 			mutex_unlock(&nfsd_mutex);
723596bbe53SNeilBrown 			return -EBUSY;
724596bbe53SNeilBrown 		}
725596bbe53SNeilBrown 		nfsd_max_blksize = bsize;
726bedbdd8bSNeil Brown 		mutex_unlock(&nfsd_mutex);
727596bbe53SNeilBrown 	}
728596bbe53SNeilBrown 	return sprintf(buf, "%d\n", nfsd_max_blksize);
729596bbe53SNeilBrown }
730596bbe53SNeilBrown 
73170c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4
7321da177e4SLinus Torvalds extern time_t nfs4_leasetime(void);
7331da177e4SLinus Torvalds 
7343dd98a3bSJeff Layton static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
7351da177e4SLinus Torvalds {
7361da177e4SLinus Torvalds 	/* if size > 10 seconds, call
7371da177e4SLinus Torvalds 	 * nfs4_reset_lease() then write out the new lease (seconds) as reply
7381da177e4SLinus Torvalds 	 */
7391da177e4SLinus Torvalds 	char *mesg = buf;
7403dd98a3bSJeff Layton 	int rv, lease;
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	if (size > 0) {
7433dd98a3bSJeff Layton 		if (nfsd_serv)
7443dd98a3bSJeff Layton 			return -EBUSY;
7451da177e4SLinus Torvalds 		rv = get_int(&mesg, &lease);
7461da177e4SLinus Torvalds 		if (rv)
7471da177e4SLinus Torvalds 			return rv;
7481da177e4SLinus Torvalds 		if (lease < 10 || lease > 3600)
7491da177e4SLinus Torvalds 			return -EINVAL;
7501da177e4SLinus Torvalds 		nfs4_reset_lease(lease);
7511da177e4SLinus Torvalds 	}
7521da177e4SLinus Torvalds 	sprintf(buf, "%ld\n", nfs4_lease_time());
7531da177e4SLinus Torvalds 	return strlen(buf);
7541da177e4SLinus Torvalds }
7551da177e4SLinus Torvalds 
7563dd98a3bSJeff Layton static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
7573dd98a3bSJeff Layton {
7583dd98a3bSJeff Layton 	ssize_t rv;
7593dd98a3bSJeff Layton 
7603dd98a3bSJeff Layton 	mutex_lock(&nfsd_mutex);
7613dd98a3bSJeff Layton 	rv = __write_leasetime(file, buf, size);
7623dd98a3bSJeff Layton 	mutex_unlock(&nfsd_mutex);
7633dd98a3bSJeff Layton 	return rv;
7643dd98a3bSJeff Layton }
7653dd98a3bSJeff Layton 
7663dd98a3bSJeff Layton extern char *nfs4_recoverydir(void);
7673dd98a3bSJeff Layton 
7683dd98a3bSJeff Layton static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
7690964a3d3SNeilBrown {
7700964a3d3SNeilBrown 	char *mesg = buf;
7710964a3d3SNeilBrown 	char *recdir;
7720964a3d3SNeilBrown 	int len, status;
7730964a3d3SNeilBrown 
7743dd98a3bSJeff Layton 	if (size > 0) {
7753dd98a3bSJeff Layton 		if (nfsd_serv)
7763dd98a3bSJeff Layton 			return -EBUSY;
7773dd98a3bSJeff Layton 		if (size > PATH_MAX || buf[size-1] != '\n')
7780964a3d3SNeilBrown 			return -EINVAL;
7790964a3d3SNeilBrown 		buf[size-1] = 0;
7800964a3d3SNeilBrown 
7810964a3d3SNeilBrown 		recdir = mesg;
7820964a3d3SNeilBrown 		len = qword_get(&mesg, recdir, size);
7830964a3d3SNeilBrown 		if (len <= 0)
7840964a3d3SNeilBrown 			return -EINVAL;
7850964a3d3SNeilBrown 
7860964a3d3SNeilBrown 		status = nfs4_reset_recoverydir(recdir);
7873dd98a3bSJeff Layton 	}
7883dd98a3bSJeff Layton 	sprintf(buf, "%s\n", nfs4_recoverydir());
7890964a3d3SNeilBrown 	return strlen(buf);
7900964a3d3SNeilBrown }
7913dd98a3bSJeff Layton 
7923dd98a3bSJeff Layton static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
7933dd98a3bSJeff Layton {
7943dd98a3bSJeff Layton 	ssize_t rv;
7953dd98a3bSJeff Layton 
7963dd98a3bSJeff Layton 	mutex_lock(&nfsd_mutex);
7973dd98a3bSJeff Layton 	rv = __write_recoverydir(file, buf, size);
7983dd98a3bSJeff Layton 	mutex_unlock(&nfsd_mutex);
7993dd98a3bSJeff Layton 	return rv;
8003dd98a3bSJeff Layton }
8013dd98a3bSJeff Layton 
80270c3b76cSNeilBrown #endif
8030964a3d3SNeilBrown 
8041da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/
8051da177e4SLinus Torvalds /*
8061da177e4SLinus Torvalds  *	populating the filesystem.
8071da177e4SLinus Torvalds  */
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
8101da177e4SLinus Torvalds {
8111da177e4SLinus Torvalds 	static struct tree_descr nfsd_files[] = {
8121da177e4SLinus Torvalds 		[NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
8131da177e4SLinus Torvalds 		[NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
8141da177e4SLinus Torvalds 		[NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
8151da177e4SLinus Torvalds 		[NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
8161da177e4SLinus Torvalds 		[NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
8171da177e4SLinus Torvalds 		[NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
8181da177e4SLinus Torvalds 		[NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
8191da177e4SLinus Torvalds 		[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
8204373ea84SWendy Cheng 		[NFSD_FO_UnlockIP] = {"unlock_ip",
8214373ea84SWendy Cheng 					&transaction_ops, S_IWUSR|S_IRUSR},
82217efa372SWendy Cheng 		[NFSD_FO_UnlockFS] = {"unlock_filesystem",
82317efa372SWendy Cheng 					&transaction_ops, S_IWUSR|S_IRUSR},
8241da177e4SLinus Torvalds 		[NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
8251da177e4SLinus Torvalds 		[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
826eed2965aSGreg Banks 		[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
82770c3b76cSNeilBrown 		[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
82880212d59SNeilBrown 		[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
829596bbe53SNeilBrown 		[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
8301da177e4SLinus Torvalds #ifdef CONFIG_NFSD_V4
8311da177e4SLinus Torvalds 		[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
8320964a3d3SNeilBrown 		[NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
8331da177e4SLinus Torvalds #endif
8341da177e4SLinus Torvalds 		/* last one */ {""}
8351da177e4SLinus Torvalds 	};
8361da177e4SLinus Torvalds 	return simple_fill_super(sb, 0x6e667364, nfsd_files);
8371da177e4SLinus Torvalds }
8381da177e4SLinus Torvalds 
839454e2398SDavid Howells static int nfsd_get_sb(struct file_system_type *fs_type,
840454e2398SDavid Howells 	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
8411da177e4SLinus Torvalds {
842454e2398SDavid Howells 	return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
8431da177e4SLinus Torvalds }
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds static struct file_system_type nfsd_fs_type = {
8461da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
8471da177e4SLinus Torvalds 	.name		= "nfsd",
8481da177e4SLinus Torvalds 	.get_sb		= nfsd_get_sb,
8491da177e4SLinus Torvalds 	.kill_sb	= kill_litter_super,
8501da177e4SLinus Torvalds };
8511da177e4SLinus Torvalds 
852e331f606SJ. Bruce Fields #ifdef CONFIG_PROC_FS
853e331f606SJ. Bruce Fields static int create_proc_exports_entry(void)
854e331f606SJ. Bruce Fields {
855e331f606SJ. Bruce Fields 	struct proc_dir_entry *entry;
856e331f606SJ. Bruce Fields 
857e331f606SJ. Bruce Fields 	entry = proc_mkdir("fs/nfs", NULL);
858e331f606SJ. Bruce Fields 	if (!entry)
859e331f606SJ. Bruce Fields 		return -ENOMEM;
8609ef2db26SDenis V. Lunev 	entry = proc_create("exports", 0, entry, &exports_operations);
861e331f606SJ. Bruce Fields 	if (!entry)
862e331f606SJ. Bruce Fields 		return -ENOMEM;
863e331f606SJ. Bruce Fields 	return 0;
864e331f606SJ. Bruce Fields }
865e331f606SJ. Bruce Fields #else /* CONFIG_PROC_FS */
866e331f606SJ. Bruce Fields static int create_proc_exports_entry(void)
867e331f606SJ. Bruce Fields {
868e331f606SJ. Bruce Fields 	return 0;
869e331f606SJ. Bruce Fields }
870e331f606SJ. Bruce Fields #endif
871e331f606SJ. Bruce Fields 
8721da177e4SLinus Torvalds static int __init init_nfsd(void)
8731da177e4SLinus Torvalds {
8741da177e4SLinus Torvalds 	int retval;
8751da177e4SLinus Torvalds 	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
8761da177e4SLinus Torvalds 
877e8ff2a84SJ. Bruce Fields 	retval = nfs4_state_init(); /* nfs4 locking state */
878e8ff2a84SJ. Bruce Fields 	if (retval)
879e8ff2a84SJ. Bruce Fields 		return retval;
8801da177e4SLinus Torvalds 	nfsd_stat_init();	/* Statistics */
881d5c3428bSJ. Bruce Fields 	retval = nfsd_reply_cache_init();
882d5c3428bSJ. Bruce Fields 	if (retval)
883d5c3428bSJ. Bruce Fields 		goto out_free_stat;
884dbf847ecSJ. Bruce Fields 	retval = nfsd_export_init();
885dbf847ecSJ. Bruce Fields 	if (retval)
886dbf847ecSJ. Bruce Fields 		goto out_free_cache;
8871da177e4SLinus Torvalds 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
888dbf847ecSJ. Bruce Fields 	retval = nfsd_idmap_init();
889dbf847ecSJ. Bruce Fields 	if (retval)
890dbf847ecSJ. Bruce Fields 		goto out_free_lockd;
891e331f606SJ. Bruce Fields 	retval = create_proc_exports_entry();
892e331f606SJ. Bruce Fields 	if (retval)
893e331f606SJ. Bruce Fields 		goto out_free_idmap;
8941da177e4SLinus Torvalds 	retval = register_filesystem(&nfsd_fs_type);
89526808d3fSJ. Bruce Fields 	if (retval)
89626808d3fSJ. Bruce Fields 		goto out_free_all;
89726808d3fSJ. Bruce Fields 	return 0;
89826808d3fSJ. Bruce Fields out_free_all:
8991da177e4SLinus Torvalds 	remove_proc_entry("fs/nfs/exports", NULL);
9001da177e4SLinus Torvalds 	remove_proc_entry("fs/nfs", NULL);
901e331f606SJ. Bruce Fields out_free_idmap:
902dbf847ecSJ. Bruce Fields 	nfsd_idmap_shutdown();
903dbf847ecSJ. Bruce Fields out_free_lockd:
9041da177e4SLinus Torvalds 	nfsd_lockd_shutdown();
905e331f606SJ. Bruce Fields 	nfsd_export_shutdown();
906dbf847ecSJ. Bruce Fields out_free_cache:
907e331f606SJ. Bruce Fields 	nfsd_reply_cache_shutdown();
908d5c3428bSJ. Bruce Fields out_free_stat:
909d5c3428bSJ. Bruce Fields 	nfsd_stat_shutdown();
91046b25895SJ. Bruce Fields 	nfsd4_free_slabs();
9111da177e4SLinus Torvalds 	return retval;
9121da177e4SLinus Torvalds }
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds static void __exit exit_nfsd(void)
9151da177e4SLinus Torvalds {
9161da177e4SLinus Torvalds 	nfsd_export_shutdown();
917d5c3428bSJ. Bruce Fields 	nfsd_reply_cache_shutdown();
9181da177e4SLinus Torvalds 	remove_proc_entry("fs/nfs/exports", NULL);
9191da177e4SLinus Torvalds 	remove_proc_entry("fs/nfs", NULL);
9201da177e4SLinus Torvalds 	nfsd_stat_shutdown();
9211da177e4SLinus Torvalds 	nfsd_lockd_shutdown();
9221da177e4SLinus Torvalds 	nfsd_idmap_shutdown();
923e8ff2a84SJ. Bruce Fields 	nfsd4_free_slabs();
9241da177e4SLinus Torvalds 	unregister_filesystem(&nfsd_fs_type);
9251da177e4SLinus Torvalds }
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
9281da177e4SLinus Torvalds MODULE_LICENSE("GPL");
9291da177e4SLinus Torvalds module_init(init_nfsd)
9301da177e4SLinus Torvalds module_exit(exit_nfsd)
931