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