[PATCH] PATCH - Create "export_operations" interface for filesystems to describe
[opensuse:kernel.git] / fs / nfsd / nfsctl.c
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/time.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 #include <linux/pagemap.h>
25 #include <linux/init.h>
26
27 #include <linux/nfs.h>
28 #include <linux/sunrpc/svc.h>
29 #include <linux/nfsd/nfsd.h>
30 #include <linux/nfsd/cache.h>
31 #include <linux/nfsd/xdr.h>
32 #include <linux/nfsd/syscall.h>
33 #include <linux/nfsd/interface.h>
34
35 #include <asm/uaccess.h>
36
37 /*
38  *      We have a single directory with 8 nodes in it.
39  */
40 enum {
41         NFSD_Root = 1,
42         NFSD_Svc,
43         NFSD_Add,
44         NFSD_Del,
45         NFSD_Export,
46         NFSD_Unexport,
47         NFSD_Getfd,
48         NFSD_Getfs,
49         NFSD_List,
50 };
51
52 /*
53  * write() for these nodes.
54  */
55 static ssize_t write_svc(struct file *file, const char *buf, size_t size);
56 static ssize_t write_add(struct file *file, const char *buf, size_t size);
57 static ssize_t write_del(struct file *file, const char *buf, size_t size);
58 static ssize_t write_export(struct file *file, const char *buf, size_t size);
59 static ssize_t write_unexport(struct file *file, const char *buf, size_t size);
60 static ssize_t write_getfd(struct file *file, const char *buf, size_t size);
61 static ssize_t write_getfs(struct file *file, const char *buf, size_t size);
62
63 static ssize_t (*write_op[])(struct file *, const char *, size_t) = {
64         [NFSD_Svc] = write_svc,
65         [NFSD_Add] = write_add,
66         [NFSD_Del] = write_del,
67         [NFSD_Export] = write_export,
68         [NFSD_Unexport] = write_unexport,
69         [NFSD_Getfd] = write_getfd,
70         [NFSD_Getfs] = write_getfs,
71 };
72
73 static ssize_t fs_write(struct file *file, const char *buf, size_t size, loff_t *pos)
74 {
75         ino_t ino =  file->f_dentry->d_inode->i_ino;
76         if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
77                 return -EINVAL;
78         return write_op[ino](file, buf, size);
79 }
80
81 /*
82  * read(), open() and release() for getfs and getfd (read/write ones).
83  * IO on these is a simple transaction - you open() the file, write() to it
84  * and that generates a (stored) response.  After that read() will simply
85  * access that response.
86  */
87
88 static ssize_t TA_read(struct file *file, char *buf, size_t size, loff_t *pos)
89 {
90         if (!file->private_data)
91                 return 0;
92         if (*pos >= file->f_dentry->d_inode->i_size)
93                 return 0;
94         if (*pos + size > file->f_dentry->d_inode->i_size)
95                 size = file->f_dentry->d_inode->i_size - *pos;
96         if (copy_to_user(buf, file->private_data + *pos, size))
97                 return -EFAULT;
98         *pos += size;
99         return size;
100 }
101
102 static ssize_t TA_open(struct inode *inode, struct file *file)
103 {
104         file->private_data = NULL;
105         return 0;
106 }
107
108 static ssize_t TA_release(struct inode *inode, struct file *file)
109 {
110         void *p = file->private_data;
111         file->private_data = NULL;
112         kfree(p);
113         return 0;
114 }
115
116 static struct file_operations writer_ops = {
117         write:  fs_write,
118 };
119
120 static struct file_operations reader_ops = {
121         write:  fs_write,
122         read:   TA_read,
123         open:   TA_open,
124         release:TA_release,
125 };
126
127 extern struct seq_operations nfs_exports_op;
128 static int exports_open(struct inode *inode, struct file *file)
129 {
130         return seq_open(file, &nfs_exports_op);
131 }
132 static struct file_operations exports_operations = {
133         open:           exports_open,
134         read:           seq_read,
135         llseek:         seq_lseek,
136         release:        seq_release,
137 };
138
139 /*
140  *      Description of fs contents.
141  */
142 static struct { char *name; struct file_operations *ops; int mode; } files[] = {
143         [NFSD_Svc] = {"svc", &writer_ops, S_IWUSR},
144         [NFSD_Add] = {"add", &writer_ops, S_IWUSR},
145         [NFSD_Del] = {"del", &writer_ops, S_IWUSR},
146         [NFSD_Export] = {"export", &writer_ops, S_IWUSR},
147         [NFSD_Unexport] = {"unexport", &writer_ops, S_IWUSR},
148         [NFSD_Getfd] = {"getfd", &reader_ops, S_IWUSR|S_IRUSR},
149         [NFSD_Getfs] = {"getfs", &reader_ops, S_IWUSR|S_IRUSR},
150         [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
151 };
152
153 /*----------------------------------------------------------------------------*/
154 /*
155  * payload - write methods
156  */
157
158 static ssize_t write_svc(struct file *file, const char *buf, size_t size)
159 {
160         struct nfsctl_svc data;
161         if (size < sizeof(data))
162                 return -EINVAL;
163         if (copy_from_user(&data, buf, size))
164                 return -EFAULT;
165         return nfsd_svc(data.svc_port, data.svc_nthreads);
166 }
167
168 static ssize_t write_add(struct file *file, const char *buf, size_t size)
169 {
170         struct nfsctl_client data;
171         if (size < sizeof(data))
172                 return -EINVAL;
173         if (copy_from_user(&data, buf, size))
174                 return -EFAULT;
175         return exp_addclient(&data);
176 }
177
178 static ssize_t write_del(struct file *file, const char *buf, size_t size)
179 {
180         struct nfsctl_client data;
181         if (size < sizeof(data))
182                 return -EINVAL;
183         if (copy_from_user(&data, buf, size))
184                 return -EFAULT;
185         return exp_delclient(&data);
186 }
187
188 static ssize_t write_export(struct file *file, const char *buf, size_t size)
189 {
190         struct nfsctl_export data;
191         if (size < sizeof(data))
192                 return -EINVAL;
193         if (copy_from_user(&data, buf, size))
194                 return -EFAULT;
195         return exp_export(&data);
196 }
197
198 static ssize_t write_unexport(struct file *file, const char *buf, size_t size)
199 {
200         struct nfsctl_export data;
201         if (size < sizeof(data))
202                 return -EINVAL;
203         if (copy_from_user(&data, buf, size))
204                 return -EFAULT;
205         return exp_unexport(&data);
206 }
207
208 static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
209 {
210         struct nfsctl_fsparm data;
211         struct sockaddr_in *sin;
212         struct svc_client *clp;
213         int err = 0;
214         struct knfsd_fh *res;
215
216         if (file->private_data)
217                 return -EINVAL;
218         if (size < sizeof(data))
219                 return -EINVAL;
220         if (copy_from_user(&data, buf, size))
221                 return -EFAULT;
222         if (data.gd_addr.sa_family != AF_INET)
223                 return -EPROTONOSUPPORT;
224         sin = (struct sockaddr_in *)&data.gd_addr;
225         if (data.gd_maxlen > NFS3_FHSIZE)
226                 data.gd_maxlen = NFS3_FHSIZE;
227         res = kmalloc(sizeof(struct knfsd_fh), GFP_KERNEL);
228         if (!res)
229                 return -ENOMEM;
230         memset(res, 0, sizeof(struct knfsd_fh));
231         exp_readlock();
232         if (!(clp = exp_getclient(sin)))
233                 err = -EPERM;
234         else
235                 err = exp_rootfh(clp, data.gd_path, res, data.gd_maxlen);
236         exp_readunlock();
237
238         down(&file->f_dentry->d_inode->i_sem);
239         if (file->private_data)
240                 err = -EINVAL;
241         if (err)
242                 kfree(res);
243         else {
244                 file->f_dentry->d_inode->i_size = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
245                 file->private_data = res;
246                 err = sizeof(data);
247         }
248         up(&file->f_dentry->d_inode->i_sem);
249
250         return err;
251 }
252
253 static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
254 {
255         struct nfsctl_fdparm data;
256         struct sockaddr_in *sin;
257         struct svc_client *clp;
258         int err = 0;
259         struct knfsd_fh fh;
260         char *res;
261
262         if (file->private_data)
263                 return -EINVAL;
264         if (size < sizeof(data))
265                 return -EINVAL;
266         if (copy_from_user(&data, buf, size))
267                 return -EFAULT;
268         if (data.gd_addr.sa_family != AF_INET)
269                 return -EPROTONOSUPPORT;
270         if (data.gd_version < 2 || data.gd_version > NFSSVC_MAXVERS)
271                 return -EINVAL;
272         res = kmalloc(NFS_FHSIZE, GFP_KERNEL);
273         if (!res)
274                 return -ENOMEM;
275         sin = (struct sockaddr_in *)&data.gd_addr;
276         exp_readlock();
277         if (!(clp = exp_getclient(sin)))
278                 err = -EPERM;
279         else
280                 err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE);
281         exp_readunlock();
282
283         down(&file->f_dentry->d_inode->i_sem);
284         if (file->private_data)
285                 err = -EINVAL;
286         if (!err && fh.fh_size > NFS_FHSIZE)
287                 err = -EINVAL;
288         if (err)
289                 kfree(res);
290         else {
291                 memset(res,0, NFS_FHSIZE);
292                 memcpy(res, &fh.fh_base, fh.fh_size);
293                 file->f_dentry->d_inode->i_size = NFS_FHSIZE;
294                 file->private_data = res;
295                 err = sizeof(data);
296         }
297         up(&file->f_dentry->d_inode->i_sem);
298
299         return err;
300 }
301
302 /*----------------------------------------------------------------------------*/
303 /*
304  *      populating the filesystem.
305  */
306
307 static struct super_operations s_ops = {
308         statfs:         simple_statfs,
309 };
310
311 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
312 {
313         struct inode *inode;
314         struct dentry *root;
315         struct dentry *dentry;
316         int i;
317
318         sb->s_blocksize = PAGE_CACHE_SIZE;
319         sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
320         sb->s_magic = 0x6e667364;
321         sb->s_op = &s_ops;
322
323         inode = new_inode(sb);
324         if (!inode)
325                 return -ENOMEM;
326         inode->i_mode = S_IFDIR | 0755;
327         inode->i_uid = inode->i_gid = 0;
328         inode->i_blksize = PAGE_CACHE_SIZE;
329         inode->i_blocks = 0;
330         inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
331         inode->i_op = &simple_dir_inode_operations;
332         inode->i_fop = &simple_dir_operations;
333         root = d_alloc_root(inode);
334         if (!root) {
335                 iput(inode);
336                 return -ENOMEM;
337         }
338         for (i = NFSD_Svc; i <= NFSD_List; i++) {
339                 struct qstr name;
340                 name.name = files[i].name;
341                 name.len = strlen(name.name);
342                 name.hash = full_name_hash(name.name, name.len);
343                 dentry = d_alloc(root, &name);
344                 if (!dentry)
345                         goto out;
346                 inode = new_inode(sb);
347                 if (!inode)
348                         goto out;
349                 inode->i_mode = S_IFREG | files[i].mode;
350                 inode->i_uid = inode->i_gid = 0;
351                 inode->i_blksize = PAGE_CACHE_SIZE;
352                 inode->i_blocks = 0;
353                 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
354                 inode->i_fop = files[i].ops;
355                 inode->i_ino = i;
356                 d_add(dentry, inode);
357         }
358         sb->s_root = root;
359         return 0;
360
361 out:
362         d_genocide(root);
363         dput(root);
364         return -ENOMEM;
365 }
366
367 static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
368         int flags, char *dev_name, void *data)
369 {
370         return get_sb_single(fs_type, flags, data, nfsd_fill_super);
371 }
372
373 static struct file_system_type nfsd_fs_type = {
374         owner:          THIS_MODULE,
375         name:           "nfsd",
376         get_sb:         nfsd_get_sb,
377         kill_sb:        kill_litter_super,
378 };
379
380 static int __init init_nfsd(void)
381 {
382         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
383
384         nfsd_stat_init();       /* Statistics */
385         nfsd_cache_init();      /* RPC reply cache */
386         nfsd_export_init();     /* Exports table */
387         nfsd_lockd_init();      /* lockd->nfsd callbacks */
388         if (proc_mkdir("fs/nfs", 0)) {
389                 struct proc_dir_entry *entry;
390                 entry = create_proc_entry("fs/nfs/exports", 0, NULL);
391                 if (entry)
392                         entry->proc_fops =  &exports_operations;
393         }
394         register_filesystem(&nfsd_fs_type);
395         return 0;
396 }
397
398 static void __exit exit_nfsd(void)
399 {
400         nfsd_export_shutdown();
401         nfsd_cache_shutdown();
402         remove_proc_entry("fs/nfs/exports", NULL);
403         remove_proc_entry("fs/nfs", NULL);
404         nfsd_stat_shutdown();
405         nfsd_lockd_shutdown();
406         unregister_filesystem(&nfsd_fs_type);
407 }
408
409 EXPORT_NO_SYMBOLS;
410 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
411 MODULE_LICENSE("GPL");
412 module_init(init_nfsd)
413 module_exit(exit_nfsd)