diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/nfsd/nfsctl.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/nfsd/nfsctl.c')
-rw-r--r-- | fs/nfsd/nfsctl.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c new file mode 100644 index 000000000000..161afdcb8f7d --- /dev/null +++ b/fs/nfsd/nfsctl.c | |||
@@ -0,0 +1,438 @@ | |||
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 | |||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/time.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/fcntl.h> | ||
17 | #include <linux/net.h> | ||
18 | #include <linux/in.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/unistd.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/proc_fs.h> | ||
23 | #include <linux/seq_file.h> | ||
24 | #include <linux/pagemap.h> | ||
25 | #include <linux/init.h> | ||
26 | |||
27 | #include <linux/nfs.h> | ||
28 | #include <linux/nfsd_idmap.h> | ||
29 | #include <linux/sunrpc/svc.h> | ||
30 | #include <linux/nfsd/nfsd.h> | ||
31 | #include <linux/nfsd/cache.h> | ||
32 | #include <linux/nfsd/xdr.h> | ||
33 | #include <linux/nfsd/syscall.h> | ||
34 | #include <linux/nfsd/interface.h> | ||
35 | |||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | /* | ||
39 | * We have a single directory with 9 nodes in it. | ||
40 | */ | ||
41 | enum { | ||
42 | NFSD_Root = 1, | ||
43 | NFSD_Svc, | ||
44 | NFSD_Add, | ||
45 | NFSD_Del, | ||
46 | NFSD_Export, | ||
47 | NFSD_Unexport, | ||
48 | NFSD_Getfd, | ||
49 | NFSD_Getfs, | ||
50 | NFSD_List, | ||
51 | NFSD_Fh, | ||
52 | NFSD_Threads, | ||
53 | NFSD_Leasetime, | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * write() for these nodes. | ||
58 | */ | ||
59 | static ssize_t write_svc(struct file *file, char *buf, size_t size); | ||
60 | static ssize_t write_add(struct file *file, char *buf, size_t size); | ||
61 | static ssize_t write_del(struct file *file, char *buf, size_t size); | ||
62 | static ssize_t write_export(struct file *file, char *buf, size_t size); | ||
63 | static ssize_t write_unexport(struct file *file, char *buf, size_t size); | ||
64 | static ssize_t write_getfd(struct file *file, char *buf, size_t size); | ||
65 | static ssize_t write_getfs(struct file *file, char *buf, size_t size); | ||
66 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size); | ||
67 | static ssize_t write_threads(struct file *file, char *buf, size_t size); | ||
68 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size); | ||
69 | |||
70 | static ssize_t (*write_op[])(struct file *, char *, size_t) = { | ||
71 | [NFSD_Svc] = write_svc, | ||
72 | [NFSD_Add] = write_add, | ||
73 | [NFSD_Del] = write_del, | ||
74 | [NFSD_Export] = write_export, | ||
75 | [NFSD_Unexport] = write_unexport, | ||
76 | [NFSD_Getfd] = write_getfd, | ||
77 | [NFSD_Getfs] = write_getfs, | ||
78 | [NFSD_Fh] = write_filehandle, | ||
79 | [NFSD_Threads] = write_threads, | ||
80 | [NFSD_Leasetime] = write_leasetime, | ||
81 | }; | ||
82 | |||
83 | static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | ||
84 | { | ||
85 | ino_t ino = file->f_dentry->d_inode->i_ino; | ||
86 | char *data; | ||
87 | ssize_t rv; | ||
88 | |||
89 | if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino]) | ||
90 | return -EINVAL; | ||
91 | |||
92 | data = simple_transaction_get(file, buf, size); | ||
93 | if (IS_ERR(data)) | ||
94 | return PTR_ERR(data); | ||
95 | |||
96 | rv = write_op[ino](file, data, size); | ||
97 | if (rv>0) { | ||
98 | simple_transaction_set(file, rv); | ||
99 | rv = size; | ||
100 | } | ||
101 | return rv; | ||
102 | } | ||
103 | |||
104 | static struct file_operations transaction_ops = { | ||
105 | .write = nfsctl_transaction_write, | ||
106 | .read = simple_transaction_read, | ||
107 | .release = simple_transaction_release, | ||
108 | }; | ||
109 | |||
110 | extern struct seq_operations nfs_exports_op; | ||
111 | static int exports_open(struct inode *inode, struct file *file) | ||
112 | { | ||
113 | return seq_open(file, &nfs_exports_op); | ||
114 | } | ||
115 | |||
116 | static struct file_operations exports_operations = { | ||
117 | .open = exports_open, | ||
118 | .read = seq_read, | ||
119 | .llseek = seq_lseek, | ||
120 | .release = seq_release, | ||
121 | }; | ||
122 | |||
123 | /*----------------------------------------------------------------------------*/ | ||
124 | /* | ||
125 | * payload - write methods | ||
126 | * If the method has a response, the response should be put in buf, | ||
127 | * and the length returned. Otherwise return 0 or and -error. | ||
128 | */ | ||
129 | |||
130 | static ssize_t write_svc(struct file *file, char *buf, size_t size) | ||
131 | { | ||
132 | struct nfsctl_svc *data; | ||
133 | if (size < sizeof(*data)) | ||
134 | return -EINVAL; | ||
135 | data = (struct nfsctl_svc*) buf; | ||
136 | return nfsd_svc(data->svc_port, data->svc_nthreads); | ||
137 | } | ||
138 | |||
139 | static ssize_t write_add(struct file *file, char *buf, size_t size) | ||
140 | { | ||
141 | struct nfsctl_client *data; | ||
142 | if (size < sizeof(*data)) | ||
143 | return -EINVAL; | ||
144 | data = (struct nfsctl_client *)buf; | ||
145 | return exp_addclient(data); | ||
146 | } | ||
147 | |||
148 | static ssize_t write_del(struct file *file, char *buf, size_t size) | ||
149 | { | ||
150 | struct nfsctl_client *data; | ||
151 | if (size < sizeof(*data)) | ||
152 | return -EINVAL; | ||
153 | data = (struct nfsctl_client *)buf; | ||
154 | return exp_delclient(data); | ||
155 | } | ||
156 | |||
157 | static ssize_t write_export(struct file *file, char *buf, size_t size) | ||
158 | { | ||
159 | struct nfsctl_export *data; | ||
160 | if (size < sizeof(*data)) | ||
161 | return -EINVAL; | ||
162 | data = (struct nfsctl_export*)buf; | ||
163 | return exp_export(data); | ||
164 | } | ||
165 | |||
166 | static ssize_t write_unexport(struct file *file, char *buf, size_t size) | ||
167 | { | ||
168 | struct nfsctl_export *data; | ||
169 | |||
170 | if (size < sizeof(*data)) | ||
171 | return -EINVAL; | ||
172 | data = (struct nfsctl_export*)buf; | ||
173 | return exp_unexport(data); | ||
174 | } | ||
175 | |||
176 | static ssize_t write_getfs(struct file *file, char *buf, size_t size) | ||
177 | { | ||
178 | struct nfsctl_fsparm *data; | ||
179 | struct sockaddr_in *sin; | ||
180 | struct auth_domain *clp; | ||
181 | int err = 0; | ||
182 | struct knfsd_fh *res; | ||
183 | |||
184 | if (size < sizeof(*data)) | ||
185 | return -EINVAL; | ||
186 | data = (struct nfsctl_fsparm*)buf; | ||
187 | err = -EPROTONOSUPPORT; | ||
188 | if (data->gd_addr.sa_family != AF_INET) | ||
189 | goto out; | ||
190 | sin = (struct sockaddr_in *)&data->gd_addr; | ||
191 | if (data->gd_maxlen > NFS3_FHSIZE) | ||
192 | data->gd_maxlen = NFS3_FHSIZE; | ||
193 | |||
194 | res = (struct knfsd_fh*)buf; | ||
195 | |||
196 | exp_readlock(); | ||
197 | if (!(clp = auth_unix_lookup(sin->sin_addr))) | ||
198 | err = -EPERM; | ||
199 | else { | ||
200 | err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); | ||
201 | auth_domain_put(clp); | ||
202 | } | ||
203 | exp_readunlock(); | ||
204 | if (err == 0) | ||
205 | err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; | ||
206 | out: | ||
207 | return err; | ||
208 | } | ||
209 | |||
210 | static ssize_t write_getfd(struct file *file, char *buf, size_t size) | ||
211 | { | ||
212 | struct nfsctl_fdparm *data; | ||
213 | struct sockaddr_in *sin; | ||
214 | struct auth_domain *clp; | ||
215 | int err = 0; | ||
216 | struct knfsd_fh fh; | ||
217 | char *res; | ||
218 | |||
219 | if (size < sizeof(*data)) | ||
220 | return -EINVAL; | ||
221 | data = (struct nfsctl_fdparm*)buf; | ||
222 | err = -EPROTONOSUPPORT; | ||
223 | if (data->gd_addr.sa_family != AF_INET) | ||
224 | goto out; | ||
225 | err = -EINVAL; | ||
226 | if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) | ||
227 | goto out; | ||
228 | |||
229 | res = buf; | ||
230 | sin = (struct sockaddr_in *)&data->gd_addr; | ||
231 | exp_readlock(); | ||
232 | if (!(clp = auth_unix_lookup(sin->sin_addr))) | ||
233 | err = -EPERM; | ||
234 | else { | ||
235 | err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); | ||
236 | auth_domain_put(clp); | ||
237 | } | ||
238 | exp_readunlock(); | ||
239 | |||
240 | if (err == 0) { | ||
241 | memset(res,0, NFS_FHSIZE); | ||
242 | memcpy(res, &fh.fh_base, fh.fh_size); | ||
243 | err = NFS_FHSIZE; | ||
244 | } | ||
245 | out: | ||
246 | return err; | ||
247 | } | ||
248 | |||
249 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size) | ||
250 | { | ||
251 | /* request is: | ||
252 | * domain path maxsize | ||
253 | * response is | ||
254 | * filehandle | ||
255 | * | ||
256 | * qword quoting is used, so filehandle will be \x.... | ||
257 | */ | ||
258 | char *dname, *path; | ||
259 | int maxsize; | ||
260 | char *mesg = buf; | ||
261 | int len; | ||
262 | struct auth_domain *dom; | ||
263 | struct knfsd_fh fh; | ||
264 | |||
265 | if (buf[size-1] != '\n') | ||
266 | return -EINVAL; | ||
267 | buf[size-1] = 0; | ||
268 | |||
269 | dname = mesg; | ||
270 | len = qword_get(&mesg, dname, size); | ||
271 | if (len <= 0) return -EINVAL; | ||
272 | |||
273 | path = dname+len+1; | ||
274 | len = qword_get(&mesg, path, size); | ||
275 | if (len <= 0) return -EINVAL; | ||
276 | |||
277 | len = get_int(&mesg, &maxsize); | ||
278 | if (len) | ||
279 | return len; | ||
280 | |||
281 | if (maxsize < NFS_FHSIZE) | ||
282 | return -EINVAL; | ||
283 | if (maxsize > NFS3_FHSIZE) | ||
284 | maxsize = NFS3_FHSIZE; | ||
285 | |||
286 | if (qword_get(&mesg, mesg, size)>0) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* we have all the words, they are in buf.. */ | ||
290 | dom = unix_domain_find(dname); | ||
291 | if (!dom) | ||
292 | return -ENOMEM; | ||
293 | |||
294 | len = exp_rootfh(dom, path, &fh, maxsize); | ||
295 | auth_domain_put(dom); | ||
296 | if (len) | ||
297 | return len; | ||
298 | |||
299 | mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; | ||
300 | qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); | ||
301 | mesg[-1] = '\n'; | ||
302 | return mesg - buf; | ||
303 | } | ||
304 | |||
305 | extern int nfsd_nrthreads(void); | ||
306 | |||
307 | static ssize_t write_threads(struct file *file, char *buf, size_t size) | ||
308 | { | ||
309 | /* if size > 0, look for a number of threads and call nfsd_svc | ||
310 | * then write out number of threads as reply | ||
311 | */ | ||
312 | char *mesg = buf; | ||
313 | int rv; | ||
314 | if (size > 0) { | ||
315 | int newthreads; | ||
316 | rv = get_int(&mesg, &newthreads); | ||
317 | if (rv) | ||
318 | return rv; | ||
319 | if (newthreads <0) | ||
320 | return -EINVAL; | ||
321 | rv = nfsd_svc(2049, newthreads); | ||
322 | if (rv) | ||
323 | return rv; | ||
324 | } | ||
325 | sprintf(buf, "%d\n", nfsd_nrthreads()); | ||
326 | return strlen(buf); | ||
327 | } | ||
328 | |||
329 | extern time_t nfs4_leasetime(void); | ||
330 | |||
331 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) | ||
332 | { | ||
333 | /* if size > 10 seconds, call | ||
334 | * nfs4_reset_lease() then write out the new lease (seconds) as reply | ||
335 | */ | ||
336 | char *mesg = buf; | ||
337 | int rv; | ||
338 | |||
339 | if (size > 0) { | ||
340 | int lease; | ||
341 | rv = get_int(&mesg, &lease); | ||
342 | if (rv) | ||
343 | return rv; | ||
344 | if (lease < 10 || lease > 3600) | ||
345 | return -EINVAL; | ||
346 | nfs4_reset_lease(lease); | ||
347 | } | ||
348 | sprintf(buf, "%ld\n", nfs4_lease_time()); | ||
349 | return strlen(buf); | ||
350 | } | ||
351 | |||
352 | /*----------------------------------------------------------------------------*/ | ||
353 | /* | ||
354 | * populating the filesystem. | ||
355 | */ | ||
356 | |||
357 | static int nfsd_fill_super(struct super_block * sb, void * data, int silent) | ||
358 | { | ||
359 | static struct tree_descr nfsd_files[] = { | ||
360 | [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR}, | ||
361 | [NFSD_Add] = {".add", &transaction_ops, S_IWUSR}, | ||
362 | [NFSD_Del] = {".del", &transaction_ops, S_IWUSR}, | ||
363 | [NFSD_Export] = {".export", &transaction_ops, S_IWUSR}, | ||
364 | [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR}, | ||
365 | [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
366 | [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
367 | [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, | ||
368 | [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
369 | [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
370 | #ifdef CONFIG_NFSD_V4 | ||
371 | [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
372 | #endif | ||
373 | /* last one */ {""} | ||
374 | }; | ||
375 | return simple_fill_super(sb, 0x6e667364, nfsd_files); | ||
376 | } | ||
377 | |||
378 | static struct super_block *nfsd_get_sb(struct file_system_type *fs_type, | ||
379 | int flags, const char *dev_name, void *data) | ||
380 | { | ||
381 | return get_sb_single(fs_type, flags, data, nfsd_fill_super); | ||
382 | } | ||
383 | |||
384 | static struct file_system_type nfsd_fs_type = { | ||
385 | .owner = THIS_MODULE, | ||
386 | .name = "nfsd", | ||
387 | .get_sb = nfsd_get_sb, | ||
388 | .kill_sb = kill_litter_super, | ||
389 | }; | ||
390 | |||
391 | static int __init init_nfsd(void) | ||
392 | { | ||
393 | int retval; | ||
394 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); | ||
395 | |||
396 | nfsd_stat_init(); /* Statistics */ | ||
397 | nfsd_cache_init(); /* RPC reply cache */ | ||
398 | nfsd_export_init(); /* Exports table */ | ||
399 | nfsd_lockd_init(); /* lockd->nfsd callbacks */ | ||
400 | #ifdef CONFIG_NFSD_V4 | ||
401 | nfsd_idmap_init(); /* Name to ID mapping */ | ||
402 | #endif /* CONFIG_NFSD_V4 */ | ||
403 | if (proc_mkdir("fs/nfs", NULL)) { | ||
404 | struct proc_dir_entry *entry; | ||
405 | entry = create_proc_entry("fs/nfs/exports", 0, NULL); | ||
406 | if (entry) | ||
407 | entry->proc_fops = &exports_operations; | ||
408 | } | ||
409 | retval = register_filesystem(&nfsd_fs_type); | ||
410 | if (retval) { | ||
411 | nfsd_export_shutdown(); | ||
412 | nfsd_cache_shutdown(); | ||
413 | remove_proc_entry("fs/nfs/exports", NULL); | ||
414 | remove_proc_entry("fs/nfs", NULL); | ||
415 | nfsd_stat_shutdown(); | ||
416 | nfsd_lockd_shutdown(); | ||
417 | } | ||
418 | return retval; | ||
419 | } | ||
420 | |||
421 | static void __exit exit_nfsd(void) | ||
422 | { | ||
423 | nfsd_export_shutdown(); | ||
424 | nfsd_cache_shutdown(); | ||
425 | remove_proc_entry("fs/nfs/exports", NULL); | ||
426 | remove_proc_entry("fs/nfs", NULL); | ||
427 | nfsd_stat_shutdown(); | ||
428 | nfsd_lockd_shutdown(); | ||
429 | #ifdef CONFIG_NFSD_V4 | ||
430 | nfsd_idmap_shutdown(); | ||
431 | #endif /* CONFIG_NFSD_V4 */ | ||
432 | unregister_filesystem(&nfsd_fs_type); | ||
433 | } | ||
434 | |||
435 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); | ||
436 | MODULE_LICENSE("GPL"); | ||
437 | module_init(init_nfsd) | ||
438 | module_exit(exit_nfsd) | ||