1/*
2 * linux/fs/nfsd/nfsctl.c
3 *
4 * Syscall interface to knfsd.
5 *
6 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7 */
8
9#include <linux/config.h>
10#include <linux/module.h>
11#include <linux/version.h>
12
13#include <linux/linkage.h>
14#include <linux/sched.h>
15#include <linux/errno.h>
16#include <linux/fs.h>
17#include <linux/fcntl.h>
18#include <linux/net.h>
19#include <linux/in.h>
20#include <linux/unistd.h>
21#include <linux/slab.h>
22#include <linux/proc_fs.h>
23#include <linux/seq_file.h>
24
25#include <linux/nfs.h>
26#include <linux/sunrpc/svc.h>
27#include <linux/nfsd/nfsd.h>
28#include <linux/nfsd/cache.h>
29#include <linux/nfsd/xdr.h>
30#include <linux/nfsd/syscall.h>
31
32#include <asm/uaccess.h>
33#include <linux/smp.h>
34#include <linux/smp_lock.h>
35#include <linux/init.h>
36
37static int	nfsctl_svc(struct nfsctl_svc *data);
38static int	nfsctl_addclient(struct nfsctl_client *data);
39static int	nfsctl_delclient(struct nfsctl_client *data);
40static int	nfsctl_export(struct nfsctl_export *data);
41static int	nfsctl_unexport(struct nfsctl_export *data);
42static int	nfsctl_getfh(struct nfsctl_fhparm *, __u8 *);
43static int	nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
44static int	nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
45#ifdef notyet
46static int	nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
47#endif
48
49extern struct seq_operations nfs_exports_op;
50static int exports_open(struct inode *inode, struct file *file)
51{
52	return seq_open(file, &nfs_exports_op);
53}
54static struct file_operations exports_operations = {
55	open:		exports_open,
56	read:		seq_read,
57	llseek:		seq_lseek,
58	release:	seq_release,
59};
60
61void proc_export_init(void)
62{
63	struct proc_dir_entry *entry;
64	if (!proc_mkdir("fs/nfs", 0))
65		return;
66	entry = create_proc_entry("fs/nfs/exports", 0, NULL);
67	if (entry)
68		entry->proc_fops =  &exports_operations;
69}
70
71static inline int
72nfsctl_svc(struct nfsctl_svc *data)
73{
74	return nfsd_svc(data->svc_port, data->svc_nthreads);
75}
76
77static inline int
78nfsctl_addclient(struct nfsctl_client *data)
79{
80	return exp_addclient(data);
81}
82
83static inline int
84nfsctl_delclient(struct nfsctl_client *data)
85{
86	return exp_delclient(data);
87}
88
89static inline int
90nfsctl_export(struct nfsctl_export *data)
91{
92	return exp_export(data);
93}
94
95static inline int
96nfsctl_unexport(struct nfsctl_export *data)
97{
98	return exp_unexport(data);
99}
100
101#ifdef notyet
102static inline int
103nfsctl_ugidupdate(nfs_ugidmap *data)
104{
105	return -EINVAL;
106}
107#endif
108
109static inline int
110nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
111{
112	struct sockaddr_in	*sin;
113	struct svc_client	*clp;
114	int			err = 0;
115
116	if (data->gd_addr.sa_family != AF_INET)
117		return -EPROTONOSUPPORT;
118	sin = (struct sockaddr_in *)&data->gd_addr;
119	if (data->gd_maxlen > NFS3_FHSIZE)
120		data->gd_maxlen = NFS3_FHSIZE;
121	exp_readlock();
122	if (!(clp = exp_getclient(sin)))
123		err = -EPERM;
124	else
125		err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
126	exp_unlock();
127	return err;
128}
129
130static inline int
131nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
132{
133	struct sockaddr_in	*sin;
134	struct svc_client	*clp;
135	int			err = 0;
136	struct	knfsd_fh	fh;
137
138	if (data->gd_addr.sa_family != AF_INET)
139		return -EPROTONOSUPPORT;
140	if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
141		return -EINVAL;
142	sin = (struct sockaddr_in *)&data->gd_addr;
143
144	exp_readlock();
145	if (!(clp = exp_getclient(sin)))
146		err = -EPERM;
147	else
148		err = exp_rootfh(clp, 0, 0, data->gd_path, &fh, NFS_FHSIZE);
149	exp_unlock();
150
151	if (err == 0) {
152		if (fh.fh_size > NFS_FHSIZE)
153			err = -EINVAL;
154		else {
155			memset(res,0, NFS_FHSIZE);
156			memcpy(res, &fh.fh_base, fh.fh_size);
157		}
158	}
159
160	return err;
161}
162
163static inline int
164nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res)
165{
166	struct sockaddr_in	*sin;
167	struct svc_client	*clp;
168	int			err = 0;
169	struct knfsd_fh		fh;
170
171	if (data->gf_addr.sa_family != AF_INET)
172		return -EPROTONOSUPPORT;
173	if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS)
174		return -EINVAL;
175	sin = (struct sockaddr_in *)&data->gf_addr;
176
177	exp_readlock();
178	if (!(clp = exp_getclient(sin)))
179		err = -EPERM;
180	else
181		err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, &fh, NFS_FHSIZE);
182	exp_unlock();
183
184	if (err == 0) {
185		if (fh.fh_size > NFS_FHSIZE)
186			err = -EINVAL;
187		else {
188			memset(res,0, NFS_FHSIZE);
189			memcpy(res, &fh.fh_base, fh.fh_size);
190		}
191	}
192
193	return err;
194}
195
196#ifdef CONFIG_NFSD
197#define handle_sys_nfsservctl sys_nfsservctl
198#endif
199
200static struct {
201	int argsize, respsize;
202}  sizes[] = {
203	/* NFSCTL_SVC        */ { sizeof(struct nfsctl_svc), 0 },
204	/* NFSCTL_ADDCLIENT  */ { sizeof(struct nfsctl_client), 0},
205	/* NFSCTL_DELCLIENT  */ { sizeof(struct nfsctl_client), 0},
206	/* NFSCTL_EXPORT     */ { sizeof(struct nfsctl_export), 0},
207	/* NFSCTL_UNEXPORT   */ { sizeof(struct nfsctl_export), 0},
208	/* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
209	/* NFSCTL_GETFH      */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
210	/* NFSCTL_GETFD      */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
211	/* NFSCTL_GETFS      */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
212};
213#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
214
215long
216asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
217{
218	struct nfsctl_arg *	argp = opaque_argp;
219	union nfsctl_res *	resp = opaque_resp;
220	struct nfsctl_arg *	arg = NULL;
221	union nfsctl_res *	res = NULL;
222	int			err;
223	int			argsize, respsize;
224
225	lock_kernel ();
226
227	err = -EPERM;
228	if (!capable(CAP_SYS_ADMIN)) {
229		goto done;
230	}
231	err = -EINVAL;
232	if (cmd<0 || cmd > CMD_MAX)
233		goto done;
234	err = -EFAULT;
235	argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
236	respsize = sizes[cmd].respsize;	/* maximum */
237	if (!access_ok(VERIFY_READ, argp, argsize)
238	 || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
239		goto done;
240	}
241	err = -ENOMEM;	/* ??? */
242	if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
243	    (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
244		goto done;
245	}
246
247	err = -EINVAL;
248	copy_from_user(arg, argp, argsize);
249	if (arg->ca_version != NFSCTL_VERSION) {
250		printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
251		goto done;
252	}
253
254	switch(cmd) {
255	case NFSCTL_SVC:
256		err = nfsctl_svc(&arg->ca_svc);
257		break;
258	case NFSCTL_ADDCLIENT:
259		err = nfsctl_addclient(&arg->ca_client);
260		break;
261	case NFSCTL_DELCLIENT:
262		err = nfsctl_delclient(&arg->ca_client);
263		break;
264	case NFSCTL_EXPORT:
265		err = nfsctl_export(&arg->ca_export);
266		break;
267	case NFSCTL_UNEXPORT:
268		err = nfsctl_unexport(&arg->ca_export);
269		break;
270#ifdef notyet
271	case NFSCTL_UGIDUPDATE:
272		err = nfsctl_ugidupdate(&arg->ca_umap);
273		break;
274#endif
275	case NFSCTL_GETFH:
276		err = nfsctl_getfh(&arg->ca_getfh, res->cr_getfh);
277		break;
278	case NFSCTL_GETFD:
279		err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
280		break;
281	case NFSCTL_GETFS:
282		err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
283		respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
284		break;
285	default:
286		err = -EINVAL;
287	}
288
289	if (!err && resp && respsize)
290		copy_to_user(resp, res, respsize);
291
292done:
293	if (arg)
294		kfree(arg);
295	if (res)
296		kfree(res);
297
298	unlock_kernel ();
299	return err;
300}
301
302EXPORT_NO_SYMBOLS;
303MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
304MODULE_LICENSE("GPL");
305
306#ifdef MODULE
307struct nfsd_linkage nfsd_linkage_s = {
308	do_nfsservctl: handle_sys_nfsservctl,
309	owner: THIS_MODULE,
310};
311#endif
312
313/*
314 * Initialize the module
315 */
316static int __init
317nfsd_init(void)
318{
319	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
320#ifdef MODULE
321	nfsd_linkage = &nfsd_linkage_s;
322#endif
323	nfsd_stat_init();	/* Statistics */
324	nfsd_cache_init();	/* RPC reply cache */
325	nfsd_export_init();	/* Exports table */
326	nfsd_lockd_init();	/* lockd->nfsd callbacks */
327	proc_export_init();
328	return 0;
329}
330
331/*
332 * Clean up the mess before unloading the module
333 */
334static void __exit
335nfsd_exit(void)
336{
337#ifdef MODULE
338	nfsd_linkage = NULL;
339#endif
340	nfsd_export_shutdown();
341	nfsd_cache_shutdown();
342	remove_proc_entry("fs/nfs/exports", NULL);
343	remove_proc_entry("fs/nfs", NULL);
344	nfsd_stat_shutdown();
345	nfsd_lockd_shutdown();
346}
347
348module_init(nfsd_init);
349module_exit(nfsd_exit);
350