diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/Makefile | 12 | ||||
-rw-r--r-- | fs/nfsd/auth.c | 63 | ||||
-rw-r--r-- | fs/nfsd/export.c | 1200 | ||||
-rw-r--r-- | fs/nfsd/lockd.c | 79 | ||||
-rw-r--r-- | fs/nfsd/nfs3proc.c | 702 | ||||
-rw-r--r-- | fs/nfsd/nfs3xdr.c | 1092 | ||||
-rw-r--r-- | fs/nfsd/nfs4acl.c | 954 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 547 | ||||
-rw-r--r-- | fs/nfsd/nfs4idmap.c | 588 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 984 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 3320 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 2536 | ||||
-rw-r--r-- | fs/nfsd/nfscache.c | 328 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 438 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 532 | ||||
-rw-r--r-- | fs/nfsd/nfsproc.c | 605 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 385 | ||||
-rw-r--r-- | fs/nfsd/nfsxdr.c | 511 | ||||
-rw-r--r-- | fs/nfsd/stats.c | 101 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 1859 |
20 files changed, 16836 insertions, 0 deletions
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile new file mode 100644 index 000000000000..b8680a247f8b --- /dev/null +++ b/fs/nfsd/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for the Linux nfs server | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_NFSD) += nfsd.o | ||
6 | |||
7 | nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ | ||
8 | export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o | ||
9 | nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o | ||
10 | nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ | ||
11 | nfs4acl.o nfs4callback.o | ||
12 | nfsd-objs := $(nfsd-y) | ||
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c new file mode 100644 index 000000000000..cfe9ce881613 --- /dev/null +++ b/fs/nfsd/auth.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/auth.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | ||
5 | */ | ||
6 | |||
7 | #include <linux/types.h> | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/sunrpc/svc.h> | ||
10 | #include <linux/sunrpc/svcauth.h> | ||
11 | #include <linux/nfsd/nfsd.h> | ||
12 | |||
13 | #define CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE)) | ||
14 | |||
15 | int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) | ||
16 | { | ||
17 | struct svc_cred *cred = &rqstp->rq_cred; | ||
18 | int i; | ||
19 | int ret; | ||
20 | |||
21 | if (exp->ex_flags & NFSEXP_ALLSQUASH) { | ||
22 | cred->cr_uid = exp->ex_anon_uid; | ||
23 | cred->cr_gid = exp->ex_anon_gid; | ||
24 | put_group_info(cred->cr_group_info); | ||
25 | cred->cr_group_info = groups_alloc(0); | ||
26 | } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) { | ||
27 | struct group_info *gi; | ||
28 | if (!cred->cr_uid) | ||
29 | cred->cr_uid = exp->ex_anon_uid; | ||
30 | if (!cred->cr_gid) | ||
31 | cred->cr_gid = exp->ex_anon_gid; | ||
32 | gi = groups_alloc(cred->cr_group_info->ngroups); | ||
33 | if (gi) | ||
34 | for (i = 0; i < cred->cr_group_info->ngroups; i++) { | ||
35 | if (!GROUP_AT(cred->cr_group_info, i)) | ||
36 | GROUP_AT(gi, i) = exp->ex_anon_gid; | ||
37 | else | ||
38 | GROUP_AT(gi, i) = GROUP_AT(cred->cr_group_info, i); | ||
39 | } | ||
40 | put_group_info(cred->cr_group_info); | ||
41 | cred->cr_group_info = gi; | ||
42 | } | ||
43 | |||
44 | if (cred->cr_uid != (uid_t) -1) | ||
45 | current->fsuid = cred->cr_uid; | ||
46 | else | ||
47 | current->fsuid = exp->ex_anon_uid; | ||
48 | if (cred->cr_gid != (gid_t) -1) | ||
49 | current->fsgid = cred->cr_gid; | ||
50 | else | ||
51 | current->fsgid = exp->ex_anon_gid; | ||
52 | |||
53 | if (!cred->cr_group_info) | ||
54 | return -ENOMEM; | ||
55 | ret = set_current_groups(cred->cr_group_info); | ||
56 | if ((cred->cr_uid)) { | ||
57 | cap_t(current->cap_effective) &= ~CAP_NFSD_MASK; | ||
58 | } else { | ||
59 | cap_t(current->cap_effective) |= (CAP_NFSD_MASK & | ||
60 | current->cap_permitted); | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c new file mode 100644 index 000000000000..9a11aa39e2e4 --- /dev/null +++ b/fs/nfsd/export.c | |||
@@ -0,0 +1,1200 @@ | |||
1 | #define MSNFS /* HACK HACK */ | ||
2 | /* | ||
3 | * linux/fs/nfsd/export.c | ||
4 | * | ||
5 | * NFS exporting and validation. | ||
6 | * | ||
7 | * We maintain a list of clients, each of which has a list of | ||
8 | * exports. To export an fs to a given client, you first have | ||
9 | * to create the client entry with NFSCTL_ADDCLIENT, which | ||
10 | * creates a client control block and adds it to the hash | ||
11 | * table. Then, you call NFSCTL_EXPORT for each fs. | ||
12 | * | ||
13 | * | ||
14 | * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de> | ||
15 | */ | ||
16 | |||
17 | #include <linux/unistd.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/stat.h> | ||
21 | #include <linux/in.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/syscalls.h> | ||
24 | #include <linux/rwsem.h> | ||
25 | #include <linux/dcache.h> | ||
26 | #include <linux/namei.h> | ||
27 | #include <linux/mount.h> | ||
28 | #include <linux/hash.h> | ||
29 | |||
30 | #include <linux/sunrpc/svc.h> | ||
31 | #include <linux/nfsd/nfsd.h> | ||
32 | #include <linux/nfsd/nfsfh.h> | ||
33 | #include <linux/nfsd/syscall.h> | ||
34 | #include <linux/lockd/bind.h> | ||
35 | |||
36 | #define NFSDDBG_FACILITY NFSDDBG_EXPORT | ||
37 | #define NFSD_PARANOIA 1 | ||
38 | |||
39 | typedef struct auth_domain svc_client; | ||
40 | typedef struct svc_export svc_export; | ||
41 | |||
42 | static void exp_do_unexport(svc_export *unexp); | ||
43 | static int exp_verify_string(char *cp, int max); | ||
44 | |||
45 | /* | ||
46 | * We have two caches. | ||
47 | * One maps client+vfsmnt+dentry to export options - the export map | ||
48 | * The other maps client+filehandle-fragment to export options. - the expkey map | ||
49 | * | ||
50 | * The export options are actually stored in the first map, and the | ||
51 | * second map contains a reference to the entry in the first map. | ||
52 | */ | ||
53 | |||
54 | #define EXPKEY_HASHBITS 8 | ||
55 | #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) | ||
56 | #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) | ||
57 | static struct cache_head *expkey_table[EXPKEY_HASHMAX]; | ||
58 | |||
59 | static inline int svc_expkey_hash(struct svc_expkey *item) | ||
60 | { | ||
61 | int hash = item->ek_fsidtype; | ||
62 | char * cp = (char*)item->ek_fsid; | ||
63 | int len = key_len(item->ek_fsidtype); | ||
64 | |||
65 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); | ||
66 | hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); | ||
67 | return hash & EXPKEY_HASHMASK; | ||
68 | } | ||
69 | |||
70 | void expkey_put(struct cache_head *item, struct cache_detail *cd) | ||
71 | { | ||
72 | if (cache_put(item, cd)) { | ||
73 | struct svc_expkey *key = container_of(item, struct svc_expkey, h); | ||
74 | if (test_bit(CACHE_VALID, &item->flags) && | ||
75 | !test_bit(CACHE_NEGATIVE, &item->flags)) | ||
76 | exp_put(key->ek_export); | ||
77 | auth_domain_put(key->ek_client); | ||
78 | kfree(key); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void expkey_request(struct cache_detail *cd, | ||
83 | struct cache_head *h, | ||
84 | char **bpp, int *blen) | ||
85 | { | ||
86 | /* client fsidtype \xfsid */ | ||
87 | struct svc_expkey *ek = container_of(h, struct svc_expkey, h); | ||
88 | char type[5]; | ||
89 | |||
90 | qword_add(bpp, blen, ek->ek_client->name); | ||
91 | snprintf(type, 5, "%d", ek->ek_fsidtype); | ||
92 | qword_add(bpp, blen, type); | ||
93 | qword_addhex(bpp, blen, (char*)ek->ek_fsid, key_len(ek->ek_fsidtype)); | ||
94 | (*bpp)[-1] = '\n'; | ||
95 | } | ||
96 | |||
97 | static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *, int); | ||
98 | static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | ||
99 | { | ||
100 | /* client fsidtype fsid [path] */ | ||
101 | char *buf; | ||
102 | int len; | ||
103 | struct auth_domain *dom = NULL; | ||
104 | int err; | ||
105 | int fsidtype; | ||
106 | char *ep; | ||
107 | struct svc_expkey key; | ||
108 | |||
109 | if (mesg[mlen-1] != '\n') | ||
110 | return -EINVAL; | ||
111 | mesg[mlen-1] = 0; | ||
112 | |||
113 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
114 | err = -ENOMEM; | ||
115 | if (!buf) goto out; | ||
116 | |||
117 | err = -EINVAL; | ||
118 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) | ||
119 | goto out; | ||
120 | |||
121 | err = -ENOENT; | ||
122 | dom = auth_domain_find(buf); | ||
123 | if (!dom) | ||
124 | goto out; | ||
125 | dprintk("found domain %s\n", buf); | ||
126 | |||
127 | err = -EINVAL; | ||
128 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) | ||
129 | goto out; | ||
130 | fsidtype = simple_strtoul(buf, &ep, 10); | ||
131 | if (*ep) | ||
132 | goto out; | ||
133 | dprintk("found fsidtype %d\n", fsidtype); | ||
134 | if (fsidtype > 2) | ||
135 | goto out; | ||
136 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) | ||
137 | goto out; | ||
138 | dprintk("found fsid length %d\n", len); | ||
139 | if (len != key_len(fsidtype)) | ||
140 | goto out; | ||
141 | |||
142 | /* OK, we seem to have a valid key */ | ||
143 | key.h.flags = 0; | ||
144 | key.h.expiry_time = get_expiry(&mesg); | ||
145 | if (key.h.expiry_time == 0) | ||
146 | goto out; | ||
147 | |||
148 | key.ek_client = dom; | ||
149 | key.ek_fsidtype = fsidtype; | ||
150 | memcpy(key.ek_fsid, buf, len); | ||
151 | |||
152 | /* now we want a pathname, or empty meaning NEGATIVE */ | ||
153 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) | ||
154 | goto out; | ||
155 | dprintk("Path seems to be <%s>\n", buf); | ||
156 | err = 0; | ||
157 | if (len == 0) { | ||
158 | struct svc_expkey *ek; | ||
159 | set_bit(CACHE_NEGATIVE, &key.h.flags); | ||
160 | ek = svc_expkey_lookup(&key, 1); | ||
161 | if (ek) | ||
162 | expkey_put(&ek->h, &svc_expkey_cache); | ||
163 | } else { | ||
164 | struct nameidata nd; | ||
165 | struct svc_expkey *ek; | ||
166 | struct svc_export *exp; | ||
167 | err = path_lookup(buf, 0, &nd); | ||
168 | if (err) | ||
169 | goto out; | ||
170 | |||
171 | dprintk("Found the path %s\n", buf); | ||
172 | exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); | ||
173 | |||
174 | err = -ENOENT; | ||
175 | if (!exp) | ||
176 | goto out_nd; | ||
177 | key.ek_export = exp; | ||
178 | dprintk("And found export\n"); | ||
179 | |||
180 | ek = svc_expkey_lookup(&key, 1); | ||
181 | if (ek) | ||
182 | expkey_put(&ek->h, &svc_expkey_cache); | ||
183 | exp_put(exp); | ||
184 | err = 0; | ||
185 | out_nd: | ||
186 | path_release(&nd); | ||
187 | } | ||
188 | cache_flush(); | ||
189 | out: | ||
190 | if (dom) | ||
191 | auth_domain_put(dom); | ||
192 | if (buf) | ||
193 | kfree(buf); | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | static int expkey_show(struct seq_file *m, | ||
198 | struct cache_detail *cd, | ||
199 | struct cache_head *h) | ||
200 | { | ||
201 | struct svc_expkey *ek ; | ||
202 | |||
203 | if (h ==NULL) { | ||
204 | seq_puts(m, "#domain fsidtype fsid [path]\n"); | ||
205 | return 0; | ||
206 | } | ||
207 | ek = container_of(h, struct svc_expkey, h); | ||
208 | seq_printf(m, "%s %d 0x%08x", ek->ek_client->name, | ||
209 | ek->ek_fsidtype, ek->ek_fsid[0]); | ||
210 | if (ek->ek_fsidtype != 1) | ||
211 | seq_printf(m, "%08x", ek->ek_fsid[1]); | ||
212 | if (ek->ek_fsidtype == 2) | ||
213 | seq_printf(m, "%08x", ek->ek_fsid[2]); | ||
214 | if (test_bit(CACHE_VALID, &h->flags) && | ||
215 | !test_bit(CACHE_NEGATIVE, &h->flags)) { | ||
216 | seq_printf(m, " "); | ||
217 | seq_path(m, ek->ek_export->ex_mnt, ek->ek_export->ex_dentry, "\\ \t\n"); | ||
218 | } | ||
219 | seq_printf(m, "\n"); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | struct cache_detail svc_expkey_cache = { | ||
224 | .hash_size = EXPKEY_HASHMAX, | ||
225 | .hash_table = expkey_table, | ||
226 | .name = "nfsd.fh", | ||
227 | .cache_put = expkey_put, | ||
228 | .cache_request = expkey_request, | ||
229 | .cache_parse = expkey_parse, | ||
230 | .cache_show = expkey_show, | ||
231 | }; | ||
232 | |||
233 | static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b) | ||
234 | { | ||
235 | if (a->ek_fsidtype != b->ek_fsidtype || | ||
236 | a->ek_client != b->ek_client || | ||
237 | memcmp(a->ek_fsid, b->ek_fsid, key_len(a->ek_fsidtype)) != 0) | ||
238 | return 0; | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *item) | ||
243 | { | ||
244 | cache_get(&item->ek_client->h); | ||
245 | new->ek_client = item->ek_client; | ||
246 | new->ek_fsidtype = item->ek_fsidtype; | ||
247 | new->ek_fsid[0] = item->ek_fsid[0]; | ||
248 | new->ek_fsid[1] = item->ek_fsid[1]; | ||
249 | new->ek_fsid[2] = item->ek_fsid[2]; | ||
250 | } | ||
251 | |||
252 | static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item) | ||
253 | { | ||
254 | cache_get(&item->ek_export->h); | ||
255 | new->ek_export = item->ek_export; | ||
256 | } | ||
257 | |||
258 | static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */ | ||
259 | |||
260 | #define EXPORT_HASHBITS 8 | ||
261 | #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) | ||
262 | #define EXPORT_HASHMASK (EXPORT_HASHMAX -1) | ||
263 | |||
264 | static struct cache_head *export_table[EXPORT_HASHMAX]; | ||
265 | |||
266 | static inline int svc_export_hash(struct svc_export *item) | ||
267 | { | ||
268 | int rv; | ||
269 | |||
270 | rv = hash_ptr(item->ex_client, EXPORT_HASHBITS); | ||
271 | rv ^= hash_ptr(item->ex_dentry, EXPORT_HASHBITS); | ||
272 | rv ^= hash_ptr(item->ex_mnt, EXPORT_HASHBITS); | ||
273 | return rv; | ||
274 | } | ||
275 | |||
276 | void svc_export_put(struct cache_head *item, struct cache_detail *cd) | ||
277 | { | ||
278 | if (cache_put(item, cd)) { | ||
279 | struct svc_export *exp = container_of(item, struct svc_export, h); | ||
280 | dput(exp->ex_dentry); | ||
281 | mntput(exp->ex_mnt); | ||
282 | auth_domain_put(exp->ex_client); | ||
283 | kfree(exp); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | static void svc_export_request(struct cache_detail *cd, | ||
288 | struct cache_head *h, | ||
289 | char **bpp, int *blen) | ||
290 | { | ||
291 | /* client path */ | ||
292 | struct svc_export *exp = container_of(h, struct svc_export, h); | ||
293 | char *pth; | ||
294 | |||
295 | qword_add(bpp, blen, exp->ex_client->name); | ||
296 | pth = d_path(exp->ex_dentry, exp->ex_mnt, *bpp, *blen); | ||
297 | if (IS_ERR(pth)) { | ||
298 | /* is this correct? */ | ||
299 | (*bpp)[0] = '\n'; | ||
300 | return; | ||
301 | } | ||
302 | qword_add(bpp, blen, pth); | ||
303 | (*bpp)[-1] = '\n'; | ||
304 | } | ||
305 | |||
306 | static struct svc_export *svc_export_lookup(struct svc_export *, int); | ||
307 | |||
308 | static int check_export(struct inode *inode, int flags) | ||
309 | { | ||
310 | |||
311 | /* We currently export only dirs and regular files. | ||
312 | * This is what umountd does. | ||
313 | */ | ||
314 | if (!S_ISDIR(inode->i_mode) && | ||
315 | !S_ISREG(inode->i_mode)) | ||
316 | return -ENOTDIR; | ||
317 | |||
318 | /* There are two requirements on a filesystem to be exportable. | ||
319 | * 1: We must be able to identify the filesystem from a number. | ||
320 | * either a device number (so FS_REQUIRES_DEV needed) | ||
321 | * or an FSID number (so NFSEXP_FSID needed). | ||
322 | * 2: We must be able to find an inode from a filehandle. | ||
323 | * This means that s_export_op must be set. | ||
324 | */ | ||
325 | if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) && | ||
326 | !(flags & NFSEXP_FSID)) { | ||
327 | dprintk("exp_export: export of non-dev fs without fsid"); | ||
328 | return -EINVAL; | ||
329 | } | ||
330 | if (!inode->i_sb->s_export_op) { | ||
331 | dprintk("exp_export: export of invalid fs type.\n"); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | /* Ok, we can export it */; | ||
336 | if (!inode->i_sb->s_export_op->find_exported_dentry) | ||
337 | inode->i_sb->s_export_op->find_exported_dentry = | ||
338 | find_exported_dentry; | ||
339 | return 0; | ||
340 | |||
341 | } | ||
342 | |||
343 | static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) | ||
344 | { | ||
345 | /* client path expiry [flags anonuid anongid fsid] */ | ||
346 | char *buf; | ||
347 | int len; | ||
348 | int err; | ||
349 | struct auth_domain *dom = NULL; | ||
350 | struct nameidata nd; | ||
351 | struct svc_export exp, *expp; | ||
352 | int an_int; | ||
353 | |||
354 | nd.dentry = NULL; | ||
355 | |||
356 | if (mesg[mlen-1] != '\n') | ||
357 | return -EINVAL; | ||
358 | mesg[mlen-1] = 0; | ||
359 | |||
360 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
361 | err = -ENOMEM; | ||
362 | if (!buf) goto out; | ||
363 | |||
364 | /* client */ | ||
365 | len = qword_get(&mesg, buf, PAGE_SIZE); | ||
366 | err = -EINVAL; | ||
367 | if (len <= 0) goto out; | ||
368 | |||
369 | err = -ENOENT; | ||
370 | dom = auth_domain_find(buf); | ||
371 | if (!dom) | ||
372 | goto out; | ||
373 | |||
374 | /* path */ | ||
375 | err = -EINVAL; | ||
376 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) | ||
377 | goto out; | ||
378 | err = path_lookup(buf, 0, &nd); | ||
379 | if (err) goto out; | ||
380 | |||
381 | exp.h.flags = 0; | ||
382 | exp.ex_client = dom; | ||
383 | exp.ex_mnt = nd.mnt; | ||
384 | exp.ex_dentry = nd.dentry; | ||
385 | |||
386 | /* expiry */ | ||
387 | err = -EINVAL; | ||
388 | exp.h.expiry_time = get_expiry(&mesg); | ||
389 | if (exp.h.expiry_time == 0) | ||
390 | goto out; | ||
391 | |||
392 | /* flags */ | ||
393 | err = get_int(&mesg, &an_int); | ||
394 | if (err == -ENOENT) | ||
395 | set_bit(CACHE_NEGATIVE, &exp.h.flags); | ||
396 | else { | ||
397 | if (err || an_int < 0) goto out; | ||
398 | exp.ex_flags= an_int; | ||
399 | |||
400 | /* anon uid */ | ||
401 | err = get_int(&mesg, &an_int); | ||
402 | if (err) goto out; | ||
403 | exp.ex_anon_uid= an_int; | ||
404 | |||
405 | /* anon gid */ | ||
406 | err = get_int(&mesg, &an_int); | ||
407 | if (err) goto out; | ||
408 | exp.ex_anon_gid= an_int; | ||
409 | |||
410 | /* fsid */ | ||
411 | err = get_int(&mesg, &an_int); | ||
412 | if (err) goto out; | ||
413 | exp.ex_fsid = an_int; | ||
414 | |||
415 | err = check_export(nd.dentry->d_inode, exp.ex_flags); | ||
416 | if (err) goto out; | ||
417 | } | ||
418 | |||
419 | expp = svc_export_lookup(&exp, 1); | ||
420 | if (expp) | ||
421 | exp_put(expp); | ||
422 | err = 0; | ||
423 | cache_flush(); | ||
424 | out: | ||
425 | if (nd.dentry) | ||
426 | path_release(&nd); | ||
427 | if (dom) | ||
428 | auth_domain_put(dom); | ||
429 | if (buf) | ||
430 | kfree(buf); | ||
431 | return err; | ||
432 | } | ||
433 | |||
434 | static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong); | ||
435 | |||
436 | static int svc_export_show(struct seq_file *m, | ||
437 | struct cache_detail *cd, | ||
438 | struct cache_head *h) | ||
439 | { | ||
440 | struct svc_export *exp ; | ||
441 | |||
442 | if (h ==NULL) { | ||
443 | seq_puts(m, "#path domain(flags)\n"); | ||
444 | return 0; | ||
445 | } | ||
446 | exp = container_of(h, struct svc_export, h); | ||
447 | seq_path(m, exp->ex_mnt, exp->ex_dentry, " \t\n\\"); | ||
448 | seq_putc(m, '\t'); | ||
449 | seq_escape(m, exp->ex_client->name, " \t\n\\"); | ||
450 | seq_putc(m, '('); | ||
451 | if (test_bit(CACHE_VALID, &h->flags) && | ||
452 | !test_bit(CACHE_NEGATIVE, &h->flags)) | ||
453 | exp_flags(m, exp->ex_flags, exp->ex_fsid, | ||
454 | exp->ex_anon_uid, exp->ex_anon_gid); | ||
455 | seq_puts(m, ")\n"); | ||
456 | return 0; | ||
457 | } | ||
458 | struct cache_detail svc_export_cache = { | ||
459 | .hash_size = EXPORT_HASHMAX, | ||
460 | .hash_table = export_table, | ||
461 | .name = "nfsd.export", | ||
462 | .cache_put = svc_export_put, | ||
463 | .cache_request = svc_export_request, | ||
464 | .cache_parse = svc_export_parse, | ||
465 | .cache_show = svc_export_show, | ||
466 | }; | ||
467 | |||
468 | static inline int svc_export_match(struct svc_export *a, struct svc_export *b) | ||
469 | { | ||
470 | return a->ex_client == b->ex_client && | ||
471 | a->ex_dentry == b->ex_dentry && | ||
472 | a->ex_mnt == b->ex_mnt; | ||
473 | } | ||
474 | static inline void svc_export_init(struct svc_export *new, struct svc_export *item) | ||
475 | { | ||
476 | cache_get(&item->ex_client->h); | ||
477 | new->ex_client = item->ex_client; | ||
478 | new->ex_dentry = dget(item->ex_dentry); | ||
479 | new->ex_mnt = mntget(item->ex_mnt); | ||
480 | } | ||
481 | |||
482 | static inline void svc_export_update(struct svc_export *new, struct svc_export *item) | ||
483 | { | ||
484 | new->ex_flags = item->ex_flags; | ||
485 | new->ex_anon_uid = item->ex_anon_uid; | ||
486 | new->ex_anon_gid = item->ex_anon_gid; | ||
487 | new->ex_fsid = item->ex_fsid; | ||
488 | } | ||
489 | |||
490 | static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */ | ||
491 | |||
492 | |||
493 | struct svc_expkey * | ||
494 | exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) | ||
495 | { | ||
496 | struct svc_expkey key, *ek; | ||
497 | int err; | ||
498 | |||
499 | if (!clp) | ||
500 | return NULL; | ||
501 | |||
502 | key.ek_client = clp; | ||
503 | key.ek_fsidtype = fsid_type; | ||
504 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); | ||
505 | |||
506 | ek = svc_expkey_lookup(&key, 0); | ||
507 | if (ek != NULL) | ||
508 | if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) | ||
509 | ek = ERR_PTR(err); | ||
510 | return ek; | ||
511 | } | ||
512 | |||
513 | static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, | ||
514 | struct svc_export *exp) | ||
515 | { | ||
516 | struct svc_expkey key, *ek; | ||
517 | |||
518 | key.ek_client = clp; | ||
519 | key.ek_fsidtype = fsid_type; | ||
520 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); | ||
521 | key.ek_export = exp; | ||
522 | key.h.expiry_time = NEVER; | ||
523 | key.h.flags = 0; | ||
524 | |||
525 | ek = svc_expkey_lookup(&key, 1); | ||
526 | if (ek) { | ||
527 | expkey_put(&ek->h, &svc_expkey_cache); | ||
528 | return 0; | ||
529 | } | ||
530 | return -ENOMEM; | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Find the client's export entry matching xdev/xino. | ||
535 | */ | ||
536 | static inline struct svc_expkey * | ||
537 | exp_get_key(svc_client *clp, dev_t dev, ino_t ino) | ||
538 | { | ||
539 | u32 fsidv[3]; | ||
540 | |||
541 | if (old_valid_dev(dev)) { | ||
542 | mk_fsid_v0(fsidv, dev, ino); | ||
543 | return exp_find_key(clp, 0, fsidv, NULL); | ||
544 | } | ||
545 | mk_fsid_v3(fsidv, dev, ino); | ||
546 | return exp_find_key(clp, 3, fsidv, NULL); | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * Find the client's export entry matching fsid | ||
551 | */ | ||
552 | static inline struct svc_expkey * | ||
553 | exp_get_fsid_key(svc_client *clp, int fsid) | ||
554 | { | ||
555 | u32 fsidv[2]; | ||
556 | |||
557 | mk_fsid_v1(fsidv, fsid); | ||
558 | |||
559 | return exp_find_key(clp, 1, fsidv, NULL); | ||
560 | } | ||
561 | |||
562 | svc_export * | ||
563 | exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, | ||
564 | struct cache_req *reqp) | ||
565 | { | ||
566 | struct svc_export *exp, key; | ||
567 | |||
568 | if (!clp) | ||
569 | return NULL; | ||
570 | |||
571 | key.ex_client = clp; | ||
572 | key.ex_mnt = mnt; | ||
573 | key.ex_dentry = dentry; | ||
574 | |||
575 | exp = svc_export_lookup(&key, 0); | ||
576 | if (exp != NULL) | ||
577 | switch (cache_check(&svc_export_cache, &exp->h, reqp)) { | ||
578 | case 0: break; | ||
579 | case -EAGAIN: | ||
580 | exp = ERR_PTR(-EAGAIN); | ||
581 | break; | ||
582 | default: | ||
583 | exp = NULL; | ||
584 | } | ||
585 | |||
586 | return exp; | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | * Find the export entry for a given dentry. | ||
591 | */ | ||
592 | struct svc_export * | ||
593 | exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, | ||
594 | struct cache_req *reqp) | ||
595 | { | ||
596 | svc_export *exp; | ||
597 | |||
598 | dget(dentry); | ||
599 | exp = exp_get_by_name(clp, mnt, dentry, reqp); | ||
600 | |||
601 | while (exp == NULL && !IS_ROOT(dentry)) { | ||
602 | struct dentry *parent; | ||
603 | |||
604 | parent = dget_parent(dentry); | ||
605 | dput(dentry); | ||
606 | dentry = parent; | ||
607 | exp = exp_get_by_name(clp, mnt, dentry, reqp); | ||
608 | } | ||
609 | dput(dentry); | ||
610 | return exp; | ||
611 | } | ||
612 | |||
613 | /* | ||
614 | * Hashtable locking. Write locks are placed only by user processes | ||
615 | * wanting to modify export information. | ||
616 | * Write locking only done in this file. Read locking | ||
617 | * needed externally. | ||
618 | */ | ||
619 | |||
620 | static DECLARE_RWSEM(hash_sem); | ||
621 | |||
622 | void | ||
623 | exp_readlock(void) | ||
624 | { | ||
625 | down_read(&hash_sem); | ||
626 | } | ||
627 | |||
628 | static inline void | ||
629 | exp_writelock(void) | ||
630 | { | ||
631 | down_write(&hash_sem); | ||
632 | } | ||
633 | |||
634 | void | ||
635 | exp_readunlock(void) | ||
636 | { | ||
637 | up_read(&hash_sem); | ||
638 | } | ||
639 | |||
640 | static inline void | ||
641 | exp_writeunlock(void) | ||
642 | { | ||
643 | up_write(&hash_sem); | ||
644 | } | ||
645 | |||
646 | static void exp_fsid_unhash(struct svc_export *exp) | ||
647 | { | ||
648 | struct svc_expkey *ek; | ||
649 | |||
650 | if ((exp->ex_flags & NFSEXP_FSID) == 0) | ||
651 | return; | ||
652 | |||
653 | ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); | ||
654 | if (ek && !IS_ERR(ek)) { | ||
655 | ek->h.expiry_time = get_seconds()-1; | ||
656 | expkey_put(&ek->h, &svc_expkey_cache); | ||
657 | } | ||
658 | svc_expkey_cache.nextcheck = get_seconds(); | ||
659 | } | ||
660 | |||
661 | static int exp_fsid_hash(svc_client *clp, struct svc_export *exp) | ||
662 | { | ||
663 | u32 fsid[2]; | ||
664 | |||
665 | if ((exp->ex_flags & NFSEXP_FSID) == 0) | ||
666 | return 0; | ||
667 | |||
668 | mk_fsid_v1(fsid, exp->ex_fsid); | ||
669 | return exp_set_key(clp, 1, fsid, exp); | ||
670 | } | ||
671 | |||
672 | static int exp_hash(struct auth_domain *clp, struct svc_export *exp) | ||
673 | { | ||
674 | u32 fsid[2]; | ||
675 | struct inode *inode = exp->ex_dentry->d_inode; | ||
676 | dev_t dev = inode->i_sb->s_dev; | ||
677 | |||
678 | if (old_valid_dev(dev)) { | ||
679 | mk_fsid_v0(fsid, dev, inode->i_ino); | ||
680 | return exp_set_key(clp, 0, fsid, exp); | ||
681 | } | ||
682 | mk_fsid_v3(fsid, dev, inode->i_ino); | ||
683 | return exp_set_key(clp, 3, fsid, exp); | ||
684 | } | ||
685 | |||
686 | static void exp_unhash(struct svc_export *exp) | ||
687 | { | ||
688 | struct svc_expkey *ek; | ||
689 | struct inode *inode = exp->ex_dentry->d_inode; | ||
690 | |||
691 | ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); | ||
692 | if (ek && !IS_ERR(ek)) { | ||
693 | ek->h.expiry_time = get_seconds()-1; | ||
694 | expkey_put(&ek->h, &svc_expkey_cache); | ||
695 | } | ||
696 | svc_expkey_cache.nextcheck = get_seconds(); | ||
697 | } | ||
698 | |||
699 | /* | ||
700 | * Export a file system. | ||
701 | */ | ||
702 | int | ||
703 | exp_export(struct nfsctl_export *nxp) | ||
704 | { | ||
705 | svc_client *clp; | ||
706 | struct svc_export *exp = NULL; | ||
707 | struct svc_export new; | ||
708 | struct svc_expkey *fsid_key = NULL; | ||
709 | struct nameidata nd; | ||
710 | int err; | ||
711 | |||
712 | /* Consistency check */ | ||
713 | err = -EINVAL; | ||
714 | if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || | ||
715 | !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) | ||
716 | goto out; | ||
717 | |||
718 | dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", | ||
719 | nxp->ex_client, nxp->ex_path, | ||
720 | (unsigned)nxp->ex_dev, (long)nxp->ex_ino, | ||
721 | nxp->ex_flags); | ||
722 | |||
723 | /* Try to lock the export table for update */ | ||
724 | exp_writelock(); | ||
725 | |||
726 | /* Look up client info */ | ||
727 | if (!(clp = auth_domain_find(nxp->ex_client))) | ||
728 | goto out_unlock; | ||
729 | |||
730 | |||
731 | /* Look up the dentry */ | ||
732 | err = path_lookup(nxp->ex_path, 0, &nd); | ||
733 | if (err) | ||
734 | goto out_unlock; | ||
735 | err = -EINVAL; | ||
736 | |||
737 | exp = exp_get_by_name(clp, nd.mnt, nd.dentry, NULL); | ||
738 | |||
739 | /* must make sure there won't be an ex_fsid clash */ | ||
740 | if ((nxp->ex_flags & NFSEXP_FSID) && | ||
741 | (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && | ||
742 | !IS_ERR(fsid_key) && | ||
743 | fsid_key->ek_export && | ||
744 | fsid_key->ek_export != exp) | ||
745 | goto finish; | ||
746 | |||
747 | if (exp) { | ||
748 | /* just a flags/id/fsid update */ | ||
749 | |||
750 | exp_fsid_unhash(exp); | ||
751 | exp->ex_flags = nxp->ex_flags; | ||
752 | exp->ex_anon_uid = nxp->ex_anon_uid; | ||
753 | exp->ex_anon_gid = nxp->ex_anon_gid; | ||
754 | exp->ex_fsid = nxp->ex_dev; | ||
755 | |||
756 | err = exp_fsid_hash(clp, exp); | ||
757 | goto finish; | ||
758 | } | ||
759 | |||
760 | err = check_export(nd.dentry->d_inode, nxp->ex_flags); | ||
761 | if (err) goto finish; | ||
762 | |||
763 | err = -ENOMEM; | ||
764 | |||
765 | dprintk("nfsd: creating export entry %p for client %p\n", exp, clp); | ||
766 | |||
767 | new.h.expiry_time = NEVER; | ||
768 | new.h.flags = 0; | ||
769 | new.ex_client = clp; | ||
770 | new.ex_mnt = nd.mnt; | ||
771 | new.ex_dentry = nd.dentry; | ||
772 | new.ex_flags = nxp->ex_flags; | ||
773 | new.ex_anon_uid = nxp->ex_anon_uid; | ||
774 | new.ex_anon_gid = nxp->ex_anon_gid; | ||
775 | new.ex_fsid = nxp->ex_dev; | ||
776 | |||
777 | exp = svc_export_lookup(&new, 1); | ||
778 | |||
779 | if (exp == NULL) | ||
780 | goto finish; | ||
781 | |||
782 | err = 0; | ||
783 | |||
784 | if (exp_hash(clp, exp) || | ||
785 | exp_fsid_hash(clp, exp)) { | ||
786 | /* failed to create at least one index */ | ||
787 | exp_do_unexport(exp); | ||
788 | cache_flush(); | ||
789 | err = -ENOMEM; | ||
790 | } | ||
791 | |||
792 | finish: | ||
793 | if (exp) | ||
794 | exp_put(exp); | ||
795 | if (fsid_key && !IS_ERR(fsid_key)) | ||
796 | expkey_put(&fsid_key->h, &svc_expkey_cache); | ||
797 | if (clp) | ||
798 | auth_domain_put(clp); | ||
799 | path_release(&nd); | ||
800 | out_unlock: | ||
801 | exp_writeunlock(); | ||
802 | out: | ||
803 | return err; | ||
804 | } | ||
805 | |||
806 | /* | ||
807 | * Unexport a file system. The export entry has already | ||
808 | * been removed from the client's list of exported fs's. | ||
809 | */ | ||
810 | static void | ||
811 | exp_do_unexport(svc_export *unexp) | ||
812 | { | ||
813 | unexp->h.expiry_time = get_seconds()-1; | ||
814 | svc_export_cache.nextcheck = get_seconds(); | ||
815 | exp_unhash(unexp); | ||
816 | exp_fsid_unhash(unexp); | ||
817 | } | ||
818 | |||
819 | |||
820 | /* | ||
821 | * unexport syscall. | ||
822 | */ | ||
823 | int | ||
824 | exp_unexport(struct nfsctl_export *nxp) | ||
825 | { | ||
826 | struct auth_domain *dom; | ||
827 | svc_export *exp; | ||
828 | struct nameidata nd; | ||
829 | int err; | ||
830 | |||
831 | /* Consistency check */ | ||
832 | if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || | ||
833 | !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) | ||
834 | return -EINVAL; | ||
835 | |||
836 | exp_writelock(); | ||
837 | |||
838 | err = -EINVAL; | ||
839 | dom = auth_domain_find(nxp->ex_client); | ||
840 | if (!dom) { | ||
841 | dprintk("nfsd: unexport couldn't find %s\n", nxp->ex_client); | ||
842 | goto out_unlock; | ||
843 | } | ||
844 | |||
845 | err = path_lookup(nxp->ex_path, 0, &nd); | ||
846 | if (err) | ||
847 | goto out_domain; | ||
848 | |||
849 | err = -EINVAL; | ||
850 | exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); | ||
851 | path_release(&nd); | ||
852 | if (!exp) | ||
853 | goto out_domain; | ||
854 | |||
855 | exp_do_unexport(exp); | ||
856 | exp_put(exp); | ||
857 | err = 0; | ||
858 | |||
859 | out_domain: | ||
860 | auth_domain_put(dom); | ||
861 | cache_flush(); | ||
862 | out_unlock: | ||
863 | exp_writeunlock(); | ||
864 | return err; | ||
865 | } | ||
866 | |||
867 | /* | ||
868 | * Obtain the root fh on behalf of a client. | ||
869 | * This could be done in user space, but I feel that it adds some safety | ||
870 | * since its harder to fool a kernel module than a user space program. | ||
871 | */ | ||
872 | int | ||
873 | exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) | ||
874 | { | ||
875 | struct svc_export *exp; | ||
876 | struct nameidata nd; | ||
877 | struct inode *inode; | ||
878 | struct svc_fh fh; | ||
879 | int err; | ||
880 | |||
881 | err = -EPERM; | ||
882 | /* NB: we probably ought to check that it's NUL-terminated */ | ||
883 | if (path_lookup(path, 0, &nd)) { | ||
884 | printk("nfsd: exp_rootfh path not found %s", path); | ||
885 | return err; | ||
886 | } | ||
887 | inode = nd.dentry->d_inode; | ||
888 | |||
889 | dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n", | ||
890 | path, nd.dentry, clp->name, | ||
891 | inode->i_sb->s_id, inode->i_ino); | ||
892 | exp = exp_parent(clp, nd.mnt, nd.dentry, NULL); | ||
893 | if (!exp) { | ||
894 | dprintk("nfsd: exp_rootfh export not found.\n"); | ||
895 | goto out; | ||
896 | } | ||
897 | |||
898 | /* | ||
899 | * fh must be initialized before calling fh_compose | ||
900 | */ | ||
901 | fh_init(&fh, maxsize); | ||
902 | if (fh_compose(&fh, exp, nd.dentry, NULL)) | ||
903 | err = -EINVAL; | ||
904 | else | ||
905 | err = 0; | ||
906 | memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh)); | ||
907 | fh_put(&fh); | ||
908 | exp_put(exp); | ||
909 | out: | ||
910 | path_release(&nd); | ||
911 | return err; | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * Called when we need the filehandle for the root of the pseudofs, | ||
916 | * for a given NFSv4 client. The root is defined to be the | ||
917 | * export point with fsid==0 | ||
918 | */ | ||
919 | int | ||
920 | exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, | ||
921 | struct cache_req *creq) | ||
922 | { | ||
923 | struct svc_expkey *fsid_key; | ||
924 | int rv; | ||
925 | u32 fsidv[2]; | ||
926 | |||
927 | mk_fsid_v1(fsidv, 0); | ||
928 | |||
929 | fsid_key = exp_find_key(clp, 1, fsidv, creq); | ||
930 | if (IS_ERR(fsid_key) && PTR_ERR(fsid_key) == -EAGAIN) | ||
931 | return nfserr_dropit; | ||
932 | if (!fsid_key || IS_ERR(fsid_key)) | ||
933 | return nfserr_perm; | ||
934 | |||
935 | rv = fh_compose(fhp, fsid_key->ek_export, | ||
936 | fsid_key->ek_export->ex_dentry, NULL); | ||
937 | expkey_put(&fsid_key->h, &svc_expkey_cache); | ||
938 | return rv; | ||
939 | } | ||
940 | |||
941 | /* Iterator */ | ||
942 | |||
943 | static void *e_start(struct seq_file *m, loff_t *pos) | ||
944 | { | ||
945 | loff_t n = *pos; | ||
946 | unsigned hash, export; | ||
947 | struct cache_head *ch; | ||
948 | |||
949 | exp_readlock(); | ||
950 | read_lock(&svc_export_cache.hash_lock); | ||
951 | if (!n--) | ||
952 | return (void *)1; | ||
953 | hash = n >> 32; | ||
954 | export = n & ((1LL<<32) - 1); | ||
955 | |||
956 | |||
957 | for (ch=export_table[hash]; ch; ch=ch->next) | ||
958 | if (!export--) | ||
959 | return ch; | ||
960 | n &= ~((1LL<<32) - 1); | ||
961 | do { | ||
962 | hash++; | ||
963 | n += 1LL<<32; | ||
964 | } while(hash < EXPORT_HASHMAX && export_table[hash]==NULL); | ||
965 | if (hash >= EXPORT_HASHMAX) | ||
966 | return NULL; | ||
967 | *pos = n+1; | ||
968 | return export_table[hash]; | ||
969 | } | ||
970 | |||
971 | static void *e_next(struct seq_file *m, void *p, loff_t *pos) | ||
972 | { | ||
973 | struct cache_head *ch = p; | ||
974 | int hash = (*pos >> 32); | ||
975 | |||
976 | if (p == (void *)1) | ||
977 | hash = 0; | ||
978 | else if (ch->next == NULL) { | ||
979 | hash++; | ||
980 | *pos += 1LL<<32; | ||
981 | } else { | ||
982 | ++*pos; | ||
983 | return ch->next; | ||
984 | } | ||
985 | *pos &= ~((1LL<<32) - 1); | ||
986 | while (hash < EXPORT_HASHMAX && export_table[hash] == NULL) { | ||
987 | hash++; | ||
988 | *pos += 1LL<<32; | ||
989 | } | ||
990 | if (hash >= EXPORT_HASHMAX) | ||
991 | return NULL; | ||
992 | ++*pos; | ||
993 | return export_table[hash]; | ||
994 | } | ||
995 | |||
996 | static void e_stop(struct seq_file *m, void *p) | ||
997 | { | ||
998 | read_unlock(&svc_export_cache.hash_lock); | ||
999 | exp_readunlock(); | ||
1000 | } | ||
1001 | |||
1002 | static struct flags { | ||
1003 | int flag; | ||
1004 | char *name[2]; | ||
1005 | } expflags[] = { | ||
1006 | { NFSEXP_READONLY, {"ro", "rw"}}, | ||
1007 | { NFSEXP_INSECURE_PORT, {"insecure", ""}}, | ||
1008 | { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}}, | ||
1009 | { NFSEXP_ALLSQUASH, {"all_squash", ""}}, | ||
1010 | { NFSEXP_ASYNC, {"async", "sync"}}, | ||
1011 | { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}}, | ||
1012 | { NFSEXP_NOHIDE, {"nohide", ""}}, | ||
1013 | { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, | ||
1014 | { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, | ||
1015 | { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, | ||
1016 | #ifdef MSNFS | ||
1017 | { NFSEXP_MSNFS, {"msnfs", ""}}, | ||
1018 | #endif | ||
1019 | { 0, {"", ""}} | ||
1020 | }; | ||
1021 | |||
1022 | static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong) | ||
1023 | { | ||
1024 | int first = 0; | ||
1025 | struct flags *flg; | ||
1026 | |||
1027 | for (flg = expflags; flg->flag; flg++) { | ||
1028 | int state = (flg->flag & flag)?0:1; | ||
1029 | if (*flg->name[state]) | ||
1030 | seq_printf(m, "%s%s", first++?",":"", flg->name[state]); | ||
1031 | } | ||
1032 | if (flag & NFSEXP_FSID) | ||
1033 | seq_printf(m, "%sfsid=%d", first++?",":"", fsid); | ||
1034 | if (anonu != (uid_t)-2 && anonu != (0x10000-2)) | ||
1035 | seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); | ||
1036 | if (anong != (gid_t)-2 && anong != (0x10000-2)) | ||
1037 | seq_printf(m, "%sanongid=%d", first++?",":"", anong); | ||
1038 | } | ||
1039 | |||
1040 | static int e_show(struct seq_file *m, void *p) | ||
1041 | { | ||
1042 | struct cache_head *cp = p; | ||
1043 | struct svc_export *exp = container_of(cp, struct svc_export, h); | ||
1044 | svc_client *clp; | ||
1045 | |||
1046 | if (p == (void *)1) { | ||
1047 | seq_puts(m, "# Version 1.1\n"); | ||
1048 | seq_puts(m, "# Path Client(Flags) # IPs\n"); | ||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
1052 | clp = exp->ex_client; | ||
1053 | cache_get(&exp->h); | ||
1054 | if (cache_check(&svc_export_cache, &exp->h, NULL)) | ||
1055 | return 0; | ||
1056 | if (cache_put(&exp->h, &svc_export_cache)) BUG(); | ||
1057 | return svc_export_show(m, &svc_export_cache, cp); | ||
1058 | } | ||
1059 | |||
1060 | struct seq_operations nfs_exports_op = { | ||
1061 | .start = e_start, | ||
1062 | .next = e_next, | ||
1063 | .stop = e_stop, | ||
1064 | .show = e_show, | ||
1065 | }; | ||
1066 | |||
1067 | /* | ||
1068 | * Add or modify a client. | ||
1069 | * Change requests may involve the list of host addresses. The list of | ||
1070 | * exports and possibly existing uid maps are left untouched. | ||
1071 | */ | ||
1072 | int | ||
1073 | exp_addclient(struct nfsctl_client *ncp) | ||
1074 | { | ||
1075 | struct auth_domain *dom; | ||
1076 | int i, err; | ||
1077 | |||
1078 | /* First, consistency check. */ | ||
1079 | err = -EINVAL; | ||
1080 | if (! exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) | ||
1081 | goto out; | ||
1082 | if (ncp->cl_naddr > NFSCLNT_ADDRMAX) | ||
1083 | goto out; | ||
1084 | |||
1085 | /* Lock the hashtable */ | ||
1086 | exp_writelock(); | ||
1087 | |||
1088 | dom = unix_domain_find(ncp->cl_ident); | ||
1089 | |||
1090 | err = -ENOMEM; | ||
1091 | if (!dom) | ||
1092 | goto out_unlock; | ||
1093 | |||
1094 | /* Insert client into hashtable. */ | ||
1095 | for (i = 0; i < ncp->cl_naddr; i++) | ||
1096 | auth_unix_add_addr(ncp->cl_addrlist[i], dom); | ||
1097 | |||
1098 | auth_unix_forget_old(dom); | ||
1099 | auth_domain_put(dom); | ||
1100 | |||
1101 | err = 0; | ||
1102 | |||
1103 | out_unlock: | ||
1104 | exp_writeunlock(); | ||
1105 | out: | ||
1106 | return err; | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * Delete a client given an identifier. | ||
1111 | */ | ||
1112 | int | ||
1113 | exp_delclient(struct nfsctl_client *ncp) | ||
1114 | { | ||
1115 | int err; | ||
1116 | struct auth_domain *dom; | ||
1117 | |||
1118 | err = -EINVAL; | ||
1119 | if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) | ||
1120 | goto out; | ||
1121 | |||
1122 | /* Lock the hashtable */ | ||
1123 | exp_writelock(); | ||
1124 | |||
1125 | dom = auth_domain_find(ncp->cl_ident); | ||
1126 | /* just make sure that no addresses work | ||
1127 | * and that it will expire soon | ||
1128 | */ | ||
1129 | if (dom) { | ||
1130 | err = auth_unix_forget_old(dom); | ||
1131 | dom->h.expiry_time = get_seconds(); | ||
1132 | auth_domain_put(dom); | ||
1133 | } | ||
1134 | |||
1135 | exp_writeunlock(); | ||
1136 | out: | ||
1137 | return err; | ||
1138 | } | ||
1139 | |||
1140 | /* | ||
1141 | * Verify that string is non-empty and does not exceed max length. | ||
1142 | */ | ||
1143 | static int | ||
1144 | exp_verify_string(char *cp, int max) | ||
1145 | { | ||
1146 | int i; | ||
1147 | |||
1148 | for (i = 0; i < max; i++) | ||
1149 | if (!cp[i]) | ||
1150 | return i; | ||
1151 | cp[i] = 0; | ||
1152 | printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp); | ||
1153 | return 0; | ||
1154 | } | ||
1155 | |||
1156 | /* | ||
1157 | * Initialize the exports module. | ||
1158 | */ | ||
1159 | void | ||
1160 | nfsd_export_init(void) | ||
1161 | { | ||
1162 | dprintk("nfsd: initializing export module.\n"); | ||
1163 | |||
1164 | cache_register(&svc_export_cache); | ||
1165 | cache_register(&svc_expkey_cache); | ||
1166 | |||
1167 | } | ||
1168 | |||
1169 | /* | ||
1170 | * Flush exports table - called when last nfsd thread is killed | ||
1171 | */ | ||
1172 | void | ||
1173 | nfsd_export_flush(void) | ||
1174 | { | ||
1175 | exp_writelock(); | ||
1176 | cache_purge(&svc_expkey_cache); | ||
1177 | cache_purge(&svc_export_cache); | ||
1178 | exp_writeunlock(); | ||
1179 | } | ||
1180 | |||
1181 | /* | ||
1182 | * Shutdown the exports module. | ||
1183 | */ | ||
1184 | void | ||
1185 | nfsd_export_shutdown(void) | ||
1186 | { | ||
1187 | |||
1188 | dprintk("nfsd: shutting down export module.\n"); | ||
1189 | |||
1190 | exp_writelock(); | ||
1191 | |||
1192 | if (cache_unregister(&svc_expkey_cache)) | ||
1193 | printk(KERN_ERR "nfsd: failed to unregister expkey cache\n"); | ||
1194 | if (cache_unregister(&svc_export_cache)) | ||
1195 | printk(KERN_ERR "nfsd: failed to unregister export cache\n"); | ||
1196 | svcauth_unix_purge(); | ||
1197 | |||
1198 | exp_writeunlock(); | ||
1199 | dprintk("nfsd: export shutdown complete.\n"); | ||
1200 | } | ||
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c new file mode 100644 index 000000000000..7b889ff15ae6 --- /dev/null +++ b/fs/nfsd/lockd.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/lockd.c | ||
3 | * | ||
4 | * This file contains all the stubs needed when communicating with lockd. | ||
5 | * This level of indirection is necessary so we can run nfsd+lockd without | ||
6 | * requiring the nfs client to be compiled in/loaded, and vice versa. | ||
7 | * | ||
8 | * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/fs.h> | ||
13 | #include <linux/file.h> | ||
14 | #include <linux/mount.h> | ||
15 | #include <linux/sunrpc/clnt.h> | ||
16 | #include <linux/sunrpc/svc.h> | ||
17 | #include <linux/nfsd/nfsd.h> | ||
18 | #include <linux/lockd/bind.h> | ||
19 | |||
20 | #define NFSDDBG_FACILITY NFSDDBG_LOCKD | ||
21 | |||
22 | /* | ||
23 | * Note: we hold the dentry use count while the file is open. | ||
24 | */ | ||
25 | static u32 | ||
26 | nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) | ||
27 | { | ||
28 | u32 nfserr; | ||
29 | struct svc_fh fh; | ||
30 | |||
31 | /* must initialize before using! but maxsize doesn't matter */ | ||
32 | fh_init(&fh,0); | ||
33 | fh.fh_handle.fh_size = f->size; | ||
34 | memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size); | ||
35 | fh.fh_export = NULL; | ||
36 | |||
37 | exp_readlock(); | ||
38 | nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp); | ||
39 | fh_put(&fh); | ||
40 | rqstp->rq_client = NULL; | ||
41 | exp_readunlock(); | ||
42 | /* nlm and nfsd don't share error codes. | ||
43 | * we invent: 0 = no error | ||
44 | * 1 = stale file handle | ||
45 | * 2 = other error | ||
46 | */ | ||
47 | switch (nfserr) { | ||
48 | case nfs_ok: | ||
49 | return 0; | ||
50 | case nfserr_stale: | ||
51 | return 1; | ||
52 | default: | ||
53 | return 2; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | nlm_fclose(struct file *filp) | ||
59 | { | ||
60 | fput(filp); | ||
61 | } | ||
62 | |||
63 | static struct nlmsvc_binding nfsd_nlm_ops = { | ||
64 | .fopen = nlm_fopen, /* open file for locking */ | ||
65 | .fclose = nlm_fclose, /* close file */ | ||
66 | }; | ||
67 | |||
68 | void | ||
69 | nfsd_lockd_init(void) | ||
70 | { | ||
71 | dprintk("nfsd: initializing lockd\n"); | ||
72 | nlmsvc_ops = &nfsd_nlm_ops; | ||
73 | } | ||
74 | |||
75 | void | ||
76 | nfsd_lockd_shutdown(void) | ||
77 | { | ||
78 | nlmsvc_ops = NULL; | ||
79 | } | ||
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c new file mode 100644 index 000000000000..041380fe667b --- /dev/null +++ b/fs/nfsd/nfs3proc.c | |||
@@ -0,0 +1,702 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfs3proc.c | ||
3 | * | ||
4 | * Process version 3 NFS requests. | ||
5 | * | ||
6 | * Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de> | ||
7 | */ | ||
8 | |||
9 | #include <linux/linkage.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/fs.h> | ||
13 | #include <linux/ext2_fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/fcntl.h> | ||
16 | #include <linux/net.h> | ||
17 | #include <linux/in.h> | ||
18 | #include <linux/unistd.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/major.h> | ||
21 | |||
22 | #include <linux/sunrpc/svc.h> | ||
23 | #include <linux/nfsd/nfsd.h> | ||
24 | #include <linux/nfsd/cache.h> | ||
25 | #include <linux/nfsd/xdr3.h> | ||
26 | #include <linux/nfs3.h> | ||
27 | |||
28 | #define NFSDDBG_FACILITY NFSDDBG_PROC | ||
29 | |||
30 | #define RETURN_STATUS(st) { resp->status = (st); return (st); } | ||
31 | |||
32 | static int nfs3_ftypes[] = { | ||
33 | 0, /* NF3NON */ | ||
34 | S_IFREG, /* NF3REG */ | ||
35 | S_IFDIR, /* NF3DIR */ | ||
36 | S_IFBLK, /* NF3BLK */ | ||
37 | S_IFCHR, /* NF3CHR */ | ||
38 | S_IFLNK, /* NF3LNK */ | ||
39 | S_IFSOCK, /* NF3SOCK */ | ||
40 | S_IFIFO, /* NF3FIFO */ | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * NULL call. | ||
45 | */ | ||
46 | static int | ||
47 | nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) | ||
48 | { | ||
49 | return nfs_ok; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Get a file's attributes | ||
54 | */ | ||
55 | static int | ||
56 | nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, | ||
57 | struct nfsd3_attrstat *resp) | ||
58 | { | ||
59 | int nfserr; | ||
60 | |||
61 | dprintk("nfsd: GETATTR(3) %s\n", | ||
62 | SVCFH_fmt(&argp->fh)); | ||
63 | |||
64 | fh_copy(&resp->fh, &argp->fh); | ||
65 | nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); | ||
66 | RETURN_STATUS(nfserr); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Set a file's attributes | ||
71 | */ | ||
72 | static int | ||
73 | nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp, | ||
74 | struct nfsd3_attrstat *resp) | ||
75 | { | ||
76 | int nfserr; | ||
77 | |||
78 | dprintk("nfsd: SETATTR(3) %s\n", | ||
79 | SVCFH_fmt(&argp->fh)); | ||
80 | |||
81 | fh_copy(&resp->fh, &argp->fh); | ||
82 | nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs, | ||
83 | argp->check_guard, argp->guardtime); | ||
84 | RETURN_STATUS(nfserr); | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Look up a path name component | ||
89 | */ | ||
90 | static int | ||
91 | nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, | ||
92 | struct nfsd3_diropres *resp) | ||
93 | { | ||
94 | int nfserr; | ||
95 | |||
96 | dprintk("nfsd: LOOKUP(3) %s %.*s\n", | ||
97 | SVCFH_fmt(&argp->fh), | ||
98 | argp->len, | ||
99 | argp->name); | ||
100 | |||
101 | fh_copy(&resp->dirfh, &argp->fh); | ||
102 | fh_init(&resp->fh, NFS3_FHSIZE); | ||
103 | |||
104 | nfserr = nfsd_lookup(rqstp, &resp->dirfh, | ||
105 | argp->name, | ||
106 | argp->len, | ||
107 | &resp->fh); | ||
108 | RETURN_STATUS(nfserr); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Check file access | ||
113 | */ | ||
114 | static int | ||
115 | nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp, | ||
116 | struct nfsd3_accessres *resp) | ||
117 | { | ||
118 | int nfserr; | ||
119 | |||
120 | dprintk("nfsd: ACCESS(3) %s 0x%x\n", | ||
121 | SVCFH_fmt(&argp->fh), | ||
122 | argp->access); | ||
123 | |||
124 | fh_copy(&resp->fh, &argp->fh); | ||
125 | resp->access = argp->access; | ||
126 | nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); | ||
127 | RETURN_STATUS(nfserr); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Read a symlink. | ||
132 | */ | ||
133 | static int | ||
134 | nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd3_readlinkargs *argp, | ||
135 | struct nfsd3_readlinkres *resp) | ||
136 | { | ||
137 | int nfserr; | ||
138 | |||
139 | dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh)); | ||
140 | |||
141 | /* Read the symlink. */ | ||
142 | fh_copy(&resp->fh, &argp->fh); | ||
143 | resp->len = NFS3_MAXPATHLEN; | ||
144 | nfserr = nfsd_readlink(rqstp, &resp->fh, argp->buffer, &resp->len); | ||
145 | RETURN_STATUS(nfserr); | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Read a portion of a file. | ||
150 | */ | ||
151 | static int | ||
152 | nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, | ||
153 | struct nfsd3_readres *resp) | ||
154 | { | ||
155 | int nfserr; | ||
156 | |||
157 | dprintk("nfsd: READ(3) %s %lu bytes at %lu\n", | ||
158 | SVCFH_fmt(&argp->fh), | ||
159 | (unsigned long) argp->count, | ||
160 | (unsigned long) argp->offset); | ||
161 | |||
162 | /* Obtain buffer pointer for payload. | ||
163 | * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof) | ||
164 | * + 1 (xdr opaque byte count) = 26 | ||
165 | */ | ||
166 | |||
167 | resp->count = argp->count; | ||
168 | if (NFSSVC_MAXBLKSIZE < resp->count) | ||
169 | resp->count = NFSSVC_MAXBLKSIZE; | ||
170 | |||
171 | svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); | ||
172 | |||
173 | fh_copy(&resp->fh, &argp->fh); | ||
174 | nfserr = nfsd_read(rqstp, &resp->fh, NULL, | ||
175 | argp->offset, | ||
176 | argp->vec, argp->vlen, | ||
177 | &resp->count); | ||
178 | if (nfserr == 0) { | ||
179 | struct inode *inode = resp->fh.fh_dentry->d_inode; | ||
180 | |||
181 | resp->eof = (argp->offset + resp->count) >= inode->i_size; | ||
182 | } | ||
183 | |||
184 | RETURN_STATUS(nfserr); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * Write data to a file | ||
189 | */ | ||
190 | static int | ||
191 | nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, | ||
192 | struct nfsd3_writeres *resp) | ||
193 | { | ||
194 | int nfserr; | ||
195 | |||
196 | dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n", | ||
197 | SVCFH_fmt(&argp->fh), | ||
198 | argp->len, | ||
199 | (unsigned long) argp->offset, | ||
200 | argp->stable? " stable" : ""); | ||
201 | |||
202 | fh_copy(&resp->fh, &argp->fh); | ||
203 | resp->committed = argp->stable; | ||
204 | nfserr = nfsd_write(rqstp, &resp->fh, NULL, | ||
205 | argp->offset, | ||
206 | argp->vec, argp->vlen, | ||
207 | argp->len, | ||
208 | &resp->committed); | ||
209 | resp->count = argp->count; | ||
210 | RETURN_STATUS(nfserr); | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * With NFSv3, CREATE processing is a lot easier than with NFSv2. | ||
215 | * At least in theory; we'll see how it fares in practice when the | ||
216 | * first reports about SunOS compatibility problems start to pour in... | ||
217 | */ | ||
218 | static int | ||
219 | nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, | ||
220 | struct nfsd3_diropres *resp) | ||
221 | { | ||
222 | svc_fh *dirfhp, *newfhp = NULL; | ||
223 | struct iattr *attr; | ||
224 | u32 nfserr; | ||
225 | |||
226 | dprintk("nfsd: CREATE(3) %s %.*s\n", | ||
227 | SVCFH_fmt(&argp->fh), | ||
228 | argp->len, | ||
229 | argp->name); | ||
230 | |||
231 | dirfhp = fh_copy(&resp->dirfh, &argp->fh); | ||
232 | newfhp = fh_init(&resp->fh, NFS3_FHSIZE); | ||
233 | attr = &argp->attrs; | ||
234 | |||
235 | /* Get the directory inode */ | ||
236 | nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE); | ||
237 | if (nfserr) | ||
238 | RETURN_STATUS(nfserr); | ||
239 | |||
240 | /* Unfudge the mode bits */ | ||
241 | attr->ia_mode &= ~S_IFMT; | ||
242 | if (!(attr->ia_valid & ATTR_MODE)) { | ||
243 | attr->ia_valid |= ATTR_MODE; | ||
244 | attr->ia_mode = S_IFREG; | ||
245 | } else { | ||
246 | attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG; | ||
247 | } | ||
248 | |||
249 | /* Now create the file and set attributes */ | ||
250 | nfserr = nfsd_create_v3(rqstp, dirfhp, argp->name, argp->len, | ||
251 | attr, newfhp, | ||
252 | argp->createmode, argp->verf, NULL); | ||
253 | |||
254 | RETURN_STATUS(nfserr); | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Make directory. This operation is not idempotent. | ||
259 | */ | ||
260 | static int | ||
261 | nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, | ||
262 | struct nfsd3_diropres *resp) | ||
263 | { | ||
264 | int nfserr; | ||
265 | |||
266 | dprintk("nfsd: MKDIR(3) %s %.*s\n", | ||
267 | SVCFH_fmt(&argp->fh), | ||
268 | argp->len, | ||
269 | argp->name); | ||
270 | |||
271 | argp->attrs.ia_valid &= ~ATTR_SIZE; | ||
272 | fh_copy(&resp->dirfh, &argp->fh); | ||
273 | fh_init(&resp->fh, NFS3_FHSIZE); | ||
274 | nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, | ||
275 | &argp->attrs, S_IFDIR, 0, &resp->fh); | ||
276 | |||
277 | RETURN_STATUS(nfserr); | ||
278 | } | ||
279 | |||
280 | static int | ||
281 | nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp, | ||
282 | struct nfsd3_diropres *resp) | ||
283 | { | ||
284 | int nfserr; | ||
285 | |||
286 | dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n", | ||
287 | SVCFH_fmt(&argp->ffh), | ||
288 | argp->flen, argp->fname, | ||
289 | argp->tlen, argp->tname); | ||
290 | |||
291 | fh_copy(&resp->dirfh, &argp->ffh); | ||
292 | fh_init(&resp->fh, NFS3_FHSIZE); | ||
293 | nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen, | ||
294 | argp->tname, argp->tlen, | ||
295 | &resp->fh, &argp->attrs); | ||
296 | RETURN_STATUS(nfserr); | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Make socket/fifo/device. | ||
301 | */ | ||
302 | static int | ||
303 | nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp, | ||
304 | struct nfsd3_diropres *resp) | ||
305 | { | ||
306 | int nfserr, type; | ||
307 | dev_t rdev = 0; | ||
308 | |||
309 | dprintk("nfsd: MKNOD(3) %s %.*s\n", | ||
310 | SVCFH_fmt(&argp->fh), | ||
311 | argp->len, | ||
312 | argp->name); | ||
313 | |||
314 | fh_copy(&resp->dirfh, &argp->fh); | ||
315 | fh_init(&resp->fh, NFS3_FHSIZE); | ||
316 | |||
317 | if (argp->ftype == 0 || argp->ftype >= NF3BAD) | ||
318 | RETURN_STATUS(nfserr_inval); | ||
319 | if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) { | ||
320 | rdev = MKDEV(argp->major, argp->minor); | ||
321 | if (MAJOR(rdev) != argp->major || | ||
322 | MINOR(rdev) != argp->minor) | ||
323 | RETURN_STATUS(nfserr_inval); | ||
324 | } else | ||
325 | if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO) | ||
326 | RETURN_STATUS(nfserr_inval); | ||
327 | |||
328 | type = nfs3_ftypes[argp->ftype]; | ||
329 | nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, | ||
330 | &argp->attrs, type, rdev, &resp->fh); | ||
331 | |||
332 | RETURN_STATUS(nfserr); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Remove file/fifo/socket etc. | ||
337 | */ | ||
338 | static int | ||
339 | nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, | ||
340 | struct nfsd3_attrstat *resp) | ||
341 | { | ||
342 | int nfserr; | ||
343 | |||
344 | dprintk("nfsd: REMOVE(3) %s %.*s\n", | ||
345 | SVCFH_fmt(&argp->fh), | ||
346 | argp->len, | ||
347 | argp->name); | ||
348 | |||
349 | /* Unlink. -S_IFDIR means file must not be a directory */ | ||
350 | fh_copy(&resp->fh, &argp->fh); | ||
351 | nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len); | ||
352 | RETURN_STATUS(nfserr); | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Remove a directory | ||
357 | */ | ||
358 | static int | ||
359 | nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, | ||
360 | struct nfsd3_attrstat *resp) | ||
361 | { | ||
362 | int nfserr; | ||
363 | |||
364 | dprintk("nfsd: RMDIR(3) %s %.*s\n", | ||
365 | SVCFH_fmt(&argp->fh), | ||
366 | argp->len, | ||
367 | argp->name); | ||
368 | |||
369 | fh_copy(&resp->fh, &argp->fh); | ||
370 | nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len); | ||
371 | RETURN_STATUS(nfserr); | ||
372 | } | ||
373 | |||
374 | static int | ||
375 | nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp, | ||
376 | struct nfsd3_renameres *resp) | ||
377 | { | ||
378 | int nfserr; | ||
379 | |||
380 | dprintk("nfsd: RENAME(3) %s %.*s ->\n", | ||
381 | SVCFH_fmt(&argp->ffh), | ||
382 | argp->flen, | ||
383 | argp->fname); | ||
384 | dprintk("nfsd: -> %s %.*s\n", | ||
385 | SVCFH_fmt(&argp->tfh), | ||
386 | argp->tlen, | ||
387 | argp->tname); | ||
388 | |||
389 | fh_copy(&resp->ffh, &argp->ffh); | ||
390 | fh_copy(&resp->tfh, &argp->tfh); | ||
391 | nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen, | ||
392 | &resp->tfh, argp->tname, argp->tlen); | ||
393 | RETURN_STATUS(nfserr); | ||
394 | } | ||
395 | |||
396 | static int | ||
397 | nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp, | ||
398 | struct nfsd3_linkres *resp) | ||
399 | { | ||
400 | int nfserr; | ||
401 | |||
402 | dprintk("nfsd: LINK(3) %s ->\n", | ||
403 | SVCFH_fmt(&argp->ffh)); | ||
404 | dprintk("nfsd: -> %s %.*s\n", | ||
405 | SVCFH_fmt(&argp->tfh), | ||
406 | argp->tlen, | ||
407 | argp->tname); | ||
408 | |||
409 | fh_copy(&resp->fh, &argp->ffh); | ||
410 | fh_copy(&resp->tfh, &argp->tfh); | ||
411 | nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen, | ||
412 | &resp->fh); | ||
413 | RETURN_STATUS(nfserr); | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * Read a portion of a directory. | ||
418 | */ | ||
419 | static int | ||
420 | nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, | ||
421 | struct nfsd3_readdirres *resp) | ||
422 | { | ||
423 | int nfserr, count; | ||
424 | |||
425 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", | ||
426 | SVCFH_fmt(&argp->fh), | ||
427 | argp->count, (u32) argp->cookie); | ||
428 | |||
429 | /* Make sure we've room for the NULL ptr & eof flag, and shrink to | ||
430 | * client read size */ | ||
431 | count = (argp->count >> 2) - 2; | ||
432 | |||
433 | /* Read directory and encode entries on the fly */ | ||
434 | fh_copy(&resp->fh, &argp->fh); | ||
435 | |||
436 | resp->buflen = count; | ||
437 | resp->common.err = nfs_ok; | ||
438 | resp->buffer = argp->buffer; | ||
439 | resp->rqstp = rqstp; | ||
440 | nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, | ||
441 | &resp->common, nfs3svc_encode_entry); | ||
442 | memcpy(resp->verf, argp->verf, 8); | ||
443 | resp->count = resp->buffer - argp->buffer; | ||
444 | if (resp->offset) | ||
445 | xdr_encode_hyper(resp->offset, argp->cookie); | ||
446 | |||
447 | RETURN_STATUS(nfserr); | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * Read a portion of a directory, including file handles and attrs. | ||
452 | * For now, we choose to ignore the dircount parameter. | ||
453 | */ | ||
454 | static int | ||
455 | nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, | ||
456 | struct nfsd3_readdirres *resp) | ||
457 | { | ||
458 | int nfserr, count = 0; | ||
459 | loff_t offset; | ||
460 | int i; | ||
461 | caddr_t page_addr = NULL; | ||
462 | |||
463 | dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", | ||
464 | SVCFH_fmt(&argp->fh), | ||
465 | argp->count, (u32) argp->cookie); | ||
466 | |||
467 | /* Convert byte count to number of words (i.e. >> 2), | ||
468 | * and reserve room for the NULL ptr & eof flag (-2 words) */ | ||
469 | resp->count = (argp->count >> 2) - 2; | ||
470 | |||
471 | /* Read directory and encode entries on the fly */ | ||
472 | fh_copy(&resp->fh, &argp->fh); | ||
473 | |||
474 | resp->common.err = nfs_ok; | ||
475 | resp->buffer = argp->buffer; | ||
476 | resp->buflen = resp->count; | ||
477 | resp->rqstp = rqstp; | ||
478 | offset = argp->cookie; | ||
479 | nfserr = nfsd_readdir(rqstp, &resp->fh, | ||
480 | &offset, | ||
481 | &resp->common, | ||
482 | nfs3svc_encode_entry_plus); | ||
483 | memcpy(resp->verf, argp->verf, 8); | ||
484 | for (i=1; i<rqstp->rq_resused ; i++) { | ||
485 | page_addr = page_address(rqstp->rq_respages[i]); | ||
486 | |||
487 | if (((caddr_t)resp->buffer >= page_addr) && | ||
488 | ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { | ||
489 | count += (caddr_t)resp->buffer - page_addr; | ||
490 | break; | ||
491 | } | ||
492 | count += PAGE_SIZE; | ||
493 | } | ||
494 | resp->count = count >> 2; | ||
495 | if (resp->offset) { | ||
496 | if (unlikely(resp->offset1)) { | ||
497 | /* we ended up with offset on a page boundary */ | ||
498 | *resp->offset = htonl(offset >> 32); | ||
499 | *resp->offset1 = htonl(offset & 0xffffffff); | ||
500 | resp->offset1 = NULL; | ||
501 | } else { | ||
502 | xdr_encode_hyper(resp->offset, offset); | ||
503 | } | ||
504 | } | ||
505 | |||
506 | RETURN_STATUS(nfserr); | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * Get file system stats | ||
511 | */ | ||
512 | static int | ||
513 | nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, | ||
514 | struct nfsd3_fsstatres *resp) | ||
515 | { | ||
516 | int nfserr; | ||
517 | |||
518 | dprintk("nfsd: FSSTAT(3) %s\n", | ||
519 | SVCFH_fmt(&argp->fh)); | ||
520 | |||
521 | nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); | ||
522 | fh_put(&argp->fh); | ||
523 | RETURN_STATUS(nfserr); | ||
524 | } | ||
525 | |||
526 | /* | ||
527 | * Get file system info | ||
528 | */ | ||
529 | static int | ||
530 | nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, | ||
531 | struct nfsd3_fsinfores *resp) | ||
532 | { | ||
533 | int nfserr; | ||
534 | |||
535 | dprintk("nfsd: FSINFO(3) %s\n", | ||
536 | SVCFH_fmt(&argp->fh)); | ||
537 | |||
538 | resp->f_rtmax = NFSSVC_MAXBLKSIZE; | ||
539 | resp->f_rtpref = NFSSVC_MAXBLKSIZE; | ||
540 | resp->f_rtmult = PAGE_SIZE; | ||
541 | resp->f_wtmax = NFSSVC_MAXBLKSIZE; | ||
542 | resp->f_wtpref = NFSSVC_MAXBLKSIZE; | ||
543 | resp->f_wtmult = PAGE_SIZE; | ||
544 | resp->f_dtpref = PAGE_SIZE; | ||
545 | resp->f_maxfilesize = ~(u32) 0; | ||
546 | resp->f_properties = NFS3_FSF_DEFAULT; | ||
547 | |||
548 | nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); | ||
549 | |||
550 | /* Check special features of the file system. May request | ||
551 | * different read/write sizes for file systems known to have | ||
552 | * problems with large blocks */ | ||
553 | if (nfserr == 0) { | ||
554 | struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb; | ||
555 | |||
556 | /* Note that we don't care for remote fs's here */ | ||
557 | if (sb->s_magic == 0x4d44 /* MSDOS_SUPER_MAGIC */) { | ||
558 | resp->f_properties = NFS3_FSF_BILLYBOY; | ||
559 | } | ||
560 | resp->f_maxfilesize = sb->s_maxbytes; | ||
561 | } | ||
562 | |||
563 | fh_put(&argp->fh); | ||
564 | RETURN_STATUS(nfserr); | ||
565 | } | ||
566 | |||
567 | /* | ||
568 | * Get pathconf info for the specified file | ||
569 | */ | ||
570 | static int | ||
571 | nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, | ||
572 | struct nfsd3_pathconfres *resp) | ||
573 | { | ||
574 | int nfserr; | ||
575 | |||
576 | dprintk("nfsd: PATHCONF(3) %s\n", | ||
577 | SVCFH_fmt(&argp->fh)); | ||
578 | |||
579 | /* Set default pathconf */ | ||
580 | resp->p_link_max = 255; /* at least */ | ||
581 | resp->p_name_max = 255; /* at least */ | ||
582 | resp->p_no_trunc = 0; | ||
583 | resp->p_chown_restricted = 1; | ||
584 | resp->p_case_insensitive = 0; | ||
585 | resp->p_case_preserving = 1; | ||
586 | |||
587 | nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); | ||
588 | |||
589 | if (nfserr == 0) { | ||
590 | struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb; | ||
591 | |||
592 | /* Note that we don't care for remote fs's here */ | ||
593 | switch (sb->s_magic) { | ||
594 | case EXT2_SUPER_MAGIC: | ||
595 | resp->p_link_max = EXT2_LINK_MAX; | ||
596 | resp->p_name_max = EXT2_NAME_LEN; | ||
597 | break; | ||
598 | case 0x4d44: /* MSDOS_SUPER_MAGIC */ | ||
599 | resp->p_case_insensitive = 1; | ||
600 | resp->p_case_preserving = 0; | ||
601 | break; | ||
602 | } | ||
603 | } | ||
604 | |||
605 | fh_put(&argp->fh); | ||
606 | RETURN_STATUS(nfserr); | ||
607 | } | ||
608 | |||
609 | |||
610 | /* | ||
611 | * Commit a file (range) to stable storage. | ||
612 | */ | ||
613 | static int | ||
614 | nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp, | ||
615 | struct nfsd3_commitres *resp) | ||
616 | { | ||
617 | int nfserr; | ||
618 | |||
619 | dprintk("nfsd: COMMIT(3) %s %u@%Lu\n", | ||
620 | SVCFH_fmt(&argp->fh), | ||
621 | argp->count, | ||
622 | (unsigned long long) argp->offset); | ||
623 | |||
624 | if (argp->offset > NFS_OFFSET_MAX) | ||
625 | RETURN_STATUS(nfserr_inval); | ||
626 | |||
627 | fh_copy(&resp->fh, &argp->fh); | ||
628 | nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count); | ||
629 | |||
630 | RETURN_STATUS(nfserr); | ||
631 | } | ||
632 | |||
633 | |||
634 | /* | ||
635 | * NFSv3 Server procedures. | ||
636 | * Only the results of non-idempotent operations are cached. | ||
637 | */ | ||
638 | #define nfs3svc_decode_voidargs NULL | ||
639 | #define nfs3svc_release_void NULL | ||
640 | #define nfs3svc_decode_fhandleargs nfs3svc_decode_fhandle | ||
641 | #define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat | ||
642 | #define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat | ||
643 | #define nfsd3_mkdirargs nfsd3_createargs | ||
644 | #define nfsd3_readdirplusargs nfsd3_readdirargs | ||
645 | #define nfsd3_fhandleargs nfsd_fhandle | ||
646 | #define nfsd3_fhandleres nfsd3_attrstat | ||
647 | #define nfsd3_attrstatres nfsd3_attrstat | ||
648 | #define nfsd3_wccstatres nfsd3_attrstat | ||
649 | #define nfsd3_createres nfsd3_diropres | ||
650 | #define nfsd3_voidres nfsd3_voidargs | ||
651 | struct nfsd3_voidargs { int dummy; }; | ||
652 | |||
653 | #define PROC(name, argt, rest, relt, cache, respsize) \ | ||
654 | { (svc_procfunc) nfsd3_proc_##name, \ | ||
655 | (kxdrproc_t) nfs3svc_decode_##argt##args, \ | ||
656 | (kxdrproc_t) nfs3svc_encode_##rest##res, \ | ||
657 | (kxdrproc_t) nfs3svc_release_##relt, \ | ||
658 | sizeof(struct nfsd3_##argt##args), \ | ||
659 | sizeof(struct nfsd3_##rest##res), \ | ||
660 | 0, \ | ||
661 | cache, \ | ||
662 | respsize, \ | ||
663 | } | ||
664 | |||
665 | #define ST 1 /* status*/ | ||
666 | #define FH 17 /* filehandle with length */ | ||
667 | #define AT 21 /* attributes */ | ||
668 | #define pAT (1+AT) /* post attributes - conditional */ | ||
669 | #define WC (7+pAT) /* WCC attributes */ | ||
670 | |||
671 | static struct svc_procedure nfsd_procedures3[22] = { | ||
672 | PROC(null, void, void, void, RC_NOCACHE, ST), | ||
673 | PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT), | ||
674 | PROC(setattr, sattr, wccstat, fhandle, RC_REPLBUFF, ST+WC), | ||
675 | PROC(lookup, dirop, dirop, fhandle2, RC_NOCACHE, ST+FH+pAT+pAT), | ||
676 | PROC(access, access, access, fhandle, RC_NOCACHE, ST+pAT+1), | ||
677 | PROC(readlink, readlink, readlink, fhandle, RC_NOCACHE, ST+pAT+1+NFS3_MAXPATHLEN/4), | ||
678 | PROC(read, read, read, fhandle, RC_NOCACHE, ST+pAT+4+NFSSVC_MAXBLKSIZE), | ||
679 | PROC(write, write, write, fhandle, RC_REPLBUFF, ST+WC+4), | ||
680 | PROC(create, create, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC), | ||
681 | PROC(mkdir, mkdir, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC), | ||
682 | PROC(symlink, symlink, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC), | ||
683 | PROC(mknod, mknod, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC), | ||
684 | PROC(remove, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC), | ||
685 | PROC(rmdir, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC), | ||
686 | PROC(rename, rename, rename, fhandle2, RC_REPLBUFF, ST+WC+WC), | ||
687 | PROC(link, link, link, fhandle2, RC_REPLBUFF, ST+pAT+WC), | ||
688 | PROC(readdir, readdir, readdir, fhandle, RC_NOCACHE, 0), | ||
689 | PROC(readdirplus,readdirplus, readdir, fhandle, RC_NOCACHE, 0), | ||
690 | PROC(fsstat, fhandle, fsstat, void, RC_NOCACHE, ST+pAT+2*6+1), | ||
691 | PROC(fsinfo, fhandle, fsinfo, void, RC_NOCACHE, ST+pAT+12), | ||
692 | PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE, ST+pAT+6), | ||
693 | PROC(commit, commit, commit, fhandle, RC_NOCACHE, ST+WC+2), | ||
694 | }; | ||
695 | |||
696 | struct svc_version nfsd_version3 = { | ||
697 | .vs_vers = 3, | ||
698 | .vs_nproc = 22, | ||
699 | .vs_proc = nfsd_procedures3, | ||
700 | .vs_dispatch = nfsd_dispatch, | ||
701 | .vs_xdrsize = NFS3_SVC_XDRSIZE, | ||
702 | }; | ||
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c new file mode 100644 index 000000000000..11f806835c5a --- /dev/null +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfs3xdr.c | ||
3 | * | ||
4 | * XDR support for nfsd/protocol version 3. | ||
5 | * | ||
6 | * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> | ||
7 | * | ||
8 | * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()! | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/nfs3.h> | ||
14 | #include <linux/list.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/dcache.h> | ||
17 | #include <linux/namei.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/vfs.h> | ||
20 | #include <linux/sunrpc/xdr.h> | ||
21 | #include <linux/sunrpc/svc.h> | ||
22 | #include <linux/nfsd/nfsd.h> | ||
23 | #include <linux/nfsd/xdr3.h> | ||
24 | |||
25 | #define NFSDDBG_FACILITY NFSDDBG_XDR | ||
26 | |||
27 | #ifdef NFSD_OPTIMIZE_SPACE | ||
28 | # define inline | ||
29 | #endif | ||
30 | |||
31 | |||
32 | /* | ||
33 | * Mapping of S_IF* types to NFS file types | ||
34 | */ | ||
35 | static u32 nfs3_ftypes[] = { | ||
36 | NF3NON, NF3FIFO, NF3CHR, NF3BAD, | ||
37 | NF3DIR, NF3BAD, NF3BLK, NF3BAD, | ||
38 | NF3REG, NF3BAD, NF3LNK, NF3BAD, | ||
39 | NF3SOCK, NF3BAD, NF3LNK, NF3BAD, | ||
40 | }; | ||
41 | |||
42 | /* | ||
43 | * XDR functions for basic NFS types | ||
44 | */ | ||
45 | static inline u32 * | ||
46 | encode_time3(u32 *p, struct timespec *time) | ||
47 | { | ||
48 | *p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec); | ||
49 | return p; | ||
50 | } | ||
51 | |||
52 | static inline u32 * | ||
53 | decode_time3(u32 *p, struct timespec *time) | ||
54 | { | ||
55 | time->tv_sec = ntohl(*p++); | ||
56 | time->tv_nsec = ntohl(*p++); | ||
57 | return p; | ||
58 | } | ||
59 | |||
60 | static inline u32 * | ||
61 | decode_fh(u32 *p, struct svc_fh *fhp) | ||
62 | { | ||
63 | unsigned int size; | ||
64 | fh_init(fhp, NFS3_FHSIZE); | ||
65 | size = ntohl(*p++); | ||
66 | if (size > NFS3_FHSIZE) | ||
67 | return NULL; | ||
68 | |||
69 | memcpy(&fhp->fh_handle.fh_base, p, size); | ||
70 | fhp->fh_handle.fh_size = size; | ||
71 | return p + XDR_QUADLEN(size); | ||
72 | } | ||
73 | |||
74 | static inline u32 * | ||
75 | encode_fh(u32 *p, struct svc_fh *fhp) | ||
76 | { | ||
77 | unsigned int size = fhp->fh_handle.fh_size; | ||
78 | *p++ = htonl(size); | ||
79 | if (size) p[XDR_QUADLEN(size)-1]=0; | ||
80 | memcpy(p, &fhp->fh_handle.fh_base, size); | ||
81 | return p + XDR_QUADLEN(size); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Decode a file name and make sure that the path contains | ||
86 | * no slashes or null bytes. | ||
87 | */ | ||
88 | static inline u32 * | ||
89 | decode_filename(u32 *p, char **namp, int *lenp) | ||
90 | { | ||
91 | char *name; | ||
92 | int i; | ||
93 | |||
94 | if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) { | ||
95 | for (i = 0, name = *namp; i < *lenp; i++, name++) { | ||
96 | if (*name == '\0' || *name == '/') | ||
97 | return NULL; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return p; | ||
102 | } | ||
103 | |||
104 | static inline u32 * | ||
105 | decode_sattr3(u32 *p, struct iattr *iap) | ||
106 | { | ||
107 | u32 tmp; | ||
108 | |||
109 | iap->ia_valid = 0; | ||
110 | |||
111 | if (*p++) { | ||
112 | iap->ia_valid |= ATTR_MODE; | ||
113 | iap->ia_mode = ntohl(*p++); | ||
114 | } | ||
115 | if (*p++) { | ||
116 | iap->ia_valid |= ATTR_UID; | ||
117 | iap->ia_uid = ntohl(*p++); | ||
118 | } | ||
119 | if (*p++) { | ||
120 | iap->ia_valid |= ATTR_GID; | ||
121 | iap->ia_gid = ntohl(*p++); | ||
122 | } | ||
123 | if (*p++) { | ||
124 | u64 newsize; | ||
125 | |||
126 | iap->ia_valid |= ATTR_SIZE; | ||
127 | p = xdr_decode_hyper(p, &newsize); | ||
128 | if (newsize <= NFS_OFFSET_MAX) | ||
129 | iap->ia_size = newsize; | ||
130 | else | ||
131 | iap->ia_size = NFS_OFFSET_MAX; | ||
132 | } | ||
133 | if ((tmp = ntohl(*p++)) == 1) { /* set to server time */ | ||
134 | iap->ia_valid |= ATTR_ATIME; | ||
135 | } else if (tmp == 2) { /* set to client time */ | ||
136 | iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; | ||
137 | iap->ia_atime.tv_sec = ntohl(*p++); | ||
138 | iap->ia_atime.tv_nsec = ntohl(*p++); | ||
139 | } | ||
140 | if ((tmp = ntohl(*p++)) == 1) { /* set to server time */ | ||
141 | iap->ia_valid |= ATTR_MTIME; | ||
142 | } else if (tmp == 2) { /* set to client time */ | ||
143 | iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; | ||
144 | iap->ia_mtime.tv_sec = ntohl(*p++); | ||
145 | iap->ia_mtime.tv_nsec = ntohl(*p++); | ||
146 | } | ||
147 | return p; | ||
148 | } | ||
149 | |||
150 | static inline u32 * | ||
151 | encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | ||
152 | { | ||
153 | struct vfsmount *mnt = fhp->fh_export->ex_mnt; | ||
154 | struct dentry *dentry = fhp->fh_dentry; | ||
155 | struct kstat stat; | ||
156 | struct timespec time; | ||
157 | |||
158 | vfs_getattr(mnt, dentry, &stat); | ||
159 | |||
160 | *p++ = htonl(nfs3_ftypes[(stat.mode & S_IFMT) >> 12]); | ||
161 | *p++ = htonl((u32) stat.mode); | ||
162 | *p++ = htonl((u32) stat.nlink); | ||
163 | *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid)); | ||
164 | *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid)); | ||
165 | if (S_ISLNK(stat.mode) && stat.size > NFS3_MAXPATHLEN) { | ||
166 | p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); | ||
167 | } else { | ||
168 | p = xdr_encode_hyper(p, (u64) stat.size); | ||
169 | } | ||
170 | p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9); | ||
171 | *p++ = htonl((u32) MAJOR(stat.rdev)); | ||
172 | *p++ = htonl((u32) MINOR(stat.rdev)); | ||
173 | if (is_fsid(fhp, rqstp->rq_reffh)) | ||
174 | p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid); | ||
175 | else | ||
176 | p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat.dev)); | ||
177 | p = xdr_encode_hyper(p, (u64) stat.ino); | ||
178 | p = encode_time3(p, &stat.atime); | ||
179 | lease_get_mtime(dentry->d_inode, &time); | ||
180 | p = encode_time3(p, &time); | ||
181 | p = encode_time3(p, &stat.ctime); | ||
182 | |||
183 | return p; | ||
184 | } | ||
185 | |||
186 | static inline u32 * | ||
187 | encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | ||
188 | { | ||
189 | struct inode *inode = fhp->fh_dentry->d_inode; | ||
190 | |||
191 | /* Attributes to follow */ | ||
192 | *p++ = xdr_one; | ||
193 | |||
194 | *p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]); | ||
195 | *p++ = htonl((u32) fhp->fh_post_mode); | ||
196 | *p++ = htonl((u32) fhp->fh_post_nlink); | ||
197 | *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid)); | ||
198 | *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid)); | ||
199 | if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) { | ||
200 | p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); | ||
201 | } else { | ||
202 | p = xdr_encode_hyper(p, (u64) fhp->fh_post_size); | ||
203 | } | ||
204 | p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9); | ||
205 | *p++ = fhp->fh_post_rdev[0]; | ||
206 | *p++ = fhp->fh_post_rdev[1]; | ||
207 | if (is_fsid(fhp, rqstp->rq_reffh)) | ||
208 | p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid); | ||
209 | else | ||
210 | p = xdr_encode_hyper(p, (u64)huge_encode_dev(inode->i_sb->s_dev)); | ||
211 | p = xdr_encode_hyper(p, (u64) inode->i_ino); | ||
212 | p = encode_time3(p, &fhp->fh_post_atime); | ||
213 | p = encode_time3(p, &fhp->fh_post_mtime); | ||
214 | p = encode_time3(p, &fhp->fh_post_ctime); | ||
215 | |||
216 | return p; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Encode post-operation attributes. | ||
221 | * The inode may be NULL if the call failed because of a stale file | ||
222 | * handle. In this case, no attributes are returned. | ||
223 | */ | ||
224 | static u32 * | ||
225 | encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | ||
226 | { | ||
227 | struct dentry *dentry = fhp->fh_dentry; | ||
228 | if (dentry && dentry->d_inode != NULL) { | ||
229 | *p++ = xdr_one; /* attributes follow */ | ||
230 | return encode_fattr3(rqstp, p, fhp); | ||
231 | } | ||
232 | *p++ = xdr_zero; | ||
233 | return p; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Enocde weak cache consistency data | ||
238 | */ | ||
239 | static u32 * | ||
240 | encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | ||
241 | { | ||
242 | struct dentry *dentry = fhp->fh_dentry; | ||
243 | |||
244 | if (dentry && dentry->d_inode && fhp->fh_post_saved) { | ||
245 | if (fhp->fh_pre_saved) { | ||
246 | *p++ = xdr_one; | ||
247 | p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size); | ||
248 | p = encode_time3(p, &fhp->fh_pre_mtime); | ||
249 | p = encode_time3(p, &fhp->fh_pre_ctime); | ||
250 | } else { | ||
251 | *p++ = xdr_zero; | ||
252 | } | ||
253 | return encode_saved_post_attr(rqstp, p, fhp); | ||
254 | } | ||
255 | /* no pre- or post-attrs */ | ||
256 | *p++ = xdr_zero; | ||
257 | return encode_post_op_attr(rqstp, p, fhp); | ||
258 | } | ||
259 | |||
260 | |||
261 | /* | ||
262 | * XDR decode functions | ||
263 | */ | ||
264 | int | ||
265 | nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct nfsd_fhandle *args) | ||
266 | { | ||
267 | if (!(p = decode_fh(p, &args->fh))) | ||
268 | return 0; | ||
269 | return xdr_argsize_check(rqstp, p); | ||
270 | } | ||
271 | |||
272 | int | ||
273 | nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p, | ||
274 | struct nfsd3_sattrargs *args) | ||
275 | { | ||
276 | if (!(p = decode_fh(p, &args->fh)) | ||
277 | || !(p = decode_sattr3(p, &args->attrs))) | ||
278 | return 0; | ||
279 | |||
280 | if ((args->check_guard = ntohl(*p++)) != 0) { | ||
281 | struct timespec time; | ||
282 | p = decode_time3(p, &time); | ||
283 | args->guardtime = time.tv_sec; | ||
284 | } | ||
285 | |||
286 | return xdr_argsize_check(rqstp, p); | ||
287 | } | ||
288 | |||
289 | int | ||
290 | nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p, | ||
291 | struct nfsd3_diropargs *args) | ||
292 | { | ||
293 | if (!(p = decode_fh(p, &args->fh)) | ||
294 | || !(p = decode_filename(p, &args->name, &args->len))) | ||
295 | return 0; | ||
296 | |||
297 | return xdr_argsize_check(rqstp, p); | ||
298 | } | ||
299 | |||
300 | int | ||
301 | nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p, | ||
302 | struct nfsd3_accessargs *args) | ||
303 | { | ||
304 | if (!(p = decode_fh(p, &args->fh))) | ||
305 | return 0; | ||
306 | args->access = ntohl(*p++); | ||
307 | |||
308 | return xdr_argsize_check(rqstp, p); | ||
309 | } | ||
310 | |||
311 | int | ||
312 | nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, | ||
313 | struct nfsd3_readargs *args) | ||
314 | { | ||
315 | unsigned int len; | ||
316 | int v,pn; | ||
317 | |||
318 | if (!(p = decode_fh(p, &args->fh)) | ||
319 | || !(p = xdr_decode_hyper(p, &args->offset))) | ||
320 | return 0; | ||
321 | |||
322 | len = args->count = ntohl(*p++); | ||
323 | |||
324 | if (len > NFSSVC_MAXBLKSIZE) | ||
325 | len = NFSSVC_MAXBLKSIZE; | ||
326 | |||
327 | /* set up the kvec */ | ||
328 | v=0; | ||
329 | while (len > 0) { | ||
330 | pn = rqstp->rq_resused; | ||
331 | svc_take_page(rqstp); | ||
332 | args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); | ||
333 | args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; | ||
334 | len -= args->vec[v].iov_len; | ||
335 | v++; | ||
336 | } | ||
337 | args->vlen = v; | ||
338 | return xdr_argsize_check(rqstp, p); | ||
339 | } | ||
340 | |||
341 | int | ||
342 | nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, | ||
343 | struct nfsd3_writeargs *args) | ||
344 | { | ||
345 | unsigned int len, v, hdr; | ||
346 | |||
347 | if (!(p = decode_fh(p, &args->fh)) | ||
348 | || !(p = xdr_decode_hyper(p, &args->offset))) | ||
349 | return 0; | ||
350 | |||
351 | args->count = ntohl(*p++); | ||
352 | args->stable = ntohl(*p++); | ||
353 | len = args->len = ntohl(*p++); | ||
354 | |||
355 | hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; | ||
356 | if (rqstp->rq_arg.len < len + hdr) | ||
357 | return 0; | ||
358 | |||
359 | args->vec[0].iov_base = (void*)p; | ||
360 | args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; | ||
361 | |||
362 | if (len > NFSSVC_MAXBLKSIZE) | ||
363 | len = NFSSVC_MAXBLKSIZE; | ||
364 | v= 0; | ||
365 | while (len > args->vec[v].iov_len) { | ||
366 | len -= args->vec[v].iov_len; | ||
367 | v++; | ||
368 | args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); | ||
369 | args->vec[v].iov_len = PAGE_SIZE; | ||
370 | } | ||
371 | args->vec[v].iov_len = len; | ||
372 | args->vlen = v+1; | ||
373 | |||
374 | return args->count == args->len && args->vec[0].iov_len > 0; | ||
375 | } | ||
376 | |||
377 | int | ||
378 | nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p, | ||
379 | struct nfsd3_createargs *args) | ||
380 | { | ||
381 | if (!(p = decode_fh(p, &args->fh)) | ||
382 | || !(p = decode_filename(p, &args->name, &args->len))) | ||
383 | return 0; | ||
384 | |||
385 | switch (args->createmode = ntohl(*p++)) { | ||
386 | case NFS3_CREATE_UNCHECKED: | ||
387 | case NFS3_CREATE_GUARDED: | ||
388 | if (!(p = decode_sattr3(p, &args->attrs))) | ||
389 | return 0; | ||
390 | break; | ||
391 | case NFS3_CREATE_EXCLUSIVE: | ||
392 | args->verf = p; | ||
393 | p += 2; | ||
394 | break; | ||
395 | default: | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | return xdr_argsize_check(rqstp, p); | ||
400 | } | ||
401 | int | ||
402 | nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p, | ||
403 | struct nfsd3_createargs *args) | ||
404 | { | ||
405 | if (!(p = decode_fh(p, &args->fh)) | ||
406 | || !(p = decode_filename(p, &args->name, &args->len)) | ||
407 | || !(p = decode_sattr3(p, &args->attrs))) | ||
408 | return 0; | ||
409 | |||
410 | return xdr_argsize_check(rqstp, p); | ||
411 | } | ||
412 | |||
413 | int | ||
414 | nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, | ||
415 | struct nfsd3_symlinkargs *args) | ||
416 | { | ||
417 | unsigned int len; | ||
418 | int avail; | ||
419 | char *old, *new; | ||
420 | struct kvec *vec; | ||
421 | |||
422 | if (!(p = decode_fh(p, &args->ffh)) | ||
423 | || !(p = decode_filename(p, &args->fname, &args->flen)) | ||
424 | || !(p = decode_sattr3(p, &args->attrs)) | ||
425 | ) | ||
426 | return 0; | ||
427 | /* now decode the pathname, which might be larger than the first page. | ||
428 | * As we have to check for nul's anyway, we copy it into a new page | ||
429 | * This page appears in the rq_res.pages list, but as pages_len is always | ||
430 | * 0, it won't get in the way | ||
431 | */ | ||
432 | svc_take_page(rqstp); | ||
433 | len = ntohl(*p++); | ||
434 | if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) | ||
435 | return 0; | ||
436 | args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); | ||
437 | args->tlen = len; | ||
438 | /* first copy and check from the first page */ | ||
439 | old = (char*)p; | ||
440 | vec = &rqstp->rq_arg.head[0]; | ||
441 | avail = vec->iov_len - (old - (char*)vec->iov_base); | ||
442 | while (len && avail && *old) { | ||
443 | *new++ = *old++; | ||
444 | len--; | ||
445 | avail--; | ||
446 | } | ||
447 | /* now copy next page if there is one */ | ||
448 | if (len && !avail && rqstp->rq_arg.page_len) { | ||
449 | avail = rqstp->rq_arg.page_len; | ||
450 | if (avail > PAGE_SIZE) avail = PAGE_SIZE; | ||
451 | old = page_address(rqstp->rq_arg.pages[0]); | ||
452 | } | ||
453 | while (len && avail && *old) { | ||
454 | *new++ = *old++; | ||
455 | len--; | ||
456 | avail--; | ||
457 | } | ||
458 | *new = '\0'; | ||
459 | if (len) | ||
460 | return 0; | ||
461 | |||
462 | return 1; | ||
463 | } | ||
464 | |||
465 | int | ||
466 | nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p, | ||
467 | struct nfsd3_mknodargs *args) | ||
468 | { | ||
469 | if (!(p = decode_fh(p, &args->fh)) | ||
470 | || !(p = decode_filename(p, &args->name, &args->len))) | ||
471 | return 0; | ||
472 | |||
473 | args->ftype = ntohl(*p++); | ||
474 | |||
475 | if (args->ftype == NF3BLK || args->ftype == NF3CHR | ||
476 | || args->ftype == NF3SOCK || args->ftype == NF3FIFO) { | ||
477 | if (!(p = decode_sattr3(p, &args->attrs))) | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | if (args->ftype == NF3BLK || args->ftype == NF3CHR) { | ||
482 | args->major = ntohl(*p++); | ||
483 | args->minor = ntohl(*p++); | ||
484 | } | ||
485 | |||
486 | return xdr_argsize_check(rqstp, p); | ||
487 | } | ||
488 | |||
489 | int | ||
490 | nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p, | ||
491 | struct nfsd3_renameargs *args) | ||
492 | { | ||
493 | if (!(p = decode_fh(p, &args->ffh)) | ||
494 | || !(p = decode_filename(p, &args->fname, &args->flen)) | ||
495 | || !(p = decode_fh(p, &args->tfh)) | ||
496 | || !(p = decode_filename(p, &args->tname, &args->tlen))) | ||
497 | return 0; | ||
498 | |||
499 | return xdr_argsize_check(rqstp, p); | ||
500 | } | ||
501 | |||
502 | int | ||
503 | nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, | ||
504 | struct nfsd3_readlinkargs *args) | ||
505 | { | ||
506 | if (!(p = decode_fh(p, &args->fh))) | ||
507 | return 0; | ||
508 | svc_take_page(rqstp); | ||
509 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); | ||
510 | |||
511 | return xdr_argsize_check(rqstp, p); | ||
512 | } | ||
513 | |||
514 | int | ||
515 | nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p, | ||
516 | struct nfsd3_linkargs *args) | ||
517 | { | ||
518 | if (!(p = decode_fh(p, &args->ffh)) | ||
519 | || !(p = decode_fh(p, &args->tfh)) | ||
520 | || !(p = decode_filename(p, &args->tname, &args->tlen))) | ||
521 | return 0; | ||
522 | |||
523 | return xdr_argsize_check(rqstp, p); | ||
524 | } | ||
525 | |||
526 | int | ||
527 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, | ||
528 | struct nfsd3_readdirargs *args) | ||
529 | { | ||
530 | if (!(p = decode_fh(p, &args->fh))) | ||
531 | return 0; | ||
532 | p = xdr_decode_hyper(p, &args->cookie); | ||
533 | args->verf = p; p += 2; | ||
534 | args->dircount = ~0; | ||
535 | args->count = ntohl(*p++); | ||
536 | |||
537 | if (args->count > PAGE_SIZE) | ||
538 | args->count = PAGE_SIZE; | ||
539 | |||
540 | svc_take_page(rqstp); | ||
541 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); | ||
542 | |||
543 | return xdr_argsize_check(rqstp, p); | ||
544 | } | ||
545 | |||
546 | int | ||
547 | nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, | ||
548 | struct nfsd3_readdirargs *args) | ||
549 | { | ||
550 | int len, pn; | ||
551 | |||
552 | if (!(p = decode_fh(p, &args->fh))) | ||
553 | return 0; | ||
554 | p = xdr_decode_hyper(p, &args->cookie); | ||
555 | args->verf = p; p += 2; | ||
556 | args->dircount = ntohl(*p++); | ||
557 | args->count = ntohl(*p++); | ||
558 | |||
559 | len = (args->count > NFSSVC_MAXBLKSIZE) ? NFSSVC_MAXBLKSIZE : | ||
560 | args->count; | ||
561 | args->count = len; | ||
562 | |||
563 | while (len > 0) { | ||
564 | pn = rqstp->rq_resused; | ||
565 | svc_take_page(rqstp); | ||
566 | if (!args->buffer) | ||
567 | args->buffer = page_address(rqstp->rq_respages[pn]); | ||
568 | len -= PAGE_SIZE; | ||
569 | } | ||
570 | |||
571 | return xdr_argsize_check(rqstp, p); | ||
572 | } | ||
573 | |||
574 | int | ||
575 | nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p, | ||
576 | struct nfsd3_commitargs *args) | ||
577 | { | ||
578 | if (!(p = decode_fh(p, &args->fh))) | ||
579 | return 0; | ||
580 | p = xdr_decode_hyper(p, &args->offset); | ||
581 | args->count = ntohl(*p++); | ||
582 | |||
583 | return xdr_argsize_check(rqstp, p); | ||
584 | } | ||
585 | |||
586 | /* | ||
587 | * XDR encode functions | ||
588 | */ | ||
589 | /* | ||
590 | * There must be an encoding function for void results so svc_process | ||
591 | * will work properly. | ||
592 | */ | ||
593 | int | ||
594 | nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy) | ||
595 | { | ||
596 | return xdr_ressize_check(rqstp, p); | ||
597 | } | ||
598 | |||
599 | /* GETATTR */ | ||
600 | int | ||
601 | nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, | ||
602 | struct nfsd3_attrstat *resp) | ||
603 | { | ||
604 | if (resp->status == 0) | ||
605 | p = encode_fattr3(rqstp, p, &resp->fh); | ||
606 | return xdr_ressize_check(rqstp, p); | ||
607 | } | ||
608 | |||
609 | /* SETATTR, REMOVE, RMDIR */ | ||
610 | int | ||
611 | nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p, | ||
612 | struct nfsd3_attrstat *resp) | ||
613 | { | ||
614 | p = encode_wcc_data(rqstp, p, &resp->fh); | ||
615 | return xdr_ressize_check(rqstp, p); | ||
616 | } | ||
617 | |||
618 | /* LOOKUP */ | ||
619 | int | ||
620 | nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p, | ||
621 | struct nfsd3_diropres *resp) | ||
622 | { | ||
623 | if (resp->status == 0) { | ||
624 | p = encode_fh(p, &resp->fh); | ||
625 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
626 | } | ||
627 | p = encode_post_op_attr(rqstp, p, &resp->dirfh); | ||
628 | return xdr_ressize_check(rqstp, p); | ||
629 | } | ||
630 | |||
631 | /* ACCESS */ | ||
632 | int | ||
633 | nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p, | ||
634 | struct nfsd3_accessres *resp) | ||
635 | { | ||
636 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
637 | if (resp->status == 0) | ||
638 | *p++ = htonl(resp->access); | ||
639 | return xdr_ressize_check(rqstp, p); | ||
640 | } | ||
641 | |||
642 | /* READLINK */ | ||
643 | int | ||
644 | nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, | ||
645 | struct nfsd3_readlinkres *resp) | ||
646 | { | ||
647 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
648 | if (resp->status == 0) { | ||
649 | *p++ = htonl(resp->len); | ||
650 | xdr_ressize_check(rqstp, p); | ||
651 | rqstp->rq_res.page_len = resp->len; | ||
652 | if (resp->len & 3) { | ||
653 | /* need to pad the tail */ | ||
654 | rqstp->rq_restailpage = 0; | ||
655 | rqstp->rq_res.tail[0].iov_base = p; | ||
656 | *p = 0; | ||
657 | rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); | ||
658 | } | ||
659 | return 1; | ||
660 | } else | ||
661 | return xdr_ressize_check(rqstp, p); | ||
662 | } | ||
663 | |||
664 | /* READ */ | ||
665 | int | ||
666 | nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p, | ||
667 | struct nfsd3_readres *resp) | ||
668 | { | ||
669 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
670 | if (resp->status == 0) { | ||
671 | *p++ = htonl(resp->count); | ||
672 | *p++ = htonl(resp->eof); | ||
673 | *p++ = htonl(resp->count); /* xdr opaque count */ | ||
674 | xdr_ressize_check(rqstp, p); | ||
675 | /* now update rqstp->rq_res to reflect data aswell */ | ||
676 | rqstp->rq_res.page_len = resp->count; | ||
677 | if (resp->count & 3) { | ||
678 | /* need to pad the tail */ | ||
679 | rqstp->rq_restailpage = 0; | ||
680 | rqstp->rq_res.tail[0].iov_base = p; | ||
681 | *p = 0; | ||
682 | rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3); | ||
683 | } | ||
684 | return 1; | ||
685 | } else | ||
686 | return xdr_ressize_check(rqstp, p); | ||
687 | } | ||
688 | |||
689 | /* WRITE */ | ||
690 | int | ||
691 | nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p, | ||
692 | struct nfsd3_writeres *resp) | ||
693 | { | ||
694 | p = encode_wcc_data(rqstp, p, &resp->fh); | ||
695 | if (resp->status == 0) { | ||
696 | *p++ = htonl(resp->count); | ||
697 | *p++ = htonl(resp->committed); | ||
698 | *p++ = htonl(nfssvc_boot.tv_sec); | ||
699 | *p++ = htonl(nfssvc_boot.tv_usec); | ||
700 | } | ||
701 | return xdr_ressize_check(rqstp, p); | ||
702 | } | ||
703 | |||
704 | /* CREATE, MKDIR, SYMLINK, MKNOD */ | ||
705 | int | ||
706 | nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p, | ||
707 | struct nfsd3_diropres *resp) | ||
708 | { | ||
709 | if (resp->status == 0) { | ||
710 | *p++ = xdr_one; | ||
711 | p = encode_fh(p, &resp->fh); | ||
712 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
713 | } | ||
714 | p = encode_wcc_data(rqstp, p, &resp->dirfh); | ||
715 | return xdr_ressize_check(rqstp, p); | ||
716 | } | ||
717 | |||
718 | /* RENAME */ | ||
719 | int | ||
720 | nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p, | ||
721 | struct nfsd3_renameres *resp) | ||
722 | { | ||
723 | p = encode_wcc_data(rqstp, p, &resp->ffh); | ||
724 | p = encode_wcc_data(rqstp, p, &resp->tfh); | ||
725 | return xdr_ressize_check(rqstp, p); | ||
726 | } | ||
727 | |||
728 | /* LINK */ | ||
729 | int | ||
730 | nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p, | ||
731 | struct nfsd3_linkres *resp) | ||
732 | { | ||
733 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
734 | p = encode_wcc_data(rqstp, p, &resp->tfh); | ||
735 | return xdr_ressize_check(rqstp, p); | ||
736 | } | ||
737 | |||
738 | /* READDIR */ | ||
739 | int | ||
740 | nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, | ||
741 | struct nfsd3_readdirres *resp) | ||
742 | { | ||
743 | p = encode_post_op_attr(rqstp, p, &resp->fh); | ||
744 | |||
745 | if (resp->status == 0) { | ||
746 | /* stupid readdir cookie */ | ||
747 | memcpy(p, resp->verf, 8); p += 2; | ||
748 | xdr_ressize_check(rqstp, p); | ||
749 | if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE) | ||
750 | return 1; /*No room for trailer */ | ||
751 | rqstp->rq_res.page_len = (resp->count) << 2; | ||
752 | |||
753 | /* add the 'tail' to the end of the 'head' page - page 0. */ | ||
754 | rqstp->rq_restailpage = 0; | ||
755 | rqstp->rq_res.tail[0].iov_base = p; | ||
756 | *p++ = 0; /* no more entries */ | ||
757 | *p++ = htonl(resp->common.err == nfserr_eof); | ||
758 | rqstp->rq_res.tail[0].iov_len = 2<<2; | ||
759 | return 1; | ||
760 | } else | ||
761 | return xdr_ressize_check(rqstp, p); | ||
762 | } | ||
763 | |||
764 | static inline u32 * | ||
765 | encode_entry_baggage(struct nfsd3_readdirres *cd, u32 *p, const char *name, | ||
766 | int namlen, ino_t ino) | ||
767 | { | ||
768 | *p++ = xdr_one; /* mark entry present */ | ||
769 | p = xdr_encode_hyper(p, ino); /* file id */ | ||
770 | p = xdr_encode_array(p, name, namlen);/* name length & name */ | ||
771 | |||
772 | cd->offset = p; /* remember pointer */ | ||
773 | p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */ | ||
774 | |||
775 | return p; | ||
776 | } | ||
777 | |||
778 | static inline u32 * | ||
779 | encode_entryplus_baggage(struct nfsd3_readdirres *cd, u32 *p, | ||
780 | struct svc_fh *fhp) | ||
781 | { | ||
782 | p = encode_post_op_attr(cd->rqstp, p, fhp); | ||
783 | *p++ = xdr_one; /* yes, a file handle follows */ | ||
784 | p = encode_fh(p, fhp); | ||
785 | fh_put(fhp); | ||
786 | return p; | ||
787 | } | ||
788 | |||
789 | static int | ||
790 | compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, | ||
791 | const char *name, int namlen) | ||
792 | { | ||
793 | struct svc_export *exp; | ||
794 | struct dentry *dparent, *dchild; | ||
795 | int rv = 0; | ||
796 | |||
797 | dparent = cd->fh.fh_dentry; | ||
798 | exp = cd->fh.fh_export; | ||
799 | |||
800 | fh_init(fhp, NFS3_FHSIZE); | ||
801 | if (isdotent(name, namlen)) { | ||
802 | if (namlen == 2) { | ||
803 | dchild = dget_parent(dparent); | ||
804 | if (dchild == dparent) { | ||
805 | /* filesystem root - cannot return filehandle for ".." */ | ||
806 | dput(dchild); | ||
807 | return 1; | ||
808 | } | ||
809 | } else | ||
810 | dchild = dget(dparent); | ||
811 | } else | ||
812 | dchild = lookup_one_len(name, dparent, namlen); | ||
813 | if (IS_ERR(dchild)) | ||
814 | return 1; | ||
815 | if (d_mountpoint(dchild) || | ||
816 | fh_compose(fhp, exp, dchild, &cd->fh) != 0 || | ||
817 | !dchild->d_inode) | ||
818 | rv = 1; | ||
819 | dput(dchild); | ||
820 | return rv; | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * Encode a directory entry. This one works for both normal readdir | ||
825 | * and readdirplus. | ||
826 | * The normal readdir reply requires 2 (fileid) + 1 (stringlen) | ||
827 | * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen. | ||
828 | * | ||
829 | * The readdirplus baggage is 1+21 words for post_op_attr, plus the | ||
830 | * file handle. | ||
831 | */ | ||
832 | |||
833 | #define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1) | ||
834 | #define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2)) | ||
835 | static int | ||
836 | encode_entry(struct readdir_cd *ccd, const char *name, | ||
837 | int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus) | ||
838 | { | ||
839 | struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres, | ||
840 | common); | ||
841 | u32 *p = cd->buffer; | ||
842 | caddr_t curr_page_addr = NULL; | ||
843 | int pn; /* current page number */ | ||
844 | int slen; /* string (name) length */ | ||
845 | int elen; /* estimated entry length in words */ | ||
846 | int num_entry_words = 0; /* actual number of words */ | ||
847 | |||
848 | if (cd->offset) { | ||
849 | u64 offset64 = offset; | ||
850 | |||
851 | if (unlikely(cd->offset1)) { | ||
852 | /* we ended up with offset on a page boundary */ | ||
853 | *cd->offset = htonl(offset64 >> 32); | ||
854 | *cd->offset1 = htonl(offset64 & 0xffffffff); | ||
855 | cd->offset1 = NULL; | ||
856 | } else { | ||
857 | xdr_encode_hyper(cd->offset, (u64) offset); | ||
858 | } | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | dprintk("encode_entry(%.*s @%ld%s)\n", | ||
863 | namlen, name, (long) offset, plus? " plus" : ""); | ||
864 | */ | ||
865 | |||
866 | /* truncate filename if too long */ | ||
867 | if (namlen > NFS3_MAXNAMLEN) | ||
868 | namlen = NFS3_MAXNAMLEN; | ||
869 | |||
870 | slen = XDR_QUADLEN(namlen); | ||
871 | elen = slen + NFS3_ENTRY_BAGGAGE | ||
872 | + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0); | ||
873 | |||
874 | if (cd->buflen < elen) { | ||
875 | cd->common.err = nfserr_toosmall; | ||
876 | return -EINVAL; | ||
877 | } | ||
878 | |||
879 | /* determine which page in rq_respages[] we are currently filling */ | ||
880 | for (pn=1; pn < cd->rqstp->rq_resused; pn++) { | ||
881 | curr_page_addr = page_address(cd->rqstp->rq_respages[pn]); | ||
882 | |||
883 | if (((caddr_t)cd->buffer >= curr_page_addr) && | ||
884 | ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE)) | ||
885 | break; | ||
886 | } | ||
887 | |||
888 | if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) { | ||
889 | /* encode entry in current page */ | ||
890 | |||
891 | p = encode_entry_baggage(cd, p, name, namlen, ino); | ||
892 | |||
893 | /* throw in readdirplus baggage */ | ||
894 | if (plus) { | ||
895 | struct svc_fh fh; | ||
896 | |||
897 | if (compose_entry_fh(cd, &fh, name, namlen) > 0) { | ||
898 | *p++ = 0; | ||
899 | *p++ = 0; | ||
900 | } else | ||
901 | p = encode_entryplus_baggage(cd, p, &fh); | ||
902 | } | ||
903 | num_entry_words = p - cd->buffer; | ||
904 | } else if (cd->rqstp->rq_respages[pn+1] != NULL) { | ||
905 | /* temporarily encode entry into next page, then move back to | ||
906 | * current and next page in rq_respages[] */ | ||
907 | u32 *p1, *tmp; | ||
908 | int len1, len2; | ||
909 | |||
910 | /* grab next page for temporary storage of entry */ | ||
911 | p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]); | ||
912 | |||
913 | p1 = encode_entry_baggage(cd, p1, name, namlen, ino); | ||
914 | |||
915 | /* throw in readdirplus baggage */ | ||
916 | if (plus) { | ||
917 | struct svc_fh fh; | ||
918 | |||
919 | if (compose_entry_fh(cd, &fh, name, namlen) > 0) { | ||
920 | /* zero out the filehandle */ | ||
921 | *p1++ = 0; | ||
922 | *p1++ = 0; | ||
923 | } else | ||
924 | p1 = encode_entryplus_baggage(cd, p1, &fh); | ||
925 | } | ||
926 | |||
927 | /* determine entry word length and lengths to go in pages */ | ||
928 | num_entry_words = p1 - tmp; | ||
929 | len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer; | ||
930 | if ((num_entry_words << 2) < len1) { | ||
931 | /* the actual number of words in the entry is less | ||
932 | * than elen and can still fit in the current page | ||
933 | */ | ||
934 | memmove(p, tmp, num_entry_words << 2); | ||
935 | p += num_entry_words; | ||
936 | |||
937 | /* update offset */ | ||
938 | cd->offset = cd->buffer + (cd->offset - tmp); | ||
939 | } else { | ||
940 | unsigned int offset_r = (cd->offset - tmp) << 2; | ||
941 | |||
942 | /* update pointer to offset location. | ||
943 | * This is a 64bit quantity, so we need to | ||
944 | * deal with 3 cases: | ||
945 | * - entirely in first page | ||
946 | * - entirely in second page | ||
947 | * - 4 bytes in each page | ||
948 | */ | ||
949 | if (offset_r + 8 <= len1) { | ||
950 | cd->offset = p + (cd->offset - tmp); | ||
951 | } else if (offset_r >= len1) { | ||
952 | cd->offset -= len1 >> 2; | ||
953 | } else { | ||
954 | /* sitting on the fence */ | ||
955 | BUG_ON(offset_r != len1 - 4); | ||
956 | cd->offset = p + (cd->offset - tmp); | ||
957 | cd->offset1 = tmp; | ||
958 | } | ||
959 | |||
960 | len2 = (num_entry_words << 2) - len1; | ||
961 | |||
962 | /* move from temp page to current and next pages */ | ||
963 | memmove(p, tmp, len1); | ||
964 | memmove(tmp, (caddr_t)tmp+len1, len2); | ||
965 | |||
966 | p = tmp + (len2 >> 2); | ||
967 | } | ||
968 | } | ||
969 | else { | ||
970 | cd->common.err = nfserr_toosmall; | ||
971 | return -EINVAL; | ||
972 | } | ||
973 | |||
974 | cd->buflen -= num_entry_words; | ||
975 | cd->buffer = p; | ||
976 | cd->common.err = nfs_ok; | ||
977 | return 0; | ||
978 | |||
979 | } | ||
980 | |||
981 | int | ||
982 | nfs3svc_encode_entry(struct readdir_cd *cd, const char *name, | ||
983 | int namlen, loff_t offset, ino_t ino, unsigned int d_type) | ||
984 | { | ||
985 | return encode_entry(cd, name, namlen, offset, ino, d_type, 0); | ||
986 | } | ||
987 | |||
988 | int | ||
989 | nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name, | ||
990 | int namlen, loff_t offset, ino_t ino, unsigned int d_type) | ||
991 | { | ||
992 | return encode_entry(cd, name, namlen, offset, ino, d_type, 1); | ||
993 | } | ||
994 | |||
995 | /* FSSTAT */ | ||
996 | int | ||
997 | nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p, | ||
998 | struct nfsd3_fsstatres *resp) | ||
999 | { | ||
1000 | struct kstatfs *s = &resp->stats; | ||
1001 | u64 bs = s->f_bsize; | ||
1002 | |||
1003 | *p++ = xdr_zero; /* no post_op_attr */ | ||
1004 | |||
1005 | if (resp->status == 0) { | ||
1006 | p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */ | ||
1007 | p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */ | ||
1008 | p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */ | ||
1009 | p = xdr_encode_hyper(p, s->f_files); /* total inodes */ | ||
1010 | p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */ | ||
1011 | p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */ | ||
1012 | *p++ = htonl(resp->invarsec); /* mean unchanged time */ | ||
1013 | } | ||
1014 | return xdr_ressize_check(rqstp, p); | ||
1015 | } | ||
1016 | |||
1017 | /* FSINFO */ | ||
1018 | int | ||
1019 | nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p, | ||
1020 | struct nfsd3_fsinfores *resp) | ||
1021 | { | ||
1022 | *p++ = xdr_zero; /* no post_op_attr */ | ||
1023 | |||
1024 | if (resp->status == 0) { | ||
1025 | *p++ = htonl(resp->f_rtmax); | ||
1026 | *p++ = htonl(resp->f_rtpref); | ||
1027 | *p++ = htonl(resp->f_rtmult); | ||
1028 | *p++ = htonl(resp->f_wtmax); | ||
1029 | *p++ = htonl(resp->f_wtpref); | ||
1030 | *p++ = htonl(resp->f_wtmult); | ||
1031 | *p++ = htonl(resp->f_dtpref); | ||
1032 | p = xdr_encode_hyper(p, resp->f_maxfilesize); | ||
1033 | *p++ = xdr_one; | ||
1034 | *p++ = xdr_zero; | ||
1035 | *p++ = htonl(resp->f_properties); | ||
1036 | } | ||
1037 | |||
1038 | return xdr_ressize_check(rqstp, p); | ||
1039 | } | ||
1040 | |||
1041 | /* PATHCONF */ | ||
1042 | int | ||
1043 | nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p, | ||
1044 | struct nfsd3_pathconfres *resp) | ||
1045 | { | ||
1046 | *p++ = xdr_zero; /* no post_op_attr */ | ||
1047 | |||
1048 | if (resp->status == 0) { | ||
1049 | *p++ = htonl(resp->p_link_max); | ||
1050 | *p++ = htonl(resp->p_name_max); | ||
1051 | *p++ = htonl(resp->p_no_trunc); | ||
1052 | *p++ = htonl(resp->p_chown_restricted); | ||
1053 | *p++ = htonl(resp->p_case_insensitive); | ||
1054 | *p++ = htonl(resp->p_case_preserving); | ||
1055 | } | ||
1056 | |||
1057 | return xdr_ressize_check(rqstp, p); | ||
1058 | } | ||
1059 | |||
1060 | /* COMMIT */ | ||
1061 | int | ||
1062 | nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p, | ||
1063 | struct nfsd3_commitres *resp) | ||
1064 | { | ||
1065 | p = encode_wcc_data(rqstp, p, &resp->fh); | ||
1066 | /* Write verifier */ | ||
1067 | if (resp->status == 0) { | ||
1068 | *p++ = htonl(nfssvc_boot.tv_sec); | ||
1069 | *p++ = htonl(nfssvc_boot.tv_usec); | ||
1070 | } | ||
1071 | return xdr_ressize_check(rqstp, p); | ||
1072 | } | ||
1073 | |||
1074 | /* | ||
1075 | * XDR release functions | ||
1076 | */ | ||
1077 | int | ||
1078 | nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p, | ||
1079 | struct nfsd3_attrstat *resp) | ||
1080 | { | ||
1081 | fh_put(&resp->fh); | ||
1082 | return 1; | ||
1083 | } | ||
1084 | |||
1085 | int | ||
1086 | nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p, | ||
1087 | struct nfsd3_fhandle_pair *resp) | ||
1088 | { | ||
1089 | fh_put(&resp->fh1); | ||
1090 | fh_put(&resp->fh2); | ||
1091 | return 1; | ||
1092 | } | ||
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c new file mode 100644 index 000000000000..11ebf6c4aa54 --- /dev/null +++ b/fs/nfsd/nfs4acl.c | |||
@@ -0,0 +1,954 @@ | |||
1 | /* | ||
2 | * fs/nfs4acl/acl.c | ||
3 | * | ||
4 | * Common NFSv4 ACL handling code. | ||
5 | * | ||
6 | * Copyright (c) 2002, 2003 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Marius Aamodt Eriksen <marius@umich.edu> | ||
10 | * Jeff Sedlak <jsedlak@umich.edu> | ||
11 | * J. Bruce Fields <bfields@umich.edu> | ||
12 | * | ||
13 | * Redistribution and use in source and binary forms, with or without | ||
14 | * modification, are permitted provided that the following conditions | ||
15 | * are met: | ||
16 | * | ||
17 | * 1. Redistributions of source code must retain the above copyright | ||
18 | * notice, this list of conditions and the following disclaimer. | ||
19 | * 2. Redistributions in binary form must reproduce the above copyright | ||
20 | * notice, this list of conditions and the following disclaimer in the | ||
21 | * documentation and/or other materials provided with the distribution. | ||
22 | * 3. Neither the name of the University nor the names of its | ||
23 | * contributors may be used to endorse or promote products derived | ||
24 | * from this software without specific prior written permission. | ||
25 | * | ||
26 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
28 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
29 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
33 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
34 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
35 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
36 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
37 | */ | ||
38 | |||
39 | #include <linux/string.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/list.h> | ||
42 | #include <linux/types.h> | ||
43 | #include <linux/fs.h> | ||
44 | #include <linux/module.h> | ||
45 | #include <linux/nfs_fs.h> | ||
46 | #include <linux/posix_acl.h> | ||
47 | #include <linux/nfs4.h> | ||
48 | #include <linux/nfs4_acl.h> | ||
49 | |||
50 | |||
51 | /* mode bit translations: */ | ||
52 | #define NFS4_READ_MODE (NFS4_ACE_READ_DATA) | ||
53 | #define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA) | ||
54 | #define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE | ||
55 | #define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE) | ||
56 | #define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL) | ||
57 | |||
58 | /* We don't support these bits; insist they be neither allowed nor denied */ | ||
59 | #define NFS4_MASK_UNSUPP (NFS4_ACE_DELETE | NFS4_ACE_WRITE_OWNER \ | ||
60 | | NFS4_ACE_READ_NAMED_ATTRS | NFS4_ACE_WRITE_NAMED_ATTRS) | ||
61 | |||
62 | /* flags used to simulate posix default ACLs */ | ||
63 | #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | ||
64 | | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE) | ||
65 | |||
66 | #define MASK_EQUAL(mask1, mask2) \ | ||
67 | ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) | ||
68 | |||
69 | static u32 | ||
70 | mask_from_posix(unsigned short perm, unsigned int flags) | ||
71 | { | ||
72 | int mask = NFS4_ANYONE_MODE; | ||
73 | |||
74 | if (flags & NFS4_ACL_OWNER) | ||
75 | mask |= NFS4_OWNER_MODE; | ||
76 | if (perm & ACL_READ) | ||
77 | mask |= NFS4_READ_MODE; | ||
78 | if (perm & ACL_WRITE) | ||
79 | mask |= NFS4_WRITE_MODE; | ||
80 | if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR)) | ||
81 | mask |= NFS4_ACE_DELETE_CHILD; | ||
82 | if (perm & ACL_EXECUTE) | ||
83 | mask |= NFS4_EXECUTE_MODE; | ||
84 | return mask; | ||
85 | } | ||
86 | |||
87 | static u32 | ||
88 | deny_mask(u32 allow_mask, unsigned int flags) | ||
89 | { | ||
90 | u32 ret = ~allow_mask & ~NFS4_MASK_UNSUPP; | ||
91 | if (!(flags & NFS4_ACL_DIR)) | ||
92 | ret &= ~NFS4_ACE_DELETE_CHILD; | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | /* XXX: modify functions to return NFS errors; they're only ever | ||
97 | * used by nfs code, after all.... */ | ||
98 | |||
99 | static int | ||
100 | mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) | ||
101 | { | ||
102 | u32 ignore = 0; | ||
103 | |||
104 | if (!(flags & NFS4_ACL_DIR)) | ||
105 | ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */ | ||
106 | perm |= ignore; | ||
107 | *mode = 0; | ||
108 | if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE) | ||
109 | *mode |= ACL_READ; | ||
110 | if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE) | ||
111 | *mode |= ACL_WRITE; | ||
112 | if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE) | ||
113 | *mode |= ACL_EXECUTE; | ||
114 | if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags))) | ||
115 | return -EINVAL; | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | struct ace_container { | ||
120 | struct nfs4_ace *ace; | ||
121 | struct list_head ace_l; | ||
122 | }; | ||
123 | |||
124 | static short ace2type(struct nfs4_ace *); | ||
125 | static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); | ||
126 | static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int); | ||
127 | int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t); | ||
128 | int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *); | ||
129 | |||
130 | struct nfs4_acl * | ||
131 | nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, | ||
132 | unsigned int flags) | ||
133 | { | ||
134 | struct nfs4_acl *acl; | ||
135 | int error = -EINVAL; | ||
136 | |||
137 | if ((pacl != NULL && | ||
138 | (posix_acl_valid(pacl) < 0 || pacl->a_count == 0)) || | ||
139 | (dpacl != NULL && | ||
140 | (posix_acl_valid(dpacl) < 0 || dpacl->a_count == 0))) | ||
141 | goto out_err; | ||
142 | |||
143 | acl = nfs4_acl_new(); | ||
144 | if (acl == NULL) { | ||
145 | error = -ENOMEM; | ||
146 | goto out_err; | ||
147 | } | ||
148 | |||
149 | if (pacl != NULL) { | ||
150 | error = _posix_to_nfsv4_one(pacl, acl, | ||
151 | flags & ~NFS4_ACL_TYPE_DEFAULT); | ||
152 | if (error < 0) | ||
153 | goto out_acl; | ||
154 | } | ||
155 | |||
156 | if (dpacl != NULL) { | ||
157 | error = _posix_to_nfsv4_one(dpacl, acl, | ||
158 | flags | NFS4_ACL_TYPE_DEFAULT); | ||
159 | if (error < 0) | ||
160 | goto out_acl; | ||
161 | } | ||
162 | |||
163 | return acl; | ||
164 | |||
165 | out_acl: | ||
166 | nfs4_acl_free(acl); | ||
167 | out_err: | ||
168 | acl = ERR_PTR(error); | ||
169 | |||
170 | return acl; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype, | ||
175 | uid_t owner, unsigned int flags) | ||
176 | { | ||
177 | int error; | ||
178 | |||
179 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | ||
180 | eflag, mask, whotype, owner); | ||
181 | if (error < 0) | ||
182 | return error; | ||
183 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
184 | eflag, deny_mask(mask, flags), whotype, owner); | ||
185 | return error; | ||
186 | } | ||
187 | |||
188 | /* We assume the acl has been verified with posix_acl_valid. */ | ||
189 | static int | ||
190 | _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, | ||
191 | unsigned int flags) | ||
192 | { | ||
193 | struct posix_acl_entry *pa, *pe, *group_owner_entry; | ||
194 | int error = -EINVAL; | ||
195 | u32 mask, mask_mask; | ||
196 | int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ? | ||
197 | NFS4_INHERITANCE_FLAGS : 0); | ||
198 | |||
199 | BUG_ON(pacl->a_count < 3); | ||
200 | pe = pacl->a_entries + pacl->a_count; | ||
201 | pa = pe - 2; /* if mask entry exists, it's second from the last. */ | ||
202 | if (pa->e_tag == ACL_MASK) | ||
203 | mask_mask = deny_mask(mask_from_posix(pa->e_perm, flags), flags); | ||
204 | else | ||
205 | mask_mask = 0; | ||
206 | |||
207 | pa = pacl->a_entries; | ||
208 | BUG_ON(pa->e_tag != ACL_USER_OBJ); | ||
209 | mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER); | ||
210 | error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_OWNER, 0, flags); | ||
211 | if (error < 0) | ||
212 | goto out; | ||
213 | pa++; | ||
214 | |||
215 | while (pa->e_tag == ACL_USER) { | ||
216 | mask = mask_from_posix(pa->e_perm, flags); | ||
217 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
218 | eflag, mask_mask, NFS4_ACL_WHO_NAMED, pa->e_id); | ||
219 | if (error < 0) | ||
220 | goto out; | ||
221 | |||
222 | |||
223 | error = nfs4_acl_add_pair(acl, eflag, mask, | ||
224 | NFS4_ACL_WHO_NAMED, pa->e_id, flags); | ||
225 | if (error < 0) | ||
226 | goto out; | ||
227 | pa++; | ||
228 | } | ||
229 | |||
230 | /* In the case of groups, we apply allow ACEs first, then deny ACEs, | ||
231 | * since a user can be in more than one group. */ | ||
232 | |||
233 | /* allow ACEs */ | ||
234 | |||
235 | if (pacl->a_count > 3) { | ||
236 | BUG_ON(pa->e_tag != ACL_GROUP_OBJ); | ||
237 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
238 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask, | ||
239 | NFS4_ACL_WHO_GROUP, 0); | ||
240 | if (error < 0) | ||
241 | goto out; | ||
242 | } | ||
243 | group_owner_entry = pa; | ||
244 | mask = mask_from_posix(pa->e_perm, flags); | ||
245 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | ||
246 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | ||
247 | NFS4_ACL_WHO_GROUP, 0); | ||
248 | if (error < 0) | ||
249 | goto out; | ||
250 | pa++; | ||
251 | |||
252 | while (pa->e_tag == ACL_GROUP) { | ||
253 | mask = mask_from_posix(pa->e_perm, flags); | ||
254 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
255 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask, | ||
256 | NFS4_ACL_WHO_NAMED, pa->e_id); | ||
257 | if (error < 0) | ||
258 | goto out; | ||
259 | |||
260 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | ||
261 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | ||
262 | NFS4_ACL_WHO_NAMED, pa->e_id); | ||
263 | if (error < 0) | ||
264 | goto out; | ||
265 | pa++; | ||
266 | } | ||
267 | |||
268 | /* deny ACEs */ | ||
269 | |||
270 | pa = group_owner_entry; | ||
271 | mask = mask_from_posix(pa->e_perm, flags); | ||
272 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
273 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | ||
274 | deny_mask(mask, flags), NFS4_ACL_WHO_GROUP, 0); | ||
275 | if (error < 0) | ||
276 | goto out; | ||
277 | pa++; | ||
278 | while (pa->e_tag == ACL_GROUP) { | ||
279 | mask = mask_from_posix(pa->e_perm, flags); | ||
280 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | ||
281 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | ||
282 | deny_mask(mask, flags), NFS4_ACL_WHO_NAMED, pa->e_id); | ||
283 | if (error < 0) | ||
284 | goto out; | ||
285 | pa++; | ||
286 | } | ||
287 | |||
288 | if (pa->e_tag == ACL_MASK) | ||
289 | pa++; | ||
290 | BUG_ON(pa->e_tag != ACL_OTHER); | ||
291 | mask = mask_from_posix(pa->e_perm, flags); | ||
292 | error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_EVERYONE, 0, flags); | ||
293 | |||
294 | out: | ||
295 | return error; | ||
296 | } | ||
297 | |||
298 | static void | ||
299 | sort_pacl_range(struct posix_acl *pacl, int start, int end) { | ||
300 | int sorted = 0, i; | ||
301 | struct posix_acl_entry tmp; | ||
302 | |||
303 | /* We just do a bubble sort; easy to do in place, and we're not | ||
304 | * expecting acl's to be long enough to justify anything more. */ | ||
305 | while (!sorted) { | ||
306 | sorted = 1; | ||
307 | for (i = start; i < end; i++) { | ||
308 | if (pacl->a_entries[i].e_id | ||
309 | > pacl->a_entries[i+1].e_id) { | ||
310 | sorted = 0; | ||
311 | tmp = pacl->a_entries[i]; | ||
312 | pacl->a_entries[i] = pacl->a_entries[i+1]; | ||
313 | pacl->a_entries[i+1] = tmp; | ||
314 | } | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | |||
319 | static void | ||
320 | sort_pacl(struct posix_acl *pacl) | ||
321 | { | ||
322 | /* posix_acl_valid requires that users and groups be in order | ||
323 | * by uid/gid. */ | ||
324 | int i, j; | ||
325 | |||
326 | if (pacl->a_count <= 4) | ||
327 | return; /* no users or groups */ | ||
328 | i = 1; | ||
329 | while (pacl->a_entries[i].e_tag == ACL_USER) | ||
330 | i++; | ||
331 | sort_pacl_range(pacl, 1, i-1); | ||
332 | |||
333 | BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ); | ||
334 | j = i++; | ||
335 | while (pacl->a_entries[j].e_tag == ACL_GROUP) | ||
336 | j++; | ||
337 | sort_pacl_range(pacl, i, j-1); | ||
338 | return; | ||
339 | } | ||
340 | |||
341 | static int | ||
342 | write_pace(struct nfs4_ace *ace, struct posix_acl *pacl, | ||
343 | struct posix_acl_entry **pace, short tag, unsigned int flags) | ||
344 | { | ||
345 | struct posix_acl_entry *this = *pace; | ||
346 | |||
347 | if (*pace == pacl->a_entries + pacl->a_count) | ||
348 | return -EINVAL; /* fell off the end */ | ||
349 | (*pace)++; | ||
350 | this->e_tag = tag; | ||
351 | if (tag == ACL_USER_OBJ) | ||
352 | flags |= NFS4_ACL_OWNER; | ||
353 | if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags)) | ||
354 | return -EINVAL; | ||
355 | this->e_id = (tag == ACL_USER || tag == ACL_GROUP ? | ||
356 | ace->who : ACL_UNDEFINED_ID); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static struct nfs4_ace * | ||
361 | get_next_v4_ace(struct list_head **p, struct list_head *head) | ||
362 | { | ||
363 | struct nfs4_ace *ace; | ||
364 | |||
365 | *p = (*p)->next; | ||
366 | if (*p == head) | ||
367 | return NULL; | ||
368 | ace = list_entry(*p, struct nfs4_ace, l_ace); | ||
369 | |||
370 | return ace; | ||
371 | } | ||
372 | |||
373 | int | ||
374 | nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, | ||
375 | struct posix_acl **dpacl, unsigned int flags) | ||
376 | { | ||
377 | struct nfs4_acl *dacl; | ||
378 | int error = -ENOMEM; | ||
379 | |||
380 | *pacl = NULL; | ||
381 | *dpacl = NULL; | ||
382 | |||
383 | dacl = nfs4_acl_new(); | ||
384 | if (dacl == NULL) | ||
385 | goto out; | ||
386 | |||
387 | error = nfs4_acl_split(acl, dacl); | ||
388 | if (error < 0) | ||
389 | goto out_acl; | ||
390 | |||
391 | if (pacl != NULL) { | ||
392 | if (acl->naces == 0) { | ||
393 | error = -ENODATA; | ||
394 | goto try_dpacl; | ||
395 | } | ||
396 | |||
397 | *pacl = _nfsv4_to_posix_one(acl, flags); | ||
398 | if (IS_ERR(*pacl)) { | ||
399 | error = PTR_ERR(*pacl); | ||
400 | *pacl = NULL; | ||
401 | goto out_acl; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | try_dpacl: | ||
406 | if (dpacl != NULL) { | ||
407 | if (dacl->naces == 0) { | ||
408 | if (pacl == NULL || *pacl == NULL) | ||
409 | error = -ENODATA; | ||
410 | goto out_acl; | ||
411 | } | ||
412 | |||
413 | error = 0; | ||
414 | *dpacl = _nfsv4_to_posix_one(dacl, flags); | ||
415 | if (IS_ERR(*dpacl)) { | ||
416 | error = PTR_ERR(*dpacl); | ||
417 | *dpacl = NULL; | ||
418 | goto out_acl; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | out_acl: | ||
423 | if (error && pacl) { | ||
424 | posix_acl_release(*pacl); | ||
425 | *pacl = NULL; | ||
426 | } | ||
427 | nfs4_acl_free(dacl); | ||
428 | out: | ||
429 | return error; | ||
430 | } | ||
431 | |||
432 | static int | ||
433 | same_who(struct nfs4_ace *a, struct nfs4_ace *b) | ||
434 | { | ||
435 | return a->whotype == b->whotype && | ||
436 | (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who); | ||
437 | } | ||
438 | |||
439 | static int | ||
440 | complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny, | ||
441 | unsigned int flags) | ||
442 | { | ||
443 | int ignore = 0; | ||
444 | if (!(flags & NFS4_ACL_DIR)) | ||
445 | ignore |= NFS4_ACE_DELETE_CHILD; | ||
446 | return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags), | ||
447 | ignore|deny->access_mask) && | ||
448 | allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && | ||
449 | deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE && | ||
450 | allow->flag == deny->flag && | ||
451 | same_who(allow, deny); | ||
452 | } | ||
453 | |||
454 | static inline int | ||
455 | user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | ||
456 | struct posix_acl *pacl, struct posix_acl_entry **pace, | ||
457 | unsigned int flags) | ||
458 | { | ||
459 | int error = -EINVAL; | ||
460 | struct nfs4_ace *ace, *ace2; | ||
461 | |||
462 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
463 | if (ace == NULL) | ||
464 | goto out; | ||
465 | if (ace2type(ace) != ACL_USER_OBJ) | ||
466 | goto out; | ||
467 | error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags); | ||
468 | if (error < 0) | ||
469 | goto out; | ||
470 | error = -EINVAL; | ||
471 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | ||
472 | if (ace2 == NULL) | ||
473 | goto out; | ||
474 | if (!complementary_ace_pair(ace, ace2, flags)) | ||
475 | goto out; | ||
476 | error = 0; | ||
477 | out: | ||
478 | return error; | ||
479 | } | ||
480 | |||
481 | static inline int | ||
482 | users_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | ||
483 | struct nfs4_ace **mask_ace, | ||
484 | struct posix_acl *pacl, struct posix_acl_entry **pace, | ||
485 | unsigned int flags) | ||
486 | { | ||
487 | int error = -EINVAL; | ||
488 | struct nfs4_ace *ace, *ace2; | ||
489 | |||
490 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
491 | if (ace == NULL) | ||
492 | goto out; | ||
493 | while (ace2type(ace) == ACL_USER) { | ||
494 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) | ||
495 | goto out; | ||
496 | if (*mask_ace && | ||
497 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | ||
498 | goto out; | ||
499 | *mask_ace = ace; | ||
500 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
501 | if (ace == NULL) | ||
502 | goto out; | ||
503 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | ||
504 | goto out; | ||
505 | error = write_pace(ace, pacl, pace, ACL_USER, flags); | ||
506 | if (error < 0) | ||
507 | goto out; | ||
508 | error = -EINVAL; | ||
509 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | ||
510 | if (ace2 == NULL) | ||
511 | goto out; | ||
512 | if (!complementary_ace_pair(ace, ace2, flags)) | ||
513 | goto out; | ||
514 | if ((*mask_ace)->flag != ace2->flag || | ||
515 | !same_who(*mask_ace, ace2)) | ||
516 | goto out; | ||
517 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
518 | if (ace == NULL) | ||
519 | goto out; | ||
520 | } | ||
521 | error = 0; | ||
522 | out: | ||
523 | return error; | ||
524 | } | ||
525 | |||
526 | static inline int | ||
527 | group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | ||
528 | struct nfs4_ace **mask_ace, | ||
529 | struct posix_acl *pacl, struct posix_acl_entry **pace, | ||
530 | unsigned int flags) | ||
531 | { | ||
532 | int error = -EINVAL; | ||
533 | struct nfs4_ace *ace, *ace2; | ||
534 | struct ace_container *ac; | ||
535 | struct list_head group_l; | ||
536 | |||
537 | INIT_LIST_HEAD(&group_l); | ||
538 | ace = list_entry(*p, struct nfs4_ace, l_ace); | ||
539 | |||
540 | /* group owner (mask and allow aces) */ | ||
541 | |||
542 | if (pacl->a_count != 3) { | ||
543 | /* then the group owner should be preceded by mask */ | ||
544 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) | ||
545 | goto out; | ||
546 | if (*mask_ace && | ||
547 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | ||
548 | goto out; | ||
549 | *mask_ace = ace; | ||
550 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
551 | if (ace == NULL) | ||
552 | goto out; | ||
553 | |||
554 | if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace)) | ||
555 | goto out; | ||
556 | } | ||
557 | |||
558 | if (ace2type(ace) != ACL_GROUP_OBJ) | ||
559 | goto out; | ||
560 | |||
561 | ac = kmalloc(sizeof(*ac), GFP_KERNEL); | ||
562 | error = -ENOMEM; | ||
563 | if (ac == NULL) | ||
564 | goto out; | ||
565 | ac->ace = ace; | ||
566 | list_add_tail(&ac->ace_l, &group_l); | ||
567 | |||
568 | error = -EINVAL; | ||
569 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | ||
570 | goto out; | ||
571 | |||
572 | error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags); | ||
573 | if (error < 0) | ||
574 | goto out; | ||
575 | |||
576 | error = -EINVAL; | ||
577 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
578 | if (ace == NULL) | ||
579 | goto out; | ||
580 | |||
581 | /* groups (mask and allow aces) */ | ||
582 | |||
583 | while (ace2type(ace) == ACL_GROUP) { | ||
584 | if (*mask_ace == NULL) | ||
585 | goto out; | ||
586 | |||
587 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE || | ||
588 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | ||
589 | goto out; | ||
590 | *mask_ace = ace; | ||
591 | |||
592 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
593 | if (ace == NULL) | ||
594 | goto out; | ||
595 | ac = kmalloc(sizeof(*ac), GFP_KERNEL); | ||
596 | error = -ENOMEM; | ||
597 | if (ac == NULL) | ||
598 | goto out; | ||
599 | error = -EINVAL; | ||
600 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE || | ||
601 | !same_who(ace, *mask_ace)) | ||
602 | goto out; | ||
603 | |||
604 | ac->ace = ace; | ||
605 | list_add_tail(&ac->ace_l, &group_l); | ||
606 | |||
607 | error = write_pace(ace, pacl, pace, ACL_GROUP, flags); | ||
608 | if (error < 0) | ||
609 | goto out; | ||
610 | error = -EINVAL; | ||
611 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
612 | if (ace == NULL) | ||
613 | goto out; | ||
614 | } | ||
615 | |||
616 | /* group owner (deny ace) */ | ||
617 | |||
618 | if (ace2type(ace) != ACL_GROUP_OBJ) | ||
619 | goto out; | ||
620 | ac = list_entry(group_l.next, struct ace_container, ace_l); | ||
621 | ace2 = ac->ace; | ||
622 | if (!complementary_ace_pair(ace2, ace, flags)) | ||
623 | goto out; | ||
624 | list_del(group_l.next); | ||
625 | kfree(ac); | ||
626 | |||
627 | /* groups (deny aces) */ | ||
628 | |||
629 | while (!list_empty(&group_l)) { | ||
630 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
631 | if (ace == NULL) | ||
632 | goto out; | ||
633 | if (ace2type(ace) != ACL_GROUP) | ||
634 | goto out; | ||
635 | ac = list_entry(group_l.next, struct ace_container, ace_l); | ||
636 | ace2 = ac->ace; | ||
637 | if (!complementary_ace_pair(ace2, ace, flags)) | ||
638 | goto out; | ||
639 | list_del(group_l.next); | ||
640 | kfree(ac); | ||
641 | } | ||
642 | |||
643 | ace = get_next_v4_ace(p, &n4acl->ace_head); | ||
644 | if (ace == NULL) | ||
645 | goto out; | ||
646 | if (ace2type(ace) != ACL_OTHER) | ||
647 | goto out; | ||
648 | error = 0; | ||
649 | out: | ||
650 | while (!list_empty(&group_l)) { | ||
651 | ac = list_entry(group_l.next, struct ace_container, ace_l); | ||
652 | list_del(group_l.next); | ||
653 | kfree(ac); | ||
654 | } | ||
655 | return error; | ||
656 | } | ||
657 | |||
658 | static inline int | ||
659 | mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | ||
660 | struct nfs4_ace **mask_ace, | ||
661 | struct posix_acl *pacl, struct posix_acl_entry **pace, | ||
662 | unsigned int flags) | ||
663 | { | ||
664 | int error = -EINVAL; | ||
665 | struct nfs4_ace *ace; | ||
666 | |||
667 | ace = list_entry(*p, struct nfs4_ace, l_ace); | ||
668 | if (pacl->a_count != 3) { | ||
669 | if (*mask_ace == NULL) | ||
670 | goto out; | ||
671 | (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags); | ||
672 | write_pace(*mask_ace, pacl, pace, ACL_MASK, flags); | ||
673 | } | ||
674 | error = 0; | ||
675 | out: | ||
676 | return error; | ||
677 | } | ||
678 | |||
679 | static inline int | ||
680 | other_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | ||
681 | struct posix_acl *pacl, struct posix_acl_entry **pace, | ||
682 | unsigned int flags) | ||
683 | { | ||
684 | int error = -EINVAL; | ||
685 | struct nfs4_ace *ace, *ace2; | ||
686 | |||
687 | ace = list_entry(*p, struct nfs4_ace, l_ace); | ||
688 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | ||
689 | goto out; | ||
690 | error = write_pace(ace, pacl, pace, ACL_OTHER, flags); | ||
691 | if (error < 0) | ||
692 | goto out; | ||
693 | error = -EINVAL; | ||
694 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | ||
695 | if (ace2 == NULL) | ||
696 | goto out; | ||
697 | if (!complementary_ace_pair(ace, ace2, flags)) | ||
698 | goto out; | ||
699 | error = 0; | ||
700 | out: | ||
701 | return error; | ||
702 | } | ||
703 | |||
704 | static int | ||
705 | calculate_posix_ace_count(struct nfs4_acl *n4acl) | ||
706 | { | ||
707 | if (n4acl->naces == 6) /* owner, owner group, and other only */ | ||
708 | return 3; | ||
709 | else { /* Otherwise there must be a mask entry. */ | ||
710 | /* Also, the remaining entries are for named users and | ||
711 | * groups, and come in threes (mask, allow, deny): */ | ||
712 | if (n4acl->naces < 7) | ||
713 | return -1; | ||
714 | if ((n4acl->naces - 7) % 3) | ||
715 | return -1; | ||
716 | return 4 + (n4acl->naces - 7)/3; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | |||
721 | static struct posix_acl * | ||
722 | _nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags) | ||
723 | { | ||
724 | struct posix_acl *pacl; | ||
725 | int error = -EINVAL, nace = 0; | ||
726 | struct list_head *p; | ||
727 | struct nfs4_ace *mask_ace = NULL; | ||
728 | struct posix_acl_entry *pace; | ||
729 | |||
730 | nace = calculate_posix_ace_count(n4acl); | ||
731 | if (nace < 0) | ||
732 | goto out_err; | ||
733 | |||
734 | pacl = posix_acl_alloc(nace, GFP_KERNEL); | ||
735 | error = -ENOMEM; | ||
736 | if (pacl == NULL) | ||
737 | goto out_err; | ||
738 | |||
739 | pace = &pacl->a_entries[0]; | ||
740 | p = &n4acl->ace_head; | ||
741 | |||
742 | error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags); | ||
743 | if (error) | ||
744 | goto out_acl; | ||
745 | |||
746 | error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); | ||
747 | if (error) | ||
748 | goto out_acl; | ||
749 | |||
750 | error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace, | ||
751 | flags); | ||
752 | if (error) | ||
753 | goto out_acl; | ||
754 | |||
755 | error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); | ||
756 | if (error) | ||
757 | goto out_acl; | ||
758 | error = other_from_v4(n4acl, &p, pacl, &pace, flags); | ||
759 | if (error) | ||
760 | goto out_acl; | ||
761 | |||
762 | error = -EINVAL; | ||
763 | if (p->next != &n4acl->ace_head) | ||
764 | goto out_acl; | ||
765 | if (pace != pacl->a_entries + pacl->a_count) | ||
766 | goto out_acl; | ||
767 | |||
768 | sort_pacl(pacl); | ||
769 | |||
770 | return pacl; | ||
771 | out_acl: | ||
772 | posix_acl_release(pacl); | ||
773 | out_err: | ||
774 | pacl = ERR_PTR(error); | ||
775 | return pacl; | ||
776 | } | ||
777 | |||
778 | int | ||
779 | nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl) | ||
780 | { | ||
781 | struct list_head *h, *n; | ||
782 | struct nfs4_ace *ace; | ||
783 | int error = 0; | ||
784 | |||
785 | list_for_each_safe(h, n, &acl->ace_head) { | ||
786 | ace = list_entry(h, struct nfs4_ace, l_ace); | ||
787 | |||
788 | if ((ace->flag & NFS4_INHERITANCE_FLAGS) | ||
789 | != NFS4_INHERITANCE_FLAGS) | ||
790 | continue; | ||
791 | |||
792 | error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, | ||
793 | ace->access_mask, ace->whotype, ace->who) == -1; | ||
794 | if (error < 0) | ||
795 | goto out; | ||
796 | |||
797 | list_del(h); | ||
798 | kfree(ace); | ||
799 | acl->naces--; | ||
800 | } | ||
801 | |||
802 | out: | ||
803 | return error; | ||
804 | } | ||
805 | |||
806 | static short | ||
807 | ace2type(struct nfs4_ace *ace) | ||
808 | { | ||
809 | switch (ace->whotype) { | ||
810 | case NFS4_ACL_WHO_NAMED: | ||
811 | return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ? | ||
812 | ACL_GROUP : ACL_USER); | ||
813 | case NFS4_ACL_WHO_OWNER: | ||
814 | return ACL_USER_OBJ; | ||
815 | case NFS4_ACL_WHO_GROUP: | ||
816 | return ACL_GROUP_OBJ; | ||
817 | case NFS4_ACL_WHO_EVERYONE: | ||
818 | return ACL_OTHER; | ||
819 | } | ||
820 | BUG(); | ||
821 | return -1; | ||
822 | } | ||
823 | |||
824 | EXPORT_SYMBOL(nfs4_acl_posix_to_nfsv4); | ||
825 | EXPORT_SYMBOL(nfs4_acl_nfsv4_to_posix); | ||
826 | |||
827 | struct nfs4_acl * | ||
828 | nfs4_acl_new(void) | ||
829 | { | ||
830 | struct nfs4_acl *acl; | ||
831 | |||
832 | if ((acl = kmalloc(sizeof(*acl), GFP_KERNEL)) == NULL) | ||
833 | return NULL; | ||
834 | |||
835 | acl->naces = 0; | ||
836 | INIT_LIST_HEAD(&acl->ace_head); | ||
837 | |||
838 | return acl; | ||
839 | } | ||
840 | |||
841 | void | ||
842 | nfs4_acl_free(struct nfs4_acl *acl) | ||
843 | { | ||
844 | struct list_head *h; | ||
845 | struct nfs4_ace *ace; | ||
846 | |||
847 | if (!acl) | ||
848 | return; | ||
849 | |||
850 | while (!list_empty(&acl->ace_head)) { | ||
851 | h = acl->ace_head.next; | ||
852 | list_del(h); | ||
853 | ace = list_entry(h, struct nfs4_ace, l_ace); | ||
854 | kfree(ace); | ||
855 | } | ||
856 | |||
857 | kfree(acl); | ||
858 | |||
859 | return; | ||
860 | } | ||
861 | |||
862 | int | ||
863 | nfs4_acl_add_ace(struct nfs4_acl *acl, u32 type, u32 flag, u32 access_mask, | ||
864 | int whotype, uid_t who) | ||
865 | { | ||
866 | struct nfs4_ace *ace; | ||
867 | |||
868 | if ((ace = kmalloc(sizeof(*ace), GFP_KERNEL)) == NULL) | ||
869 | return -1; | ||
870 | |||
871 | ace->type = type; | ||
872 | ace->flag = flag; | ||
873 | ace->access_mask = access_mask; | ||
874 | ace->whotype = whotype; | ||
875 | ace->who = who; | ||
876 | |||
877 | list_add_tail(&ace->l_ace, &acl->ace_head); | ||
878 | acl->naces++; | ||
879 | |||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static struct { | ||
884 | char *string; | ||
885 | int stringlen; | ||
886 | int type; | ||
887 | } s2t_map[] = { | ||
888 | { | ||
889 | .string = "OWNER@", | ||
890 | .stringlen = sizeof("OWNER@") - 1, | ||
891 | .type = NFS4_ACL_WHO_OWNER, | ||
892 | }, | ||
893 | { | ||
894 | .string = "GROUP@", | ||
895 | .stringlen = sizeof("GROUP@") - 1, | ||
896 | .type = NFS4_ACL_WHO_GROUP, | ||
897 | }, | ||
898 | { | ||
899 | .string = "EVERYONE@", | ||
900 | .stringlen = sizeof("EVERYONE@") - 1, | ||
901 | .type = NFS4_ACL_WHO_EVERYONE, | ||
902 | }, | ||
903 | }; | ||
904 | |||
905 | int | ||
906 | nfs4_acl_get_whotype(char *p, u32 len) | ||
907 | { | ||
908 | int i; | ||
909 | |||
910 | for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) { | ||
911 | if (s2t_map[i].stringlen == len && | ||
912 | 0 == memcmp(s2t_map[i].string, p, len)) | ||
913 | return s2t_map[i].type; | ||
914 | } | ||
915 | return NFS4_ACL_WHO_NAMED; | ||
916 | } | ||
917 | |||
918 | int | ||
919 | nfs4_acl_write_who(int who, char *p) | ||
920 | { | ||
921 | int i; | ||
922 | |||
923 | for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) { | ||
924 | if (s2t_map[i].type == who) { | ||
925 | memcpy(p, s2t_map[i].string, s2t_map[i].stringlen); | ||
926 | return s2t_map[i].stringlen; | ||
927 | } | ||
928 | } | ||
929 | BUG(); | ||
930 | return -1; | ||
931 | } | ||
932 | |||
933 | static inline int | ||
934 | match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who) | ||
935 | { | ||
936 | switch (ace->whotype) { | ||
937 | case NFS4_ACL_WHO_NAMED: | ||
938 | return who == ace->who; | ||
939 | case NFS4_ACL_WHO_OWNER: | ||
940 | return who == owner; | ||
941 | case NFS4_ACL_WHO_GROUP: | ||
942 | return who == group; | ||
943 | case NFS4_ACL_WHO_EVERYONE: | ||
944 | return 1; | ||
945 | default: | ||
946 | return 0; | ||
947 | } | ||
948 | } | ||
949 | |||
950 | EXPORT_SYMBOL(nfs4_acl_new); | ||
951 | EXPORT_SYMBOL(nfs4_acl_free); | ||
952 | EXPORT_SYMBOL(nfs4_acl_add_ace); | ||
953 | EXPORT_SYMBOL(nfs4_acl_get_whotype); | ||
954 | EXPORT_SYMBOL(nfs4_acl_write_who); | ||
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c new file mode 100644 index 000000000000..c70de9c2af74 --- /dev/null +++ b/fs/nfsd/nfs4callback.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfs4callback.c | ||
3 | * | ||
4 | * Copyright (c) 2001 The Regents of the University of Michigan. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Kendrick Smith <kmsmith@umich.edu> | ||
8 | * Andy Adamson <andros@umich.edu> | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions | ||
12 | * are met: | ||
13 | * | ||
14 | * 1. Redistributions of source code must retain the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer. | ||
16 | * 2. Redistributions in binary form must reproduce the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer in the | ||
18 | * documentation and/or other materials provided with the distribution. | ||
19 | * 3. Neither the name of the University nor the names of its | ||
20 | * contributors may be used to endorse or promote products derived | ||
21 | * from this software without specific prior written permission. | ||
22 | * | ||
23 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
24 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
26 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
30 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
34 | */ | ||
35 | |||
36 | #include <linux/config.h> | ||
37 | #include <linux/module.h> | ||
38 | #include <linux/list.h> | ||
39 | #include <linux/inet.h> | ||
40 | #include <linux/errno.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/sunrpc/xdr.h> | ||
43 | #include <linux/sunrpc/svc.h> | ||
44 | #include <linux/sunrpc/clnt.h> | ||
45 | #include <linux/nfsd/nfsd.h> | ||
46 | #include <linux/nfsd/state.h> | ||
47 | #include <linux/sunrpc/sched.h> | ||
48 | #include <linux/nfs4.h> | ||
49 | |||
50 | #define NFSDDBG_FACILITY NFSDDBG_PROC | ||
51 | |||
52 | #define NFSPROC4_CB_NULL 0 | ||
53 | #define NFSPROC4_CB_COMPOUND 1 | ||
54 | |||
55 | /* declarations */ | ||
56 | static void nfs4_cb_null(struct rpc_task *task); | ||
57 | extern spinlock_t recall_lock; | ||
58 | |||
59 | /* Index of predefined Linux callback client operations */ | ||
60 | |||
61 | enum { | ||
62 | NFSPROC4_CLNT_CB_NULL = 0, | ||
63 | NFSPROC4_CLNT_CB_RECALL, | ||
64 | }; | ||
65 | |||
66 | enum nfs_cb_opnum4 { | ||
67 | OP_CB_RECALL = 4, | ||
68 | }; | ||
69 | |||
70 | #define NFS4_MAXTAGLEN 20 | ||
71 | |||
72 | #define NFS4_enc_cb_null_sz 0 | ||
73 | #define NFS4_dec_cb_null_sz 0 | ||
74 | #define cb_compound_enc_hdr_sz 4 | ||
75 | #define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2)) | ||
76 | #define op_enc_sz 1 | ||
77 | #define op_dec_sz 2 | ||
78 | #define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2)) | ||
79 | #define enc_stateid_sz 16 | ||
80 | #define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \ | ||
81 | 1 + enc_stateid_sz + \ | ||
82 | enc_nfs4_fh_sz) | ||
83 | |||
84 | #define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \ | ||
85 | op_dec_sz) | ||
86 | |||
87 | /* | ||
88 | * Generic encode routines from fs/nfs/nfs4xdr.c | ||
89 | */ | ||
90 | static inline u32 * | ||
91 | xdr_writemem(u32 *p, const void *ptr, int nbytes) | ||
92 | { | ||
93 | int tmp = XDR_QUADLEN(nbytes); | ||
94 | if (!tmp) | ||
95 | return p; | ||
96 | p[tmp-1] = 0; | ||
97 | memcpy(p, ptr, nbytes); | ||
98 | return p + tmp; | ||
99 | } | ||
100 | |||
101 | #define WRITE32(n) *p++ = htonl(n) | ||
102 | #define WRITEMEM(ptr,nbytes) do { \ | ||
103 | p = xdr_writemem(p, ptr, nbytes); \ | ||
104 | } while (0) | ||
105 | #define RESERVE_SPACE(nbytes) do { \ | ||
106 | p = xdr_reserve_space(xdr, nbytes); \ | ||
107 | if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __FUNCTION__); \ | ||
108 | BUG_ON(!p); \ | ||
109 | } while (0) | ||
110 | |||
111 | /* | ||
112 | * Generic decode routines from fs/nfs/nfs4xdr.c | ||
113 | */ | ||
114 | #define DECODE_TAIL \ | ||
115 | status = 0; \ | ||
116 | out: \ | ||
117 | return status; \ | ||
118 | xdr_error: \ | ||
119 | dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
120 | status = -EIO; \ | ||
121 | goto out | ||
122 | |||
123 | #define READ32(x) (x) = ntohl(*p++) | ||
124 | #define READ64(x) do { \ | ||
125 | (x) = (u64)ntohl(*p++) << 32; \ | ||
126 | (x) |= ntohl(*p++); \ | ||
127 | } while (0) | ||
128 | #define READTIME(x) do { \ | ||
129 | p++; \ | ||
130 | (x.tv_sec) = ntohl(*p++); \ | ||
131 | (x.tv_nsec) = ntohl(*p++); \ | ||
132 | } while (0) | ||
133 | #define READ_BUF(nbytes) do { \ | ||
134 | p = xdr_inline_decode(xdr, nbytes); \ | ||
135 | if (!p) { \ | ||
136 | dprintk("NFSD: %s: reply buffer overflowed in line %d.", \ | ||
137 | __FUNCTION__, __LINE__); \ | ||
138 | return -EIO; \ | ||
139 | } \ | ||
140 | } while (0) | ||
141 | |||
142 | struct nfs4_cb_compound_hdr { | ||
143 | int status; | ||
144 | u32 ident; | ||
145 | u32 nops; | ||
146 | u32 taglen; | ||
147 | char * tag; | ||
148 | }; | ||
149 | |||
150 | static struct { | ||
151 | int stat; | ||
152 | int errno; | ||
153 | } nfs_cb_errtbl[] = { | ||
154 | { NFS4_OK, 0 }, | ||
155 | { NFS4ERR_PERM, EPERM }, | ||
156 | { NFS4ERR_NOENT, ENOENT }, | ||
157 | { NFS4ERR_IO, EIO }, | ||
158 | { NFS4ERR_NXIO, ENXIO }, | ||
159 | { NFS4ERR_ACCESS, EACCES }, | ||
160 | { NFS4ERR_EXIST, EEXIST }, | ||
161 | { NFS4ERR_XDEV, EXDEV }, | ||
162 | { NFS4ERR_NOTDIR, ENOTDIR }, | ||
163 | { NFS4ERR_ISDIR, EISDIR }, | ||
164 | { NFS4ERR_INVAL, EINVAL }, | ||
165 | { NFS4ERR_FBIG, EFBIG }, | ||
166 | { NFS4ERR_NOSPC, ENOSPC }, | ||
167 | { NFS4ERR_ROFS, EROFS }, | ||
168 | { NFS4ERR_MLINK, EMLINK }, | ||
169 | { NFS4ERR_NAMETOOLONG, ENAMETOOLONG }, | ||
170 | { NFS4ERR_NOTEMPTY, ENOTEMPTY }, | ||
171 | { NFS4ERR_DQUOT, EDQUOT }, | ||
172 | { NFS4ERR_STALE, ESTALE }, | ||
173 | { NFS4ERR_BADHANDLE, EBADHANDLE }, | ||
174 | { NFS4ERR_BAD_COOKIE, EBADCOOKIE }, | ||
175 | { NFS4ERR_NOTSUPP, ENOTSUPP }, | ||
176 | { NFS4ERR_TOOSMALL, ETOOSMALL }, | ||
177 | { NFS4ERR_SERVERFAULT, ESERVERFAULT }, | ||
178 | { NFS4ERR_BADTYPE, EBADTYPE }, | ||
179 | { NFS4ERR_LOCKED, EAGAIN }, | ||
180 | { NFS4ERR_RESOURCE, EREMOTEIO }, | ||
181 | { NFS4ERR_SYMLINK, ELOOP }, | ||
182 | { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, | ||
183 | { NFS4ERR_DEADLOCK, EDEADLK }, | ||
184 | { -1, EIO } | ||
185 | }; | ||
186 | |||
187 | static int | ||
188 | nfs_cb_stat_to_errno(int stat) | ||
189 | { | ||
190 | int i; | ||
191 | for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) { | ||
192 | if (nfs_cb_errtbl[i].stat == stat) | ||
193 | return nfs_cb_errtbl[i].errno; | ||
194 | } | ||
195 | /* If we cannot translate the error, the recovery routines should | ||
196 | * handle it. | ||
197 | * Note: remaining NFSv4 error codes have values > 10000, so should | ||
198 | * not conflict with native Linux error codes. | ||
199 | */ | ||
200 | return stat; | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * XDR encode | ||
205 | */ | ||
206 | |||
207 | static int | ||
208 | encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr) | ||
209 | { | ||
210 | u32 * p; | ||
211 | |||
212 | RESERVE_SPACE(16); | ||
213 | WRITE32(0); /* tag length is always 0 */ | ||
214 | WRITE32(NFS4_MINOR_VERSION); | ||
215 | WRITE32(hdr->ident); | ||
216 | WRITE32(hdr->nops); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int | ||
221 | encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec) | ||
222 | { | ||
223 | u32 *p; | ||
224 | int len = cb_rec->cbr_fhlen; | ||
225 | |||
226 | RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len); | ||
227 | WRITE32(OP_CB_RECALL); | ||
228 | WRITEMEM(&cb_rec->cbr_stateid, sizeof(stateid_t)); | ||
229 | WRITE32(cb_rec->cbr_trunc); | ||
230 | WRITE32(len); | ||
231 | WRITEMEM(cb_rec->cbr_fhval, len); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int | ||
236 | nfs4_xdr_enc_cb_null(struct rpc_rqst *req, u32 *p) | ||
237 | { | ||
238 | struct xdr_stream xdrs, *xdr = &xdrs; | ||
239 | |||
240 | xdr_init_encode(&xdrs, &req->rq_snd_buf, p); | ||
241 | RESERVE_SPACE(0); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static int | ||
246 | nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, u32 *p, struct nfs4_cb_recall *args) | ||
247 | { | ||
248 | struct xdr_stream xdr; | ||
249 | struct nfs4_cb_compound_hdr hdr = { | ||
250 | .ident = args->cbr_ident, | ||
251 | .nops = 1, | ||
252 | }; | ||
253 | |||
254 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
255 | encode_cb_compound_hdr(&xdr, &hdr); | ||
256 | return (encode_cb_recall(&xdr, args)); | ||
257 | } | ||
258 | |||
259 | |||
260 | static int | ||
261 | decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){ | ||
262 | u32 *p; | ||
263 | |||
264 | READ_BUF(8); | ||
265 | READ32(hdr->status); | ||
266 | READ32(hdr->taglen); | ||
267 | READ_BUF(hdr->taglen + 4); | ||
268 | hdr->tag = (char *)p; | ||
269 | p += XDR_QUADLEN(hdr->taglen); | ||
270 | READ32(hdr->nops); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int | ||
275 | decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) | ||
276 | { | ||
277 | u32 *p; | ||
278 | u32 op; | ||
279 | int32_t nfserr; | ||
280 | |||
281 | READ_BUF(8); | ||
282 | READ32(op); | ||
283 | if (op != expected) { | ||
284 | dprintk("NFSD: decode_cb_op_hdr: Callback server returned " | ||
285 | " operation %d but we issued a request for %d\n", | ||
286 | op, expected); | ||
287 | return -EIO; | ||
288 | } | ||
289 | READ32(nfserr); | ||
290 | if (nfserr != NFS_OK) | ||
291 | return -nfs_cb_stat_to_errno(nfserr); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int | ||
296 | nfs4_xdr_dec_cb_null(struct rpc_rqst *req, u32 *p) | ||
297 | { | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int | ||
302 | nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, u32 *p) | ||
303 | { | ||
304 | struct xdr_stream xdr; | ||
305 | struct nfs4_cb_compound_hdr hdr; | ||
306 | int status; | ||
307 | |||
308 | xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); | ||
309 | status = decode_cb_compound_hdr(&xdr, &hdr); | ||
310 | if (status) | ||
311 | goto out; | ||
312 | status = decode_cb_op_hdr(&xdr, OP_CB_RECALL); | ||
313 | out: | ||
314 | return status; | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * RPC procedure tables | ||
319 | */ | ||
320 | #ifndef MAX | ||
321 | # define MAX(a, b) (((a) > (b))? (a) : (b)) | ||
322 | #endif | ||
323 | |||
324 | #define PROC(proc, call, argtype, restype) \ | ||
325 | [NFSPROC4_CLNT_##proc] = { \ | ||
326 | .p_proc = NFSPROC4_CB_##call, \ | ||
327 | .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ | ||
328 | .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ | ||
329 | .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ | ||
330 | } | ||
331 | |||
332 | struct rpc_procinfo nfs4_cb_procedures[] = { | ||
333 | PROC(CB_NULL, NULL, enc_cb_null, dec_cb_null), | ||
334 | PROC(CB_RECALL, COMPOUND, enc_cb_recall, dec_cb_recall), | ||
335 | }; | ||
336 | |||
337 | struct rpc_version nfs_cb_version4 = { | ||
338 | .number = 1, | ||
339 | .nrprocs = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]), | ||
340 | .procs = nfs4_cb_procedures | ||
341 | }; | ||
342 | |||
343 | static struct rpc_version * nfs_cb_version[] = { | ||
344 | NULL, | ||
345 | &nfs_cb_version4, | ||
346 | }; | ||
347 | |||
348 | /* | ||
349 | * Use the SETCLIENTID credential | ||
350 | */ | ||
351 | struct rpc_cred * | ||
352 | nfsd4_lookupcred(struct nfs4_client *clp, int taskflags) | ||
353 | { | ||
354 | struct auth_cred acred; | ||
355 | struct rpc_clnt *clnt = clp->cl_callback.cb_client; | ||
356 | struct rpc_cred *ret; | ||
357 | |||
358 | get_group_info(clp->cl_cred.cr_group_info); | ||
359 | acred.uid = clp->cl_cred.cr_uid; | ||
360 | acred.gid = clp->cl_cred.cr_gid; | ||
361 | acred.group_info = clp->cl_cred.cr_group_info; | ||
362 | |||
363 | dprintk("NFSD: looking up %s cred\n", | ||
364 | clnt->cl_auth->au_ops->au_name); | ||
365 | ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags); | ||
366 | put_group_info(clp->cl_cred.cr_group_info); | ||
367 | return ret; | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... | ||
372 | */ | ||
373 | void | ||
374 | nfsd4_probe_callback(struct nfs4_client *clp) | ||
375 | { | ||
376 | struct sockaddr_in addr; | ||
377 | struct nfs4_callback *cb = &clp->cl_callback; | ||
378 | struct rpc_timeout timeparms; | ||
379 | struct rpc_xprt * xprt; | ||
380 | struct rpc_program * program = &cb->cb_program; | ||
381 | struct rpc_stat * stat = &cb->cb_stat; | ||
382 | struct rpc_clnt * clnt; | ||
383 | struct rpc_message msg = { | ||
384 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], | ||
385 | .rpc_argp = clp, | ||
386 | }; | ||
387 | char hostname[32]; | ||
388 | int status; | ||
389 | |||
390 | dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d\n", | ||
391 | cb->cb_parsed, atomic_read(&cb->cb_set)); | ||
392 | if (!cb->cb_parsed || atomic_read(&cb->cb_set)) | ||
393 | return; | ||
394 | |||
395 | /* Initialize address */ | ||
396 | memset(&addr, 0, sizeof(addr)); | ||
397 | addr.sin_family = AF_INET; | ||
398 | addr.sin_port = htons(cb->cb_port); | ||
399 | addr.sin_addr.s_addr = htonl(cb->cb_addr); | ||
400 | |||
401 | /* Initialize timeout */ | ||
402 | timeparms.to_initval = (NFSD_LEASE_TIME/4) * HZ; | ||
403 | timeparms.to_retries = 0; | ||
404 | timeparms.to_maxval = (NFSD_LEASE_TIME/2) * HZ; | ||
405 | timeparms.to_exponential = 1; | ||
406 | |||
407 | /* Create RPC transport */ | ||
408 | if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) { | ||
409 | dprintk("NFSD: couldn't create callback transport!\n"); | ||
410 | goto out_err; | ||
411 | } | ||
412 | |||
413 | /* Initialize rpc_program */ | ||
414 | program->name = "nfs4_cb"; | ||
415 | program->number = cb->cb_prog; | ||
416 | program->nrvers = sizeof(nfs_cb_version)/sizeof(nfs_cb_version[0]); | ||
417 | program->version = nfs_cb_version; | ||
418 | program->stats = stat; | ||
419 | |||
420 | /* Initialize rpc_stat */ | ||
421 | memset(stat, 0, sizeof(struct rpc_stat)); | ||
422 | stat->program = program; | ||
423 | |||
424 | /* Create RPC client | ||
425 | * | ||
426 | * XXX AUTH_UNIX only - need AUTH_GSS.... | ||
427 | */ | ||
428 | sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr)); | ||
429 | if (!(clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX))) { | ||
430 | dprintk("NFSD: couldn't create callback client\n"); | ||
431 | goto out_xprt; | ||
432 | } | ||
433 | clnt->cl_intr = 0; | ||
434 | clnt->cl_softrtry = 1; | ||
435 | clnt->cl_chatty = 1; | ||
436 | |||
437 | /* Kick rpciod, put the call on the wire. */ | ||
438 | |||
439 | if (rpciod_up() != 0) { | ||
440 | dprintk("nfsd: couldn't start rpciod for callbacks!\n"); | ||
441 | goto out_clnt; | ||
442 | } | ||
443 | |||
444 | /* the task holds a reference to the nfs4_client struct */ | ||
445 | cb->cb_client = clnt; | ||
446 | atomic_inc(&clp->cl_count); | ||
447 | |||
448 | msg.rpc_cred = nfsd4_lookupcred(clp,0); | ||
449 | if (IS_ERR(msg.rpc_cred)) | ||
450 | goto out_rpciod; | ||
451 | status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, NULL); | ||
452 | put_rpccred(msg.rpc_cred); | ||
453 | |||
454 | if (status != 0) { | ||
455 | dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n"); | ||
456 | goto out_rpciod; | ||
457 | } | ||
458 | return; | ||
459 | |||
460 | out_rpciod: | ||
461 | atomic_dec(&clp->cl_count); | ||
462 | rpciod_down(); | ||
463 | out_clnt: | ||
464 | rpc_shutdown_client(clnt); | ||
465 | goto out_err; | ||
466 | out_xprt: | ||
467 | xprt_destroy(xprt); | ||
468 | out_err: | ||
469 | dprintk("NFSD: warning: no callback path to client %.*s\n", | ||
470 | (int)clp->cl_name.len, clp->cl_name.data); | ||
471 | cb->cb_client = NULL; | ||
472 | } | ||
473 | |||
474 | static void | ||
475 | nfs4_cb_null(struct rpc_task *task) | ||
476 | { | ||
477 | struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; | ||
478 | struct nfs4_callback *cb = &clp->cl_callback; | ||
479 | u32 addr = htonl(cb->cb_addr); | ||
480 | |||
481 | dprintk("NFSD: nfs4_cb_null task->tk_status %d\n", task->tk_status); | ||
482 | |||
483 | if (task->tk_status < 0) { | ||
484 | dprintk("NFSD: callback establishment to client %.*s failed\n", | ||
485 | (int)clp->cl_name.len, clp->cl_name.data); | ||
486 | goto out; | ||
487 | } | ||
488 | atomic_set(&cb->cb_set, 1); | ||
489 | dprintk("NFSD: callback set to client %u.%u.%u.%u\n", NIPQUAD(addr)); | ||
490 | out: | ||
491 | put_nfs4_client(clp); | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * called with dp->dl_count inc'ed. | ||
496 | * nfs4_lock_state() may or may not have been called. | ||
497 | */ | ||
498 | void | ||
499 | nfsd4_cb_recall(struct nfs4_delegation *dp) | ||
500 | { | ||
501 | struct nfs4_client *clp = dp->dl_client; | ||
502 | struct rpc_clnt *clnt = clp->cl_callback.cb_client; | ||
503 | struct nfs4_cb_recall *cbr = &dp->dl_recall; | ||
504 | struct rpc_message msg = { | ||
505 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], | ||
506 | .rpc_argp = cbr, | ||
507 | }; | ||
508 | int retries = 1; | ||
509 | int status = 0; | ||
510 | |||
511 | if ((!atomic_read(&clp->cl_callback.cb_set)) || !clnt) | ||
512 | return; | ||
513 | |||
514 | msg.rpc_cred = nfsd4_lookupcred(clp, 0); | ||
515 | if (IS_ERR(msg.rpc_cred)) | ||
516 | goto out; | ||
517 | |||
518 | cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */ | ||
519 | cbr->cbr_dp = dp; | ||
520 | |||
521 | status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT); | ||
522 | while (retries--) { | ||
523 | switch (status) { | ||
524 | case -EIO: | ||
525 | /* Network partition? */ | ||
526 | case -EBADHANDLE: | ||
527 | case -NFS4ERR_BAD_STATEID: | ||
528 | /* Race: client probably got cb_recall | ||
529 | * before open reply granting delegation */ | ||
530 | break; | ||
531 | default: | ||
532 | goto out_put_cred; | ||
533 | } | ||
534 | ssleep(2); | ||
535 | status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT); | ||
536 | } | ||
537 | out_put_cred: | ||
538 | put_rpccred(msg.rpc_cred); | ||
539 | out: | ||
540 | if (status == -EIO) | ||
541 | atomic_set(&clp->cl_callback.cb_set, 0); | ||
542 | /* Success or failure, now we're either waiting for lease expiration | ||
543 | * or deleg_return. */ | ||
544 | dprintk("NFSD: nfs4_cb_recall: dp %p dl_flock %p dl_count %d\n",dp, dp->dl_flock, atomic_read(&dp->dl_count)); | ||
545 | nfs4_put_delegation(dp); | ||
546 | return; | ||
547 | } | ||
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c new file mode 100644 index 000000000000..4ba540841cf6 --- /dev/null +++ b/fs/nfsd/nfs4idmap.c | |||
@@ -0,0 +1,588 @@ | |||
1 | /* | ||
2 | * fs/nfsd/nfs4idmap.c | ||
3 | * | ||
4 | * Mapping of UID/GIDs to name and vice versa. | ||
5 | * | ||
6 | * Copyright (c) 2002, 2003 The Regents of the University of | ||
7 | * Michigan. All rights reserved. | ||
8 | * | ||
9 | * Marius Aamodt Eriksen <marius@umich.edu> | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or without | ||
12 | * modification, are permitted provided that the following conditions | ||
13 | * are met: | ||
14 | * | ||
15 | * 1. Redistributions of source code must retain the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer. | ||
17 | * 2. Redistributions in binary form must reproduce the above copyright | ||
18 | * notice, this list of conditions and the following disclaimer in the | ||
19 | * documentation and/or other materials provided with the distribution. | ||
20 | * 3. Neither the name of the University nor the names of its | ||
21 | * contributors may be used to endorse or promote products derived | ||
22 | * from this software without specific prior written permission. | ||
23 | * | ||
24 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
25 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
35 | */ | ||
36 | |||
37 | #include <linux/config.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/init.h> | ||
40 | |||
41 | #include <linux/mm.h> | ||
42 | #include <linux/utsname.h> | ||
43 | #include <linux/errno.h> | ||
44 | #include <linux/string.h> | ||
45 | #include <linux/sunrpc/clnt.h> | ||
46 | #include <linux/nfs.h> | ||
47 | #include <linux/nfs4.h> | ||
48 | #include <linux/nfs_fs.h> | ||
49 | #include <linux/nfs_page.h> | ||
50 | #include <linux/smp_lock.h> | ||
51 | #include <linux/sunrpc/cache.h> | ||
52 | #include <linux/nfsd_idmap.h> | ||
53 | #include <linux/list.h> | ||
54 | #include <linux/sched.h> | ||
55 | #include <linux/time.h> | ||
56 | #include <linux/seq_file.h> | ||
57 | #include <linux/sunrpc/svcauth.h> | ||
58 | |||
59 | /* | ||
60 | * Cache entry | ||
61 | */ | ||
62 | |||
63 | /* | ||
64 | * XXX we know that IDMAP_NAMESZ < PAGE_SIZE, but it's ugly to rely on | ||
65 | * that. | ||
66 | */ | ||
67 | |||
68 | #define IDMAP_TYPE_USER 0 | ||
69 | #define IDMAP_TYPE_GROUP 1 | ||
70 | |||
71 | struct ent { | ||
72 | struct cache_head h; | ||
73 | int type; /* User / Group */ | ||
74 | uid_t id; | ||
75 | char name[IDMAP_NAMESZ]; | ||
76 | char authname[IDMAP_NAMESZ]; | ||
77 | }; | ||
78 | |||
79 | #define DefineSimpleCacheLookupMap(STRUCT, FUNC) \ | ||
80 | DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ | ||
81 | (struct STRUCT *item, int set), /*no setup */, \ | ||
82 | & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ | ||
83 | STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0) | ||
84 | |||
85 | /* Common entry handling */ | ||
86 | |||
87 | #define ENT_HASHBITS 8 | ||
88 | #define ENT_HASHMAX (1 << ENT_HASHBITS) | ||
89 | #define ENT_HASHMASK (ENT_HASHMAX - 1) | ||
90 | |||
91 | static inline void | ||
92 | ent_init(struct ent *new, struct ent *itm) | ||
93 | { | ||
94 | new->id = itm->id; | ||
95 | new->type = itm->type; | ||
96 | |||
97 | strlcpy(new->name, itm->name, sizeof(new->name)); | ||
98 | strlcpy(new->authname, itm->authname, sizeof(new->name)); | ||
99 | } | ||
100 | |||
101 | static inline void | ||
102 | ent_update(struct ent *new, struct ent *itm) | ||
103 | { | ||
104 | ent_init(new, itm); | ||
105 | } | ||
106 | |||
107 | void | ||
108 | ent_put(struct cache_head *ch, struct cache_detail *cd) | ||
109 | { | ||
110 | if (cache_put(ch, cd)) { | ||
111 | struct ent *map = container_of(ch, struct ent, h); | ||
112 | kfree(map); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * ID -> Name cache | ||
118 | */ | ||
119 | |||
120 | static struct cache_head *idtoname_table[ENT_HASHMAX]; | ||
121 | |||
122 | static uint32_t | ||
123 | idtoname_hash(struct ent *ent) | ||
124 | { | ||
125 | uint32_t hash; | ||
126 | |||
127 | hash = hash_str(ent->authname, ENT_HASHBITS); | ||
128 | hash = hash_long(hash ^ ent->id, ENT_HASHBITS); | ||
129 | |||
130 | /* Flip LSB for user/group */ | ||
131 | if (ent->type == IDMAP_TYPE_GROUP) | ||
132 | hash ^= 1; | ||
133 | |||
134 | return hash; | ||
135 | } | ||
136 | |||
137 | static void | ||
138 | idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, | ||
139 | int *blen) | ||
140 | { | ||
141 | struct ent *ent = container_of(ch, struct ent, h); | ||
142 | char idstr[11]; | ||
143 | |||
144 | qword_add(bpp, blen, ent->authname); | ||
145 | snprintf(idstr, sizeof(idstr), "%d", ent->id); | ||
146 | qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user"); | ||
147 | qword_add(bpp, blen, idstr); | ||
148 | |||
149 | (*bpp)[-1] = '\n'; | ||
150 | } | ||
151 | |||
152 | static inline int | ||
153 | idtoname_match(struct ent *a, struct ent *b) | ||
154 | { | ||
155 | return (a->id == b->id && a->type == b->type && | ||
156 | strcmp(a->authname, b->authname) == 0); | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) | ||
161 | { | ||
162 | struct ent *ent; | ||
163 | |||
164 | if (h == NULL) { | ||
165 | seq_puts(m, "#domain type id [name]\n"); | ||
166 | return 0; | ||
167 | } | ||
168 | ent = container_of(h, struct ent, h); | ||
169 | seq_printf(m, "%s %s %d", ent->authname, | ||
170 | ent->type == IDMAP_TYPE_GROUP ? "group" : "user", | ||
171 | ent->id); | ||
172 | if (test_bit(CACHE_VALID, &h->flags)) | ||
173 | seq_printf(m, " %s", ent->name); | ||
174 | seq_printf(m, "\n"); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static void | ||
179 | warn_no_idmapd(struct cache_detail *detail) | ||
180 | { | ||
181 | printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n", | ||
182 | detail->last_close? "died" : "not been started"); | ||
183 | } | ||
184 | |||
185 | |||
186 | static int idtoname_parse(struct cache_detail *, char *, int); | ||
187 | static struct ent *idtoname_lookup(struct ent *, int); | ||
188 | |||
189 | struct cache_detail idtoname_cache = { | ||
190 | .hash_size = ENT_HASHMAX, | ||
191 | .hash_table = idtoname_table, | ||
192 | .name = "nfs4.idtoname", | ||
193 | .cache_put = ent_put, | ||
194 | .cache_request = idtoname_request, | ||
195 | .cache_parse = idtoname_parse, | ||
196 | .cache_show = idtoname_show, | ||
197 | .warn_no_listener = warn_no_idmapd, | ||
198 | }; | ||
199 | |||
200 | int | ||
201 | idtoname_parse(struct cache_detail *cd, char *buf, int buflen) | ||
202 | { | ||
203 | struct ent ent, *res; | ||
204 | char *buf1, *bp; | ||
205 | int error = -EINVAL; | ||
206 | |||
207 | if (buf[buflen - 1] != '\n') | ||
208 | return (-EINVAL); | ||
209 | buf[buflen - 1]= '\0'; | ||
210 | |||
211 | buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
212 | if (buf1 == NULL) | ||
213 | return (-ENOMEM); | ||
214 | |||
215 | memset(&ent, 0, sizeof(ent)); | ||
216 | |||
217 | /* Authentication name */ | ||
218 | if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) | ||
219 | goto out; | ||
220 | memcpy(ent.authname, buf1, sizeof(ent.authname)); | ||
221 | |||
222 | /* Type */ | ||
223 | if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) | ||
224 | goto out; | ||
225 | ent.type = strcmp(buf1, "user") == 0 ? | ||
226 | IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; | ||
227 | |||
228 | /* ID */ | ||
229 | if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) | ||
230 | goto out; | ||
231 | ent.id = simple_strtoul(buf1, &bp, 10); | ||
232 | if (bp == buf1) | ||
233 | goto out; | ||
234 | |||
235 | /* expiry */ | ||
236 | ent.h.expiry_time = get_expiry(&buf); | ||
237 | if (ent.h.expiry_time == 0) | ||
238 | goto out; | ||
239 | |||
240 | /* Name */ | ||
241 | error = qword_get(&buf, buf1, PAGE_SIZE); | ||
242 | if (error == -EINVAL) | ||
243 | goto out; | ||
244 | if (error == -ENOENT) | ||
245 | set_bit(CACHE_NEGATIVE, &ent.h.flags); | ||
246 | else { | ||
247 | if (error >= IDMAP_NAMESZ) { | ||
248 | error = -EINVAL; | ||
249 | goto out; | ||
250 | } | ||
251 | memcpy(ent.name, buf1, sizeof(ent.name)); | ||
252 | } | ||
253 | error = -ENOMEM; | ||
254 | if ((res = idtoname_lookup(&ent, 1)) == NULL) | ||
255 | goto out; | ||
256 | |||
257 | ent_put(&res->h, &idtoname_cache); | ||
258 | |||
259 | error = 0; | ||
260 | out: | ||
261 | kfree(buf1); | ||
262 | |||
263 | return error; | ||
264 | } | ||
265 | |||
266 | static DefineSimpleCacheLookupMap(ent, idtoname); | ||
267 | |||
268 | /* | ||
269 | * Name -> ID cache | ||
270 | */ | ||
271 | |||
272 | static struct cache_head *nametoid_table[ENT_HASHMAX]; | ||
273 | |||
274 | static inline int | ||
275 | nametoid_hash(struct ent *ent) | ||
276 | { | ||
277 | return hash_str(ent->name, ENT_HASHBITS); | ||
278 | } | ||
279 | |||
280 | void | ||
281 | nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, | ||
282 | int *blen) | ||
283 | { | ||
284 | struct ent *ent = container_of(ch, struct ent, h); | ||
285 | |||
286 | qword_add(bpp, blen, ent->authname); | ||
287 | qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user"); | ||
288 | qword_add(bpp, blen, ent->name); | ||
289 | |||
290 | (*bpp)[-1] = '\n'; | ||
291 | } | ||
292 | |||
293 | static inline int | ||
294 | nametoid_match(struct ent *a, struct ent *b) | ||
295 | { | ||
296 | return (a->type == b->type && strcmp(a->name, b->name) == 0 && | ||
297 | strcmp(a->authname, b->authname) == 0); | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) | ||
302 | { | ||
303 | struct ent *ent; | ||
304 | |||
305 | if (h == NULL) { | ||
306 | seq_puts(m, "#domain type name [id]\n"); | ||
307 | return 0; | ||
308 | } | ||
309 | ent = container_of(h, struct ent, h); | ||
310 | seq_printf(m, "%s %s %s", ent->authname, | ||
311 | ent->type == IDMAP_TYPE_GROUP ? "group" : "user", | ||
312 | ent->name); | ||
313 | if (test_bit(CACHE_VALID, &h->flags)) | ||
314 | seq_printf(m, " %d", ent->id); | ||
315 | seq_printf(m, "\n"); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static struct ent *nametoid_lookup(struct ent *, int); | ||
320 | int nametoid_parse(struct cache_detail *, char *, int); | ||
321 | |||
322 | struct cache_detail nametoid_cache = { | ||
323 | .hash_size = ENT_HASHMAX, | ||
324 | .hash_table = nametoid_table, | ||
325 | .name = "nfs4.nametoid", | ||
326 | .cache_put = ent_put, | ||
327 | .cache_request = nametoid_request, | ||
328 | .cache_parse = nametoid_parse, | ||
329 | .cache_show = nametoid_show, | ||
330 | .warn_no_listener = warn_no_idmapd, | ||
331 | }; | ||
332 | |||
333 | int | ||
334 | nametoid_parse(struct cache_detail *cd, char *buf, int buflen) | ||
335 | { | ||
336 | struct ent ent, *res; | ||
337 | char *buf1; | ||
338 | int error = -EINVAL; | ||
339 | |||
340 | if (buf[buflen - 1] != '\n') | ||
341 | return (-EINVAL); | ||
342 | buf[buflen - 1]= '\0'; | ||
343 | |||
344 | buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
345 | if (buf1 == NULL) | ||
346 | return (-ENOMEM); | ||
347 | |||
348 | memset(&ent, 0, sizeof(ent)); | ||
349 | |||
350 | /* Authentication name */ | ||
351 | if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) | ||
352 | goto out; | ||
353 | memcpy(ent.authname, buf1, sizeof(ent.authname)); | ||
354 | |||
355 | /* Type */ | ||
356 | if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) | ||
357 | goto out; | ||
358 | ent.type = strcmp(buf1, "user") == 0 ? | ||
359 | IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; | ||
360 | |||
361 | /* Name */ | ||
362 | error = qword_get(&buf, buf1, PAGE_SIZE); | ||
363 | if (error <= 0 || error >= IDMAP_NAMESZ) | ||
364 | goto out; | ||
365 | memcpy(ent.name, buf1, sizeof(ent.name)); | ||
366 | |||
367 | /* expiry */ | ||
368 | ent.h.expiry_time = get_expiry(&buf); | ||
369 | if (ent.h.expiry_time == 0) | ||
370 | goto out; | ||
371 | |||
372 | /* ID */ | ||
373 | error = get_int(&buf, &ent.id); | ||
374 | if (error == -EINVAL) | ||
375 | goto out; | ||
376 | if (error == -ENOENT) | ||
377 | set_bit(CACHE_NEGATIVE, &ent.h.flags); | ||
378 | |||
379 | error = -ENOMEM; | ||
380 | if ((res = nametoid_lookup(&ent, 1)) == NULL) | ||
381 | goto out; | ||
382 | |||
383 | ent_put(&res->h, &nametoid_cache); | ||
384 | error = 0; | ||
385 | out: | ||
386 | kfree(buf1); | ||
387 | |||
388 | return (error); | ||
389 | } | ||
390 | |||
391 | static DefineSimpleCacheLookupMap(ent, nametoid); | ||
392 | |||
393 | /* | ||
394 | * Exported API | ||
395 | */ | ||
396 | |||
397 | void | ||
398 | nfsd_idmap_init(void) | ||
399 | { | ||
400 | cache_register(&idtoname_cache); | ||
401 | cache_register(&nametoid_cache); | ||
402 | } | ||
403 | |||
404 | void | ||
405 | nfsd_idmap_shutdown(void) | ||
406 | { | ||
407 | cache_unregister(&idtoname_cache); | ||
408 | cache_unregister(&nametoid_cache); | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * Deferred request handling | ||
413 | */ | ||
414 | |||
415 | struct idmap_defer_req { | ||
416 | struct cache_req req; | ||
417 | struct cache_deferred_req deferred_req; | ||
418 | wait_queue_head_t waitq; | ||
419 | atomic_t count; | ||
420 | }; | ||
421 | |||
422 | static inline void | ||
423 | put_mdr(struct idmap_defer_req *mdr) | ||
424 | { | ||
425 | if (atomic_dec_and_test(&mdr->count)) | ||
426 | kfree(mdr); | ||
427 | } | ||
428 | |||
429 | static inline void | ||
430 | get_mdr(struct idmap_defer_req *mdr) | ||
431 | { | ||
432 | atomic_inc(&mdr->count); | ||
433 | } | ||
434 | |||
435 | static void | ||
436 | idmap_revisit(struct cache_deferred_req *dreq, int toomany) | ||
437 | { | ||
438 | struct idmap_defer_req *mdr = | ||
439 | container_of(dreq, struct idmap_defer_req, deferred_req); | ||
440 | |||
441 | wake_up(&mdr->waitq); | ||
442 | put_mdr(mdr); | ||
443 | } | ||
444 | |||
445 | static struct cache_deferred_req * | ||
446 | idmap_defer(struct cache_req *req) | ||
447 | { | ||
448 | struct idmap_defer_req *mdr = | ||
449 | container_of(req, struct idmap_defer_req, req); | ||
450 | |||
451 | mdr->deferred_req.revisit = idmap_revisit; | ||
452 | get_mdr(mdr); | ||
453 | return (&mdr->deferred_req); | ||
454 | } | ||
455 | |||
456 | static inline int | ||
457 | do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, | ||
458 | struct cache_detail *detail, struct ent **item, | ||
459 | struct idmap_defer_req *mdr) | ||
460 | { | ||
461 | *item = lookup_fn(key, 0); | ||
462 | if (!*item) | ||
463 | return -ENOMEM; | ||
464 | return cache_check(detail, &(*item)->h, &mdr->req); | ||
465 | } | ||
466 | |||
467 | static inline int | ||
468 | do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *, int), | ||
469 | struct ent *key, struct cache_detail *detail, | ||
470 | struct ent **item) | ||
471 | { | ||
472 | int ret = -ENOMEM; | ||
473 | |||
474 | *item = lookup_fn(key, 0); | ||
475 | if (!*item) | ||
476 | goto out_err; | ||
477 | ret = -ETIMEDOUT; | ||
478 | if (!test_bit(CACHE_VALID, &(*item)->h.flags) | ||
479 | || (*item)->h.expiry_time < get_seconds() | ||
480 | || detail->flush_time > (*item)->h.last_refresh) | ||
481 | goto out_put; | ||
482 | ret = -ENOENT; | ||
483 | if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags)) | ||
484 | goto out_put; | ||
485 | return 0; | ||
486 | out_put: | ||
487 | ent_put(&(*item)->h, detail); | ||
488 | out_err: | ||
489 | *item = NULL; | ||
490 | return ret; | ||
491 | } | ||
492 | |||
493 | static int | ||
494 | idmap_lookup(struct svc_rqst *rqstp, | ||
495 | struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, | ||
496 | struct cache_detail *detail, struct ent **item) | ||
497 | { | ||
498 | struct idmap_defer_req *mdr; | ||
499 | int ret; | ||
500 | |||
501 | mdr = kmalloc(sizeof(*mdr), GFP_KERNEL); | ||
502 | if (!mdr) | ||
503 | return -ENOMEM; | ||
504 | memset(mdr, 0, sizeof(*mdr)); | ||
505 | atomic_set(&mdr->count, 1); | ||
506 | init_waitqueue_head(&mdr->waitq); | ||
507 | mdr->req.defer = idmap_defer; | ||
508 | ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr); | ||
509 | if (ret == -EAGAIN) { | ||
510 | wait_event_interruptible_timeout(mdr->waitq, | ||
511 | test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ); | ||
512 | ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item); | ||
513 | } | ||
514 | put_mdr(mdr); | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | static int | ||
519 | idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, | ||
520 | uid_t *id) | ||
521 | { | ||
522 | struct ent *item, key = { | ||
523 | .type = type, | ||
524 | }; | ||
525 | int ret; | ||
526 | |||
527 | if (namelen + 1 > sizeof(key.name)) | ||
528 | return -EINVAL; | ||
529 | memcpy(key.name, name, namelen); | ||
530 | key.name[namelen] = '\0'; | ||
531 | strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); | ||
532 | ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item); | ||
533 | if (ret == -ENOENT) | ||
534 | ret = -ESRCH; /* nfserr_badname */ | ||
535 | if (ret) | ||
536 | return ret; | ||
537 | *id = item->id; | ||
538 | ent_put(&item->h, &nametoid_cache); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int | ||
543 | idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) | ||
544 | { | ||
545 | struct ent *item, key = { | ||
546 | .id = id, | ||
547 | .type = type, | ||
548 | }; | ||
549 | int ret; | ||
550 | |||
551 | strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); | ||
552 | ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item); | ||
553 | if (ret == -ENOENT) | ||
554 | return sprintf(name, "%u", id); | ||
555 | if (ret) | ||
556 | return ret; | ||
557 | ret = strlen(item->name); | ||
558 | BUG_ON(ret > IDMAP_NAMESZ); | ||
559 | memcpy(name, item->name, ret); | ||
560 | ent_put(&item->h, &idtoname_cache); | ||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | int | ||
565 | nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, | ||
566 | __u32 *id) | ||
567 | { | ||
568 | return idmap_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); | ||
569 | } | ||
570 | |||
571 | int | ||
572 | nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, | ||
573 | __u32 *id) | ||
574 | { | ||
575 | return idmap_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); | ||
576 | } | ||
577 | |||
578 | int | ||
579 | nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) | ||
580 | { | ||
581 | return idmap_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); | ||
582 | } | ||
583 | |||
584 | int | ||
585 | nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) | ||
586 | { | ||
587 | return idmap_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); | ||
588 | } | ||
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c new file mode 100644 index 000000000000..e8158741e8b5 --- /dev/null +++ b/fs/nfsd/nfs4proc.c | |||
@@ -0,0 +1,984 @@ | |||
1 | /* | ||
2 | * fs/nfsd/nfs4proc.c | ||
3 | * | ||
4 | * Server-side procedures for NFSv4. | ||
5 | * | ||
6 | * Copyright (c) 2002 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Kendrick Smith <kmsmith@umich.edu> | ||
10 | * Andy Adamson <andros@umich.edu> | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or without | ||
13 | * modification, are permitted provided that the following conditions | ||
14 | * are met: | ||
15 | * | ||
16 | * 1. Redistributions of source code must retain the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer. | ||
18 | * 2. Redistributions in binary form must reproduce the above copyright | ||
19 | * notice, this list of conditions and the following disclaimer in the | ||
20 | * documentation and/or other materials provided with the distribution. | ||
21 | * 3. Neither the name of the University nor the names of its | ||
22 | * contributors may be used to endorse or promote products derived | ||
23 | * from this software without specific prior written permission. | ||
24 | * | ||
25 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
26 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
27 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
28 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
34 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
36 | * | ||
37 | * Note: some routines in this file are just trivial wrappers | ||
38 | * (e.g. nfsd4_lookup()) defined solely for the sake of consistent | ||
39 | * naming. Since all such routines have been declared "inline", | ||
40 | * there shouldn't be any associated overhead. At some point in | ||
41 | * the future, I might inline these "by hand" to clean up a | ||
42 | * little. | ||
43 | */ | ||
44 | |||
45 | #include <linux/param.h> | ||
46 | #include <linux/major.h> | ||
47 | #include <linux/slab.h> | ||
48 | |||
49 | #include <linux/sunrpc/svc.h> | ||
50 | #include <linux/nfsd/nfsd.h> | ||
51 | #include <linux/nfsd/cache.h> | ||
52 | #include <linux/nfs4.h> | ||
53 | #include <linux/nfsd/state.h> | ||
54 | #include <linux/nfsd/xdr4.h> | ||
55 | #include <linux/nfs4_acl.h> | ||
56 | |||
57 | #define NFSDDBG_FACILITY NFSDDBG_PROC | ||
58 | |||
59 | static inline void | ||
60 | fh_dup2(struct svc_fh *dst, struct svc_fh *src) | ||
61 | { | ||
62 | fh_put(dst); | ||
63 | dget(src->fh_dentry); | ||
64 | if (src->fh_export) | ||
65 | cache_get(&src->fh_export->h); | ||
66 | *dst = *src; | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | ||
71 | { | ||
72 | int accmode, status; | ||
73 | |||
74 | if (open->op_truncate && | ||
75 | !(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) | ||
76 | return nfserr_inval; | ||
77 | |||
78 | accmode = MAY_NOP; | ||
79 | if (open->op_share_access & NFS4_SHARE_ACCESS_READ) | ||
80 | accmode = MAY_READ; | ||
81 | if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE) | ||
82 | accmode |= (MAY_WRITE | MAY_TRUNC); | ||
83 | accmode |= MAY_OWNER_OVERRIDE; | ||
84 | |||
85 | status = fh_verify(rqstp, current_fh, S_IFREG, accmode); | ||
86 | |||
87 | return status; | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | ||
92 | { | ||
93 | struct svc_fh resfh; | ||
94 | int status; | ||
95 | |||
96 | fh_init(&resfh, NFS4_FHSIZE); | ||
97 | open->op_truncate = 0; | ||
98 | |||
99 | if (open->op_create) { | ||
100 | /* | ||
101 | * Note: create modes (UNCHECKED,GUARDED...) are the same | ||
102 | * in NFSv4 as in v3. | ||
103 | */ | ||
104 | status = nfsd_create_v3(rqstp, current_fh, open->op_fname.data, | ||
105 | open->op_fname.len, &open->op_iattr, | ||
106 | &resfh, open->op_createmode, | ||
107 | (u32 *)open->op_verf.data, &open->op_truncate); | ||
108 | } | ||
109 | else { | ||
110 | status = nfsd_lookup(rqstp, current_fh, | ||
111 | open->op_fname.data, open->op_fname.len, &resfh); | ||
112 | fh_unlock(current_fh); | ||
113 | } | ||
114 | |||
115 | if (!status) { | ||
116 | set_change_info(&open->op_cinfo, current_fh); | ||
117 | |||
118 | /* set reply cache */ | ||
119 | fh_dup2(current_fh, &resfh); | ||
120 | open->op_stateowner->so_replay.rp_openfh_len = | ||
121 | resfh.fh_handle.fh_size; | ||
122 | memcpy(open->op_stateowner->so_replay.rp_openfh, | ||
123 | &resfh.fh_handle.fh_base, | ||
124 | resfh.fh_handle.fh_size); | ||
125 | |||
126 | status = do_open_permission(rqstp, current_fh, open); | ||
127 | } | ||
128 | |||
129 | fh_put(&resfh); | ||
130 | return status; | ||
131 | } | ||
132 | |||
133 | static int | ||
134 | do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | ||
135 | { | ||
136 | int status; | ||
137 | |||
138 | /* Only reclaims from previously confirmed clients are valid */ | ||
139 | if ((status = nfs4_check_open_reclaim(&open->op_clientid))) | ||
140 | return status; | ||
141 | |||
142 | /* We don't know the target directory, and therefore can not | ||
143 | * set the change info | ||
144 | */ | ||
145 | |||
146 | memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); | ||
147 | |||
148 | /* set replay cache */ | ||
149 | open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size; | ||
150 | memcpy(open->op_stateowner->so_replay.rp_openfh, | ||
151 | ¤t_fh->fh_handle.fh_base, | ||
152 | current_fh->fh_handle.fh_size); | ||
153 | |||
154 | open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && | ||
155 | (open->op_iattr.ia_size == 0); | ||
156 | |||
157 | status = do_open_permission(rqstp, current_fh, open); | ||
158 | |||
159 | return status; | ||
160 | } | ||
161 | |||
162 | |||
163 | static inline int | ||
164 | nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | ||
165 | { | ||
166 | int status; | ||
167 | dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", | ||
168 | (int)open->op_fname.len, open->op_fname.data, | ||
169 | open->op_stateowner); | ||
170 | |||
171 | if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) | ||
172 | return nfserr_grace; | ||
173 | |||
174 | if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) | ||
175 | return nfserr_no_grace; | ||
176 | |||
177 | /* This check required by spec. */ | ||
178 | if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) | ||
179 | return nfserr_inval; | ||
180 | |||
181 | nfs4_lock_state(); | ||
182 | |||
183 | /* check seqid for replay. set nfs4_owner */ | ||
184 | status = nfsd4_process_open1(open); | ||
185 | if (status == NFSERR_REPLAY_ME) { | ||
186 | struct nfs4_replay *rp = &open->op_stateowner->so_replay; | ||
187 | fh_put(current_fh); | ||
188 | current_fh->fh_handle.fh_size = rp->rp_openfh_len; | ||
189 | memcpy(¤t_fh->fh_handle.fh_base, rp->rp_openfh, | ||
190 | rp->rp_openfh_len); | ||
191 | status = fh_verify(rqstp, current_fh, 0, MAY_NOP); | ||
192 | if (status) | ||
193 | dprintk("nfsd4_open: replay failed" | ||
194 | " restoring previous filehandle\n"); | ||
195 | else | ||
196 | status = NFSERR_REPLAY_ME; | ||
197 | } | ||
198 | if (status) | ||
199 | goto out; | ||
200 | switch (open->op_claim_type) { | ||
201 | case NFS4_OPEN_CLAIM_NULL: | ||
202 | /* | ||
203 | * (1) set CURRENT_FH to the file being opened, | ||
204 | * creating it if necessary, (2) set open->op_cinfo, | ||
205 | * (3) set open->op_truncate if the file is to be | ||
206 | * truncated after opening, (4) do permission checking. | ||
207 | */ | ||
208 | status = do_open_lookup(rqstp, current_fh, open); | ||
209 | if (status) | ||
210 | goto out; | ||
211 | break; | ||
212 | case NFS4_OPEN_CLAIM_PREVIOUS: | ||
213 | /* | ||
214 | * The CURRENT_FH is already set to the file being | ||
215 | * opened. (1) set open->op_cinfo, (2) set | ||
216 | * open->op_truncate if the file is to be truncated | ||
217 | * after opening, (3) do permission checking. | ||
218 | */ | ||
219 | status = do_open_fhandle(rqstp, current_fh, open); | ||
220 | if (status) | ||
221 | goto out; | ||
222 | break; | ||
223 | case NFS4_OPEN_CLAIM_DELEGATE_CUR: | ||
224 | case NFS4_OPEN_CLAIM_DELEGATE_PREV: | ||
225 | printk("NFSD: unsupported OPEN claim type %d\n", | ||
226 | open->op_claim_type); | ||
227 | status = nfserr_notsupp; | ||
228 | goto out; | ||
229 | default: | ||
230 | printk("NFSD: Invalid OPEN claim type %d\n", | ||
231 | open->op_claim_type); | ||
232 | status = nfserr_inval; | ||
233 | goto out; | ||
234 | } | ||
235 | /* | ||
236 | * nfsd4_process_open2() does the actual opening of the file. If | ||
237 | * successful, it (1) truncates the file if open->op_truncate was | ||
238 | * set, (2) sets open->op_stateid, (3) sets open->op_delegation. | ||
239 | */ | ||
240 | status = nfsd4_process_open2(rqstp, current_fh, open); | ||
241 | out: | ||
242 | if (open->op_stateowner) | ||
243 | nfs4_get_stateowner(open->op_stateowner); | ||
244 | nfs4_unlock_state(); | ||
245 | return status; | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * filehandle-manipulating ops. | ||
250 | */ | ||
251 | static inline int | ||
252 | nfsd4_getfh(struct svc_fh *current_fh, struct svc_fh **getfh) | ||
253 | { | ||
254 | if (!current_fh->fh_dentry) | ||
255 | return nfserr_nofilehandle; | ||
256 | |||
257 | *getfh = current_fh; | ||
258 | return nfs_ok; | ||
259 | } | ||
260 | |||
261 | static inline int | ||
262 | nfsd4_putfh(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_putfh *putfh) | ||
263 | { | ||
264 | fh_put(current_fh); | ||
265 | current_fh->fh_handle.fh_size = putfh->pf_fhlen; | ||
266 | memcpy(¤t_fh->fh_handle.fh_base, putfh->pf_fhval, putfh->pf_fhlen); | ||
267 | return fh_verify(rqstp, current_fh, 0, MAY_NOP); | ||
268 | } | ||
269 | |||
270 | static inline int | ||
271 | nfsd4_putrootfh(struct svc_rqst *rqstp, struct svc_fh *current_fh) | ||
272 | { | ||
273 | int status; | ||
274 | |||
275 | fh_put(current_fh); | ||
276 | status = exp_pseudoroot(rqstp->rq_client, current_fh, | ||
277 | &rqstp->rq_chandle); | ||
278 | if (!status) | ||
279 | status = nfserrno(nfsd_setuser(rqstp, current_fh->fh_export)); | ||
280 | return status; | ||
281 | } | ||
282 | |||
283 | static inline int | ||
284 | nfsd4_restorefh(struct svc_fh *current_fh, struct svc_fh *save_fh) | ||
285 | { | ||
286 | if (!save_fh->fh_dentry) | ||
287 | return nfserr_restorefh; | ||
288 | |||
289 | fh_dup2(current_fh, save_fh); | ||
290 | return nfs_ok; | ||
291 | } | ||
292 | |||
293 | static inline int | ||
294 | nfsd4_savefh(struct svc_fh *current_fh, struct svc_fh *save_fh) | ||
295 | { | ||
296 | if (!current_fh->fh_dentry) | ||
297 | return nfserr_nofilehandle; | ||
298 | |||
299 | fh_dup2(save_fh, current_fh); | ||
300 | return nfs_ok; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * misc nfsv4 ops | ||
305 | */ | ||
306 | static inline int | ||
307 | nfsd4_access(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_access *access) | ||
308 | { | ||
309 | if (access->ac_req_access & ~NFS3_ACCESS_FULL) | ||
310 | return nfserr_inval; | ||
311 | |||
312 | access->ac_resp_access = access->ac_req_access; | ||
313 | return nfsd_access(rqstp, current_fh, &access->ac_resp_access, &access->ac_supported); | ||
314 | } | ||
315 | |||
316 | static inline int | ||
317 | nfsd4_commit(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_commit *commit) | ||
318 | { | ||
319 | int status; | ||
320 | |||
321 | u32 *p = (u32 *)commit->co_verf.data; | ||
322 | *p++ = nfssvc_boot.tv_sec; | ||
323 | *p++ = nfssvc_boot.tv_usec; | ||
324 | |||
325 | status = nfsd_commit(rqstp, current_fh, commit->co_offset, commit->co_count); | ||
326 | if (status == nfserr_symlink) | ||
327 | status = nfserr_inval; | ||
328 | return status; | ||
329 | } | ||
330 | |||
331 | static int | ||
332 | nfsd4_create(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_create *create) | ||
333 | { | ||
334 | struct svc_fh resfh; | ||
335 | int status; | ||
336 | dev_t rdev; | ||
337 | |||
338 | fh_init(&resfh, NFS4_FHSIZE); | ||
339 | |||
340 | status = fh_verify(rqstp, current_fh, S_IFDIR, MAY_CREATE); | ||
341 | if (status == nfserr_symlink) | ||
342 | status = nfserr_notdir; | ||
343 | if (status) | ||
344 | return status; | ||
345 | |||
346 | switch (create->cr_type) { | ||
347 | case NF4LNK: | ||
348 | /* ugh! we have to null-terminate the linktext, or | ||
349 | * vfs_symlink() will choke. it is always safe to | ||
350 | * null-terminate by brute force, since at worst we | ||
351 | * will overwrite the first byte of the create namelen | ||
352 | * in the XDR buffer, which has already been extracted | ||
353 | * during XDR decode. | ||
354 | */ | ||
355 | create->cr_linkname[create->cr_linklen] = 0; | ||
356 | |||
357 | status = nfsd_symlink(rqstp, current_fh, create->cr_name, | ||
358 | create->cr_namelen, create->cr_linkname, | ||
359 | create->cr_linklen, &resfh, &create->cr_iattr); | ||
360 | break; | ||
361 | |||
362 | case NF4BLK: | ||
363 | rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); | ||
364 | if (MAJOR(rdev) != create->cr_specdata1 || | ||
365 | MINOR(rdev) != create->cr_specdata2) | ||
366 | return nfserr_inval; | ||
367 | status = nfsd_create(rqstp, current_fh, create->cr_name, | ||
368 | create->cr_namelen, &create->cr_iattr, | ||
369 | S_IFBLK, rdev, &resfh); | ||
370 | break; | ||
371 | |||
372 | case NF4CHR: | ||
373 | rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); | ||
374 | if (MAJOR(rdev) != create->cr_specdata1 || | ||
375 | MINOR(rdev) != create->cr_specdata2) | ||
376 | return nfserr_inval; | ||
377 | status = nfsd_create(rqstp, current_fh, create->cr_name, | ||
378 | create->cr_namelen, &create->cr_iattr, | ||
379 | S_IFCHR, rdev, &resfh); | ||
380 | break; | ||
381 | |||
382 | case NF4SOCK: | ||
383 | status = nfsd_create(rqstp, current_fh, create->cr_name, | ||
384 | create->cr_namelen, &create->cr_iattr, | ||
385 | S_IFSOCK, 0, &resfh); | ||
386 | break; | ||
387 | |||
388 | case NF4FIFO: | ||
389 | status = nfsd_create(rqstp, current_fh, create->cr_name, | ||
390 | create->cr_namelen, &create->cr_iattr, | ||
391 | S_IFIFO, 0, &resfh); | ||
392 | break; | ||
393 | |||
394 | case NF4DIR: | ||
395 | create->cr_iattr.ia_valid &= ~ATTR_SIZE; | ||
396 | status = nfsd_create(rqstp, current_fh, create->cr_name, | ||
397 | create->cr_namelen, &create->cr_iattr, | ||
398 | S_IFDIR, 0, &resfh); | ||
399 | break; | ||
400 | |||
401 | default: | ||
402 | status = nfserr_badtype; | ||
403 | } | ||
404 | |||
405 | if (!status) { | ||
406 | fh_unlock(current_fh); | ||
407 | set_change_info(&create->cr_cinfo, current_fh); | ||
408 | fh_dup2(current_fh, &resfh); | ||
409 | } | ||
410 | |||
411 | fh_put(&resfh); | ||
412 | return status; | ||
413 | } | ||
414 | |||
415 | static inline int | ||
416 | nfsd4_getattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_getattr *getattr) | ||
417 | { | ||
418 | int status; | ||
419 | |||
420 | status = fh_verify(rqstp, current_fh, 0, MAY_NOP); | ||
421 | if (status) | ||
422 | return status; | ||
423 | |||
424 | if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) | ||
425 | return nfserr_inval; | ||
426 | |||
427 | getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; | ||
428 | getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; | ||
429 | |||
430 | getattr->ga_fhp = current_fh; | ||
431 | return nfs_ok; | ||
432 | } | ||
433 | |||
434 | static inline int | ||
435 | nfsd4_link(struct svc_rqst *rqstp, struct svc_fh *current_fh, | ||
436 | struct svc_fh *save_fh, struct nfsd4_link *link) | ||
437 | { | ||
438 | int status = nfserr_nofilehandle; | ||
439 | |||
440 | if (!save_fh->fh_dentry) | ||
441 | return status; | ||
442 | status = nfsd_link(rqstp, current_fh, link->li_name, link->li_namelen, save_fh); | ||
443 | if (!status) | ||
444 | set_change_info(&link->li_cinfo, current_fh); | ||
445 | return status; | ||
446 | } | ||
447 | |||
448 | static int | ||
449 | nfsd4_lookupp(struct svc_rqst *rqstp, struct svc_fh *current_fh) | ||
450 | { | ||
451 | struct svc_fh tmp_fh; | ||
452 | int ret; | ||
453 | |||
454 | fh_init(&tmp_fh, NFS4_FHSIZE); | ||
455 | if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh, | ||
456 | &rqstp->rq_chandle)) != 0) | ||
457 | return ret; | ||
458 | if (tmp_fh.fh_dentry == current_fh->fh_dentry) { | ||
459 | fh_put(&tmp_fh); | ||
460 | return nfserr_noent; | ||
461 | } | ||
462 | fh_put(&tmp_fh); | ||
463 | return nfsd_lookup(rqstp, current_fh, "..", 2, current_fh); | ||
464 | } | ||
465 | |||
466 | static inline int | ||
467 | nfsd4_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lookup *lookup) | ||
468 | { | ||
469 | return nfsd_lookup(rqstp, current_fh, lookup->lo_name, lookup->lo_len, current_fh); | ||
470 | } | ||
471 | |||
472 | static inline int | ||
473 | nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read) | ||
474 | { | ||
475 | int status; | ||
476 | struct file *filp = NULL; | ||
477 | |||
478 | /* no need to check permission - this will be done in nfsd_read() */ | ||
479 | |||
480 | if (read->rd_offset >= OFFSET_MAX) | ||
481 | return nfserr_inval; | ||
482 | |||
483 | nfs4_lock_state(); | ||
484 | /* check stateid */ | ||
485 | if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid, | ||
486 | CHECK_FH | RD_STATE, &filp))) { | ||
487 | dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); | ||
488 | goto out; | ||
489 | } | ||
490 | status = nfs_ok; | ||
491 | out: | ||
492 | nfs4_unlock_state(); | ||
493 | read->rd_rqstp = rqstp; | ||
494 | read->rd_fhp = current_fh; | ||
495 | read->rd_filp = filp; | ||
496 | return status; | ||
497 | } | ||
498 | |||
499 | static inline int | ||
500 | nfsd4_readdir(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_readdir *readdir) | ||
501 | { | ||
502 | u64 cookie = readdir->rd_cookie; | ||
503 | static const nfs4_verifier zeroverf; | ||
504 | |||
505 | /* no need to check permission - this will be done in nfsd_readdir() */ | ||
506 | |||
507 | if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) | ||
508 | return nfserr_inval; | ||
509 | |||
510 | readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; | ||
511 | readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; | ||
512 | |||
513 | if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || | ||
514 | (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) | ||
515 | return nfserr_bad_cookie; | ||
516 | |||
517 | readdir->rd_rqstp = rqstp; | ||
518 | readdir->rd_fhp = current_fh; | ||
519 | return nfs_ok; | ||
520 | } | ||
521 | |||
522 | static inline int | ||
523 | nfsd4_readlink(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_readlink *readlink) | ||
524 | { | ||
525 | readlink->rl_rqstp = rqstp; | ||
526 | readlink->rl_fhp = current_fh; | ||
527 | return nfs_ok; | ||
528 | } | ||
529 | |||
530 | static inline int | ||
531 | nfsd4_remove(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_remove *remove) | ||
532 | { | ||
533 | int status; | ||
534 | |||
535 | status = nfsd_unlink(rqstp, current_fh, 0, remove->rm_name, remove->rm_namelen); | ||
536 | if (status == nfserr_symlink) | ||
537 | return nfserr_notdir; | ||
538 | if (!status) { | ||
539 | fh_unlock(current_fh); | ||
540 | set_change_info(&remove->rm_cinfo, current_fh); | ||
541 | } | ||
542 | return status; | ||
543 | } | ||
544 | |||
545 | static inline int | ||
546 | nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh, | ||
547 | struct svc_fh *save_fh, struct nfsd4_rename *rename) | ||
548 | { | ||
549 | int status = nfserr_nofilehandle; | ||
550 | |||
551 | if (!save_fh->fh_dentry) | ||
552 | return status; | ||
553 | status = nfsd_rename(rqstp, save_fh, rename->rn_sname, | ||
554 | rename->rn_snamelen, current_fh, | ||
555 | rename->rn_tname, rename->rn_tnamelen); | ||
556 | |||
557 | /* the underlying filesystem returns different error's than required | ||
558 | * by NFSv4. both save_fh and current_fh have been verified.. */ | ||
559 | if (status == nfserr_isdir) | ||
560 | status = nfserr_exist; | ||
561 | else if ((status == nfserr_notdir) && | ||
562 | (S_ISDIR(save_fh->fh_dentry->d_inode->i_mode) && | ||
563 | S_ISDIR(current_fh->fh_dentry->d_inode->i_mode))) | ||
564 | status = nfserr_exist; | ||
565 | else if (status == nfserr_symlink) | ||
566 | status = nfserr_notdir; | ||
567 | |||
568 | if (!status) { | ||
569 | set_change_info(&rename->rn_sinfo, current_fh); | ||
570 | set_change_info(&rename->rn_tinfo, save_fh); | ||
571 | } | ||
572 | return status; | ||
573 | } | ||
574 | |||
575 | static inline int | ||
576 | nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr) | ||
577 | { | ||
578 | int status = nfs_ok; | ||
579 | |||
580 | if (!current_fh->fh_dentry) | ||
581 | return nfserr_nofilehandle; | ||
582 | |||
583 | status = nfs_ok; | ||
584 | if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { | ||
585 | nfs4_lock_state(); | ||
586 | if ((status = nfs4_preprocess_stateid_op(current_fh, | ||
587 | &setattr->sa_stateid, | ||
588 | CHECK_FH | WR_STATE, NULL))) { | ||
589 | dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); | ||
590 | goto out_unlock; | ||
591 | } | ||
592 | nfs4_unlock_state(); | ||
593 | } | ||
594 | status = nfs_ok; | ||
595 | if (setattr->sa_acl != NULL) | ||
596 | status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl); | ||
597 | if (status) | ||
598 | goto out; | ||
599 | status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, | ||
600 | 0, (time_t)0); | ||
601 | out: | ||
602 | return status; | ||
603 | out_unlock: | ||
604 | nfs4_unlock_state(); | ||
605 | return status; | ||
606 | } | ||
607 | |||
608 | static inline int | ||
609 | nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_write *write) | ||
610 | { | ||
611 | stateid_t *stateid = &write->wr_stateid; | ||
612 | struct file *filp = NULL; | ||
613 | u32 *p; | ||
614 | int status = nfs_ok; | ||
615 | |||
616 | /* no need to check permission - this will be done in nfsd_write() */ | ||
617 | |||
618 | if (write->wr_offset >= OFFSET_MAX) | ||
619 | return nfserr_inval; | ||
620 | |||
621 | nfs4_lock_state(); | ||
622 | if ((status = nfs4_preprocess_stateid_op(current_fh, stateid, | ||
623 | CHECK_FH | WR_STATE, &filp))) { | ||
624 | dprintk("NFSD: nfsd4_write: couldn't process stateid!\n"); | ||
625 | goto out; | ||
626 | } | ||
627 | nfs4_unlock_state(); | ||
628 | |||
629 | write->wr_bytes_written = write->wr_buflen; | ||
630 | write->wr_how_written = write->wr_stable_how; | ||
631 | p = (u32 *)write->wr_verifier.data; | ||
632 | *p++ = nfssvc_boot.tv_sec; | ||
633 | *p++ = nfssvc_boot.tv_usec; | ||
634 | |||
635 | status = nfsd_write(rqstp, current_fh, filp, write->wr_offset, | ||
636 | write->wr_vec, write->wr_vlen, write->wr_buflen, | ||
637 | &write->wr_how_written); | ||
638 | |||
639 | if (status == nfserr_symlink) | ||
640 | status = nfserr_inval; | ||
641 | return status; | ||
642 | out: | ||
643 | nfs4_unlock_state(); | ||
644 | return status; | ||
645 | } | ||
646 | |||
647 | /* This routine never returns NFS_OK! If there are no other errors, it | ||
648 | * will return NFSERR_SAME or NFSERR_NOT_SAME depending on whether the | ||
649 | * attributes matched. VERIFY is implemented by mapping NFSERR_SAME | ||
650 | * to NFS_OK after the call; NVERIFY by mapping NFSERR_NOT_SAME to NFS_OK. | ||
651 | */ | ||
652 | static int | ||
653 | nfsd4_verify(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_verify *verify) | ||
654 | { | ||
655 | u32 *buf, *p; | ||
656 | int count; | ||
657 | int status; | ||
658 | |||
659 | status = fh_verify(rqstp, current_fh, 0, MAY_NOP); | ||
660 | if (status) | ||
661 | return status; | ||
662 | |||
663 | if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) | ||
664 | || (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1)) | ||
665 | return nfserr_attrnotsupp; | ||
666 | if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) | ||
667 | || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) | ||
668 | return nfserr_inval; | ||
669 | if (verify->ve_attrlen & 3) | ||
670 | return nfserr_inval; | ||
671 | |||
672 | /* count in words: | ||
673 | * bitmap_len(1) + bitmap(2) + attr_len(1) = 4 | ||
674 | */ | ||
675 | count = 4 + (verify->ve_attrlen >> 2); | ||
676 | buf = kmalloc(count << 2, GFP_KERNEL); | ||
677 | if (!buf) | ||
678 | return nfserr_resource; | ||
679 | |||
680 | status = nfsd4_encode_fattr(current_fh, current_fh->fh_export, | ||
681 | current_fh->fh_dentry, buf, | ||
682 | &count, verify->ve_bmval, | ||
683 | rqstp); | ||
684 | |||
685 | /* this means that nfsd4_encode_fattr() ran out of space */ | ||
686 | if (status == nfserr_resource && count == 0) | ||
687 | status = nfserr_not_same; | ||
688 | if (status) | ||
689 | goto out_kfree; | ||
690 | |||
691 | p = buf + 3; | ||
692 | status = nfserr_not_same; | ||
693 | if (ntohl(*p++) != verify->ve_attrlen) | ||
694 | goto out_kfree; | ||
695 | if (!memcmp(p, verify->ve_attrval, verify->ve_attrlen)) | ||
696 | status = nfserr_same; | ||
697 | |||
698 | out_kfree: | ||
699 | kfree(buf); | ||
700 | return status; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * NULL call. | ||
705 | */ | ||
706 | static int | ||
707 | nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) | ||
708 | { | ||
709 | return nfs_ok; | ||
710 | } | ||
711 | |||
712 | |||
713 | /* | ||
714 | * COMPOUND call. | ||
715 | */ | ||
716 | static int | ||
717 | nfsd4_proc_compound(struct svc_rqst *rqstp, | ||
718 | struct nfsd4_compoundargs *args, | ||
719 | struct nfsd4_compoundres *resp) | ||
720 | { | ||
721 | struct nfsd4_op *op; | ||
722 | struct svc_fh *current_fh = NULL; | ||
723 | struct svc_fh *save_fh = NULL; | ||
724 | struct nfs4_stateowner *replay_owner = NULL; | ||
725 | int slack_space; /* in words, not bytes! */ | ||
726 | int status; | ||
727 | |||
728 | status = nfserr_resource; | ||
729 | current_fh = kmalloc(sizeof(*current_fh), GFP_KERNEL); | ||
730 | if (current_fh == NULL) | ||
731 | goto out; | ||
732 | fh_init(current_fh, NFS4_FHSIZE); | ||
733 | save_fh = kmalloc(sizeof(*save_fh), GFP_KERNEL); | ||
734 | if (save_fh == NULL) | ||
735 | goto out; | ||
736 | fh_init(save_fh, NFS4_FHSIZE); | ||
737 | |||
738 | resp->xbuf = &rqstp->rq_res; | ||
739 | resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len; | ||
740 | resp->tagp = resp->p; | ||
741 | /* reserve space for: taglen, tag, and opcnt */ | ||
742 | resp->p += 2 + XDR_QUADLEN(args->taglen); | ||
743 | resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE; | ||
744 | resp->taglen = args->taglen; | ||
745 | resp->tag = args->tag; | ||
746 | resp->opcnt = 0; | ||
747 | resp->rqstp = rqstp; | ||
748 | |||
749 | /* | ||
750 | * According to RFC3010, this takes precedence over all other errors. | ||
751 | */ | ||
752 | status = nfserr_minor_vers_mismatch; | ||
753 | if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION) | ||
754 | goto out; | ||
755 | |||
756 | status = nfs_ok; | ||
757 | while (!status && resp->opcnt < args->opcnt) { | ||
758 | op = &args->ops[resp->opcnt++]; | ||
759 | |||
760 | /* | ||
761 | * The XDR decode routines may have pre-set op->status; | ||
762 | * for example, if there is a miscellaneous XDR error | ||
763 | * it will be set to nfserr_bad_xdr. | ||
764 | */ | ||
765 | if (op->status) | ||
766 | goto encode_op; | ||
767 | |||
768 | /* We must be able to encode a successful response to | ||
769 | * this operation, with enough room left over to encode a | ||
770 | * failed response to the next operation. If we don't | ||
771 | * have enough room, fail with ERR_RESOURCE. | ||
772 | */ | ||
773 | /* FIXME - is slack_space *really* words, or bytes??? - neilb */ | ||
774 | slack_space = (char *)resp->end - (char *)resp->p; | ||
775 | if (slack_space < COMPOUND_SLACK_SPACE + COMPOUND_ERR_SLACK_SPACE) { | ||
776 | BUG_ON(slack_space < COMPOUND_ERR_SLACK_SPACE); | ||
777 | op->status = nfserr_resource; | ||
778 | goto encode_op; | ||
779 | } | ||
780 | |||
781 | /* All operations except RENEW, SETCLIENTID, RESTOREFH | ||
782 | * SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH | ||
783 | * require a valid current filehandle | ||
784 | * | ||
785 | * SETATTR NOFILEHANDLE error handled in nfsd4_setattr | ||
786 | * due to required returned bitmap argument | ||
787 | */ | ||
788 | if ((!current_fh->fh_dentry) && | ||
789 | !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) || | ||
790 | (op->opnum == OP_SETCLIENTID) || | ||
791 | (op->opnum == OP_SETCLIENTID_CONFIRM) || | ||
792 | (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) || | ||
793 | (op->opnum == OP_RELEASE_LOCKOWNER) || | ||
794 | (op->opnum == OP_SETATTR))) { | ||
795 | op->status = nfserr_nofilehandle; | ||
796 | goto encode_op; | ||
797 | } | ||
798 | switch (op->opnum) { | ||
799 | case OP_ACCESS: | ||
800 | op->status = nfsd4_access(rqstp, current_fh, &op->u.access); | ||
801 | break; | ||
802 | case OP_CLOSE: | ||
803 | op->status = nfsd4_close(rqstp, current_fh, &op->u.close); | ||
804 | replay_owner = op->u.close.cl_stateowner; | ||
805 | break; | ||
806 | case OP_COMMIT: | ||
807 | op->status = nfsd4_commit(rqstp, current_fh, &op->u.commit); | ||
808 | break; | ||
809 | case OP_CREATE: | ||
810 | op->status = nfsd4_create(rqstp, current_fh, &op->u.create); | ||
811 | break; | ||
812 | case OP_DELEGRETURN: | ||
813 | op->status = nfsd4_delegreturn(rqstp, current_fh, &op->u.delegreturn); | ||
814 | break; | ||
815 | case OP_GETATTR: | ||
816 | op->status = nfsd4_getattr(rqstp, current_fh, &op->u.getattr); | ||
817 | break; | ||
818 | case OP_GETFH: | ||
819 | op->status = nfsd4_getfh(current_fh, &op->u.getfh); | ||
820 | break; | ||
821 | case OP_LINK: | ||
822 | op->status = nfsd4_link(rqstp, current_fh, save_fh, &op->u.link); | ||
823 | break; | ||
824 | case OP_LOCK: | ||
825 | op->status = nfsd4_lock(rqstp, current_fh, &op->u.lock); | ||
826 | replay_owner = op->u.lock.lk_stateowner; | ||
827 | break; | ||
828 | case OP_LOCKT: | ||
829 | op->status = nfsd4_lockt(rqstp, current_fh, &op->u.lockt); | ||
830 | break; | ||
831 | case OP_LOCKU: | ||
832 | op->status = nfsd4_locku(rqstp, current_fh, &op->u.locku); | ||
833 | replay_owner = op->u.locku.lu_stateowner; | ||
834 | break; | ||
835 | case OP_LOOKUP: | ||
836 | op->status = nfsd4_lookup(rqstp, current_fh, &op->u.lookup); | ||
837 | break; | ||
838 | case OP_LOOKUPP: | ||
839 | op->status = nfsd4_lookupp(rqstp, current_fh); | ||
840 | break; | ||
841 | case OP_NVERIFY: | ||
842 | op->status = nfsd4_verify(rqstp, current_fh, &op->u.nverify); | ||
843 | if (op->status == nfserr_not_same) | ||
844 | op->status = nfs_ok; | ||
845 | break; | ||
846 | case OP_OPEN: | ||
847 | op->status = nfsd4_open(rqstp, current_fh, &op->u.open); | ||
848 | replay_owner = op->u.open.op_stateowner; | ||
849 | break; | ||
850 | case OP_OPEN_CONFIRM: | ||
851 | op->status = nfsd4_open_confirm(rqstp, current_fh, &op->u.open_confirm); | ||
852 | replay_owner = op->u.open_confirm.oc_stateowner; | ||
853 | break; | ||
854 | case OP_OPEN_DOWNGRADE: | ||
855 | op->status = nfsd4_open_downgrade(rqstp, current_fh, &op->u.open_downgrade); | ||
856 | replay_owner = op->u.open_downgrade.od_stateowner; | ||
857 | break; | ||
858 | case OP_PUTFH: | ||
859 | op->status = nfsd4_putfh(rqstp, current_fh, &op->u.putfh); | ||
860 | break; | ||
861 | case OP_PUTROOTFH: | ||
862 | op->status = nfsd4_putrootfh(rqstp, current_fh); | ||
863 | break; | ||
864 | case OP_READ: | ||
865 | op->status = nfsd4_read(rqstp, current_fh, &op->u.read); | ||
866 | break; | ||
867 | case OP_READDIR: | ||
868 | op->status = nfsd4_readdir(rqstp, current_fh, &op->u.readdir); | ||
869 | break; | ||
870 | case OP_READLINK: | ||
871 | op->status = nfsd4_readlink(rqstp, current_fh, &op->u.readlink); | ||
872 | break; | ||
873 | case OP_REMOVE: | ||
874 | op->status = nfsd4_remove(rqstp, current_fh, &op->u.remove); | ||
875 | break; | ||
876 | case OP_RENAME: | ||
877 | op->status = nfsd4_rename(rqstp, current_fh, save_fh, &op->u.rename); | ||
878 | break; | ||
879 | case OP_RENEW: | ||
880 | op->status = nfsd4_renew(&op->u.renew); | ||
881 | break; | ||
882 | case OP_RESTOREFH: | ||
883 | op->status = nfsd4_restorefh(current_fh, save_fh); | ||
884 | break; | ||
885 | case OP_SAVEFH: | ||
886 | op->status = nfsd4_savefh(current_fh, save_fh); | ||
887 | break; | ||
888 | case OP_SETATTR: | ||
889 | op->status = nfsd4_setattr(rqstp, current_fh, &op->u.setattr); | ||
890 | break; | ||
891 | case OP_SETCLIENTID: | ||
892 | op->status = nfsd4_setclientid(rqstp, &op->u.setclientid); | ||
893 | break; | ||
894 | case OP_SETCLIENTID_CONFIRM: | ||
895 | op->status = nfsd4_setclientid_confirm(rqstp, &op->u.setclientid_confirm); | ||
896 | break; | ||
897 | case OP_VERIFY: | ||
898 | op->status = nfsd4_verify(rqstp, current_fh, &op->u.verify); | ||
899 | if (op->status == nfserr_same) | ||
900 | op->status = nfs_ok; | ||
901 | break; | ||
902 | case OP_WRITE: | ||
903 | op->status = nfsd4_write(rqstp, current_fh, &op->u.write); | ||
904 | break; | ||
905 | case OP_RELEASE_LOCKOWNER: | ||
906 | op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner); | ||
907 | break; | ||
908 | default: | ||
909 | BUG_ON(op->status == nfs_ok); | ||
910 | break; | ||
911 | } | ||
912 | |||
913 | encode_op: | ||
914 | if (op->status == NFSERR_REPLAY_ME) { | ||
915 | op->replay = &replay_owner->so_replay; | ||
916 | nfsd4_encode_replay(resp, op); | ||
917 | status = op->status = op->replay->rp_status; | ||
918 | } else { | ||
919 | nfsd4_encode_operation(resp, op); | ||
920 | status = op->status; | ||
921 | } | ||
922 | if (replay_owner && (replay_owner != (void *)(-1))) { | ||
923 | nfs4_put_stateowner(replay_owner); | ||
924 | replay_owner = NULL; | ||
925 | } | ||
926 | } | ||
927 | |||
928 | out: | ||
929 | nfsd4_release_compoundargs(args); | ||
930 | if (current_fh) | ||
931 | fh_put(current_fh); | ||
932 | kfree(current_fh); | ||
933 | if (save_fh) | ||
934 | fh_put(save_fh); | ||
935 | kfree(save_fh); | ||
936 | return status; | ||
937 | } | ||
938 | |||
939 | #define nfs4svc_decode_voidargs NULL | ||
940 | #define nfs4svc_release_void NULL | ||
941 | #define nfsd4_voidres nfsd4_voidargs | ||
942 | #define nfs4svc_release_compound NULL | ||
943 | struct nfsd4_voidargs { int dummy; }; | ||
944 | |||
945 | #define PROC(name, argt, rest, relt, cache, respsize) \ | ||
946 | { (svc_procfunc) nfsd4_proc_##name, \ | ||
947 | (kxdrproc_t) nfs4svc_decode_##argt##args, \ | ||
948 | (kxdrproc_t) nfs4svc_encode_##rest##res, \ | ||
949 | (kxdrproc_t) nfs4svc_release_##relt, \ | ||
950 | sizeof(struct nfsd4_##argt##args), \ | ||
951 | sizeof(struct nfsd4_##rest##res), \ | ||
952 | 0, \ | ||
953 | cache, \ | ||
954 | respsize, \ | ||
955 | } | ||
956 | |||
957 | /* | ||
958 | * TODO: At the present time, the NFSv4 server does not do XID caching | ||
959 | * of requests. Implementing XID caching would not be a serious problem, | ||
960 | * although it would require a mild change in interfaces since one | ||
961 | * doesn't know whether an NFSv4 request is idempotent until after the | ||
962 | * XDR decode. However, XID caching totally confuses pynfs (Peter | ||
963 | * Astrand's regression testsuite for NFSv4 servers), which reuses | ||
964 | * XID's liberally, so I've left it unimplemented until pynfs generates | ||
965 | * better XID's. | ||
966 | */ | ||
967 | static struct svc_procedure nfsd_procedures4[2] = { | ||
968 | PROC(null, void, void, void, RC_NOCACHE, 1), | ||
969 | PROC(compound, compound, compound, compound, RC_NOCACHE, NFSD_BUFSIZE) | ||
970 | }; | ||
971 | |||
972 | struct svc_version nfsd_version4 = { | ||
973 | .vs_vers = 4, | ||
974 | .vs_nproc = 2, | ||
975 | .vs_proc = nfsd_procedures4, | ||
976 | .vs_dispatch = nfsd_dispatch, | ||
977 | .vs_xdrsize = NFS4_SVC_XDRSIZE, | ||
978 | }; | ||
979 | |||
980 | /* | ||
981 | * Local variables: | ||
982 | * c-basic-offset: 8 | ||
983 | * End: | ||
984 | */ | ||
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c new file mode 100644 index 000000000000..579f7fea7968 --- /dev/null +++ b/fs/nfsd/nfs4state.c | |||
@@ -0,0 +1,3320 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfs4state.c | ||
3 | * | ||
4 | * Copyright (c) 2001 The Regents of the University of Michigan. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Kendrick Smith <kmsmith@umich.edu> | ||
8 | * Andy Adamson <kandros@umich.edu> | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions | ||
12 | * are met: | ||
13 | * | ||
14 | * 1. Redistributions of source code must retain the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer. | ||
16 | * 2. Redistributions in binary form must reproduce the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer in the | ||
18 | * documentation and/or other materials provided with the distribution. | ||
19 | * 3. Neither the name of the University nor the names of its | ||
20 | * contributors may be used to endorse or promote products derived | ||
21 | * from this software without specific prior written permission. | ||
22 | * | ||
23 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
24 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
26 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
30 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
34 | * | ||
35 | */ | ||
36 | |||
37 | #include <linux/param.h> | ||
38 | #include <linux/major.h> | ||
39 | #include <linux/slab.h> | ||
40 | |||
41 | #include <linux/sunrpc/svc.h> | ||
42 | #include <linux/nfsd/nfsd.h> | ||
43 | #include <linux/nfsd/cache.h> | ||
44 | #include <linux/mount.h> | ||
45 | #include <linux/workqueue.h> | ||
46 | #include <linux/smp_lock.h> | ||
47 | #include <linux/kthread.h> | ||
48 | #include <linux/nfs4.h> | ||
49 | #include <linux/nfsd/state.h> | ||
50 | #include <linux/nfsd/xdr4.h> | ||
51 | |||
52 | #define NFSDDBG_FACILITY NFSDDBG_PROC | ||
53 | |||
54 | /* Globals */ | ||
55 | static time_t lease_time = 90; /* default lease time */ | ||
56 | static time_t old_lease_time = 90; /* past incarnation lease time */ | ||
57 | static u32 nfs4_reclaim_init = 0; | ||
58 | time_t boot_time; | ||
59 | static time_t grace_end = 0; | ||
60 | static u32 current_clientid = 1; | ||
61 | static u32 current_ownerid = 1; | ||
62 | static u32 current_fileid = 1; | ||
63 | static u32 current_delegid = 1; | ||
64 | static u32 nfs4_init; | ||
65 | stateid_t zerostateid; /* bits all 0 */ | ||
66 | stateid_t onestateid; /* bits all 1 */ | ||
67 | |||
68 | /* debug counters */ | ||
69 | u32 list_add_perfile = 0; | ||
70 | u32 list_del_perfile = 0; | ||
71 | u32 add_perclient = 0; | ||
72 | u32 del_perclient = 0; | ||
73 | u32 alloc_file = 0; | ||
74 | u32 free_file = 0; | ||
75 | u32 vfsopen = 0; | ||
76 | u32 vfsclose = 0; | ||
77 | u32 alloc_delegation= 0; | ||
78 | u32 free_delegation= 0; | ||
79 | |||
80 | /* forward declarations */ | ||
81 | struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); | ||
82 | static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); | ||
83 | static void release_stateid_lockowners(struct nfs4_stateid *open_stp); | ||
84 | |||
85 | /* Locking: | ||
86 | * | ||
87 | * client_sema: | ||
88 | * protects clientid_hashtbl[], clientstr_hashtbl[], | ||
89 | * unconfstr_hashtbl[], uncofid_hashtbl[]. | ||
90 | */ | ||
91 | static DECLARE_MUTEX(client_sema); | ||
92 | |||
93 | void | ||
94 | nfs4_lock_state(void) | ||
95 | { | ||
96 | down(&client_sema); | ||
97 | } | ||
98 | |||
99 | void | ||
100 | nfs4_unlock_state(void) | ||
101 | { | ||
102 | up(&client_sema); | ||
103 | } | ||
104 | |||
105 | static inline u32 | ||
106 | opaque_hashval(const void *ptr, int nbytes) | ||
107 | { | ||
108 | unsigned char *cptr = (unsigned char *) ptr; | ||
109 | |||
110 | u32 x = 0; | ||
111 | while (nbytes--) { | ||
112 | x *= 37; | ||
113 | x += *cptr++; | ||
114 | } | ||
115 | return x; | ||
116 | } | ||
117 | |||
118 | /* forward declarations */ | ||
119 | static void release_stateowner(struct nfs4_stateowner *sop); | ||
120 | static void release_stateid(struct nfs4_stateid *stp, int flags); | ||
121 | static void release_file(struct nfs4_file *fp); | ||
122 | |||
123 | /* | ||
124 | * Delegation state | ||
125 | */ | ||
126 | |||
127 | /* recall_lock protects the del_recall_lru */ | ||
128 | spinlock_t recall_lock; | ||
129 | static struct list_head del_recall_lru; | ||
130 | |||
131 | static struct nfs4_delegation * | ||
132 | alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) | ||
133 | { | ||
134 | struct nfs4_delegation *dp; | ||
135 | struct nfs4_file *fp = stp->st_file; | ||
136 | struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; | ||
137 | |||
138 | dprintk("NFSD alloc_init_deleg\n"); | ||
139 | if ((dp = kmalloc(sizeof(struct nfs4_delegation), | ||
140 | GFP_KERNEL)) == NULL) | ||
141 | return dp; | ||
142 | INIT_LIST_HEAD(&dp->dl_del_perfile); | ||
143 | INIT_LIST_HEAD(&dp->dl_del_perclnt); | ||
144 | INIT_LIST_HEAD(&dp->dl_recall_lru); | ||
145 | dp->dl_client = clp; | ||
146 | dp->dl_file = fp; | ||
147 | dp->dl_flock = NULL; | ||
148 | get_file(stp->st_vfs_file); | ||
149 | dp->dl_vfs_file = stp->st_vfs_file; | ||
150 | dp->dl_type = type; | ||
151 | dp->dl_recall.cbr_dp = NULL; | ||
152 | dp->dl_recall.cbr_ident = cb->cb_ident; | ||
153 | dp->dl_recall.cbr_trunc = 0; | ||
154 | dp->dl_stateid.si_boot = boot_time; | ||
155 | dp->dl_stateid.si_stateownerid = current_delegid++; | ||
156 | dp->dl_stateid.si_fileid = 0; | ||
157 | dp->dl_stateid.si_generation = 0; | ||
158 | dp->dl_fhlen = current_fh->fh_handle.fh_size; | ||
159 | memcpy(dp->dl_fhval, ¤t_fh->fh_handle.fh_base, | ||
160 | current_fh->fh_handle.fh_size); | ||
161 | dp->dl_time = 0; | ||
162 | atomic_set(&dp->dl_count, 1); | ||
163 | list_add(&dp->dl_del_perfile, &fp->fi_del_perfile); | ||
164 | list_add(&dp->dl_del_perclnt, &clp->cl_del_perclnt); | ||
165 | alloc_delegation++; | ||
166 | return dp; | ||
167 | } | ||
168 | |||
169 | void | ||
170 | nfs4_put_delegation(struct nfs4_delegation *dp) | ||
171 | { | ||
172 | if (atomic_dec_and_test(&dp->dl_count)) { | ||
173 | dprintk("NFSD: freeing dp %p\n",dp); | ||
174 | kfree(dp); | ||
175 | free_delegation++; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* Remove the associated file_lock first, then remove the delegation. | ||
180 | * lease_modify() is called to remove the FS_LEASE file_lock from | ||
181 | * the i_flock list, eventually calling nfsd's lock_manager | ||
182 | * fl_release_callback. | ||
183 | */ | ||
184 | static void | ||
185 | nfs4_close_delegation(struct nfs4_delegation *dp) | ||
186 | { | ||
187 | struct file *filp = dp->dl_vfs_file; | ||
188 | |||
189 | dprintk("NFSD: close_delegation dp %p\n",dp); | ||
190 | dp->dl_vfs_file = NULL; | ||
191 | /* The following nfsd_close may not actually close the file, | ||
192 | * but we want to remove the lease in any case. */ | ||
193 | setlease(filp, F_UNLCK, &dp->dl_flock); | ||
194 | nfsd_close(filp); | ||
195 | vfsclose++; | ||
196 | } | ||
197 | |||
198 | /* Called under the state lock. */ | ||
199 | static void | ||
200 | unhash_delegation(struct nfs4_delegation *dp) | ||
201 | { | ||
202 | list_del_init(&dp->dl_del_perfile); | ||
203 | list_del_init(&dp->dl_del_perclnt); | ||
204 | spin_lock(&recall_lock); | ||
205 | list_del_init(&dp->dl_recall_lru); | ||
206 | spin_unlock(&recall_lock); | ||
207 | nfs4_close_delegation(dp); | ||
208 | nfs4_put_delegation(dp); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * SETCLIENTID state | ||
213 | */ | ||
214 | |||
215 | /* Hash tables for nfs4_clientid state */ | ||
216 | #define CLIENT_HASH_BITS 4 | ||
217 | #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) | ||
218 | #define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) | ||
219 | |||
220 | #define clientid_hashval(id) \ | ||
221 | ((id) & CLIENT_HASH_MASK) | ||
222 | #define clientstr_hashval(name, namelen) \ | ||
223 | (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK) | ||
224 | /* | ||
225 | * reclaim_str_hashtbl[] holds known client info from previous reset/reboot | ||
226 | * used in reboot/reset lease grace period processing | ||
227 | * | ||
228 | * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed | ||
229 | * setclientid_confirmed info. | ||
230 | * | ||
231 | * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed | ||
232 | * setclientid info. | ||
233 | * | ||
234 | * client_lru holds client queue ordered by nfs4_client.cl_time | ||
235 | * for lease renewal. | ||
236 | * | ||
237 | * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time | ||
238 | * for last close replay. | ||
239 | */ | ||
240 | static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; | ||
241 | static int reclaim_str_hashtbl_size = 0; | ||
242 | static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; | ||
243 | static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE]; | ||
244 | static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE]; | ||
245 | static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; | ||
246 | static struct list_head client_lru; | ||
247 | static struct list_head close_lru; | ||
248 | |||
249 | static inline void | ||
250 | renew_client(struct nfs4_client *clp) | ||
251 | { | ||
252 | /* | ||
253 | * Move client to the end to the LRU list. | ||
254 | */ | ||
255 | dprintk("renewing client (clientid %08x/%08x)\n", | ||
256 | clp->cl_clientid.cl_boot, | ||
257 | clp->cl_clientid.cl_id); | ||
258 | list_move_tail(&clp->cl_lru, &client_lru); | ||
259 | clp->cl_time = get_seconds(); | ||
260 | } | ||
261 | |||
262 | /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ | ||
263 | static int | ||
264 | STALE_CLIENTID(clientid_t *clid) | ||
265 | { | ||
266 | if (clid->cl_boot == boot_time) | ||
267 | return 0; | ||
268 | dprintk("NFSD stale clientid (%08x/%08x)\n", | ||
269 | clid->cl_boot, clid->cl_id); | ||
270 | return 1; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * XXX Should we use a slab cache ? | ||
275 | * This type of memory management is somewhat inefficient, but we use it | ||
276 | * anyway since SETCLIENTID is not a common operation. | ||
277 | */ | ||
278 | static inline struct nfs4_client * | ||
279 | alloc_client(struct xdr_netobj name) | ||
280 | { | ||
281 | struct nfs4_client *clp; | ||
282 | |||
283 | if ((clp = kmalloc(sizeof(struct nfs4_client), GFP_KERNEL))!= NULL) { | ||
284 | memset(clp, 0, sizeof(*clp)); | ||
285 | if ((clp->cl_name.data = kmalloc(name.len, GFP_KERNEL)) != NULL) { | ||
286 | memcpy(clp->cl_name.data, name.data, name.len); | ||
287 | clp->cl_name.len = name.len; | ||
288 | } | ||
289 | else { | ||
290 | kfree(clp); | ||
291 | clp = NULL; | ||
292 | } | ||
293 | } | ||
294 | return clp; | ||
295 | } | ||
296 | |||
297 | static inline void | ||
298 | free_client(struct nfs4_client *clp) | ||
299 | { | ||
300 | if (clp->cl_cred.cr_group_info) | ||
301 | put_group_info(clp->cl_cred.cr_group_info); | ||
302 | kfree(clp->cl_name.data); | ||
303 | kfree(clp); | ||
304 | } | ||
305 | |||
306 | void | ||
307 | put_nfs4_client(struct nfs4_client *clp) | ||
308 | { | ||
309 | if (atomic_dec_and_test(&clp->cl_count)) | ||
310 | free_client(clp); | ||
311 | } | ||
312 | |||
313 | static void | ||
314 | expire_client(struct nfs4_client *clp) | ||
315 | { | ||
316 | struct nfs4_stateowner *sop; | ||
317 | struct nfs4_delegation *dp; | ||
318 | struct nfs4_callback *cb = &clp->cl_callback; | ||
319 | struct rpc_clnt *clnt = clp->cl_callback.cb_client; | ||
320 | struct list_head reaplist; | ||
321 | |||
322 | dprintk("NFSD: expire_client cl_count %d\n", | ||
323 | atomic_read(&clp->cl_count)); | ||
324 | |||
325 | /* shutdown rpc client, ending any outstanding recall rpcs */ | ||
326 | if (atomic_read(&cb->cb_set) == 1 && clnt) { | ||
327 | rpc_shutdown_client(clnt); | ||
328 | clnt = clp->cl_callback.cb_client = NULL; | ||
329 | } | ||
330 | |||
331 | INIT_LIST_HEAD(&reaplist); | ||
332 | spin_lock(&recall_lock); | ||
333 | while (!list_empty(&clp->cl_del_perclnt)) { | ||
334 | dp = list_entry(clp->cl_del_perclnt.next, struct nfs4_delegation, dl_del_perclnt); | ||
335 | dprintk("NFSD: expire client. dp %p, fp %p\n", dp, | ||
336 | dp->dl_flock); | ||
337 | list_del_init(&dp->dl_del_perclnt); | ||
338 | list_move(&dp->dl_recall_lru, &reaplist); | ||
339 | } | ||
340 | spin_unlock(&recall_lock); | ||
341 | while (!list_empty(&reaplist)) { | ||
342 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); | ||
343 | list_del_init(&dp->dl_recall_lru); | ||
344 | unhash_delegation(dp); | ||
345 | } | ||
346 | list_del(&clp->cl_idhash); | ||
347 | list_del(&clp->cl_strhash); | ||
348 | list_del(&clp->cl_lru); | ||
349 | while (!list_empty(&clp->cl_perclient)) { | ||
350 | sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient); | ||
351 | release_stateowner(sop); | ||
352 | } | ||
353 | put_nfs4_client(clp); | ||
354 | } | ||
355 | |||
356 | static struct nfs4_client * | ||
357 | create_client(struct xdr_netobj name) { | ||
358 | struct nfs4_client *clp; | ||
359 | |||
360 | if (!(clp = alloc_client(name))) | ||
361 | goto out; | ||
362 | atomic_set(&clp->cl_count, 1); | ||
363 | atomic_set(&clp->cl_callback.cb_set, 0); | ||
364 | clp->cl_callback.cb_parsed = 0; | ||
365 | INIT_LIST_HEAD(&clp->cl_idhash); | ||
366 | INIT_LIST_HEAD(&clp->cl_strhash); | ||
367 | INIT_LIST_HEAD(&clp->cl_perclient); | ||
368 | INIT_LIST_HEAD(&clp->cl_del_perclnt); | ||
369 | INIT_LIST_HEAD(&clp->cl_lru); | ||
370 | out: | ||
371 | return clp; | ||
372 | } | ||
373 | |||
374 | static void | ||
375 | copy_verf(struct nfs4_client *target, nfs4_verifier *source) { | ||
376 | memcpy(target->cl_verifier.data, source->data, sizeof(target->cl_verifier.data)); | ||
377 | } | ||
378 | |||
379 | static void | ||
380 | copy_clid(struct nfs4_client *target, struct nfs4_client *source) { | ||
381 | target->cl_clientid.cl_boot = source->cl_clientid.cl_boot; | ||
382 | target->cl_clientid.cl_id = source->cl_clientid.cl_id; | ||
383 | } | ||
384 | |||
385 | static void | ||
386 | copy_cred(struct svc_cred *target, struct svc_cred *source) { | ||
387 | |||
388 | target->cr_uid = source->cr_uid; | ||
389 | target->cr_gid = source->cr_gid; | ||
390 | target->cr_group_info = source->cr_group_info; | ||
391 | get_group_info(target->cr_group_info); | ||
392 | } | ||
393 | |||
394 | static int | ||
395 | cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { | ||
396 | if (!n1 || !n2) | ||
397 | return 0; | ||
398 | return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len)); | ||
399 | } | ||
400 | |||
401 | static int | ||
402 | cmp_verf(nfs4_verifier *v1, nfs4_verifier *v2) { | ||
403 | return(!memcmp(v1->data,v2->data,sizeof(v1->data))); | ||
404 | } | ||
405 | |||
406 | static int | ||
407 | cmp_clid(clientid_t * cl1, clientid_t * cl2) { | ||
408 | return((cl1->cl_boot == cl2->cl_boot) && | ||
409 | (cl1->cl_id == cl2->cl_id)); | ||
410 | } | ||
411 | |||
412 | /* XXX what about NGROUP */ | ||
413 | static int | ||
414 | cmp_creds(struct svc_cred *cr1, struct svc_cred *cr2){ | ||
415 | return(cr1->cr_uid == cr2->cr_uid); | ||
416 | |||
417 | } | ||
418 | |||
419 | static void | ||
420 | gen_clid(struct nfs4_client *clp) { | ||
421 | clp->cl_clientid.cl_boot = boot_time; | ||
422 | clp->cl_clientid.cl_id = current_clientid++; | ||
423 | } | ||
424 | |||
425 | static void | ||
426 | gen_confirm(struct nfs4_client *clp) { | ||
427 | struct timespec tv; | ||
428 | u32 * p; | ||
429 | |||
430 | tv = CURRENT_TIME; | ||
431 | p = (u32 *)clp->cl_confirm.data; | ||
432 | *p++ = tv.tv_sec; | ||
433 | *p++ = tv.tv_nsec; | ||
434 | } | ||
435 | |||
436 | static int | ||
437 | check_name(struct xdr_netobj name) { | ||
438 | |||
439 | if (name.len == 0) | ||
440 | return 0; | ||
441 | if (name.len > NFS4_OPAQUE_LIMIT) { | ||
442 | printk("NFSD: check_name: name too long(%d)!\n", name.len); | ||
443 | return 0; | ||
444 | } | ||
445 | return 1; | ||
446 | } | ||
447 | |||
448 | void | ||
449 | add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) | ||
450 | { | ||
451 | unsigned int idhashval; | ||
452 | |||
453 | list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); | ||
454 | idhashval = clientid_hashval(clp->cl_clientid.cl_id); | ||
455 | list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); | ||
456 | list_add_tail(&clp->cl_lru, &client_lru); | ||
457 | clp->cl_time = get_seconds(); | ||
458 | } | ||
459 | |||
460 | void | ||
461 | move_to_confirmed(struct nfs4_client *clp) | ||
462 | { | ||
463 | unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); | ||
464 | unsigned int strhashval; | ||
465 | |||
466 | dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); | ||
467 | list_del_init(&clp->cl_strhash); | ||
468 | list_del_init(&clp->cl_idhash); | ||
469 | list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); | ||
470 | strhashval = clientstr_hashval(clp->cl_name.data, | ||
471 | clp->cl_name.len); | ||
472 | list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); | ||
473 | renew_client(clp); | ||
474 | } | ||
475 | |||
476 | static struct nfs4_client * | ||
477 | find_confirmed_client(clientid_t *clid) | ||
478 | { | ||
479 | struct nfs4_client *clp; | ||
480 | unsigned int idhashval = clientid_hashval(clid->cl_id); | ||
481 | |||
482 | list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { | ||
483 | if (cmp_clid(&clp->cl_clientid, clid)) | ||
484 | return clp; | ||
485 | } | ||
486 | return NULL; | ||
487 | } | ||
488 | |||
489 | static struct nfs4_client * | ||
490 | find_unconfirmed_client(clientid_t *clid) | ||
491 | { | ||
492 | struct nfs4_client *clp; | ||
493 | unsigned int idhashval = clientid_hashval(clid->cl_id); | ||
494 | |||
495 | list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { | ||
496 | if (cmp_clid(&clp->cl_clientid, clid)) | ||
497 | return clp; | ||
498 | } | ||
499 | return NULL; | ||
500 | } | ||
501 | |||
502 | /* a helper function for parse_callback */ | ||
503 | static int | ||
504 | parse_octet(unsigned int *lenp, char **addrp) | ||
505 | { | ||
506 | unsigned int len = *lenp; | ||
507 | char *p = *addrp; | ||
508 | int n = -1; | ||
509 | char c; | ||
510 | |||
511 | for (;;) { | ||
512 | if (!len) | ||
513 | break; | ||
514 | len--; | ||
515 | c = *p++; | ||
516 | if (c == '.') | ||
517 | break; | ||
518 | if ((c < '0') || (c > '9')) { | ||
519 | n = -1; | ||
520 | break; | ||
521 | } | ||
522 | if (n < 0) | ||
523 | n = 0; | ||
524 | n = (n * 10) + (c - '0'); | ||
525 | if (n > 255) { | ||
526 | n = -1; | ||
527 | break; | ||
528 | } | ||
529 | } | ||
530 | *lenp = len; | ||
531 | *addrp = p; | ||
532 | return n; | ||
533 | } | ||
534 | |||
535 | /* parse and set the setclientid ipv4 callback address */ | ||
536 | int | ||
537 | parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp) | ||
538 | { | ||
539 | int temp = 0; | ||
540 | u32 cbaddr = 0; | ||
541 | u16 cbport = 0; | ||
542 | u32 addrlen = addr_len; | ||
543 | char *addr = addr_val; | ||
544 | int i, shift; | ||
545 | |||
546 | /* ipaddress */ | ||
547 | shift = 24; | ||
548 | for(i = 4; i > 0 ; i--) { | ||
549 | if ((temp = parse_octet(&addrlen, &addr)) < 0) { | ||
550 | return 0; | ||
551 | } | ||
552 | cbaddr |= (temp << shift); | ||
553 | if (shift > 0) | ||
554 | shift -= 8; | ||
555 | } | ||
556 | *cbaddrp = cbaddr; | ||
557 | |||
558 | /* port */ | ||
559 | shift = 8; | ||
560 | for(i = 2; i > 0 ; i--) { | ||
561 | if ((temp = parse_octet(&addrlen, &addr)) < 0) { | ||
562 | return 0; | ||
563 | } | ||
564 | cbport |= (temp << shift); | ||
565 | if (shift > 0) | ||
566 | shift -= 8; | ||
567 | } | ||
568 | *cbportp = cbport; | ||
569 | return 1; | ||
570 | } | ||
571 | |||
572 | void | ||
573 | gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) | ||
574 | { | ||
575 | struct nfs4_callback *cb = &clp->cl_callback; | ||
576 | |||
577 | /* Currently, we only support tcp for the callback channel */ | ||
578 | if ((se->se_callback_netid_len != 3) || memcmp((char *)se->se_callback_netid_val, "tcp", 3)) | ||
579 | goto out_err; | ||
580 | |||
581 | if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val, | ||
582 | &cb->cb_addr, &cb->cb_port))) | ||
583 | goto out_err; | ||
584 | cb->cb_prog = se->se_callback_prog; | ||
585 | cb->cb_ident = se->se_callback_ident; | ||
586 | cb->cb_parsed = 1; | ||
587 | return; | ||
588 | out_err: | ||
589 | printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) " | ||
590 | "will not receive delegations\n", | ||
591 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
592 | |||
593 | cb->cb_parsed = 0; | ||
594 | return; | ||
595 | } | ||
596 | |||
597 | /* | ||
598 | * RFC 3010 has a complex implmentation description of processing a | ||
599 | * SETCLIENTID request consisting of 5 bullets, labeled as | ||
600 | * CASE0 - CASE4 below. | ||
601 | * | ||
602 | * NOTES: | ||
603 | * callback information will be processed in a future patch | ||
604 | * | ||
605 | * an unconfirmed record is added when: | ||
606 | * NORMAL (part of CASE 4): there is no confirmed nor unconfirmed record. | ||
607 | * CASE 1: confirmed record found with matching name, principal, | ||
608 | * verifier, and clientid. | ||
609 | * CASE 2: confirmed record found with matching name, principal, | ||
610 | * and there is no unconfirmed record with matching | ||
611 | * name and principal | ||
612 | * | ||
613 | * an unconfirmed record is replaced when: | ||
614 | * CASE 3: confirmed record found with matching name, principal, | ||
615 | * and an unconfirmed record is found with matching | ||
616 | * name, principal, and with clientid and | ||
617 | * confirm that does not match the confirmed record. | ||
618 | * CASE 4: there is no confirmed record with matching name and | ||
619 | * principal. there is an unconfirmed record with | ||
620 | * matching name, principal. | ||
621 | * | ||
622 | * an unconfirmed record is deleted when: | ||
623 | * CASE 1: an unconfirmed record that matches input name, verifier, | ||
624 | * and confirmed clientid. | ||
625 | * CASE 4: any unconfirmed records with matching name and principal | ||
626 | * that exist after an unconfirmed record has been replaced | ||
627 | * as described above. | ||
628 | * | ||
629 | */ | ||
630 | int | ||
631 | nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | ||
632 | { | ||
633 | u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; | ||
634 | struct xdr_netobj clname = { | ||
635 | .len = setclid->se_namelen, | ||
636 | .data = setclid->se_name, | ||
637 | }; | ||
638 | nfs4_verifier clverifier = setclid->se_verf; | ||
639 | unsigned int strhashval; | ||
640 | struct nfs4_client * conf, * unconf, * new, * clp; | ||
641 | int status; | ||
642 | |||
643 | status = nfserr_inval; | ||
644 | if (!check_name(clname)) | ||
645 | goto out; | ||
646 | |||
647 | /* | ||
648 | * XXX The Duplicate Request Cache (DRC) has been checked (??) | ||
649 | * We get here on a DRC miss. | ||
650 | */ | ||
651 | |||
652 | strhashval = clientstr_hashval(clname.data, clname.len); | ||
653 | |||
654 | conf = NULL; | ||
655 | nfs4_lock_state(); | ||
656 | list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) { | ||
657 | if (!cmp_name(&clp->cl_name, &clname)) | ||
658 | continue; | ||
659 | /* | ||
660 | * CASE 0: | ||
661 | * clname match, confirmed, different principal | ||
662 | * or different ip_address | ||
663 | */ | ||
664 | status = nfserr_clid_inuse; | ||
665 | if (!cmp_creds(&clp->cl_cred,&rqstp->rq_cred)) { | ||
666 | printk("NFSD: setclientid: string in use by client" | ||
667 | "(clientid %08x/%08x)\n", | ||
668 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
669 | goto out; | ||
670 | } | ||
671 | if (clp->cl_addr != ip_addr) { | ||
672 | printk("NFSD: setclientid: string in use by client" | ||
673 | "(clientid %08x/%08x)\n", | ||
674 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
675 | goto out; | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * cl_name match from a previous SETCLIENTID operation | ||
680 | * XXX check for additional matches? | ||
681 | */ | ||
682 | conf = clp; | ||
683 | break; | ||
684 | } | ||
685 | unconf = NULL; | ||
686 | list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) { | ||
687 | if (!cmp_name(&clp->cl_name, &clname)) | ||
688 | continue; | ||
689 | /* cl_name match from a previous SETCLIENTID operation */ | ||
690 | unconf = clp; | ||
691 | break; | ||
692 | } | ||
693 | status = nfserr_resource; | ||
694 | if (!conf) { | ||
695 | /* | ||
696 | * CASE 4: | ||
697 | * placed first, because it is the normal case. | ||
698 | */ | ||
699 | if (unconf) | ||
700 | expire_client(unconf); | ||
701 | if (!(new = create_client(clname))) | ||
702 | goto out; | ||
703 | copy_verf(new, &clverifier); | ||
704 | new->cl_addr = ip_addr; | ||
705 | copy_cred(&new->cl_cred,&rqstp->rq_cred); | ||
706 | gen_clid(new); | ||
707 | gen_confirm(new); | ||
708 | gen_callback(new, setclid); | ||
709 | add_to_unconfirmed(new, strhashval); | ||
710 | } else if (cmp_verf(&conf->cl_verifier, &clverifier)) { | ||
711 | /* | ||
712 | * CASE 1: | ||
713 | * cl_name match, confirmed, principal match | ||
714 | * verifier match: probable callback update | ||
715 | * | ||
716 | * remove any unconfirmed nfs4_client with | ||
717 | * matching cl_name, cl_verifier, and cl_clientid | ||
718 | * | ||
719 | * create and insert an unconfirmed nfs4_client with same | ||
720 | * cl_name, cl_verifier, and cl_clientid as existing | ||
721 | * nfs4_client, but with the new callback info and a | ||
722 | * new cl_confirm | ||
723 | */ | ||
724 | if ((unconf) && | ||
725 | cmp_verf(&unconf->cl_verifier, &conf->cl_verifier) && | ||
726 | cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) { | ||
727 | expire_client(unconf); | ||
728 | } | ||
729 | if (!(new = create_client(clname))) | ||
730 | goto out; | ||
731 | copy_verf(new,&conf->cl_verifier); | ||
732 | new->cl_addr = ip_addr; | ||
733 | copy_cred(&new->cl_cred,&rqstp->rq_cred); | ||
734 | copy_clid(new, conf); | ||
735 | gen_confirm(new); | ||
736 | gen_callback(new, setclid); | ||
737 | add_to_unconfirmed(new,strhashval); | ||
738 | } else if (!unconf) { | ||
739 | /* | ||
740 | * CASE 2: | ||
741 | * clname match, confirmed, principal match | ||
742 | * verfier does not match | ||
743 | * no unconfirmed. create a new unconfirmed nfs4_client | ||
744 | * using input clverifier, clname, and callback info | ||
745 | * and generate a new cl_clientid and cl_confirm. | ||
746 | */ | ||
747 | if (!(new = create_client(clname))) | ||
748 | goto out; | ||
749 | copy_verf(new,&clverifier); | ||
750 | new->cl_addr = ip_addr; | ||
751 | copy_cred(&new->cl_cred,&rqstp->rq_cred); | ||
752 | gen_clid(new); | ||
753 | gen_confirm(new); | ||
754 | gen_callback(new, setclid); | ||
755 | add_to_unconfirmed(new, strhashval); | ||
756 | } else if (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) { | ||
757 | /* | ||
758 | * CASE3: | ||
759 | * confirmed found (name, principal match) | ||
760 | * confirmed verifier does not match input clverifier | ||
761 | * | ||
762 | * unconfirmed found (name match) | ||
763 | * confirmed->cl_confirm != unconfirmed->cl_confirm | ||
764 | * | ||
765 | * remove unconfirmed. | ||
766 | * | ||
767 | * create an unconfirmed nfs4_client | ||
768 | * with same cl_name as existing confirmed nfs4_client, | ||
769 | * but with new callback info, new cl_clientid, | ||
770 | * new cl_verifier and a new cl_confirm | ||
771 | */ | ||
772 | expire_client(unconf); | ||
773 | if (!(new = create_client(clname))) | ||
774 | goto out; | ||
775 | copy_verf(new,&clverifier); | ||
776 | new->cl_addr = ip_addr; | ||
777 | copy_cred(&new->cl_cred,&rqstp->rq_cred); | ||
778 | gen_clid(new); | ||
779 | gen_confirm(new); | ||
780 | gen_callback(new, setclid); | ||
781 | add_to_unconfirmed(new, strhashval); | ||
782 | } else { | ||
783 | /* No cases hit !!! */ | ||
784 | status = nfserr_inval; | ||
785 | goto out; | ||
786 | |||
787 | } | ||
788 | setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; | ||
789 | setclid->se_clientid.cl_id = new->cl_clientid.cl_id; | ||
790 | memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data)); | ||
791 | status = nfs_ok; | ||
792 | out: | ||
793 | nfs4_unlock_state(); | ||
794 | return status; | ||
795 | } | ||
796 | |||
797 | |||
798 | /* | ||
799 | * RFC 3010 has a complex implmentation description of processing a | ||
800 | * SETCLIENTID_CONFIRM request consisting of 4 bullets describing | ||
801 | * processing on a DRC miss, labeled as CASE1 - CASE4 below. | ||
802 | * | ||
803 | * NOTE: callback information will be processed here in a future patch | ||
804 | */ | ||
805 | int | ||
806 | nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confirm *setclientid_confirm) | ||
807 | { | ||
808 | u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; | ||
809 | struct nfs4_client *clp, *conf = NULL, *unconf = NULL; | ||
810 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; | ||
811 | clientid_t * clid = &setclientid_confirm->sc_clientid; | ||
812 | int status; | ||
813 | |||
814 | if (STALE_CLIENTID(clid)) | ||
815 | return nfserr_stale_clientid; | ||
816 | /* | ||
817 | * XXX The Duplicate Request Cache (DRC) has been checked (??) | ||
818 | * We get here on a DRC miss. | ||
819 | */ | ||
820 | |||
821 | nfs4_lock_state(); | ||
822 | clp = find_confirmed_client(clid); | ||
823 | if (clp) { | ||
824 | status = nfserr_inval; | ||
825 | /* | ||
826 | * Found a record for this clientid. If the IP addresses | ||
827 | * don't match, return ERR_INVAL just as if the record had | ||
828 | * not been found. | ||
829 | */ | ||
830 | if (clp->cl_addr != ip_addr) { | ||
831 | printk("NFSD: setclientid: string in use by client" | ||
832 | "(clientid %08x/%08x)\n", | ||
833 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
834 | goto out; | ||
835 | } | ||
836 | conf = clp; | ||
837 | } | ||
838 | clp = find_unconfirmed_client(clid); | ||
839 | if (clp) { | ||
840 | status = nfserr_inval; | ||
841 | if (clp->cl_addr != ip_addr) { | ||
842 | printk("NFSD: setclientid: string in use by client" | ||
843 | "(clientid %08x/%08x)\n", | ||
844 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
845 | goto out; | ||
846 | } | ||
847 | unconf = clp; | ||
848 | } | ||
849 | /* CASE 1: | ||
850 | * unconf record that matches input clientid and input confirm. | ||
851 | * conf record that matches input clientid. | ||
852 | * conf and unconf records match names, verifiers | ||
853 | */ | ||
854 | if ((conf && unconf) && | ||
855 | (cmp_verf(&unconf->cl_confirm, &confirm)) && | ||
856 | (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) && | ||
857 | (cmp_name(&conf->cl_name,&unconf->cl_name)) && | ||
858 | (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) { | ||
859 | if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred)) | ||
860 | status = nfserr_clid_inuse; | ||
861 | else { | ||
862 | expire_client(conf); | ||
863 | clp = unconf; | ||
864 | move_to_confirmed(unconf); | ||
865 | status = nfs_ok; | ||
866 | } | ||
867 | goto out; | ||
868 | } | ||
869 | /* CASE 2: | ||
870 | * conf record that matches input clientid. | ||
871 | * if unconf record that matches input clientid, then unconf->cl_name | ||
872 | * or unconf->cl_verifier don't match the conf record. | ||
873 | */ | ||
874 | if ((conf && !unconf) || | ||
875 | ((conf && unconf) && | ||
876 | (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) || | ||
877 | !cmp_name(&conf->cl_name, &unconf->cl_name)))) { | ||
878 | if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) { | ||
879 | status = nfserr_clid_inuse; | ||
880 | } else { | ||
881 | clp = conf; | ||
882 | status = nfs_ok; | ||
883 | } | ||
884 | goto out; | ||
885 | } | ||
886 | /* CASE 3: | ||
887 | * conf record not found. | ||
888 | * unconf record found. | ||
889 | * unconf->cl_confirm matches input confirm | ||
890 | */ | ||
891 | if (!conf && unconf && cmp_verf(&unconf->cl_confirm, &confirm)) { | ||
892 | if (!cmp_creds(&unconf->cl_cred, &rqstp->rq_cred)) { | ||
893 | status = nfserr_clid_inuse; | ||
894 | } else { | ||
895 | status = nfs_ok; | ||
896 | clp = unconf; | ||
897 | move_to_confirmed(unconf); | ||
898 | } | ||
899 | goto out; | ||
900 | } | ||
901 | /* CASE 4: | ||
902 | * conf record not found, or if conf, then conf->cl_confirm does not | ||
903 | * match input confirm. | ||
904 | * unconf record not found, or if unconf, then unconf->cl_confirm | ||
905 | * does not match input confirm. | ||
906 | */ | ||
907 | if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) && | ||
908 | (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, &confirm)))) { | ||
909 | status = nfserr_stale_clientid; | ||
910 | goto out; | ||
911 | } | ||
912 | /* check that we have hit one of the cases...*/ | ||
913 | status = nfserr_inval; | ||
914 | goto out; | ||
915 | out: | ||
916 | if (!status) | ||
917 | nfsd4_probe_callback(clp); | ||
918 | nfs4_unlock_state(); | ||
919 | return status; | ||
920 | } | ||
921 | |||
922 | /* | ||
923 | * Open owner state (share locks) | ||
924 | */ | ||
925 | |||
926 | /* hash tables for nfs4_stateowner */ | ||
927 | #define OWNER_HASH_BITS 8 | ||
928 | #define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS) | ||
929 | #define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1) | ||
930 | |||
931 | #define ownerid_hashval(id) \ | ||
932 | ((id) & OWNER_HASH_MASK) | ||
933 | #define ownerstr_hashval(clientid, ownername) \ | ||
934 | (((clientid) + opaque_hashval((ownername.data), (ownername.len))) & OWNER_HASH_MASK) | ||
935 | |||
936 | static struct list_head ownerid_hashtbl[OWNER_HASH_SIZE]; | ||
937 | static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; | ||
938 | |||
939 | /* hash table for nfs4_file */ | ||
940 | #define FILE_HASH_BITS 8 | ||
941 | #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) | ||
942 | #define FILE_HASH_MASK (FILE_HASH_SIZE - 1) | ||
943 | /* hash table for (open)nfs4_stateid */ | ||
944 | #define STATEID_HASH_BITS 10 | ||
945 | #define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) | ||
946 | #define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) | ||
947 | |||
948 | #define file_hashval(x) \ | ||
949 | hash_ptr(x, FILE_HASH_BITS) | ||
950 | #define stateid_hashval(owner_id, file_id) \ | ||
951 | (((owner_id) + (file_id)) & STATEID_HASH_MASK) | ||
952 | |||
953 | static struct list_head file_hashtbl[FILE_HASH_SIZE]; | ||
954 | static struct list_head stateid_hashtbl[STATEID_HASH_SIZE]; | ||
955 | |||
956 | /* OPEN Share state helper functions */ | ||
957 | static inline struct nfs4_file * | ||
958 | alloc_init_file(struct inode *ino) | ||
959 | { | ||
960 | struct nfs4_file *fp; | ||
961 | unsigned int hashval = file_hashval(ino); | ||
962 | |||
963 | if ((fp = kmalloc(sizeof(struct nfs4_file),GFP_KERNEL))) { | ||
964 | INIT_LIST_HEAD(&fp->fi_hash); | ||
965 | INIT_LIST_HEAD(&fp->fi_perfile); | ||
966 | INIT_LIST_HEAD(&fp->fi_del_perfile); | ||
967 | list_add(&fp->fi_hash, &file_hashtbl[hashval]); | ||
968 | fp->fi_inode = igrab(ino); | ||
969 | fp->fi_id = current_fileid++; | ||
970 | alloc_file++; | ||
971 | return fp; | ||
972 | } | ||
973 | return NULL; | ||
974 | } | ||
975 | |||
976 | static void | ||
977 | release_all_files(void) | ||
978 | { | ||
979 | int i; | ||
980 | struct nfs4_file *fp; | ||
981 | |||
982 | for (i=0;i<FILE_HASH_SIZE;i++) { | ||
983 | while (!list_empty(&file_hashtbl[i])) { | ||
984 | fp = list_entry(file_hashtbl[i].next, struct nfs4_file, fi_hash); | ||
985 | /* this should never be more than once... */ | ||
986 | if (!list_empty(&fp->fi_perfile) || !list_empty(&fp->fi_del_perfile)) { | ||
987 | printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); | ||
988 | } | ||
989 | release_file(fp); | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | kmem_cache_t *stateowner_slab = NULL; | ||
995 | |||
996 | static int | ||
997 | nfsd4_init_slabs(void) | ||
998 | { | ||
999 | stateowner_slab = kmem_cache_create("nfsd4_stateowners", | ||
1000 | sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL); | ||
1001 | if (stateowner_slab == NULL) { | ||
1002 | dprintk("nfsd4: out of memory while initializing nfsv4\n"); | ||
1003 | return -ENOMEM; | ||
1004 | } | ||
1005 | return 0; | ||
1006 | } | ||
1007 | |||
1008 | static void | ||
1009 | nfsd4_free_slabs(void) | ||
1010 | { | ||
1011 | int status = 0; | ||
1012 | |||
1013 | if (stateowner_slab) | ||
1014 | status = kmem_cache_destroy(stateowner_slab); | ||
1015 | stateowner_slab = NULL; | ||
1016 | BUG_ON(status); | ||
1017 | } | ||
1018 | |||
1019 | void | ||
1020 | nfs4_free_stateowner(struct kref *kref) | ||
1021 | { | ||
1022 | struct nfs4_stateowner *sop = | ||
1023 | container_of(kref, struct nfs4_stateowner, so_ref); | ||
1024 | kfree(sop->so_owner.data); | ||
1025 | kmem_cache_free(stateowner_slab, sop); | ||
1026 | } | ||
1027 | |||
1028 | static inline struct nfs4_stateowner * | ||
1029 | alloc_stateowner(struct xdr_netobj *owner) | ||
1030 | { | ||
1031 | struct nfs4_stateowner *sop; | ||
1032 | |||
1033 | if ((sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL))) { | ||
1034 | if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { | ||
1035 | memcpy(sop->so_owner.data, owner->data, owner->len); | ||
1036 | sop->so_owner.len = owner->len; | ||
1037 | kref_init(&sop->so_ref); | ||
1038 | return sop; | ||
1039 | } | ||
1040 | kmem_cache_free(stateowner_slab, sop); | ||
1041 | } | ||
1042 | return NULL; | ||
1043 | } | ||
1044 | |||
1045 | static struct nfs4_stateowner * | ||
1046 | alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) { | ||
1047 | struct nfs4_stateowner *sop; | ||
1048 | struct nfs4_replay *rp; | ||
1049 | unsigned int idhashval; | ||
1050 | |||
1051 | if (!(sop = alloc_stateowner(&open->op_owner))) | ||
1052 | return NULL; | ||
1053 | idhashval = ownerid_hashval(current_ownerid); | ||
1054 | INIT_LIST_HEAD(&sop->so_idhash); | ||
1055 | INIT_LIST_HEAD(&sop->so_strhash); | ||
1056 | INIT_LIST_HEAD(&sop->so_perclient); | ||
1057 | INIT_LIST_HEAD(&sop->so_perfilestate); | ||
1058 | INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */ | ||
1059 | INIT_LIST_HEAD(&sop->so_close_lru); | ||
1060 | sop->so_time = 0; | ||
1061 | list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); | ||
1062 | list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); | ||
1063 | list_add(&sop->so_perclient, &clp->cl_perclient); | ||
1064 | add_perclient++; | ||
1065 | sop->so_is_open_owner = 1; | ||
1066 | sop->so_id = current_ownerid++; | ||
1067 | sop->so_client = clp; | ||
1068 | sop->so_seqid = open->op_seqid; | ||
1069 | sop->so_confirmed = 0; | ||
1070 | rp = &sop->so_replay; | ||
1071 | rp->rp_status = NFSERR_SERVERFAULT; | ||
1072 | rp->rp_buflen = 0; | ||
1073 | rp->rp_buf = rp->rp_ibuf; | ||
1074 | return sop; | ||
1075 | } | ||
1076 | |||
1077 | static void | ||
1078 | release_stateid_lockowners(struct nfs4_stateid *open_stp) | ||
1079 | { | ||
1080 | struct nfs4_stateowner *lock_sop; | ||
1081 | |||
1082 | while (!list_empty(&open_stp->st_perlockowner)) { | ||
1083 | lock_sop = list_entry(open_stp->st_perlockowner.next, | ||
1084 | struct nfs4_stateowner, so_perlockowner); | ||
1085 | /* list_del(&open_stp->st_perlockowner); */ | ||
1086 | BUG_ON(lock_sop->so_is_open_owner); | ||
1087 | release_stateowner(lock_sop); | ||
1088 | } | ||
1089 | } | ||
1090 | |||
1091 | static void | ||
1092 | unhash_stateowner(struct nfs4_stateowner *sop) | ||
1093 | { | ||
1094 | struct nfs4_stateid *stp; | ||
1095 | |||
1096 | list_del(&sop->so_idhash); | ||
1097 | list_del(&sop->so_strhash); | ||
1098 | if (sop->so_is_open_owner) { | ||
1099 | list_del(&sop->so_perclient); | ||
1100 | del_perclient++; | ||
1101 | } | ||
1102 | list_del(&sop->so_perlockowner); | ||
1103 | while (!list_empty(&sop->so_perfilestate)) { | ||
1104 | stp = list_entry(sop->so_perfilestate.next, | ||
1105 | struct nfs4_stateid, st_perfilestate); | ||
1106 | if (sop->so_is_open_owner) | ||
1107 | release_stateid(stp, OPEN_STATE); | ||
1108 | else | ||
1109 | release_stateid(stp, LOCK_STATE); | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | static void | ||
1114 | release_stateowner(struct nfs4_stateowner *sop) | ||
1115 | { | ||
1116 | unhash_stateowner(sop); | ||
1117 | list_del(&sop->so_close_lru); | ||
1118 | nfs4_put_stateowner(sop); | ||
1119 | } | ||
1120 | |||
1121 | static inline void | ||
1122 | init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { | ||
1123 | struct nfs4_stateowner *sop = open->op_stateowner; | ||
1124 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); | ||
1125 | |||
1126 | INIT_LIST_HEAD(&stp->st_hash); | ||
1127 | INIT_LIST_HEAD(&stp->st_perfilestate); | ||
1128 | INIT_LIST_HEAD(&stp->st_perlockowner); | ||
1129 | INIT_LIST_HEAD(&stp->st_perfile); | ||
1130 | list_add(&stp->st_hash, &stateid_hashtbl[hashval]); | ||
1131 | list_add(&stp->st_perfilestate, &sop->so_perfilestate); | ||
1132 | list_add_perfile++; | ||
1133 | list_add(&stp->st_perfile, &fp->fi_perfile); | ||
1134 | stp->st_stateowner = sop; | ||
1135 | stp->st_file = fp; | ||
1136 | stp->st_stateid.si_boot = boot_time; | ||
1137 | stp->st_stateid.si_stateownerid = sop->so_id; | ||
1138 | stp->st_stateid.si_fileid = fp->fi_id; | ||
1139 | stp->st_stateid.si_generation = 0; | ||
1140 | stp->st_access_bmap = 0; | ||
1141 | stp->st_deny_bmap = 0; | ||
1142 | __set_bit(open->op_share_access, &stp->st_access_bmap); | ||
1143 | __set_bit(open->op_share_deny, &stp->st_deny_bmap); | ||
1144 | } | ||
1145 | |||
1146 | static void | ||
1147 | release_stateid(struct nfs4_stateid *stp, int flags) | ||
1148 | { | ||
1149 | struct file *filp = stp->st_vfs_file; | ||
1150 | |||
1151 | list_del(&stp->st_hash); | ||
1152 | list_del_perfile++; | ||
1153 | list_del(&stp->st_perfile); | ||
1154 | list_del(&stp->st_perfilestate); | ||
1155 | if (flags & OPEN_STATE) { | ||
1156 | release_stateid_lockowners(stp); | ||
1157 | stp->st_vfs_file = NULL; | ||
1158 | nfsd_close(filp); | ||
1159 | vfsclose++; | ||
1160 | } else if (flags & LOCK_STATE) | ||
1161 | locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); | ||
1162 | kfree(stp); | ||
1163 | stp = NULL; | ||
1164 | } | ||
1165 | |||
1166 | static void | ||
1167 | release_file(struct nfs4_file *fp) | ||
1168 | { | ||
1169 | free_file++; | ||
1170 | list_del(&fp->fi_hash); | ||
1171 | iput(fp->fi_inode); | ||
1172 | kfree(fp); | ||
1173 | } | ||
1174 | |||
1175 | void | ||
1176 | move_to_close_lru(struct nfs4_stateowner *sop) | ||
1177 | { | ||
1178 | dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); | ||
1179 | |||
1180 | unhash_stateowner(sop); | ||
1181 | list_add_tail(&sop->so_close_lru, &close_lru); | ||
1182 | sop->so_time = get_seconds(); | ||
1183 | } | ||
1184 | |||
1185 | void | ||
1186 | release_state_owner(struct nfs4_stateid *stp, int flag) | ||
1187 | { | ||
1188 | struct nfs4_stateowner *sop = stp->st_stateowner; | ||
1189 | struct nfs4_file *fp = stp->st_file; | ||
1190 | |||
1191 | dprintk("NFSD: release_state_owner\n"); | ||
1192 | release_stateid(stp, flag); | ||
1193 | |||
1194 | /* place unused nfs4_stateowners on so_close_lru list to be | ||
1195 | * released by the laundromat service after the lease period | ||
1196 | * to enable us to handle CLOSE replay | ||
1197 | */ | ||
1198 | if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) | ||
1199 | move_to_close_lru(sop); | ||
1200 | /* unused nfs4_file's are releseed. XXX slab cache? */ | ||
1201 | if (list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) { | ||
1202 | release_file(fp); | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | static int | ||
1207 | cmp_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, clientid_t *clid) { | ||
1208 | return ((sop->so_owner.len == owner->len) && | ||
1209 | !memcmp(sop->so_owner.data, owner->data, owner->len) && | ||
1210 | (sop->so_client->cl_clientid.cl_id == clid->cl_id)); | ||
1211 | } | ||
1212 | |||
1213 | static struct nfs4_stateowner * | ||
1214 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) | ||
1215 | { | ||
1216 | struct nfs4_stateowner *so = NULL; | ||
1217 | |||
1218 | list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { | ||
1219 | if (cmp_owner_str(so, &open->op_owner, &open->op_clientid)) | ||
1220 | return so; | ||
1221 | } | ||
1222 | return NULL; | ||
1223 | } | ||
1224 | |||
1225 | /* search file_hashtbl[] for file */ | ||
1226 | static struct nfs4_file * | ||
1227 | find_file(struct inode *ino) | ||
1228 | { | ||
1229 | unsigned int hashval = file_hashval(ino); | ||
1230 | struct nfs4_file *fp; | ||
1231 | |||
1232 | list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { | ||
1233 | if (fp->fi_inode == ino) | ||
1234 | return fp; | ||
1235 | } | ||
1236 | return NULL; | ||
1237 | } | ||
1238 | |||
1239 | #define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0) | ||
1240 | #define TEST_DENY(x) ((x >= 0 || x < 5)?1:0) | ||
1241 | |||
1242 | void | ||
1243 | set_access(unsigned int *access, unsigned long bmap) { | ||
1244 | int i; | ||
1245 | |||
1246 | *access = 0; | ||
1247 | for (i = 1; i < 4; i++) { | ||
1248 | if (test_bit(i, &bmap)) | ||
1249 | *access |= i; | ||
1250 | } | ||
1251 | } | ||
1252 | |||
1253 | void | ||
1254 | set_deny(unsigned int *deny, unsigned long bmap) { | ||
1255 | int i; | ||
1256 | |||
1257 | *deny = 0; | ||
1258 | for (i = 0; i < 4; i++) { | ||
1259 | if (test_bit(i, &bmap)) | ||
1260 | *deny |= i ; | ||
1261 | } | ||
1262 | } | ||
1263 | |||
1264 | static int | ||
1265 | test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { | ||
1266 | unsigned int access, deny; | ||
1267 | |||
1268 | set_access(&access, stp->st_access_bmap); | ||
1269 | set_deny(&deny, stp->st_deny_bmap); | ||
1270 | if ((access & open->op_share_deny) || (deny & open->op_share_access)) | ||
1271 | return 0; | ||
1272 | return 1; | ||
1273 | } | ||
1274 | |||
1275 | /* | ||
1276 | * Called to check deny when READ with all zero stateid or | ||
1277 | * WRITE with all zero or all one stateid | ||
1278 | */ | ||
1279 | int | ||
1280 | nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) | ||
1281 | { | ||
1282 | struct inode *ino = current_fh->fh_dentry->d_inode; | ||
1283 | struct nfs4_file *fp; | ||
1284 | struct nfs4_stateid *stp; | ||
1285 | |||
1286 | dprintk("NFSD: nfs4_share_conflict\n"); | ||
1287 | |||
1288 | fp = find_file(ino); | ||
1289 | if (fp) { | ||
1290 | /* Search for conflicting share reservations */ | ||
1291 | list_for_each_entry(stp, &fp->fi_perfile, st_perfile) { | ||
1292 | if (test_bit(deny_type, &stp->st_deny_bmap) || | ||
1293 | test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) | ||
1294 | return nfserr_share_denied; | ||
1295 | } | ||
1296 | } | ||
1297 | return nfs_ok; | ||
1298 | } | ||
1299 | |||
1300 | static inline void | ||
1301 | nfs4_file_downgrade(struct file *filp, unsigned int share_access) | ||
1302 | { | ||
1303 | if (share_access & NFS4_SHARE_ACCESS_WRITE) { | ||
1304 | put_write_access(filp->f_dentry->d_inode); | ||
1305 | filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE; | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | /* | ||
1310 | * Recall a delegation | ||
1311 | */ | ||
1312 | static int | ||
1313 | do_recall(void *__dp) | ||
1314 | { | ||
1315 | struct nfs4_delegation *dp = __dp; | ||
1316 | |||
1317 | daemonize("nfsv4-recall"); | ||
1318 | |||
1319 | nfsd4_cb_recall(dp); | ||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | /* | ||
1324 | * Spawn a thread to perform a recall on the delegation represented | ||
1325 | * by the lease (file_lock) | ||
1326 | * | ||
1327 | * Called from break_lease() with lock_kernel() held. | ||
1328 | * Note: we assume break_lease will only call this *once* for any given | ||
1329 | * lease. | ||
1330 | */ | ||
1331 | static | ||
1332 | void nfsd_break_deleg_cb(struct file_lock *fl) | ||
1333 | { | ||
1334 | struct nfs4_delegation *dp= (struct nfs4_delegation *)fl->fl_owner; | ||
1335 | struct task_struct *t; | ||
1336 | |||
1337 | dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl); | ||
1338 | if (!dp) | ||
1339 | return; | ||
1340 | |||
1341 | /* We're assuming the state code never drops its reference | ||
1342 | * without first removing the lease. Since we're in this lease | ||
1343 | * callback (and since the lease code is serialized by the kernel | ||
1344 | * lock) we know the server hasn't removed the lease yet, we know | ||
1345 | * it's safe to take a reference: */ | ||
1346 | atomic_inc(&dp->dl_count); | ||
1347 | |||
1348 | spin_lock(&recall_lock); | ||
1349 | list_add_tail(&dp->dl_recall_lru, &del_recall_lru); | ||
1350 | spin_unlock(&recall_lock); | ||
1351 | |||
1352 | /* only place dl_time is set. protected by lock_kernel*/ | ||
1353 | dp->dl_time = get_seconds(); | ||
1354 | |||
1355 | /* XXX need to merge NFSD_LEASE_TIME with fs/locks.c:lease_break_time */ | ||
1356 | fl->fl_break_time = jiffies + NFSD_LEASE_TIME * HZ; | ||
1357 | |||
1358 | t = kthread_run(do_recall, dp, "%s", "nfs4_cb_recall"); | ||
1359 | if (IS_ERR(t)) { | ||
1360 | struct nfs4_client *clp = dp->dl_client; | ||
1361 | |||
1362 | printk(KERN_INFO "NFSD: Callback thread failed for " | ||
1363 | "for client (clientid %08x/%08x)\n", | ||
1364 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
1365 | nfs4_put_delegation(dp); | ||
1366 | } | ||
1367 | } | ||
1368 | |||
1369 | /* | ||
1370 | * The file_lock is being reapd. | ||
1371 | * | ||
1372 | * Called by locks_free_lock() with lock_kernel() held. | ||
1373 | */ | ||
1374 | static | ||
1375 | void nfsd_release_deleg_cb(struct file_lock *fl) | ||
1376 | { | ||
1377 | struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; | ||
1378 | |||
1379 | dprintk("NFSD nfsd_release_deleg_cb: fl %p dp %p dl_count %d\n", fl,dp, atomic_read(&dp->dl_count)); | ||
1380 | |||
1381 | if (!(fl->fl_flags & FL_LEASE) || !dp) | ||
1382 | return; | ||
1383 | dp->dl_flock = NULL; | ||
1384 | } | ||
1385 | |||
1386 | /* | ||
1387 | * Set the delegation file_lock back pointer. | ||
1388 | * | ||
1389 | * Called from __setlease() with lock_kernel() held. | ||
1390 | */ | ||
1391 | static | ||
1392 | void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) | ||
1393 | { | ||
1394 | struct nfs4_delegation *dp = (struct nfs4_delegation *)new->fl_owner; | ||
1395 | |||
1396 | dprintk("NFSD: nfsd_copy_lock_deleg_cb: new fl %p dp %p\n", new, dp); | ||
1397 | if (!dp) | ||
1398 | return; | ||
1399 | dp->dl_flock = new; | ||
1400 | } | ||
1401 | |||
1402 | /* | ||
1403 | * Called from __setlease() with lock_kernel() held | ||
1404 | */ | ||
1405 | static | ||
1406 | int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try) | ||
1407 | { | ||
1408 | struct nfs4_delegation *onlistd = | ||
1409 | (struct nfs4_delegation *)onlist->fl_owner; | ||
1410 | struct nfs4_delegation *tryd = | ||
1411 | (struct nfs4_delegation *)try->fl_owner; | ||
1412 | |||
1413 | if (onlist->fl_lmops != try->fl_lmops) | ||
1414 | return 0; | ||
1415 | |||
1416 | return onlistd->dl_client == tryd->dl_client; | ||
1417 | } | ||
1418 | |||
1419 | |||
1420 | static | ||
1421 | int nfsd_change_deleg_cb(struct file_lock **onlist, int arg) | ||
1422 | { | ||
1423 | if (arg & F_UNLCK) | ||
1424 | return lease_modify(onlist, arg); | ||
1425 | else | ||
1426 | return -EAGAIN; | ||
1427 | } | ||
1428 | |||
1429 | struct lock_manager_operations nfsd_lease_mng_ops = { | ||
1430 | .fl_break = nfsd_break_deleg_cb, | ||
1431 | .fl_release_private = nfsd_release_deleg_cb, | ||
1432 | .fl_copy_lock = nfsd_copy_lock_deleg_cb, | ||
1433 | .fl_mylease = nfsd_same_client_deleg_cb, | ||
1434 | .fl_change = nfsd_change_deleg_cb, | ||
1435 | }; | ||
1436 | |||
1437 | |||
1438 | /* | ||
1439 | * nfsd4_process_open1() | ||
1440 | * lookup stateowner. | ||
1441 | * found: | ||
1442 | * check confirmed | ||
1443 | * confirmed: | ||
1444 | * check seqid | ||
1445 | * not confirmed: | ||
1446 | * delete owner | ||
1447 | * create new owner | ||
1448 | * notfound: | ||
1449 | * verify clientid | ||
1450 | * create new owner | ||
1451 | * | ||
1452 | * called with nfs4_lock_state() held. | ||
1453 | */ | ||
1454 | int | ||
1455 | nfsd4_process_open1(struct nfsd4_open *open) | ||
1456 | { | ||
1457 | int status; | ||
1458 | clientid_t *clientid = &open->op_clientid; | ||
1459 | struct nfs4_client *clp = NULL; | ||
1460 | unsigned int strhashval; | ||
1461 | struct nfs4_stateowner *sop = NULL; | ||
1462 | |||
1463 | status = nfserr_inval; | ||
1464 | if (!check_name(open->op_owner)) | ||
1465 | goto out; | ||
1466 | |||
1467 | if (STALE_CLIENTID(&open->op_clientid)) | ||
1468 | return nfserr_stale_clientid; | ||
1469 | |||
1470 | strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); | ||
1471 | sop = find_openstateowner_str(strhashval, open); | ||
1472 | if (sop) { | ||
1473 | open->op_stateowner = sop; | ||
1474 | /* check for replay */ | ||
1475 | if (open->op_seqid == sop->so_seqid){ | ||
1476 | if (sop->so_replay.rp_buflen) | ||
1477 | return NFSERR_REPLAY_ME; | ||
1478 | else { | ||
1479 | /* The original OPEN failed so spectacularly | ||
1480 | * that we don't even have replay data saved! | ||
1481 | * Therefore, we have no choice but to continue | ||
1482 | * processing this OPEN; presumably, we'll | ||
1483 | * fail again for the same reason. | ||
1484 | */ | ||
1485 | dprintk("nfsd4_process_open1:" | ||
1486 | " replay with no replay cache\n"); | ||
1487 | goto renew; | ||
1488 | } | ||
1489 | } else if (sop->so_confirmed) { | ||
1490 | if (open->op_seqid == sop->so_seqid + 1) | ||
1491 | goto renew; | ||
1492 | status = nfserr_bad_seqid; | ||
1493 | goto out; | ||
1494 | } else { | ||
1495 | /* If we get here, we received an OPEN for an | ||
1496 | * unconfirmed nfs4_stateowner. Since the seqid's are | ||
1497 | * different, purge the existing nfs4_stateowner, and | ||
1498 | * instantiate a new one. | ||
1499 | */ | ||
1500 | clp = sop->so_client; | ||
1501 | release_stateowner(sop); | ||
1502 | } | ||
1503 | } else { | ||
1504 | /* nfs4_stateowner not found. | ||
1505 | * Verify clientid and instantiate new nfs4_stateowner. | ||
1506 | * If verify fails this is presumably the result of the | ||
1507 | * client's lease expiring. | ||
1508 | */ | ||
1509 | status = nfserr_expired; | ||
1510 | clp = find_confirmed_client(clientid); | ||
1511 | if (clp == NULL) | ||
1512 | goto out; | ||
1513 | } | ||
1514 | status = nfserr_resource; | ||
1515 | sop = alloc_init_open_stateowner(strhashval, clp, open); | ||
1516 | if (sop == NULL) | ||
1517 | goto out; | ||
1518 | open->op_stateowner = sop; | ||
1519 | renew: | ||
1520 | status = nfs_ok; | ||
1521 | renew_client(sop->so_client); | ||
1522 | out: | ||
1523 | if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) | ||
1524 | status = nfserr_reclaim_bad; | ||
1525 | return status; | ||
1526 | } | ||
1527 | |||
1528 | static int | ||
1529 | nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) | ||
1530 | { | ||
1531 | struct nfs4_stateid *local; | ||
1532 | int status = nfserr_share_denied; | ||
1533 | struct nfs4_stateowner *sop = open->op_stateowner; | ||
1534 | |||
1535 | list_for_each_entry(local, &fp->fi_perfile, st_perfile) { | ||
1536 | /* ignore lock owners */ | ||
1537 | if (local->st_stateowner->so_is_open_owner == 0) | ||
1538 | continue; | ||
1539 | /* remember if we have seen this open owner */ | ||
1540 | if (local->st_stateowner == sop) | ||
1541 | *stpp = local; | ||
1542 | /* check for conflicting share reservations */ | ||
1543 | if (!test_share(local, open)) | ||
1544 | goto out; | ||
1545 | } | ||
1546 | status = 0; | ||
1547 | out: | ||
1548 | return status; | ||
1549 | } | ||
1550 | |||
1551 | static int | ||
1552 | nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, | ||
1553 | struct svc_fh *cur_fh, int flags) | ||
1554 | { | ||
1555 | struct nfs4_stateid *stp; | ||
1556 | int status; | ||
1557 | |||
1558 | stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL); | ||
1559 | if (stp == NULL) | ||
1560 | return nfserr_resource; | ||
1561 | |||
1562 | status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, &stp->st_vfs_file); | ||
1563 | if (status) { | ||
1564 | if (status == nfserr_dropit) | ||
1565 | status = nfserr_jukebox; | ||
1566 | kfree(stp); | ||
1567 | return status; | ||
1568 | } | ||
1569 | vfsopen++; | ||
1570 | *stpp = stp; | ||
1571 | return 0; | ||
1572 | } | ||
1573 | |||
1574 | static inline int | ||
1575 | nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, | ||
1576 | struct nfsd4_open *open) | ||
1577 | { | ||
1578 | struct iattr iattr = { | ||
1579 | .ia_valid = ATTR_SIZE, | ||
1580 | .ia_size = 0, | ||
1581 | }; | ||
1582 | if (!open->op_truncate) | ||
1583 | return 0; | ||
1584 | if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) | ||
1585 | return -EINVAL; | ||
1586 | return nfsd_setattr(rqstp, fh, &iattr, 0, (time_t)0); | ||
1587 | } | ||
1588 | |||
1589 | static int | ||
1590 | nfs4_upgrade_open(struct svc_rqst *rqstp, struct svc_fh *cur_fh, struct nfs4_stateid *stp, struct nfsd4_open *open) | ||
1591 | { | ||
1592 | struct file *filp = stp->st_vfs_file; | ||
1593 | struct inode *inode = filp->f_dentry->d_inode; | ||
1594 | unsigned int share_access; | ||
1595 | int status; | ||
1596 | |||
1597 | set_access(&share_access, stp->st_access_bmap); | ||
1598 | share_access = ~share_access; | ||
1599 | share_access &= open->op_share_access; | ||
1600 | |||
1601 | if (!(share_access & NFS4_SHARE_ACCESS_WRITE)) | ||
1602 | return nfsd4_truncate(rqstp, cur_fh, open); | ||
1603 | |||
1604 | status = get_write_access(inode); | ||
1605 | if (status) | ||
1606 | return nfserrno(status); | ||
1607 | status = nfsd4_truncate(rqstp, cur_fh, open); | ||
1608 | if (status) { | ||
1609 | put_write_access(inode); | ||
1610 | return status; | ||
1611 | } | ||
1612 | /* remember the open */ | ||
1613 | filp->f_mode = (filp->f_mode | FMODE_WRITE) & ~FMODE_READ; | ||
1614 | set_bit(open->op_share_access, &stp->st_access_bmap); | ||
1615 | set_bit(open->op_share_deny, &stp->st_deny_bmap); | ||
1616 | |||
1617 | return nfs_ok; | ||
1618 | } | ||
1619 | |||
1620 | |||
1621 | /* decrement seqid on successful reclaim, it will be bumped in encode_open */ | ||
1622 | static void | ||
1623 | nfs4_set_claim_prev(struct nfsd4_open *open, int *status) | ||
1624 | { | ||
1625 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) { | ||
1626 | if (*status) | ||
1627 | *status = nfserr_reclaim_bad; | ||
1628 | else { | ||
1629 | open->op_stateowner->so_confirmed = 1; | ||
1630 | open->op_stateowner->so_seqid--; | ||
1631 | } | ||
1632 | } | ||
1633 | } | ||
1634 | |||
1635 | /* | ||
1636 | * Attempt to hand out a delegation. | ||
1637 | */ | ||
1638 | static void | ||
1639 | nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_stateid *stp) | ||
1640 | { | ||
1641 | struct nfs4_delegation *dp; | ||
1642 | struct nfs4_stateowner *sop = stp->st_stateowner; | ||
1643 | struct nfs4_callback *cb = &sop->so_client->cl_callback; | ||
1644 | struct file_lock fl, *flp = &fl; | ||
1645 | int status, flag = 0; | ||
1646 | |||
1647 | flag = NFS4_OPEN_DELEGATE_NONE; | ||
1648 | if (open->op_claim_type != NFS4_OPEN_CLAIM_NULL | ||
1649 | || !atomic_read(&cb->cb_set) || !sop->so_confirmed) | ||
1650 | goto out; | ||
1651 | |||
1652 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | ||
1653 | flag = NFS4_OPEN_DELEGATE_WRITE; | ||
1654 | else | ||
1655 | flag = NFS4_OPEN_DELEGATE_READ; | ||
1656 | |||
1657 | dp = alloc_init_deleg(sop->so_client, stp, fh, flag); | ||
1658 | if (dp == NULL) { | ||
1659 | flag = NFS4_OPEN_DELEGATE_NONE; | ||
1660 | goto out; | ||
1661 | } | ||
1662 | locks_init_lock(&fl); | ||
1663 | fl.fl_lmops = &nfsd_lease_mng_ops; | ||
1664 | fl.fl_flags = FL_LEASE; | ||
1665 | fl.fl_end = OFFSET_MAX; | ||
1666 | fl.fl_owner = (fl_owner_t)dp; | ||
1667 | fl.fl_file = stp->st_vfs_file; | ||
1668 | fl.fl_pid = current->tgid; | ||
1669 | |||
1670 | /* setlease checks to see if delegation should be handed out. | ||
1671 | * the lock_manager callbacks fl_mylease and fl_change are used | ||
1672 | */ | ||
1673 | if ((status = setlease(stp->st_vfs_file, | ||
1674 | flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK, &flp))) { | ||
1675 | dprintk("NFSD: setlease failed [%d], no delegation\n", status); | ||
1676 | list_del(&dp->dl_del_perfile); | ||
1677 | list_del(&dp->dl_del_perclnt); | ||
1678 | nfs4_put_delegation(dp); | ||
1679 | free_delegation++; | ||
1680 | flag = NFS4_OPEN_DELEGATE_NONE; | ||
1681 | goto out; | ||
1682 | } | ||
1683 | |||
1684 | memcpy(&open->op_delegate_stateid, &dp->dl_stateid, sizeof(dp->dl_stateid)); | ||
1685 | |||
1686 | dprintk("NFSD: delegation stateid=(%08x/%08x/%08x/%08x)\n\n", | ||
1687 | dp->dl_stateid.si_boot, | ||
1688 | dp->dl_stateid.si_stateownerid, | ||
1689 | dp->dl_stateid.si_fileid, | ||
1690 | dp->dl_stateid.si_generation); | ||
1691 | out: | ||
1692 | open->op_delegate_type = flag; | ||
1693 | } | ||
1694 | |||
1695 | /* | ||
1696 | * called with nfs4_lock_state() held. | ||
1697 | */ | ||
1698 | int | ||
1699 | nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | ||
1700 | { | ||
1701 | struct nfs4_file *fp = NULL; | ||
1702 | struct inode *ino = current_fh->fh_dentry->d_inode; | ||
1703 | struct nfs4_stateid *stp = NULL; | ||
1704 | int status; | ||
1705 | |||
1706 | status = nfserr_inval; | ||
1707 | if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny)) | ||
1708 | goto out; | ||
1709 | /* | ||
1710 | * Lookup file; if found, lookup stateid and check open request, | ||
1711 | * and check for delegations in the process of being recalled. | ||
1712 | * If not found, create the nfs4_file struct | ||
1713 | */ | ||
1714 | fp = find_file(ino); | ||
1715 | if (fp) { | ||
1716 | if ((status = nfs4_check_open(fp, open, &stp))) | ||
1717 | goto out; | ||
1718 | } else { | ||
1719 | status = nfserr_resource; | ||
1720 | fp = alloc_init_file(ino); | ||
1721 | if (fp == NULL) | ||
1722 | goto out; | ||
1723 | } | ||
1724 | |||
1725 | /* | ||
1726 | * OPEN the file, or upgrade an existing OPEN. | ||
1727 | * If truncate fails, the OPEN fails. | ||
1728 | */ | ||
1729 | if (stp) { | ||
1730 | /* Stateid was found, this is an OPEN upgrade */ | ||
1731 | status = nfs4_upgrade_open(rqstp, current_fh, stp, open); | ||
1732 | if (status) | ||
1733 | goto out; | ||
1734 | } else { | ||
1735 | /* Stateid was not found, this is a new OPEN */ | ||
1736 | int flags = 0; | ||
1737 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | ||
1738 | flags = MAY_WRITE; | ||
1739 | else | ||
1740 | flags = MAY_READ; | ||
1741 | if ((status = nfs4_new_open(rqstp, &stp, current_fh, flags))) | ||
1742 | goto out; | ||
1743 | init_stateid(stp, fp, open); | ||
1744 | status = nfsd4_truncate(rqstp, current_fh, open); | ||
1745 | if (status) { | ||
1746 | release_stateid(stp, OPEN_STATE); | ||
1747 | goto out; | ||
1748 | } | ||
1749 | } | ||
1750 | memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); | ||
1751 | |||
1752 | /* | ||
1753 | * Attempt to hand out a delegation. No error return, because the | ||
1754 | * OPEN succeeds even if we fail. | ||
1755 | */ | ||
1756 | nfs4_open_delegation(current_fh, open, stp); | ||
1757 | |||
1758 | status = nfs_ok; | ||
1759 | |||
1760 | dprintk("nfs4_process_open2: stateid=(%08x/%08x/%08x/%08x)\n", | ||
1761 | stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, | ||
1762 | stp->st_stateid.si_fileid, stp->st_stateid.si_generation); | ||
1763 | out: | ||
1764 | /* take the opportunity to clean up unused state */ | ||
1765 | if (fp && list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) | ||
1766 | release_file(fp); | ||
1767 | |||
1768 | /* CLAIM_PREVIOUS has different error returns */ | ||
1769 | nfs4_set_claim_prev(open, &status); | ||
1770 | /* | ||
1771 | * To finish the open response, we just need to set the rflags. | ||
1772 | */ | ||
1773 | open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX; | ||
1774 | if (!open->op_stateowner->so_confirmed) | ||
1775 | open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; | ||
1776 | |||
1777 | return status; | ||
1778 | } | ||
1779 | |||
1780 | static struct work_struct laundromat_work; | ||
1781 | static void laundromat_main(void *); | ||
1782 | static DECLARE_WORK(laundromat_work, laundromat_main, NULL); | ||
1783 | |||
1784 | int | ||
1785 | nfsd4_renew(clientid_t *clid) | ||
1786 | { | ||
1787 | struct nfs4_client *clp; | ||
1788 | int status; | ||
1789 | |||
1790 | nfs4_lock_state(); | ||
1791 | dprintk("process_renew(%08x/%08x): starting\n", | ||
1792 | clid->cl_boot, clid->cl_id); | ||
1793 | status = nfserr_stale_clientid; | ||
1794 | if (STALE_CLIENTID(clid)) | ||
1795 | goto out; | ||
1796 | clp = find_confirmed_client(clid); | ||
1797 | status = nfserr_expired; | ||
1798 | if (clp == NULL) { | ||
1799 | /* We assume the client took too long to RENEW. */ | ||
1800 | dprintk("nfsd4_renew: clientid not found!\n"); | ||
1801 | goto out; | ||
1802 | } | ||
1803 | renew_client(clp); | ||
1804 | status = nfserr_cb_path_down; | ||
1805 | if (!list_empty(&clp->cl_del_perclnt) | ||
1806 | && !atomic_read(&clp->cl_callback.cb_set)) | ||
1807 | goto out; | ||
1808 | status = nfs_ok; | ||
1809 | out: | ||
1810 | nfs4_unlock_state(); | ||
1811 | return status; | ||
1812 | } | ||
1813 | |||
1814 | time_t | ||
1815 | nfs4_laundromat(void) | ||
1816 | { | ||
1817 | struct nfs4_client *clp; | ||
1818 | struct nfs4_stateowner *sop; | ||
1819 | struct nfs4_delegation *dp; | ||
1820 | struct list_head *pos, *next, reaplist; | ||
1821 | time_t cutoff = get_seconds() - NFSD_LEASE_TIME; | ||
1822 | time_t t, clientid_val = NFSD_LEASE_TIME; | ||
1823 | time_t u, test_val = NFSD_LEASE_TIME; | ||
1824 | |||
1825 | nfs4_lock_state(); | ||
1826 | |||
1827 | dprintk("NFSD: laundromat service - starting\n"); | ||
1828 | list_for_each_safe(pos, next, &client_lru) { | ||
1829 | clp = list_entry(pos, struct nfs4_client, cl_lru); | ||
1830 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { | ||
1831 | t = clp->cl_time - cutoff; | ||
1832 | if (clientid_val > t) | ||
1833 | clientid_val = t; | ||
1834 | break; | ||
1835 | } | ||
1836 | dprintk("NFSD: purging unused client (clientid %08x)\n", | ||
1837 | clp->cl_clientid.cl_id); | ||
1838 | expire_client(clp); | ||
1839 | } | ||
1840 | INIT_LIST_HEAD(&reaplist); | ||
1841 | spin_lock(&recall_lock); | ||
1842 | list_for_each_safe(pos, next, &del_recall_lru) { | ||
1843 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | ||
1844 | if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { | ||
1845 | u = dp->dl_time - cutoff; | ||
1846 | if (test_val > u) | ||
1847 | test_val = u; | ||
1848 | break; | ||
1849 | } | ||
1850 | dprintk("NFSD: purging unused delegation dp %p, fp %p\n", | ||
1851 | dp, dp->dl_flock); | ||
1852 | list_move(&dp->dl_recall_lru, &reaplist); | ||
1853 | } | ||
1854 | spin_unlock(&recall_lock); | ||
1855 | list_for_each_safe(pos, next, &reaplist) { | ||
1856 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | ||
1857 | list_del_init(&dp->dl_recall_lru); | ||
1858 | unhash_delegation(dp); | ||
1859 | } | ||
1860 | test_val = NFSD_LEASE_TIME; | ||
1861 | list_for_each_safe(pos, next, &close_lru) { | ||
1862 | sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); | ||
1863 | if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { | ||
1864 | u = sop->so_time - cutoff; | ||
1865 | if (test_val > u) | ||
1866 | test_val = u; | ||
1867 | break; | ||
1868 | } | ||
1869 | dprintk("NFSD: purging unused open stateowner (so_id %d)\n", | ||
1870 | sop->so_id); | ||
1871 | list_del(&sop->so_close_lru); | ||
1872 | nfs4_put_stateowner(sop); | ||
1873 | } | ||
1874 | if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) | ||
1875 | clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT; | ||
1876 | nfs4_unlock_state(); | ||
1877 | return clientid_val; | ||
1878 | } | ||
1879 | |||
1880 | void | ||
1881 | laundromat_main(void *not_used) | ||
1882 | { | ||
1883 | time_t t; | ||
1884 | |||
1885 | t = nfs4_laundromat(); | ||
1886 | dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); | ||
1887 | schedule_delayed_work(&laundromat_work, t*HZ); | ||
1888 | } | ||
1889 | |||
1890 | /* search ownerid_hashtbl[] and close_lru for stateid owner | ||
1891 | * (stateid->si_stateownerid) | ||
1892 | */ | ||
1893 | struct nfs4_stateowner * | ||
1894 | find_openstateowner_id(u32 st_id, int flags) { | ||
1895 | struct nfs4_stateowner *local = NULL; | ||
1896 | |||
1897 | dprintk("NFSD: find_openstateowner_id %d\n", st_id); | ||
1898 | if (flags & CLOSE_STATE) { | ||
1899 | list_for_each_entry(local, &close_lru, so_close_lru) { | ||
1900 | if (local->so_id == st_id) | ||
1901 | return local; | ||
1902 | } | ||
1903 | } | ||
1904 | return NULL; | ||
1905 | } | ||
1906 | |||
1907 | static inline int | ||
1908 | nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) | ||
1909 | { | ||
1910 | return fhp->fh_dentry->d_inode != stp->st_vfs_file->f_dentry->d_inode; | ||
1911 | } | ||
1912 | |||
1913 | static int | ||
1914 | STALE_STATEID(stateid_t *stateid) | ||
1915 | { | ||
1916 | if (stateid->si_boot == boot_time) | ||
1917 | return 0; | ||
1918 | printk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n", | ||
1919 | stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid, | ||
1920 | stateid->si_generation); | ||
1921 | return 1; | ||
1922 | } | ||
1923 | |||
1924 | static inline int | ||
1925 | access_permit_read(unsigned long access_bmap) | ||
1926 | { | ||
1927 | return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) || | ||
1928 | test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap) || | ||
1929 | test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap); | ||
1930 | } | ||
1931 | |||
1932 | static inline int | ||
1933 | access_permit_write(unsigned long access_bmap) | ||
1934 | { | ||
1935 | return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) || | ||
1936 | test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap); | ||
1937 | } | ||
1938 | |||
1939 | static | ||
1940 | int nfs4_check_openmode(struct nfs4_stateid *stp, int flags) | ||
1941 | { | ||
1942 | int status = nfserr_openmode; | ||
1943 | |||
1944 | if ((flags & WR_STATE) && (!access_permit_write(stp->st_access_bmap))) | ||
1945 | goto out; | ||
1946 | if ((flags & RD_STATE) && (!access_permit_read(stp->st_access_bmap))) | ||
1947 | goto out; | ||
1948 | status = nfs_ok; | ||
1949 | out: | ||
1950 | return status; | ||
1951 | } | ||
1952 | |||
1953 | static inline int | ||
1954 | nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) | ||
1955 | { | ||
1956 | if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) | ||
1957 | return nfserr_openmode; | ||
1958 | else | ||
1959 | return nfs_ok; | ||
1960 | } | ||
1961 | |||
1962 | static inline int | ||
1963 | check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) | ||
1964 | { | ||
1965 | /* Trying to call delegreturn with a special stateid? Yuch: */ | ||
1966 | if (!(flags & (RD_STATE | WR_STATE))) | ||
1967 | return nfserr_bad_stateid; | ||
1968 | else if (ONE_STATEID(stateid) && (flags & RD_STATE)) | ||
1969 | return nfs_ok; | ||
1970 | else if (nfs4_in_grace()) { | ||
1971 | /* Answer in remaining cases depends on existance of | ||
1972 | * conflicting state; so we must wait out the grace period. */ | ||
1973 | return nfserr_grace; | ||
1974 | } else if (flags & WR_STATE) | ||
1975 | return nfs4_share_conflict(current_fh, | ||
1976 | NFS4_SHARE_DENY_WRITE); | ||
1977 | else /* (flags & RD_STATE) && ZERO_STATEID(stateid) */ | ||
1978 | return nfs4_share_conflict(current_fh, | ||
1979 | NFS4_SHARE_DENY_READ); | ||
1980 | } | ||
1981 | |||
1982 | /* | ||
1983 | * Allow READ/WRITE during grace period on recovered state only for files | ||
1984 | * that are not able to provide mandatory locking. | ||
1985 | */ | ||
1986 | static inline int | ||
1987 | io_during_grace_disallowed(struct inode *inode, int flags) | ||
1988 | { | ||
1989 | return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE)) | ||
1990 | && MANDATORY_LOCK(inode); | ||
1991 | } | ||
1992 | |||
1993 | /* | ||
1994 | * Checks for stateid operations | ||
1995 | */ | ||
1996 | int | ||
1997 | nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int flags, struct file **filpp) | ||
1998 | { | ||
1999 | struct nfs4_stateid *stp = NULL; | ||
2000 | struct nfs4_delegation *dp = NULL; | ||
2001 | stateid_t *stidp; | ||
2002 | struct inode *ino = current_fh->fh_dentry->d_inode; | ||
2003 | int status; | ||
2004 | |||
2005 | dprintk("NFSD: preprocess_stateid_op: stateid = (%08x/%08x/%08x/%08x)\n", | ||
2006 | stateid->si_boot, stateid->si_stateownerid, | ||
2007 | stateid->si_fileid, stateid->si_generation); | ||
2008 | if (filpp) | ||
2009 | *filpp = NULL; | ||
2010 | |||
2011 | if (io_during_grace_disallowed(ino, flags)) | ||
2012 | return nfserr_grace; | ||
2013 | |||
2014 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) | ||
2015 | return check_special_stateids(current_fh, stateid, flags); | ||
2016 | |||
2017 | /* STALE STATEID */ | ||
2018 | status = nfserr_stale_stateid; | ||
2019 | if (STALE_STATEID(stateid)) | ||
2020 | goto out; | ||
2021 | |||
2022 | /* BAD STATEID */ | ||
2023 | status = nfserr_bad_stateid; | ||
2024 | if (!stateid->si_fileid) { /* delegation stateid */ | ||
2025 | if(!(dp = find_delegation_stateid(ino, stateid))) { | ||
2026 | dprintk("NFSD: delegation stateid not found\n"); | ||
2027 | if (nfs4_in_grace()) | ||
2028 | status = nfserr_grace; | ||
2029 | goto out; | ||
2030 | } | ||
2031 | stidp = &dp->dl_stateid; | ||
2032 | } else { /* open or lock stateid */ | ||
2033 | if (!(stp = find_stateid(stateid, flags))) { | ||
2034 | dprintk("NFSD: open or lock stateid not found\n"); | ||
2035 | if (nfs4_in_grace()) | ||
2036 | status = nfserr_grace; | ||
2037 | goto out; | ||
2038 | } | ||
2039 | if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) | ||
2040 | goto out; | ||
2041 | if (!stp->st_stateowner->so_confirmed) | ||
2042 | goto out; | ||
2043 | stidp = &stp->st_stateid; | ||
2044 | } | ||
2045 | if (stateid->si_generation > stidp->si_generation) | ||
2046 | goto out; | ||
2047 | |||
2048 | /* OLD STATEID */ | ||
2049 | status = nfserr_old_stateid; | ||
2050 | if (stateid->si_generation < stidp->si_generation) | ||
2051 | goto out; | ||
2052 | if (stp) { | ||
2053 | if ((status = nfs4_check_openmode(stp,flags))) | ||
2054 | goto out; | ||
2055 | renew_client(stp->st_stateowner->so_client); | ||
2056 | if (filpp) | ||
2057 | *filpp = stp->st_vfs_file; | ||
2058 | } else if (dp) { | ||
2059 | if ((status = nfs4_check_delegmode(dp, flags))) | ||
2060 | goto out; | ||
2061 | renew_client(dp->dl_client); | ||
2062 | if (flags & DELEG_RET) | ||
2063 | unhash_delegation(dp); | ||
2064 | if (filpp) | ||
2065 | *filpp = dp->dl_vfs_file; | ||
2066 | } | ||
2067 | status = nfs_ok; | ||
2068 | out: | ||
2069 | return status; | ||
2070 | } | ||
2071 | |||
2072 | |||
2073 | /* | ||
2074 | * Checks for sequence id mutating operations. | ||
2075 | */ | ||
2076 | int | ||
2077 | nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid) | ||
2078 | { | ||
2079 | int status; | ||
2080 | struct nfs4_stateid *stp; | ||
2081 | struct nfs4_stateowner *sop; | ||
2082 | |||
2083 | dprintk("NFSD: preprocess_seqid_op: seqid=%d " | ||
2084 | "stateid = (%08x/%08x/%08x/%08x)\n", seqid, | ||
2085 | stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid, | ||
2086 | stateid->si_generation); | ||
2087 | |||
2088 | *stpp = NULL; | ||
2089 | *sopp = NULL; | ||
2090 | |||
2091 | status = nfserr_bad_stateid; | ||
2092 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) { | ||
2093 | printk("NFSD: preprocess_seqid_op: magic stateid!\n"); | ||
2094 | goto out; | ||
2095 | } | ||
2096 | |||
2097 | status = nfserr_stale_stateid; | ||
2098 | if (STALE_STATEID(stateid)) | ||
2099 | goto out; | ||
2100 | /* | ||
2101 | * We return BAD_STATEID if filehandle doesn't match stateid, | ||
2102 | * the confirmed flag is incorrecly set, or the generation | ||
2103 | * number is incorrect. | ||
2104 | * If there is no entry in the openfile table for this id, | ||
2105 | * we can't always return BAD_STATEID; | ||
2106 | * this might be a retransmitted CLOSE which has arrived after | ||
2107 | * the openfile has been released. | ||
2108 | */ | ||
2109 | if (!(stp = find_stateid(stateid, flags))) | ||
2110 | goto no_nfs4_stateid; | ||
2111 | |||
2112 | status = nfserr_bad_stateid; | ||
2113 | |||
2114 | /* for new lock stateowners: | ||
2115 | * check that the lock->v.new.open_stateid | ||
2116 | * refers to an open stateowner | ||
2117 | * | ||
2118 | * check that the lockclid (nfs4_lock->v.new.clientid) is the same | ||
2119 | * as the open_stateid->st_stateowner->so_client->clientid | ||
2120 | */ | ||
2121 | if (lockclid) { | ||
2122 | struct nfs4_stateowner *sop = stp->st_stateowner; | ||
2123 | struct nfs4_client *clp = sop->so_client; | ||
2124 | |||
2125 | if (!sop->so_is_open_owner) | ||
2126 | goto out; | ||
2127 | if (!cmp_clid(&clp->cl_clientid, lockclid)) | ||
2128 | goto out; | ||
2129 | } | ||
2130 | |||
2131 | if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) { | ||
2132 | printk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); | ||
2133 | goto out; | ||
2134 | } | ||
2135 | |||
2136 | *stpp = stp; | ||
2137 | *sopp = sop = stp->st_stateowner; | ||
2138 | |||
2139 | /* | ||
2140 | * We now validate the seqid and stateid generation numbers. | ||
2141 | * For the moment, we ignore the possibility of | ||
2142 | * generation number wraparound. | ||
2143 | */ | ||
2144 | if (seqid != sop->so_seqid + 1) | ||
2145 | goto check_replay; | ||
2146 | |||
2147 | if (sop->so_confirmed) { | ||
2148 | if (flags & CONFIRM) { | ||
2149 | printk("NFSD: preprocess_seqid_op: expected unconfirmed stateowner!\n"); | ||
2150 | goto out; | ||
2151 | } | ||
2152 | } | ||
2153 | else { | ||
2154 | if (!(flags & CONFIRM)) { | ||
2155 | printk("NFSD: preprocess_seqid_op: stateowner not confirmed yet!\n"); | ||
2156 | goto out; | ||
2157 | } | ||
2158 | } | ||
2159 | if (stateid->si_generation > stp->st_stateid.si_generation) { | ||
2160 | printk("NFSD: preprocess_seqid_op: future stateid?!\n"); | ||
2161 | goto out; | ||
2162 | } | ||
2163 | |||
2164 | status = nfserr_old_stateid; | ||
2165 | if (stateid->si_generation < stp->st_stateid.si_generation) { | ||
2166 | printk("NFSD: preprocess_seqid_op: old stateid!\n"); | ||
2167 | goto out; | ||
2168 | } | ||
2169 | /* XXX renew the client lease here */ | ||
2170 | status = nfs_ok; | ||
2171 | |||
2172 | out: | ||
2173 | return status; | ||
2174 | |||
2175 | no_nfs4_stateid: | ||
2176 | |||
2177 | /* | ||
2178 | * We determine whether this is a bad stateid or a replay, | ||
2179 | * starting by trying to look up the stateowner. | ||
2180 | * If stateowner is not found - stateid is bad. | ||
2181 | */ | ||
2182 | if (!(sop = find_openstateowner_id(stateid->si_stateownerid, flags))) { | ||
2183 | printk("NFSD: preprocess_seqid_op: no stateowner or nfs4_stateid!\n"); | ||
2184 | status = nfserr_bad_stateid; | ||
2185 | goto out; | ||
2186 | } | ||
2187 | *sopp = sop; | ||
2188 | |||
2189 | check_replay: | ||
2190 | if (seqid == sop->so_seqid) { | ||
2191 | printk("NFSD: preprocess_seqid_op: retransmission?\n"); | ||
2192 | /* indicate replay to calling function */ | ||
2193 | status = NFSERR_REPLAY_ME; | ||
2194 | } else { | ||
2195 | printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d\n", sop->so_seqid +1, seqid); | ||
2196 | |||
2197 | *sopp = NULL; | ||
2198 | status = nfserr_bad_seqid; | ||
2199 | } | ||
2200 | goto out; | ||
2201 | } | ||
2202 | |||
2203 | int | ||
2204 | nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_confirm *oc) | ||
2205 | { | ||
2206 | int status; | ||
2207 | struct nfs4_stateowner *sop; | ||
2208 | struct nfs4_stateid *stp; | ||
2209 | |||
2210 | dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", | ||
2211 | (int)current_fh->fh_dentry->d_name.len, | ||
2212 | current_fh->fh_dentry->d_name.name); | ||
2213 | |||
2214 | if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) | ||
2215 | goto out; | ||
2216 | |||
2217 | nfs4_lock_state(); | ||
2218 | |||
2219 | if ((status = nfs4_preprocess_seqid_op(current_fh, oc->oc_seqid, | ||
2220 | &oc->oc_req_stateid, | ||
2221 | CHECK_FH | CONFIRM | OPEN_STATE, | ||
2222 | &oc->oc_stateowner, &stp, NULL))) | ||
2223 | goto out; | ||
2224 | |||
2225 | sop = oc->oc_stateowner; | ||
2226 | sop->so_confirmed = 1; | ||
2227 | update_stateid(&stp->st_stateid); | ||
2228 | memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); | ||
2229 | dprintk("NFSD: nfsd4_open_confirm: success, seqid=%d " | ||
2230 | "stateid=(%08x/%08x/%08x/%08x)\n", oc->oc_seqid, | ||
2231 | stp->st_stateid.si_boot, | ||
2232 | stp->st_stateid.si_stateownerid, | ||
2233 | stp->st_stateid.si_fileid, | ||
2234 | stp->st_stateid.si_generation); | ||
2235 | out: | ||
2236 | if (oc->oc_stateowner) | ||
2237 | nfs4_get_stateowner(oc->oc_stateowner); | ||
2238 | nfs4_unlock_state(); | ||
2239 | return status; | ||
2240 | } | ||
2241 | |||
2242 | |||
2243 | /* | ||
2244 | * unset all bits in union bitmap (bmap) that | ||
2245 | * do not exist in share (from successful OPEN_DOWNGRADE) | ||
2246 | */ | ||
2247 | static void | ||
2248 | reset_union_bmap_access(unsigned long access, unsigned long *bmap) | ||
2249 | { | ||
2250 | int i; | ||
2251 | for (i = 1; i < 4; i++) { | ||
2252 | if ((i & access) != i) | ||
2253 | __clear_bit(i, bmap); | ||
2254 | } | ||
2255 | } | ||
2256 | |||
2257 | static void | ||
2258 | reset_union_bmap_deny(unsigned long deny, unsigned long *bmap) | ||
2259 | { | ||
2260 | int i; | ||
2261 | for (i = 0; i < 4; i++) { | ||
2262 | if ((i & deny) != i) | ||
2263 | __clear_bit(i, bmap); | ||
2264 | } | ||
2265 | } | ||
2266 | |||
2267 | int | ||
2268 | nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od) | ||
2269 | { | ||
2270 | int status; | ||
2271 | struct nfs4_stateid *stp; | ||
2272 | unsigned int share_access; | ||
2273 | |||
2274 | dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", | ||
2275 | (int)current_fh->fh_dentry->d_name.len, | ||
2276 | current_fh->fh_dentry->d_name.name); | ||
2277 | |||
2278 | if (!TEST_ACCESS(od->od_share_access) || !TEST_DENY(od->od_share_deny)) | ||
2279 | return nfserr_inval; | ||
2280 | |||
2281 | nfs4_lock_state(); | ||
2282 | if ((status = nfs4_preprocess_seqid_op(current_fh, od->od_seqid, | ||
2283 | &od->od_stateid, | ||
2284 | CHECK_FH | OPEN_STATE, | ||
2285 | &od->od_stateowner, &stp, NULL))) | ||
2286 | goto out; | ||
2287 | |||
2288 | status = nfserr_inval; | ||
2289 | if (!test_bit(od->od_share_access, &stp->st_access_bmap)) { | ||
2290 | dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n", | ||
2291 | stp->st_access_bmap, od->od_share_access); | ||
2292 | goto out; | ||
2293 | } | ||
2294 | if (!test_bit(od->od_share_deny, &stp->st_deny_bmap)) { | ||
2295 | dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n", | ||
2296 | stp->st_deny_bmap, od->od_share_deny); | ||
2297 | goto out; | ||
2298 | } | ||
2299 | set_access(&share_access, stp->st_access_bmap); | ||
2300 | nfs4_file_downgrade(stp->st_vfs_file, | ||
2301 | share_access & ~od->od_share_access); | ||
2302 | |||
2303 | reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap); | ||
2304 | reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap); | ||
2305 | |||
2306 | update_stateid(&stp->st_stateid); | ||
2307 | memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); | ||
2308 | status = nfs_ok; | ||
2309 | out: | ||
2310 | if (od->od_stateowner) | ||
2311 | nfs4_get_stateowner(od->od_stateowner); | ||
2312 | nfs4_unlock_state(); | ||
2313 | return status; | ||
2314 | } | ||
2315 | |||
2316 | /* | ||
2317 | * nfs4_unlock_state() called after encode | ||
2318 | */ | ||
2319 | int | ||
2320 | nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_close *close) | ||
2321 | { | ||
2322 | int status; | ||
2323 | struct nfs4_stateid *stp; | ||
2324 | |||
2325 | dprintk("NFSD: nfsd4_close on file %.*s\n", | ||
2326 | (int)current_fh->fh_dentry->d_name.len, | ||
2327 | current_fh->fh_dentry->d_name.name); | ||
2328 | |||
2329 | nfs4_lock_state(); | ||
2330 | /* check close_lru for replay */ | ||
2331 | if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid, | ||
2332 | &close->cl_stateid, | ||
2333 | CHECK_FH | OPEN_STATE | CLOSE_STATE, | ||
2334 | &close->cl_stateowner, &stp, NULL))) | ||
2335 | goto out; | ||
2336 | /* | ||
2337 | * Return success, but first update the stateid. | ||
2338 | */ | ||
2339 | status = nfs_ok; | ||
2340 | update_stateid(&stp->st_stateid); | ||
2341 | memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t)); | ||
2342 | |||
2343 | /* release_state_owner() calls nfsd_close() if needed */ | ||
2344 | release_state_owner(stp, OPEN_STATE); | ||
2345 | out: | ||
2346 | if (close->cl_stateowner) | ||
2347 | nfs4_get_stateowner(close->cl_stateowner); | ||
2348 | nfs4_unlock_state(); | ||
2349 | return status; | ||
2350 | } | ||
2351 | |||
2352 | int | ||
2353 | nfsd4_delegreturn(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_delegreturn *dr) | ||
2354 | { | ||
2355 | int status; | ||
2356 | |||
2357 | if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) | ||
2358 | goto out; | ||
2359 | |||
2360 | nfs4_lock_state(); | ||
2361 | status = nfs4_preprocess_stateid_op(current_fh, &dr->dr_stateid, DELEG_RET, NULL); | ||
2362 | nfs4_unlock_state(); | ||
2363 | out: | ||
2364 | return status; | ||
2365 | } | ||
2366 | |||
2367 | |||
2368 | /* | ||
2369 | * Lock owner state (byte-range locks) | ||
2370 | */ | ||
2371 | #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) | ||
2372 | #define LOCK_HASH_BITS 8 | ||
2373 | #define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS) | ||
2374 | #define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1) | ||
2375 | |||
2376 | #define lockownerid_hashval(id) \ | ||
2377 | ((id) & LOCK_HASH_MASK) | ||
2378 | |||
2379 | static inline unsigned int | ||
2380 | lock_ownerstr_hashval(struct inode *inode, u32 cl_id, | ||
2381 | struct xdr_netobj *ownername) | ||
2382 | { | ||
2383 | return (file_hashval(inode) + cl_id | ||
2384 | + opaque_hashval(ownername->data, ownername->len)) | ||
2385 | & LOCK_HASH_MASK; | ||
2386 | } | ||
2387 | |||
2388 | static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; | ||
2389 | static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; | ||
2390 | static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; | ||
2391 | |||
2392 | struct nfs4_stateid * | ||
2393 | find_stateid(stateid_t *stid, int flags) | ||
2394 | { | ||
2395 | struct nfs4_stateid *local = NULL; | ||
2396 | u32 st_id = stid->si_stateownerid; | ||
2397 | u32 f_id = stid->si_fileid; | ||
2398 | unsigned int hashval; | ||
2399 | |||
2400 | dprintk("NFSD: find_stateid flags 0x%x\n",flags); | ||
2401 | if ((flags & LOCK_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) { | ||
2402 | hashval = stateid_hashval(st_id, f_id); | ||
2403 | list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { | ||
2404 | if ((local->st_stateid.si_stateownerid == st_id) && | ||
2405 | (local->st_stateid.si_fileid == f_id)) | ||
2406 | return local; | ||
2407 | } | ||
2408 | } | ||
2409 | if ((flags & OPEN_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) { | ||
2410 | hashval = stateid_hashval(st_id, f_id); | ||
2411 | list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { | ||
2412 | if ((local->st_stateid.si_stateownerid == st_id) && | ||
2413 | (local->st_stateid.si_fileid == f_id)) | ||
2414 | return local; | ||
2415 | } | ||
2416 | } else | ||
2417 | printk("NFSD: find_stateid: ERROR: no state flag\n"); | ||
2418 | return NULL; | ||
2419 | } | ||
2420 | |||
2421 | static struct nfs4_delegation * | ||
2422 | find_delegation_stateid(struct inode *ino, stateid_t *stid) | ||
2423 | { | ||
2424 | struct nfs4_delegation *dp = NULL; | ||
2425 | struct nfs4_file *fp = NULL; | ||
2426 | u32 st_id; | ||
2427 | |||
2428 | dprintk("NFSD:find_delegation_stateid stateid=(%08x/%08x/%08x/%08x)\n", | ||
2429 | stid->si_boot, stid->si_stateownerid, | ||
2430 | stid->si_fileid, stid->si_generation); | ||
2431 | |||
2432 | st_id = stid->si_stateownerid; | ||
2433 | fp = find_file(ino); | ||
2434 | if (fp) { | ||
2435 | list_for_each_entry(dp, &fp->fi_del_perfile, dl_del_perfile) { | ||
2436 | if(dp->dl_stateid.si_stateownerid == st_id) { | ||
2437 | dprintk("NFSD: find_delegation dp %p\n",dp); | ||
2438 | return dp; | ||
2439 | } | ||
2440 | } | ||
2441 | } | ||
2442 | return NULL; | ||
2443 | } | ||
2444 | |||
2445 | /* | ||
2446 | * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that | ||
2447 | * we can't properly handle lock requests that go beyond the (2^63 - 1)-th | ||
2448 | * byte, because of sign extension problems. Since NFSv4 calls for 64-bit | ||
2449 | * locking, this prevents us from being completely protocol-compliant. The | ||
2450 | * real solution to this problem is to start using unsigned file offsets in | ||
2451 | * the VFS, but this is a very deep change! | ||
2452 | */ | ||
2453 | static inline void | ||
2454 | nfs4_transform_lock_offset(struct file_lock *lock) | ||
2455 | { | ||
2456 | if (lock->fl_start < 0) | ||
2457 | lock->fl_start = OFFSET_MAX; | ||
2458 | if (lock->fl_end < 0) | ||
2459 | lock->fl_end = OFFSET_MAX; | ||
2460 | } | ||
2461 | |||
2462 | int | ||
2463 | nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval) | ||
2464 | { | ||
2465 | struct nfs4_stateowner *local = NULL; | ||
2466 | int status = 0; | ||
2467 | |||
2468 | if (hashval >= LOCK_HASH_SIZE) | ||
2469 | goto out; | ||
2470 | list_for_each_entry(local, &lock_ownerid_hashtbl[hashval], so_idhash) { | ||
2471 | if (local == sop) { | ||
2472 | status = 1; | ||
2473 | goto out; | ||
2474 | } | ||
2475 | } | ||
2476 | out: | ||
2477 | return status; | ||
2478 | } | ||
2479 | |||
2480 | |||
2481 | static inline void | ||
2482 | nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) | ||
2483 | { | ||
2484 | struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner; | ||
2485 | unsigned int hval = lockownerid_hashval(sop->so_id); | ||
2486 | |||
2487 | deny->ld_sop = NULL; | ||
2488 | if (nfs4_verify_lock_stateowner(sop, hval)) { | ||
2489 | kref_get(&sop->so_ref); | ||
2490 | deny->ld_sop = sop; | ||
2491 | deny->ld_clientid = sop->so_client->cl_clientid; | ||
2492 | } | ||
2493 | deny->ld_start = fl->fl_start; | ||
2494 | deny->ld_length = ~(u64)0; | ||
2495 | if (fl->fl_end != ~(u64)0) | ||
2496 | deny->ld_length = fl->fl_end - fl->fl_start + 1; | ||
2497 | deny->ld_type = NFS4_READ_LT; | ||
2498 | if (fl->fl_type != F_RDLCK) | ||
2499 | deny->ld_type = NFS4_WRITE_LT; | ||
2500 | } | ||
2501 | |||
2502 | static struct nfs4_stateowner * | ||
2503 | find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid) | ||
2504 | { | ||
2505 | struct nfs4_stateowner *local = NULL; | ||
2506 | int i; | ||
2507 | |||
2508 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | ||
2509 | list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { | ||
2510 | if (!cmp_owner_str(local, owner, clid)) | ||
2511 | continue; | ||
2512 | return local; | ||
2513 | } | ||
2514 | } | ||
2515 | return NULL; | ||
2516 | } | ||
2517 | |||
2518 | static struct nfs4_stateowner * | ||
2519 | find_lockstateowner_str(struct inode *inode, clientid_t *clid, | ||
2520 | struct xdr_netobj *owner) | ||
2521 | { | ||
2522 | unsigned int hashval = lock_ownerstr_hashval(inode, clid->cl_id, owner); | ||
2523 | struct nfs4_stateowner *op; | ||
2524 | |||
2525 | list_for_each_entry(op, &lock_ownerstr_hashtbl[hashval], so_strhash) { | ||
2526 | if (cmp_owner_str(op, owner, clid)) | ||
2527 | return op; | ||
2528 | } | ||
2529 | return NULL; | ||
2530 | } | ||
2531 | |||
2532 | /* | ||
2533 | * Alloc a lock owner structure. | ||
2534 | * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has | ||
2535 | * occured. | ||
2536 | * | ||
2537 | * strhashval = lock_ownerstr_hashval | ||
2538 | * so_seqid = lock->lk_new_lock_seqid - 1: it gets bumped in encode | ||
2539 | */ | ||
2540 | |||
2541 | static struct nfs4_stateowner * | ||
2542 | alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { | ||
2543 | struct nfs4_stateowner *sop; | ||
2544 | struct nfs4_replay *rp; | ||
2545 | unsigned int idhashval; | ||
2546 | |||
2547 | if (!(sop = alloc_stateowner(&lock->lk_new_owner))) | ||
2548 | return NULL; | ||
2549 | idhashval = lockownerid_hashval(current_ownerid); | ||
2550 | INIT_LIST_HEAD(&sop->so_idhash); | ||
2551 | INIT_LIST_HEAD(&sop->so_strhash); | ||
2552 | INIT_LIST_HEAD(&sop->so_perclient); | ||
2553 | INIT_LIST_HEAD(&sop->so_perfilestate); | ||
2554 | INIT_LIST_HEAD(&sop->so_perlockowner); | ||
2555 | INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ | ||
2556 | sop->so_time = 0; | ||
2557 | list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); | ||
2558 | list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); | ||
2559 | list_add(&sop->so_perlockowner, &open_stp->st_perlockowner); | ||
2560 | sop->so_is_open_owner = 0; | ||
2561 | sop->so_id = current_ownerid++; | ||
2562 | sop->so_client = clp; | ||
2563 | sop->so_seqid = lock->lk_new_lock_seqid - 1; | ||
2564 | sop->so_confirmed = 1; | ||
2565 | rp = &sop->so_replay; | ||
2566 | rp->rp_status = NFSERR_SERVERFAULT; | ||
2567 | rp->rp_buflen = 0; | ||
2568 | rp->rp_buf = rp->rp_ibuf; | ||
2569 | return sop; | ||
2570 | } | ||
2571 | |||
2572 | struct nfs4_stateid * | ||
2573 | alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) | ||
2574 | { | ||
2575 | struct nfs4_stateid *stp; | ||
2576 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); | ||
2577 | |||
2578 | if ((stp = kmalloc(sizeof(struct nfs4_stateid), | ||
2579 | GFP_KERNEL)) == NULL) | ||
2580 | goto out; | ||
2581 | INIT_LIST_HEAD(&stp->st_hash); | ||
2582 | INIT_LIST_HEAD(&stp->st_perfile); | ||
2583 | INIT_LIST_HEAD(&stp->st_perfilestate); | ||
2584 | INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */ | ||
2585 | list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); | ||
2586 | list_add(&stp->st_perfile, &fp->fi_perfile); | ||
2587 | list_add_perfile++; | ||
2588 | list_add(&stp->st_perfilestate, &sop->so_perfilestate); | ||
2589 | stp->st_stateowner = sop; | ||
2590 | stp->st_file = fp; | ||
2591 | stp->st_stateid.si_boot = boot_time; | ||
2592 | stp->st_stateid.si_stateownerid = sop->so_id; | ||
2593 | stp->st_stateid.si_fileid = fp->fi_id; | ||
2594 | stp->st_stateid.si_generation = 0; | ||
2595 | stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */ | ||
2596 | stp->st_access_bmap = open_stp->st_access_bmap; | ||
2597 | stp->st_deny_bmap = open_stp->st_deny_bmap; | ||
2598 | |||
2599 | out: | ||
2600 | return stp; | ||
2601 | } | ||
2602 | |||
2603 | int | ||
2604 | check_lock_length(u64 offset, u64 length) | ||
2605 | { | ||
2606 | return ((length == 0) || ((length != ~(u64)0) && | ||
2607 | LOFF_OVERFLOW(offset, length))); | ||
2608 | } | ||
2609 | |||
2610 | /* | ||
2611 | * LOCK operation | ||
2612 | */ | ||
2613 | int | ||
2614 | nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock) | ||
2615 | { | ||
2616 | struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL; | ||
2617 | struct nfs4_stateid *lock_stp; | ||
2618 | struct file *filp; | ||
2619 | struct file_lock file_lock; | ||
2620 | struct file_lock *conflock; | ||
2621 | int status = 0; | ||
2622 | unsigned int strhashval; | ||
2623 | |||
2624 | dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", | ||
2625 | (long long) lock->lk_offset, | ||
2626 | (long long) lock->lk_length); | ||
2627 | |||
2628 | if (nfs4_in_grace() && !lock->lk_reclaim) | ||
2629 | return nfserr_grace; | ||
2630 | if (!nfs4_in_grace() && lock->lk_reclaim) | ||
2631 | return nfserr_no_grace; | ||
2632 | |||
2633 | if (check_lock_length(lock->lk_offset, lock->lk_length)) | ||
2634 | return nfserr_inval; | ||
2635 | |||
2636 | nfs4_lock_state(); | ||
2637 | |||
2638 | if (lock->lk_is_new) { | ||
2639 | /* | ||
2640 | * Client indicates that this is a new lockowner. | ||
2641 | * Use open owner and open stateid to create lock owner and lock | ||
2642 | * stateid. | ||
2643 | */ | ||
2644 | struct nfs4_stateid *open_stp = NULL; | ||
2645 | struct nfs4_file *fp; | ||
2646 | |||
2647 | status = nfserr_stale_clientid; | ||
2648 | if (STALE_CLIENTID(&lock->lk_new_clientid)) { | ||
2649 | printk("NFSD: nfsd4_lock: clientid is stale!\n"); | ||
2650 | goto out; | ||
2651 | } | ||
2652 | |||
2653 | /* is the new lock seqid presented by the client zero? */ | ||
2654 | status = nfserr_bad_seqid; | ||
2655 | if (lock->v.new.lock_seqid != 0) | ||
2656 | goto out; | ||
2657 | |||
2658 | /* validate and update open stateid and open seqid */ | ||
2659 | status = nfs4_preprocess_seqid_op(current_fh, | ||
2660 | lock->lk_new_open_seqid, | ||
2661 | &lock->lk_new_open_stateid, | ||
2662 | CHECK_FH | OPEN_STATE, | ||
2663 | &open_sop, &open_stp, | ||
2664 | &lock->v.new.clientid); | ||
2665 | if (status) { | ||
2666 | if (lock->lk_reclaim) | ||
2667 | status = nfserr_reclaim_bad; | ||
2668 | goto out; | ||
2669 | } | ||
2670 | /* create lockowner and lock stateid */ | ||
2671 | fp = open_stp->st_file; | ||
2672 | strhashval = lock_ownerstr_hashval(fp->fi_inode, | ||
2673 | open_sop->so_client->cl_clientid.cl_id, | ||
2674 | &lock->v.new.owner); | ||
2675 | /* | ||
2676 | * If we already have this lock owner, the client is in | ||
2677 | * error (or our bookeeping is wrong!) | ||
2678 | * for asking for a 'new lock'. | ||
2679 | */ | ||
2680 | status = nfserr_bad_stateid; | ||
2681 | lock_sop = find_lockstateowner(&lock->v.new.owner, | ||
2682 | &lock->v.new.clientid); | ||
2683 | if (lock_sop) | ||
2684 | goto out; | ||
2685 | status = nfserr_resource; | ||
2686 | if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) | ||
2687 | goto out; | ||
2688 | if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner, | ||
2689 | fp, open_stp)) == NULL) { | ||
2690 | release_stateowner(lock->lk_stateowner); | ||
2691 | lock->lk_stateowner = NULL; | ||
2692 | goto out; | ||
2693 | } | ||
2694 | /* bump the open seqid used to create the lock */ | ||
2695 | open_sop->so_seqid++; | ||
2696 | } else { | ||
2697 | /* lock (lock owner + lock stateid) already exists */ | ||
2698 | status = nfs4_preprocess_seqid_op(current_fh, | ||
2699 | lock->lk_old_lock_seqid, | ||
2700 | &lock->lk_old_lock_stateid, | ||
2701 | CHECK_FH | LOCK_STATE, | ||
2702 | &lock->lk_stateowner, &lock_stp, NULL); | ||
2703 | if (status) | ||
2704 | goto out; | ||
2705 | } | ||
2706 | /* lock->lk_stateowner and lock_stp have been created or found */ | ||
2707 | filp = lock_stp->st_vfs_file; | ||
2708 | |||
2709 | if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) { | ||
2710 | printk("NFSD: nfsd4_lock: permission denied!\n"); | ||
2711 | goto out; | ||
2712 | } | ||
2713 | |||
2714 | locks_init_lock(&file_lock); | ||
2715 | switch (lock->lk_type) { | ||
2716 | case NFS4_READ_LT: | ||
2717 | case NFS4_READW_LT: | ||
2718 | file_lock.fl_type = F_RDLCK; | ||
2719 | break; | ||
2720 | case NFS4_WRITE_LT: | ||
2721 | case NFS4_WRITEW_LT: | ||
2722 | file_lock.fl_type = F_WRLCK; | ||
2723 | break; | ||
2724 | default: | ||
2725 | status = nfserr_inval; | ||
2726 | goto out; | ||
2727 | } | ||
2728 | file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner; | ||
2729 | file_lock.fl_pid = current->tgid; | ||
2730 | file_lock.fl_file = filp; | ||
2731 | file_lock.fl_flags = FL_POSIX; | ||
2732 | |||
2733 | file_lock.fl_start = lock->lk_offset; | ||
2734 | if ((lock->lk_length == ~(u64)0) || | ||
2735 | LOFF_OVERFLOW(lock->lk_offset, lock->lk_length)) | ||
2736 | file_lock.fl_end = ~(u64)0; | ||
2737 | else | ||
2738 | file_lock.fl_end = lock->lk_offset + lock->lk_length - 1; | ||
2739 | nfs4_transform_lock_offset(&file_lock); | ||
2740 | |||
2741 | /* | ||
2742 | * Try to lock the file in the VFS. | ||
2743 | * Note: locks.c uses the BKL to protect the inode's lock list. | ||
2744 | */ | ||
2745 | |||
2746 | status = posix_lock_file(filp, &file_lock); | ||
2747 | if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) | ||
2748 | file_lock.fl_ops->fl_release_private(&file_lock); | ||
2749 | dprintk("NFSD: nfsd4_lock: posix_lock_file status %d\n",status); | ||
2750 | switch (-status) { | ||
2751 | case 0: /* success! */ | ||
2752 | update_stateid(&lock_stp->st_stateid); | ||
2753 | memcpy(&lock->lk_resp_stateid, &lock_stp->st_stateid, | ||
2754 | sizeof(stateid_t)); | ||
2755 | goto out; | ||
2756 | case (EAGAIN): | ||
2757 | goto conflicting_lock; | ||
2758 | case (EDEADLK): | ||
2759 | status = nfserr_deadlock; | ||
2760 | default: | ||
2761 | dprintk("NFSD: nfsd4_lock: posix_lock_file() failed! status %d\n",status); | ||
2762 | goto out_destroy_new_stateid; | ||
2763 | } | ||
2764 | |||
2765 | conflicting_lock: | ||
2766 | dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); | ||
2767 | status = nfserr_denied; | ||
2768 | /* XXX There is a race here. Future patch needed to provide | ||
2769 | * an atomic posix_lock_and_test_file | ||
2770 | */ | ||
2771 | if (!(conflock = posix_test_lock(filp, &file_lock))) { | ||
2772 | status = nfserr_serverfault; | ||
2773 | goto out; | ||
2774 | } | ||
2775 | nfs4_set_lock_denied(conflock, &lock->lk_denied); | ||
2776 | |||
2777 | out_destroy_new_stateid: | ||
2778 | if (lock->lk_is_new) { | ||
2779 | dprintk("NFSD: nfsd4_lock: destroy new stateid!\n"); | ||
2780 | /* | ||
2781 | * An error encountered after instantiation of the new | ||
2782 | * stateid has forced us to destroy it. | ||
2783 | */ | ||
2784 | if (!seqid_mutating_err(status)) | ||
2785 | open_sop->so_seqid--; | ||
2786 | |||
2787 | release_state_owner(lock_stp, LOCK_STATE); | ||
2788 | } | ||
2789 | out: | ||
2790 | if (lock->lk_stateowner) | ||
2791 | nfs4_get_stateowner(lock->lk_stateowner); | ||
2792 | nfs4_unlock_state(); | ||
2793 | return status; | ||
2794 | } | ||
2795 | |||
2796 | /* | ||
2797 | * LOCKT operation | ||
2798 | */ | ||
2799 | int | ||
2800 | nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lockt *lockt) | ||
2801 | { | ||
2802 | struct inode *inode; | ||
2803 | struct file file; | ||
2804 | struct file_lock file_lock; | ||
2805 | struct file_lock *conflicting_lock; | ||
2806 | int status; | ||
2807 | |||
2808 | if (nfs4_in_grace()) | ||
2809 | return nfserr_grace; | ||
2810 | |||
2811 | if (check_lock_length(lockt->lt_offset, lockt->lt_length)) | ||
2812 | return nfserr_inval; | ||
2813 | |||
2814 | lockt->lt_stateowner = NULL; | ||
2815 | nfs4_lock_state(); | ||
2816 | |||
2817 | status = nfserr_stale_clientid; | ||
2818 | if (STALE_CLIENTID(&lockt->lt_clientid)) { | ||
2819 | printk("NFSD: nfsd4_lockt: clientid is stale!\n"); | ||
2820 | goto out; | ||
2821 | } | ||
2822 | |||
2823 | if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) { | ||
2824 | printk("NFSD: nfsd4_lockt: fh_verify() failed!\n"); | ||
2825 | if (status == nfserr_symlink) | ||
2826 | status = nfserr_inval; | ||
2827 | goto out; | ||
2828 | } | ||
2829 | |||
2830 | inode = current_fh->fh_dentry->d_inode; | ||
2831 | locks_init_lock(&file_lock); | ||
2832 | switch (lockt->lt_type) { | ||
2833 | case NFS4_READ_LT: | ||
2834 | case NFS4_READW_LT: | ||
2835 | file_lock.fl_type = F_RDLCK; | ||
2836 | break; | ||
2837 | case NFS4_WRITE_LT: | ||
2838 | case NFS4_WRITEW_LT: | ||
2839 | file_lock.fl_type = F_WRLCK; | ||
2840 | break; | ||
2841 | default: | ||
2842 | printk("NFSD: nfs4_lockt: bad lock type!\n"); | ||
2843 | status = nfserr_inval; | ||
2844 | goto out; | ||
2845 | } | ||
2846 | |||
2847 | lockt->lt_stateowner = find_lockstateowner_str(inode, | ||
2848 | &lockt->lt_clientid, &lockt->lt_owner); | ||
2849 | if (lockt->lt_stateowner) | ||
2850 | file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; | ||
2851 | file_lock.fl_pid = current->tgid; | ||
2852 | file_lock.fl_flags = FL_POSIX; | ||
2853 | |||
2854 | file_lock.fl_start = lockt->lt_offset; | ||
2855 | if ((lockt->lt_length == ~(u64)0) || LOFF_OVERFLOW(lockt->lt_offset, lockt->lt_length)) | ||
2856 | file_lock.fl_end = ~(u64)0; | ||
2857 | else | ||
2858 | file_lock.fl_end = lockt->lt_offset + lockt->lt_length - 1; | ||
2859 | |||
2860 | nfs4_transform_lock_offset(&file_lock); | ||
2861 | |||
2862 | /* posix_test_lock uses the struct file _only_ to resolve the inode. | ||
2863 | * since LOCKT doesn't require an OPEN, and therefore a struct | ||
2864 | * file may not exist, pass posix_test_lock a struct file with | ||
2865 | * only the dentry:inode set. | ||
2866 | */ | ||
2867 | memset(&file, 0, sizeof (struct file)); | ||
2868 | file.f_dentry = current_fh->fh_dentry; | ||
2869 | |||
2870 | status = nfs_ok; | ||
2871 | conflicting_lock = posix_test_lock(&file, &file_lock); | ||
2872 | if (conflicting_lock) { | ||
2873 | status = nfserr_denied; | ||
2874 | nfs4_set_lock_denied(conflicting_lock, &lockt->lt_denied); | ||
2875 | } | ||
2876 | out: | ||
2877 | nfs4_unlock_state(); | ||
2878 | return status; | ||
2879 | } | ||
2880 | |||
2881 | int | ||
2882 | nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_locku *locku) | ||
2883 | { | ||
2884 | struct nfs4_stateid *stp; | ||
2885 | struct file *filp = NULL; | ||
2886 | struct file_lock file_lock; | ||
2887 | int status; | ||
2888 | |||
2889 | dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", | ||
2890 | (long long) locku->lu_offset, | ||
2891 | (long long) locku->lu_length); | ||
2892 | |||
2893 | if (check_lock_length(locku->lu_offset, locku->lu_length)) | ||
2894 | return nfserr_inval; | ||
2895 | |||
2896 | nfs4_lock_state(); | ||
2897 | |||
2898 | if ((status = nfs4_preprocess_seqid_op(current_fh, | ||
2899 | locku->lu_seqid, | ||
2900 | &locku->lu_stateid, | ||
2901 | CHECK_FH | LOCK_STATE, | ||
2902 | &locku->lu_stateowner, &stp, NULL))) | ||
2903 | goto out; | ||
2904 | |||
2905 | filp = stp->st_vfs_file; | ||
2906 | BUG_ON(!filp); | ||
2907 | locks_init_lock(&file_lock); | ||
2908 | file_lock.fl_type = F_UNLCK; | ||
2909 | file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; | ||
2910 | file_lock.fl_pid = current->tgid; | ||
2911 | file_lock.fl_file = filp; | ||
2912 | file_lock.fl_flags = FL_POSIX; | ||
2913 | file_lock.fl_start = locku->lu_offset; | ||
2914 | |||
2915 | if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) | ||
2916 | file_lock.fl_end = ~(u64)0; | ||
2917 | else | ||
2918 | file_lock.fl_end = locku->lu_offset + locku->lu_length - 1; | ||
2919 | nfs4_transform_lock_offset(&file_lock); | ||
2920 | |||
2921 | /* | ||
2922 | * Try to unlock the file in the VFS. | ||
2923 | */ | ||
2924 | status = posix_lock_file(filp, &file_lock); | ||
2925 | if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) | ||
2926 | file_lock.fl_ops->fl_release_private(&file_lock); | ||
2927 | if (status) { | ||
2928 | printk("NFSD: nfs4_locku: posix_lock_file failed!\n"); | ||
2929 | goto out_nfserr; | ||
2930 | } | ||
2931 | /* | ||
2932 | * OK, unlock succeeded; the only thing left to do is update the stateid. | ||
2933 | */ | ||
2934 | update_stateid(&stp->st_stateid); | ||
2935 | memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t)); | ||
2936 | |||
2937 | out: | ||
2938 | if (locku->lu_stateowner) | ||
2939 | nfs4_get_stateowner(locku->lu_stateowner); | ||
2940 | nfs4_unlock_state(); | ||
2941 | return status; | ||
2942 | |||
2943 | out_nfserr: | ||
2944 | status = nfserrno(status); | ||
2945 | goto out; | ||
2946 | } | ||
2947 | |||
2948 | /* | ||
2949 | * returns | ||
2950 | * 1: locks held by lockowner | ||
2951 | * 0: no locks held by lockowner | ||
2952 | */ | ||
2953 | static int | ||
2954 | check_for_locks(struct file *filp, struct nfs4_stateowner *lowner) | ||
2955 | { | ||
2956 | struct file_lock **flpp; | ||
2957 | struct inode *inode = filp->f_dentry->d_inode; | ||
2958 | int status = 0; | ||
2959 | |||
2960 | lock_kernel(); | ||
2961 | for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { | ||
2962 | if ((*flpp)->fl_owner == (fl_owner_t)lowner) | ||
2963 | status = 1; | ||
2964 | goto out; | ||
2965 | } | ||
2966 | out: | ||
2967 | unlock_kernel(); | ||
2968 | return status; | ||
2969 | } | ||
2970 | |||
2971 | int | ||
2972 | nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) | ||
2973 | { | ||
2974 | clientid_t *clid = &rlockowner->rl_clientid; | ||
2975 | struct nfs4_stateowner *local = NULL; | ||
2976 | struct xdr_netobj *owner = &rlockowner->rl_owner; | ||
2977 | int status; | ||
2978 | |||
2979 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", | ||
2980 | clid->cl_boot, clid->cl_id); | ||
2981 | |||
2982 | /* XXX check for lease expiration */ | ||
2983 | |||
2984 | status = nfserr_stale_clientid; | ||
2985 | if (STALE_CLIENTID(clid)) { | ||
2986 | printk("NFSD: nfsd4_release_lockowner: clientid is stale!\n"); | ||
2987 | return status; | ||
2988 | } | ||
2989 | |||
2990 | nfs4_lock_state(); | ||
2991 | |||
2992 | status = nfs_ok; | ||
2993 | local = find_lockstateowner(owner, clid); | ||
2994 | if (local) { | ||
2995 | struct nfs4_stateid *stp; | ||
2996 | |||
2997 | /* check for any locks held by any stateid | ||
2998 | * associated with the (lock) stateowner */ | ||
2999 | status = nfserr_locks_held; | ||
3000 | list_for_each_entry(stp, &local->so_perfilestate, | ||
3001 | st_perfilestate) { | ||
3002 | if (check_for_locks(stp->st_vfs_file, local)) | ||
3003 | goto out; | ||
3004 | } | ||
3005 | /* no locks held by (lock) stateowner */ | ||
3006 | status = nfs_ok; | ||
3007 | release_stateowner(local); | ||
3008 | } | ||
3009 | out: | ||
3010 | nfs4_unlock_state(); | ||
3011 | return status; | ||
3012 | } | ||
3013 | |||
3014 | static inline struct nfs4_client_reclaim * | ||
3015 | alloc_reclaim(int namelen) | ||
3016 | { | ||
3017 | struct nfs4_client_reclaim *crp = NULL; | ||
3018 | |||
3019 | crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); | ||
3020 | if (!crp) | ||
3021 | return NULL; | ||
3022 | crp->cr_name.data = kmalloc(namelen, GFP_KERNEL); | ||
3023 | if (!crp->cr_name.data) { | ||
3024 | kfree(crp); | ||
3025 | return NULL; | ||
3026 | } | ||
3027 | return crp; | ||
3028 | } | ||
3029 | |||
3030 | /* | ||
3031 | * failure => all reset bets are off, nfserr_no_grace... | ||
3032 | */ | ||
3033 | static int | ||
3034 | nfs4_client_to_reclaim(char *name, int namlen) | ||
3035 | { | ||
3036 | unsigned int strhashval; | ||
3037 | struct nfs4_client_reclaim *crp = NULL; | ||
3038 | |||
3039 | dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name); | ||
3040 | crp = alloc_reclaim(namlen); | ||
3041 | if (!crp) | ||
3042 | return 0; | ||
3043 | strhashval = clientstr_hashval(name, namlen); | ||
3044 | INIT_LIST_HEAD(&crp->cr_strhash); | ||
3045 | list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); | ||
3046 | memcpy(crp->cr_name.data, name, namlen); | ||
3047 | crp->cr_name.len = namlen; | ||
3048 | reclaim_str_hashtbl_size++; | ||
3049 | return 1; | ||
3050 | } | ||
3051 | |||
3052 | static void | ||
3053 | nfs4_release_reclaim(void) | ||
3054 | { | ||
3055 | struct nfs4_client_reclaim *crp = NULL; | ||
3056 | int i; | ||
3057 | |||
3058 | BUG_ON(!nfs4_reclaim_init); | ||
3059 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
3060 | while (!list_empty(&reclaim_str_hashtbl[i])) { | ||
3061 | crp = list_entry(reclaim_str_hashtbl[i].next, | ||
3062 | struct nfs4_client_reclaim, cr_strhash); | ||
3063 | list_del(&crp->cr_strhash); | ||
3064 | kfree(crp->cr_name.data); | ||
3065 | kfree(crp); | ||
3066 | reclaim_str_hashtbl_size--; | ||
3067 | } | ||
3068 | } | ||
3069 | BUG_ON(reclaim_str_hashtbl_size); | ||
3070 | } | ||
3071 | |||
3072 | /* | ||
3073 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ | ||
3074 | struct nfs4_client_reclaim * | ||
3075 | nfs4_find_reclaim_client(clientid_t *clid) | ||
3076 | { | ||
3077 | unsigned int strhashval; | ||
3078 | struct nfs4_client *clp; | ||
3079 | struct nfs4_client_reclaim *crp = NULL; | ||
3080 | |||
3081 | |||
3082 | /* find clientid in conf_id_hashtbl */ | ||
3083 | clp = find_confirmed_client(clid); | ||
3084 | if (clp == NULL) | ||
3085 | return NULL; | ||
3086 | |||
3087 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n", | ||
3088 | clp->cl_name.len, clp->cl_name.data); | ||
3089 | |||
3090 | /* find clp->cl_name in reclaim_str_hashtbl */ | ||
3091 | strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); | ||
3092 | list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { | ||
3093 | if (cmp_name(&crp->cr_name, &clp->cl_name)) { | ||
3094 | return crp; | ||
3095 | } | ||
3096 | } | ||
3097 | return NULL; | ||
3098 | } | ||
3099 | |||
3100 | /* | ||
3101 | * Called from OPEN. Look for clientid in reclaim list. | ||
3102 | */ | ||
3103 | int | ||
3104 | nfs4_check_open_reclaim(clientid_t *clid) | ||
3105 | { | ||
3106 | struct nfs4_client_reclaim *crp; | ||
3107 | |||
3108 | if ((crp = nfs4_find_reclaim_client(clid)) == NULL) | ||
3109 | return nfserr_reclaim_bad; | ||
3110 | return nfs_ok; | ||
3111 | } | ||
3112 | |||
3113 | |||
3114 | /* | ||
3115 | * Start and stop routines | ||
3116 | */ | ||
3117 | |||
3118 | static void | ||
3119 | __nfs4_state_init(void) | ||
3120 | { | ||
3121 | int i; | ||
3122 | time_t grace_time; | ||
3123 | |||
3124 | if (!nfs4_reclaim_init) { | ||
3125 | for (i = 0; i < CLIENT_HASH_SIZE; i++) | ||
3126 | INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); | ||
3127 | reclaim_str_hashtbl_size = 0; | ||
3128 | nfs4_reclaim_init = 1; | ||
3129 | } | ||
3130 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
3131 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); | ||
3132 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); | ||
3133 | INIT_LIST_HEAD(&unconf_str_hashtbl[i]); | ||
3134 | INIT_LIST_HEAD(&unconf_id_hashtbl[i]); | ||
3135 | } | ||
3136 | for (i = 0; i < FILE_HASH_SIZE; i++) { | ||
3137 | INIT_LIST_HEAD(&file_hashtbl[i]); | ||
3138 | } | ||
3139 | for (i = 0; i < OWNER_HASH_SIZE; i++) { | ||
3140 | INIT_LIST_HEAD(&ownerstr_hashtbl[i]); | ||
3141 | INIT_LIST_HEAD(&ownerid_hashtbl[i]); | ||
3142 | } | ||
3143 | for (i = 0; i < STATEID_HASH_SIZE; i++) { | ||
3144 | INIT_LIST_HEAD(&stateid_hashtbl[i]); | ||
3145 | INIT_LIST_HEAD(&lockstateid_hashtbl[i]); | ||
3146 | } | ||
3147 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | ||
3148 | INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); | ||
3149 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); | ||
3150 | } | ||
3151 | memset(&zerostateid, 0, sizeof(stateid_t)); | ||
3152 | memset(&onestateid, ~0, sizeof(stateid_t)); | ||
3153 | |||
3154 | INIT_LIST_HEAD(&close_lru); | ||
3155 | INIT_LIST_HEAD(&client_lru); | ||
3156 | INIT_LIST_HEAD(&del_recall_lru); | ||
3157 | spin_lock_init(&recall_lock); | ||
3158 | boot_time = get_seconds(); | ||
3159 | grace_time = max(old_lease_time, lease_time); | ||
3160 | if (reclaim_str_hashtbl_size == 0) | ||
3161 | grace_time = 0; | ||
3162 | if (grace_time) | ||
3163 | printk("NFSD: starting %ld-second grace period\n", grace_time); | ||
3164 | grace_end = boot_time + grace_time; | ||
3165 | INIT_WORK(&laundromat_work,laundromat_main, NULL); | ||
3166 | schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); | ||
3167 | } | ||
3168 | |||
3169 | int | ||
3170 | nfs4_state_init(void) | ||
3171 | { | ||
3172 | int status; | ||
3173 | |||
3174 | if (nfs4_init) | ||
3175 | return 0; | ||
3176 | status = nfsd4_init_slabs(); | ||
3177 | if (status) | ||
3178 | return status; | ||
3179 | __nfs4_state_init(); | ||
3180 | nfs4_init = 1; | ||
3181 | return 0; | ||
3182 | } | ||
3183 | |||
3184 | int | ||
3185 | nfs4_in_grace(void) | ||
3186 | { | ||
3187 | return get_seconds() < grace_end; | ||
3188 | } | ||
3189 | |||
3190 | void | ||
3191 | set_no_grace(void) | ||
3192 | { | ||
3193 | printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n"); | ||
3194 | grace_end = get_seconds(); | ||
3195 | } | ||
3196 | |||
3197 | time_t | ||
3198 | nfs4_lease_time(void) | ||
3199 | { | ||
3200 | return lease_time; | ||
3201 | } | ||
3202 | |||
3203 | static void | ||
3204 | __nfs4_state_shutdown(void) | ||
3205 | { | ||
3206 | int i; | ||
3207 | struct nfs4_client *clp = NULL; | ||
3208 | struct nfs4_delegation *dp = NULL; | ||
3209 | struct nfs4_stateowner *sop = NULL; | ||
3210 | struct list_head *pos, *next, reaplist; | ||
3211 | |||
3212 | list_for_each_safe(pos, next, &close_lru) { | ||
3213 | sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); | ||
3214 | list_del(&sop->so_close_lru); | ||
3215 | nfs4_put_stateowner(sop); | ||
3216 | } | ||
3217 | |||
3218 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
3219 | while (!list_empty(&conf_id_hashtbl[i])) { | ||
3220 | clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); | ||
3221 | expire_client(clp); | ||
3222 | } | ||
3223 | while (!list_empty(&unconf_str_hashtbl[i])) { | ||
3224 | clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); | ||
3225 | expire_client(clp); | ||
3226 | } | ||
3227 | } | ||
3228 | INIT_LIST_HEAD(&reaplist); | ||
3229 | spin_lock(&recall_lock); | ||
3230 | list_for_each_safe(pos, next, &del_recall_lru) { | ||
3231 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | ||
3232 | list_move(&dp->dl_recall_lru, &reaplist); | ||
3233 | } | ||
3234 | spin_unlock(&recall_lock); | ||
3235 | list_for_each_safe(pos, next, &reaplist) { | ||
3236 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | ||
3237 | list_del_init(&dp->dl_recall_lru); | ||
3238 | unhash_delegation(dp); | ||
3239 | } | ||
3240 | |||
3241 | release_all_files(); | ||
3242 | cancel_delayed_work(&laundromat_work); | ||
3243 | flush_scheduled_work(); | ||
3244 | nfs4_init = 0; | ||
3245 | dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n", | ||
3246 | list_add_perfile, list_del_perfile); | ||
3247 | dprintk("NFSD: add_perclient %d del_perclient %d\n", | ||
3248 | add_perclient, del_perclient); | ||
3249 | dprintk("NFSD: alloc_file %d free_file %d\n", | ||
3250 | alloc_file, free_file); | ||
3251 | dprintk("NFSD: vfsopen %d vfsclose %d\n", | ||
3252 | vfsopen, vfsclose); | ||
3253 | dprintk("NFSD: alloc_delegation %d free_delegation %d\n", | ||
3254 | alloc_delegation, free_delegation); | ||
3255 | |||
3256 | } | ||
3257 | |||
3258 | void | ||
3259 | nfs4_state_shutdown(void) | ||
3260 | { | ||
3261 | nfs4_lock_state(); | ||
3262 | nfs4_release_reclaim(); | ||
3263 | __nfs4_state_shutdown(); | ||
3264 | nfsd4_free_slabs(); | ||
3265 | nfs4_unlock_state(); | ||
3266 | } | ||
3267 | |||
3268 | /* | ||
3269 | * Called when leasetime is changed. | ||
3270 | * | ||
3271 | * if nfsd is not started, simply set the global lease. | ||
3272 | * | ||
3273 | * if nfsd(s) are running, lease change requires nfsv4 state to be reset. | ||
3274 | * e.g: boot_time is reset, existing nfs4_client structs are | ||
3275 | * used to fill reclaim_str_hashtbl, then all state (except for the | ||
3276 | * reclaim_str_hashtbl) is re-initialized. | ||
3277 | * | ||
3278 | * if the old lease time is greater than the new lease time, the grace | ||
3279 | * period needs to be set to the old lease time to allow clients to reclaim | ||
3280 | * their state. XXX - we may want to set the grace period == lease time | ||
3281 | * after an initial grace period == old lease time | ||
3282 | * | ||
3283 | * if an error occurs in this process, the new lease is set, but the server | ||
3284 | * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace | ||
3285 | * which means OPEN/LOCK/READ/WRITE will fail during grace period. | ||
3286 | * | ||
3287 | * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and | ||
3288 | * OPEN and LOCK reclaims. | ||
3289 | */ | ||
3290 | void | ||
3291 | nfs4_reset_lease(time_t leasetime) | ||
3292 | { | ||
3293 | struct nfs4_client *clp; | ||
3294 | int i; | ||
3295 | |||
3296 | printk("NFSD: New leasetime %ld\n",leasetime); | ||
3297 | if (!nfs4_init) | ||
3298 | return; | ||
3299 | nfs4_lock_state(); | ||
3300 | old_lease_time = lease_time; | ||
3301 | lease_time = leasetime; | ||
3302 | |||
3303 | nfs4_release_reclaim(); | ||
3304 | |||
3305 | /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */ | ||
3306 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
3307 | list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) { | ||
3308 | if (!nfs4_client_to_reclaim(clp->cl_name.data, | ||
3309 | clp->cl_name.len)) { | ||
3310 | nfs4_release_reclaim(); | ||
3311 | goto init_state; | ||
3312 | } | ||
3313 | } | ||
3314 | } | ||
3315 | init_state: | ||
3316 | __nfs4_state_shutdown(); | ||
3317 | __nfs4_state_init(); | ||
3318 | nfs4_unlock_state(); | ||
3319 | } | ||
3320 | |||
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c new file mode 100644 index 000000000000..36a058a112d5 --- /dev/null +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -0,0 +1,2536 @@ | |||
1 | /* | ||
2 | * fs/nfs/nfs4xdr.c | ||
3 | * | ||
4 | * Server-side XDR for NFSv4 | ||
5 | * | ||
6 | * Copyright (c) 2002 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Kendrick Smith <kmsmith@umich.edu> | ||
10 | * Andy Adamson <andros@umich.edu> | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or without | ||
13 | * modification, are permitted provided that the following conditions | ||
14 | * are met: | ||
15 | * | ||
16 | * 1. Redistributions of source code must retain the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer. | ||
18 | * 2. Redistributions in binary form must reproduce the above copyright | ||
19 | * notice, this list of conditions and the following disclaimer in the | ||
20 | * documentation and/or other materials provided with the distribution. | ||
21 | * 3. Neither the name of the University nor the names of its | ||
22 | * contributors may be used to endorse or promote products derived | ||
23 | * from this software without specific prior written permission. | ||
24 | * | ||
25 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
26 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
27 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
28 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
34 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
36 | * | ||
37 | * TODO: Neil Brown made the following observation: We currently | ||
38 | * initially reserve NFSD_BUFSIZE space on the transmit queue and | ||
39 | * never release any of that until the request is complete. | ||
40 | * It would be good to calculate a new maximum response size while | ||
41 | * decoding the COMPOUND, and call svc_reserve with this number | ||
42 | * at the end of nfs4svc_decode_compoundargs. | ||
43 | */ | ||
44 | |||
45 | #include <linux/param.h> | ||
46 | #include <linux/smp.h> | ||
47 | #include <linux/smp_lock.h> | ||
48 | #include <linux/fs.h> | ||
49 | #include <linux/namei.h> | ||
50 | #include <linux/vfs.h> | ||
51 | #include <linux/sunrpc/xdr.h> | ||
52 | #include <linux/sunrpc/svc.h> | ||
53 | #include <linux/sunrpc/clnt.h> | ||
54 | #include <linux/nfsd/nfsd.h> | ||
55 | #include <linux/nfsd/state.h> | ||
56 | #include <linux/nfsd/xdr4.h> | ||
57 | #include <linux/nfsd_idmap.h> | ||
58 | #include <linux/nfs4.h> | ||
59 | #include <linux/nfs4_acl.h> | ||
60 | |||
61 | #define NFSDDBG_FACILITY NFSDDBG_XDR | ||
62 | |||
63 | static int | ||
64 | check_filename(char *str, int len, int err) | ||
65 | { | ||
66 | int i; | ||
67 | |||
68 | if (len == 0) | ||
69 | return nfserr_inval; | ||
70 | if (isdotent(str, len)) | ||
71 | return err; | ||
72 | for (i = 0; i < len; i++) | ||
73 | if (str[i] == '/') | ||
74 | return err; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * START OF "GENERIC" DECODE ROUTINES. | ||
80 | * These may look a little ugly since they are imported from a "generic" | ||
81 | * set of XDR encode/decode routines which are intended to be shared by | ||
82 | * all of our NFSv4 implementations (OpenBSD, MacOS X...). | ||
83 | * | ||
84 | * If the pain of reading these is too great, it should be a straightforward | ||
85 | * task to translate them into Linux-specific versions which are more | ||
86 | * consistent with the style used in NFSv2/v3... | ||
87 | */ | ||
88 | #define DECODE_HEAD \ | ||
89 | u32 *p; \ | ||
90 | int status | ||
91 | #define DECODE_TAIL \ | ||
92 | status = 0; \ | ||
93 | out: \ | ||
94 | return status; \ | ||
95 | xdr_error: \ | ||
96 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
97 | status = nfserr_bad_xdr; \ | ||
98 | goto out | ||
99 | |||
100 | #define READ32(x) (x) = ntohl(*p++) | ||
101 | #define READ64(x) do { \ | ||
102 | (x) = (u64)ntohl(*p++) << 32; \ | ||
103 | (x) |= ntohl(*p++); \ | ||
104 | } while (0) | ||
105 | #define READTIME(x) do { \ | ||
106 | p++; \ | ||
107 | (x) = ntohl(*p++); \ | ||
108 | p++; \ | ||
109 | } while (0) | ||
110 | #define READMEM(x,nbytes) do { \ | ||
111 | x = (char *)p; \ | ||
112 | p += XDR_QUADLEN(nbytes); \ | ||
113 | } while (0) | ||
114 | #define SAVEMEM(x,nbytes) do { \ | ||
115 | if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ | ||
116 | savemem(argp, p, nbytes) : \ | ||
117 | (char *)p)) { \ | ||
118 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
119 | goto xdr_error; \ | ||
120 | } \ | ||
121 | p += XDR_QUADLEN(nbytes); \ | ||
122 | } while (0) | ||
123 | #define COPYMEM(x,nbytes) do { \ | ||
124 | memcpy((x), p, nbytes); \ | ||
125 | p += XDR_QUADLEN(nbytes); \ | ||
126 | } while (0) | ||
127 | |||
128 | /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */ | ||
129 | #define READ_BUF(nbytes) do { \ | ||
130 | if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \ | ||
131 | p = argp->p; \ | ||
132 | argp->p += XDR_QUADLEN(nbytes); \ | ||
133 | } else if (!(p = read_buf(argp, nbytes))) { \ | ||
134 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
135 | goto xdr_error; \ | ||
136 | } \ | ||
137 | } while (0) | ||
138 | |||
139 | u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes) | ||
140 | { | ||
141 | /* We want more bytes than seem to be available. | ||
142 | * Maybe we need a new page, maybe we have just run out | ||
143 | */ | ||
144 | int avail = (char*)argp->end - (char*)argp->p; | ||
145 | u32 *p; | ||
146 | if (avail + argp->pagelen < nbytes) | ||
147 | return NULL; | ||
148 | if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ | ||
149 | return NULL; | ||
150 | /* ok, we can do it with the current plus the next page */ | ||
151 | if (nbytes <= sizeof(argp->tmp)) | ||
152 | p = argp->tmp; | ||
153 | else { | ||
154 | if (argp->tmpp) | ||
155 | kfree(argp->tmpp); | ||
156 | p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL); | ||
157 | if (!p) | ||
158 | return NULL; | ||
159 | |||
160 | } | ||
161 | memcpy(p, argp->p, avail); | ||
162 | /* step to next page */ | ||
163 | argp->p = page_address(argp->pagelist[0]); | ||
164 | argp->pagelist++; | ||
165 | if (argp->pagelen < PAGE_SIZE) { | ||
166 | argp->end = p + (argp->pagelen>>2); | ||
167 | argp->pagelen = 0; | ||
168 | } else { | ||
169 | argp->end = p + (PAGE_SIZE>>2); | ||
170 | argp->pagelen -= PAGE_SIZE; | ||
171 | } | ||
172 | memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); | ||
173 | argp->p += XDR_QUADLEN(nbytes - avail); | ||
174 | return p; | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | defer_free(struct nfsd4_compoundargs *argp, | ||
179 | void (*release)(const void *), void *p) | ||
180 | { | ||
181 | struct tmpbuf *tb; | ||
182 | |||
183 | tb = kmalloc(sizeof(*tb), GFP_KERNEL); | ||
184 | if (!tb) | ||
185 | return -ENOMEM; | ||
186 | tb->buf = p; | ||
187 | tb->release = release; | ||
188 | tb->next = argp->to_free; | ||
189 | argp->to_free = tb; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes) | ||
194 | { | ||
195 | void *new = NULL; | ||
196 | if (p == argp->tmp) { | ||
197 | new = kmalloc(nbytes, GFP_KERNEL); | ||
198 | if (!new) return NULL; | ||
199 | p = new; | ||
200 | memcpy(p, argp->tmp, nbytes); | ||
201 | } else { | ||
202 | if (p != argp->tmpp) | ||
203 | BUG(); | ||
204 | argp->tmpp = NULL; | ||
205 | } | ||
206 | if (defer_free(argp, kfree, p)) { | ||
207 | kfree(new); | ||
208 | return NULL; | ||
209 | } else | ||
210 | return (char *)p; | ||
211 | } | ||
212 | |||
213 | |||
214 | static int | ||
215 | nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) | ||
216 | { | ||
217 | u32 bmlen; | ||
218 | DECODE_HEAD; | ||
219 | |||
220 | bmval[0] = 0; | ||
221 | bmval[1] = 0; | ||
222 | |||
223 | READ_BUF(4); | ||
224 | READ32(bmlen); | ||
225 | if (bmlen > 1000) | ||
226 | goto xdr_error; | ||
227 | |||
228 | READ_BUF(bmlen << 2); | ||
229 | if (bmlen > 0) | ||
230 | READ32(bmval[0]); | ||
231 | if (bmlen > 1) | ||
232 | READ32(bmval[1]); | ||
233 | |||
234 | DECODE_TAIL; | ||
235 | } | ||
236 | |||
237 | static int | ||
238 | nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr, | ||
239 | struct nfs4_acl **acl) | ||
240 | { | ||
241 | int expected_len, len = 0; | ||
242 | u32 dummy32; | ||
243 | char *buf; | ||
244 | |||
245 | DECODE_HEAD; | ||
246 | iattr->ia_valid = 0; | ||
247 | if ((status = nfsd4_decode_bitmap(argp, bmval))) | ||
248 | return status; | ||
249 | |||
250 | /* | ||
251 | * According to spec, unsupported attributes return ERR_NOTSUPP; | ||
252 | * read-only attributes return ERR_INVAL. | ||
253 | */ | ||
254 | if ((bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) || (bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1)) | ||
255 | return nfserr_attrnotsupp; | ||
256 | if ((bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0) || (bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1)) | ||
257 | return nfserr_inval; | ||
258 | |||
259 | READ_BUF(4); | ||
260 | READ32(expected_len); | ||
261 | |||
262 | if (bmval[0] & FATTR4_WORD0_SIZE) { | ||
263 | READ_BUF(8); | ||
264 | len += 8; | ||
265 | READ64(iattr->ia_size); | ||
266 | iattr->ia_valid |= ATTR_SIZE; | ||
267 | } | ||
268 | if (bmval[0] & FATTR4_WORD0_ACL) { | ||
269 | int nace, i; | ||
270 | struct nfs4_ace ace; | ||
271 | |||
272 | READ_BUF(4); len += 4; | ||
273 | READ32(nace); | ||
274 | |||
275 | *acl = nfs4_acl_new(); | ||
276 | if (*acl == NULL) { | ||
277 | status = -ENOMEM; | ||
278 | goto out_nfserr; | ||
279 | } | ||
280 | defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl); | ||
281 | |||
282 | for (i = 0; i < nace; i++) { | ||
283 | READ_BUF(16); len += 16; | ||
284 | READ32(ace.type); | ||
285 | READ32(ace.flag); | ||
286 | READ32(ace.access_mask); | ||
287 | READ32(dummy32); | ||
288 | READ_BUF(dummy32); | ||
289 | len += XDR_QUADLEN(dummy32) << 2; | ||
290 | READMEM(buf, dummy32); | ||
291 | ace.whotype = nfs4_acl_get_whotype(buf, dummy32); | ||
292 | status = 0; | ||
293 | if (ace.whotype != NFS4_ACL_WHO_NAMED) | ||
294 | ace.who = 0; | ||
295 | else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP) | ||
296 | status = nfsd_map_name_to_gid(argp->rqstp, | ||
297 | buf, dummy32, &ace.who); | ||
298 | else | ||
299 | status = nfsd_map_name_to_uid(argp->rqstp, | ||
300 | buf, dummy32, &ace.who); | ||
301 | if (status) | ||
302 | goto out_nfserr; | ||
303 | if (nfs4_acl_add_ace(*acl, ace.type, ace.flag, | ||
304 | ace.access_mask, ace.whotype, ace.who) != 0) { | ||
305 | status = -ENOMEM; | ||
306 | goto out_nfserr; | ||
307 | } | ||
308 | } | ||
309 | } else | ||
310 | *acl = NULL; | ||
311 | if (bmval[1] & FATTR4_WORD1_MODE) { | ||
312 | READ_BUF(4); | ||
313 | len += 4; | ||
314 | READ32(iattr->ia_mode); | ||
315 | iattr->ia_mode &= (S_IFMT | S_IALLUGO); | ||
316 | iattr->ia_valid |= ATTR_MODE; | ||
317 | } | ||
318 | if (bmval[1] & FATTR4_WORD1_OWNER) { | ||
319 | READ_BUF(4); | ||
320 | len += 4; | ||
321 | READ32(dummy32); | ||
322 | READ_BUF(dummy32); | ||
323 | len += (XDR_QUADLEN(dummy32) << 2); | ||
324 | READMEM(buf, dummy32); | ||
325 | if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid))) | ||
326 | goto out_nfserr; | ||
327 | iattr->ia_valid |= ATTR_UID; | ||
328 | } | ||
329 | if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { | ||
330 | READ_BUF(4); | ||
331 | len += 4; | ||
332 | READ32(dummy32); | ||
333 | READ_BUF(dummy32); | ||
334 | len += (XDR_QUADLEN(dummy32) << 2); | ||
335 | READMEM(buf, dummy32); | ||
336 | if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid))) | ||
337 | goto out_nfserr; | ||
338 | iattr->ia_valid |= ATTR_GID; | ||
339 | } | ||
340 | if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { | ||
341 | READ_BUF(4); | ||
342 | len += 4; | ||
343 | READ32(dummy32); | ||
344 | switch (dummy32) { | ||
345 | case NFS4_SET_TO_CLIENT_TIME: | ||
346 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
347 | all 32 bits of 'nseconds'. */ | ||
348 | READ_BUF(12); | ||
349 | len += 12; | ||
350 | READ32(dummy32); | ||
351 | if (dummy32) | ||
352 | return nfserr_inval; | ||
353 | READ32(iattr->ia_atime.tv_sec); | ||
354 | READ32(iattr->ia_atime.tv_nsec); | ||
355 | if (iattr->ia_atime.tv_nsec >= (u32)1000000000) | ||
356 | return nfserr_inval; | ||
357 | iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); | ||
358 | break; | ||
359 | case NFS4_SET_TO_SERVER_TIME: | ||
360 | iattr->ia_valid |= ATTR_ATIME; | ||
361 | break; | ||
362 | default: | ||
363 | goto xdr_error; | ||
364 | } | ||
365 | } | ||
366 | if (bmval[1] & FATTR4_WORD1_TIME_METADATA) { | ||
367 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
368 | all 32 bits of 'nseconds'. */ | ||
369 | READ_BUF(12); | ||
370 | len += 12; | ||
371 | READ32(dummy32); | ||
372 | if (dummy32) | ||
373 | return nfserr_inval; | ||
374 | READ32(iattr->ia_ctime.tv_sec); | ||
375 | READ32(iattr->ia_ctime.tv_nsec); | ||
376 | if (iattr->ia_ctime.tv_nsec >= (u32)1000000000) | ||
377 | return nfserr_inval; | ||
378 | iattr->ia_valid |= ATTR_CTIME; | ||
379 | } | ||
380 | if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { | ||
381 | READ_BUF(4); | ||
382 | len += 4; | ||
383 | READ32(dummy32); | ||
384 | switch (dummy32) { | ||
385 | case NFS4_SET_TO_CLIENT_TIME: | ||
386 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
387 | all 32 bits of 'nseconds'. */ | ||
388 | READ_BUF(12); | ||
389 | len += 12; | ||
390 | READ32(dummy32); | ||
391 | if (dummy32) | ||
392 | return nfserr_inval; | ||
393 | READ32(iattr->ia_mtime.tv_sec); | ||
394 | READ32(iattr->ia_mtime.tv_nsec); | ||
395 | if (iattr->ia_mtime.tv_nsec >= (u32)1000000000) | ||
396 | return nfserr_inval; | ||
397 | iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); | ||
398 | break; | ||
399 | case NFS4_SET_TO_SERVER_TIME: | ||
400 | iattr->ia_valid |= ATTR_MTIME; | ||
401 | break; | ||
402 | default: | ||
403 | goto xdr_error; | ||
404 | } | ||
405 | } | ||
406 | if (len != expected_len) | ||
407 | goto xdr_error; | ||
408 | |||
409 | DECODE_TAIL; | ||
410 | |||
411 | out_nfserr: | ||
412 | status = nfserrno(status); | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | static int | ||
417 | nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access) | ||
418 | { | ||
419 | DECODE_HEAD; | ||
420 | |||
421 | READ_BUF(4); | ||
422 | READ32(access->ac_req_access); | ||
423 | |||
424 | DECODE_TAIL; | ||
425 | } | ||
426 | |||
427 | static int | ||
428 | nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) | ||
429 | { | ||
430 | DECODE_HEAD; | ||
431 | |||
432 | close->cl_stateowner = NULL; | ||
433 | READ_BUF(4 + sizeof(stateid_t)); | ||
434 | READ32(close->cl_seqid); | ||
435 | READ32(close->cl_stateid.si_generation); | ||
436 | COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
437 | |||
438 | DECODE_TAIL; | ||
439 | } | ||
440 | |||
441 | |||
442 | static int | ||
443 | nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit) | ||
444 | { | ||
445 | DECODE_HEAD; | ||
446 | |||
447 | READ_BUF(12); | ||
448 | READ64(commit->co_offset); | ||
449 | READ32(commit->co_count); | ||
450 | |||
451 | DECODE_TAIL; | ||
452 | } | ||
453 | |||
454 | static int | ||
455 | nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create) | ||
456 | { | ||
457 | DECODE_HEAD; | ||
458 | |||
459 | READ_BUF(4); | ||
460 | READ32(create->cr_type); | ||
461 | switch (create->cr_type) { | ||
462 | case NF4LNK: | ||
463 | READ_BUF(4); | ||
464 | READ32(create->cr_linklen); | ||
465 | READ_BUF(create->cr_linklen); | ||
466 | SAVEMEM(create->cr_linkname, create->cr_linklen); | ||
467 | break; | ||
468 | case NF4BLK: | ||
469 | case NF4CHR: | ||
470 | READ_BUF(8); | ||
471 | READ32(create->cr_specdata1); | ||
472 | READ32(create->cr_specdata2); | ||
473 | break; | ||
474 | case NF4SOCK: | ||
475 | case NF4FIFO: | ||
476 | case NF4DIR: | ||
477 | default: | ||
478 | break; | ||
479 | } | ||
480 | |||
481 | READ_BUF(4); | ||
482 | READ32(create->cr_namelen); | ||
483 | READ_BUF(create->cr_namelen); | ||
484 | SAVEMEM(create->cr_name, create->cr_namelen); | ||
485 | if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) | ||
486 | return status; | ||
487 | |||
488 | if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl))) | ||
489 | goto out; | ||
490 | |||
491 | DECODE_TAIL; | ||
492 | } | ||
493 | |||
494 | static inline int | ||
495 | nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr) | ||
496 | { | ||
497 | DECODE_HEAD; | ||
498 | |||
499 | READ_BUF(sizeof(stateid_t)); | ||
500 | READ32(dr->dr_stateid.si_generation); | ||
501 | COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
502 | |||
503 | DECODE_TAIL; | ||
504 | } | ||
505 | |||
506 | static inline int | ||
507 | nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr) | ||
508 | { | ||
509 | return nfsd4_decode_bitmap(argp, getattr->ga_bmval); | ||
510 | } | ||
511 | |||
512 | static int | ||
513 | nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) | ||
514 | { | ||
515 | DECODE_HEAD; | ||
516 | |||
517 | READ_BUF(4); | ||
518 | READ32(link->li_namelen); | ||
519 | READ_BUF(link->li_namelen); | ||
520 | SAVEMEM(link->li_name, link->li_namelen); | ||
521 | if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) | ||
522 | return status; | ||
523 | |||
524 | DECODE_TAIL; | ||
525 | } | ||
526 | |||
527 | static int | ||
528 | nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) | ||
529 | { | ||
530 | DECODE_HEAD; | ||
531 | |||
532 | lock->lk_stateowner = NULL; | ||
533 | /* | ||
534 | * type, reclaim(boolean), offset, length, new_lock_owner(boolean) | ||
535 | */ | ||
536 | READ_BUF(28); | ||
537 | READ32(lock->lk_type); | ||
538 | if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) | ||
539 | goto xdr_error; | ||
540 | READ32(lock->lk_reclaim); | ||
541 | READ64(lock->lk_offset); | ||
542 | READ64(lock->lk_length); | ||
543 | READ32(lock->lk_is_new); | ||
544 | |||
545 | if (lock->lk_is_new) { | ||
546 | READ_BUF(36); | ||
547 | READ32(lock->lk_new_open_seqid); | ||
548 | READ32(lock->lk_new_open_stateid.si_generation); | ||
549 | |||
550 | COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
551 | READ32(lock->lk_new_lock_seqid); | ||
552 | COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); | ||
553 | READ32(lock->lk_new_owner.len); | ||
554 | READ_BUF(lock->lk_new_owner.len); | ||
555 | READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); | ||
556 | } else { | ||
557 | READ_BUF(20); | ||
558 | READ32(lock->lk_old_lock_stateid.si_generation); | ||
559 | COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
560 | READ32(lock->lk_old_lock_seqid); | ||
561 | } | ||
562 | |||
563 | DECODE_TAIL; | ||
564 | } | ||
565 | |||
566 | static int | ||
567 | nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt) | ||
568 | { | ||
569 | DECODE_HEAD; | ||
570 | |||
571 | READ_BUF(32); | ||
572 | READ32(lockt->lt_type); | ||
573 | if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) | ||
574 | goto xdr_error; | ||
575 | READ64(lockt->lt_offset); | ||
576 | READ64(lockt->lt_length); | ||
577 | COPYMEM(&lockt->lt_clientid, 8); | ||
578 | READ32(lockt->lt_owner.len); | ||
579 | READ_BUF(lockt->lt_owner.len); | ||
580 | READMEM(lockt->lt_owner.data, lockt->lt_owner.len); | ||
581 | |||
582 | DECODE_TAIL; | ||
583 | } | ||
584 | |||
585 | static int | ||
586 | nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) | ||
587 | { | ||
588 | DECODE_HEAD; | ||
589 | |||
590 | locku->lu_stateowner = NULL; | ||
591 | READ_BUF(24 + sizeof(stateid_t)); | ||
592 | READ32(locku->lu_type); | ||
593 | if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) | ||
594 | goto xdr_error; | ||
595 | READ32(locku->lu_seqid); | ||
596 | READ32(locku->lu_stateid.si_generation); | ||
597 | COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
598 | READ64(locku->lu_offset); | ||
599 | READ64(locku->lu_length); | ||
600 | |||
601 | DECODE_TAIL; | ||
602 | } | ||
603 | |||
604 | static int | ||
605 | nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup) | ||
606 | { | ||
607 | DECODE_HEAD; | ||
608 | |||
609 | READ_BUF(4); | ||
610 | READ32(lookup->lo_len); | ||
611 | READ_BUF(lookup->lo_len); | ||
612 | SAVEMEM(lookup->lo_name, lookup->lo_len); | ||
613 | if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) | ||
614 | return status; | ||
615 | |||
616 | DECODE_TAIL; | ||
617 | } | ||
618 | |||
619 | static int | ||
620 | nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | ||
621 | { | ||
622 | DECODE_HEAD; | ||
623 | |||
624 | memset(open->op_bmval, 0, sizeof(open->op_bmval)); | ||
625 | open->op_iattr.ia_valid = 0; | ||
626 | open->op_stateowner = NULL; | ||
627 | |||
628 | /* seqid, share_access, share_deny, clientid, ownerlen */ | ||
629 | READ_BUF(16 + sizeof(clientid_t)); | ||
630 | READ32(open->op_seqid); | ||
631 | READ32(open->op_share_access); | ||
632 | READ32(open->op_share_deny); | ||
633 | COPYMEM(&open->op_clientid, sizeof(clientid_t)); | ||
634 | READ32(open->op_owner.len); | ||
635 | |||
636 | /* owner, open_flag */ | ||
637 | READ_BUF(open->op_owner.len + 4); | ||
638 | SAVEMEM(open->op_owner.data, open->op_owner.len); | ||
639 | READ32(open->op_create); | ||
640 | switch (open->op_create) { | ||
641 | case NFS4_OPEN_NOCREATE: | ||
642 | break; | ||
643 | case NFS4_OPEN_CREATE: | ||
644 | READ_BUF(4); | ||
645 | READ32(open->op_createmode); | ||
646 | switch (open->op_createmode) { | ||
647 | case NFS4_CREATE_UNCHECKED: | ||
648 | case NFS4_CREATE_GUARDED: | ||
649 | if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl))) | ||
650 | goto out; | ||
651 | break; | ||
652 | case NFS4_CREATE_EXCLUSIVE: | ||
653 | READ_BUF(8); | ||
654 | COPYMEM(open->op_verf.data, 8); | ||
655 | break; | ||
656 | default: | ||
657 | goto xdr_error; | ||
658 | } | ||
659 | break; | ||
660 | default: | ||
661 | goto xdr_error; | ||
662 | } | ||
663 | |||
664 | /* open_claim */ | ||
665 | READ_BUF(4); | ||
666 | READ32(open->op_claim_type); | ||
667 | switch (open->op_claim_type) { | ||
668 | case NFS4_OPEN_CLAIM_NULL: | ||
669 | case NFS4_OPEN_CLAIM_DELEGATE_PREV: | ||
670 | READ_BUF(4); | ||
671 | READ32(open->op_fname.len); | ||
672 | READ_BUF(open->op_fname.len); | ||
673 | SAVEMEM(open->op_fname.data, open->op_fname.len); | ||
674 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | ||
675 | return status; | ||
676 | break; | ||
677 | case NFS4_OPEN_CLAIM_PREVIOUS: | ||
678 | READ_BUF(4); | ||
679 | READ32(open->op_delegate_type); | ||
680 | break; | ||
681 | case NFS4_OPEN_CLAIM_DELEGATE_CUR: | ||
682 | READ_BUF(sizeof(stateid_t) + 4); | ||
683 | COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
684 | READ32(open->op_fname.len); | ||
685 | READ_BUF(open->op_fname.len); | ||
686 | SAVEMEM(open->op_fname.data, open->op_fname.len); | ||
687 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | ||
688 | return status; | ||
689 | break; | ||
690 | default: | ||
691 | goto xdr_error; | ||
692 | } | ||
693 | |||
694 | DECODE_TAIL; | ||
695 | } | ||
696 | |||
697 | static int | ||
698 | nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf) | ||
699 | { | ||
700 | DECODE_HEAD; | ||
701 | |||
702 | open_conf->oc_stateowner = NULL; | ||
703 | READ_BUF(4 + sizeof(stateid_t)); | ||
704 | READ32(open_conf->oc_req_stateid.si_generation); | ||
705 | COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
706 | READ32(open_conf->oc_seqid); | ||
707 | |||
708 | DECODE_TAIL; | ||
709 | } | ||
710 | |||
711 | static int | ||
712 | nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down) | ||
713 | { | ||
714 | DECODE_HEAD; | ||
715 | |||
716 | open_down->od_stateowner = NULL; | ||
717 | READ_BUF(12 + sizeof(stateid_t)); | ||
718 | READ32(open_down->od_stateid.si_generation); | ||
719 | COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
720 | READ32(open_down->od_seqid); | ||
721 | READ32(open_down->od_share_access); | ||
722 | READ32(open_down->od_share_deny); | ||
723 | |||
724 | DECODE_TAIL; | ||
725 | } | ||
726 | |||
727 | static int | ||
728 | nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) | ||
729 | { | ||
730 | DECODE_HEAD; | ||
731 | |||
732 | READ_BUF(4); | ||
733 | READ32(putfh->pf_fhlen); | ||
734 | if (putfh->pf_fhlen > NFS4_FHSIZE) | ||
735 | goto xdr_error; | ||
736 | READ_BUF(putfh->pf_fhlen); | ||
737 | SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen); | ||
738 | |||
739 | DECODE_TAIL; | ||
740 | } | ||
741 | |||
742 | static int | ||
743 | nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) | ||
744 | { | ||
745 | DECODE_HEAD; | ||
746 | |||
747 | READ_BUF(sizeof(stateid_t) + 12); | ||
748 | READ32(read->rd_stateid.si_generation); | ||
749 | COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
750 | READ64(read->rd_offset); | ||
751 | READ32(read->rd_length); | ||
752 | |||
753 | DECODE_TAIL; | ||
754 | } | ||
755 | |||
756 | static int | ||
757 | nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir) | ||
758 | { | ||
759 | DECODE_HEAD; | ||
760 | |||
761 | READ_BUF(24); | ||
762 | READ64(readdir->rd_cookie); | ||
763 | COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data)); | ||
764 | READ32(readdir->rd_dircount); /* just in case you needed a useless field... */ | ||
765 | READ32(readdir->rd_maxcount); | ||
766 | if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval))) | ||
767 | goto out; | ||
768 | |||
769 | DECODE_TAIL; | ||
770 | } | ||
771 | |||
772 | static int | ||
773 | nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove) | ||
774 | { | ||
775 | DECODE_HEAD; | ||
776 | |||
777 | READ_BUF(4); | ||
778 | READ32(remove->rm_namelen); | ||
779 | READ_BUF(remove->rm_namelen); | ||
780 | SAVEMEM(remove->rm_name, remove->rm_namelen); | ||
781 | if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) | ||
782 | return status; | ||
783 | |||
784 | DECODE_TAIL; | ||
785 | } | ||
786 | |||
787 | static int | ||
788 | nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename) | ||
789 | { | ||
790 | DECODE_HEAD; | ||
791 | |||
792 | READ_BUF(4); | ||
793 | READ32(rename->rn_snamelen); | ||
794 | READ_BUF(rename->rn_snamelen + 4); | ||
795 | SAVEMEM(rename->rn_sname, rename->rn_snamelen); | ||
796 | READ32(rename->rn_tnamelen); | ||
797 | READ_BUF(rename->rn_tnamelen); | ||
798 | SAVEMEM(rename->rn_tname, rename->rn_tnamelen); | ||
799 | if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) | ||
800 | return status; | ||
801 | if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) | ||
802 | return status; | ||
803 | |||
804 | DECODE_TAIL; | ||
805 | } | ||
806 | |||
807 | static int | ||
808 | nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) | ||
809 | { | ||
810 | DECODE_HEAD; | ||
811 | |||
812 | READ_BUF(sizeof(clientid_t)); | ||
813 | COPYMEM(clientid, sizeof(clientid_t)); | ||
814 | |||
815 | DECODE_TAIL; | ||
816 | } | ||
817 | |||
818 | static int | ||
819 | nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) | ||
820 | { | ||
821 | DECODE_HEAD; | ||
822 | |||
823 | READ_BUF(sizeof(stateid_t)); | ||
824 | READ32(setattr->sa_stateid.si_generation); | ||
825 | COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
826 | if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl))) | ||
827 | goto out; | ||
828 | |||
829 | DECODE_TAIL; | ||
830 | } | ||
831 | |||
832 | static int | ||
833 | nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid) | ||
834 | { | ||
835 | DECODE_HEAD; | ||
836 | |||
837 | READ_BUF(12); | ||
838 | COPYMEM(setclientid->se_verf.data, 8); | ||
839 | READ32(setclientid->se_namelen); | ||
840 | |||
841 | READ_BUF(setclientid->se_namelen + 8); | ||
842 | SAVEMEM(setclientid->se_name, setclientid->se_namelen); | ||
843 | READ32(setclientid->se_callback_prog); | ||
844 | READ32(setclientid->se_callback_netid_len); | ||
845 | |||
846 | READ_BUF(setclientid->se_callback_netid_len + 4); | ||
847 | SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); | ||
848 | READ32(setclientid->se_callback_addr_len); | ||
849 | |||
850 | READ_BUF(setclientid->se_callback_addr_len + 4); | ||
851 | SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); | ||
852 | READ32(setclientid->se_callback_ident); | ||
853 | |||
854 | DECODE_TAIL; | ||
855 | } | ||
856 | |||
857 | static int | ||
858 | nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c) | ||
859 | { | ||
860 | DECODE_HEAD; | ||
861 | |||
862 | READ_BUF(8 + sizeof(nfs4_verifier)); | ||
863 | COPYMEM(&scd_c->sc_clientid, 8); | ||
864 | COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier)); | ||
865 | |||
866 | DECODE_TAIL; | ||
867 | } | ||
868 | |||
869 | /* Also used for NVERIFY */ | ||
870 | static int | ||
871 | nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) | ||
872 | { | ||
873 | #if 0 | ||
874 | struct nfsd4_compoundargs save = { | ||
875 | .p = argp->p, | ||
876 | .end = argp->end, | ||
877 | .rqstp = argp->rqstp, | ||
878 | }; | ||
879 | u32 ve_bmval[2]; | ||
880 | struct iattr ve_iattr; /* request */ | ||
881 | struct nfs4_acl *ve_acl; /* request */ | ||
882 | #endif | ||
883 | DECODE_HEAD; | ||
884 | |||
885 | if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) | ||
886 | goto out; | ||
887 | |||
888 | /* For convenience's sake, we compare raw xdr'd attributes in | ||
889 | * nfsd4_proc_verify; however we still decode here just to return | ||
890 | * correct error in case of bad xdr. */ | ||
891 | #if 0 | ||
892 | status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl); | ||
893 | if (status == nfserr_inval) { | ||
894 | status = nfserrno(status); | ||
895 | goto out; | ||
896 | } | ||
897 | #endif | ||
898 | READ_BUF(4); | ||
899 | READ32(verify->ve_attrlen); | ||
900 | READ_BUF(verify->ve_attrlen); | ||
901 | SAVEMEM(verify->ve_attrval, verify->ve_attrlen); | ||
902 | |||
903 | DECODE_TAIL; | ||
904 | } | ||
905 | |||
906 | static int | ||
907 | nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) | ||
908 | { | ||
909 | int avail; | ||
910 | int v; | ||
911 | int len; | ||
912 | DECODE_HEAD; | ||
913 | |||
914 | READ_BUF(sizeof(stateid_opaque_t) + 20); | ||
915 | READ32(write->wr_stateid.si_generation); | ||
916 | COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
917 | READ64(write->wr_offset); | ||
918 | READ32(write->wr_stable_how); | ||
919 | if (write->wr_stable_how > 2) | ||
920 | goto xdr_error; | ||
921 | READ32(write->wr_buflen); | ||
922 | |||
923 | /* Sorry .. no magic macros for this.. * | ||
924 | * READ_BUF(write->wr_buflen); | ||
925 | * SAVEMEM(write->wr_buf, write->wr_buflen); | ||
926 | */ | ||
927 | avail = (char*)argp->end - (char*)argp->p; | ||
928 | if (avail + argp->pagelen < write->wr_buflen) { | ||
929 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); | ||
930 | goto xdr_error; | ||
931 | } | ||
932 | write->wr_vec[0].iov_base = p; | ||
933 | write->wr_vec[0].iov_len = avail; | ||
934 | v = 0; | ||
935 | len = write->wr_buflen; | ||
936 | while (len > write->wr_vec[v].iov_len) { | ||
937 | len -= write->wr_vec[v].iov_len; | ||
938 | v++; | ||
939 | write->wr_vec[v].iov_base = page_address(argp->pagelist[0]); | ||
940 | argp->pagelist++; | ||
941 | if (argp->pagelen >= PAGE_SIZE) { | ||
942 | write->wr_vec[v].iov_len = PAGE_SIZE; | ||
943 | argp->pagelen -= PAGE_SIZE; | ||
944 | } else { | ||
945 | write->wr_vec[v].iov_len = argp->pagelen; | ||
946 | argp->pagelen -= len; | ||
947 | } | ||
948 | } | ||
949 | argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len); | ||
950 | argp->p = (u32*) (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); | ||
951 | write->wr_vec[v].iov_len = len; | ||
952 | write->wr_vlen = v+1; | ||
953 | |||
954 | DECODE_TAIL; | ||
955 | } | ||
956 | |||
957 | static int | ||
958 | nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner) | ||
959 | { | ||
960 | DECODE_HEAD; | ||
961 | |||
962 | READ_BUF(12); | ||
963 | COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t)); | ||
964 | READ32(rlockowner->rl_owner.len); | ||
965 | READ_BUF(rlockowner->rl_owner.len); | ||
966 | READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len); | ||
967 | |||
968 | DECODE_TAIL; | ||
969 | } | ||
970 | |||
971 | static int | ||
972 | nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | ||
973 | { | ||
974 | DECODE_HEAD; | ||
975 | struct nfsd4_op *op; | ||
976 | int i; | ||
977 | |||
978 | /* | ||
979 | * XXX: According to spec, we should check the tag | ||
980 | * for UTF-8 compliance. I'm postponing this for | ||
981 | * now because it seems that some clients do use | ||
982 | * binary tags. | ||
983 | */ | ||
984 | READ_BUF(4); | ||
985 | READ32(argp->taglen); | ||
986 | READ_BUF(argp->taglen + 8); | ||
987 | SAVEMEM(argp->tag, argp->taglen); | ||
988 | READ32(argp->minorversion); | ||
989 | READ32(argp->opcnt); | ||
990 | |||
991 | if (argp->taglen > NFSD4_MAX_TAGLEN) | ||
992 | goto xdr_error; | ||
993 | if (argp->opcnt > 100) | ||
994 | goto xdr_error; | ||
995 | |||
996 | if (argp->opcnt > sizeof(argp->iops)/sizeof(argp->iops[0])) { | ||
997 | argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); | ||
998 | if (!argp->ops) { | ||
999 | argp->ops = argp->iops; | ||
1000 | printk(KERN_INFO "nfsd: couldn't allocate room for COMPOUND\n"); | ||
1001 | goto xdr_error; | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | for (i = 0; i < argp->opcnt; i++) { | ||
1006 | op = &argp->ops[i]; | ||
1007 | op->replay = NULL; | ||
1008 | |||
1009 | /* | ||
1010 | * We can't use READ_BUF() here because we need to handle | ||
1011 | * a missing opcode as an OP_WRITE + 1. So we need to check | ||
1012 | * to see if we're truly at the end of our buffer or if there | ||
1013 | * is another page we need to flip to. | ||
1014 | */ | ||
1015 | |||
1016 | if (argp->p == argp->end) { | ||
1017 | if (argp->pagelen < 4) { | ||
1018 | /* There isn't an opcode still on the wire */ | ||
1019 | op->opnum = OP_WRITE + 1; | ||
1020 | op->status = nfserr_bad_xdr; | ||
1021 | argp->opcnt = i+1; | ||
1022 | break; | ||
1023 | } | ||
1024 | |||
1025 | /* | ||
1026 | * False alarm. We just hit a page boundary, but there | ||
1027 | * is still data available. Move pointer across page | ||
1028 | * boundary. *snip from READ_BUF* | ||
1029 | */ | ||
1030 | argp->p = page_address(argp->pagelist[0]); | ||
1031 | argp->pagelist++; | ||
1032 | if (argp->pagelen < PAGE_SIZE) { | ||
1033 | argp->end = p + (argp->pagelen>>2); | ||
1034 | argp->pagelen = 0; | ||
1035 | } else { | ||
1036 | argp->end = p + (PAGE_SIZE>>2); | ||
1037 | argp->pagelen -= PAGE_SIZE; | ||
1038 | } | ||
1039 | } | ||
1040 | op->opnum = ntohl(*argp->p++); | ||
1041 | |||
1042 | switch (op->opnum) { | ||
1043 | case 2: /* Reserved operation */ | ||
1044 | op->opnum = OP_ILLEGAL; | ||
1045 | if (argp->minorversion == 0) | ||
1046 | op->status = nfserr_op_illegal; | ||
1047 | else | ||
1048 | op->status = nfserr_minor_vers_mismatch; | ||
1049 | break; | ||
1050 | case OP_ACCESS: | ||
1051 | op->status = nfsd4_decode_access(argp, &op->u.access); | ||
1052 | break; | ||
1053 | case OP_CLOSE: | ||
1054 | op->status = nfsd4_decode_close(argp, &op->u.close); | ||
1055 | break; | ||
1056 | case OP_COMMIT: | ||
1057 | op->status = nfsd4_decode_commit(argp, &op->u.commit); | ||
1058 | break; | ||
1059 | case OP_CREATE: | ||
1060 | op->status = nfsd4_decode_create(argp, &op->u.create); | ||
1061 | break; | ||
1062 | case OP_DELEGRETURN: | ||
1063 | op->status = nfsd4_decode_delegreturn(argp, &op->u.delegreturn); | ||
1064 | break; | ||
1065 | case OP_GETATTR: | ||
1066 | op->status = nfsd4_decode_getattr(argp, &op->u.getattr); | ||
1067 | break; | ||
1068 | case OP_GETFH: | ||
1069 | op->status = nfs_ok; | ||
1070 | break; | ||
1071 | case OP_LINK: | ||
1072 | op->status = nfsd4_decode_link(argp, &op->u.link); | ||
1073 | break; | ||
1074 | case OP_LOCK: | ||
1075 | op->status = nfsd4_decode_lock(argp, &op->u.lock); | ||
1076 | break; | ||
1077 | case OP_LOCKT: | ||
1078 | op->status = nfsd4_decode_lockt(argp, &op->u.lockt); | ||
1079 | break; | ||
1080 | case OP_LOCKU: | ||
1081 | op->status = nfsd4_decode_locku(argp, &op->u.locku); | ||
1082 | break; | ||
1083 | case OP_LOOKUP: | ||
1084 | op->status = nfsd4_decode_lookup(argp, &op->u.lookup); | ||
1085 | break; | ||
1086 | case OP_LOOKUPP: | ||
1087 | op->status = nfs_ok; | ||
1088 | break; | ||
1089 | case OP_NVERIFY: | ||
1090 | op->status = nfsd4_decode_verify(argp, &op->u.nverify); | ||
1091 | break; | ||
1092 | case OP_OPEN: | ||
1093 | op->status = nfsd4_decode_open(argp, &op->u.open); | ||
1094 | break; | ||
1095 | case OP_OPEN_CONFIRM: | ||
1096 | op->status = nfsd4_decode_open_confirm(argp, &op->u.open_confirm); | ||
1097 | break; | ||
1098 | case OP_OPEN_DOWNGRADE: | ||
1099 | op->status = nfsd4_decode_open_downgrade(argp, &op->u.open_downgrade); | ||
1100 | break; | ||
1101 | case OP_PUTFH: | ||
1102 | op->status = nfsd4_decode_putfh(argp, &op->u.putfh); | ||
1103 | break; | ||
1104 | case OP_PUTROOTFH: | ||
1105 | op->status = nfs_ok; | ||
1106 | break; | ||
1107 | case OP_READ: | ||
1108 | op->status = nfsd4_decode_read(argp, &op->u.read); | ||
1109 | break; | ||
1110 | case OP_READDIR: | ||
1111 | op->status = nfsd4_decode_readdir(argp, &op->u.readdir); | ||
1112 | break; | ||
1113 | case OP_READLINK: | ||
1114 | op->status = nfs_ok; | ||
1115 | break; | ||
1116 | case OP_REMOVE: | ||
1117 | op->status = nfsd4_decode_remove(argp, &op->u.remove); | ||
1118 | break; | ||
1119 | case OP_RENAME: | ||
1120 | op->status = nfsd4_decode_rename(argp, &op->u.rename); | ||
1121 | break; | ||
1122 | case OP_RESTOREFH: | ||
1123 | op->status = nfs_ok; | ||
1124 | break; | ||
1125 | case OP_RENEW: | ||
1126 | op->status = nfsd4_decode_renew(argp, &op->u.renew); | ||
1127 | break; | ||
1128 | case OP_SAVEFH: | ||
1129 | op->status = nfs_ok; | ||
1130 | break; | ||
1131 | case OP_SETATTR: | ||
1132 | op->status = nfsd4_decode_setattr(argp, &op->u.setattr); | ||
1133 | break; | ||
1134 | case OP_SETCLIENTID: | ||
1135 | op->status = nfsd4_decode_setclientid(argp, &op->u.setclientid); | ||
1136 | break; | ||
1137 | case OP_SETCLIENTID_CONFIRM: | ||
1138 | op->status = nfsd4_decode_setclientid_confirm(argp, &op->u.setclientid_confirm); | ||
1139 | break; | ||
1140 | case OP_VERIFY: | ||
1141 | op->status = nfsd4_decode_verify(argp, &op->u.verify); | ||
1142 | break; | ||
1143 | case OP_WRITE: | ||
1144 | op->status = nfsd4_decode_write(argp, &op->u.write); | ||
1145 | break; | ||
1146 | case OP_RELEASE_LOCKOWNER: | ||
1147 | op->status = nfsd4_decode_release_lockowner(argp, &op->u.release_lockowner); | ||
1148 | break; | ||
1149 | default: | ||
1150 | op->opnum = OP_ILLEGAL; | ||
1151 | op->status = nfserr_op_illegal; | ||
1152 | break; | ||
1153 | } | ||
1154 | |||
1155 | if (op->status) { | ||
1156 | argp->opcnt = i+1; | ||
1157 | break; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | DECODE_TAIL; | ||
1162 | } | ||
1163 | /* | ||
1164 | * END OF "GENERIC" DECODE ROUTINES. | ||
1165 | */ | ||
1166 | |||
1167 | /* | ||
1168 | * START OF "GENERIC" ENCODE ROUTINES. | ||
1169 | * These may look a little ugly since they are imported from a "generic" | ||
1170 | * set of XDR encode/decode routines which are intended to be shared by | ||
1171 | * all of our NFSv4 implementations (OpenBSD, MacOS X...). | ||
1172 | * | ||
1173 | * If the pain of reading these is too great, it should be a straightforward | ||
1174 | * task to translate them into Linux-specific versions which are more | ||
1175 | * consistent with the style used in NFSv2/v3... | ||
1176 | */ | ||
1177 | #define ENCODE_HEAD u32 *p | ||
1178 | |||
1179 | #define WRITE32(n) *p++ = htonl(n) | ||
1180 | #define WRITE64(n) do { \ | ||
1181 | *p++ = htonl((u32)((n) >> 32)); \ | ||
1182 | *p++ = htonl((u32)(n)); \ | ||
1183 | } while (0) | ||
1184 | #define WRITEMEM(ptr,nbytes) do { \ | ||
1185 | *(p + XDR_QUADLEN(nbytes) -1) = 0; \ | ||
1186 | memcpy(p, ptr, nbytes); \ | ||
1187 | p += XDR_QUADLEN(nbytes); \ | ||
1188 | } while (0) | ||
1189 | #define WRITECINFO(c) do { \ | ||
1190 | *p++ = htonl(c.atomic); \ | ||
1191 | *p++ = htonl(c.before_ctime_sec); \ | ||
1192 | *p++ = htonl(c.before_ctime_nsec); \ | ||
1193 | *p++ = htonl(c.after_ctime_sec); \ | ||
1194 | *p++ = htonl(c.after_ctime_nsec); \ | ||
1195 | } while (0) | ||
1196 | |||
1197 | #define RESERVE_SPACE(nbytes) do { \ | ||
1198 | p = resp->p; \ | ||
1199 | BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end); \ | ||
1200 | } while (0) | ||
1201 | #define ADJUST_ARGS() resp->p = p | ||
1202 | |||
1203 | /* | ||
1204 | * Header routine to setup seqid operation replay cache | ||
1205 | */ | ||
1206 | #define ENCODE_SEQID_OP_HEAD \ | ||
1207 | u32 *p; \ | ||
1208 | u32 *save; \ | ||
1209 | \ | ||
1210 | save = resp->p; | ||
1211 | |||
1212 | /* | ||
1213 | * Routine for encoding the result of a | ||
1214 | * "seqid-mutating" NFSv4 operation. This is | ||
1215 | * where seqids are incremented, and the | ||
1216 | * replay cache is filled. | ||
1217 | */ | ||
1218 | |||
1219 | #define ENCODE_SEQID_OP_TAIL(stateowner) do { \ | ||
1220 | if (seqid_mutating_err(nfserr) && stateowner) { \ | ||
1221 | if (stateowner->so_confirmed) \ | ||
1222 | stateowner->so_seqid++; \ | ||
1223 | stateowner->so_replay.rp_status = nfserr; \ | ||
1224 | stateowner->so_replay.rp_buflen = \ | ||
1225 | (((char *)(resp)->p - (char *)save)); \ | ||
1226 | memcpy(stateowner->so_replay.rp_buf, save, \ | ||
1227 | stateowner->so_replay.rp_buflen); \ | ||
1228 | } } while (0); | ||
1229 | |||
1230 | |||
1231 | static u32 nfs4_ftypes[16] = { | ||
1232 | NF4BAD, NF4FIFO, NF4CHR, NF4BAD, | ||
1233 | NF4DIR, NF4BAD, NF4BLK, NF4BAD, | ||
1234 | NF4REG, NF4BAD, NF4LNK, NF4BAD, | ||
1235 | NF4SOCK, NF4BAD, NF4LNK, NF4BAD, | ||
1236 | }; | ||
1237 | |||
1238 | static int | ||
1239 | nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, | ||
1240 | u32 **p, int *buflen) | ||
1241 | { | ||
1242 | int status; | ||
1243 | |||
1244 | if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4) | ||
1245 | return nfserr_resource; | ||
1246 | if (whotype != NFS4_ACL_WHO_NAMED) | ||
1247 | status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1)); | ||
1248 | else if (group) | ||
1249 | status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1)); | ||
1250 | else | ||
1251 | status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1)); | ||
1252 | if (status < 0) | ||
1253 | return nfserrno(status); | ||
1254 | *p = xdr_encode_opaque(*p, NULL, status); | ||
1255 | *buflen -= (XDR_QUADLEN(status) << 2) + 4; | ||
1256 | BUG_ON(*buflen < 0); | ||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static inline int | ||
1261 | nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen) | ||
1262 | { | ||
1263 | return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen); | ||
1264 | } | ||
1265 | |||
1266 | static inline int | ||
1267 | nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen) | ||
1268 | { | ||
1269 | return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen); | ||
1270 | } | ||
1271 | |||
1272 | static inline int | ||
1273 | nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, | ||
1274 | u32 **p, int *buflen) | ||
1275 | { | ||
1276 | return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); | ||
1277 | } | ||
1278 | |||
1279 | |||
1280 | /* | ||
1281 | * Note: @fhp can be NULL; in this case, we might have to compose the filehandle | ||
1282 | * ourselves. | ||
1283 | * | ||
1284 | * @countp is the buffer size in _words_; upon successful return this becomes | ||
1285 | * replaced with the number of words written. | ||
1286 | */ | ||
1287 | int | ||
1288 | nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | ||
1289 | struct dentry *dentry, u32 *buffer, int *countp, u32 *bmval, | ||
1290 | struct svc_rqst *rqstp) | ||
1291 | { | ||
1292 | u32 bmval0 = bmval[0]; | ||
1293 | u32 bmval1 = bmval[1]; | ||
1294 | struct kstat stat; | ||
1295 | struct svc_fh tempfh; | ||
1296 | struct kstatfs statfs; | ||
1297 | int buflen = *countp << 2; | ||
1298 | u32 *attrlenp; | ||
1299 | u32 dummy; | ||
1300 | u64 dummy64; | ||
1301 | u32 *p = buffer; | ||
1302 | int status; | ||
1303 | int aclsupport = 0; | ||
1304 | struct nfs4_acl *acl = NULL; | ||
1305 | |||
1306 | BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); | ||
1307 | BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0); | ||
1308 | BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1); | ||
1309 | |||
1310 | status = vfs_getattr(exp->ex_mnt, dentry, &stat); | ||
1311 | if (status) | ||
1312 | goto out_nfserr; | ||
1313 | if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) || | ||
1314 | (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | | ||
1315 | FATTR4_WORD1_SPACE_TOTAL))) { | ||
1316 | status = vfs_statfs(dentry->d_inode->i_sb, &statfs); | ||
1317 | if (status) | ||
1318 | goto out_nfserr; | ||
1319 | } | ||
1320 | if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) { | ||
1321 | fh_init(&tempfh, NFS4_FHSIZE); | ||
1322 | status = fh_compose(&tempfh, exp, dentry, NULL); | ||
1323 | if (status) | ||
1324 | goto out; | ||
1325 | fhp = &tempfh; | ||
1326 | } | ||
1327 | if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT | ||
1328 | | FATTR4_WORD0_SUPPORTED_ATTRS)) { | ||
1329 | status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); | ||
1330 | aclsupport = (status == 0); | ||
1331 | if (bmval0 & FATTR4_WORD0_ACL) { | ||
1332 | if (status == -EOPNOTSUPP) | ||
1333 | bmval0 &= ~FATTR4_WORD0_ACL; | ||
1334 | else if (status == -EINVAL) { | ||
1335 | status = nfserr_attrnotsupp; | ||
1336 | goto out; | ||
1337 | } else if (status != 0) | ||
1338 | goto out_nfserr; | ||
1339 | } | ||
1340 | } | ||
1341 | if ((buflen -= 16) < 0) | ||
1342 | goto out_resource; | ||
1343 | |||
1344 | WRITE32(2); | ||
1345 | WRITE32(bmval0); | ||
1346 | WRITE32(bmval1); | ||
1347 | attrlenp = p++; /* to be backfilled later */ | ||
1348 | |||
1349 | if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { | ||
1350 | if ((buflen -= 12) < 0) | ||
1351 | goto out_resource; | ||
1352 | WRITE32(2); | ||
1353 | WRITE32(aclsupport ? | ||
1354 | NFSD_SUPPORTED_ATTRS_WORD0 : | ||
1355 | NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL); | ||
1356 | WRITE32(NFSD_SUPPORTED_ATTRS_WORD1); | ||
1357 | } | ||
1358 | if (bmval0 & FATTR4_WORD0_TYPE) { | ||
1359 | if ((buflen -= 4) < 0) | ||
1360 | goto out_resource; | ||
1361 | dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12]; | ||
1362 | if (dummy == NF4BAD) | ||
1363 | goto out_serverfault; | ||
1364 | WRITE32(dummy); | ||
1365 | } | ||
1366 | if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { | ||
1367 | if ((buflen -= 4) < 0) | ||
1368 | goto out_resource; | ||
1369 | WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME ); | ||
1370 | } | ||
1371 | if (bmval0 & FATTR4_WORD0_CHANGE) { | ||
1372 | /* | ||
1373 | * Note: This _must_ be consistent with the scheme for writing | ||
1374 | * change_info, so any changes made here must be reflected there | ||
1375 | * as well. (See xdr4.h:set_change_info() and the WRITECINFO() | ||
1376 | * macro above.) | ||
1377 | */ | ||
1378 | if ((buflen -= 8) < 0) | ||
1379 | goto out_resource; | ||
1380 | WRITE32(stat.ctime.tv_sec); | ||
1381 | WRITE32(stat.ctime.tv_nsec); | ||
1382 | } | ||
1383 | if (bmval0 & FATTR4_WORD0_SIZE) { | ||
1384 | if ((buflen -= 8) < 0) | ||
1385 | goto out_resource; | ||
1386 | WRITE64(stat.size); | ||
1387 | } | ||
1388 | if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { | ||
1389 | if ((buflen -= 4) < 0) | ||
1390 | goto out_resource; | ||
1391 | WRITE32(1); | ||
1392 | } | ||
1393 | if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { | ||
1394 | if ((buflen -= 4) < 0) | ||
1395 | goto out_resource; | ||
1396 | WRITE32(1); | ||
1397 | } | ||
1398 | if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { | ||
1399 | if ((buflen -= 4) < 0) | ||
1400 | goto out_resource; | ||
1401 | WRITE32(0); | ||
1402 | } | ||
1403 | if (bmval0 & FATTR4_WORD0_FSID) { | ||
1404 | if ((buflen -= 16) < 0) | ||
1405 | goto out_resource; | ||
1406 | if (is_fsid(fhp, rqstp->rq_reffh)) { | ||
1407 | WRITE64((u64)exp->ex_fsid); | ||
1408 | WRITE64((u64)0); | ||
1409 | } else { | ||
1410 | WRITE32(0); | ||
1411 | WRITE32(MAJOR(stat.dev)); | ||
1412 | WRITE32(0); | ||
1413 | WRITE32(MINOR(stat.dev)); | ||
1414 | } | ||
1415 | } | ||
1416 | if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { | ||
1417 | if ((buflen -= 4) < 0) | ||
1418 | goto out_resource; | ||
1419 | WRITE32(0); | ||
1420 | } | ||
1421 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { | ||
1422 | if ((buflen -= 4) < 0) | ||
1423 | goto out_resource; | ||
1424 | WRITE32(NFSD_LEASE_TIME); | ||
1425 | } | ||
1426 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { | ||
1427 | if ((buflen -= 4) < 0) | ||
1428 | goto out_resource; | ||
1429 | WRITE32(0); | ||
1430 | } | ||
1431 | if (bmval0 & FATTR4_WORD0_ACL) { | ||
1432 | struct nfs4_ace *ace; | ||
1433 | struct list_head *h; | ||
1434 | |||
1435 | if (acl == NULL) { | ||
1436 | if ((buflen -= 4) < 0) | ||
1437 | goto out_resource; | ||
1438 | |||
1439 | WRITE32(0); | ||
1440 | goto out_acl; | ||
1441 | } | ||
1442 | if ((buflen -= 4) < 0) | ||
1443 | goto out_resource; | ||
1444 | WRITE32(acl->naces); | ||
1445 | |||
1446 | list_for_each(h, &acl->ace_head) { | ||
1447 | ace = list_entry(h, struct nfs4_ace, l_ace); | ||
1448 | |||
1449 | if ((buflen -= 4*3) < 0) | ||
1450 | goto out_resource; | ||
1451 | WRITE32(ace->type); | ||
1452 | WRITE32(ace->flag); | ||
1453 | WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); | ||
1454 | status = nfsd4_encode_aclname(rqstp, ace->whotype, | ||
1455 | ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP, | ||
1456 | &p, &buflen); | ||
1457 | if (status == nfserr_resource) | ||
1458 | goto out_resource; | ||
1459 | if (status) | ||
1460 | goto out; | ||
1461 | } | ||
1462 | } | ||
1463 | out_acl: | ||
1464 | if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { | ||
1465 | if ((buflen -= 4) < 0) | ||
1466 | goto out_resource; | ||
1467 | WRITE32(aclsupport ? | ||
1468 | ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); | ||
1469 | } | ||
1470 | if (bmval0 & FATTR4_WORD0_CANSETTIME) { | ||
1471 | if ((buflen -= 4) < 0) | ||
1472 | goto out_resource; | ||
1473 | WRITE32(1); | ||
1474 | } | ||
1475 | if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { | ||
1476 | if ((buflen -= 4) < 0) | ||
1477 | goto out_resource; | ||
1478 | WRITE32(1); | ||
1479 | } | ||
1480 | if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { | ||
1481 | if ((buflen -= 4) < 0) | ||
1482 | goto out_resource; | ||
1483 | WRITE32(1); | ||
1484 | } | ||
1485 | if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { | ||
1486 | if ((buflen -= 4) < 0) | ||
1487 | goto out_resource; | ||
1488 | WRITE32(1); | ||
1489 | } | ||
1490 | if (bmval0 & FATTR4_WORD0_FILEHANDLE) { | ||
1491 | buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4; | ||
1492 | if (buflen < 0) | ||
1493 | goto out_resource; | ||
1494 | WRITE32(fhp->fh_handle.fh_size); | ||
1495 | WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size); | ||
1496 | } | ||
1497 | if (bmval0 & FATTR4_WORD0_FILEID) { | ||
1498 | if ((buflen -= 8) < 0) | ||
1499 | goto out_resource; | ||
1500 | WRITE64((u64) stat.ino); | ||
1501 | } | ||
1502 | if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { | ||
1503 | if ((buflen -= 8) < 0) | ||
1504 | goto out_resource; | ||
1505 | WRITE64((u64) statfs.f_ffree); | ||
1506 | } | ||
1507 | if (bmval0 & FATTR4_WORD0_FILES_FREE) { | ||
1508 | if ((buflen -= 8) < 0) | ||
1509 | goto out_resource; | ||
1510 | WRITE64((u64) statfs.f_ffree); | ||
1511 | } | ||
1512 | if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { | ||
1513 | if ((buflen -= 8) < 0) | ||
1514 | goto out_resource; | ||
1515 | WRITE64((u64) statfs.f_files); | ||
1516 | } | ||
1517 | if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { | ||
1518 | if ((buflen -= 4) < 0) | ||
1519 | goto out_resource; | ||
1520 | WRITE32(1); | ||
1521 | } | ||
1522 | if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { | ||
1523 | if ((buflen -= 8) < 0) | ||
1524 | goto out_resource; | ||
1525 | WRITE64(~(u64)0); | ||
1526 | } | ||
1527 | if (bmval0 & FATTR4_WORD0_MAXLINK) { | ||
1528 | if ((buflen -= 4) < 0) | ||
1529 | goto out_resource; | ||
1530 | WRITE32(255); | ||
1531 | } | ||
1532 | if (bmval0 & FATTR4_WORD0_MAXNAME) { | ||
1533 | if ((buflen -= 4) < 0) | ||
1534 | goto out_resource; | ||
1535 | WRITE32(~(u32) 0); | ||
1536 | } | ||
1537 | if (bmval0 & FATTR4_WORD0_MAXREAD) { | ||
1538 | if ((buflen -= 8) < 0) | ||
1539 | goto out_resource; | ||
1540 | WRITE64((u64) NFSSVC_MAXBLKSIZE); | ||
1541 | } | ||
1542 | if (bmval0 & FATTR4_WORD0_MAXWRITE) { | ||
1543 | if ((buflen -= 8) < 0) | ||
1544 | goto out_resource; | ||
1545 | WRITE64((u64) NFSSVC_MAXBLKSIZE); | ||
1546 | } | ||
1547 | if (bmval1 & FATTR4_WORD1_MODE) { | ||
1548 | if ((buflen -= 4) < 0) | ||
1549 | goto out_resource; | ||
1550 | WRITE32(stat.mode & S_IALLUGO); | ||
1551 | } | ||
1552 | if (bmval1 & FATTR4_WORD1_NO_TRUNC) { | ||
1553 | if ((buflen -= 4) < 0) | ||
1554 | goto out_resource; | ||
1555 | WRITE32(1); | ||
1556 | } | ||
1557 | if (bmval1 & FATTR4_WORD1_NUMLINKS) { | ||
1558 | if ((buflen -= 4) < 0) | ||
1559 | goto out_resource; | ||
1560 | WRITE32(stat.nlink); | ||
1561 | } | ||
1562 | if (bmval1 & FATTR4_WORD1_OWNER) { | ||
1563 | status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen); | ||
1564 | if (status == nfserr_resource) | ||
1565 | goto out_resource; | ||
1566 | if (status) | ||
1567 | goto out; | ||
1568 | } | ||
1569 | if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { | ||
1570 | status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen); | ||
1571 | if (status == nfserr_resource) | ||
1572 | goto out_resource; | ||
1573 | if (status) | ||
1574 | goto out; | ||
1575 | } | ||
1576 | if (bmval1 & FATTR4_WORD1_RAWDEV) { | ||
1577 | if ((buflen -= 8) < 0) | ||
1578 | goto out_resource; | ||
1579 | WRITE32((u32) MAJOR(stat.rdev)); | ||
1580 | WRITE32((u32) MINOR(stat.rdev)); | ||
1581 | } | ||
1582 | if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { | ||
1583 | if ((buflen -= 8) < 0) | ||
1584 | goto out_resource; | ||
1585 | dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; | ||
1586 | WRITE64(dummy64); | ||
1587 | } | ||
1588 | if (bmval1 & FATTR4_WORD1_SPACE_FREE) { | ||
1589 | if ((buflen -= 8) < 0) | ||
1590 | goto out_resource; | ||
1591 | dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; | ||
1592 | WRITE64(dummy64); | ||
1593 | } | ||
1594 | if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { | ||
1595 | if ((buflen -= 8) < 0) | ||
1596 | goto out_resource; | ||
1597 | dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; | ||
1598 | WRITE64(dummy64); | ||
1599 | } | ||
1600 | if (bmval1 & FATTR4_WORD1_SPACE_USED) { | ||
1601 | if ((buflen -= 8) < 0) | ||
1602 | goto out_resource; | ||
1603 | dummy64 = (u64)stat.blocks << 9; | ||
1604 | WRITE64(dummy64); | ||
1605 | } | ||
1606 | if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { | ||
1607 | if ((buflen -= 12) < 0) | ||
1608 | goto out_resource; | ||
1609 | WRITE32(0); | ||
1610 | WRITE32(stat.atime.tv_sec); | ||
1611 | WRITE32(stat.atime.tv_nsec); | ||
1612 | } | ||
1613 | if (bmval1 & FATTR4_WORD1_TIME_DELTA) { | ||
1614 | if ((buflen -= 12) < 0) | ||
1615 | goto out_resource; | ||
1616 | WRITE32(0); | ||
1617 | WRITE32(1); | ||
1618 | WRITE32(0); | ||
1619 | } | ||
1620 | if (bmval1 & FATTR4_WORD1_TIME_METADATA) { | ||
1621 | if ((buflen -= 12) < 0) | ||
1622 | goto out_resource; | ||
1623 | WRITE32(0); | ||
1624 | WRITE32(stat.ctime.tv_sec); | ||
1625 | WRITE32(stat.ctime.tv_nsec); | ||
1626 | } | ||
1627 | if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { | ||
1628 | if ((buflen -= 12) < 0) | ||
1629 | goto out_resource; | ||
1630 | WRITE32(0); | ||
1631 | WRITE32(stat.mtime.tv_sec); | ||
1632 | WRITE32(stat.mtime.tv_nsec); | ||
1633 | } | ||
1634 | if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { | ||
1635 | struct dentry *mnt_pnt, *mnt_root; | ||
1636 | |||
1637 | if ((buflen -= 8) < 0) | ||
1638 | goto out_resource; | ||
1639 | mnt_root = exp->ex_mnt->mnt_root; | ||
1640 | if (mnt_root->d_inode == dentry->d_inode) { | ||
1641 | mnt_pnt = exp->ex_mnt->mnt_mountpoint; | ||
1642 | WRITE64((u64) mnt_pnt->d_inode->i_ino); | ||
1643 | } else | ||
1644 | WRITE64((u64) stat.ino); | ||
1645 | } | ||
1646 | *attrlenp = htonl((char *)p - (char *)attrlenp - 4); | ||
1647 | *countp = p - buffer; | ||
1648 | status = nfs_ok; | ||
1649 | |||
1650 | out: | ||
1651 | nfs4_acl_free(acl); | ||
1652 | if (fhp == &tempfh) | ||
1653 | fh_put(&tempfh); | ||
1654 | return status; | ||
1655 | out_nfserr: | ||
1656 | status = nfserrno(status); | ||
1657 | goto out; | ||
1658 | out_resource: | ||
1659 | *countp = 0; | ||
1660 | status = nfserr_resource; | ||
1661 | goto out; | ||
1662 | out_serverfault: | ||
1663 | status = nfserr_serverfault; | ||
1664 | goto out; | ||
1665 | } | ||
1666 | |||
1667 | static int | ||
1668 | nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, | ||
1669 | const char *name, int namlen, u32 *p, int *buflen) | ||
1670 | { | ||
1671 | struct svc_export *exp = cd->rd_fhp->fh_export; | ||
1672 | struct dentry *dentry; | ||
1673 | int nfserr; | ||
1674 | |||
1675 | dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); | ||
1676 | if (IS_ERR(dentry)) | ||
1677 | return nfserrno(PTR_ERR(dentry)); | ||
1678 | |||
1679 | exp_get(exp); | ||
1680 | if (d_mountpoint(dentry)) { | ||
1681 | if (nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp)) { | ||
1682 | /* | ||
1683 | * -EAGAIN is the only error returned from | ||
1684 | * nfsd_cross_mnt() and it indicates that an | ||
1685 | * up-call has been initiated to fill in the export | ||
1686 | * options on exp. When the answer comes back, | ||
1687 | * this call will be retried. | ||
1688 | */ | ||
1689 | nfserr = nfserr_dropit; | ||
1690 | goto out_put; | ||
1691 | } | ||
1692 | |||
1693 | } | ||
1694 | nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, | ||
1695 | cd->rd_rqstp); | ||
1696 | out_put: | ||
1697 | dput(dentry); | ||
1698 | exp_put(exp); | ||
1699 | return nfserr; | ||
1700 | } | ||
1701 | |||
1702 | static u32 * | ||
1703 | nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr) | ||
1704 | { | ||
1705 | u32 *attrlenp; | ||
1706 | |||
1707 | if (buflen < 6) | ||
1708 | return NULL; | ||
1709 | *p++ = htonl(2); | ||
1710 | *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ | ||
1711 | *p++ = htonl(0); /* bmval1 */ | ||
1712 | |||
1713 | attrlenp = p++; | ||
1714 | *p++ = nfserr; /* no htonl */ | ||
1715 | *attrlenp = htonl((char *)p - (char *)attrlenp - 4); | ||
1716 | return p; | ||
1717 | } | ||
1718 | |||
1719 | static int | ||
1720 | nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen, | ||
1721 | loff_t offset, ino_t ino, unsigned int d_type) | ||
1722 | { | ||
1723 | struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); | ||
1724 | int buflen; | ||
1725 | u32 *p = cd->buffer; | ||
1726 | int nfserr = nfserr_toosmall; | ||
1727 | |||
1728 | /* In nfsv4, "." and ".." never make it onto the wire.. */ | ||
1729 | if (name && isdotent(name, namlen)) { | ||
1730 | cd->common.err = nfs_ok; | ||
1731 | return 0; | ||
1732 | } | ||
1733 | |||
1734 | if (cd->offset) | ||
1735 | xdr_encode_hyper(cd->offset, (u64) offset); | ||
1736 | |||
1737 | buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); | ||
1738 | if (buflen < 0) | ||
1739 | goto fail; | ||
1740 | |||
1741 | *p++ = xdr_one; /* mark entry present */ | ||
1742 | cd->offset = p; /* remember pointer */ | ||
1743 | p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ | ||
1744 | p = xdr_encode_array(p, name, namlen); /* name length & name */ | ||
1745 | |||
1746 | nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen); | ||
1747 | switch (nfserr) { | ||
1748 | case nfs_ok: | ||
1749 | p += buflen; | ||
1750 | break; | ||
1751 | case nfserr_resource: | ||
1752 | nfserr = nfserr_toosmall; | ||
1753 | goto fail; | ||
1754 | case nfserr_dropit: | ||
1755 | goto fail; | ||
1756 | default: | ||
1757 | /* | ||
1758 | * If the client requested the RDATTR_ERROR attribute, | ||
1759 | * we stuff the error code into this attribute | ||
1760 | * and continue. If this attribute was not requested, | ||
1761 | * then in accordance with the spec, we fail the | ||
1762 | * entire READDIR operation(!) | ||
1763 | */ | ||
1764 | if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) | ||
1765 | goto fail; | ||
1766 | nfserr = nfserr_toosmall; | ||
1767 | p = nfsd4_encode_rdattr_error(p, buflen, nfserr); | ||
1768 | if (p == NULL) | ||
1769 | goto fail; | ||
1770 | } | ||
1771 | cd->buflen -= (p - cd->buffer); | ||
1772 | cd->buffer = p; | ||
1773 | cd->common.err = nfs_ok; | ||
1774 | return 0; | ||
1775 | fail: | ||
1776 | cd->common.err = nfserr; | ||
1777 | return -EINVAL; | ||
1778 | } | ||
1779 | |||
1780 | static void | ||
1781 | nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_access *access) | ||
1782 | { | ||
1783 | ENCODE_HEAD; | ||
1784 | |||
1785 | if (!nfserr) { | ||
1786 | RESERVE_SPACE(8); | ||
1787 | WRITE32(access->ac_supported); | ||
1788 | WRITE32(access->ac_resp_access); | ||
1789 | ADJUST_ARGS(); | ||
1790 | } | ||
1791 | } | ||
1792 | |||
1793 | static void | ||
1794 | nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_close *close) | ||
1795 | { | ||
1796 | ENCODE_SEQID_OP_HEAD; | ||
1797 | |||
1798 | if (!nfserr) { | ||
1799 | RESERVE_SPACE(sizeof(stateid_t)); | ||
1800 | WRITE32(close->cl_stateid.si_generation); | ||
1801 | WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1802 | ADJUST_ARGS(); | ||
1803 | } | ||
1804 | ENCODE_SEQID_OP_TAIL(close->cl_stateowner); | ||
1805 | } | ||
1806 | |||
1807 | |||
1808 | static void | ||
1809 | nfsd4_encode_commit(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_commit *commit) | ||
1810 | { | ||
1811 | ENCODE_HEAD; | ||
1812 | |||
1813 | if (!nfserr) { | ||
1814 | RESERVE_SPACE(8); | ||
1815 | WRITEMEM(commit->co_verf.data, 8); | ||
1816 | ADJUST_ARGS(); | ||
1817 | } | ||
1818 | } | ||
1819 | |||
1820 | static void | ||
1821 | nfsd4_encode_create(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_create *create) | ||
1822 | { | ||
1823 | ENCODE_HEAD; | ||
1824 | |||
1825 | if (!nfserr) { | ||
1826 | RESERVE_SPACE(32); | ||
1827 | WRITECINFO(create->cr_cinfo); | ||
1828 | WRITE32(2); | ||
1829 | WRITE32(create->cr_bmval[0]); | ||
1830 | WRITE32(create->cr_bmval[1]); | ||
1831 | ADJUST_ARGS(); | ||
1832 | } | ||
1833 | } | ||
1834 | |||
1835 | static int | ||
1836 | nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_getattr *getattr) | ||
1837 | { | ||
1838 | struct svc_fh *fhp = getattr->ga_fhp; | ||
1839 | int buflen; | ||
1840 | |||
1841 | if (nfserr) | ||
1842 | return nfserr; | ||
1843 | |||
1844 | buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); | ||
1845 | nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, | ||
1846 | resp->p, &buflen, getattr->ga_bmval, | ||
1847 | resp->rqstp); | ||
1848 | |||
1849 | if (!nfserr) | ||
1850 | resp->p += buflen; | ||
1851 | return nfserr; | ||
1852 | } | ||
1853 | |||
1854 | static void | ||
1855 | nfsd4_encode_getfh(struct nfsd4_compoundres *resp, int nfserr, struct svc_fh *fhp) | ||
1856 | { | ||
1857 | unsigned int len; | ||
1858 | ENCODE_HEAD; | ||
1859 | |||
1860 | if (!nfserr) { | ||
1861 | len = fhp->fh_handle.fh_size; | ||
1862 | RESERVE_SPACE(len + 4); | ||
1863 | WRITE32(len); | ||
1864 | WRITEMEM(&fhp->fh_handle.fh_base, len); | ||
1865 | ADJUST_ARGS(); | ||
1866 | } | ||
1867 | } | ||
1868 | |||
1869 | /* | ||
1870 | * Including all fields other than the name, a LOCK4denied structure requires | ||
1871 | * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. | ||
1872 | */ | ||
1873 | static void | ||
1874 | nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld) | ||
1875 | { | ||
1876 | ENCODE_HEAD; | ||
1877 | |||
1878 | RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0)); | ||
1879 | WRITE64(ld->ld_start); | ||
1880 | WRITE64(ld->ld_length); | ||
1881 | WRITE32(ld->ld_type); | ||
1882 | if (ld->ld_sop) { | ||
1883 | WRITEMEM(&ld->ld_clientid, 8); | ||
1884 | WRITE32(ld->ld_sop->so_owner.len); | ||
1885 | WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len); | ||
1886 | kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner); | ||
1887 | } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ | ||
1888 | WRITE64((u64)0); /* clientid */ | ||
1889 | WRITE32(0); /* length of owner name */ | ||
1890 | } | ||
1891 | ADJUST_ARGS(); | ||
1892 | } | ||
1893 | |||
1894 | static void | ||
1895 | nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock) | ||
1896 | { | ||
1897 | |||
1898 | ENCODE_SEQID_OP_HEAD; | ||
1899 | |||
1900 | if (!nfserr) { | ||
1901 | RESERVE_SPACE(4 + sizeof(stateid_t)); | ||
1902 | WRITE32(lock->lk_resp_stateid.si_generation); | ||
1903 | WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1904 | ADJUST_ARGS(); | ||
1905 | } else if (nfserr == nfserr_denied) | ||
1906 | nfsd4_encode_lock_denied(resp, &lock->lk_denied); | ||
1907 | |||
1908 | ENCODE_SEQID_OP_TAIL(lock->lk_stateowner); | ||
1909 | } | ||
1910 | |||
1911 | static void | ||
1912 | nfsd4_encode_lockt(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lockt *lockt) | ||
1913 | { | ||
1914 | if (nfserr == nfserr_denied) | ||
1915 | nfsd4_encode_lock_denied(resp, &lockt->lt_denied); | ||
1916 | } | ||
1917 | |||
1918 | static void | ||
1919 | nfsd4_encode_locku(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_locku *locku) | ||
1920 | { | ||
1921 | ENCODE_SEQID_OP_HEAD; | ||
1922 | |||
1923 | if (!nfserr) { | ||
1924 | RESERVE_SPACE(sizeof(stateid_t)); | ||
1925 | WRITE32(locku->lu_stateid.si_generation); | ||
1926 | WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1927 | ADJUST_ARGS(); | ||
1928 | } | ||
1929 | |||
1930 | ENCODE_SEQID_OP_TAIL(locku->lu_stateowner); | ||
1931 | } | ||
1932 | |||
1933 | |||
1934 | static void | ||
1935 | nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link *link) | ||
1936 | { | ||
1937 | ENCODE_HEAD; | ||
1938 | |||
1939 | if (!nfserr) { | ||
1940 | RESERVE_SPACE(20); | ||
1941 | WRITECINFO(link->li_cinfo); | ||
1942 | ADJUST_ARGS(); | ||
1943 | } | ||
1944 | } | ||
1945 | |||
1946 | |||
1947 | static void | ||
1948 | nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open *open) | ||
1949 | { | ||
1950 | ENCODE_SEQID_OP_HEAD; | ||
1951 | |||
1952 | if (nfserr) | ||
1953 | goto out; | ||
1954 | |||
1955 | RESERVE_SPACE(36 + sizeof(stateid_t)); | ||
1956 | WRITE32(open->op_stateid.si_generation); | ||
1957 | WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1958 | WRITECINFO(open->op_cinfo); | ||
1959 | WRITE32(open->op_rflags); | ||
1960 | WRITE32(2); | ||
1961 | WRITE32(open->op_bmval[0]); | ||
1962 | WRITE32(open->op_bmval[1]); | ||
1963 | WRITE32(open->op_delegate_type); | ||
1964 | ADJUST_ARGS(); | ||
1965 | |||
1966 | switch (open->op_delegate_type) { | ||
1967 | case NFS4_OPEN_DELEGATE_NONE: | ||
1968 | break; | ||
1969 | case NFS4_OPEN_DELEGATE_READ: | ||
1970 | RESERVE_SPACE(20 + sizeof(stateid_t)); | ||
1971 | WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
1972 | WRITE32(0); | ||
1973 | |||
1974 | /* | ||
1975 | * TODO: ACE's in delegations | ||
1976 | */ | ||
1977 | WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); | ||
1978 | WRITE32(0); | ||
1979 | WRITE32(0); | ||
1980 | WRITE32(0); /* XXX: is NULL principal ok? */ | ||
1981 | ADJUST_ARGS(); | ||
1982 | break; | ||
1983 | case NFS4_OPEN_DELEGATE_WRITE: | ||
1984 | RESERVE_SPACE(32 + sizeof(stateid_t)); | ||
1985 | WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
1986 | WRITE32(0); | ||
1987 | |||
1988 | /* | ||
1989 | * TODO: space_limit's in delegations | ||
1990 | */ | ||
1991 | WRITE32(NFS4_LIMIT_SIZE); | ||
1992 | WRITE32(~(u32)0); | ||
1993 | WRITE32(~(u32)0); | ||
1994 | |||
1995 | /* | ||
1996 | * TODO: ACE's in delegations | ||
1997 | */ | ||
1998 | WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); | ||
1999 | WRITE32(0); | ||
2000 | WRITE32(0); | ||
2001 | WRITE32(0); /* XXX: is NULL principal ok? */ | ||
2002 | ADJUST_ARGS(); | ||
2003 | break; | ||
2004 | default: | ||
2005 | BUG(); | ||
2006 | } | ||
2007 | /* XXX save filehandle here */ | ||
2008 | out: | ||
2009 | ENCODE_SEQID_OP_TAIL(open->op_stateowner); | ||
2010 | } | ||
2011 | |||
2012 | static void | ||
2013 | nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_confirm *oc) | ||
2014 | { | ||
2015 | ENCODE_SEQID_OP_HEAD; | ||
2016 | |||
2017 | if (!nfserr) { | ||
2018 | RESERVE_SPACE(sizeof(stateid_t)); | ||
2019 | WRITE32(oc->oc_resp_stateid.si_generation); | ||
2020 | WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
2021 | ADJUST_ARGS(); | ||
2022 | } | ||
2023 | |||
2024 | ENCODE_SEQID_OP_TAIL(oc->oc_stateowner); | ||
2025 | } | ||
2026 | |||
2027 | static void | ||
2028 | nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_downgrade *od) | ||
2029 | { | ||
2030 | ENCODE_SEQID_OP_HEAD; | ||
2031 | |||
2032 | if (!nfserr) { | ||
2033 | RESERVE_SPACE(sizeof(stateid_t)); | ||
2034 | WRITE32(od->od_stateid.si_generation); | ||
2035 | WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
2036 | ADJUST_ARGS(); | ||
2037 | } | ||
2038 | |||
2039 | ENCODE_SEQID_OP_TAIL(od->od_stateowner); | ||
2040 | } | ||
2041 | |||
2042 | static int | ||
2043 | nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) | ||
2044 | { | ||
2045 | u32 eof; | ||
2046 | int v, pn; | ||
2047 | unsigned long maxcount; | ||
2048 | long len; | ||
2049 | ENCODE_HEAD; | ||
2050 | |||
2051 | if (nfserr) | ||
2052 | return nfserr; | ||
2053 | if (resp->xbuf->page_len) | ||
2054 | return nfserr_resource; | ||
2055 | |||
2056 | RESERVE_SPACE(8); /* eof flag and byte count */ | ||
2057 | |||
2058 | maxcount = NFSSVC_MAXBLKSIZE; | ||
2059 | if (maxcount > read->rd_length) | ||
2060 | maxcount = read->rd_length; | ||
2061 | |||
2062 | len = maxcount; | ||
2063 | v = 0; | ||
2064 | while (len > 0) { | ||
2065 | pn = resp->rqstp->rq_resused; | ||
2066 | svc_take_page(resp->rqstp); | ||
2067 | read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]); | ||
2068 | read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; | ||
2069 | v++; | ||
2070 | len -= PAGE_SIZE; | ||
2071 | } | ||
2072 | read->rd_vlen = v; | ||
2073 | |||
2074 | nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp, | ||
2075 | read->rd_offset, read->rd_iov, read->rd_vlen, | ||
2076 | &maxcount); | ||
2077 | |||
2078 | if (nfserr == nfserr_symlink) | ||
2079 | nfserr = nfserr_inval; | ||
2080 | if (nfserr) | ||
2081 | return nfserr; | ||
2082 | eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); | ||
2083 | |||
2084 | WRITE32(eof); | ||
2085 | WRITE32(maxcount); | ||
2086 | ADJUST_ARGS(); | ||
2087 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2088 | |||
2089 | resp->xbuf->page_len = maxcount; | ||
2090 | |||
2091 | /* read zero bytes -> don't set up tail */ | ||
2092 | if(!maxcount) | ||
2093 | return 0; | ||
2094 | |||
2095 | /* set up page for remaining responses */ | ||
2096 | svc_take_page(resp->rqstp); | ||
2097 | resp->xbuf->tail[0].iov_base = | ||
2098 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2099 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2100 | resp->xbuf->tail[0].iov_len = 0; | ||
2101 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2102 | resp->end = resp->p + PAGE_SIZE/4; | ||
2103 | |||
2104 | if (maxcount&3) { | ||
2105 | *(resp->p)++ = 0; | ||
2106 | resp->xbuf->tail[0].iov_base += maxcount&3; | ||
2107 | resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); | ||
2108 | } | ||
2109 | return 0; | ||
2110 | } | ||
2111 | |||
2112 | static int | ||
2113 | nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink) | ||
2114 | { | ||
2115 | int maxcount; | ||
2116 | char *page; | ||
2117 | ENCODE_HEAD; | ||
2118 | |||
2119 | if (nfserr) | ||
2120 | return nfserr; | ||
2121 | if (resp->xbuf->page_len) | ||
2122 | return nfserr_resource; | ||
2123 | |||
2124 | svc_take_page(resp->rqstp); | ||
2125 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2126 | |||
2127 | maxcount = PAGE_SIZE; | ||
2128 | RESERVE_SPACE(4); | ||
2129 | |||
2130 | /* | ||
2131 | * XXX: By default, the ->readlink() VFS op will truncate symlinks | ||
2132 | * if they would overflow the buffer. Is this kosher in NFSv4? If | ||
2133 | * not, one easy fix is: if ->readlink() precisely fills the buffer, | ||
2134 | * assume that truncation occurred, and return NFS4ERR_RESOURCE. | ||
2135 | */ | ||
2136 | nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); | ||
2137 | if (nfserr == nfserr_isdir) | ||
2138 | return nfserr_inval; | ||
2139 | if (nfserr) | ||
2140 | return nfserr; | ||
2141 | |||
2142 | WRITE32(maxcount); | ||
2143 | ADJUST_ARGS(); | ||
2144 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2145 | |||
2146 | svc_take_page(resp->rqstp); | ||
2147 | resp->xbuf->tail[0].iov_base = | ||
2148 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2149 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2150 | resp->xbuf->tail[0].iov_len = 0; | ||
2151 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2152 | resp->end = resp->p + PAGE_SIZE/4; | ||
2153 | |||
2154 | resp->xbuf->page_len = maxcount; | ||
2155 | if (maxcount&3) { | ||
2156 | *(resp->p)++ = 0; | ||
2157 | resp->xbuf->tail[0].iov_base += maxcount&3; | ||
2158 | resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); | ||
2159 | } | ||
2160 | return 0; | ||
2161 | } | ||
2162 | |||
2163 | static int | ||
2164 | nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readdir *readdir) | ||
2165 | { | ||
2166 | int maxcount; | ||
2167 | loff_t offset; | ||
2168 | u32 *page, *savep; | ||
2169 | ENCODE_HEAD; | ||
2170 | |||
2171 | if (nfserr) | ||
2172 | return nfserr; | ||
2173 | if (resp->xbuf->page_len) | ||
2174 | return nfserr_resource; | ||
2175 | |||
2176 | RESERVE_SPACE(8); /* verifier */ | ||
2177 | savep = p; | ||
2178 | |||
2179 | /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ | ||
2180 | WRITE32(0); | ||
2181 | WRITE32(0); | ||
2182 | ADJUST_ARGS(); | ||
2183 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2184 | |||
2185 | maxcount = PAGE_SIZE; | ||
2186 | if (maxcount > readdir->rd_maxcount) | ||
2187 | maxcount = readdir->rd_maxcount; | ||
2188 | |||
2189 | /* | ||
2190 | * Convert from bytes to words, account for the two words already | ||
2191 | * written, make sure to leave two words at the end for the next | ||
2192 | * pointer and eof field. | ||
2193 | */ | ||
2194 | maxcount = (maxcount >> 2) - 4; | ||
2195 | if (maxcount < 0) { | ||
2196 | nfserr = nfserr_toosmall; | ||
2197 | goto err_no_verf; | ||
2198 | } | ||
2199 | |||
2200 | svc_take_page(resp->rqstp); | ||
2201 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2202 | readdir->common.err = 0; | ||
2203 | readdir->buflen = maxcount; | ||
2204 | readdir->buffer = page; | ||
2205 | readdir->offset = NULL; | ||
2206 | |||
2207 | offset = readdir->rd_cookie; | ||
2208 | nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, | ||
2209 | &offset, | ||
2210 | &readdir->common, nfsd4_encode_dirent); | ||
2211 | if (nfserr == nfs_ok && | ||
2212 | readdir->common.err == nfserr_toosmall && | ||
2213 | readdir->buffer == page) | ||
2214 | nfserr = nfserr_toosmall; | ||
2215 | if (nfserr == nfserr_symlink) | ||
2216 | nfserr = nfserr_notdir; | ||
2217 | if (nfserr) | ||
2218 | goto err_no_verf; | ||
2219 | |||
2220 | if (readdir->offset) | ||
2221 | xdr_encode_hyper(readdir->offset, offset); | ||
2222 | |||
2223 | p = readdir->buffer; | ||
2224 | *p++ = 0; /* no more entries */ | ||
2225 | *p++ = htonl(readdir->common.err == nfserr_eof); | ||
2226 | resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2227 | |||
2228 | /* allocate a page for the tail */ | ||
2229 | svc_take_page(resp->rqstp); | ||
2230 | resp->xbuf->tail[0].iov_base = | ||
2231 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2232 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2233 | resp->xbuf->tail[0].iov_len = 0; | ||
2234 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2235 | resp->end = resp->p + PAGE_SIZE/4; | ||
2236 | |||
2237 | return 0; | ||
2238 | err_no_verf: | ||
2239 | p = savep; | ||
2240 | ADJUST_ARGS(); | ||
2241 | return nfserr; | ||
2242 | } | ||
2243 | |||
2244 | static void | ||
2245 | nfsd4_encode_remove(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_remove *remove) | ||
2246 | { | ||
2247 | ENCODE_HEAD; | ||
2248 | |||
2249 | if (!nfserr) { | ||
2250 | RESERVE_SPACE(20); | ||
2251 | WRITECINFO(remove->rm_cinfo); | ||
2252 | ADJUST_ARGS(); | ||
2253 | } | ||
2254 | } | ||
2255 | |||
2256 | static void | ||
2257 | nfsd4_encode_rename(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_rename *rename) | ||
2258 | { | ||
2259 | ENCODE_HEAD; | ||
2260 | |||
2261 | if (!nfserr) { | ||
2262 | RESERVE_SPACE(40); | ||
2263 | WRITECINFO(rename->rn_sinfo); | ||
2264 | WRITECINFO(rename->rn_tinfo); | ||
2265 | ADJUST_ARGS(); | ||
2266 | } | ||
2267 | } | ||
2268 | |||
2269 | /* | ||
2270 | * The SETATTR encode routine is special -- it always encodes a bitmap, | ||
2271 | * regardless of the error status. | ||
2272 | */ | ||
2273 | static void | ||
2274 | nfsd4_encode_setattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setattr *setattr) | ||
2275 | { | ||
2276 | ENCODE_HEAD; | ||
2277 | |||
2278 | RESERVE_SPACE(12); | ||
2279 | if (nfserr) { | ||
2280 | WRITE32(2); | ||
2281 | WRITE32(0); | ||
2282 | WRITE32(0); | ||
2283 | } | ||
2284 | else { | ||
2285 | WRITE32(2); | ||
2286 | WRITE32(setattr->sa_bmval[0]); | ||
2287 | WRITE32(setattr->sa_bmval[1]); | ||
2288 | } | ||
2289 | ADJUST_ARGS(); | ||
2290 | } | ||
2291 | |||
2292 | static void | ||
2293 | nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setclientid *scd) | ||
2294 | { | ||
2295 | ENCODE_HEAD; | ||
2296 | |||
2297 | if (!nfserr) { | ||
2298 | RESERVE_SPACE(8 + sizeof(nfs4_verifier)); | ||
2299 | WRITEMEM(&scd->se_clientid, 8); | ||
2300 | WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier)); | ||
2301 | ADJUST_ARGS(); | ||
2302 | } | ||
2303 | else if (nfserr == nfserr_clid_inuse) { | ||
2304 | RESERVE_SPACE(8); | ||
2305 | WRITE32(0); | ||
2306 | WRITE32(0); | ||
2307 | ADJUST_ARGS(); | ||
2308 | } | ||
2309 | } | ||
2310 | |||
2311 | static void | ||
2312 | nfsd4_encode_write(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_write *write) | ||
2313 | { | ||
2314 | ENCODE_HEAD; | ||
2315 | |||
2316 | if (!nfserr) { | ||
2317 | RESERVE_SPACE(16); | ||
2318 | WRITE32(write->wr_bytes_written); | ||
2319 | WRITE32(write->wr_how_written); | ||
2320 | WRITEMEM(write->wr_verifier.data, 8); | ||
2321 | ADJUST_ARGS(); | ||
2322 | } | ||
2323 | } | ||
2324 | |||
2325 | void | ||
2326 | nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | ||
2327 | { | ||
2328 | u32 *statp; | ||
2329 | ENCODE_HEAD; | ||
2330 | |||
2331 | RESERVE_SPACE(8); | ||
2332 | WRITE32(op->opnum); | ||
2333 | statp = p++; /* to be backfilled at the end */ | ||
2334 | ADJUST_ARGS(); | ||
2335 | |||
2336 | switch (op->opnum) { | ||
2337 | case OP_ACCESS: | ||
2338 | nfsd4_encode_access(resp, op->status, &op->u.access); | ||
2339 | break; | ||
2340 | case OP_CLOSE: | ||
2341 | nfsd4_encode_close(resp, op->status, &op->u.close); | ||
2342 | break; | ||
2343 | case OP_COMMIT: | ||
2344 | nfsd4_encode_commit(resp, op->status, &op->u.commit); | ||
2345 | break; | ||
2346 | case OP_CREATE: | ||
2347 | nfsd4_encode_create(resp, op->status, &op->u.create); | ||
2348 | break; | ||
2349 | case OP_DELEGRETURN: | ||
2350 | break; | ||
2351 | case OP_GETATTR: | ||
2352 | op->status = nfsd4_encode_getattr(resp, op->status, &op->u.getattr); | ||
2353 | break; | ||
2354 | case OP_GETFH: | ||
2355 | nfsd4_encode_getfh(resp, op->status, op->u.getfh); | ||
2356 | break; | ||
2357 | case OP_LINK: | ||
2358 | nfsd4_encode_link(resp, op->status, &op->u.link); | ||
2359 | break; | ||
2360 | case OP_LOCK: | ||
2361 | nfsd4_encode_lock(resp, op->status, &op->u.lock); | ||
2362 | break; | ||
2363 | case OP_LOCKT: | ||
2364 | nfsd4_encode_lockt(resp, op->status, &op->u.lockt); | ||
2365 | break; | ||
2366 | case OP_LOCKU: | ||
2367 | nfsd4_encode_locku(resp, op->status, &op->u.locku); | ||
2368 | break; | ||
2369 | case OP_LOOKUP: | ||
2370 | break; | ||
2371 | case OP_LOOKUPP: | ||
2372 | break; | ||
2373 | case OP_NVERIFY: | ||
2374 | break; | ||
2375 | case OP_OPEN: | ||
2376 | nfsd4_encode_open(resp, op->status, &op->u.open); | ||
2377 | break; | ||
2378 | case OP_OPEN_CONFIRM: | ||
2379 | nfsd4_encode_open_confirm(resp, op->status, &op->u.open_confirm); | ||
2380 | break; | ||
2381 | case OP_OPEN_DOWNGRADE: | ||
2382 | nfsd4_encode_open_downgrade(resp, op->status, &op->u.open_downgrade); | ||
2383 | break; | ||
2384 | case OP_PUTFH: | ||
2385 | break; | ||
2386 | case OP_PUTROOTFH: | ||
2387 | break; | ||
2388 | case OP_READ: | ||
2389 | op->status = nfsd4_encode_read(resp, op->status, &op->u.read); | ||
2390 | break; | ||
2391 | case OP_READDIR: | ||
2392 | op->status = nfsd4_encode_readdir(resp, op->status, &op->u.readdir); | ||
2393 | break; | ||
2394 | case OP_READLINK: | ||
2395 | op->status = nfsd4_encode_readlink(resp, op->status, &op->u.readlink); | ||
2396 | break; | ||
2397 | case OP_REMOVE: | ||
2398 | nfsd4_encode_remove(resp, op->status, &op->u.remove); | ||
2399 | break; | ||
2400 | case OP_RENAME: | ||
2401 | nfsd4_encode_rename(resp, op->status, &op->u.rename); | ||
2402 | break; | ||
2403 | case OP_RENEW: | ||
2404 | break; | ||
2405 | case OP_RESTOREFH: | ||
2406 | break; | ||
2407 | case OP_SAVEFH: | ||
2408 | break; | ||
2409 | case OP_SETATTR: | ||
2410 | nfsd4_encode_setattr(resp, op->status, &op->u.setattr); | ||
2411 | break; | ||
2412 | case OP_SETCLIENTID: | ||
2413 | nfsd4_encode_setclientid(resp, op->status, &op->u.setclientid); | ||
2414 | break; | ||
2415 | case OP_SETCLIENTID_CONFIRM: | ||
2416 | break; | ||
2417 | case OP_VERIFY: | ||
2418 | break; | ||
2419 | case OP_WRITE: | ||
2420 | nfsd4_encode_write(resp, op->status, &op->u.write); | ||
2421 | break; | ||
2422 | case OP_RELEASE_LOCKOWNER: | ||
2423 | break; | ||
2424 | default: | ||
2425 | break; | ||
2426 | } | ||
2427 | |||
2428 | /* | ||
2429 | * Note: We write the status directly, instead of using WRITE32(), | ||
2430 | * since it is already in network byte order. | ||
2431 | */ | ||
2432 | *statp = op->status; | ||
2433 | } | ||
2434 | |||
2435 | /* | ||
2436 | * Encode the reply stored in the stateowner reply cache | ||
2437 | * | ||
2438 | * XDR note: do not encode rp->rp_buflen: the buffer contains the | ||
2439 | * previously sent already encoded operation. | ||
2440 | * | ||
2441 | * called with nfs4_lock_state() held | ||
2442 | */ | ||
2443 | void | ||
2444 | nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | ||
2445 | { | ||
2446 | ENCODE_HEAD; | ||
2447 | struct nfs4_replay *rp = op->replay; | ||
2448 | |||
2449 | BUG_ON(!rp); | ||
2450 | |||
2451 | RESERVE_SPACE(8); | ||
2452 | WRITE32(op->opnum); | ||
2453 | *p++ = rp->rp_status; /* already xdr'ed */ | ||
2454 | ADJUST_ARGS(); | ||
2455 | |||
2456 | RESERVE_SPACE(rp->rp_buflen); | ||
2457 | WRITEMEM(rp->rp_buf, rp->rp_buflen); | ||
2458 | ADJUST_ARGS(); | ||
2459 | } | ||
2460 | |||
2461 | /* | ||
2462 | * END OF "GENERIC" ENCODE ROUTINES. | ||
2463 | */ | ||
2464 | |||
2465 | int | ||
2466 | nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy) | ||
2467 | { | ||
2468 | return xdr_ressize_check(rqstp, p); | ||
2469 | } | ||
2470 | |||
2471 | void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args) | ||
2472 | { | ||
2473 | if (args->ops != args->iops) { | ||
2474 | kfree(args->ops); | ||
2475 | args->ops = args->iops; | ||
2476 | } | ||
2477 | if (args->tmpp) { | ||
2478 | kfree(args->tmpp); | ||
2479 | args->tmpp = NULL; | ||
2480 | } | ||
2481 | while (args->to_free) { | ||
2482 | struct tmpbuf *tb = args->to_free; | ||
2483 | args->to_free = tb->next; | ||
2484 | tb->release(tb->buf); | ||
2485 | kfree(tb); | ||
2486 | } | ||
2487 | } | ||
2488 | |||
2489 | int | ||
2490 | nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args) | ||
2491 | { | ||
2492 | int status; | ||
2493 | |||
2494 | args->p = p; | ||
2495 | args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; | ||
2496 | args->pagelist = rqstp->rq_arg.pages; | ||
2497 | args->pagelen = rqstp->rq_arg.page_len; | ||
2498 | args->tmpp = NULL; | ||
2499 | args->to_free = NULL; | ||
2500 | args->ops = args->iops; | ||
2501 | args->rqstp = rqstp; | ||
2502 | |||
2503 | status = nfsd4_decode_compound(args); | ||
2504 | if (status) { | ||
2505 | nfsd4_release_compoundargs(args); | ||
2506 | } | ||
2507 | return !status; | ||
2508 | } | ||
2509 | |||
2510 | int | ||
2511 | nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundres *resp) | ||
2512 | { | ||
2513 | /* | ||
2514 | * All that remains is to write the tag and operation count... | ||
2515 | */ | ||
2516 | struct kvec *iov; | ||
2517 | p = resp->tagp; | ||
2518 | *p++ = htonl(resp->taglen); | ||
2519 | memcpy(p, resp->tag, resp->taglen); | ||
2520 | p += XDR_QUADLEN(resp->taglen); | ||
2521 | *p++ = htonl(resp->opcnt); | ||
2522 | |||
2523 | if (rqstp->rq_res.page_len) | ||
2524 | iov = &rqstp->rq_res.tail[0]; | ||
2525 | else | ||
2526 | iov = &rqstp->rq_res.head[0]; | ||
2527 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; | ||
2528 | BUG_ON(iov->iov_len > PAGE_SIZE); | ||
2529 | return 1; | ||
2530 | } | ||
2531 | |||
2532 | /* | ||
2533 | * Local variables: | ||
2534 | * c-basic-offset: 8 | ||
2535 | * End: | ||
2536 | */ | ||
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c new file mode 100644 index 000000000000..119e4d4495b8 --- /dev/null +++ b/fs/nfsd/nfscache.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfscache.c | ||
3 | * | ||
4 | * Request reply cache. This is currently a global cache, but this may | ||
5 | * change in the future and be a per-client cache. | ||
6 | * | ||
7 | * This code is heavily inspired by the 44BSD implementation, although | ||
8 | * it does things a bit differently. | ||
9 | * | ||
10 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/time.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/list.h> | ||
19 | |||
20 | #include <linux/sunrpc/svc.h> | ||
21 | #include <linux/nfsd/nfsd.h> | ||
22 | #include <linux/nfsd/cache.h> | ||
23 | |||
24 | /* Size of reply cache. Common values are: | ||
25 | * 4.3BSD: 128 | ||
26 | * 4.4BSD: 256 | ||
27 | * Solaris2: 1024 | ||
28 | * DEC Unix: 512-4096 | ||
29 | */ | ||
30 | #define CACHESIZE 1024 | ||
31 | #define HASHSIZE 64 | ||
32 | #define REQHASH(xid) ((((xid) >> 24) ^ (xid)) & (HASHSIZE-1)) | ||
33 | |||
34 | static struct hlist_head * hash_list; | ||
35 | static struct list_head lru_head; | ||
36 | static int cache_disabled = 1; | ||
37 | |||
38 | static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec); | ||
39 | |||
40 | /* | ||
41 | * locking for the reply cache: | ||
42 | * A cache entry is "single use" if c_state == RC_INPROG | ||
43 | * Otherwise, it when accessing _prev or _next, the lock must be held. | ||
44 | */ | ||
45 | static DEFINE_SPINLOCK(cache_lock); | ||
46 | |||
47 | void | ||
48 | nfsd_cache_init(void) | ||
49 | { | ||
50 | struct svc_cacherep *rp; | ||
51 | int i; | ||
52 | |||
53 | INIT_LIST_HEAD(&lru_head); | ||
54 | i = CACHESIZE; | ||
55 | while(i) { | ||
56 | rp = kmalloc(sizeof(*rp), GFP_KERNEL); | ||
57 | if (!rp) break; | ||
58 | list_add(&rp->c_lru, &lru_head); | ||
59 | rp->c_state = RC_UNUSED; | ||
60 | rp->c_type = RC_NOCACHE; | ||
61 | INIT_HLIST_NODE(&rp->c_hash); | ||
62 | i--; | ||
63 | } | ||
64 | |||
65 | if (i) | ||
66 | printk (KERN_ERR "nfsd: cannot allocate all %d cache entries, only got %d\n", | ||
67 | CACHESIZE, CACHESIZE-i); | ||
68 | |||
69 | hash_list = kmalloc (HASHSIZE * sizeof(struct hlist_head), GFP_KERNEL); | ||
70 | if (!hash_list) { | ||
71 | nfsd_cache_shutdown(); | ||
72 | printk (KERN_ERR "nfsd: cannot allocate %Zd bytes for hash list\n", | ||
73 | HASHSIZE * sizeof(struct hlist_head)); | ||
74 | return; | ||
75 | } | ||
76 | memset(hash_list, 0, HASHSIZE * sizeof(struct hlist_head)); | ||
77 | |||
78 | cache_disabled = 0; | ||
79 | } | ||
80 | |||
81 | void | ||
82 | nfsd_cache_shutdown(void) | ||
83 | { | ||
84 | struct svc_cacherep *rp; | ||
85 | |||
86 | while (!list_empty(&lru_head)) { | ||
87 | rp = list_entry(lru_head.next, struct svc_cacherep, c_lru); | ||
88 | if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF) | ||
89 | kfree(rp->c_replvec.iov_base); | ||
90 | list_del(&rp->c_lru); | ||
91 | kfree(rp); | ||
92 | } | ||
93 | |||
94 | cache_disabled = 1; | ||
95 | |||
96 | if (hash_list) | ||
97 | kfree (hash_list); | ||
98 | hash_list = NULL; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Move cache entry to end of LRU list | ||
103 | */ | ||
104 | static void | ||
105 | lru_put_end(struct svc_cacherep *rp) | ||
106 | { | ||
107 | list_del(&rp->c_lru); | ||
108 | list_add_tail(&rp->c_lru, &lru_head); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Move a cache entry from one hash list to another | ||
113 | */ | ||
114 | static void | ||
115 | hash_refile(struct svc_cacherep *rp) | ||
116 | { | ||
117 | hlist_del_init(&rp->c_hash); | ||
118 | hlist_add_head(&rp->c_hash, hash_list + REQHASH(rp->c_xid)); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Try to find an entry matching the current call in the cache. When none | ||
123 | * is found, we grab the oldest unlocked entry off the LRU list. | ||
124 | * Note that no operation within the loop may sleep. | ||
125 | */ | ||
126 | int | ||
127 | nfsd_cache_lookup(struct svc_rqst *rqstp, int type) | ||
128 | { | ||
129 | struct hlist_node *hn; | ||
130 | struct hlist_head *rh; | ||
131 | struct svc_cacherep *rp; | ||
132 | u32 xid = rqstp->rq_xid, | ||
133 | proto = rqstp->rq_prot, | ||
134 | vers = rqstp->rq_vers, | ||
135 | proc = rqstp->rq_proc; | ||
136 | unsigned long age; | ||
137 | int rtn; | ||
138 | |||
139 | rqstp->rq_cacherep = NULL; | ||
140 | if (cache_disabled || type == RC_NOCACHE) { | ||
141 | nfsdstats.rcnocache++; | ||
142 | return RC_DOIT; | ||
143 | } | ||
144 | |||
145 | spin_lock(&cache_lock); | ||
146 | rtn = RC_DOIT; | ||
147 | |||
148 | rh = &hash_list[REQHASH(xid)]; | ||
149 | hlist_for_each_entry(rp, hn, rh, c_hash) { | ||
150 | if (rp->c_state != RC_UNUSED && | ||
151 | xid == rp->c_xid && proc == rp->c_proc && | ||
152 | proto == rp->c_prot && vers == rp->c_vers && | ||
153 | time_before(jiffies, rp->c_timestamp + 120*HZ) && | ||
154 | memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, sizeof(rp->c_addr))==0) { | ||
155 | nfsdstats.rchits++; | ||
156 | goto found_entry; | ||
157 | } | ||
158 | } | ||
159 | nfsdstats.rcmisses++; | ||
160 | |||
161 | /* This loop shouldn't take more than a few iterations normally */ | ||
162 | { | ||
163 | int safe = 0; | ||
164 | list_for_each_entry(rp, &lru_head, c_lru) { | ||
165 | if (rp->c_state != RC_INPROG) | ||
166 | break; | ||
167 | if (safe++ > CACHESIZE) { | ||
168 | printk("nfsd: loop in repcache LRU list\n"); | ||
169 | cache_disabled = 1; | ||
170 | goto out; | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /* This should not happen */ | ||
176 | if (rp == NULL) { | ||
177 | static int complaints; | ||
178 | |||
179 | printk(KERN_WARNING "nfsd: all repcache entries locked!\n"); | ||
180 | if (++complaints > 5) { | ||
181 | printk(KERN_WARNING "nfsd: disabling repcache.\n"); | ||
182 | cache_disabled = 1; | ||
183 | } | ||
184 | goto out; | ||
185 | } | ||
186 | |||
187 | rqstp->rq_cacherep = rp; | ||
188 | rp->c_state = RC_INPROG; | ||
189 | rp->c_xid = xid; | ||
190 | rp->c_proc = proc; | ||
191 | rp->c_addr = rqstp->rq_addr; | ||
192 | rp->c_prot = proto; | ||
193 | rp->c_vers = vers; | ||
194 | rp->c_timestamp = jiffies; | ||
195 | |||
196 | hash_refile(rp); | ||
197 | |||
198 | /* release any buffer */ | ||
199 | if (rp->c_type == RC_REPLBUFF) { | ||
200 | kfree(rp->c_replvec.iov_base); | ||
201 | rp->c_replvec.iov_base = NULL; | ||
202 | } | ||
203 | rp->c_type = RC_NOCACHE; | ||
204 | out: | ||
205 | spin_unlock(&cache_lock); | ||
206 | return rtn; | ||
207 | |||
208 | found_entry: | ||
209 | /* We found a matching entry which is either in progress or done. */ | ||
210 | age = jiffies - rp->c_timestamp; | ||
211 | rp->c_timestamp = jiffies; | ||
212 | lru_put_end(rp); | ||
213 | |||
214 | rtn = RC_DROPIT; | ||
215 | /* Request being processed or excessive rexmits */ | ||
216 | if (rp->c_state == RC_INPROG || age < RC_DELAY) | ||
217 | goto out; | ||
218 | |||
219 | /* From the hall of fame of impractical attacks: | ||
220 | * Is this a user who tries to snoop on the cache? */ | ||
221 | rtn = RC_DOIT; | ||
222 | if (!rqstp->rq_secure && rp->c_secure) | ||
223 | goto out; | ||
224 | |||
225 | /* Compose RPC reply header */ | ||
226 | switch (rp->c_type) { | ||
227 | case RC_NOCACHE: | ||
228 | break; | ||
229 | case RC_REPLSTAT: | ||
230 | svc_putu32(&rqstp->rq_res.head[0], rp->c_replstat); | ||
231 | rtn = RC_REPLY; | ||
232 | break; | ||
233 | case RC_REPLBUFF: | ||
234 | if (!nfsd_cache_append(rqstp, &rp->c_replvec)) | ||
235 | goto out; /* should not happen */ | ||
236 | rtn = RC_REPLY; | ||
237 | break; | ||
238 | default: | ||
239 | printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type); | ||
240 | rp->c_state = RC_UNUSED; | ||
241 | } | ||
242 | |||
243 | goto out; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Update a cache entry. This is called from nfsd_dispatch when | ||
248 | * the procedure has been executed and the complete reply is in | ||
249 | * rqstp->rq_res. | ||
250 | * | ||
251 | * We're copying around data here rather than swapping buffers because | ||
252 | * the toplevel loop requires max-sized buffers, which would be a waste | ||
253 | * of memory for a cache with a max reply size of 100 bytes (diropokres). | ||
254 | * | ||
255 | * If we should start to use different types of cache entries tailored | ||
256 | * specifically for attrstat and fh's, we may save even more space. | ||
257 | * | ||
258 | * Also note that a cachetype of RC_NOCACHE can legally be passed when | ||
259 | * nfsd failed to encode a reply that otherwise would have been cached. | ||
260 | * In this case, nfsd_cache_update is called with statp == NULL. | ||
261 | */ | ||
262 | void | ||
263 | nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp) | ||
264 | { | ||
265 | struct svc_cacherep *rp; | ||
266 | struct kvec *resv = &rqstp->rq_res.head[0], *cachv; | ||
267 | int len; | ||
268 | |||
269 | if (!(rp = rqstp->rq_cacherep) || cache_disabled) | ||
270 | return; | ||
271 | |||
272 | len = resv->iov_len - ((char*)statp - (char*)resv->iov_base); | ||
273 | len >>= 2; | ||
274 | |||
275 | /* Don't cache excessive amounts of data and XDR failures */ | ||
276 | if (!statp || len > (256 >> 2)) { | ||
277 | rp->c_state = RC_UNUSED; | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | switch (cachetype) { | ||
282 | case RC_REPLSTAT: | ||
283 | if (len != 1) | ||
284 | printk("nfsd: RC_REPLSTAT/reply len %d!\n",len); | ||
285 | rp->c_replstat = *statp; | ||
286 | break; | ||
287 | case RC_REPLBUFF: | ||
288 | cachv = &rp->c_replvec; | ||
289 | cachv->iov_base = kmalloc(len << 2, GFP_KERNEL); | ||
290 | if (!cachv->iov_base) { | ||
291 | spin_lock(&cache_lock); | ||
292 | rp->c_state = RC_UNUSED; | ||
293 | spin_unlock(&cache_lock); | ||
294 | return; | ||
295 | } | ||
296 | cachv->iov_len = len << 2; | ||
297 | memcpy(cachv->iov_base, statp, len << 2); | ||
298 | break; | ||
299 | } | ||
300 | spin_lock(&cache_lock); | ||
301 | lru_put_end(rp); | ||
302 | rp->c_secure = rqstp->rq_secure; | ||
303 | rp->c_type = cachetype; | ||
304 | rp->c_state = RC_DONE; | ||
305 | rp->c_timestamp = jiffies; | ||
306 | spin_unlock(&cache_lock); | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Copy cached reply to current reply buffer. Should always fit. | ||
312 | * FIXME as reply is in a page, we should just attach the page, and | ||
313 | * keep a refcount.... | ||
314 | */ | ||
315 | static int | ||
316 | nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data) | ||
317 | { | ||
318 | struct kvec *vec = &rqstp->rq_res.head[0]; | ||
319 | |||
320 | if (vec->iov_len + data->iov_len > PAGE_SIZE) { | ||
321 | printk(KERN_WARNING "nfsd: cached reply too large (%Zd).\n", | ||
322 | data->iov_len); | ||
323 | return 0; | ||
324 | } | ||
325 | memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len); | ||
326 | vec->iov_len += data->iov_len; | ||
327 | return 1; | ||
328 | } | ||
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) | ||
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c new file mode 100644 index 000000000000..7a3e397b4ed3 --- /dev/null +++ b/fs/nfsd/nfsfh.c | |||
@@ -0,0 +1,532 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfsfh.c | ||
3 | * | ||
4 | * NFS server file handle treatment. | ||
5 | * | ||
6 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | ||
7 | * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org> | ||
8 | * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999 | ||
9 | * ... and again Southern-Winter 2001 to support export_operations | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/smp_lock.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/unistd.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/stat.h> | ||
19 | #include <linux/dcache.h> | ||
20 | #include <linux/mount.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | |||
23 | #include <linux/sunrpc/svc.h> | ||
24 | #include <linux/nfsd/nfsd.h> | ||
25 | |||
26 | #define NFSDDBG_FACILITY NFSDDBG_FH | ||
27 | #define NFSD_PARANOIA 1 | ||
28 | /* #define NFSD_DEBUG_VERBOSE 1 */ | ||
29 | |||
30 | |||
31 | static int nfsd_nr_verified; | ||
32 | static int nfsd_nr_put; | ||
33 | |||
34 | extern struct export_operations export_op_default; | ||
35 | |||
36 | #define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) | ||
37 | |||
38 | /* | ||
39 | * our acceptability function. | ||
40 | * if NOSUBTREECHECK, accept anything | ||
41 | * if not, require that we can walk up to exp->ex_dentry | ||
42 | * doing some checks on the 'x' bits | ||
43 | */ | ||
44 | static int nfsd_acceptable(void *expv, struct dentry *dentry) | ||
45 | { | ||
46 | struct svc_export *exp = expv; | ||
47 | int rv; | ||
48 | struct dentry *tdentry; | ||
49 | struct dentry *parent; | ||
50 | |||
51 | if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) | ||
52 | return 1; | ||
53 | |||
54 | tdentry = dget(dentry); | ||
55 | while (tdentry != exp->ex_dentry && ! IS_ROOT(tdentry)) { | ||
56 | /* make sure parents give x permission to user */ | ||
57 | int err; | ||
58 | parent = dget_parent(tdentry); | ||
59 | err = permission(parent->d_inode, MAY_EXEC, NULL); | ||
60 | if (err < 0) { | ||
61 | dput(parent); | ||
62 | break; | ||
63 | } | ||
64 | dput(tdentry); | ||
65 | tdentry = parent; | ||
66 | } | ||
67 | if (tdentry != exp->ex_dentry) | ||
68 | dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name); | ||
69 | rv = (tdentry == exp->ex_dentry); | ||
70 | dput(tdentry); | ||
71 | return rv; | ||
72 | } | ||
73 | |||
74 | /* Type check. The correct error return for type mismatches does not seem to be | ||
75 | * generally agreed upon. SunOS seems to use EISDIR if file isn't S_IFREG; a | ||
76 | * comment in the NFSv3 spec says this is incorrect (implementation notes for | ||
77 | * the write call). | ||
78 | */ | ||
79 | static inline int | ||
80 | nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) | ||
81 | { | ||
82 | /* Type can be negative when creating hardlinks - not to a dir */ | ||
83 | if (type > 0 && (mode & S_IFMT) != type) { | ||
84 | if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) | ||
85 | return nfserr_symlink; | ||
86 | else if (type == S_IFDIR) | ||
87 | return nfserr_notdir; | ||
88 | else if ((mode & S_IFMT) == S_IFDIR) | ||
89 | return nfserr_isdir; | ||
90 | else | ||
91 | return nfserr_inval; | ||
92 | } | ||
93 | if (type < 0 && (mode & S_IFMT) == -type) { | ||
94 | if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) | ||
95 | return nfserr_symlink; | ||
96 | else if (type == -S_IFDIR) | ||
97 | return nfserr_isdir; | ||
98 | else | ||
99 | return nfserr_notdir; | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Perform sanity checks on the dentry in a client's file handle. | ||
106 | * | ||
107 | * Note that the file handle dentry may need to be freed even after | ||
108 | * an error return. | ||
109 | * | ||
110 | * This is only called at the start of an nfsproc call, so fhp points to | ||
111 | * a svc_fh which is all 0 except for the over-the-wire file handle. | ||
112 | */ | ||
113 | u32 | ||
114 | fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) | ||
115 | { | ||
116 | struct knfsd_fh *fh = &fhp->fh_handle; | ||
117 | struct svc_export *exp = NULL; | ||
118 | struct dentry *dentry; | ||
119 | u32 error = 0; | ||
120 | |||
121 | dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); | ||
122 | |||
123 | /* keep this filehandle for possible reference when encoding attributes */ | ||
124 | rqstp->rq_reffh = fh; | ||
125 | |||
126 | if (!fhp->fh_dentry) { | ||
127 | __u32 *datap=NULL; | ||
128 | __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ | ||
129 | int fileid_type; | ||
130 | int data_left = fh->fh_size/4; | ||
131 | |||
132 | error = nfserr_stale; | ||
133 | if (rqstp->rq_client == NULL) | ||
134 | goto out; | ||
135 | if (rqstp->rq_vers > 2) | ||
136 | error = nfserr_badhandle; | ||
137 | if (rqstp->rq_vers == 4 && fh->fh_size == 0) | ||
138 | return nfserr_nofilehandle; | ||
139 | |||
140 | if (fh->fh_version == 1) { | ||
141 | int len; | ||
142 | datap = fh->fh_auth; | ||
143 | if (--data_left<0) goto out; | ||
144 | switch (fh->fh_auth_type) { | ||
145 | case 0: break; | ||
146 | default: goto out; | ||
147 | } | ||
148 | len = key_len(fh->fh_fsid_type) / 4; | ||
149 | if (len == 0) goto out; | ||
150 | if (fh->fh_fsid_type == 2) { | ||
151 | /* deprecated, convert to type 3 */ | ||
152 | len = 3; | ||
153 | fh->fh_fsid_type = 3; | ||
154 | fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); | ||
155 | fh->fh_fsid[1] = fh->fh_fsid[2]; | ||
156 | } | ||
157 | if ((data_left -= len)<0) goto out; | ||
158 | exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); | ||
159 | datap += len; | ||
160 | } else { | ||
161 | dev_t xdev; | ||
162 | ino_t xino; | ||
163 | if (fh->fh_size != NFS_FHSIZE) | ||
164 | goto out; | ||
165 | /* assume old filehandle format */ | ||
166 | xdev = old_decode_dev(fh->ofh_xdev); | ||
167 | xino = u32_to_ino_t(fh->ofh_xino); | ||
168 | mk_fsid_v0(tfh, xdev, xino); | ||
169 | exp = exp_find(rqstp->rq_client, 0, tfh, &rqstp->rq_chandle); | ||
170 | } | ||
171 | |||
172 | error = nfserr_dropit; | ||
173 | if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN) | ||
174 | goto out; | ||
175 | |||
176 | error = nfserr_stale; | ||
177 | if (!exp || IS_ERR(exp)) | ||
178 | goto out; | ||
179 | |||
180 | /* Check if the request originated from a secure port. */ | ||
181 | error = nfserr_perm; | ||
182 | if (!rqstp->rq_secure && EX_SECURE(exp)) { | ||
183 | printk(KERN_WARNING | ||
184 | "nfsd: request from insecure port (%u.%u.%u.%u:%d)!\n", | ||
185 | NIPQUAD(rqstp->rq_addr.sin_addr.s_addr), | ||
186 | ntohs(rqstp->rq_addr.sin_port)); | ||
187 | goto out; | ||
188 | } | ||
189 | |||
190 | /* Set user creds for this exportpoint */ | ||
191 | error = nfsd_setuser(rqstp, exp); | ||
192 | if (error) { | ||
193 | error = nfserrno(error); | ||
194 | goto out; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Look up the dentry using the NFS file handle. | ||
199 | */ | ||
200 | error = nfserr_stale; | ||
201 | if (rqstp->rq_vers > 2) | ||
202 | error = nfserr_badhandle; | ||
203 | |||
204 | if (fh->fh_version != 1) { | ||
205 | tfh[0] = fh->ofh_ino; | ||
206 | tfh[1] = fh->ofh_generation; | ||
207 | tfh[2] = fh->ofh_dirino; | ||
208 | datap = tfh; | ||
209 | data_left = 3; | ||
210 | if (fh->ofh_dirino == 0) | ||
211 | fileid_type = 1; | ||
212 | else | ||
213 | fileid_type = 2; | ||
214 | } else | ||
215 | fileid_type = fh->fh_fileid_type; | ||
216 | |||
217 | if (fileid_type == 0) | ||
218 | dentry = dget(exp->ex_dentry); | ||
219 | else { | ||
220 | struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; | ||
221 | dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, | ||
222 | datap, data_left, | ||
223 | fileid_type, | ||
224 | nfsd_acceptable, exp); | ||
225 | } | ||
226 | if (dentry == NULL) | ||
227 | goto out; | ||
228 | if (IS_ERR(dentry)) { | ||
229 | if (PTR_ERR(dentry) != -EINVAL) | ||
230 | error = nfserrno(PTR_ERR(dentry)); | ||
231 | goto out; | ||
232 | } | ||
233 | #ifdef NFSD_PARANOIA | ||
234 | if (S_ISDIR(dentry->d_inode->i_mode) && | ||
235 | (dentry->d_flags & DCACHE_DISCONNECTED)) { | ||
236 | printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", | ||
237 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
238 | } | ||
239 | #endif | ||
240 | |||
241 | fhp->fh_dentry = dentry; | ||
242 | fhp->fh_export = exp; | ||
243 | nfsd_nr_verified++; | ||
244 | } else { | ||
245 | /* just rechecking permissions | ||
246 | * (e.g. nfsproc_create calls fh_verify, then nfsd_create does as well) | ||
247 | */ | ||
248 | dprintk("nfsd: fh_verify - just checking\n"); | ||
249 | dentry = fhp->fh_dentry; | ||
250 | exp = fhp->fh_export; | ||
251 | } | ||
252 | cache_get(&exp->h); | ||
253 | |||
254 | error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); | ||
255 | if (error) | ||
256 | goto out; | ||
257 | |||
258 | /* Finally, check access permissions. */ | ||
259 | error = nfsd_permission(exp, dentry, access); | ||
260 | |||
261 | #ifdef NFSD_PARANOIA_EXTREME | ||
262 | if (error) { | ||
263 | printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", | ||
264 | dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24)); | ||
265 | } | ||
266 | #endif | ||
267 | out: | ||
268 | if (exp && !IS_ERR(exp)) | ||
269 | exp_put(exp); | ||
270 | if (error == nfserr_stale) | ||
271 | nfsdstats.fh_stale++; | ||
272 | return error; | ||
273 | } | ||
274 | |||
275 | |||
276 | /* | ||
277 | * Compose a file handle for an NFS reply. | ||
278 | * | ||
279 | * Note that when first composed, the dentry may not yet have | ||
280 | * an inode. In this case a call to fh_update should be made | ||
281 | * before the fh goes out on the wire ... | ||
282 | */ | ||
283 | static inline int _fh_update(struct dentry *dentry, struct svc_export *exp, | ||
284 | __u32 *datap, int *maxsize) | ||
285 | { | ||
286 | struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; | ||
287 | |||
288 | if (dentry == exp->ex_dentry) { | ||
289 | *maxsize = 0; | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | return CALL(nop,encode_fh)(dentry, datap, maxsize, | ||
294 | !(exp->ex_flags&NFSEXP_NOSUBTREECHECK)); | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * for composing old style file handles | ||
299 | */ | ||
300 | static inline void _fh_update_old(struct dentry *dentry, | ||
301 | struct svc_export *exp, | ||
302 | struct knfsd_fh *fh) | ||
303 | { | ||
304 | fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino); | ||
305 | fh->ofh_generation = dentry->d_inode->i_generation; | ||
306 | if (S_ISDIR(dentry->d_inode->i_mode) || | ||
307 | (exp->ex_flags & NFSEXP_NOSUBTREECHECK)) | ||
308 | fh->ofh_dirino = 0; | ||
309 | } | ||
310 | |||
311 | int | ||
312 | fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh) | ||
313 | { | ||
314 | /* ref_fh is a reference file handle. | ||
315 | * if it is non-null, then we should compose a filehandle which is | ||
316 | * of the same version, where possible. | ||
317 | * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca | ||
318 | * Then create a 32byte filehandle using nfs_fhbase_old | ||
319 | * | ||
320 | */ | ||
321 | |||
322 | u8 ref_fh_version = 0; | ||
323 | u8 ref_fh_fsid_type = 0; | ||
324 | struct inode * inode = dentry->d_inode; | ||
325 | struct dentry *parent = dentry->d_parent; | ||
326 | __u32 *datap; | ||
327 | dev_t ex_dev = exp->ex_dentry->d_inode->i_sb->s_dev; | ||
328 | |||
329 | dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n", | ||
330 | MAJOR(ex_dev), MINOR(ex_dev), | ||
331 | (long) exp->ex_dentry->d_inode->i_ino, | ||
332 | parent->d_name.name, dentry->d_name.name, | ||
333 | (inode ? inode->i_ino : 0)); | ||
334 | |||
335 | if (ref_fh) { | ||
336 | ref_fh_version = ref_fh->fh_handle.fh_version; | ||
337 | if (ref_fh_version == 0xca) | ||
338 | ref_fh_fsid_type = 0; | ||
339 | else | ||
340 | ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type; | ||
341 | if (ref_fh_fsid_type > 3) | ||
342 | ref_fh_fsid_type = 0; | ||
343 | |||
344 | /* make sure ref_fh type works for given export */ | ||
345 | if (ref_fh_fsid_type == 1 && | ||
346 | !(exp->ex_flags & NFSEXP_FSID)) { | ||
347 | /* if we don't have an fsid, we cannot provide one... */ | ||
348 | ref_fh_fsid_type = 0; | ||
349 | } | ||
350 | } else if (exp->ex_flags & NFSEXP_FSID) | ||
351 | ref_fh_fsid_type = 1; | ||
352 | |||
353 | if (!old_valid_dev(ex_dev) && ref_fh_fsid_type == 0) { | ||
354 | /* for newer device numbers, we must use a newer fsid format */ | ||
355 | ref_fh_version = 1; | ||
356 | ref_fh_fsid_type = 3; | ||
357 | } | ||
358 | if (old_valid_dev(ex_dev) && | ||
359 | (ref_fh_fsid_type == 2 || ref_fh_fsid_type == 3)) | ||
360 | /* must use type1 for smaller device numbers */ | ||
361 | ref_fh_fsid_type = 0; | ||
362 | |||
363 | if (ref_fh == fhp) | ||
364 | fh_put(ref_fh); | ||
365 | |||
366 | if (fhp->fh_locked || fhp->fh_dentry) { | ||
367 | printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n", | ||
368 | parent->d_name.name, dentry->d_name.name); | ||
369 | } | ||
370 | if (fhp->fh_maxsize < NFS_FHSIZE) | ||
371 | printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n", | ||
372 | fhp->fh_maxsize, parent->d_name.name, dentry->d_name.name); | ||
373 | |||
374 | fhp->fh_dentry = dget(dentry); /* our internal copy */ | ||
375 | fhp->fh_export = exp; | ||
376 | cache_get(&exp->h); | ||
377 | |||
378 | if (ref_fh_version == 0xca) { | ||
379 | /* old style filehandle please */ | ||
380 | memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE); | ||
381 | fhp->fh_handle.fh_size = NFS_FHSIZE; | ||
382 | fhp->fh_handle.ofh_dcookie = 0xfeebbaca; | ||
383 | fhp->fh_handle.ofh_dev = old_encode_dev(ex_dev); | ||
384 | fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev; | ||
385 | fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_dentry->d_inode->i_ino); | ||
386 | fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry)); | ||
387 | if (inode) | ||
388 | _fh_update_old(dentry, exp, &fhp->fh_handle); | ||
389 | } else { | ||
390 | int len; | ||
391 | fhp->fh_handle.fh_version = 1; | ||
392 | fhp->fh_handle.fh_auth_type = 0; | ||
393 | datap = fhp->fh_handle.fh_auth+0; | ||
394 | fhp->fh_handle.fh_fsid_type = ref_fh_fsid_type; | ||
395 | switch (ref_fh_fsid_type) { | ||
396 | case 0: | ||
397 | /* | ||
398 | * fsid_type 0: | ||
399 | * 2byte major, 2byte minor, 4byte inode | ||
400 | */ | ||
401 | mk_fsid_v0(datap, ex_dev, | ||
402 | exp->ex_dentry->d_inode->i_ino); | ||
403 | break; | ||
404 | case 1: | ||
405 | /* fsid_type 1 == 4 bytes filesystem id */ | ||
406 | mk_fsid_v1(datap, exp->ex_fsid); | ||
407 | break; | ||
408 | case 2: | ||
409 | /* | ||
410 | * fsid_type 2: | ||
411 | * 4byte major, 4byte minor, 4byte inode | ||
412 | */ | ||
413 | mk_fsid_v2(datap, ex_dev, | ||
414 | exp->ex_dentry->d_inode->i_ino); | ||
415 | break; | ||
416 | case 3: | ||
417 | /* | ||
418 | * fsid_type 3: | ||
419 | * 4byte devicenumber, 4byte inode | ||
420 | */ | ||
421 | mk_fsid_v3(datap, ex_dev, | ||
422 | exp->ex_dentry->d_inode->i_ino); | ||
423 | break; | ||
424 | } | ||
425 | len = key_len(ref_fh_fsid_type); | ||
426 | datap += len/4; | ||
427 | fhp->fh_handle.fh_size = 4 + len; | ||
428 | |||
429 | if (inode) { | ||
430 | int size = (fhp->fh_maxsize-len-4)/4; | ||
431 | fhp->fh_handle.fh_fileid_type = | ||
432 | _fh_update(dentry, exp, datap, &size); | ||
433 | fhp->fh_handle.fh_size += size*4; | ||
434 | } | ||
435 | if (fhp->fh_handle.fh_fileid_type == 255) | ||
436 | return nfserr_opnotsupp; | ||
437 | } | ||
438 | |||
439 | nfsd_nr_verified++; | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * Update file handle information after changing a dentry. | ||
445 | * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create | ||
446 | */ | ||
447 | int | ||
448 | fh_update(struct svc_fh *fhp) | ||
449 | { | ||
450 | struct dentry *dentry; | ||
451 | __u32 *datap; | ||
452 | |||
453 | if (!fhp->fh_dentry) | ||
454 | goto out_bad; | ||
455 | |||
456 | dentry = fhp->fh_dentry; | ||
457 | if (!dentry->d_inode) | ||
458 | goto out_negative; | ||
459 | if (fhp->fh_handle.fh_version != 1) { | ||
460 | _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle); | ||
461 | } else { | ||
462 | int size; | ||
463 | if (fhp->fh_handle.fh_fileid_type != 0) | ||
464 | goto out_uptodate; | ||
465 | datap = fhp->fh_handle.fh_auth+ | ||
466 | fhp->fh_handle.fh_size/4 -1; | ||
467 | size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; | ||
468 | fhp->fh_handle.fh_fileid_type = | ||
469 | _fh_update(dentry, fhp->fh_export, datap, &size); | ||
470 | fhp->fh_handle.fh_size += size*4; | ||
471 | if (fhp->fh_handle.fh_fileid_type == 255) | ||
472 | return nfserr_opnotsupp; | ||
473 | } | ||
474 | out: | ||
475 | return 0; | ||
476 | |||
477 | out_bad: | ||
478 | printk(KERN_ERR "fh_update: fh not verified!\n"); | ||
479 | goto out; | ||
480 | out_negative: | ||
481 | printk(KERN_ERR "fh_update: %s/%s still negative!\n", | ||
482 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
483 | goto out; | ||
484 | out_uptodate: | ||
485 | printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n", | ||
486 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
487 | goto out; | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * Release a file handle. | ||
492 | */ | ||
493 | void | ||
494 | fh_put(struct svc_fh *fhp) | ||
495 | { | ||
496 | struct dentry * dentry = fhp->fh_dentry; | ||
497 | struct svc_export * exp = fhp->fh_export; | ||
498 | if (dentry) { | ||
499 | fh_unlock(fhp); | ||
500 | fhp->fh_dentry = NULL; | ||
501 | dput(dentry); | ||
502 | #ifdef CONFIG_NFSD_V3 | ||
503 | fhp->fh_pre_saved = 0; | ||
504 | fhp->fh_post_saved = 0; | ||
505 | #endif | ||
506 | nfsd_nr_put++; | ||
507 | } | ||
508 | if (exp) { | ||
509 | svc_export_put(&exp->h, &svc_export_cache); | ||
510 | fhp->fh_export = NULL; | ||
511 | } | ||
512 | return; | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * Shorthand for dprintk()'s | ||
517 | */ | ||
518 | char * SVCFH_fmt(struct svc_fh *fhp) | ||
519 | { | ||
520 | struct knfsd_fh *fh = &fhp->fh_handle; | ||
521 | |||
522 | static char buf[80]; | ||
523 | sprintf(buf, "%d: %08x %08x %08x %08x %08x %08x", | ||
524 | fh->fh_size, | ||
525 | fh->fh_base.fh_pad[0], | ||
526 | fh->fh_base.fh_pad[1], | ||
527 | fh->fh_base.fh_pad[2], | ||
528 | fh->fh_base.fh_pad[3], | ||
529 | fh->fh_base.fh_pad[4], | ||
530 | fh->fh_base.fh_pad[5]); | ||
531 | return buf; | ||
532 | } | ||
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c new file mode 100644 index 000000000000..757f9d208034 --- /dev/null +++ b/fs/nfsd/nfsproc.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * nfsproc2.c Process version 2 NFS requests. | ||
3 | * linux/fs/nfsd/nfs2proc.c | ||
4 | * | ||
5 | * Process version 2 NFS requests. | ||
6 | * | ||
7 | * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de> | ||
8 | */ | ||
9 | |||
10 | #include <linux/linkage.h> | ||
11 | #include <linux/time.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/fcntl.h> | ||
16 | #include <linux/net.h> | ||
17 | #include <linux/in.h> | ||
18 | #include <linux/namei.h> | ||
19 | #include <linux/unistd.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include <linux/sunrpc/svc.h> | ||
23 | #include <linux/nfsd/nfsd.h> | ||
24 | #include <linux/nfsd/cache.h> | ||
25 | #include <linux/nfsd/xdr.h> | ||
26 | |||
27 | typedef struct svc_rqst svc_rqst; | ||
28 | typedef struct svc_buf svc_buf; | ||
29 | |||
30 | #define NFSDDBG_FACILITY NFSDDBG_PROC | ||
31 | |||
32 | |||
33 | static int | ||
34 | nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) | ||
35 | { | ||
36 | return nfs_ok; | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * Get a file's attributes | ||
41 | * N.B. After this call resp->fh needs an fh_put | ||
42 | */ | ||
43 | static int | ||
44 | nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, | ||
45 | struct nfsd_attrstat *resp) | ||
46 | { | ||
47 | dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); | ||
48 | |||
49 | fh_copy(&resp->fh, &argp->fh); | ||
50 | return fh_verify(rqstp, &resp->fh, 0, MAY_NOP); | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * Set a file's attributes | ||
55 | * N.B. After this call resp->fh needs an fh_put | ||
56 | */ | ||
57 | static int | ||
58 | nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, | ||
59 | struct nfsd_attrstat *resp) | ||
60 | { | ||
61 | dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", | ||
62 | SVCFH_fmt(&argp->fh), | ||
63 | argp->attrs.ia_valid, (long) argp->attrs.ia_size); | ||
64 | |||
65 | fh_copy(&resp->fh, &argp->fh); | ||
66 | return nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Look up a path name component | ||
71 | * Note: the dentry in the resp->fh may be negative if the file | ||
72 | * doesn't exist yet. | ||
73 | * N.B. After this call resp->fh needs an fh_put | ||
74 | */ | ||
75 | static int | ||
76 | nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, | ||
77 | struct nfsd_diropres *resp) | ||
78 | { | ||
79 | int nfserr; | ||
80 | |||
81 | dprintk("nfsd: LOOKUP %s %.*s\n", | ||
82 | SVCFH_fmt(&argp->fh), argp->len, argp->name); | ||
83 | |||
84 | fh_init(&resp->fh, NFS_FHSIZE); | ||
85 | nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len, | ||
86 | &resp->fh); | ||
87 | |||
88 | fh_put(&argp->fh); | ||
89 | return nfserr; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Read a symlink. | ||
94 | */ | ||
95 | static int | ||
96 | nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_readlinkargs *argp, | ||
97 | struct nfsd_readlinkres *resp) | ||
98 | { | ||
99 | int nfserr; | ||
100 | |||
101 | dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh)); | ||
102 | |||
103 | /* Read the symlink. */ | ||
104 | resp->len = NFS_MAXPATHLEN; | ||
105 | nfserr = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len); | ||
106 | |||
107 | fh_put(&argp->fh); | ||
108 | return nfserr; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Read a portion of a file. | ||
113 | * N.B. After this call resp->fh needs an fh_put | ||
114 | */ | ||
115 | static int | ||
116 | nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, | ||
117 | struct nfsd_readres *resp) | ||
118 | { | ||
119 | int nfserr; | ||
120 | |||
121 | dprintk("nfsd: READ %s %d bytes at %d\n", | ||
122 | SVCFH_fmt(&argp->fh), | ||
123 | argp->count, argp->offset); | ||
124 | |||
125 | /* Obtain buffer pointer for payload. 19 is 1 word for | ||
126 | * status, 17 words for fattr, and 1 word for the byte count. | ||
127 | */ | ||
128 | |||
129 | if (NFSSVC_MAXBLKSIZE < argp->count) { | ||
130 | printk(KERN_NOTICE | ||
131 | "oversized read request from %u.%u.%u.%u:%d (%d bytes)\n", | ||
132 | NIPQUAD(rqstp->rq_addr.sin_addr.s_addr), | ||
133 | ntohs(rqstp->rq_addr.sin_port), | ||
134 | argp->count); | ||
135 | argp->count = NFSSVC_MAXBLKSIZE; | ||
136 | } | ||
137 | svc_reserve(rqstp, (19<<2) + argp->count + 4); | ||
138 | |||
139 | resp->count = argp->count; | ||
140 | nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, | ||
141 | argp->offset, | ||
142 | argp->vec, argp->vlen, | ||
143 | &resp->count); | ||
144 | |||
145 | return nfserr; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Write data to a file | ||
150 | * N.B. After this call resp->fh needs an fh_put | ||
151 | */ | ||
152 | static int | ||
153 | nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, | ||
154 | struct nfsd_attrstat *resp) | ||
155 | { | ||
156 | int nfserr; | ||
157 | int stable = 1; | ||
158 | |||
159 | dprintk("nfsd: WRITE %s %d bytes at %d\n", | ||
160 | SVCFH_fmt(&argp->fh), | ||
161 | argp->len, argp->offset); | ||
162 | |||
163 | nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, | ||
164 | argp->offset, | ||
165 | argp->vec, argp->vlen, | ||
166 | argp->len, | ||
167 | &stable); | ||
168 | return nfserr; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * CREATE processing is complicated. The keyword here is `overloaded.' | ||
173 | * The parent directory is kept locked between the check for existence | ||
174 | * and the actual create() call in compliance with VFS protocols. | ||
175 | * N.B. After this call _both_ argp->fh and resp->fh need an fh_put | ||
176 | */ | ||
177 | static int | ||
178 | nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, | ||
179 | struct nfsd_diropres *resp) | ||
180 | { | ||
181 | svc_fh *dirfhp = &argp->fh; | ||
182 | svc_fh *newfhp = &resp->fh; | ||
183 | struct iattr *attr = &argp->attrs; | ||
184 | struct inode *inode; | ||
185 | struct dentry *dchild; | ||
186 | int nfserr, type, mode; | ||
187 | dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size); | ||
188 | |||
189 | dprintk("nfsd: CREATE %s %.*s\n", | ||
190 | SVCFH_fmt(dirfhp), argp->len, argp->name); | ||
191 | |||
192 | /* First verify the parent file handle */ | ||
193 | nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC); | ||
194 | if (nfserr) | ||
195 | goto done; /* must fh_put dirfhp even on error */ | ||
196 | |||
197 | /* Check for MAY_WRITE in nfsd_create if necessary */ | ||
198 | |||
199 | nfserr = nfserr_acces; | ||
200 | if (!argp->len) | ||
201 | goto done; | ||
202 | nfserr = nfserr_exist; | ||
203 | if (isdotent(argp->name, argp->len)) | ||
204 | goto done; | ||
205 | fh_lock(dirfhp); | ||
206 | dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); | ||
207 | if (IS_ERR(dchild)) { | ||
208 | nfserr = nfserrno(PTR_ERR(dchild)); | ||
209 | goto out_unlock; | ||
210 | } | ||
211 | fh_init(newfhp, NFS_FHSIZE); | ||
212 | nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp); | ||
213 | if (!nfserr && !dchild->d_inode) | ||
214 | nfserr = nfserr_noent; | ||
215 | dput(dchild); | ||
216 | if (nfserr) { | ||
217 | if (nfserr != nfserr_noent) | ||
218 | goto out_unlock; | ||
219 | /* | ||
220 | * If the new file handle wasn't verified, we can't tell | ||
221 | * whether the file exists or not. Time to bail ... | ||
222 | */ | ||
223 | nfserr = nfserr_acces; | ||
224 | if (!newfhp->fh_dentry) { | ||
225 | printk(KERN_WARNING | ||
226 | "nfsd_proc_create: file handle not verified\n"); | ||
227 | goto out_unlock; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | inode = newfhp->fh_dentry->d_inode; | ||
232 | |||
233 | /* Unfudge the mode bits */ | ||
234 | if (attr->ia_valid & ATTR_MODE) { | ||
235 | type = attr->ia_mode & S_IFMT; | ||
236 | mode = attr->ia_mode & ~S_IFMT; | ||
237 | if (!type) { | ||
238 | /* no type, so if target exists, assume same as that, | ||
239 | * else assume a file */ | ||
240 | if (inode) { | ||
241 | type = inode->i_mode & S_IFMT; | ||
242 | switch(type) { | ||
243 | case S_IFCHR: | ||
244 | case S_IFBLK: | ||
245 | /* reserve rdev for later checking */ | ||
246 | rdev = inode->i_rdev; | ||
247 | attr->ia_valid |= ATTR_SIZE; | ||
248 | |||
249 | /* FALLTHROUGH */ | ||
250 | case S_IFIFO: | ||
251 | /* this is probably a permission check.. | ||
252 | * at least IRIX implements perm checking on | ||
253 | * echo thing > device-special-file-or-pipe | ||
254 | * by doing a CREATE with type==0 | ||
255 | */ | ||
256 | nfserr = nfsd_permission(newfhp->fh_export, | ||
257 | newfhp->fh_dentry, | ||
258 | MAY_WRITE|MAY_LOCAL_ACCESS); | ||
259 | if (nfserr && nfserr != nfserr_rofs) | ||
260 | goto out_unlock; | ||
261 | } | ||
262 | } else | ||
263 | type = S_IFREG; | ||
264 | } | ||
265 | } else if (inode) { | ||
266 | type = inode->i_mode & S_IFMT; | ||
267 | mode = inode->i_mode & ~S_IFMT; | ||
268 | } else { | ||
269 | type = S_IFREG; | ||
270 | mode = 0; /* ??? */ | ||
271 | } | ||
272 | |||
273 | attr->ia_valid |= ATTR_MODE; | ||
274 | attr->ia_mode = mode; | ||
275 | |||
276 | /* Special treatment for non-regular files according to the | ||
277 | * gospel of sun micro | ||
278 | */ | ||
279 | if (type != S_IFREG) { | ||
280 | int is_borc = 0; | ||
281 | if (type != S_IFBLK && type != S_IFCHR) { | ||
282 | rdev = 0; | ||
283 | } else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) { | ||
284 | /* If you think you've seen the worst, grok this. */ | ||
285 | type = S_IFIFO; | ||
286 | } else { | ||
287 | /* Okay, char or block special */ | ||
288 | is_borc = 1; | ||
289 | if (!rdev) | ||
290 | rdev = wanted; | ||
291 | } | ||
292 | |||
293 | /* we've used the SIZE information, so discard it */ | ||
294 | attr->ia_valid &= ~ATTR_SIZE; | ||
295 | |||
296 | /* Make sure the type and device matches */ | ||
297 | nfserr = nfserr_exist; | ||
298 | if (inode && type != (inode->i_mode & S_IFMT)) | ||
299 | goto out_unlock; | ||
300 | } | ||
301 | |||
302 | nfserr = 0; | ||
303 | if (!inode) { | ||
304 | /* File doesn't exist. Create it and set attrs */ | ||
305 | nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len, | ||
306 | attr, type, rdev, newfhp); | ||
307 | } else if (type == S_IFREG) { | ||
308 | dprintk("nfsd: existing %s, valid=%x, size=%ld\n", | ||
309 | argp->name, attr->ia_valid, (long) attr->ia_size); | ||
310 | /* File already exists. We ignore all attributes except | ||
311 | * size, so that creat() behaves exactly like | ||
312 | * open(..., O_CREAT|O_TRUNC|O_WRONLY). | ||
313 | */ | ||
314 | attr->ia_valid &= ATTR_SIZE; | ||
315 | if (attr->ia_valid) | ||
316 | nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time_t)0); | ||
317 | } | ||
318 | |||
319 | out_unlock: | ||
320 | /* We don't really need to unlock, as fh_put does it. */ | ||
321 | fh_unlock(dirfhp); | ||
322 | |||
323 | done: | ||
324 | fh_put(dirfhp); | ||
325 | return nfserr; | ||
326 | } | ||
327 | |||
328 | static int | ||
329 | nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, | ||
330 | void *resp) | ||
331 | { | ||
332 | int nfserr; | ||
333 | |||
334 | dprintk("nfsd: REMOVE %s %.*s\n", SVCFH_fmt(&argp->fh), | ||
335 | argp->len, argp->name); | ||
336 | |||
337 | /* Unlink. -SIFDIR means file must not be a directory */ | ||
338 | nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len); | ||
339 | fh_put(&argp->fh); | ||
340 | return nfserr; | ||
341 | } | ||
342 | |||
343 | static int | ||
344 | nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp, | ||
345 | void *resp) | ||
346 | { | ||
347 | int nfserr; | ||
348 | |||
349 | dprintk("nfsd: RENAME %s %.*s -> \n", | ||
350 | SVCFH_fmt(&argp->ffh), argp->flen, argp->fname); | ||
351 | dprintk("nfsd: -> %s %.*s\n", | ||
352 | SVCFH_fmt(&argp->tfh), argp->tlen, argp->tname); | ||
353 | |||
354 | nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen, | ||
355 | &argp->tfh, argp->tname, argp->tlen); | ||
356 | fh_put(&argp->ffh); | ||
357 | fh_put(&argp->tfh); | ||
358 | return nfserr; | ||
359 | } | ||
360 | |||
361 | static int | ||
362 | nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp, | ||
363 | void *resp) | ||
364 | { | ||
365 | int nfserr; | ||
366 | |||
367 | dprintk("nfsd: LINK %s ->\n", | ||
368 | SVCFH_fmt(&argp->ffh)); | ||
369 | dprintk("nfsd: %s %.*s\n", | ||
370 | SVCFH_fmt(&argp->tfh), | ||
371 | argp->tlen, | ||
372 | argp->tname); | ||
373 | |||
374 | nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, | ||
375 | &argp->ffh); | ||
376 | fh_put(&argp->ffh); | ||
377 | fh_put(&argp->tfh); | ||
378 | return nfserr; | ||
379 | } | ||
380 | |||
381 | static int | ||
382 | nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp, | ||
383 | void *resp) | ||
384 | { | ||
385 | struct svc_fh newfh; | ||
386 | int nfserr; | ||
387 | |||
388 | dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n", | ||
389 | SVCFH_fmt(&argp->ffh), argp->flen, argp->fname, | ||
390 | argp->tlen, argp->tname); | ||
391 | |||
392 | fh_init(&newfh, NFS_FHSIZE); | ||
393 | /* | ||
394 | * Create the link, look up new file and set attrs. | ||
395 | */ | ||
396 | nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, | ||
397 | argp->tname, argp->tlen, | ||
398 | &newfh, &argp->attrs); | ||
399 | |||
400 | |||
401 | fh_put(&argp->ffh); | ||
402 | fh_put(&newfh); | ||
403 | return nfserr; | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * Make directory. This operation is not idempotent. | ||
408 | * N.B. After this call resp->fh needs an fh_put | ||
409 | */ | ||
410 | static int | ||
411 | nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp, | ||
412 | struct nfsd_diropres *resp) | ||
413 | { | ||
414 | int nfserr; | ||
415 | |||
416 | dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name); | ||
417 | |||
418 | if (resp->fh.fh_dentry) { | ||
419 | printk(KERN_WARNING | ||
420 | "nfsd_proc_mkdir: response already verified??\n"); | ||
421 | } | ||
422 | |||
423 | argp->attrs.ia_valid &= ~ATTR_SIZE; | ||
424 | fh_init(&resp->fh, NFS_FHSIZE); | ||
425 | nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, | ||
426 | &argp->attrs, S_IFDIR, 0, &resp->fh); | ||
427 | fh_put(&argp->fh); | ||
428 | return nfserr; | ||
429 | } | ||
430 | |||
431 | /* | ||
432 | * Remove a directory | ||
433 | */ | ||
434 | static int | ||
435 | nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, | ||
436 | void *resp) | ||
437 | { | ||
438 | int nfserr; | ||
439 | |||
440 | dprintk("nfsd: RMDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name); | ||
441 | |||
442 | nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len); | ||
443 | fh_put(&argp->fh); | ||
444 | return nfserr; | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * Read a portion of a directory. | ||
449 | */ | ||
450 | static int | ||
451 | nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp, | ||
452 | struct nfsd_readdirres *resp) | ||
453 | { | ||
454 | int nfserr, count; | ||
455 | loff_t offset; | ||
456 | |||
457 | dprintk("nfsd: READDIR %s %d bytes at %d\n", | ||
458 | SVCFH_fmt(&argp->fh), | ||
459 | argp->count, argp->cookie); | ||
460 | |||
461 | /* Shrink to the client read size */ | ||
462 | count = (argp->count >> 2) - 2; | ||
463 | |||
464 | /* Make sure we've room for the NULL ptr & eof flag */ | ||
465 | count -= 2; | ||
466 | if (count < 0) | ||
467 | count = 0; | ||
468 | |||
469 | resp->buffer = argp->buffer; | ||
470 | resp->offset = NULL; | ||
471 | resp->buflen = count; | ||
472 | resp->common.err = nfs_ok; | ||
473 | /* Read directory and encode entries on the fly */ | ||
474 | offset = argp->cookie; | ||
475 | nfserr = nfsd_readdir(rqstp, &argp->fh, &offset, | ||
476 | &resp->common, nfssvc_encode_entry); | ||
477 | |||
478 | resp->count = resp->buffer - argp->buffer; | ||
479 | if (resp->offset) | ||
480 | *resp->offset = htonl(offset); | ||
481 | |||
482 | fh_put(&argp->fh); | ||
483 | return nfserr; | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * Get file system info | ||
488 | */ | ||
489 | static int | ||
490 | nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, | ||
491 | struct nfsd_statfsres *resp) | ||
492 | { | ||
493 | int nfserr; | ||
494 | |||
495 | dprintk("nfsd: STATFS %s\n", SVCFH_fmt(&argp->fh)); | ||
496 | |||
497 | nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); | ||
498 | fh_put(&argp->fh); | ||
499 | return nfserr; | ||
500 | } | ||
501 | |||
502 | /* | ||
503 | * NFSv2 Server procedures. | ||
504 | * Only the results of non-idempotent operations are cached. | ||
505 | */ | ||
506 | #define nfsd_proc_none NULL | ||
507 | #define nfssvc_release_none NULL | ||
508 | struct nfsd_void { int dummy; }; | ||
509 | |||
510 | #define PROC(name, argt, rest, relt, cache, respsize) \ | ||
511 | { (svc_procfunc) nfsd_proc_##name, \ | ||
512 | (kxdrproc_t) nfssvc_decode_##argt, \ | ||
513 | (kxdrproc_t) nfssvc_encode_##rest, \ | ||
514 | (kxdrproc_t) nfssvc_release_##relt, \ | ||
515 | sizeof(struct nfsd_##argt), \ | ||
516 | sizeof(struct nfsd_##rest), \ | ||
517 | 0, \ | ||
518 | cache, \ | ||
519 | respsize, \ | ||
520 | } | ||
521 | |||
522 | #define ST 1 /* status */ | ||
523 | #define FH 8 /* filehandle */ | ||
524 | #define AT 18 /* attributes */ | ||
525 | |||
526 | static struct svc_procedure nfsd_procedures2[18] = { | ||
527 | PROC(null, void, void, none, RC_NOCACHE, ST), | ||
528 | PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT), | ||
529 | PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), | ||
530 | PROC(none, void, void, none, RC_NOCACHE, ST), | ||
531 | PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT), | ||
532 | PROC(readlink, readlinkargs, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4), | ||
533 | PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE), | ||
534 | PROC(none, void, void, none, RC_NOCACHE, ST), | ||
535 | PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), | ||
536 | PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), | ||
537 | PROC(remove, diropargs, void, none, RC_REPLSTAT, ST), | ||
538 | PROC(rename, renameargs, void, none, RC_REPLSTAT, ST), | ||
539 | PROC(link, linkargs, void, none, RC_REPLSTAT, ST), | ||
540 | PROC(symlink, symlinkargs, void, none, RC_REPLSTAT, ST), | ||
541 | PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), | ||
542 | PROC(rmdir, diropargs, void, none, RC_REPLSTAT, ST), | ||
543 | PROC(readdir, readdirargs, readdirres, none, RC_NOCACHE, 0), | ||
544 | PROC(statfs, fhandle, statfsres, none, RC_NOCACHE, ST+5), | ||
545 | }; | ||
546 | |||
547 | |||
548 | struct svc_version nfsd_version2 = { | ||
549 | .vs_vers = 2, | ||
550 | .vs_nproc = 18, | ||
551 | .vs_proc = nfsd_procedures2, | ||
552 | .vs_dispatch = nfsd_dispatch, | ||
553 | .vs_xdrsize = NFS2_SVC_XDRSIZE, | ||
554 | }; | ||
555 | |||
556 | /* | ||
557 | * Map errnos to NFS errnos. | ||
558 | */ | ||
559 | int | ||
560 | nfserrno (int errno) | ||
561 | { | ||
562 | static struct { | ||
563 | int nfserr; | ||
564 | int syserr; | ||
565 | } nfs_errtbl[] = { | ||
566 | { nfs_ok, 0 }, | ||
567 | { nfserr_perm, -EPERM }, | ||
568 | { nfserr_noent, -ENOENT }, | ||
569 | { nfserr_io, -EIO }, | ||
570 | { nfserr_nxio, -ENXIO }, | ||
571 | { nfserr_acces, -EACCES }, | ||
572 | { nfserr_exist, -EEXIST }, | ||
573 | { nfserr_xdev, -EXDEV }, | ||
574 | { nfserr_mlink, -EMLINK }, | ||
575 | { nfserr_nodev, -ENODEV }, | ||
576 | { nfserr_notdir, -ENOTDIR }, | ||
577 | { nfserr_isdir, -EISDIR }, | ||
578 | { nfserr_inval, -EINVAL }, | ||
579 | { nfserr_fbig, -EFBIG }, | ||
580 | { nfserr_nospc, -ENOSPC }, | ||
581 | { nfserr_rofs, -EROFS }, | ||
582 | { nfserr_mlink, -EMLINK }, | ||
583 | { nfserr_nametoolong, -ENAMETOOLONG }, | ||
584 | { nfserr_notempty, -ENOTEMPTY }, | ||
585 | #ifdef EDQUOT | ||
586 | { nfserr_dquot, -EDQUOT }, | ||
587 | #endif | ||
588 | { nfserr_stale, -ESTALE }, | ||
589 | { nfserr_jukebox, -ETIMEDOUT }, | ||
590 | { nfserr_dropit, -EAGAIN }, | ||
591 | { nfserr_dropit, -ENOMEM }, | ||
592 | { nfserr_badname, -ESRCH }, | ||
593 | { nfserr_io, -ETXTBSY }, | ||
594 | { -1, -EIO } | ||
595 | }; | ||
596 | int i; | ||
597 | |||
598 | for (i = 0; nfs_errtbl[i].nfserr != -1; i++) { | ||
599 | if (nfs_errtbl[i].syserr == errno) | ||
600 | return nfs_errtbl[i].nfserr; | ||
601 | } | ||
602 | printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno); | ||
603 | return nfserr_io; | ||
604 | } | ||
605 | |||
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c new file mode 100644 index 000000000000..39551657e656 --- /dev/null +++ b/fs/nfsd/nfssvc.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/nfssvc.c | ||
3 | * | ||
4 | * Central processing for nfsd. | ||
5 | * | ||
6 | * Authors: Olaf Kirch (okir@monad.swb.de) | ||
7 | * | ||
8 | * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | |||
14 | #include <linux/time.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/nfs.h> | ||
17 | #include <linux/in.h> | ||
18 | #include <linux/uio.h> | ||
19 | #include <linux/unistd.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/smp.h> | ||
22 | #include <linux/smp_lock.h> | ||
23 | #include <linux/fs_struct.h> | ||
24 | |||
25 | #include <linux/sunrpc/types.h> | ||
26 | #include <linux/sunrpc/stats.h> | ||
27 | #include <linux/sunrpc/svc.h> | ||
28 | #include <linux/sunrpc/svcsock.h> | ||
29 | #include <linux/sunrpc/cache.h> | ||
30 | #include <linux/nfsd/nfsd.h> | ||
31 | #include <linux/nfsd/stats.h> | ||
32 | #include <linux/nfsd/cache.h> | ||
33 | #include <linux/lockd/bind.h> | ||
34 | |||
35 | #define NFSDDBG_FACILITY NFSDDBG_SVC | ||
36 | |||
37 | /* these signals will be delivered to an nfsd thread | ||
38 | * when handling a request | ||
39 | */ | ||
40 | #define ALLOWED_SIGS (sigmask(SIGKILL)) | ||
41 | /* these signals will be delivered to an nfsd thread | ||
42 | * when not handling a request. i.e. when waiting | ||
43 | */ | ||
44 | #define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT)) | ||
45 | /* if the last thread dies with SIGHUP, then the exports table is | ||
46 | * left unchanged ( like 2.4-{0-9} ). Any other signal will clear | ||
47 | * the exports table (like 2.2). | ||
48 | */ | ||
49 | #define SIG_NOCLEAN SIGHUP | ||
50 | |||
51 | extern struct svc_program nfsd_program; | ||
52 | static void nfsd(struct svc_rqst *rqstp); | ||
53 | struct timeval nfssvc_boot; | ||
54 | static struct svc_serv *nfsd_serv; | ||
55 | static atomic_t nfsd_busy; | ||
56 | static unsigned long nfsd_last_call; | ||
57 | static DEFINE_SPINLOCK(nfsd_call_lock); | ||
58 | |||
59 | struct nfsd_list { | ||
60 | struct list_head list; | ||
61 | struct task_struct *task; | ||
62 | }; | ||
63 | static struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list); | ||
64 | |||
65 | /* | ||
66 | * Maximum number of nfsd processes | ||
67 | */ | ||
68 | #define NFSD_MAXSERVS 8192 | ||
69 | |||
70 | int nfsd_nrthreads(void) | ||
71 | { | ||
72 | if (nfsd_serv == NULL) | ||
73 | return 0; | ||
74 | else | ||
75 | return nfsd_serv->sv_nrthreads; | ||
76 | } | ||
77 | |||
78 | int | ||
79 | nfsd_svc(unsigned short port, int nrservs) | ||
80 | { | ||
81 | int error; | ||
82 | int none_left; | ||
83 | struct list_head *victim; | ||
84 | |||
85 | lock_kernel(); | ||
86 | dprintk("nfsd: creating service\n"); | ||
87 | error = -EINVAL; | ||
88 | if (nrservs <= 0) | ||
89 | nrservs = 0; | ||
90 | if (nrservs > NFSD_MAXSERVS) | ||
91 | nrservs = NFSD_MAXSERVS; | ||
92 | |||
93 | /* Readahead param cache - will no-op if it already exists */ | ||
94 | error = nfsd_racache_init(2*nrservs); | ||
95 | if (error<0) | ||
96 | goto out; | ||
97 | error = nfs4_state_init(); | ||
98 | if (error<0) | ||
99 | goto out; | ||
100 | if (!nfsd_serv) { | ||
101 | atomic_set(&nfsd_busy, 0); | ||
102 | error = -ENOMEM; | ||
103 | nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE); | ||
104 | if (nfsd_serv == NULL) | ||
105 | goto out; | ||
106 | error = svc_makesock(nfsd_serv, IPPROTO_UDP, port); | ||
107 | if (error < 0) | ||
108 | goto failure; | ||
109 | |||
110 | #ifdef CONFIG_NFSD_TCP | ||
111 | error = svc_makesock(nfsd_serv, IPPROTO_TCP, port); | ||
112 | if (error < 0) | ||
113 | goto failure; | ||
114 | #endif | ||
115 | do_gettimeofday(&nfssvc_boot); /* record boot time */ | ||
116 | } else | ||
117 | nfsd_serv->sv_nrthreads++; | ||
118 | nrservs -= (nfsd_serv->sv_nrthreads-1); | ||
119 | while (nrservs > 0) { | ||
120 | nrservs--; | ||
121 | __module_get(THIS_MODULE); | ||
122 | error = svc_create_thread(nfsd, nfsd_serv); | ||
123 | if (error < 0) { | ||
124 | module_put(THIS_MODULE); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | victim = nfsd_list.next; | ||
129 | while (nrservs < 0 && victim != &nfsd_list) { | ||
130 | struct nfsd_list *nl = | ||
131 | list_entry(victim,struct nfsd_list, list); | ||
132 | victim = victim->next; | ||
133 | send_sig(SIG_NOCLEAN, nl->task, 1); | ||
134 | nrservs++; | ||
135 | } | ||
136 | failure: | ||
137 | none_left = (nfsd_serv->sv_nrthreads == 1); | ||
138 | svc_destroy(nfsd_serv); /* Release server */ | ||
139 | if (none_left) { | ||
140 | nfsd_serv = NULL; | ||
141 | nfsd_racache_shutdown(); | ||
142 | nfs4_state_shutdown(); | ||
143 | } | ||
144 | out: | ||
145 | unlock_kernel(); | ||
146 | return error; | ||
147 | } | ||
148 | |||
149 | static inline void | ||
150 | update_thread_usage(int busy_threads) | ||
151 | { | ||
152 | unsigned long prev_call; | ||
153 | unsigned long diff; | ||
154 | int decile; | ||
155 | |||
156 | spin_lock(&nfsd_call_lock); | ||
157 | prev_call = nfsd_last_call; | ||
158 | nfsd_last_call = jiffies; | ||
159 | decile = busy_threads*10/nfsdstats.th_cnt; | ||
160 | if (decile>0 && decile <= 10) { | ||
161 | diff = nfsd_last_call - prev_call; | ||
162 | if ( (nfsdstats.th_usage[decile-1] += diff) >= NFSD_USAGE_WRAP) | ||
163 | nfsdstats.th_usage[decile-1] -= NFSD_USAGE_WRAP; | ||
164 | if (decile == 10) | ||
165 | nfsdstats.th_fullcnt++; | ||
166 | } | ||
167 | spin_unlock(&nfsd_call_lock); | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * This is the NFS server kernel thread | ||
172 | */ | ||
173 | static void | ||
174 | nfsd(struct svc_rqst *rqstp) | ||
175 | { | ||
176 | struct svc_serv *serv = rqstp->rq_server; | ||
177 | struct fs_struct *fsp; | ||
178 | int err; | ||
179 | struct nfsd_list me; | ||
180 | sigset_t shutdown_mask, allowed_mask; | ||
181 | |||
182 | /* Lock module and set up kernel thread */ | ||
183 | lock_kernel(); | ||
184 | daemonize("nfsd"); | ||
185 | |||
186 | /* After daemonize() this kernel thread shares current->fs | ||
187 | * with the init process. We need to create files with a | ||
188 | * umask of 0 instead of init's umask. */ | ||
189 | fsp = copy_fs_struct(current->fs); | ||
190 | if (!fsp) { | ||
191 | printk("Unable to start nfsd thread: out of memory\n"); | ||
192 | goto out; | ||
193 | } | ||
194 | exit_fs(current); | ||
195 | current->fs = fsp; | ||
196 | current->fs->umask = 0; | ||
197 | |||
198 | siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); | ||
199 | siginitsetinv(&allowed_mask, ALLOWED_SIGS); | ||
200 | |||
201 | nfsdstats.th_cnt++; | ||
202 | |||
203 | lockd_up(); /* start lockd */ | ||
204 | |||
205 | me.task = current; | ||
206 | list_add(&me.list, &nfsd_list); | ||
207 | |||
208 | unlock_kernel(); | ||
209 | |||
210 | /* | ||
211 | * We want less throttling in balance_dirty_pages() so that nfs to | ||
212 | * localhost doesn't cause nfsd to lock up due to all the client's | ||
213 | * dirty pages. | ||
214 | */ | ||
215 | current->flags |= PF_LESS_THROTTLE; | ||
216 | |||
217 | /* | ||
218 | * The main request loop | ||
219 | */ | ||
220 | for (;;) { | ||
221 | /* Block all but the shutdown signals */ | ||
222 | sigprocmask(SIG_SETMASK, &shutdown_mask, NULL); | ||
223 | |||
224 | /* | ||
225 | * Find a socket with data available and call its | ||
226 | * recvfrom routine. | ||
227 | */ | ||
228 | while ((err = svc_recv(serv, rqstp, | ||
229 | 60*60*HZ)) == -EAGAIN) | ||
230 | ; | ||
231 | if (err < 0) | ||
232 | break; | ||
233 | update_thread_usage(atomic_read(&nfsd_busy)); | ||
234 | atomic_inc(&nfsd_busy); | ||
235 | |||
236 | /* Lock the export hash tables for reading. */ | ||
237 | exp_readlock(); | ||
238 | |||
239 | /* Process request with signals blocked. */ | ||
240 | sigprocmask(SIG_SETMASK, &allowed_mask, NULL); | ||
241 | |||
242 | svc_process(serv, rqstp); | ||
243 | |||
244 | /* Unlock export hash tables */ | ||
245 | exp_readunlock(); | ||
246 | update_thread_usage(atomic_read(&nfsd_busy)); | ||
247 | atomic_dec(&nfsd_busy); | ||
248 | } | ||
249 | |||
250 | if (err != -EINTR) { | ||
251 | printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); | ||
252 | } else { | ||
253 | unsigned int signo; | ||
254 | |||
255 | for (signo = 1; signo <= _NSIG; signo++) | ||
256 | if (sigismember(¤t->pending.signal, signo) && | ||
257 | !sigismember(¤t->blocked, signo)) | ||
258 | break; | ||
259 | err = signo; | ||
260 | } | ||
261 | |||
262 | lock_kernel(); | ||
263 | |||
264 | /* Release lockd */ | ||
265 | lockd_down(); | ||
266 | |||
267 | /* Check if this is last thread */ | ||
268 | if (serv->sv_nrthreads==1) { | ||
269 | |||
270 | printk(KERN_WARNING "nfsd: last server has exited\n"); | ||
271 | if (err != SIG_NOCLEAN) { | ||
272 | printk(KERN_WARNING "nfsd: unexporting all filesystems\n"); | ||
273 | nfsd_export_flush(); | ||
274 | } | ||
275 | nfsd_serv = NULL; | ||
276 | nfsd_racache_shutdown(); /* release read-ahead cache */ | ||
277 | nfs4_state_shutdown(); | ||
278 | } | ||
279 | list_del(&me.list); | ||
280 | nfsdstats.th_cnt --; | ||
281 | |||
282 | out: | ||
283 | /* Release the thread */ | ||
284 | svc_exit_thread(rqstp); | ||
285 | |||
286 | /* Release module */ | ||
287 | module_put_and_exit(0); | ||
288 | } | ||
289 | |||
290 | int | ||
291 | nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp) | ||
292 | { | ||
293 | struct svc_procedure *proc; | ||
294 | kxdrproc_t xdr; | ||
295 | u32 nfserr; | ||
296 | u32 *nfserrp; | ||
297 | |||
298 | dprintk("nfsd_dispatch: vers %d proc %d\n", | ||
299 | rqstp->rq_vers, rqstp->rq_proc); | ||
300 | proc = rqstp->rq_procinfo; | ||
301 | |||
302 | /* Check whether we have this call in the cache. */ | ||
303 | switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) { | ||
304 | case RC_INTR: | ||
305 | case RC_DROPIT: | ||
306 | return 0; | ||
307 | case RC_REPLY: | ||
308 | return 1; | ||
309 | case RC_DOIT:; | ||
310 | /* do it */ | ||
311 | } | ||
312 | |||
313 | /* Decode arguments */ | ||
314 | xdr = proc->pc_decode; | ||
315 | if (xdr && !xdr(rqstp, (u32*)rqstp->rq_arg.head[0].iov_base, | ||
316 | rqstp->rq_argp)) { | ||
317 | dprintk("nfsd: failed to decode arguments!\n"); | ||
318 | nfsd_cache_update(rqstp, RC_NOCACHE, NULL); | ||
319 | *statp = rpc_garbage_args; | ||
320 | return 1; | ||
321 | } | ||
322 | |||
323 | /* need to grab the location to store the status, as | ||
324 | * nfsv4 does some encoding while processing | ||
325 | */ | ||
326 | nfserrp = rqstp->rq_res.head[0].iov_base | ||
327 | + rqstp->rq_res.head[0].iov_len; | ||
328 | rqstp->rq_res.head[0].iov_len += sizeof(u32); | ||
329 | |||
330 | /* Now call the procedure handler, and encode NFS status. */ | ||
331 | nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); | ||
332 | if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2) | ||
333 | nfserr = nfserr_dropit; | ||
334 | if (nfserr == nfserr_dropit) { | ||
335 | dprintk("nfsd: Dropping request due to malloc failure!\n"); | ||
336 | nfsd_cache_update(rqstp, RC_NOCACHE, NULL); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | if (rqstp->rq_proc != 0) | ||
341 | *nfserrp++ = nfserr; | ||
342 | |||
343 | /* Encode result. | ||
344 | * For NFSv2, additional info is never returned in case of an error. | ||
345 | */ | ||
346 | if (!(nfserr && rqstp->rq_vers == 2)) { | ||
347 | xdr = proc->pc_encode; | ||
348 | if (xdr && !xdr(rqstp, nfserrp, | ||
349 | rqstp->rq_resp)) { | ||
350 | /* Failed to encode result. Release cache entry */ | ||
351 | dprintk("nfsd: failed to encode result!\n"); | ||
352 | nfsd_cache_update(rqstp, RC_NOCACHE, NULL); | ||
353 | *statp = rpc_system_err; | ||
354 | return 1; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | /* Store reply in cache. */ | ||
359 | nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); | ||
360 | return 1; | ||
361 | } | ||
362 | |||
363 | extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; | ||
364 | |||
365 | static struct svc_version * nfsd_version[] = { | ||
366 | [2] = &nfsd_version2, | ||
367 | #if defined(CONFIG_NFSD_V3) | ||
368 | [3] = &nfsd_version3, | ||
369 | #endif | ||
370 | #if defined(CONFIG_NFSD_V4) | ||
371 | [4] = &nfsd_version4, | ||
372 | #endif | ||
373 | }; | ||
374 | |||
375 | #define NFSD_NRVERS (sizeof(nfsd_version)/sizeof(nfsd_version[0])) | ||
376 | struct svc_program nfsd_program = { | ||
377 | .pg_prog = NFS_PROGRAM, /* program number */ | ||
378 | .pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */ | ||
379 | .pg_vers = nfsd_version, /* version table */ | ||
380 | .pg_name = "nfsd", /* program name */ | ||
381 | .pg_class = "nfsd", /* authentication class */ | ||
382 | .pg_stats = &nfsd_svcstats, /* version table */ | ||
383 | .pg_authenticate = &svc_set_client, /* export authentication */ | ||
384 | |||
385 | }; | ||
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c new file mode 100644 index 000000000000..948b08287c99 --- /dev/null +++ b/fs/nfsd/nfsxdr.c | |||
@@ -0,0 +1,511 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/xdr.c | ||
3 | * | ||
4 | * XDR support for nfsd | ||
5 | * | ||
6 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <linux/nfs.h> | ||
12 | #include <linux/vfs.h> | ||
13 | #include <linux/sunrpc/xdr.h> | ||
14 | #include <linux/sunrpc/svc.h> | ||
15 | #include <linux/nfsd/nfsd.h> | ||
16 | #include <linux/nfsd/xdr.h> | ||
17 | #include <linux/mm.h> | ||
18 | |||
19 | #define NFSDDBG_FACILITY NFSDDBG_XDR | ||
20 | |||
21 | |||
22 | #ifdef NFSD_OPTIMIZE_SPACE | ||
23 | # define inline | ||
24 | #endif | ||
25 | |||
26 | /* | ||
27 | * Mapping of S_IF* types to NFS file types | ||
28 | */ | ||
29 | static u32 nfs_ftypes[] = { | ||
30 | NFNON, NFCHR, NFCHR, NFBAD, | ||
31 | NFDIR, NFBAD, NFBLK, NFBAD, | ||
32 | NFREG, NFBAD, NFLNK, NFBAD, | ||
33 | NFSOCK, NFBAD, NFLNK, NFBAD, | ||
34 | }; | ||
35 | |||
36 | |||
37 | /* | ||
38 | * XDR functions for basic NFS types | ||
39 | */ | ||
40 | static inline u32 * | ||
41 | decode_fh(u32 *p, struct svc_fh *fhp) | ||
42 | { | ||
43 | fh_init(fhp, NFS_FHSIZE); | ||
44 | memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE); | ||
45 | fhp->fh_handle.fh_size = NFS_FHSIZE; | ||
46 | |||
47 | /* FIXME: Look up export pointer here and verify | ||
48 | * Sun Secure RPC if requested */ | ||
49 | return p + (NFS_FHSIZE >> 2); | ||
50 | } | ||
51 | |||
52 | static inline u32 * | ||
53 | encode_fh(u32 *p, struct svc_fh *fhp) | ||
54 | { | ||
55 | memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE); | ||
56 | return p + (NFS_FHSIZE>> 2); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Decode a file name and make sure that the path contains | ||
61 | * no slashes or null bytes. | ||
62 | */ | ||
63 | static inline u32 * | ||
64 | decode_filename(u32 *p, char **namp, int *lenp) | ||
65 | { | ||
66 | char *name; | ||
67 | int i; | ||
68 | |||
69 | if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) { | ||
70 | for (i = 0, name = *namp; i < *lenp; i++, name++) { | ||
71 | if (*name == '\0' || *name == '/') | ||
72 | return NULL; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return p; | ||
77 | } | ||
78 | |||
79 | static inline u32 * | ||
80 | decode_pathname(u32 *p, char **namp, int *lenp) | ||
81 | { | ||
82 | char *name; | ||
83 | int i; | ||
84 | |||
85 | if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) { | ||
86 | for (i = 0, name = *namp; i < *lenp; i++, name++) { | ||
87 | if (*name == '\0') | ||
88 | return NULL; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | return p; | ||
93 | } | ||
94 | |||
95 | static inline u32 * | ||
96 | decode_sattr(u32 *p, struct iattr *iap) | ||
97 | { | ||
98 | u32 tmp, tmp1; | ||
99 | |||
100 | iap->ia_valid = 0; | ||
101 | |||
102 | /* Sun client bug compatibility check: some sun clients seem to | ||
103 | * put 0xffff in the mode field when they mean 0xffffffff. | ||
104 | * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah. | ||
105 | */ | ||
106 | if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) { | ||
107 | iap->ia_valid |= ATTR_MODE; | ||
108 | iap->ia_mode = tmp; | ||
109 | } | ||
110 | if ((tmp = ntohl(*p++)) != (u32)-1) { | ||
111 | iap->ia_valid |= ATTR_UID; | ||
112 | iap->ia_uid = tmp; | ||
113 | } | ||
114 | if ((tmp = ntohl(*p++)) != (u32)-1) { | ||
115 | iap->ia_valid |= ATTR_GID; | ||
116 | iap->ia_gid = tmp; | ||
117 | } | ||
118 | if ((tmp = ntohl(*p++)) != (u32)-1) { | ||
119 | iap->ia_valid |= ATTR_SIZE; | ||
120 | iap->ia_size = tmp; | ||
121 | } | ||
122 | tmp = ntohl(*p++); tmp1 = ntohl(*p++); | ||
123 | if (tmp != (u32)-1 && tmp1 != (u32)-1) { | ||
124 | iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; | ||
125 | iap->ia_atime.tv_sec = tmp; | ||
126 | iap->ia_atime.tv_nsec = tmp1 * 1000; | ||
127 | } | ||
128 | tmp = ntohl(*p++); tmp1 = ntohl(*p++); | ||
129 | if (tmp != (u32)-1 && tmp1 != (u32)-1) { | ||
130 | iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; | ||
131 | iap->ia_mtime.tv_sec = tmp; | ||
132 | iap->ia_mtime.tv_nsec = tmp1 * 1000; | ||
133 | /* | ||
134 | * Passing the invalid value useconds=1000000 for mtime | ||
135 | * is a Sun convention for "set both mtime and atime to | ||
136 | * current server time". It's needed to make permissions | ||
137 | * checks for the "touch" program across v2 mounts to | ||
138 | * Solaris and Irix boxes work correctly. See description of | ||
139 | * sattr in section 6.1 of "NFS Illustrated" by | ||
140 | * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 | ||
141 | */ | ||
142 | if (tmp1 == 1000000) | ||
143 | iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET); | ||
144 | } | ||
145 | return p; | ||
146 | } | ||
147 | |||
148 | static inline u32 * | ||
149 | encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | ||
150 | { | ||
151 | struct vfsmount *mnt = fhp->fh_export->ex_mnt; | ||
152 | struct dentry *dentry = fhp->fh_dentry; | ||
153 | struct kstat stat; | ||
154 | int type; | ||
155 | struct timespec time; | ||
156 | |||
157 | vfs_getattr(mnt, dentry, &stat); | ||
158 | type = (stat.mode & S_IFMT); | ||
159 | |||
160 | *p++ = htonl(nfs_ftypes[type >> 12]); | ||
161 | *p++ = htonl((u32) stat.mode); | ||
162 | *p++ = htonl((u32) stat.nlink); | ||
163 | *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid)); | ||
164 | *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid)); | ||
165 | |||
166 | if (S_ISLNK(type) && stat.size > NFS_MAXPATHLEN) { | ||
167 | *p++ = htonl(NFS_MAXPATHLEN); | ||
168 | } else { | ||
169 | *p++ = htonl((u32) stat.size); | ||
170 | } | ||
171 | *p++ = htonl((u32) stat.blksize); | ||
172 | if (S_ISCHR(type) || S_ISBLK(type)) | ||
173 | *p++ = htonl(new_encode_dev(stat.rdev)); | ||
174 | else | ||
175 | *p++ = htonl(0xffffffff); | ||
176 | *p++ = htonl((u32) stat.blocks); | ||
177 | if (is_fsid(fhp, rqstp->rq_reffh)) | ||
178 | *p++ = htonl((u32) fhp->fh_export->ex_fsid); | ||
179 | else | ||
180 | *p++ = htonl(new_encode_dev(stat.dev)); | ||
181 | *p++ = htonl((u32) stat.ino); | ||
182 | *p++ = htonl((u32) stat.atime.tv_sec); | ||
183 | *p++ = htonl(stat.atime.tv_nsec ? stat.atime.tv_nsec / 1000 : 0); | ||
184 | lease_get_mtime(dentry->d_inode, &time); | ||
185 | *p++ = htonl((u32) time.tv_sec); | ||
186 | *p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); | ||
187 | *p++ = htonl((u32) stat.ctime.tv_sec); | ||
188 | *p++ = htonl(stat.ctime.tv_nsec ? stat.ctime.tv_nsec / 1000 : 0); | ||
189 | |||
190 | return p; | ||
191 | } | ||
192 | |||
193 | |||
194 | /* | ||
195 | * XDR decode functions | ||
196 | */ | ||
197 | int | ||
198 | nfssvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) | ||
199 | { | ||
200 | return xdr_argsize_check(rqstp, p); | ||
201 | } | ||
202 | |||
203 | int | ||
204 | nfssvc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct nfsd_fhandle *args) | ||
205 | { | ||
206 | if (!(p = decode_fh(p, &args->fh))) | ||
207 | return 0; | ||
208 | return xdr_argsize_check(rqstp, p); | ||
209 | } | ||
210 | |||
211 | int | ||
212 | nfssvc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p, | ||
213 | struct nfsd_sattrargs *args) | ||
214 | { | ||
215 | if (!(p = decode_fh(p, &args->fh)) | ||
216 | || !(p = decode_sattr(p, &args->attrs))) | ||
217 | return 0; | ||
218 | |||
219 | return xdr_argsize_check(rqstp, p); | ||
220 | } | ||
221 | |||
222 | int | ||
223 | nfssvc_decode_diropargs(struct svc_rqst *rqstp, u32 *p, | ||
224 | struct nfsd_diropargs *args) | ||
225 | { | ||
226 | if (!(p = decode_fh(p, &args->fh)) | ||
227 | || !(p = decode_filename(p, &args->name, &args->len))) | ||
228 | return 0; | ||
229 | |||
230 | return xdr_argsize_check(rqstp, p); | ||
231 | } | ||
232 | |||
233 | int | ||
234 | nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p, | ||
235 | struct nfsd_readargs *args) | ||
236 | { | ||
237 | unsigned int len; | ||
238 | int v,pn; | ||
239 | if (!(p = decode_fh(p, &args->fh))) | ||
240 | return 0; | ||
241 | |||
242 | args->offset = ntohl(*p++); | ||
243 | len = args->count = ntohl(*p++); | ||
244 | p++; /* totalcount - unused */ | ||
245 | |||
246 | if (len > NFSSVC_MAXBLKSIZE) | ||
247 | len = NFSSVC_MAXBLKSIZE; | ||
248 | |||
249 | /* set up somewhere to store response. | ||
250 | * We take pages, put them on reslist and include in iovec | ||
251 | */ | ||
252 | v=0; | ||
253 | while (len > 0) { | ||
254 | pn=rqstp->rq_resused; | ||
255 | svc_take_page(rqstp); | ||
256 | args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); | ||
257 | args->vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; | ||
258 | len -= args->vec[v].iov_len; | ||
259 | v++; | ||
260 | } | ||
261 | args->vlen = v; | ||
262 | return xdr_argsize_check(rqstp, p); | ||
263 | } | ||
264 | |||
265 | int | ||
266 | nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, | ||
267 | struct nfsd_writeargs *args) | ||
268 | { | ||
269 | unsigned int len; | ||
270 | int v; | ||
271 | if (!(p = decode_fh(p, &args->fh))) | ||
272 | return 0; | ||
273 | |||
274 | p++; /* beginoffset */ | ||
275 | args->offset = ntohl(*p++); /* offset */ | ||
276 | p++; /* totalcount */ | ||
277 | len = args->len = ntohl(*p++); | ||
278 | args->vec[0].iov_base = (void*)p; | ||
279 | args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - | ||
280 | (((void*)p) - rqstp->rq_arg.head[0].iov_base); | ||
281 | if (len > NFSSVC_MAXBLKSIZE) | ||
282 | len = NFSSVC_MAXBLKSIZE; | ||
283 | v = 0; | ||
284 | while (len > args->vec[v].iov_len) { | ||
285 | len -= args->vec[v].iov_len; | ||
286 | v++; | ||
287 | args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); | ||
288 | args->vec[v].iov_len = PAGE_SIZE; | ||
289 | } | ||
290 | args->vec[v].iov_len = len; | ||
291 | args->vlen = v+1; | ||
292 | return args->vec[0].iov_len > 0; | ||
293 | } | ||
294 | |||
295 | int | ||
296 | nfssvc_decode_createargs(struct svc_rqst *rqstp, u32 *p, | ||
297 | struct nfsd_createargs *args) | ||
298 | { | ||
299 | if (!(p = decode_fh(p, &args->fh)) | ||
300 | || !(p = decode_filename(p, &args->name, &args->len)) | ||
301 | || !(p = decode_sattr(p, &args->attrs))) | ||
302 | return 0; | ||
303 | |||
304 | return xdr_argsize_check(rqstp, p); | ||
305 | } | ||
306 | |||
307 | int | ||
308 | nfssvc_decode_renameargs(struct svc_rqst *rqstp, u32 *p, | ||
309 | struct nfsd_renameargs *args) | ||
310 | { | ||
311 | if (!(p = decode_fh(p, &args->ffh)) | ||
312 | || !(p = decode_filename(p, &args->fname, &args->flen)) | ||
313 | || !(p = decode_fh(p, &args->tfh)) | ||
314 | || !(p = decode_filename(p, &args->tname, &args->tlen))) | ||
315 | return 0; | ||
316 | |||
317 | return xdr_argsize_check(rqstp, p); | ||
318 | } | ||
319 | |||
320 | int | ||
321 | nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, struct nfsd_readlinkargs *args) | ||
322 | { | ||
323 | if (!(p = decode_fh(p, &args->fh))) | ||
324 | return 0; | ||
325 | svc_take_page(rqstp); | ||
326 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); | ||
327 | |||
328 | return xdr_argsize_check(rqstp, p); | ||
329 | } | ||
330 | |||
331 | int | ||
332 | nfssvc_decode_linkargs(struct svc_rqst *rqstp, u32 *p, | ||
333 | struct nfsd_linkargs *args) | ||
334 | { | ||
335 | if (!(p = decode_fh(p, &args->ffh)) | ||
336 | || !(p = decode_fh(p, &args->tfh)) | ||
337 | || !(p = decode_filename(p, &args->tname, &args->tlen))) | ||
338 | return 0; | ||
339 | |||
340 | return xdr_argsize_check(rqstp, p); | ||
341 | } | ||
342 | |||
343 | int | ||
344 | nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, | ||
345 | struct nfsd_symlinkargs *args) | ||
346 | { | ||
347 | if (!(p = decode_fh(p, &args->ffh)) | ||
348 | || !(p = decode_filename(p, &args->fname, &args->flen)) | ||
349 | || !(p = decode_pathname(p, &args->tname, &args->tlen)) | ||
350 | || !(p = decode_sattr(p, &args->attrs))) | ||
351 | return 0; | ||
352 | |||
353 | return xdr_argsize_check(rqstp, p); | ||
354 | } | ||
355 | |||
356 | int | ||
357 | nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, | ||
358 | struct nfsd_readdirargs *args) | ||
359 | { | ||
360 | if (!(p = decode_fh(p, &args->fh))) | ||
361 | return 0; | ||
362 | args->cookie = ntohl(*p++); | ||
363 | args->count = ntohl(*p++); | ||
364 | if (args->count > PAGE_SIZE) | ||
365 | args->count = PAGE_SIZE; | ||
366 | |||
367 | svc_take_page(rqstp); | ||
368 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); | ||
369 | |||
370 | return xdr_argsize_check(rqstp, p); | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * XDR encode functions | ||
375 | */ | ||
376 | int | ||
377 | nfssvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) | ||
378 | { | ||
379 | return xdr_ressize_check(rqstp, p); | ||
380 | } | ||
381 | |||
382 | int | ||
383 | nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, | ||
384 | struct nfsd_attrstat *resp) | ||
385 | { | ||
386 | p = encode_fattr(rqstp, p, &resp->fh); | ||
387 | return xdr_ressize_check(rqstp, p); | ||
388 | } | ||
389 | |||
390 | int | ||
391 | nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p, | ||
392 | struct nfsd_diropres *resp) | ||
393 | { | ||
394 | p = encode_fh(p, &resp->fh); | ||
395 | p = encode_fattr(rqstp, p, &resp->fh); | ||
396 | return xdr_ressize_check(rqstp, p); | ||
397 | } | ||
398 | |||
399 | int | ||
400 | nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, | ||
401 | struct nfsd_readlinkres *resp) | ||
402 | { | ||
403 | *p++ = htonl(resp->len); | ||
404 | xdr_ressize_check(rqstp, p); | ||
405 | rqstp->rq_res.page_len = resp->len; | ||
406 | if (resp->len & 3) { | ||
407 | /* need to pad the tail */ | ||
408 | rqstp->rq_restailpage = 0; | ||
409 | rqstp->rq_res.tail[0].iov_base = p; | ||
410 | *p = 0; | ||
411 | rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); | ||
412 | } | ||
413 | return 1; | ||
414 | } | ||
415 | |||
416 | int | ||
417 | nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, | ||
418 | struct nfsd_readres *resp) | ||
419 | { | ||
420 | p = encode_fattr(rqstp, p, &resp->fh); | ||
421 | *p++ = htonl(resp->count); | ||
422 | xdr_ressize_check(rqstp, p); | ||
423 | |||
424 | /* now update rqstp->rq_res to reflect data aswell */ | ||
425 | rqstp->rq_res.page_len = resp->count; | ||
426 | if (resp->count & 3) { | ||
427 | /* need to pad the tail */ | ||
428 | rqstp->rq_restailpage = 0; | ||
429 | rqstp->rq_res.tail[0].iov_base = p; | ||
430 | *p = 0; | ||
431 | rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3); | ||
432 | } | ||
433 | return 1; | ||
434 | } | ||
435 | |||
436 | int | ||
437 | nfssvc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, | ||
438 | struct nfsd_readdirres *resp) | ||
439 | { | ||
440 | xdr_ressize_check(rqstp, p); | ||
441 | p = resp->buffer; | ||
442 | *p++ = 0; /* no more entries */ | ||
443 | *p++ = htonl((resp->common.err == nfserr_eof)); | ||
444 | rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1; | ||
445 | |||
446 | return 1; | ||
447 | } | ||
448 | |||
449 | int | ||
450 | nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, | ||
451 | struct nfsd_statfsres *resp) | ||
452 | { | ||
453 | struct kstatfs *stat = &resp->stats; | ||
454 | |||
455 | *p++ = htonl(NFSSVC_MAXBLKSIZE); /* max transfer size */ | ||
456 | *p++ = htonl(stat->f_bsize); | ||
457 | *p++ = htonl(stat->f_blocks); | ||
458 | *p++ = htonl(stat->f_bfree); | ||
459 | *p++ = htonl(stat->f_bavail); | ||
460 | return xdr_ressize_check(rqstp, p); | ||
461 | } | ||
462 | |||
463 | int | ||
464 | nfssvc_encode_entry(struct readdir_cd *ccd, const char *name, | ||
465 | int namlen, loff_t offset, ino_t ino, unsigned int d_type) | ||
466 | { | ||
467 | struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common); | ||
468 | u32 *p = cd->buffer; | ||
469 | int buflen, slen; | ||
470 | |||
471 | /* | ||
472 | dprintk("nfsd: entry(%.*s off %ld ino %ld)\n", | ||
473 | namlen, name, offset, ino); | ||
474 | */ | ||
475 | |||
476 | if (offset > ~((u32) 0)) { | ||
477 | cd->common.err = nfserr_fbig; | ||
478 | return -EINVAL; | ||
479 | } | ||
480 | if (cd->offset) | ||
481 | *cd->offset = htonl(offset); | ||
482 | if (namlen > NFS2_MAXNAMLEN) | ||
483 | namlen = NFS2_MAXNAMLEN;/* truncate filename */ | ||
484 | |||
485 | slen = XDR_QUADLEN(namlen); | ||
486 | if ((buflen = cd->buflen - slen - 4) < 0) { | ||
487 | cd->common.err = nfserr_toosmall; | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | *p++ = xdr_one; /* mark entry present */ | ||
491 | *p++ = htonl((u32) ino); /* file id */ | ||
492 | p = xdr_encode_array(p, name, namlen);/* name length & name */ | ||
493 | cd->offset = p; /* remember pointer */ | ||
494 | *p++ = ~(u32) 0; /* offset of next entry */ | ||
495 | |||
496 | cd->buflen = buflen; | ||
497 | cd->buffer = p; | ||
498 | cd->common.err = nfs_ok; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | /* | ||
503 | * XDR release functions | ||
504 | */ | ||
505 | int | ||
506 | nfssvc_release_fhandle(struct svc_rqst *rqstp, u32 *p, | ||
507 | struct nfsd_fhandle *resp) | ||
508 | { | ||
509 | fh_put(&resp->fh); | ||
510 | return 1; | ||
511 | } | ||
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c new file mode 100644 index 000000000000..1cf955bcc526 --- /dev/null +++ b/fs/nfsd/stats.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * linux/fs/nfsd/stats.c | ||
3 | * | ||
4 | * procfs-based user access to knfsd statistics | ||
5 | * | ||
6 | * /proc/net/rpc/nfsd | ||
7 | * | ||
8 | * Format: | ||
9 | * rc <hits> <misses> <nocache> | ||
10 | * Statistsics for the reply cache | ||
11 | * fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache> | ||
12 | * statistics for filehandle lookup | ||
13 | * io <bytes-read> <bytes-writtten> | ||
14 | * statistics for IO throughput | ||
15 | * th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%> | ||
16 | * time (seconds) when nfsd thread usage above thresholds | ||
17 | * and number of times that all threads were in use | ||
18 | * ra cache-size <10% <20% <30% ... <100% not-found | ||
19 | * number of times that read-ahead entry was found that deep in | ||
20 | * the cache. | ||
21 | * plus generic RPC stats (see net/sunrpc/stats.c) | ||
22 | * | ||
23 | * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <linux/proc_fs.h> | ||
29 | #include <linux/seq_file.h> | ||
30 | #include <linux/stat.h> | ||
31 | #include <linux/module.h> | ||
32 | |||
33 | #include <linux/sunrpc/svc.h> | ||
34 | #include <linux/sunrpc/stats.h> | ||
35 | #include <linux/nfsd/nfsd.h> | ||
36 | #include <linux/nfsd/stats.h> | ||
37 | |||
38 | struct nfsd_stats nfsdstats; | ||
39 | struct svc_stat nfsd_svcstats = { | ||
40 | .program = &nfsd_program, | ||
41 | }; | ||
42 | |||
43 | static int nfsd_proc_show(struct seq_file *seq, void *v) | ||
44 | { | ||
45 | int i; | ||
46 | |||
47 | seq_printf(seq, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n", | ||
48 | nfsdstats.rchits, | ||
49 | nfsdstats.rcmisses, | ||
50 | nfsdstats.rcnocache, | ||
51 | nfsdstats.fh_stale, | ||
52 | nfsdstats.fh_lookup, | ||
53 | nfsdstats.fh_anon, | ||
54 | nfsdstats.fh_nocache_dir, | ||
55 | nfsdstats.fh_nocache_nondir, | ||
56 | nfsdstats.io_read, | ||
57 | nfsdstats.io_write); | ||
58 | /* thread usage: */ | ||
59 | seq_printf(seq, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt); | ||
60 | for (i=0; i<10; i++) { | ||
61 | unsigned int jifs = nfsdstats.th_usage[i]; | ||
62 | unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ; | ||
63 | seq_printf(seq, " %u.%03u", sec, msec); | ||
64 | } | ||
65 | |||
66 | /* newline and ra-cache */ | ||
67 | seq_printf(seq, "\nra %u", nfsdstats.ra_size); | ||
68 | for (i=0; i<11; i++) | ||
69 | seq_printf(seq, " %u", nfsdstats.ra_depth[i]); | ||
70 | seq_putc(seq, '\n'); | ||
71 | |||
72 | /* show my rpc info */ | ||
73 | svc_seq_show(seq, &nfsd_svcstats); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int nfsd_proc_open(struct inode *inode, struct file *file) | ||
79 | { | ||
80 | return single_open(file, nfsd_proc_show, NULL); | ||
81 | } | ||
82 | |||
83 | static struct file_operations nfsd_proc_fops = { | ||
84 | .owner = THIS_MODULE, | ||
85 | .open = nfsd_proc_open, | ||
86 | .read = seq_read, | ||
87 | .llseek = seq_lseek, | ||
88 | .release = single_release, | ||
89 | }; | ||
90 | |||
91 | void | ||
92 | nfsd_stat_init(void) | ||
93 | { | ||
94 | svc_proc_register(&nfsd_svcstats, &nfsd_proc_fops); | ||
95 | } | ||
96 | |||
97 | void | ||
98 | nfsd_stat_shutdown(void) | ||
99 | { | ||
100 | svc_proc_unregister("nfsd"); | ||
101 | } | ||
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c new file mode 100644 index 000000000000..e3e9d217236e --- /dev/null +++ b/fs/nfsd/vfs.c | |||
@@ -0,0 +1,1859 @@ | |||
1 | #define MSNFS /* HACK HACK */ | ||
2 | /* | ||
3 | * linux/fs/nfsd/vfs.c | ||
4 | * | ||
5 | * File operations used by nfsd. Some of these have been ripped from | ||
6 | * other parts of the kernel because they weren't exported, others | ||
7 | * are partial duplicates with added or changed functionality. | ||
8 | * | ||
9 | * Note that several functions dget() the dentry upon which they want | ||
10 | * to act, most notably those that create directory entries. Response | ||
11 | * dentry's are dput()'d if necessary in the release callback. | ||
12 | * So if you notice code paths that apparently fail to dput() the | ||
13 | * dentry, don't worry--they have been taken care of. | ||
14 | * | ||
15 | * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de> | ||
16 | * Zerocpy NFS support (C) 2002 Hirokazu Takahashi <taka@valinux.co.jp> | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/time.h> | ||
22 | #include <linux/errno.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/file.h> | ||
25 | #include <linux/mount.h> | ||
26 | #include <linux/major.h> | ||
27 | #include <linux/ext2_fs.h> | ||
28 | #include <linux/proc_fs.h> | ||
29 | #include <linux/stat.h> | ||
30 | #include <linux/fcntl.h> | ||
31 | #include <linux/net.h> | ||
32 | #include <linux/unistd.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/pagemap.h> | ||
35 | #include <linux/in.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/namei.h> | ||
38 | #include <linux/vfs.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include <linux/sunrpc/svc.h> | ||
41 | #include <linux/nfsd/nfsd.h> | ||
42 | #ifdef CONFIG_NFSD_V3 | ||
43 | #include <linux/nfs3.h> | ||
44 | #include <linux/nfsd/xdr3.h> | ||
45 | #endif /* CONFIG_NFSD_V3 */ | ||
46 | #include <linux/nfsd/nfsfh.h> | ||
47 | #include <linux/quotaops.h> | ||
48 | #include <linux/dnotify.h> | ||
49 | #ifdef CONFIG_NFSD_V4 | ||
50 | #include <linux/posix_acl.h> | ||
51 | #include <linux/posix_acl_xattr.h> | ||
52 | #include <linux/xattr_acl.h> | ||
53 | #include <linux/xattr.h> | ||
54 | #include <linux/nfs4.h> | ||
55 | #include <linux/nfs4_acl.h> | ||
56 | #include <linux/nfsd_idmap.h> | ||
57 | #include <linux/security.h> | ||
58 | #endif /* CONFIG_NFSD_V4 */ | ||
59 | |||
60 | #include <asm/uaccess.h> | ||
61 | |||
62 | #define NFSDDBG_FACILITY NFSDDBG_FILEOP | ||
63 | #define NFSD_PARANOIA | ||
64 | |||
65 | |||
66 | /* We must ignore files (but only files) which might have mandatory | ||
67 | * locks on them because there is no way to know if the accesser has | ||
68 | * the lock. | ||
69 | */ | ||
70 | #define IS_ISMNDLK(i) (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i)) | ||
71 | |||
72 | /* | ||
73 | * This is a cache of readahead params that help us choose the proper | ||
74 | * readahead strategy. Initially, we set all readahead parameters to 0 | ||
75 | * and let the VFS handle things. | ||
76 | * If you increase the number of cached files very much, you'll need to | ||
77 | * add a hash table here. | ||
78 | */ | ||
79 | struct raparms { | ||
80 | struct raparms *p_next; | ||
81 | unsigned int p_count; | ||
82 | ino_t p_ino; | ||
83 | dev_t p_dev; | ||
84 | int p_set; | ||
85 | struct file_ra_state p_ra; | ||
86 | }; | ||
87 | |||
88 | static struct raparms * raparml; | ||
89 | static struct raparms * raparm_cache; | ||
90 | |||
91 | /* | ||
92 | * Called from nfsd_lookup and encode_dirent. Check if we have crossed | ||
93 | * a mount point. | ||
94 | * Returns -EAGAIN leaving *dpp and *expp unchanged, | ||
95 | * or nfs_ok having possibly changed *dpp and *expp | ||
96 | */ | ||
97 | int | ||
98 | nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, | ||
99 | struct svc_export **expp) | ||
100 | { | ||
101 | struct svc_export *exp = *expp, *exp2 = NULL; | ||
102 | struct dentry *dentry = *dpp; | ||
103 | struct vfsmount *mnt = mntget(exp->ex_mnt); | ||
104 | struct dentry *mounts = dget(dentry); | ||
105 | int err = nfs_ok; | ||
106 | |||
107 | while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); | ||
108 | |||
109 | exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle); | ||
110 | if (IS_ERR(exp2)) { | ||
111 | err = PTR_ERR(exp2); | ||
112 | dput(mounts); | ||
113 | mntput(mnt); | ||
114 | goto out; | ||
115 | } | ||
116 | if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) { | ||
117 | /* successfully crossed mount point */ | ||
118 | exp_put(exp); | ||
119 | *expp = exp2; | ||
120 | dput(dentry); | ||
121 | *dpp = mounts; | ||
122 | } else { | ||
123 | if (exp2) exp_put(exp2); | ||
124 | dput(mounts); | ||
125 | } | ||
126 | mntput(mnt); | ||
127 | out: | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * Look up one component of a pathname. | ||
133 | * N.B. After this call _both_ fhp and resfh need an fh_put | ||
134 | * | ||
135 | * If the lookup would cross a mountpoint, and the mounted filesystem | ||
136 | * is exported to the client with NFSEXP_NOHIDE, then the lookup is | ||
137 | * accepted as it stands and the mounted directory is | ||
138 | * returned. Otherwise the covered directory is returned. | ||
139 | * NOTE: this mountpoint crossing is not supported properly by all | ||
140 | * clients and is explicitly disallowed for NFSv3 | ||
141 | * NeilBrown <neilb@cse.unsw.edu.au> | ||
142 | */ | ||
143 | int | ||
144 | nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | ||
145 | int len, struct svc_fh *resfh) | ||
146 | { | ||
147 | struct svc_export *exp; | ||
148 | struct dentry *dparent; | ||
149 | struct dentry *dentry; | ||
150 | int err; | ||
151 | |||
152 | dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); | ||
153 | |||
154 | /* Obtain dentry and export. */ | ||
155 | err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); | ||
156 | if (err) | ||
157 | return err; | ||
158 | |||
159 | dparent = fhp->fh_dentry; | ||
160 | exp = fhp->fh_export; | ||
161 | exp_get(exp); | ||
162 | |||
163 | err = nfserr_acces; | ||
164 | |||
165 | /* Lookup the name, but don't follow links */ | ||
166 | if (isdotent(name, len)) { | ||
167 | if (len==1) | ||
168 | dentry = dget(dparent); | ||
169 | else if (dparent != exp->ex_dentry) { | ||
170 | dentry = dget_parent(dparent); | ||
171 | } else if (!EX_NOHIDE(exp)) | ||
172 | dentry = dget(dparent); /* .. == . just like at / */ | ||
173 | else { | ||
174 | /* checking mountpoint crossing is very different when stepping up */ | ||
175 | struct svc_export *exp2 = NULL; | ||
176 | struct dentry *dp; | ||
177 | struct vfsmount *mnt = mntget(exp->ex_mnt); | ||
178 | dentry = dget(dparent); | ||
179 | while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) | ||
180 | ; | ||
181 | dp = dget_parent(dentry); | ||
182 | dput(dentry); | ||
183 | dentry = dp; | ||
184 | |||
185 | exp2 = exp_parent(exp->ex_client, mnt, dentry, | ||
186 | &rqstp->rq_chandle); | ||
187 | if (IS_ERR(exp2)) { | ||
188 | err = PTR_ERR(exp2); | ||
189 | dput(dentry); | ||
190 | mntput(mnt); | ||
191 | goto out_nfserr; | ||
192 | } | ||
193 | if (!exp2) { | ||
194 | dput(dentry); | ||
195 | dentry = dget(dparent); | ||
196 | } else { | ||
197 | exp_put(exp); | ||
198 | exp = exp2; | ||
199 | } | ||
200 | mntput(mnt); | ||
201 | } | ||
202 | } else { | ||
203 | fh_lock(fhp); | ||
204 | dentry = lookup_one_len(name, dparent, len); | ||
205 | err = PTR_ERR(dentry); | ||
206 | if (IS_ERR(dentry)) | ||
207 | goto out_nfserr; | ||
208 | /* | ||
209 | * check if we have crossed a mount point ... | ||
210 | */ | ||
211 | if (d_mountpoint(dentry)) { | ||
212 | if ((err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { | ||
213 | dput(dentry); | ||
214 | goto out_nfserr; | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | /* | ||
219 | * Note: we compose the file handle now, but as the | ||
220 | * dentry may be negative, it may need to be updated. | ||
221 | */ | ||
222 | err = fh_compose(resfh, exp, dentry, fhp); | ||
223 | if (!err && !dentry->d_inode) | ||
224 | err = nfserr_noent; | ||
225 | dput(dentry); | ||
226 | out: | ||
227 | exp_put(exp); | ||
228 | return err; | ||
229 | |||
230 | out_nfserr: | ||
231 | err = nfserrno(err); | ||
232 | goto out; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Set various file attributes. | ||
237 | * N.B. After this call fhp needs an fh_put | ||
238 | */ | ||
239 | int | ||
240 | nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | ||
241 | int check_guard, time_t guardtime) | ||
242 | { | ||
243 | struct dentry *dentry; | ||
244 | struct inode *inode; | ||
245 | int accmode = MAY_SATTR; | ||
246 | int ftype = 0; | ||
247 | int imode; | ||
248 | int err; | ||
249 | int size_change = 0; | ||
250 | |||
251 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) | ||
252 | accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE; | ||
253 | if (iap->ia_valid & ATTR_SIZE) | ||
254 | ftype = S_IFREG; | ||
255 | |||
256 | /* Get inode */ | ||
257 | err = fh_verify(rqstp, fhp, ftype, accmode); | ||
258 | if (err || !iap->ia_valid) | ||
259 | goto out; | ||
260 | |||
261 | dentry = fhp->fh_dentry; | ||
262 | inode = dentry->d_inode; | ||
263 | |||
264 | /* NFSv2 does not differentiate between "set-[ac]time-to-now" | ||
265 | * which only requires access, and "set-[ac]time-to-X" which | ||
266 | * requires ownership. | ||
267 | * So if it looks like it might be "set both to the same time which | ||
268 | * is close to now", and if inode_change_ok fails, then we | ||
269 | * convert to "set to now" instead of "set to explicit time" | ||
270 | * | ||
271 | * We only call inode_change_ok as the last test as technically | ||
272 | * it is not an interface that we should be using. It is only | ||
273 | * valid if the filesystem does not define it's own i_op->setattr. | ||
274 | */ | ||
275 | #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) | ||
276 | #define MAX_TOUCH_TIME_ERROR (30*60) | ||
277 | if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET | ||
278 | && iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec | ||
279 | ) { | ||
280 | /* Looks probable. Now just make sure time is in the right ballpark. | ||
281 | * Solaris, at least, doesn't seem to care what the time request is. | ||
282 | * We require it be within 30 minutes of now. | ||
283 | */ | ||
284 | time_t delta = iap->ia_atime.tv_sec - get_seconds(); | ||
285 | if (delta<0) delta = -delta; | ||
286 | if (delta < MAX_TOUCH_TIME_ERROR && | ||
287 | inode_change_ok(inode, iap) != 0) { | ||
288 | /* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME | ||
289 | * this will cause notify_change to set these times to "now" | ||
290 | */ | ||
291 | iap->ia_valid &= ~BOTH_TIME_SET; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* The size case is special. It changes the file as well as the attributes. */ | ||
296 | if (iap->ia_valid & ATTR_SIZE) { | ||
297 | if (iap->ia_size < inode->i_size) { | ||
298 | err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); | ||
299 | if (err) | ||
300 | goto out; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * If we are changing the size of the file, then | ||
305 | * we need to break all leases. | ||
306 | */ | ||
307 | err = break_lease(inode, FMODE_WRITE | O_NONBLOCK); | ||
308 | if (err == -EWOULDBLOCK) | ||
309 | err = -ETIMEDOUT; | ||
310 | if (err) /* ENOMEM or EWOULDBLOCK */ | ||
311 | goto out_nfserr; | ||
312 | |||
313 | err = get_write_access(inode); | ||
314 | if (err) | ||
315 | goto out_nfserr; | ||
316 | |||
317 | size_change = 1; | ||
318 | err = locks_verify_truncate(inode, NULL, iap->ia_size); | ||
319 | if (err) { | ||
320 | put_write_access(inode); | ||
321 | goto out_nfserr; | ||
322 | } | ||
323 | DQUOT_INIT(inode); | ||
324 | } | ||
325 | |||
326 | imode = inode->i_mode; | ||
327 | if (iap->ia_valid & ATTR_MODE) { | ||
328 | iap->ia_mode &= S_IALLUGO; | ||
329 | imode = iap->ia_mode |= (imode & ~S_IALLUGO); | ||
330 | } | ||
331 | |||
332 | /* Revoke setuid/setgid bit on chown/chgrp */ | ||
333 | if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) | ||
334 | iap->ia_valid |= ATTR_KILL_SUID; | ||
335 | if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid) | ||
336 | iap->ia_valid |= ATTR_KILL_SGID; | ||
337 | |||
338 | /* Change the attributes. */ | ||
339 | |||
340 | iap->ia_valid |= ATTR_CTIME; | ||
341 | |||
342 | err = nfserr_notsync; | ||
343 | if (!check_guard || guardtime == inode->i_ctime.tv_sec) { | ||
344 | fh_lock(fhp); | ||
345 | err = notify_change(dentry, iap); | ||
346 | err = nfserrno(err); | ||
347 | fh_unlock(fhp); | ||
348 | } | ||
349 | if (size_change) | ||
350 | put_write_access(inode); | ||
351 | if (!err) | ||
352 | if (EX_ISSYNC(fhp->fh_export)) | ||
353 | write_inode_now(inode, 1); | ||
354 | out: | ||
355 | return err; | ||
356 | |||
357 | out_nfserr: | ||
358 | err = nfserrno(err); | ||
359 | goto out; | ||
360 | } | ||
361 | |||
362 | #if defined(CONFIG_NFSD_V4) | ||
363 | |||
364 | static int | ||
365 | set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) | ||
366 | { | ||
367 | int len; | ||
368 | size_t buflen; | ||
369 | char *buf = NULL; | ||
370 | int error = 0; | ||
371 | struct inode *inode = dentry->d_inode; | ||
372 | |||
373 | buflen = posix_acl_xattr_size(pacl->a_count); | ||
374 | buf = kmalloc(buflen, GFP_KERNEL); | ||
375 | error = -ENOMEM; | ||
376 | if (buf == NULL) | ||
377 | goto out; | ||
378 | |||
379 | len = posix_acl_to_xattr(pacl, buf, buflen); | ||
380 | if (len < 0) { | ||
381 | error = len; | ||
382 | goto out; | ||
383 | } | ||
384 | |||
385 | error = -EOPNOTSUPP; | ||
386 | if (inode->i_op && inode->i_op->setxattr) { | ||
387 | down(&inode->i_sem); | ||
388 | security_inode_setxattr(dentry, key, buf, len, 0); | ||
389 | error = inode->i_op->setxattr(dentry, key, buf, len, 0); | ||
390 | if (!error) | ||
391 | security_inode_post_setxattr(dentry, key, buf, len, 0); | ||
392 | up(&inode->i_sem); | ||
393 | } | ||
394 | out: | ||
395 | kfree(buf); | ||
396 | return error; | ||
397 | } | ||
398 | |||
399 | int | ||
400 | nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
401 | struct nfs4_acl *acl) | ||
402 | { | ||
403 | int error; | ||
404 | struct dentry *dentry; | ||
405 | struct inode *inode; | ||
406 | struct posix_acl *pacl = NULL, *dpacl = NULL; | ||
407 | unsigned int flags = 0; | ||
408 | |||
409 | /* Get inode */ | ||
410 | error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); | ||
411 | if (error) | ||
412 | goto out; | ||
413 | |||
414 | dentry = fhp->fh_dentry; | ||
415 | inode = dentry->d_inode; | ||
416 | if (S_ISDIR(inode->i_mode)) | ||
417 | flags = NFS4_ACL_DIR; | ||
418 | |||
419 | error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); | ||
420 | if (error == -EINVAL) { | ||
421 | error = nfserr_attrnotsupp; | ||
422 | goto out; | ||
423 | } else if (error < 0) | ||
424 | goto out_nfserr; | ||
425 | |||
426 | if (pacl) { | ||
427 | error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS); | ||
428 | if (error < 0) | ||
429 | goto out_nfserr; | ||
430 | } | ||
431 | |||
432 | if (dpacl) { | ||
433 | error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT); | ||
434 | if (error < 0) | ||
435 | goto out_nfserr; | ||
436 | } | ||
437 | |||
438 | error = nfs_ok; | ||
439 | |||
440 | out: | ||
441 | posix_acl_release(pacl); | ||
442 | posix_acl_release(dpacl); | ||
443 | return (error); | ||
444 | out_nfserr: | ||
445 | error = nfserrno(error); | ||
446 | goto out; | ||
447 | } | ||
448 | |||
449 | static struct posix_acl * | ||
450 | _get_posix_acl(struct dentry *dentry, char *key) | ||
451 | { | ||
452 | struct inode *inode = dentry->d_inode; | ||
453 | char *buf = NULL; | ||
454 | int buflen, error = 0; | ||
455 | struct posix_acl *pacl = NULL; | ||
456 | |||
457 | error = -EOPNOTSUPP; | ||
458 | if (inode->i_op == NULL) | ||
459 | goto out_err; | ||
460 | if (inode->i_op->getxattr == NULL) | ||
461 | goto out_err; | ||
462 | |||
463 | error = security_inode_getxattr(dentry, key); | ||
464 | if (error) | ||
465 | goto out_err; | ||
466 | |||
467 | buflen = inode->i_op->getxattr(dentry, key, NULL, 0); | ||
468 | if (buflen <= 0) { | ||
469 | error = buflen < 0 ? buflen : -ENODATA; | ||
470 | goto out_err; | ||
471 | } | ||
472 | |||
473 | buf = kmalloc(buflen, GFP_KERNEL); | ||
474 | if (buf == NULL) { | ||
475 | error = -ENOMEM; | ||
476 | goto out_err; | ||
477 | } | ||
478 | |||
479 | error = inode->i_op->getxattr(dentry, key, buf, buflen); | ||
480 | if (error < 0) | ||
481 | goto out_err; | ||
482 | |||
483 | pacl = posix_acl_from_xattr(buf, buflen); | ||
484 | out: | ||
485 | kfree(buf); | ||
486 | return pacl; | ||
487 | out_err: | ||
488 | pacl = ERR_PTR(error); | ||
489 | goto out; | ||
490 | } | ||
491 | |||
492 | int | ||
493 | nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl) | ||
494 | { | ||
495 | struct inode *inode = dentry->d_inode; | ||
496 | int error = 0; | ||
497 | struct posix_acl *pacl = NULL, *dpacl = NULL; | ||
498 | unsigned int flags = 0; | ||
499 | |||
500 | pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS); | ||
501 | if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA) | ||
502 | pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); | ||
503 | if (IS_ERR(pacl)) { | ||
504 | error = PTR_ERR(pacl); | ||
505 | pacl = NULL; | ||
506 | goto out; | ||
507 | } | ||
508 | |||
509 | if (S_ISDIR(inode->i_mode)) { | ||
510 | dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT); | ||
511 | if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA) | ||
512 | dpacl = NULL; | ||
513 | else if (IS_ERR(dpacl)) { | ||
514 | error = PTR_ERR(dpacl); | ||
515 | dpacl = NULL; | ||
516 | goto out; | ||
517 | } | ||
518 | flags = NFS4_ACL_DIR; | ||
519 | } | ||
520 | |||
521 | *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags); | ||
522 | if (IS_ERR(*acl)) { | ||
523 | error = PTR_ERR(*acl); | ||
524 | *acl = NULL; | ||
525 | } | ||
526 | out: | ||
527 | posix_acl_release(pacl); | ||
528 | posix_acl_release(dpacl); | ||
529 | return error; | ||
530 | } | ||
531 | |||
532 | #endif /* defined(CONFIG_NFS_V4) */ | ||
533 | |||
534 | #ifdef CONFIG_NFSD_V3 | ||
535 | /* | ||
536 | * Check server access rights to a file system object | ||
537 | */ | ||
538 | struct accessmap { | ||
539 | u32 access; | ||
540 | int how; | ||
541 | }; | ||
542 | static struct accessmap nfs3_regaccess[] = { | ||
543 | { NFS3_ACCESS_READ, MAY_READ }, | ||
544 | { NFS3_ACCESS_EXECUTE, MAY_EXEC }, | ||
545 | { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_TRUNC }, | ||
546 | { NFS3_ACCESS_EXTEND, MAY_WRITE }, | ||
547 | |||
548 | { 0, 0 } | ||
549 | }; | ||
550 | |||
551 | static struct accessmap nfs3_diraccess[] = { | ||
552 | { NFS3_ACCESS_READ, MAY_READ }, | ||
553 | { NFS3_ACCESS_LOOKUP, MAY_EXEC }, | ||
554 | { NFS3_ACCESS_MODIFY, MAY_EXEC|MAY_WRITE|MAY_TRUNC }, | ||
555 | { NFS3_ACCESS_EXTEND, MAY_EXEC|MAY_WRITE }, | ||
556 | { NFS3_ACCESS_DELETE, MAY_REMOVE }, | ||
557 | |||
558 | { 0, 0 } | ||
559 | }; | ||
560 | |||
561 | static struct accessmap nfs3_anyaccess[] = { | ||
562 | /* Some clients - Solaris 2.6 at least, make an access call | ||
563 | * to the server to check for access for things like /dev/null | ||
564 | * (which really, the server doesn't care about). So | ||
565 | * We provide simple access checking for them, looking | ||
566 | * mainly at mode bits, and we make sure to ignore read-only | ||
567 | * filesystem checks | ||
568 | */ | ||
569 | { NFS3_ACCESS_READ, MAY_READ }, | ||
570 | { NFS3_ACCESS_EXECUTE, MAY_EXEC }, | ||
571 | { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_LOCAL_ACCESS }, | ||
572 | { NFS3_ACCESS_EXTEND, MAY_WRITE|MAY_LOCAL_ACCESS }, | ||
573 | |||
574 | { 0, 0 } | ||
575 | }; | ||
576 | |||
577 | int | ||
578 | nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *supported) | ||
579 | { | ||
580 | struct accessmap *map; | ||
581 | struct svc_export *export; | ||
582 | struct dentry *dentry; | ||
583 | u32 query, result = 0, sresult = 0; | ||
584 | unsigned int error; | ||
585 | |||
586 | error = fh_verify(rqstp, fhp, 0, MAY_NOP); | ||
587 | if (error) | ||
588 | goto out; | ||
589 | |||
590 | export = fhp->fh_export; | ||
591 | dentry = fhp->fh_dentry; | ||
592 | |||
593 | if (S_ISREG(dentry->d_inode->i_mode)) | ||
594 | map = nfs3_regaccess; | ||
595 | else if (S_ISDIR(dentry->d_inode->i_mode)) | ||
596 | map = nfs3_diraccess; | ||
597 | else | ||
598 | map = nfs3_anyaccess; | ||
599 | |||
600 | |||
601 | query = *access; | ||
602 | for (; map->access; map++) { | ||
603 | if (map->access & query) { | ||
604 | unsigned int err2; | ||
605 | |||
606 | sresult |= map->access; | ||
607 | |||
608 | err2 = nfsd_permission(export, dentry, map->how); | ||
609 | switch (err2) { | ||
610 | case nfs_ok: | ||
611 | result |= map->access; | ||
612 | break; | ||
613 | |||
614 | /* the following error codes just mean the access was not allowed, | ||
615 | * rather than an error occurred */ | ||
616 | case nfserr_rofs: | ||
617 | case nfserr_acces: | ||
618 | case nfserr_perm: | ||
619 | /* simply don't "or" in the access bit. */ | ||
620 | break; | ||
621 | default: | ||
622 | error = err2; | ||
623 | goto out; | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | *access = result; | ||
628 | if (supported) | ||
629 | *supported = sresult; | ||
630 | |||
631 | out: | ||
632 | return error; | ||
633 | } | ||
634 | #endif /* CONFIG_NFSD_V3 */ | ||
635 | |||
636 | |||
637 | |||
638 | /* | ||
639 | * Open an existing file or directory. | ||
640 | * The access argument indicates the type of open (read/write/lock) | ||
641 | * N.B. After this call fhp needs an fh_put | ||
642 | */ | ||
643 | int | ||
644 | nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | ||
645 | int access, struct file **filp) | ||
646 | { | ||
647 | struct dentry *dentry; | ||
648 | struct inode *inode; | ||
649 | int flags = O_RDONLY|O_LARGEFILE, err; | ||
650 | |||
651 | /* | ||
652 | * If we get here, then the client has already done an "open", | ||
653 | * and (hopefully) checked permission - so allow OWNER_OVERRIDE | ||
654 | * in case a chmod has now revoked permission. | ||
655 | */ | ||
656 | err = fh_verify(rqstp, fhp, type, access | MAY_OWNER_OVERRIDE); | ||
657 | if (err) | ||
658 | goto out; | ||
659 | |||
660 | dentry = fhp->fh_dentry; | ||
661 | inode = dentry->d_inode; | ||
662 | |||
663 | /* Disallow write access to files with the append-only bit set | ||
664 | * or any access when mandatory locking enabled | ||
665 | */ | ||
666 | err = nfserr_perm; | ||
667 | if (IS_APPEND(inode) && (access & MAY_WRITE)) | ||
668 | goto out; | ||
669 | if (IS_ISMNDLK(inode)) | ||
670 | goto out; | ||
671 | |||
672 | if (!inode->i_fop) | ||
673 | goto out; | ||
674 | |||
675 | /* | ||
676 | * Check to see if there are any leases on this file. | ||
677 | * This may block while leases are broken. | ||
678 | */ | ||
679 | err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0)); | ||
680 | if (err == -EWOULDBLOCK) | ||
681 | err = -ETIMEDOUT; | ||
682 | if (err) /* NOMEM or WOULDBLOCK */ | ||
683 | goto out_nfserr; | ||
684 | |||
685 | if (access & MAY_WRITE) { | ||
686 | flags = O_WRONLY|O_LARGEFILE; | ||
687 | |||
688 | DQUOT_INIT(inode); | ||
689 | } | ||
690 | *filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_mnt), flags); | ||
691 | if (IS_ERR(*filp)) | ||
692 | err = PTR_ERR(*filp); | ||
693 | out_nfserr: | ||
694 | if (err) | ||
695 | err = nfserrno(err); | ||
696 | out: | ||
697 | return err; | ||
698 | } | ||
699 | |||
700 | /* | ||
701 | * Close a file. | ||
702 | */ | ||
703 | void | ||
704 | nfsd_close(struct file *filp) | ||
705 | { | ||
706 | fput(filp); | ||
707 | } | ||
708 | |||
709 | /* | ||
710 | * Sync a file | ||
711 | * As this calls fsync (not fdatasync) there is no need for a write_inode | ||
712 | * after it. | ||
713 | */ | ||
714 | static inline void nfsd_dosync(struct file *filp, struct dentry *dp, | ||
715 | struct file_operations *fop) | ||
716 | { | ||
717 | struct inode *inode = dp->d_inode; | ||
718 | int (*fsync) (struct file *, struct dentry *, int); | ||
719 | |||
720 | filemap_fdatawrite(inode->i_mapping); | ||
721 | if (fop && (fsync = fop->fsync)) | ||
722 | fsync(filp, dp, 0); | ||
723 | filemap_fdatawait(inode->i_mapping); | ||
724 | } | ||
725 | |||
726 | |||
727 | static void | ||
728 | nfsd_sync(struct file *filp) | ||
729 | { | ||
730 | struct inode *inode = filp->f_dentry->d_inode; | ||
731 | dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); | ||
732 | down(&inode->i_sem); | ||
733 | nfsd_dosync(filp, filp->f_dentry, filp->f_op); | ||
734 | up(&inode->i_sem); | ||
735 | } | ||
736 | |||
737 | static void | ||
738 | nfsd_sync_dir(struct dentry *dp) | ||
739 | { | ||
740 | nfsd_dosync(NULL, dp, dp->d_inode->i_fop); | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * Obtain the readahead parameters for the file | ||
745 | * specified by (dev, ino). | ||
746 | */ | ||
747 | static DEFINE_SPINLOCK(ra_lock); | ||
748 | |||
749 | static inline struct raparms * | ||
750 | nfsd_get_raparms(dev_t dev, ino_t ino) | ||
751 | { | ||
752 | struct raparms *ra, **rap, **frap = NULL; | ||
753 | int depth = 0; | ||
754 | |||
755 | spin_lock(&ra_lock); | ||
756 | for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { | ||
757 | if (ra->p_ino == ino && ra->p_dev == dev) | ||
758 | goto found; | ||
759 | depth++; | ||
760 | if (ra->p_count == 0) | ||
761 | frap = rap; | ||
762 | } | ||
763 | depth = nfsdstats.ra_size*11/10; | ||
764 | if (!frap) { | ||
765 | spin_unlock(&ra_lock); | ||
766 | return NULL; | ||
767 | } | ||
768 | rap = frap; | ||
769 | ra = *frap; | ||
770 | ra->p_dev = dev; | ||
771 | ra->p_ino = ino; | ||
772 | ra->p_set = 0; | ||
773 | found: | ||
774 | if (rap != &raparm_cache) { | ||
775 | *rap = ra->p_next; | ||
776 | ra->p_next = raparm_cache; | ||
777 | raparm_cache = ra; | ||
778 | } | ||
779 | ra->p_count++; | ||
780 | nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++; | ||
781 | spin_unlock(&ra_lock); | ||
782 | return ra; | ||
783 | } | ||
784 | |||
785 | /* | ||
786 | * Grab and keep cached pages assosiated with a file in the svc_rqst | ||
787 | * so that they can be passed to the netowork sendmsg/sendpage routines | ||
788 | * directrly. They will be released after the sending has completed. | ||
789 | */ | ||
790 | static int | ||
791 | nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size) | ||
792 | { | ||
793 | unsigned long count = desc->count; | ||
794 | struct svc_rqst *rqstp = desc->arg.data; | ||
795 | |||
796 | if (size > count) | ||
797 | size = count; | ||
798 | |||
799 | if (rqstp->rq_res.page_len == 0) { | ||
800 | get_page(page); | ||
801 | rqstp->rq_respages[rqstp->rq_resused++] = page; | ||
802 | rqstp->rq_res.page_base = offset; | ||
803 | rqstp->rq_res.page_len = size; | ||
804 | } else if (page != rqstp->rq_respages[rqstp->rq_resused-1]) { | ||
805 | get_page(page); | ||
806 | rqstp->rq_respages[rqstp->rq_resused++] = page; | ||
807 | rqstp->rq_res.page_len += size; | ||
808 | } else { | ||
809 | rqstp->rq_res.page_len += size; | ||
810 | } | ||
811 | |||
812 | desc->count = count - size; | ||
813 | desc->written += size; | ||
814 | return size; | ||
815 | } | ||
816 | |||
817 | static inline int | ||
818 | nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | ||
819 | loff_t offset, struct kvec *vec, int vlen, unsigned long *count) | ||
820 | { | ||
821 | struct inode *inode; | ||
822 | struct raparms *ra; | ||
823 | mm_segment_t oldfs; | ||
824 | int err; | ||
825 | |||
826 | err = nfserr_perm; | ||
827 | inode = file->f_dentry->d_inode; | ||
828 | #ifdef MSNFS | ||
829 | if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && | ||
830 | (!lock_may_read(inode, offset, *count))) | ||
831 | goto out; | ||
832 | #endif | ||
833 | |||
834 | /* Get readahead parameters */ | ||
835 | ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino); | ||
836 | |||
837 | if (ra && ra->p_set) | ||
838 | file->f_ra = ra->p_ra; | ||
839 | |||
840 | if (file->f_op->sendfile) { | ||
841 | svc_pushback_unused_pages(rqstp); | ||
842 | err = file->f_op->sendfile(file, &offset, *count, | ||
843 | nfsd_read_actor, rqstp); | ||
844 | } else { | ||
845 | oldfs = get_fs(); | ||
846 | set_fs(KERNEL_DS); | ||
847 | err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset); | ||
848 | set_fs(oldfs); | ||
849 | } | ||
850 | |||
851 | /* Write back readahead params */ | ||
852 | if (ra) { | ||
853 | spin_lock(&ra_lock); | ||
854 | ra->p_ra = file->f_ra; | ||
855 | ra->p_set = 1; | ||
856 | ra->p_count--; | ||
857 | spin_unlock(&ra_lock); | ||
858 | } | ||
859 | |||
860 | if (err >= 0) { | ||
861 | nfsdstats.io_read += err; | ||
862 | *count = err; | ||
863 | err = 0; | ||
864 | dnotify_parent(file->f_dentry, DN_ACCESS); | ||
865 | } else | ||
866 | err = nfserrno(err); | ||
867 | out: | ||
868 | return err; | ||
869 | } | ||
870 | |||
871 | static inline int | ||
872 | nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | ||
873 | loff_t offset, struct kvec *vec, int vlen, | ||
874 | unsigned long cnt, int *stablep) | ||
875 | { | ||
876 | struct svc_export *exp; | ||
877 | struct dentry *dentry; | ||
878 | struct inode *inode; | ||
879 | mm_segment_t oldfs; | ||
880 | int err = 0; | ||
881 | int stable = *stablep; | ||
882 | |||
883 | err = nfserr_perm; | ||
884 | |||
885 | #ifdef MSNFS | ||
886 | if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && | ||
887 | (!lock_may_write(file->f_dentry->d_inode, offset, cnt))) | ||
888 | goto out; | ||
889 | #endif | ||
890 | |||
891 | dentry = file->f_dentry; | ||
892 | inode = dentry->d_inode; | ||
893 | exp = fhp->fh_export; | ||
894 | |||
895 | /* | ||
896 | * Request sync writes if | ||
897 | * - the sync export option has been set, or | ||
898 | * - the client requested O_SYNC behavior (NFSv3 feature). | ||
899 | * - The file system doesn't support fsync(). | ||
900 | * When gathered writes have been configured for this volume, | ||
901 | * flushing the data to disk is handled separately below. | ||
902 | */ | ||
903 | |||
904 | if (file->f_op->fsync == 0) {/* COMMIT3 cannot work */ | ||
905 | stable = 2; | ||
906 | *stablep = 2; /* FILE_SYNC */ | ||
907 | } | ||
908 | |||
909 | if (!EX_ISSYNC(exp)) | ||
910 | stable = 0; | ||
911 | if (stable && !EX_WGATHER(exp)) | ||
912 | file->f_flags |= O_SYNC; | ||
913 | |||
914 | /* Write the data. */ | ||
915 | oldfs = get_fs(); set_fs(KERNEL_DS); | ||
916 | err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); | ||
917 | set_fs(oldfs); | ||
918 | if (err >= 0) { | ||
919 | nfsdstats.io_write += cnt; | ||
920 | dnotify_parent(file->f_dentry, DN_MODIFY); | ||
921 | } | ||
922 | |||
923 | /* clear setuid/setgid flag after write */ | ||
924 | if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { | ||
925 | struct iattr ia; | ||
926 | ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID; | ||
927 | |||
928 | down(&inode->i_sem); | ||
929 | notify_change(dentry, &ia); | ||
930 | up(&inode->i_sem); | ||
931 | } | ||
932 | |||
933 | if (err >= 0 && stable) { | ||
934 | static ino_t last_ino; | ||
935 | static dev_t last_dev; | ||
936 | |||
937 | /* | ||
938 | * Gathered writes: If another process is currently | ||
939 | * writing to the file, there's a high chance | ||
940 | * this is another nfsd (triggered by a bulk write | ||
941 | * from a client's biod). Rather than syncing the | ||
942 | * file with each write request, we sleep for 10 msec. | ||
943 | * | ||
944 | * I don't know if this roughly approximates | ||
945 | * C. Juszak's idea of gathered writes, but it's a | ||
946 | * nice and simple solution (IMHO), and it seems to | ||
947 | * work:-) | ||
948 | */ | ||
949 | if (EX_WGATHER(exp)) { | ||
950 | if (atomic_read(&inode->i_writecount) > 1 | ||
951 | || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) { | ||
952 | dprintk("nfsd: write defer %d\n", current->pid); | ||
953 | msleep(10); | ||
954 | dprintk("nfsd: write resume %d\n", current->pid); | ||
955 | } | ||
956 | |||
957 | if (inode->i_state & I_DIRTY) { | ||
958 | dprintk("nfsd: write sync %d\n", current->pid); | ||
959 | nfsd_sync(file); | ||
960 | } | ||
961 | #if 0 | ||
962 | wake_up(&inode->i_wait); | ||
963 | #endif | ||
964 | } | ||
965 | last_ino = inode->i_ino; | ||
966 | last_dev = inode->i_sb->s_dev; | ||
967 | } | ||
968 | |||
969 | dprintk("nfsd: write complete err=%d\n", err); | ||
970 | if (err >= 0) | ||
971 | err = 0; | ||
972 | else | ||
973 | err = nfserrno(err); | ||
974 | out: | ||
975 | return err; | ||
976 | } | ||
977 | |||
978 | /* | ||
979 | * Read data from a file. count must contain the requested read count | ||
980 | * on entry. On return, *count contains the number of bytes actually read. | ||
981 | * N.B. After this call fhp needs an fh_put | ||
982 | */ | ||
983 | int | ||
984 | nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | ||
985 | loff_t offset, struct kvec *vec, int vlen, | ||
986 | unsigned long *count) | ||
987 | { | ||
988 | int err; | ||
989 | |||
990 | if (file) { | ||
991 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | ||
992 | MAY_READ|MAY_OWNER_OVERRIDE); | ||
993 | if (err) | ||
994 | goto out; | ||
995 | err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); | ||
996 | } else { | ||
997 | err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file); | ||
998 | if (err) | ||
999 | goto out; | ||
1000 | err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); | ||
1001 | nfsd_close(file); | ||
1002 | } | ||
1003 | out: | ||
1004 | return err; | ||
1005 | } | ||
1006 | |||
1007 | /* | ||
1008 | * Write data to a file. | ||
1009 | * The stable flag requests synchronous writes. | ||
1010 | * N.B. After this call fhp needs an fh_put | ||
1011 | */ | ||
1012 | int | ||
1013 | nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | ||
1014 | loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, | ||
1015 | int *stablep) | ||
1016 | { | ||
1017 | int err = 0; | ||
1018 | |||
1019 | if (file) { | ||
1020 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | ||
1021 | MAY_WRITE|MAY_OWNER_OVERRIDE); | ||
1022 | if (err) | ||
1023 | goto out; | ||
1024 | err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt, | ||
1025 | stablep); | ||
1026 | } else { | ||
1027 | err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file); | ||
1028 | if (err) | ||
1029 | goto out; | ||
1030 | |||
1031 | if (cnt) | ||
1032 | err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, | ||
1033 | cnt, stablep); | ||
1034 | nfsd_close(file); | ||
1035 | } | ||
1036 | out: | ||
1037 | return err; | ||
1038 | } | ||
1039 | |||
1040 | #ifdef CONFIG_NFSD_V3 | ||
1041 | /* | ||
1042 | * Commit all pending writes to stable storage. | ||
1043 | * Strictly speaking, we could sync just the indicated file region here, | ||
1044 | * but there's currently no way we can ask the VFS to do so. | ||
1045 | * | ||
1046 | * Unfortunately we cannot lock the file to make sure we return full WCC | ||
1047 | * data to the client, as locking happens lower down in the filesystem. | ||
1048 | */ | ||
1049 | int | ||
1050 | nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
1051 | loff_t offset, unsigned long count) | ||
1052 | { | ||
1053 | struct file *file; | ||
1054 | int err; | ||
1055 | |||
1056 | if ((u64)count > ~(u64)offset) | ||
1057 | return nfserr_inval; | ||
1058 | |||
1059 | if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0) | ||
1060 | return err; | ||
1061 | if (EX_ISSYNC(fhp->fh_export)) { | ||
1062 | if (file->f_op && file->f_op->fsync) { | ||
1063 | nfsd_sync(file); | ||
1064 | } else { | ||
1065 | err = nfserr_notsupp; | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | nfsd_close(file); | ||
1070 | return err; | ||
1071 | } | ||
1072 | #endif /* CONFIG_NFSD_V3 */ | ||
1073 | |||
1074 | /* | ||
1075 | * Create a file (regular, directory, device, fifo); UNIX sockets | ||
1076 | * not yet implemented. | ||
1077 | * If the response fh has been verified, the parent directory should | ||
1078 | * already be locked. Note that the parent directory is left locked. | ||
1079 | * | ||
1080 | * N.B. Every call to nfsd_create needs an fh_put for _both_ fhp and resfhp | ||
1081 | */ | ||
1082 | int | ||
1083 | nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
1084 | char *fname, int flen, struct iattr *iap, | ||
1085 | int type, dev_t rdev, struct svc_fh *resfhp) | ||
1086 | { | ||
1087 | struct dentry *dentry, *dchild = NULL; | ||
1088 | struct inode *dirp; | ||
1089 | int err; | ||
1090 | |||
1091 | err = nfserr_perm; | ||
1092 | if (!flen) | ||
1093 | goto out; | ||
1094 | err = nfserr_exist; | ||
1095 | if (isdotent(fname, flen)) | ||
1096 | goto out; | ||
1097 | |||
1098 | err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); | ||
1099 | if (err) | ||
1100 | goto out; | ||
1101 | |||
1102 | dentry = fhp->fh_dentry; | ||
1103 | dirp = dentry->d_inode; | ||
1104 | |||
1105 | err = nfserr_notdir; | ||
1106 | if(!dirp->i_op || !dirp->i_op->lookup) | ||
1107 | goto out; | ||
1108 | /* | ||
1109 | * Check whether the response file handle has been verified yet. | ||
1110 | * If it has, the parent directory should already be locked. | ||
1111 | */ | ||
1112 | if (!resfhp->fh_dentry) { | ||
1113 | /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */ | ||
1114 | fh_lock(fhp); | ||
1115 | dchild = lookup_one_len(fname, dentry, flen); | ||
1116 | err = PTR_ERR(dchild); | ||
1117 | if (IS_ERR(dchild)) | ||
1118 | goto out_nfserr; | ||
1119 | err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); | ||
1120 | if (err) | ||
1121 | goto out; | ||
1122 | } else { | ||
1123 | /* called from nfsd_proc_create */ | ||
1124 | dchild = dget(resfhp->fh_dentry); | ||
1125 | if (!fhp->fh_locked) { | ||
1126 | /* not actually possible */ | ||
1127 | printk(KERN_ERR | ||
1128 | "nfsd_create: parent %s/%s not locked!\n", | ||
1129 | dentry->d_parent->d_name.name, | ||
1130 | dentry->d_name.name); | ||
1131 | err = -EIO; | ||
1132 | goto out; | ||
1133 | } | ||
1134 | } | ||
1135 | /* | ||
1136 | * Make sure the child dentry is still negative ... | ||
1137 | */ | ||
1138 | err = nfserr_exist; | ||
1139 | if (dchild->d_inode) { | ||
1140 | dprintk("nfsd_create: dentry %s/%s not negative!\n", | ||
1141 | dentry->d_name.name, dchild->d_name.name); | ||
1142 | goto out; | ||
1143 | } | ||
1144 | |||
1145 | if (!(iap->ia_valid & ATTR_MODE)) | ||
1146 | iap->ia_mode = 0; | ||
1147 | iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type; | ||
1148 | |||
1149 | /* | ||
1150 | * Get the dir op function pointer. | ||
1151 | */ | ||
1152 | err = nfserr_perm; | ||
1153 | switch (type) { | ||
1154 | case S_IFREG: | ||
1155 | err = vfs_create(dirp, dchild, iap->ia_mode, NULL); | ||
1156 | break; | ||
1157 | case S_IFDIR: | ||
1158 | err = vfs_mkdir(dirp, dchild, iap->ia_mode); | ||
1159 | break; | ||
1160 | case S_IFCHR: | ||
1161 | case S_IFBLK: | ||
1162 | case S_IFIFO: | ||
1163 | case S_IFSOCK: | ||
1164 | err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev); | ||
1165 | break; | ||
1166 | default: | ||
1167 | printk("nfsd: bad file type %o in nfsd_create\n", type); | ||
1168 | err = -EINVAL; | ||
1169 | } | ||
1170 | if (err < 0) | ||
1171 | goto out_nfserr; | ||
1172 | |||
1173 | if (EX_ISSYNC(fhp->fh_export)) { | ||
1174 | nfsd_sync_dir(dentry); | ||
1175 | write_inode_now(dchild->d_inode, 1); | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | /* Set file attributes. Mode has already been set and | ||
1180 | * setting uid/gid works only for root. Irix appears to | ||
1181 | * send along the gid when it tries to implement setgid | ||
1182 | * directories via NFS. | ||
1183 | */ | ||
1184 | err = 0; | ||
1185 | if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) | ||
1186 | err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); | ||
1187 | /* | ||
1188 | * Update the file handle to get the new inode info. | ||
1189 | */ | ||
1190 | if (!err) | ||
1191 | err = fh_update(resfhp); | ||
1192 | out: | ||
1193 | if (dchild && !IS_ERR(dchild)) | ||
1194 | dput(dchild); | ||
1195 | return err; | ||
1196 | |||
1197 | out_nfserr: | ||
1198 | err = nfserrno(err); | ||
1199 | goto out; | ||
1200 | } | ||
1201 | |||
1202 | #ifdef CONFIG_NFSD_V3 | ||
1203 | /* | ||
1204 | * NFSv3 version of nfsd_create | ||
1205 | */ | ||
1206 | int | ||
1207 | nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
1208 | char *fname, int flen, struct iattr *iap, | ||
1209 | struct svc_fh *resfhp, int createmode, u32 *verifier, | ||
1210 | int *truncp) | ||
1211 | { | ||
1212 | struct dentry *dentry, *dchild = NULL; | ||
1213 | struct inode *dirp; | ||
1214 | int err; | ||
1215 | __u32 v_mtime=0, v_atime=0; | ||
1216 | int v_mode=0; | ||
1217 | |||
1218 | err = nfserr_perm; | ||
1219 | if (!flen) | ||
1220 | goto out; | ||
1221 | err = nfserr_exist; | ||
1222 | if (isdotent(fname, flen)) | ||
1223 | goto out; | ||
1224 | if (!(iap->ia_valid & ATTR_MODE)) | ||
1225 | iap->ia_mode = 0; | ||
1226 | err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); | ||
1227 | if (err) | ||
1228 | goto out; | ||
1229 | |||
1230 | dentry = fhp->fh_dentry; | ||
1231 | dirp = dentry->d_inode; | ||
1232 | |||
1233 | /* Get all the sanity checks out of the way before | ||
1234 | * we lock the parent. */ | ||
1235 | err = nfserr_notdir; | ||
1236 | if(!dirp->i_op || !dirp->i_op->lookup) | ||
1237 | goto out; | ||
1238 | fh_lock(fhp); | ||
1239 | |||
1240 | /* | ||
1241 | * Compose the response file handle. | ||
1242 | */ | ||
1243 | dchild = lookup_one_len(fname, dentry, flen); | ||
1244 | err = PTR_ERR(dchild); | ||
1245 | if (IS_ERR(dchild)) | ||
1246 | goto out_nfserr; | ||
1247 | |||
1248 | err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); | ||
1249 | if (err) | ||
1250 | goto out; | ||
1251 | |||
1252 | if (createmode == NFS3_CREATE_EXCLUSIVE) { | ||
1253 | /* while the verifier would fit in mtime+atime, | ||
1254 | * solaris7 gets confused (bugid 4218508) if these have | ||
1255 | * the high bit set, so we use the mode as well | ||
1256 | */ | ||
1257 | v_mtime = verifier[0]&0x7fffffff; | ||
1258 | v_atime = verifier[1]&0x7fffffff; | ||
1259 | v_mode = S_IFREG | ||
1260 | | ((verifier[0]&0x80000000) >> (32-7)) /* u+x */ | ||
1261 | | ((verifier[1]&0x80000000) >> (32-9)) /* u+r */ | ||
1262 | ; | ||
1263 | } | ||
1264 | |||
1265 | if (dchild->d_inode) { | ||
1266 | err = 0; | ||
1267 | |||
1268 | switch (createmode) { | ||
1269 | case NFS3_CREATE_UNCHECKED: | ||
1270 | if (! S_ISREG(dchild->d_inode->i_mode)) | ||
1271 | err = nfserr_exist; | ||
1272 | else if (truncp) { | ||
1273 | /* in nfsv4, we need to treat this case a little | ||
1274 | * differently. we don't want to truncate the | ||
1275 | * file now; this would be wrong if the OPEN | ||
1276 | * fails for some other reason. furthermore, | ||
1277 | * if the size is nonzero, we should ignore it | ||
1278 | * according to spec! | ||
1279 | */ | ||
1280 | *truncp = (iap->ia_valid & ATTR_SIZE) && !iap->ia_size; | ||
1281 | } | ||
1282 | else { | ||
1283 | iap->ia_valid &= ATTR_SIZE; | ||
1284 | goto set_attr; | ||
1285 | } | ||
1286 | break; | ||
1287 | case NFS3_CREATE_EXCLUSIVE: | ||
1288 | if ( dchild->d_inode->i_mtime.tv_sec == v_mtime | ||
1289 | && dchild->d_inode->i_atime.tv_sec == v_atime | ||
1290 | && dchild->d_inode->i_mode == v_mode | ||
1291 | && dchild->d_inode->i_size == 0 ) | ||
1292 | break; | ||
1293 | /* fallthru */ | ||
1294 | case NFS3_CREATE_GUARDED: | ||
1295 | err = nfserr_exist; | ||
1296 | } | ||
1297 | goto out; | ||
1298 | } | ||
1299 | |||
1300 | err = vfs_create(dirp, dchild, iap->ia_mode, NULL); | ||
1301 | if (err < 0) | ||
1302 | goto out_nfserr; | ||
1303 | |||
1304 | if (EX_ISSYNC(fhp->fh_export)) { | ||
1305 | nfsd_sync_dir(dentry); | ||
1306 | /* setattr will sync the child (or not) */ | ||
1307 | } | ||
1308 | |||
1309 | /* | ||
1310 | * Update the filehandle to get the new inode info. | ||
1311 | */ | ||
1312 | err = fh_update(resfhp); | ||
1313 | if (err) | ||
1314 | goto out; | ||
1315 | |||
1316 | if (createmode == NFS3_CREATE_EXCLUSIVE) { | ||
1317 | /* Cram the verifier into atime/mtime/mode */ | ||
1318 | iap->ia_valid = ATTR_MTIME|ATTR_ATIME | ||
1319 | | ATTR_MTIME_SET|ATTR_ATIME_SET | ||
1320 | | ATTR_MODE; | ||
1321 | /* XXX someone who knows this better please fix it for nsec */ | ||
1322 | iap->ia_mtime.tv_sec = v_mtime; | ||
1323 | iap->ia_atime.tv_sec = v_atime; | ||
1324 | iap->ia_mtime.tv_nsec = 0; | ||
1325 | iap->ia_atime.tv_nsec = 0; | ||
1326 | iap->ia_mode = v_mode; | ||
1327 | } | ||
1328 | |||
1329 | /* Set file attributes. | ||
1330 | * Mode has already been set but we might need to reset it | ||
1331 | * for CREATE_EXCLUSIVE | ||
1332 | * Irix appears to send along the gid when it tries to | ||
1333 | * implement setgid directories via NFS. Clear out all that cruft. | ||
1334 | */ | ||
1335 | set_attr: | ||
1336 | if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0) | ||
1337 | err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); | ||
1338 | |||
1339 | out: | ||
1340 | fh_unlock(fhp); | ||
1341 | if (dchild && !IS_ERR(dchild)) | ||
1342 | dput(dchild); | ||
1343 | return err; | ||
1344 | |||
1345 | out_nfserr: | ||
1346 | err = nfserrno(err); | ||
1347 | goto out; | ||
1348 | } | ||
1349 | #endif /* CONFIG_NFSD_V3 */ | ||
1350 | |||
1351 | /* | ||
1352 | * Read a symlink. On entry, *lenp must contain the maximum path length that | ||
1353 | * fits into the buffer. On return, it contains the true length. | ||
1354 | * N.B. After this call fhp needs an fh_put | ||
1355 | */ | ||
1356 | int | ||
1357 | nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) | ||
1358 | { | ||
1359 | struct dentry *dentry; | ||
1360 | struct inode *inode; | ||
1361 | mm_segment_t oldfs; | ||
1362 | int err; | ||
1363 | |||
1364 | err = fh_verify(rqstp, fhp, S_IFLNK, MAY_NOP); | ||
1365 | if (err) | ||
1366 | goto out; | ||
1367 | |||
1368 | dentry = fhp->fh_dentry; | ||
1369 | inode = dentry->d_inode; | ||
1370 | |||
1371 | err = nfserr_inval; | ||
1372 | if (!inode->i_op || !inode->i_op->readlink) | ||
1373 | goto out; | ||
1374 | |||
1375 | touch_atime(fhp->fh_export->ex_mnt, dentry); | ||
1376 | /* N.B. Why does this call need a get_fs()?? | ||
1377 | * Remove the set_fs and watch the fireworks:-) --okir | ||
1378 | */ | ||
1379 | |||
1380 | oldfs = get_fs(); set_fs(KERNEL_DS); | ||
1381 | err = inode->i_op->readlink(dentry, buf, *lenp); | ||
1382 | set_fs(oldfs); | ||
1383 | |||
1384 | if (err < 0) | ||
1385 | goto out_nfserr; | ||
1386 | *lenp = err; | ||
1387 | err = 0; | ||
1388 | out: | ||
1389 | return err; | ||
1390 | |||
1391 | out_nfserr: | ||
1392 | err = nfserrno(err); | ||
1393 | goto out; | ||
1394 | } | ||
1395 | |||
1396 | /* | ||
1397 | * Create a symlink and look up its inode | ||
1398 | * N.B. After this call _both_ fhp and resfhp need an fh_put | ||
1399 | */ | ||
1400 | int | ||
1401 | nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
1402 | char *fname, int flen, | ||
1403 | char *path, int plen, | ||
1404 | struct svc_fh *resfhp, | ||
1405 | struct iattr *iap) | ||
1406 | { | ||
1407 | struct dentry *dentry, *dnew; | ||
1408 | int err, cerr; | ||
1409 | umode_t mode; | ||
1410 | |||
1411 | err = nfserr_noent; | ||
1412 | if (!flen || !plen) | ||
1413 | goto out; | ||
1414 | err = nfserr_exist; | ||
1415 | if (isdotent(fname, flen)) | ||
1416 | goto out; | ||
1417 | |||
1418 | err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); | ||
1419 | if (err) | ||
1420 | goto out; | ||
1421 | fh_lock(fhp); | ||
1422 | dentry = fhp->fh_dentry; | ||
1423 | dnew = lookup_one_len(fname, dentry, flen); | ||
1424 | err = PTR_ERR(dnew); | ||
1425 | if (IS_ERR(dnew)) | ||
1426 | goto out_nfserr; | ||
1427 | |||
1428 | mode = S_IALLUGO; | ||
1429 | /* Only the MODE ATTRibute is even vaguely meaningful */ | ||
1430 | if (iap && (iap->ia_valid & ATTR_MODE)) | ||
1431 | mode = iap->ia_mode & S_IALLUGO; | ||
1432 | |||
1433 | if (unlikely(path[plen] != 0)) { | ||
1434 | char *path_alloced = kmalloc(plen+1, GFP_KERNEL); | ||
1435 | if (path_alloced == NULL) | ||
1436 | err = -ENOMEM; | ||
1437 | else { | ||
1438 | strncpy(path_alloced, path, plen); | ||
1439 | path_alloced[plen] = 0; | ||
1440 | err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode); | ||
1441 | kfree(path_alloced); | ||
1442 | } | ||
1443 | } else | ||
1444 | err = vfs_symlink(dentry->d_inode, dnew, path, mode); | ||
1445 | |||
1446 | if (!err) { | ||
1447 | if (EX_ISSYNC(fhp->fh_export)) | ||
1448 | nfsd_sync_dir(dentry); | ||
1449 | } else | ||
1450 | err = nfserrno(err); | ||
1451 | fh_unlock(fhp); | ||
1452 | |||
1453 | cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp); | ||
1454 | dput(dnew); | ||
1455 | if (err==0) err = cerr; | ||
1456 | out: | ||
1457 | return err; | ||
1458 | |||
1459 | out_nfserr: | ||
1460 | err = nfserrno(err); | ||
1461 | goto out; | ||
1462 | } | ||
1463 | |||
1464 | /* | ||
1465 | * Create a hardlink | ||
1466 | * N.B. After this call _both_ ffhp and tfhp need an fh_put | ||
1467 | */ | ||
1468 | int | ||
1469 | nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, | ||
1470 | char *name, int len, struct svc_fh *tfhp) | ||
1471 | { | ||
1472 | struct dentry *ddir, *dnew, *dold; | ||
1473 | struct inode *dirp, *dest; | ||
1474 | int err; | ||
1475 | |||
1476 | err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); | ||
1477 | if (err) | ||
1478 | goto out; | ||
1479 | err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); | ||
1480 | if (err) | ||
1481 | goto out; | ||
1482 | |||
1483 | err = nfserr_perm; | ||
1484 | if (!len) | ||
1485 | goto out; | ||
1486 | err = nfserr_exist; | ||
1487 | if (isdotent(name, len)) | ||
1488 | goto out; | ||
1489 | |||
1490 | fh_lock(ffhp); | ||
1491 | ddir = ffhp->fh_dentry; | ||
1492 | dirp = ddir->d_inode; | ||
1493 | |||
1494 | dnew = lookup_one_len(name, ddir, len); | ||
1495 | err = PTR_ERR(dnew); | ||
1496 | if (IS_ERR(dnew)) | ||
1497 | goto out_nfserr; | ||
1498 | |||
1499 | dold = tfhp->fh_dentry; | ||
1500 | dest = dold->d_inode; | ||
1501 | |||
1502 | err = vfs_link(dold, dirp, dnew); | ||
1503 | if (!err) { | ||
1504 | if (EX_ISSYNC(ffhp->fh_export)) { | ||
1505 | nfsd_sync_dir(ddir); | ||
1506 | write_inode_now(dest, 1); | ||
1507 | } | ||
1508 | } else { | ||
1509 | if (err == -EXDEV && rqstp->rq_vers == 2) | ||
1510 | err = nfserr_acces; | ||
1511 | else | ||
1512 | err = nfserrno(err); | ||
1513 | } | ||
1514 | |||
1515 | fh_unlock(ffhp); | ||
1516 | dput(dnew); | ||
1517 | out: | ||
1518 | return err; | ||
1519 | |||
1520 | out_nfserr: | ||
1521 | err = nfserrno(err); | ||
1522 | goto out; | ||
1523 | } | ||
1524 | |||
1525 | /* | ||
1526 | * Rename a file | ||
1527 | * N.B. After this call _both_ ffhp and tfhp need an fh_put | ||
1528 | */ | ||
1529 | int | ||
1530 | nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, | ||
1531 | struct svc_fh *tfhp, char *tname, int tlen) | ||
1532 | { | ||
1533 | struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap; | ||
1534 | struct inode *fdir, *tdir; | ||
1535 | int err; | ||
1536 | |||
1537 | err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); | ||
1538 | if (err) | ||
1539 | goto out; | ||
1540 | err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); | ||
1541 | if (err) | ||
1542 | goto out; | ||
1543 | |||
1544 | fdentry = ffhp->fh_dentry; | ||
1545 | fdir = fdentry->d_inode; | ||
1546 | |||
1547 | tdentry = tfhp->fh_dentry; | ||
1548 | tdir = tdentry->d_inode; | ||
1549 | |||
1550 | err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; | ||
1551 | if (fdir->i_sb != tdir->i_sb) | ||
1552 | goto out; | ||
1553 | |||
1554 | err = nfserr_perm; | ||
1555 | if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) | ||
1556 | goto out; | ||
1557 | |||
1558 | /* cannot use fh_lock as we need deadlock protective ordering | ||
1559 | * so do it by hand */ | ||
1560 | trap = lock_rename(tdentry, fdentry); | ||
1561 | ffhp->fh_locked = tfhp->fh_locked = 1; | ||
1562 | fill_pre_wcc(ffhp); | ||
1563 | fill_pre_wcc(tfhp); | ||
1564 | |||
1565 | odentry = lookup_one_len(fname, fdentry, flen); | ||
1566 | err = PTR_ERR(odentry); | ||
1567 | if (IS_ERR(odentry)) | ||
1568 | goto out_nfserr; | ||
1569 | |||
1570 | err = -ENOENT; | ||
1571 | if (!odentry->d_inode) | ||
1572 | goto out_dput_old; | ||
1573 | err = -EINVAL; | ||
1574 | if (odentry == trap) | ||
1575 | goto out_dput_old; | ||
1576 | |||
1577 | ndentry = lookup_one_len(tname, tdentry, tlen); | ||
1578 | err = PTR_ERR(ndentry); | ||
1579 | if (IS_ERR(ndentry)) | ||
1580 | goto out_dput_old; | ||
1581 | err = -ENOTEMPTY; | ||
1582 | if (ndentry == trap) | ||
1583 | goto out_dput_new; | ||
1584 | |||
1585 | #ifdef MSNFS | ||
1586 | if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && | ||
1587 | ((atomic_read(&odentry->d_count) > 1) | ||
1588 | || (atomic_read(&ndentry->d_count) > 1))) { | ||
1589 | err = nfserr_perm; | ||
1590 | } else | ||
1591 | #endif | ||
1592 | err = vfs_rename(fdir, odentry, tdir, ndentry); | ||
1593 | if (!err && EX_ISSYNC(tfhp->fh_export)) { | ||
1594 | nfsd_sync_dir(tdentry); | ||
1595 | nfsd_sync_dir(fdentry); | ||
1596 | } | ||
1597 | |||
1598 | out_dput_new: | ||
1599 | dput(ndentry); | ||
1600 | out_dput_old: | ||
1601 | dput(odentry); | ||
1602 | out_nfserr: | ||
1603 | if (err) | ||
1604 | err = nfserrno(err); | ||
1605 | |||
1606 | /* we cannot reply on fh_unlock on the two filehandles, | ||
1607 | * as that would do the wrong thing if the two directories | ||
1608 | * were the same, so again we do it by hand | ||
1609 | */ | ||
1610 | fill_post_wcc(ffhp); | ||
1611 | fill_post_wcc(tfhp); | ||
1612 | unlock_rename(tdentry, fdentry); | ||
1613 | ffhp->fh_locked = tfhp->fh_locked = 0; | ||
1614 | |||
1615 | out: | ||
1616 | return err; | ||
1617 | } | ||
1618 | |||
1619 | /* | ||
1620 | * Unlink a file or directory | ||
1621 | * N.B. After this call fhp needs an fh_put | ||
1622 | */ | ||
1623 | int | ||
1624 | nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | ||
1625 | char *fname, int flen) | ||
1626 | { | ||
1627 | struct dentry *dentry, *rdentry; | ||
1628 | struct inode *dirp; | ||
1629 | int err; | ||
1630 | |||
1631 | err = nfserr_acces; | ||
1632 | if (!flen || isdotent(fname, flen)) | ||
1633 | goto out; | ||
1634 | err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); | ||
1635 | if (err) | ||
1636 | goto out; | ||
1637 | |||
1638 | fh_lock(fhp); | ||
1639 | dentry = fhp->fh_dentry; | ||
1640 | dirp = dentry->d_inode; | ||
1641 | |||
1642 | rdentry = lookup_one_len(fname, dentry, flen); | ||
1643 | err = PTR_ERR(rdentry); | ||
1644 | if (IS_ERR(rdentry)) | ||
1645 | goto out_nfserr; | ||
1646 | |||
1647 | if (!rdentry->d_inode) { | ||
1648 | dput(rdentry); | ||
1649 | err = nfserr_noent; | ||
1650 | goto out; | ||
1651 | } | ||
1652 | |||
1653 | if (!type) | ||
1654 | type = rdentry->d_inode->i_mode & S_IFMT; | ||
1655 | |||
1656 | if (type != S_IFDIR) { /* It's UNLINK */ | ||
1657 | #ifdef MSNFS | ||
1658 | if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && | ||
1659 | (atomic_read(&rdentry->d_count) > 1)) { | ||
1660 | err = nfserr_perm; | ||
1661 | } else | ||
1662 | #endif | ||
1663 | err = vfs_unlink(dirp, rdentry); | ||
1664 | } else { /* It's RMDIR */ | ||
1665 | err = vfs_rmdir(dirp, rdentry); | ||
1666 | } | ||
1667 | |||
1668 | dput(rdentry); | ||
1669 | |||
1670 | if (err) | ||
1671 | goto out_nfserr; | ||
1672 | if (EX_ISSYNC(fhp->fh_export)) | ||
1673 | nfsd_sync_dir(dentry); | ||
1674 | |||
1675 | out: | ||
1676 | return err; | ||
1677 | |||
1678 | out_nfserr: | ||
1679 | err = nfserrno(err); | ||
1680 | goto out; | ||
1681 | } | ||
1682 | |||
1683 | /* | ||
1684 | * Read entries from a directory. | ||
1685 | * The NFSv3/4 verifier we ignore for now. | ||
1686 | */ | ||
1687 | int | ||
1688 | nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, | ||
1689 | struct readdir_cd *cdp, encode_dent_fn func) | ||
1690 | { | ||
1691 | int err; | ||
1692 | struct file *file; | ||
1693 | loff_t offset = *offsetp; | ||
1694 | |||
1695 | err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); | ||
1696 | if (err) | ||
1697 | goto out; | ||
1698 | |||
1699 | offset = vfs_llseek(file, offset, 0); | ||
1700 | if (offset < 0) { | ||
1701 | err = nfserrno((int)offset); | ||
1702 | goto out_close; | ||
1703 | } | ||
1704 | |||
1705 | /* | ||
1706 | * Read the directory entries. This silly loop is necessary because | ||
1707 | * readdir() is not guaranteed to fill up the entire buffer, but | ||
1708 | * may choose to do less. | ||
1709 | */ | ||
1710 | |||
1711 | do { | ||
1712 | cdp->err = nfserr_eof; /* will be cleared on successful read */ | ||
1713 | err = vfs_readdir(file, (filldir_t) func, cdp); | ||
1714 | } while (err >=0 && cdp->err == nfs_ok); | ||
1715 | if (err) | ||
1716 | err = nfserrno(err); | ||
1717 | else | ||
1718 | err = cdp->err; | ||
1719 | *offsetp = vfs_llseek(file, 0, 1); | ||
1720 | |||
1721 | if (err == nfserr_eof || err == nfserr_toosmall) | ||
1722 | err = nfs_ok; /* can still be found in ->err */ | ||
1723 | out_close: | ||
1724 | nfsd_close(file); | ||
1725 | out: | ||
1726 | return err; | ||
1727 | } | ||
1728 | |||
1729 | /* | ||
1730 | * Get file system stats | ||
1731 | * N.B. After this call fhp needs an fh_put | ||
1732 | */ | ||
1733 | int | ||
1734 | nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) | ||
1735 | { | ||
1736 | int err = fh_verify(rqstp, fhp, 0, MAY_NOP); | ||
1737 | if (!err && vfs_statfs(fhp->fh_dentry->d_inode->i_sb,stat)) | ||
1738 | err = nfserr_io; | ||
1739 | return err; | ||
1740 | } | ||
1741 | |||
1742 | /* | ||
1743 | * Check for a user's access permissions to this inode. | ||
1744 | */ | ||
1745 | int | ||
1746 | nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) | ||
1747 | { | ||
1748 | struct inode *inode = dentry->d_inode; | ||
1749 | int err; | ||
1750 | |||
1751 | if (acc == MAY_NOP) | ||
1752 | return 0; | ||
1753 | #if 0 | ||
1754 | dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", | ||
1755 | acc, | ||
1756 | (acc & MAY_READ)? " read" : "", | ||
1757 | (acc & MAY_WRITE)? " write" : "", | ||
1758 | (acc & MAY_EXEC)? " exec" : "", | ||
1759 | (acc & MAY_SATTR)? " sattr" : "", | ||
1760 | (acc & MAY_TRUNC)? " trunc" : "", | ||
1761 | (acc & MAY_LOCK)? " lock" : "", | ||
1762 | (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", | ||
1763 | inode->i_mode, | ||
1764 | IS_IMMUTABLE(inode)? " immut" : "", | ||
1765 | IS_APPEND(inode)? " append" : "", | ||
1766 | IS_RDONLY(inode)? " ro" : ""); | ||
1767 | dprintk(" owner %d/%d user %d/%d\n", | ||
1768 | inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); | ||
1769 | #endif | ||
1770 | |||
1771 | /* Normally we reject any write/sattr etc access on a read-only file | ||
1772 | * system. But if it is IRIX doing check on write-access for a | ||
1773 | * device special file, we ignore rofs. | ||
1774 | */ | ||
1775 | if (!(acc & MAY_LOCAL_ACCESS)) | ||
1776 | if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { | ||
1777 | if (EX_RDONLY(exp) || IS_RDONLY(inode)) | ||
1778 | return nfserr_rofs; | ||
1779 | if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) | ||
1780 | return nfserr_perm; | ||
1781 | } | ||
1782 | if ((acc & MAY_TRUNC) && IS_APPEND(inode)) | ||
1783 | return nfserr_perm; | ||
1784 | |||
1785 | if (acc & MAY_LOCK) { | ||
1786 | /* If we cannot rely on authentication in NLM requests, | ||
1787 | * just allow locks, otherwise require read permission, or | ||
1788 | * ownership | ||
1789 | */ | ||
1790 | if (exp->ex_flags & NFSEXP_NOAUTHNLM) | ||
1791 | return 0; | ||
1792 | else | ||
1793 | acc = MAY_READ | MAY_OWNER_OVERRIDE; | ||
1794 | } | ||
1795 | /* | ||
1796 | * The file owner always gets access permission for accesses that | ||
1797 | * would normally be checked at open time. This is to make | ||
1798 | * file access work even when the client has done a fchmod(fd, 0). | ||
1799 | * | ||
1800 | * However, `cp foo bar' should fail nevertheless when bar is | ||
1801 | * readonly. A sensible way to do this might be to reject all | ||
1802 | * attempts to truncate a read-only file, because a creat() call | ||
1803 | * always implies file truncation. | ||
1804 | * ... but this isn't really fair. A process may reasonably call | ||
1805 | * ftruncate on an open file descriptor on a file with perm 000. | ||
1806 | * We must trust the client to do permission checking - using "ACCESS" | ||
1807 | * with NFSv3. | ||
1808 | */ | ||
1809 | if ((acc & MAY_OWNER_OVERRIDE) && | ||
1810 | inode->i_uid == current->fsuid) | ||
1811 | return 0; | ||
1812 | |||
1813 | err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); | ||
1814 | |||
1815 | /* Allow read access to binaries even when mode 111 */ | ||
1816 | if (err == -EACCES && S_ISREG(inode->i_mode) && | ||
1817 | acc == (MAY_READ | MAY_OWNER_OVERRIDE)) | ||
1818 | err = permission(inode, MAY_EXEC, NULL); | ||
1819 | |||
1820 | return err? nfserrno(err) : 0; | ||
1821 | } | ||
1822 | |||
1823 | void | ||
1824 | nfsd_racache_shutdown(void) | ||
1825 | { | ||
1826 | if (!raparm_cache) | ||
1827 | return; | ||
1828 | dprintk("nfsd: freeing readahead buffers.\n"); | ||
1829 | kfree(raparml); | ||
1830 | raparm_cache = raparml = NULL; | ||
1831 | } | ||
1832 | /* | ||
1833 | * Initialize readahead param cache | ||
1834 | */ | ||
1835 | int | ||
1836 | nfsd_racache_init(int cache_size) | ||
1837 | { | ||
1838 | int i; | ||
1839 | |||
1840 | if (raparm_cache) | ||
1841 | return 0; | ||
1842 | raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); | ||
1843 | |||
1844 | if (raparml != NULL) { | ||
1845 | dprintk("nfsd: allocating %d readahead buffers.\n", | ||
1846 | cache_size); | ||
1847 | memset(raparml, 0, sizeof(struct raparms) * cache_size); | ||
1848 | for (i = 0; i < cache_size - 1; i++) { | ||
1849 | raparml[i].p_next = raparml + i + 1; | ||
1850 | } | ||
1851 | raparm_cache = raparml; | ||
1852 | } else { | ||
1853 | printk(KERN_WARNING | ||
1854 | "nfsd: Could not allocate memory read-ahead cache.\n"); | ||
1855 | return -ENOMEM; | ||
1856 | } | ||
1857 | nfsdstats.ra_size = cache_size; | ||
1858 | return 0; | ||
1859 | } | ||