[PATCH] knfsd cleanups
[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/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
24 #include <linux/nfs.h>
25 #include <linux/sunrpc/svc.h>
26 #include <linux/nfsd/nfsd.h>
27 #include <linux/nfsd/cache.h>
28 #include <linux/nfsd/xdr.h>
29 #include <linux/nfsd/syscall.h>
30
31 #include <asm/uaccess.h>
32 #include <linux/smp.h>
33 #include <linux/smp_lock.h>
34
35 static int      nfsctl_svc(struct nfsctl_svc *data);
36 static int      nfsctl_addclient(struct nfsctl_client *data);
37 static int      nfsctl_delclient(struct nfsctl_client *data);
38 static int      nfsctl_export(struct nfsctl_export *data);
39 static int      nfsctl_unexport(struct nfsctl_export *data);
40 static int      nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
41 static int      nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
42 #ifdef notyet
43 static int      nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
44 #endif
45
46 static int      initialized;
47
48 int exp_procfs_exports(char *buffer, char **start, off_t offset,
49                              int length, int *eof, void *data);
50
51 void proc_export_init(void)
52 {
53         if (!proc_mkdir("fs/nfs", 0))
54                 return;
55         create_proc_read_entry("fs/nfs/exports", 0, 0, exp_procfs_exports,NULL);
56 }
57
58
59 /*
60  * Initialize nfsd
61  */
62 static void
63 nfsd_init(void)
64 {
65         nfsd_stat_init();       /* Statistics */
66         nfsd_cache_init();      /* RPC reply cache */
67         nfsd_export_init();     /* Exports table */
68         nfsd_lockd_init();      /* lockd->nfsd callbacks */
69         proc_export_init();
70         initialized = 1;
71 }
72
73 static inline int
74 nfsctl_svc(struct nfsctl_svc *data)
75 {
76         return nfsd_svc(data->svc_port, data->svc_nthreads);
77 }
78
79 static inline int
80 nfsctl_addclient(struct nfsctl_client *data)
81 {
82         return exp_addclient(data);
83 }
84
85 static inline int
86 nfsctl_delclient(struct nfsctl_client *data)
87 {
88         return exp_delclient(data);
89 }
90
91 static inline int
92 nfsctl_export(struct nfsctl_export *data)
93 {
94         return exp_export(data);
95 }
96
97 static inline int
98 nfsctl_unexport(struct nfsctl_export *data)
99 {
100         return exp_unexport(data);
101 }
102
103 #ifdef notyet
104 static inline int
105 nfsctl_ugidupdate(nfs_ugidmap *data)
106 {
107         return -EINVAL;
108 }
109 #endif
110
111 static inline int
112 nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
113 {
114         struct sockaddr_in      *sin;
115         struct svc_client       *clp;
116         int                     err = 0;
117
118         if (data->gd_addr.sa_family != AF_INET)
119                 return -EPROTONOSUPPORT;
120         sin = (struct sockaddr_in *)&data->gd_addr;
121         if (data->gd_maxlen > NFS3_FHSIZE)
122                 data->gd_maxlen = NFS3_FHSIZE;
123         exp_readlock();
124         if (!(clp = exp_getclient(sin)))
125                 err = -EPERM;
126         else
127                 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
128         exp_unlock();
129         return err;
130 }
131
132 static inline int
133 nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
134 {
135         struct sockaddr_in      *sin;
136         struct svc_client       *clp;
137         int                     err = 0;
138         struct  knfsd_fh        fh;
139
140         if (data->gd_addr.sa_family != AF_INET)
141                 return -EPROTONOSUPPORT;
142         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
143                 return -EINVAL;
144         sin = (struct sockaddr_in *)&data->gd_addr;
145
146         exp_readlock();
147         if (!(clp = exp_getclient(sin)))
148                 err = -EPERM;
149         else
150                 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
151         exp_unlock();
152
153         if (err == 0) {
154                 if (fh.fh_size > NFS_FHSIZE)
155                         err = -EINVAL;
156                 else {
157                         memset(res,0, NFS_FHSIZE);
158                         memcpy(res, &fh.fh_base, fh.fh_size);
159                 }
160         }
161
162         return err;
163 }
164
165 #ifdef CONFIG_NFSD
166 #define handle_sys_nfsservctl sys_nfsservctl
167 #endif
168
169 static struct {
170         int argsize, respsize;
171 }  sizes[] = {
172         /* NFSCTL_SVC        */ { sizeof(struct nfsctl_svc), 0 },
173         /* NFSCTL_ADDCLIENT  */ { sizeof(struct nfsctl_client), 0},
174         /* NFSCTL_DELCLIENT  */ { sizeof(struct nfsctl_client), 0},
175         /* NFSCTL_EXPORT     */ { sizeof(struct nfsctl_export), 0},
176         /* NFSCTL_UNEXPORT   */ { sizeof(struct nfsctl_export), 0},
177         /* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
178         /* NFSCTL_GETFH      */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
179         /* NFSCTL_GETFD      */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
180         /* NFSCTL_GETFS      */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
181 };
182 #define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
183
184 long
185 asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
186 {
187         struct nfsctl_arg *     argp = opaque_argp;
188         union nfsctl_res *      resp = opaque_resp;
189         struct nfsctl_arg *     arg = NULL;
190         union nfsctl_res *      res = NULL;
191         int                     err;
192         int                     argsize, respsize;
193
194         MOD_INC_USE_COUNT;
195         lock_kernel ();
196         if (!initialized)
197                 nfsd_init();
198         err = -EPERM;
199         if (!capable(CAP_SYS_ADMIN)) {
200                 goto done;
201         }
202         err = -EINVAL;
203         if (cmd<0 || cmd > CMD_MAX)
204                 goto done;
205         err = -EFAULT;
206         argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
207         respsize = sizes[cmd].respsize; /* maximum */
208         if (!access_ok(VERIFY_READ, argp, argsize)
209          || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
210                 goto done;
211         }
212         err = -ENOMEM;  /* ??? */
213         if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
214             (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
215                 goto done;
216         }
217
218         err = -EINVAL;
219         copy_from_user(arg, argp, argsize);
220         if (arg->ca_version != NFSCTL_VERSION) {
221                 printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
222                 goto done;
223         }
224
225         switch(cmd) {
226         case NFSCTL_SVC:
227                 err = nfsctl_svc(&arg->ca_svc);
228                 break;
229         case NFSCTL_ADDCLIENT:
230                 err = nfsctl_addclient(&arg->ca_client);
231                 break;
232         case NFSCTL_DELCLIENT:
233                 err = nfsctl_delclient(&arg->ca_client);
234                 break;
235         case NFSCTL_EXPORT:
236                 err = nfsctl_export(&arg->ca_export);
237                 break;
238         case NFSCTL_UNEXPORT:
239                 err = nfsctl_unexport(&arg->ca_export);
240                 break;
241 #ifdef notyet
242         case NFSCTL_UGIDUPDATE:
243                 err = nfsctl_ugidupdate(&arg->ca_umap);
244                 break;
245 #endif
246         case NFSCTL_GETFD:
247                 err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
248                 break;
249         case NFSCTL_GETFS:
250                 err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
251                 respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
252                 break;
253         default:
254                 err = -EINVAL;
255         }
256
257         if (!err && resp && respsize)
258                 copy_to_user(resp, res, respsize);
259
260 done:
261         if (arg)
262                 kfree(arg);
263         if (res)
264                 kfree(res);
265
266         unlock_kernel ();
267         MOD_DEC_USE_COUNT;
268         return err;
269 }
270
271 #ifdef MODULE
272 /* New-style module support since 2.1.18 */
273 EXPORT_NO_SYMBOLS;
274 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
275 MODULE_LICENSE("GPL");
276
277 struct nfsd_linkage nfsd_linkage_s = {
278         do_nfsservctl: handle_sys_nfsservctl,
279 };
280
281 /*
282  * Initialize the module
283  */
284 int
285 init_module(void)
286 {
287         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
288         nfsd_linkage = &nfsd_linkage_s;
289         return 0;
290 }
291
292 /*
293  * Clean up the mess before unloading the module
294  */
295 void
296 cleanup_module(void)
297 {
298         nfsd_linkage = NULL;
299         nfsd_export_shutdown();
300         nfsd_cache_shutdown();
301         remove_proc_entry("fs/nfs/exports", NULL);
302         remove_proc_entry("fs/nfs", NULL);
303         nfsd_stat_shutdown();
304         nfsd_lockd_shutdown();
305 }
306 #endif