v2.4.9.9 -> v2.4.9.10
[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_getfh(struct nfsctl_fhparm *, __u8 *);
41 static int      nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
42 static int      nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
43 #ifdef notyet
44 static int      nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
45 #endif
46
47 static int      initialized;
48
49 int exp_procfs_exports(char *buffer, char **start, off_t offset,
50                              int length, int *eof, void *data);
51
52 void proc_export_init(void)
53 {
54         if (!proc_mkdir("fs/nfs", 0))
55                 return;
56         create_proc_read_entry("fs/nfs/exports", 0, 0, exp_procfs_exports,NULL);
57 }
58
59
60 /*
61  * Initialize nfsd
62  */
63 static void
64 nfsd_init(void)
65 {
66         nfsd_stat_init();       /* Statistics */
67         nfsd_cache_init();      /* RPC reply cache */
68         nfsd_export_init();     /* Exports table */
69         nfsd_lockd_init();      /* lockd->nfsd callbacks */
70         proc_export_init();
71         initialized = 1;
72 }
73
74 static inline int
75 nfsctl_svc(struct nfsctl_svc *data)
76 {
77         return nfsd_svc(data->svc_port, data->svc_nthreads);
78 }
79
80 static inline int
81 nfsctl_addclient(struct nfsctl_client *data)
82 {
83         return exp_addclient(data);
84 }
85
86 static inline int
87 nfsctl_delclient(struct nfsctl_client *data)
88 {
89         return exp_delclient(data);
90 }
91
92 static inline int
93 nfsctl_export(struct nfsctl_export *data)
94 {
95         return exp_export(data);
96 }
97
98 static inline int
99 nfsctl_unexport(struct nfsctl_export *data)
100 {
101         return exp_unexport(data);
102 }
103
104 #ifdef notyet
105 static inline int
106 nfsctl_ugidupdate(nfs_ugidmap *data)
107 {
108         return -EINVAL;
109 }
110 #endif
111
112 static inline int
113 nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
114 {
115         struct sockaddr_in      *sin;
116         struct svc_client       *clp;
117         int                     err = 0;
118
119         if (data->gd_addr.sa_family != AF_INET)
120                 return -EPROTONOSUPPORT;
121         sin = (struct sockaddr_in *)&data->gd_addr;
122         if (data->gd_maxlen > NFS3_FHSIZE)
123                 data->gd_maxlen = NFS3_FHSIZE;
124         exp_readlock();
125         if (!(clp = exp_getclient(sin)))
126                 err = -EPERM;
127         else
128                 err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
129         exp_unlock();
130         return err;
131 }
132
133 static inline int
134 nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
135 {
136         struct sockaddr_in      *sin;
137         struct svc_client       *clp;
138         int                     err = 0;
139         struct  knfsd_fh        fh;
140
141         if (data->gd_addr.sa_family != AF_INET)
142                 return -EPROTONOSUPPORT;
143         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
144                 return -EINVAL;
145         sin = (struct sockaddr_in *)&data->gd_addr;
146
147         exp_readlock();
148         if (!(clp = exp_getclient(sin)))
149                 err = -EPERM;
150         else
151                 err = exp_rootfh(clp, 0, 0, data->gd_path, &fh, NFS_FHSIZE);
152         exp_unlock();
153
154         if (err == 0) {
155                 if (fh.fh_size > NFS_FHSIZE)
156                         err = -EINVAL;
157                 else {
158                         memset(res,0, NFS_FHSIZE);
159                         memcpy(res, &fh.fh_base, fh.fh_size);
160                 }
161         }
162
163         return err;
164 }
165
166 static inline int
167 nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res)
168 {
169         struct sockaddr_in      *sin;
170         struct svc_client       *clp;
171         int                     err = 0;
172         struct knfsd_fh         fh;
173
174         if (data->gf_addr.sa_family != AF_INET)
175                 return -EPROTONOSUPPORT;
176         if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS)
177                 return -EINVAL;
178         sin = (struct sockaddr_in *)&data->gf_addr;
179
180         exp_readlock();
181         if (!(clp = exp_getclient(sin)))
182                 err = -EPERM;
183         else
184                 err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, &fh, NFS_FHSIZE);
185         exp_unlock();
186
187         if (err == 0) {
188                 if (fh.fh_size > NFS_FHSIZE)
189                         err = -EINVAL;
190                 else {
191                         memset(res,0, NFS_FHSIZE);
192                         memcpy(res, &fh.fh_base, fh.fh_size);
193                 }
194         }
195
196         return err;
197 }
198
199 #ifdef CONFIG_NFSD
200 #define handle_sys_nfsservctl sys_nfsservctl
201 #endif
202
203 static struct {
204         int argsize, respsize;
205 }  sizes[] = {
206         /* NFSCTL_SVC        */ { sizeof(struct nfsctl_svc), 0 },
207         /* NFSCTL_ADDCLIENT  */ { sizeof(struct nfsctl_client), 0},
208         /* NFSCTL_DELCLIENT  */ { sizeof(struct nfsctl_client), 0},
209         /* NFSCTL_EXPORT     */ { sizeof(struct nfsctl_export), 0},
210         /* NFSCTL_UNEXPORT   */ { sizeof(struct nfsctl_export), 0},
211         /* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
212         /* NFSCTL_GETFH      */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
213         /* NFSCTL_GETFD      */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
214         /* NFSCTL_GETFS      */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
215 };
216 #define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
217
218 long
219 asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
220 {
221         struct nfsctl_arg *     argp = opaque_argp;
222         union nfsctl_res *      resp = opaque_resp;
223         struct nfsctl_arg *     arg = NULL;
224         union nfsctl_res *      res = NULL;
225         int                     err;
226         int                     argsize, respsize;
227
228         MOD_INC_USE_COUNT;
229         lock_kernel ();
230         if (!initialized)
231                 nfsd_init();
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         MOD_DEC_USE_COUNT;
305         return err;
306 }
307
308 #ifdef MODULE
309 /* New-style module support since 2.1.18 */
310 EXPORT_NO_SYMBOLS;
311 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
312
313 struct nfsd_linkage nfsd_linkage_s = {
314         do_nfsservctl: handle_sys_nfsservctl,
315 };
316
317 /*
318  * Initialize the module
319  */
320 int
321 init_module(void)
322 {
323         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
324         nfsd_linkage = &nfsd_linkage_s;
325         return 0;
326 }
327
328 /*
329  * Clean up the mess before unloading the module
330  */
331 void
332 cleanup_module(void)
333 {
334         nfsd_linkage = NULL;
335         nfsd_export_shutdown();
336         nfsd_cache_shutdown();
337         remove_proc_entry("fs/nfs/exports", NULL);
338         remove_proc_entry("fs/nfs", NULL);
339         nfsd_stat_shutdown();
340         nfsd_lockd_shutdown();
341 }
342 #endif