diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2010-12-14 09:57:12 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-12-16 12:37:23 -0500 |
commit | 3460f29a27344db8c7af62cafdb961286ef0b6cd (patch) | |
tree | be7c8acc0c8c971114f8136534c613395a4d2951 /fs/lockd | |
parent | f6048709391336cf27fb5c1cfca8e792103e5a73 (diff) |
lockd: Introduce new-style XDR functions for NLMv4
We'd like to prevent local buffer overflows caused by malicious or
broken servers. New xdr_stream style decoders can do that.
For efficiency, we also want to be able to pass xdr_streams from
call_encode() to all XDR encoding functions, rather than building
an xdr_stream in every XDR encoding function in the kernel.
Same idea as the NLM v3 XDR overhaul.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Tested-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/lockd')
-rw-r--r-- | fs/lockd/Makefile | 2 | ||||
-rw-r--r-- | fs/lockd/clnt4xdr.c | 621 | ||||
-rw-r--r-- | fs/lockd/xdr4.c | 255 |
3 files changed, 622 insertions, 256 deletions
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index d0488b3bd00b..ca58d64374ca 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile | |||
@@ -6,5 +6,5 @@ obj-$(CONFIG_LOCKD) += lockd.o | |||
6 | 6 | ||
7 | lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \ | 7 | lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \ |
8 | svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o | 8 | svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o |
9 | lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o | 9 | lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o |
10 | lockd-objs := $(lockd-objs-y) | 10 | lockd-objs := $(lockd-objs-y) |
diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c new file mode 100644 index 000000000000..1a1c3e21ed2c --- /dev/null +++ b/fs/lockd/clnt4xdr.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * linux/fs/lockd/clnt4xdr.c | ||
3 | * | ||
4 | * XDR functions to encode/decode NLM version 4 RPC arguments and results. | ||
5 | * | ||
6 | * NLM client-side only. | ||
7 | * | ||
8 | * Copyright (C) 2010, Oracle. All rights reserved. | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/sunrpc/xdr.h> | ||
13 | #include <linux/sunrpc/clnt.h> | ||
14 | #include <linux/sunrpc/stats.h> | ||
15 | #include <linux/lockd/lockd.h> | ||
16 | |||
17 | #define NLMDBG_FACILITY NLMDBG_XDR | ||
18 | |||
19 | #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) | ||
20 | # error "NLM host name cannot be larger than XDR_MAX_NETOBJ!" | ||
21 | #endif | ||
22 | |||
23 | #if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN) | ||
24 | # error "NLM host name cannot be larger than NLM's maximum string length!" | ||
25 | #endif | ||
26 | |||
27 | /* | ||
28 | * Declare the space requirements for NLM arguments and replies as | ||
29 | * number of 32bit-words | ||
30 | */ | ||
31 | #define NLM4_void_sz (0) | ||
32 | #define NLM4_cookie_sz (1+(NLM_MAXCOOKIELEN>>2)) | ||
33 | #define NLM4_caller_sz (1+(NLMCLNT_OHSIZE>>2)) | ||
34 | #define NLM4_owner_sz (1+(NLMCLNT_OHSIZE>>2)) | ||
35 | #define NLM4_fhandle_sz (1+(NFS3_FHSIZE>>2)) | ||
36 | #define NLM4_lock_sz (5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz) | ||
37 | #define NLM4_holder_sz (6+NLM4_owner_sz) | ||
38 | |||
39 | #define NLM4_testargs_sz (NLM4_cookie_sz+1+NLM4_lock_sz) | ||
40 | #define NLM4_lockargs_sz (NLM4_cookie_sz+4+NLM4_lock_sz) | ||
41 | #define NLM4_cancargs_sz (NLM4_cookie_sz+2+NLM4_lock_sz) | ||
42 | #define NLM4_unlockargs_sz (NLM4_cookie_sz+NLM4_lock_sz) | ||
43 | |||
44 | #define NLM4_testres_sz (NLM4_cookie_sz+1+NLM4_holder_sz) | ||
45 | #define NLM4_res_sz (NLM4_cookie_sz+1) | ||
46 | #define NLM4_norep_sz (0) | ||
47 | |||
48 | |||
49 | static s64 loff_t_to_s64(loff_t offset) | ||
50 | { | ||
51 | s64 res; | ||
52 | |||
53 | if (offset >= NLM4_OFFSET_MAX) | ||
54 | res = NLM4_OFFSET_MAX; | ||
55 | else if (offset <= -NLM4_OFFSET_MAX) | ||
56 | res = -NLM4_OFFSET_MAX; | ||
57 | else | ||
58 | res = offset; | ||
59 | return res; | ||
60 | } | ||
61 | |||
62 | static void nlm4_compute_offsets(const struct nlm_lock *lock, | ||
63 | u64 *l_offset, u64 *l_len) | ||
64 | { | ||
65 | const struct file_lock *fl = &lock->fl; | ||
66 | |||
67 | BUG_ON(fl->fl_start > NLM4_OFFSET_MAX); | ||
68 | BUG_ON(fl->fl_end > NLM4_OFFSET_MAX && | ||
69 | fl->fl_end != OFFSET_MAX); | ||
70 | |||
71 | *l_offset = loff_t_to_s64(fl->fl_start); | ||
72 | if (fl->fl_end == OFFSET_MAX) | ||
73 | *l_len = 0; | ||
74 | else | ||
75 | *l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Handle decode buffer overflows out-of-line. | ||
80 | */ | ||
81 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
82 | { | ||
83 | dprintk("lockd: %s prematurely hit the end of our receive buffer. " | ||
84 | "Remaining buffer length is %tu words.\n", | ||
85 | func, xdr->end - xdr->p); | ||
86 | } | ||
87 | |||
88 | |||
89 | /* | ||
90 | * Encode/decode NLMv4 basic data types | ||
91 | * | ||
92 | * Basic NLMv4 data types are defined in Appendix II, section 6.1.4 | ||
93 | * of RFC 1813: "NFS Version 3 Protocol Specification" and in Chapter | ||
94 | * 10 of X/Open's "Protocols for Interworking: XNFS, Version 3W". | ||
95 | * | ||
96 | * Not all basic data types have their own encoding and decoding | ||
97 | * functions. For run-time efficiency, some data types are encoded | ||
98 | * or decoded inline. | ||
99 | */ | ||
100 | |||
101 | static void encode_bool(struct xdr_stream *xdr, const int value) | ||
102 | { | ||
103 | __be32 *p; | ||
104 | |||
105 | p = xdr_reserve_space(xdr, 4); | ||
106 | *p = value ? xdr_one : xdr_zero; | ||
107 | } | ||
108 | |||
109 | static void encode_int32(struct xdr_stream *xdr, const s32 value) | ||
110 | { | ||
111 | __be32 *p; | ||
112 | |||
113 | p = xdr_reserve_space(xdr, 4); | ||
114 | *p = cpu_to_be32(value); | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * typedef opaque netobj<MAXNETOBJ_SZ> | ||
119 | */ | ||
120 | static void encode_netobj(struct xdr_stream *xdr, | ||
121 | const u8 *data, const unsigned int length) | ||
122 | { | ||
123 | __be32 *p; | ||
124 | |||
125 | BUG_ON(length > XDR_MAX_NETOBJ); | ||
126 | p = xdr_reserve_space(xdr, 4 + length); | ||
127 | xdr_encode_opaque(p, data, length); | ||
128 | } | ||
129 | |||
130 | static int decode_netobj(struct xdr_stream *xdr, | ||
131 | struct xdr_netobj *obj) | ||
132 | { | ||
133 | u32 length; | ||
134 | __be32 *p; | ||
135 | |||
136 | p = xdr_inline_decode(xdr, 4); | ||
137 | if (unlikely(p == NULL)) | ||
138 | goto out_overflow; | ||
139 | length = be32_to_cpup(p++); | ||
140 | if (unlikely(length > XDR_MAX_NETOBJ)) | ||
141 | goto out_size; | ||
142 | obj->len = length; | ||
143 | obj->data = (u8 *)p; | ||
144 | return 0; | ||
145 | out_size: | ||
146 | dprintk("NFS: returned netobj was too long: %u\n", length); | ||
147 | return -EIO; | ||
148 | out_overflow: | ||
149 | print_overflow_msg(__func__, xdr); | ||
150 | return -EIO; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * netobj cookie; | ||
155 | */ | ||
156 | static void encode_cookie(struct xdr_stream *xdr, | ||
157 | const struct nlm_cookie *cookie) | ||
158 | { | ||
159 | BUG_ON(cookie->len > NLM_MAXCOOKIELEN); | ||
160 | encode_netobj(xdr, (u8 *)&cookie->data, cookie->len); | ||
161 | } | ||
162 | |||
163 | static int decode_cookie(struct xdr_stream *xdr, | ||
164 | struct nlm_cookie *cookie) | ||
165 | { | ||
166 | u32 length; | ||
167 | __be32 *p; | ||
168 | |||
169 | p = xdr_inline_decode(xdr, 4); | ||
170 | if (unlikely(p == NULL)) | ||
171 | goto out_overflow; | ||
172 | length = be32_to_cpup(p++); | ||
173 | /* apparently HPUX can return empty cookies */ | ||
174 | if (length == 0) | ||
175 | goto out_hpux; | ||
176 | if (length > NLM_MAXCOOKIELEN) | ||
177 | goto out_size; | ||
178 | p = xdr_inline_decode(xdr, length); | ||
179 | if (unlikely(p == NULL)) | ||
180 | goto out_overflow; | ||
181 | cookie->len = length; | ||
182 | memcpy(cookie->data, p, length); | ||
183 | return 0; | ||
184 | out_hpux: | ||
185 | cookie->len = 4; | ||
186 | memset(cookie->data, 0, 4); | ||
187 | return 0; | ||
188 | out_size: | ||
189 | dprintk("NFS: returned cookie was too long: %u\n", length); | ||
190 | return -EIO; | ||
191 | out_overflow: | ||
192 | print_overflow_msg(__func__, xdr); | ||
193 | return -EIO; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * netobj fh; | ||
198 | */ | ||
199 | static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh) | ||
200 | { | ||
201 | BUG_ON(fh->size > NFS3_FHSIZE); | ||
202 | encode_netobj(xdr, (u8 *)&fh->data, fh->size); | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * enum nlm4_stats { | ||
207 | * NLM4_GRANTED = 0, | ||
208 | * NLM4_DENIED = 1, | ||
209 | * NLM4_DENIED_NOLOCKS = 2, | ||
210 | * NLM4_BLOCKED = 3, | ||
211 | * NLM4_DENIED_GRACE_PERIOD = 4, | ||
212 | * NLM4_DEADLCK = 5, | ||
213 | * NLM4_ROFS = 6, | ||
214 | * NLM4_STALE_FH = 7, | ||
215 | * NLM4_FBIG = 8, | ||
216 | * NLM4_FAILED = 9 | ||
217 | * }; | ||
218 | * | ||
219 | * struct nlm4_stat { | ||
220 | * nlm4_stats stat; | ||
221 | * }; | ||
222 | * | ||
223 | * NB: we don't swap bytes for the NLM status values. The upper | ||
224 | * layers deal directly with the status value in network byte | ||
225 | * order. | ||
226 | */ | ||
227 | static void encode_nlm4_stat(struct xdr_stream *xdr, | ||
228 | const __be32 stat) | ||
229 | { | ||
230 | __be32 *p; | ||
231 | |||
232 | BUG_ON(be32_to_cpu(stat) > NLM_FAILED); | ||
233 | p = xdr_reserve_space(xdr, 4); | ||
234 | *p = stat; | ||
235 | } | ||
236 | |||
237 | static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat) | ||
238 | { | ||
239 | __be32 *p; | ||
240 | |||
241 | p = xdr_inline_decode(xdr, 4); | ||
242 | if (unlikely(p == NULL)) | ||
243 | goto out_overflow; | ||
244 | if (unlikely(*p > nlm4_failed)) | ||
245 | goto out_bad_xdr; | ||
246 | *stat = *p; | ||
247 | return 0; | ||
248 | out_bad_xdr: | ||
249 | dprintk("%s: server returned invalid nlm4_stats value: %u\n", | ||
250 | __func__, be32_to_cpup(p)); | ||
251 | return -EIO; | ||
252 | out_overflow: | ||
253 | print_overflow_msg(__func__, xdr); | ||
254 | return -EIO; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * struct nlm4_holder { | ||
259 | * bool exclusive; | ||
260 | * int32 svid; | ||
261 | * netobj oh; | ||
262 | * uint64 l_offset; | ||
263 | * uint64 l_len; | ||
264 | * }; | ||
265 | */ | ||
266 | static void encode_nlm4_holder(struct xdr_stream *xdr, | ||
267 | const struct nlm_res *result) | ||
268 | { | ||
269 | const struct nlm_lock *lock = &result->lock; | ||
270 | u64 l_offset, l_len; | ||
271 | __be32 *p; | ||
272 | |||
273 | encode_bool(xdr, lock->fl.fl_type == F_RDLCK); | ||
274 | encode_int32(xdr, lock->svid); | ||
275 | encode_netobj(xdr, lock->oh.data, lock->oh.len); | ||
276 | |||
277 | p = xdr_reserve_space(xdr, 4 + 4); | ||
278 | nlm4_compute_offsets(lock, &l_offset, &l_len); | ||
279 | p = xdr_encode_hyper(p, l_offset); | ||
280 | xdr_encode_hyper(p, l_len); | ||
281 | } | ||
282 | |||
283 | static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) | ||
284 | { | ||
285 | struct nlm_lock *lock = &result->lock; | ||
286 | struct file_lock *fl = &lock->fl; | ||
287 | u64 l_offset, l_len; | ||
288 | u32 exclusive; | ||
289 | int error; | ||
290 | __be32 *p; | ||
291 | s32 end; | ||
292 | |||
293 | memset(lock, 0, sizeof(*lock)); | ||
294 | locks_init_lock(fl); | ||
295 | |||
296 | p = xdr_inline_decode(xdr, 4 + 4); | ||
297 | if (unlikely(p == NULL)) | ||
298 | goto out_overflow; | ||
299 | exclusive = be32_to_cpup(p++); | ||
300 | lock->svid = be32_to_cpup(p); | ||
301 | fl->fl_pid = (pid_t)lock->svid; | ||
302 | |||
303 | error = decode_netobj(xdr, &lock->oh); | ||
304 | if (unlikely(error)) | ||
305 | goto out; | ||
306 | |||
307 | p = xdr_inline_decode(xdr, 8 + 8); | ||
308 | if (unlikely(p == NULL)) | ||
309 | goto out_overflow; | ||
310 | |||
311 | fl->fl_flags = FL_POSIX; | ||
312 | fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK; | ||
313 | p = xdr_decode_hyper(p, &l_offset); | ||
314 | xdr_decode_hyper(p, &l_len); | ||
315 | end = l_offset + l_len - 1; | ||
316 | |||
317 | fl->fl_start = (loff_t)l_offset; | ||
318 | if (l_len == 0 || end < 0) | ||
319 | fl->fl_end = OFFSET_MAX; | ||
320 | else | ||
321 | fl->fl_end = (loff_t)end; | ||
322 | error = 0; | ||
323 | out: | ||
324 | return error; | ||
325 | out_overflow: | ||
326 | print_overflow_msg(__func__, xdr); | ||
327 | return -EIO; | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * string caller_name<LM_MAXSTRLEN>; | ||
332 | */ | ||
333 | static void encode_caller_name(struct xdr_stream *xdr, const char *name) | ||
334 | { | ||
335 | /* NB: client-side does not set lock->len */ | ||
336 | u32 length = strlen(name); | ||
337 | __be32 *p; | ||
338 | |||
339 | BUG_ON(length > NLM_MAXSTRLEN); | ||
340 | p = xdr_reserve_space(xdr, 4 + length); | ||
341 | xdr_encode_opaque(p, name, length); | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * struct nlm4_lock { | ||
346 | * string caller_name<LM_MAXSTRLEN>; | ||
347 | * netobj fh; | ||
348 | * netobj oh; | ||
349 | * int32 svid; | ||
350 | * uint64 l_offset; | ||
351 | * uint64 l_len; | ||
352 | * }; | ||
353 | */ | ||
354 | static void encode_nlm4_lock(struct xdr_stream *xdr, | ||
355 | const struct nlm_lock *lock) | ||
356 | { | ||
357 | u64 l_offset, l_len; | ||
358 | __be32 *p; | ||
359 | |||
360 | encode_caller_name(xdr, lock->caller); | ||
361 | encode_fh(xdr, &lock->fh); | ||
362 | encode_netobj(xdr, lock->oh.data, lock->oh.len); | ||
363 | |||
364 | p = xdr_reserve_space(xdr, 4 + 8 + 8); | ||
365 | *p++ = cpu_to_be32(lock->svid); | ||
366 | |||
367 | nlm4_compute_offsets(lock, &l_offset, &l_len); | ||
368 | p = xdr_encode_hyper(p, l_offset); | ||
369 | xdr_encode_hyper(p, l_len); | ||
370 | } | ||
371 | |||
372 | |||
373 | /* | ||
374 | * NLMv4 XDR encode functions | ||
375 | * | ||
376 | * NLMv4 argument types are defined in Appendix II of RFC 1813: | ||
377 | * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's | ||
378 | * "Protocols for Interworking: XNFS, Version 3W". | ||
379 | */ | ||
380 | |||
381 | /* | ||
382 | * struct nlm4_testargs { | ||
383 | * netobj cookie; | ||
384 | * bool exclusive; | ||
385 | * struct nlm4_lock alock; | ||
386 | * }; | ||
387 | */ | ||
388 | static int nlm4_xdr_enc_testargs(struct rpc_rqst *req, __be32 *p, | ||
389 | const struct nlm_args *args) | ||
390 | { | ||
391 | const struct nlm_lock *lock = &args->lock; | ||
392 | struct xdr_stream xdr; | ||
393 | |||
394 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
395 | encode_cookie(&xdr, &args->cookie); | ||
396 | encode_bool(&xdr, lock->fl.fl_type == F_WRLCK); | ||
397 | encode_nlm4_lock(&xdr, lock); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * struct nlm4_lockargs { | ||
403 | * netobj cookie; | ||
404 | * bool block; | ||
405 | * bool exclusive; | ||
406 | * struct nlm4_lock alock; | ||
407 | * bool reclaim; | ||
408 | * int state; | ||
409 | * }; | ||
410 | */ | ||
411 | static int nlm4_xdr_enc_lockargs(struct rpc_rqst *req, __be32 *p, | ||
412 | const struct nlm_args *args) | ||
413 | { | ||
414 | const struct nlm_lock *lock = &args->lock; | ||
415 | struct xdr_stream xdr; | ||
416 | |||
417 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
418 | encode_cookie(&xdr, &args->cookie); | ||
419 | encode_bool(&xdr, args->block); | ||
420 | encode_bool(&xdr, lock->fl.fl_type == F_WRLCK); | ||
421 | encode_nlm4_lock(&xdr, lock); | ||
422 | encode_bool(&xdr, args->reclaim); | ||
423 | encode_int32(&xdr, args->state); | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | /* | ||
428 | * struct nlm4_cancargs { | ||
429 | * netobj cookie; | ||
430 | * bool block; | ||
431 | * bool exclusive; | ||
432 | * struct nlm4_lock alock; | ||
433 | * }; | ||
434 | */ | ||
435 | static int nlm4_xdr_enc_cancargs(struct rpc_rqst *req, __be32 *p, | ||
436 | const struct nlm_args *args) | ||
437 | { | ||
438 | const struct nlm_lock *lock = &args->lock; | ||
439 | struct xdr_stream xdr; | ||
440 | |||
441 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
442 | encode_cookie(&xdr, &args->cookie); | ||
443 | encode_bool(&xdr, args->block); | ||
444 | encode_bool(&xdr, lock->fl.fl_type == F_WRLCK); | ||
445 | encode_nlm4_lock(&xdr, lock); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * struct nlm4_unlockargs { | ||
451 | * netobj cookie; | ||
452 | * struct nlm4_lock alock; | ||
453 | * }; | ||
454 | */ | ||
455 | static int nlm4_xdr_enc_unlockargs(struct rpc_rqst *req, __be32 *p, | ||
456 | const struct nlm_args *args) | ||
457 | { | ||
458 | const struct nlm_lock *lock = &args->lock; | ||
459 | struct xdr_stream xdr; | ||
460 | |||
461 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
462 | encode_cookie(&xdr, &args->cookie); | ||
463 | encode_nlm4_lock(&xdr, lock); | ||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * struct nlm4_res { | ||
469 | * netobj cookie; | ||
470 | * nlm4_stat stat; | ||
471 | * }; | ||
472 | */ | ||
473 | static int nlm4_xdr_enc_res(struct rpc_rqst *req, __be32 *p, | ||
474 | const struct nlm_res *result) | ||
475 | { | ||
476 | struct xdr_stream xdr; | ||
477 | |||
478 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
479 | encode_cookie(&xdr, &result->cookie); | ||
480 | encode_nlm4_stat(&xdr, result->status); | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * union nlm4_testrply switch (nlm4_stats stat) { | ||
486 | * case NLM4_DENIED: | ||
487 | * struct nlm4_holder holder; | ||
488 | * default: | ||
489 | * void; | ||
490 | * }; | ||
491 | * | ||
492 | * struct nlm4_testres { | ||
493 | * netobj cookie; | ||
494 | * nlm4_testrply test_stat; | ||
495 | * }; | ||
496 | */ | ||
497 | static int nlm4_xdr_enc_testres(struct rpc_rqst *req, __be32 *p, | ||
498 | const struct nlm_res *result) | ||
499 | { | ||
500 | struct xdr_stream xdr; | ||
501 | |||
502 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | ||
503 | encode_cookie(&xdr, &result->cookie); | ||
504 | encode_nlm4_stat(&xdr, result->status); | ||
505 | if (result->status == nlm_lck_denied) | ||
506 | encode_nlm4_holder(&xdr, result); | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | |||
511 | /* | ||
512 | * NLMv4 XDR decode functions | ||
513 | * | ||
514 | * NLMv4 argument types are defined in Appendix II of RFC 1813: | ||
515 | * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's | ||
516 | * "Protocols for Interworking: XNFS, Version 3W". | ||
517 | */ | ||
518 | |||
519 | /* | ||
520 | * union nlm4_testrply switch (nlm4_stats stat) { | ||
521 | * case NLM4_DENIED: | ||
522 | * struct nlm4_holder holder; | ||
523 | * default: | ||
524 | * void; | ||
525 | * }; | ||
526 | * | ||
527 | * struct nlm4_testres { | ||
528 | * netobj cookie; | ||
529 | * nlm4_testrply test_stat; | ||
530 | * }; | ||
531 | */ | ||
532 | static int decode_nlm4_testrply(struct xdr_stream *xdr, | ||
533 | struct nlm_res *result) | ||
534 | { | ||
535 | int error; | ||
536 | |||
537 | error = decode_nlm4_stat(xdr, &result->status); | ||
538 | if (unlikely(error)) | ||
539 | goto out; | ||
540 | if (result->status == nlm_lck_denied) | ||
541 | error = decode_nlm4_holder(xdr, result); | ||
542 | out: | ||
543 | return error; | ||
544 | } | ||
545 | |||
546 | static int nlm4_xdr_dec_testres(struct rpc_rqst *req, __be32 *p, | ||
547 | struct nlm_res *result) | ||
548 | { | ||
549 | struct xdr_stream xdr; | ||
550 | int error; | ||
551 | |||
552 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
553 | error = decode_cookie(&xdr, &result->cookie); | ||
554 | if (unlikely(error)) | ||
555 | goto out; | ||
556 | error = decode_nlm4_testrply(&xdr, result); | ||
557 | out: | ||
558 | return error; | ||
559 | } | ||
560 | |||
561 | /* | ||
562 | * struct nlm4_res { | ||
563 | * netobj cookie; | ||
564 | * nlm4_stat stat; | ||
565 | * }; | ||
566 | */ | ||
567 | static int nlm4_xdr_dec_res(struct rpc_rqst *req, __be32 *p, | ||
568 | struct nlm_res *result) | ||
569 | { | ||
570 | struct xdr_stream xdr; | ||
571 | int error; | ||
572 | |||
573 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
574 | error = decode_cookie(&xdr, &result->cookie); | ||
575 | if (unlikely(error)) | ||
576 | goto out; | ||
577 | error = decode_nlm4_stat(&xdr, &result->status); | ||
578 | out: | ||
579 | return error; | ||
580 | } | ||
581 | |||
582 | |||
583 | /* | ||
584 | * For NLM, a void procedure really returns nothing | ||
585 | */ | ||
586 | #define nlm4_xdr_dec_norep NULL | ||
587 | |||
588 | #define PROC(proc, argtype, restype) \ | ||
589 | [NLMPROC_##proc] = { \ | ||
590 | .p_proc = NLMPROC_##proc, \ | ||
591 | .p_encode = (kxdrproc_t)nlm4_xdr_enc_##argtype, \ | ||
592 | .p_decode = (kxdrproc_t)nlm4_xdr_dec_##restype, \ | ||
593 | .p_arglen = NLM4_##argtype##_sz, \ | ||
594 | .p_replen = NLM4_##restype##_sz, \ | ||
595 | .p_statidx = NLMPROC_##proc, \ | ||
596 | .p_name = #proc, \ | ||
597 | } | ||
598 | |||
599 | static struct rpc_procinfo nlm4_procedures[] = { | ||
600 | PROC(TEST, testargs, testres), | ||
601 | PROC(LOCK, lockargs, res), | ||
602 | PROC(CANCEL, cancargs, res), | ||
603 | PROC(UNLOCK, unlockargs, res), | ||
604 | PROC(GRANTED, testargs, res), | ||
605 | PROC(TEST_MSG, testargs, norep), | ||
606 | PROC(LOCK_MSG, lockargs, norep), | ||
607 | PROC(CANCEL_MSG, cancargs, norep), | ||
608 | PROC(UNLOCK_MSG, unlockargs, norep), | ||
609 | PROC(GRANTED_MSG, testargs, norep), | ||
610 | PROC(TEST_RES, testres, norep), | ||
611 | PROC(LOCK_RES, res, norep), | ||
612 | PROC(CANCEL_RES, res, norep), | ||
613 | PROC(UNLOCK_RES, res, norep), | ||
614 | PROC(GRANTED_RES, res, norep), | ||
615 | }; | ||
616 | |||
617 | struct rpc_version nlm_version4 = { | ||
618 | .number = 4, | ||
619 | .nrprocs = ARRAY_SIZE(nlm4_procedures), | ||
620 | .procs = nlm4_procedures, | ||
621 | }; | ||
diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index ad9dbbc9145d..dfa4789cd460 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c | |||
@@ -93,15 +93,6 @@ nlm4_decode_fh(__be32 *p, struct nfs_fh *f) | |||
93 | return p + XDR_QUADLEN(f->size); | 93 | return p + XDR_QUADLEN(f->size); |
94 | } | 94 | } |
95 | 95 | ||
96 | static __be32 * | ||
97 | nlm4_encode_fh(__be32 *p, struct nfs_fh *f) | ||
98 | { | ||
99 | *p++ = htonl(f->size); | ||
100 | if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */ | ||
101 | memcpy(p, f->data, f->size); | ||
102 | return p + XDR_QUADLEN(f->size); | ||
103 | } | ||
104 | |||
105 | /* | 96 | /* |
106 | * Encode and decode owner handle | 97 | * Encode and decode owner handle |
107 | */ | 98 | */ |
@@ -112,12 +103,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh) | |||
112 | } | 103 | } |
113 | 104 | ||
114 | static __be32 * | 105 | static __be32 * |
115 | nlm4_encode_oh(__be32 *p, struct xdr_netobj *oh) | ||
116 | { | ||
117 | return xdr_encode_netobj(p, oh); | ||
118 | } | ||
119 | |||
120 | static __be32 * | ||
121 | nlm4_decode_lock(__be32 *p, struct nlm_lock *lock) | 106 | nlm4_decode_lock(__be32 *p, struct nlm_lock *lock) |
122 | { | 107 | { |
123 | struct file_lock *fl = &lock->fl; | 108 | struct file_lock *fl = &lock->fl; |
@@ -150,38 +135,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock) | |||
150 | } | 135 | } |
151 | 136 | ||
152 | /* | 137 | /* |
153 | * Encode a lock as part of an NLM call | ||
154 | */ | ||
155 | static __be32 * | ||
156 | nlm4_encode_lock(__be32 *p, struct nlm_lock *lock) | ||
157 | { | ||
158 | struct file_lock *fl = &lock->fl; | ||
159 | __s64 start, len; | ||
160 | |||
161 | if (!(p = xdr_encode_string(p, lock->caller)) | ||
162 | || !(p = nlm4_encode_fh(p, &lock->fh)) | ||
163 | || !(p = nlm4_encode_oh(p, &lock->oh))) | ||
164 | return NULL; | ||
165 | |||
166 | if (fl->fl_start > NLM4_OFFSET_MAX | ||
167 | || (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX)) | ||
168 | return NULL; | ||
169 | |||
170 | *p++ = htonl(lock->svid); | ||
171 | |||
172 | start = loff_t_to_s64(fl->fl_start); | ||
173 | if (fl->fl_end == OFFSET_MAX) | ||
174 | len = 0; | ||
175 | else | ||
176 | len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); | ||
177 | |||
178 | p = xdr_encode_hyper(p, start); | ||
179 | p = xdr_encode_hyper(p, len); | ||
180 | |||
181 | return p; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Encode result of a TEST/TEST_MSG call | 138 | * Encode result of a TEST/TEST_MSG call |
186 | */ | 139 | */ |
187 | static __be32 * | 140 | static __be32 * |
@@ -379,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) | |||
379 | { | 332 | { |
380 | return xdr_ressize_check(rqstp, p); | 333 | return xdr_ressize_check(rqstp, p); |
381 | } | 334 | } |
382 | |||
383 | /* | ||
384 | * Now, the client side XDR functions | ||
385 | */ | ||
386 | #ifdef NLMCLNT_SUPPORT_SHARES | ||
387 | static int | ||
388 | nlm4clt_decode_void(struct rpc_rqst *req, __be32 *p, void *ptr) | ||
389 | { | ||
390 | return 0; | ||
391 | } | ||
392 | #endif | ||
393 | |||
394 | static int | ||
395 | nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) | ||
396 | { | ||
397 | struct nlm_lock *lock = &argp->lock; | ||
398 | |||
399 | if (!(p = nlm4_encode_cookie(p, &argp->cookie))) | ||
400 | return -EIO; | ||
401 | *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; | ||
402 | if (!(p = nlm4_encode_lock(p, lock))) | ||
403 | return -EIO; | ||
404 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int | ||
409 | nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) | ||
410 | { | ||
411 | if (!(p = nlm4_decode_cookie(p, &resp->cookie))) | ||
412 | return -EIO; | ||
413 | resp->status = *p++; | ||
414 | if (resp->status == nlm_lck_denied) { | ||
415 | struct file_lock *fl = &resp->lock.fl; | ||
416 | u32 excl; | ||
417 | __u64 start, len; | ||
418 | __s64 end; | ||
419 | |||
420 | memset(&resp->lock, 0, sizeof(resp->lock)); | ||
421 | locks_init_lock(fl); | ||
422 | excl = ntohl(*p++); | ||
423 | resp->lock.svid = ntohl(*p++); | ||
424 | fl->fl_pid = (pid_t)resp->lock.svid; | ||
425 | if (!(p = nlm4_decode_oh(p, &resp->lock.oh))) | ||
426 | return -EIO; | ||
427 | |||
428 | fl->fl_flags = FL_POSIX; | ||
429 | fl->fl_type = excl? F_WRLCK : F_RDLCK; | ||
430 | p = xdr_decode_hyper(p, &start); | ||
431 | p = xdr_decode_hyper(p, &len); | ||
432 | end = start + len - 1; | ||
433 | |||
434 | fl->fl_start = s64_to_loff_t(start); | ||
435 | if (len == 0 || end < 0) | ||
436 | fl->fl_end = OFFSET_MAX; | ||
437 | else | ||
438 | fl->fl_end = s64_to_loff_t(end); | ||
439 | } | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | |||
444 | static int | ||
445 | nlm4clt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) | ||
446 | { | ||
447 | struct nlm_lock *lock = &argp->lock; | ||
448 | |||
449 | if (!(p = nlm4_encode_cookie(p, &argp->cookie))) | ||
450 | return -EIO; | ||
451 | *p++ = argp->block? xdr_one : xdr_zero; | ||
452 | *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; | ||
453 | if (!(p = nlm4_encode_lock(p, lock))) | ||
454 | return -EIO; | ||
455 | *p++ = argp->reclaim? xdr_one : xdr_zero; | ||
456 | *p++ = htonl(argp->state); | ||
457 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int | ||
462 | nlm4clt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) | ||
463 | { | ||
464 | struct nlm_lock *lock = &argp->lock; | ||
465 | |||
466 | if (!(p = nlm4_encode_cookie(p, &argp->cookie))) | ||
467 | return -EIO; | ||
468 | *p++ = argp->block? xdr_one : xdr_zero; | ||
469 | *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; | ||
470 | if (!(p = nlm4_encode_lock(p, lock))) | ||
471 | return -EIO; | ||
472 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static int | ||
477 | nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) | ||
478 | { | ||
479 | struct nlm_lock *lock = &argp->lock; | ||
480 | |||
481 | if (!(p = nlm4_encode_cookie(p, &argp->cookie))) | ||
482 | return -EIO; | ||
483 | if (!(p = nlm4_encode_lock(p, lock))) | ||
484 | return -EIO; | ||
485 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int | ||
490 | nlm4clt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) | ||
491 | { | ||
492 | if (!(p = nlm4_encode_cookie(p, &resp->cookie))) | ||
493 | return -EIO; | ||
494 | *p++ = resp->status; | ||
495 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int | ||
500 | nlm4clt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) | ||
501 | { | ||
502 | if (!(p = nlm4_encode_testres(p, resp))) | ||
503 | return -EIO; | ||
504 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int | ||
509 | nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) | ||
510 | { | ||
511 | if (!(p = nlm4_decode_cookie(p, &resp->cookie))) | ||
512 | return -EIO; | ||
513 | resp->status = *p++; | ||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) | ||
518 | # error "NLM host name cannot be larger than XDR_MAX_NETOBJ!" | ||
519 | #endif | ||
520 | |||
521 | #if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN) | ||
522 | # error "NLM host name cannot be larger than NLM's maximum string length!" | ||
523 | #endif | ||
524 | |||
525 | /* | ||
526 | * Buffer requirements for NLM | ||
527 | */ | ||
528 | #define NLM4_void_sz 0 | ||
529 | #define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN) | ||
530 | #define NLM4_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE) | ||
531 | #define NLM4_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE) | ||
532 | #define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE) | ||
533 | #define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz | ||
534 | #define NLM4_holder_sz 6+NLM4_owner_sz | ||
535 | |||
536 | #define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz | ||
537 | #define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz | ||
538 | #define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz | ||
539 | #define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz | ||
540 | |||
541 | #define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz | ||
542 | #define NLM4_res_sz NLM4_cookie_sz+1 | ||
543 | #define NLM4_norep_sz 0 | ||
544 | |||
545 | /* | ||
546 | * For NLM, a void procedure really returns nothing | ||
547 | */ | ||
548 | #define nlm4clt_decode_norep NULL | ||
549 | |||
550 | #define PROC(proc, argtype, restype) \ | ||
551 | [NLMPROC_##proc] = { \ | ||
552 | .p_proc = NLMPROC_##proc, \ | ||
553 | .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \ | ||
554 | .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \ | ||
555 | .p_arglen = NLM4_##argtype##_sz, \ | ||
556 | .p_replen = NLM4_##restype##_sz, \ | ||
557 | .p_statidx = NLMPROC_##proc, \ | ||
558 | .p_name = #proc, \ | ||
559 | } | ||
560 | |||
561 | static struct rpc_procinfo nlm4_procedures[] = { | ||
562 | PROC(TEST, testargs, testres), | ||
563 | PROC(LOCK, lockargs, res), | ||
564 | PROC(CANCEL, cancargs, res), | ||
565 | PROC(UNLOCK, unlockargs, res), | ||
566 | PROC(GRANTED, testargs, res), | ||
567 | PROC(TEST_MSG, testargs, norep), | ||
568 | PROC(LOCK_MSG, lockargs, norep), | ||
569 | PROC(CANCEL_MSG, cancargs, norep), | ||
570 | PROC(UNLOCK_MSG, unlockargs, norep), | ||
571 | PROC(GRANTED_MSG, testargs, norep), | ||
572 | PROC(TEST_RES, testres, norep), | ||
573 | PROC(LOCK_RES, res, norep), | ||
574 | PROC(CANCEL_RES, res, norep), | ||
575 | PROC(UNLOCK_RES, res, norep), | ||
576 | PROC(GRANTED_RES, res, norep), | ||
577 | #ifdef NLMCLNT_SUPPORT_SHARES | ||
578 | PROC(SHARE, shareargs, shareres), | ||
579 | PROC(UNSHARE, shareargs, shareres), | ||
580 | PROC(NM_LOCK, lockargs, res), | ||
581 | PROC(FREE_ALL, notify, void), | ||
582 | #endif | ||
583 | }; | ||
584 | |||
585 | struct rpc_version nlm_version4 = { | ||
586 | .number = 4, | ||
587 | .nrprocs = 24, | ||
588 | .procs = nlm4_procedures, | ||
589 | }; | ||