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 
37 static int	nfsctl_svc(struct nfsctl_svc *data);
38 static int	nfsctl_addclient(struct nfsctl_client *data);
39 static int	nfsctl_delclient(struct nfsctl_client *data);
40 static int	nfsctl_export(struct nfsctl_export *data);
41 static int	nfsctl_unexport(struct nfsctl_export *data);
42 static int	nfsctl_getfh(struct nfsctl_fhparm *, __u8 *);
43 static int	nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
44 static int	nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
45 #ifdef notyet
46 static int	nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
47 #endif
48 
49 extern struct seq_operations nfs_exports_op;
exports_open(struct inode * inode,struct file * file)50 static int exports_open(struct inode *inode, struct file *file)
51 {
52 	return seq_open(file, &nfs_exports_op);
53 }
54 static struct file_operations exports_operations = {
55 	open:		exports_open,
56 	read:		seq_read,
57 	llseek:		seq_lseek,
58 	release:	seq_release,
59 };
60 
proc_export_init(void)61 void 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 
71 static inline int
nfsctl_svc(struct nfsctl_svc * data)72 nfsctl_svc(struct nfsctl_svc *data)
73 {
74 	return nfsd_svc(data->svc_port, data->svc_nthreads);
75 }
76 
77 static inline int
nfsctl_addclient(struct nfsctl_client * data)78 nfsctl_addclient(struct nfsctl_client *data)
79 {
80 	return exp_addclient(data);
81 }
82 
83 static inline int
nfsctl_delclient(struct nfsctl_client * data)84 nfsctl_delclient(struct nfsctl_client *data)
85 {
86 	return exp_delclient(data);
87 }
88 
89 static inline int
nfsctl_export(struct nfsctl_export * data)90 nfsctl_export(struct nfsctl_export *data)
91 {
92 	return exp_export(data);
93 }
94 
95 static inline int
nfsctl_unexport(struct nfsctl_export * data)96 nfsctl_unexport(struct nfsctl_export *data)
97 {
98 	return exp_unexport(data);
99 }
100 
101 #ifdef notyet
102 static inline int
nfsctl_ugidupdate(nfs_ugidmap * data)103 nfsctl_ugidupdate(nfs_ugidmap *data)
104 {
105 	return -EINVAL;
106 }
107 #endif
108 
109 static inline int
nfsctl_getfs(struct nfsctl_fsparm * data,struct knfsd_fh * res)110 nfsctl_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 
130 static inline int
nfsctl_getfd(struct nfsctl_fdparm * data,__u8 * res)131 nfsctl_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 
163 static inline int
nfsctl_getfh(struct nfsctl_fhparm * data,__u8 * res)164 nfsctl_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 
200 static 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 
215 #ifdef MODULE
216 long
handle_sys_nfsservctl(int cmd,void * opaque_argp,void * opaque_resp)217 handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
218 #else
219 long
220 asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
221 #endif
222 {
223 	struct nfsctl_arg *	argp = opaque_argp;
224 	union nfsctl_res *	resp = opaque_resp;
225 	struct nfsctl_arg *	arg = NULL;
226 	union nfsctl_res *	res = NULL;
227 	int			err;
228 	int			argsize, respsize;
229 
230 	lock_kernel ();
231 
232 	err = -EPERM;
233 	if (!capable(CAP_SYS_ADMIN)) {
234 		goto done;
235 	}
236 	err = -EINVAL;
237 	if (cmd<0 || cmd > CMD_MAX)
238 		goto done;
239 	err = -EFAULT;
240 	argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
241 	respsize = sizes[cmd].respsize;	/* maximum */
242 	if (!access_ok(VERIFY_READ, argp, argsize)
243 	 || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
244 		goto done;
245 	}
246 	err = -ENOMEM;	/* ??? */
247 	if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
248 	    (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
249 		goto done;
250 	}
251 
252 	err = -EINVAL;
253 	copy_from_user(arg, argp, argsize);
254 	if (arg->ca_version != NFSCTL_VERSION) {
255 		printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
256 		goto done;
257 	}
258 
259 	switch(cmd) {
260 	case NFSCTL_SVC:
261 		err = nfsctl_svc(&arg->ca_svc);
262 		break;
263 	case NFSCTL_ADDCLIENT:
264 		err = nfsctl_addclient(&arg->ca_client);
265 		break;
266 	case NFSCTL_DELCLIENT:
267 		err = nfsctl_delclient(&arg->ca_client);
268 		break;
269 	case NFSCTL_EXPORT:
270 		err = nfsctl_export(&arg->ca_export);
271 		break;
272 	case NFSCTL_UNEXPORT:
273 		err = nfsctl_unexport(&arg->ca_export);
274 		break;
275 #ifdef notyet
276 	case NFSCTL_UGIDUPDATE:
277 		err = nfsctl_ugidupdate(&arg->ca_umap);
278 		break;
279 #endif
280 	case NFSCTL_GETFH:
281 		err = nfsctl_getfh(&arg->ca_getfh, res->cr_getfh);
282 		break;
283 	case NFSCTL_GETFD:
284 		err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
285 		break;
286 	case NFSCTL_GETFS:
287 		err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
288 		respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
289 		break;
290 	default:
291 		err = -EINVAL;
292 	}
293 
294 	if (!err && resp && respsize)
295 		copy_to_user(resp, res, respsize);
296 
297 done:
298 	if (arg)
299 		kfree(arg);
300 	if (res)
301 		kfree(res);
302 
303 	unlock_kernel ();
304 	return err;
305 }
306 
307 EXPORT_NO_SYMBOLS;
308 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
309 MODULE_LICENSE("GPL");
310 
311 #ifdef MODULE
312 struct nfsd_linkage nfsd_linkage_s = {
313 	do_nfsservctl: handle_sys_nfsservctl,
314 	owner: THIS_MODULE,
315 };
316 #endif
317 
318 /*
319  * Initialize the module
320  */
321 static int __init
nfsd_init(void)322 nfsd_init(void)
323 {
324 	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
325 #ifdef MODULE
326 	nfsd_linkage = &nfsd_linkage_s;
327 #endif
328 	nfsd_stat_init();	/* Statistics */
329 	nfsd_cache_init();	/* RPC reply cache */
330 	nfsd_export_init();	/* Exports table */
331 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
332 	proc_export_init();
333 	return 0;
334 }
335 
336 /*
337  * Clean up the mess before unloading the module
338  */
339 static void __exit
nfsd_exit(void)340 nfsd_exit(void)
341 {
342 #ifdef MODULE
343 	nfsd_linkage = NULL;
344 #endif
345 	nfsd_export_shutdown();
346 	nfsd_cache_shutdown();
347 	remove_proc_entry("fs/nfs/exports", NULL);
348 	remove_proc_entry("fs/nfs", NULL);
349 	nfsd_stat_shutdown();
350 	nfsd_lockd_shutdown();
351 }
352 
353 module_init(nfsd_init);
354 module_exit(nfsd_exit);
355