1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/fs/lockd/clntxdr.c |
4 | * |
5 | * XDR functions to encode/decode NLM version 3 RPC arguments and results. |
6 | * NLM version 3 is backwards compatible with NLM versions 1 and 2. |
7 | * |
8 | * NLM client-side only. |
9 | * |
10 | * Copyright (C) 2010, Oracle. All rights reserved. |
11 | */ |
12 | |
13 | #include <linux/types.h> |
14 | #include <linux/sunrpc/xdr.h> |
15 | #include <linux/sunrpc/clnt.h> |
16 | #include <linux/sunrpc/stats.h> |
17 | #include <linux/lockd/lockd.h> |
18 | |
19 | #include <uapi/linux/nfs2.h> |
20 | |
21 | #define NLMDBG_FACILITY NLMDBG_XDR |
22 | |
23 | #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) |
24 | # error "NLM host name cannot be larger than XDR_MAX_NETOBJ!" |
25 | #endif |
26 | |
27 | /* |
28 | * Declare the space requirements for NLM arguments and replies as |
29 | * number of 32bit-words |
30 | */ |
31 | #define NLM_cookie_sz (1+(NLM_MAXCOOKIELEN>>2)) |
32 | #define NLM_caller_sz (1+(NLMCLNT_OHSIZE>>2)) |
33 | #define NLM_owner_sz (1+(NLMCLNT_OHSIZE>>2)) |
34 | #define NLM_fhandle_sz (1+(NFS2_FHSIZE>>2)) |
35 | #define NLM_lock_sz (3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz) |
36 | #define NLM_holder_sz (4+NLM_owner_sz) |
37 | |
38 | #define NLM_testargs_sz (NLM_cookie_sz+1+NLM_lock_sz) |
39 | #define NLM_lockargs_sz (NLM_cookie_sz+4+NLM_lock_sz) |
40 | #define NLM_cancargs_sz (NLM_cookie_sz+2+NLM_lock_sz) |
41 | #define NLM_unlockargs_sz (NLM_cookie_sz+NLM_lock_sz) |
42 | |
43 | #define NLM_testres_sz (NLM_cookie_sz+1+NLM_holder_sz) |
44 | #define NLM_res_sz (NLM_cookie_sz+1) |
45 | #define NLM_norep_sz (0) |
46 | |
47 | |
48 | static s32 loff_t_to_s32(loff_t offset) |
49 | { |
50 | s32 res; |
51 | |
52 | if (offset >= NLM_OFFSET_MAX) |
53 | res = NLM_OFFSET_MAX; |
54 | else if (offset <= -NLM_OFFSET_MAX) |
55 | res = -NLM_OFFSET_MAX; |
56 | else |
57 | res = offset; |
58 | return res; |
59 | } |
60 | |
61 | static void nlm_compute_offsets(const struct nlm_lock *lock, |
62 | u32 *l_offset, u32 *l_len) |
63 | { |
64 | const struct file_lock *fl = &lock->fl; |
65 | |
66 | *l_offset = loff_t_to_s32(offset: fl->fl_start); |
67 | if (fl->fl_end == OFFSET_MAX) |
68 | *l_len = 0; |
69 | else |
70 | *l_len = loff_t_to_s32(offset: fl->fl_end - fl->fl_start + 1); |
71 | } |
72 | |
73 | /* |
74 | * Encode/decode NLMv3 basic data types |
75 | * |
76 | * Basic NLMv3 data types are not defined in an IETF standards |
77 | * document. X/Open has a description of these data types that |
78 | * is useful. See Chapter 10 of "Protocols for Interworking: |
79 | * XNFS, Version 3W". |
80 | * |
81 | * Not all basic data types have their own encoding and decoding |
82 | * functions. For run-time efficiency, some data types are encoded |
83 | * or decoded inline. |
84 | */ |
85 | |
86 | static void encode_bool(struct xdr_stream *xdr, const int value) |
87 | { |
88 | __be32 *p; |
89 | |
90 | p = xdr_reserve_space(xdr, nbytes: 4); |
91 | *p = value ? xdr_one : xdr_zero; |
92 | } |
93 | |
94 | static void encode_int32(struct xdr_stream *xdr, const s32 value) |
95 | { |
96 | __be32 *p; |
97 | |
98 | p = xdr_reserve_space(xdr, nbytes: 4); |
99 | *p = cpu_to_be32(value); |
100 | } |
101 | |
102 | /* |
103 | * typedef opaque netobj<MAXNETOBJ_SZ> |
104 | */ |
105 | static void encode_netobj(struct xdr_stream *xdr, |
106 | const u8 *data, const unsigned int length) |
107 | { |
108 | __be32 *p; |
109 | |
110 | p = xdr_reserve_space(xdr, nbytes: 4 + length); |
111 | xdr_encode_opaque(p, ptr: data, len: length); |
112 | } |
113 | |
114 | static int decode_netobj(struct xdr_stream *xdr, |
115 | struct xdr_netobj *obj) |
116 | { |
117 | ssize_t ret; |
118 | |
119 | ret = xdr_stream_decode_opaque_inline(xdr, ptr: (void *)&obj->data, |
120 | XDR_MAX_NETOBJ); |
121 | if (unlikely(ret < 0)) |
122 | return -EIO; |
123 | obj->len = ret; |
124 | return 0; |
125 | } |
126 | |
127 | /* |
128 | * netobj cookie; |
129 | */ |
130 | static void encode_cookie(struct xdr_stream *xdr, |
131 | const struct nlm_cookie *cookie) |
132 | { |
133 | encode_netobj(xdr, data: (u8 *)&cookie->data, length: cookie->len); |
134 | } |
135 | |
136 | static int decode_cookie(struct xdr_stream *xdr, |
137 | struct nlm_cookie *cookie) |
138 | { |
139 | u32 length; |
140 | __be32 *p; |
141 | |
142 | p = xdr_inline_decode(xdr, nbytes: 4); |
143 | if (unlikely(p == NULL)) |
144 | goto out_overflow; |
145 | length = be32_to_cpup(p: p++); |
146 | /* apparently HPUX can return empty cookies */ |
147 | if (length == 0) |
148 | goto out_hpux; |
149 | if (length > NLM_MAXCOOKIELEN) |
150 | goto out_size; |
151 | p = xdr_inline_decode(xdr, nbytes: length); |
152 | if (unlikely(p == NULL)) |
153 | goto out_overflow; |
154 | cookie->len = length; |
155 | memcpy(cookie->data, p, length); |
156 | return 0; |
157 | out_hpux: |
158 | cookie->len = 4; |
159 | memset(cookie->data, 0, 4); |
160 | return 0; |
161 | out_size: |
162 | dprintk("NFS: returned cookie was too long: %u\n" , length); |
163 | return -EIO; |
164 | out_overflow: |
165 | return -EIO; |
166 | } |
167 | |
168 | /* |
169 | * netobj fh; |
170 | */ |
171 | static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh) |
172 | { |
173 | encode_netobj(xdr, data: (u8 *)&fh->data, NFS2_FHSIZE); |
174 | } |
175 | |
176 | /* |
177 | * enum nlm_stats { |
178 | * LCK_GRANTED = 0, |
179 | * LCK_DENIED = 1, |
180 | * LCK_DENIED_NOLOCKS = 2, |
181 | * LCK_BLOCKED = 3, |
182 | * LCK_DENIED_GRACE_PERIOD = 4 |
183 | * }; |
184 | * |
185 | * |
186 | * struct nlm_stat { |
187 | * nlm_stats stat; |
188 | * }; |
189 | * |
190 | * NB: we don't swap bytes for the NLM status values. The upper |
191 | * layers deal directly with the status value in network byte |
192 | * order. |
193 | */ |
194 | |
195 | static void encode_nlm_stat(struct xdr_stream *xdr, |
196 | const __be32 stat) |
197 | { |
198 | __be32 *p; |
199 | |
200 | WARN_ON_ONCE(be32_to_cpu(stat) > NLM_LCK_DENIED_GRACE_PERIOD); |
201 | p = xdr_reserve_space(xdr, nbytes: 4); |
202 | *p = stat; |
203 | } |
204 | |
205 | static int decode_nlm_stat(struct xdr_stream *xdr, |
206 | __be32 *stat) |
207 | { |
208 | __be32 *p; |
209 | |
210 | p = xdr_inline_decode(xdr, nbytes: 4); |
211 | if (unlikely(p == NULL)) |
212 | goto out_overflow; |
213 | if (unlikely(ntohl(*p) > ntohl(nlm_lck_denied_grace_period))) |
214 | goto out_enum; |
215 | *stat = *p; |
216 | return 0; |
217 | out_enum: |
218 | dprintk("%s: server returned invalid nlm_stats value: %u\n" , |
219 | __func__, be32_to_cpup(p)); |
220 | return -EIO; |
221 | out_overflow: |
222 | return -EIO; |
223 | } |
224 | |
225 | /* |
226 | * struct nlm_holder { |
227 | * bool exclusive; |
228 | * int uppid; |
229 | * netobj oh; |
230 | * unsigned l_offset; |
231 | * unsigned l_len; |
232 | * }; |
233 | */ |
234 | static void encode_nlm_holder(struct xdr_stream *xdr, |
235 | const struct nlm_res *result) |
236 | { |
237 | const struct nlm_lock *lock = &result->lock; |
238 | u32 l_offset, l_len; |
239 | __be32 *p; |
240 | |
241 | encode_bool(xdr, value: lock->fl.fl_type == F_RDLCK); |
242 | encode_int32(xdr, value: lock->svid); |
243 | encode_netobj(xdr, data: lock->oh.data, length: lock->oh.len); |
244 | |
245 | p = xdr_reserve_space(xdr, nbytes: 4 + 4); |
246 | nlm_compute_offsets(lock, l_offset: &l_offset, l_len: &l_len); |
247 | *p++ = cpu_to_be32(l_offset); |
248 | *p = cpu_to_be32(l_len); |
249 | } |
250 | |
251 | static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result) |
252 | { |
253 | struct nlm_lock *lock = &result->lock; |
254 | struct file_lock *fl = &lock->fl; |
255 | u32 exclusive, l_offset, l_len; |
256 | int error; |
257 | __be32 *p; |
258 | s32 end; |
259 | |
260 | memset(lock, 0, sizeof(*lock)); |
261 | locks_init_lock(fl); |
262 | |
263 | p = xdr_inline_decode(xdr, nbytes: 4 + 4); |
264 | if (unlikely(p == NULL)) |
265 | goto out_overflow; |
266 | exclusive = be32_to_cpup(p: p++); |
267 | lock->svid = be32_to_cpup(p); |
268 | fl->fl_pid = (pid_t)lock->svid; |
269 | |
270 | error = decode_netobj(xdr, obj: &lock->oh); |
271 | if (unlikely(error)) |
272 | goto out; |
273 | |
274 | p = xdr_inline_decode(xdr, nbytes: 4 + 4); |
275 | if (unlikely(p == NULL)) |
276 | goto out_overflow; |
277 | |
278 | fl->fl_flags = FL_POSIX; |
279 | fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK; |
280 | l_offset = be32_to_cpup(p: p++); |
281 | l_len = be32_to_cpup(p); |
282 | end = l_offset + l_len - 1; |
283 | |
284 | fl->fl_start = (loff_t)l_offset; |
285 | if (l_len == 0 || end < 0) |
286 | fl->fl_end = OFFSET_MAX; |
287 | else |
288 | fl->fl_end = (loff_t)end; |
289 | error = 0; |
290 | out: |
291 | return error; |
292 | out_overflow: |
293 | return -EIO; |
294 | } |
295 | |
296 | /* |
297 | * string caller_name<LM_MAXSTRLEN>; |
298 | */ |
299 | static void encode_caller_name(struct xdr_stream *xdr, const char *name) |
300 | { |
301 | /* NB: client-side does not set lock->len */ |
302 | u32 length = strlen(name); |
303 | __be32 *p; |
304 | |
305 | p = xdr_reserve_space(xdr, nbytes: 4 + length); |
306 | xdr_encode_opaque(p, ptr: name, len: length); |
307 | } |
308 | |
309 | /* |
310 | * struct nlm_lock { |
311 | * string caller_name<LM_MAXSTRLEN>; |
312 | * netobj fh; |
313 | * netobj oh; |
314 | * int uppid; |
315 | * unsigned l_offset; |
316 | * unsigned l_len; |
317 | * }; |
318 | */ |
319 | static void encode_nlm_lock(struct xdr_stream *xdr, |
320 | const struct nlm_lock *lock) |
321 | { |
322 | u32 l_offset, l_len; |
323 | __be32 *p; |
324 | |
325 | encode_caller_name(xdr, name: lock->caller); |
326 | encode_fh(xdr, fh: &lock->fh); |
327 | encode_netobj(xdr, data: lock->oh.data, length: lock->oh.len); |
328 | |
329 | p = xdr_reserve_space(xdr, nbytes: 4 + 4 + 4); |
330 | *p++ = cpu_to_be32(lock->svid); |
331 | |
332 | nlm_compute_offsets(lock, l_offset: &l_offset, l_len: &l_len); |
333 | *p++ = cpu_to_be32(l_offset); |
334 | *p = cpu_to_be32(l_len); |
335 | } |
336 | |
337 | |
338 | /* |
339 | * NLMv3 XDR encode functions |
340 | * |
341 | * NLMv3 argument types are defined in Chapter 10 of The Open Group's |
342 | * "Protocols for Interworking: XNFS, Version 3W". |
343 | */ |
344 | |
345 | /* |
346 | * struct nlm_testargs { |
347 | * netobj cookie; |
348 | * bool exclusive; |
349 | * struct nlm_lock alock; |
350 | * }; |
351 | */ |
352 | static void nlm_xdr_enc_testargs(struct rpc_rqst *req, |
353 | struct xdr_stream *xdr, |
354 | const void *data) |
355 | { |
356 | const struct nlm_args *args = data; |
357 | const struct nlm_lock *lock = &args->lock; |
358 | |
359 | encode_cookie(xdr, cookie: &args->cookie); |
360 | encode_bool(xdr, value: lock->fl.fl_type == F_WRLCK); |
361 | encode_nlm_lock(xdr, lock); |
362 | } |
363 | |
364 | /* |
365 | * struct nlm_lockargs { |
366 | * netobj cookie; |
367 | * bool block; |
368 | * bool exclusive; |
369 | * struct nlm_lock alock; |
370 | * bool reclaim; |
371 | * int state; |
372 | * }; |
373 | */ |
374 | static void nlm_xdr_enc_lockargs(struct rpc_rqst *req, |
375 | struct xdr_stream *xdr, |
376 | const void *data) |
377 | { |
378 | const struct nlm_args *args = data; |
379 | const struct nlm_lock *lock = &args->lock; |
380 | |
381 | encode_cookie(xdr, cookie: &args->cookie); |
382 | encode_bool(xdr, value: args->block); |
383 | encode_bool(xdr, value: lock->fl.fl_type == F_WRLCK); |
384 | encode_nlm_lock(xdr, lock); |
385 | encode_bool(xdr, value: args->reclaim); |
386 | encode_int32(xdr, value: args->state); |
387 | } |
388 | |
389 | /* |
390 | * struct nlm_cancargs { |
391 | * netobj cookie; |
392 | * bool block; |
393 | * bool exclusive; |
394 | * struct nlm_lock alock; |
395 | * }; |
396 | */ |
397 | static void nlm_xdr_enc_cancargs(struct rpc_rqst *req, |
398 | struct xdr_stream *xdr, |
399 | const void *data) |
400 | { |
401 | const struct nlm_args *args = data; |
402 | const struct nlm_lock *lock = &args->lock; |
403 | |
404 | encode_cookie(xdr, cookie: &args->cookie); |
405 | encode_bool(xdr, value: args->block); |
406 | encode_bool(xdr, value: lock->fl.fl_type == F_WRLCK); |
407 | encode_nlm_lock(xdr, lock); |
408 | } |
409 | |
410 | /* |
411 | * struct nlm_unlockargs { |
412 | * netobj cookie; |
413 | * struct nlm_lock alock; |
414 | * }; |
415 | */ |
416 | static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req, |
417 | struct xdr_stream *xdr, |
418 | const void *data) |
419 | { |
420 | const struct nlm_args *args = data; |
421 | const struct nlm_lock *lock = &args->lock; |
422 | |
423 | encode_cookie(xdr, cookie: &args->cookie); |
424 | encode_nlm_lock(xdr, lock); |
425 | } |
426 | |
427 | /* |
428 | * struct nlm_res { |
429 | * netobj cookie; |
430 | * nlm_stat stat; |
431 | * }; |
432 | */ |
433 | static void nlm_xdr_enc_res(struct rpc_rqst *req, |
434 | struct xdr_stream *xdr, |
435 | const void *data) |
436 | { |
437 | const struct nlm_res *result = data; |
438 | |
439 | encode_cookie(xdr, cookie: &result->cookie); |
440 | encode_nlm_stat(xdr, stat: result->status); |
441 | } |
442 | |
443 | /* |
444 | * union nlm_testrply switch (nlm_stats stat) { |
445 | * case LCK_DENIED: |
446 | * struct nlm_holder holder; |
447 | * default: |
448 | * void; |
449 | * }; |
450 | * |
451 | * struct nlm_testres { |
452 | * netobj cookie; |
453 | * nlm_testrply test_stat; |
454 | * }; |
455 | */ |
456 | static void encode_nlm_testrply(struct xdr_stream *xdr, |
457 | const struct nlm_res *result) |
458 | { |
459 | if (result->status == nlm_lck_denied) |
460 | encode_nlm_holder(xdr, result); |
461 | } |
462 | |
463 | static void nlm_xdr_enc_testres(struct rpc_rqst *req, |
464 | struct xdr_stream *xdr, |
465 | const void *data) |
466 | { |
467 | const struct nlm_res *result = data; |
468 | |
469 | encode_cookie(xdr, cookie: &result->cookie); |
470 | encode_nlm_stat(xdr, stat: result->status); |
471 | encode_nlm_testrply(xdr, result); |
472 | } |
473 | |
474 | |
475 | /* |
476 | * NLMv3 XDR decode functions |
477 | * |
478 | * NLMv3 result types are defined in Chapter 10 of The Open Group's |
479 | * "Protocols for Interworking: XNFS, Version 3W". |
480 | */ |
481 | |
482 | /* |
483 | * union nlm_testrply switch (nlm_stats stat) { |
484 | * case LCK_DENIED: |
485 | * struct nlm_holder holder; |
486 | * default: |
487 | * void; |
488 | * }; |
489 | * |
490 | * struct nlm_testres { |
491 | * netobj cookie; |
492 | * nlm_testrply test_stat; |
493 | * }; |
494 | */ |
495 | static int decode_nlm_testrply(struct xdr_stream *xdr, |
496 | struct nlm_res *result) |
497 | { |
498 | int error; |
499 | |
500 | error = decode_nlm_stat(xdr, stat: &result->status); |
501 | if (unlikely(error)) |
502 | goto out; |
503 | if (result->status == nlm_lck_denied) |
504 | error = decode_nlm_holder(xdr, result); |
505 | out: |
506 | return error; |
507 | } |
508 | |
509 | static int nlm_xdr_dec_testres(struct rpc_rqst *req, |
510 | struct xdr_stream *xdr, |
511 | void *data) |
512 | { |
513 | struct nlm_res *result = data; |
514 | int error; |
515 | |
516 | error = decode_cookie(xdr, cookie: &result->cookie); |
517 | if (unlikely(error)) |
518 | goto out; |
519 | error = decode_nlm_testrply(xdr, result); |
520 | out: |
521 | return error; |
522 | } |
523 | |
524 | /* |
525 | * struct nlm_res { |
526 | * netobj cookie; |
527 | * nlm_stat stat; |
528 | * }; |
529 | */ |
530 | static int nlm_xdr_dec_res(struct rpc_rqst *req, |
531 | struct xdr_stream *xdr, |
532 | void *data) |
533 | { |
534 | struct nlm_res *result = data; |
535 | int error; |
536 | |
537 | error = decode_cookie(xdr, cookie: &result->cookie); |
538 | if (unlikely(error)) |
539 | goto out; |
540 | error = decode_nlm_stat(xdr, stat: &result->status); |
541 | out: |
542 | return error; |
543 | } |
544 | |
545 | |
546 | /* |
547 | * For NLM, a void procedure really returns nothing |
548 | */ |
549 | #define nlm_xdr_dec_norep NULL |
550 | |
551 | #define PROC(proc, argtype, restype) \ |
552 | [NLMPROC_##proc] = { \ |
553 | .p_proc = NLMPROC_##proc, \ |
554 | .p_encode = nlm_xdr_enc_##argtype, \ |
555 | .p_decode = nlm_xdr_dec_##restype, \ |
556 | .p_arglen = NLM_##argtype##_sz, \ |
557 | .p_replen = NLM_##restype##_sz, \ |
558 | .p_statidx = NLMPROC_##proc, \ |
559 | .p_name = #proc, \ |
560 | } |
561 | |
562 | static const struct rpc_procinfo nlm_procedures[] = { |
563 | PROC(TEST, testargs, testres), |
564 | PROC(LOCK, lockargs, res), |
565 | PROC(CANCEL, cancargs, res), |
566 | PROC(UNLOCK, unlockargs, res), |
567 | PROC(GRANTED, testargs, res), |
568 | PROC(TEST_MSG, testargs, norep), |
569 | PROC(LOCK_MSG, lockargs, norep), |
570 | PROC(CANCEL_MSG, cancargs, norep), |
571 | PROC(UNLOCK_MSG, unlockargs, norep), |
572 | PROC(GRANTED_MSG, testargs, norep), |
573 | PROC(TEST_RES, testres, norep), |
574 | PROC(LOCK_RES, res, norep), |
575 | PROC(CANCEL_RES, res, norep), |
576 | PROC(UNLOCK_RES, res, norep), |
577 | PROC(GRANTED_RES, res, norep), |
578 | }; |
579 | |
580 | static unsigned int nlm_version1_counts[ARRAY_SIZE(nlm_procedures)]; |
581 | static const struct rpc_version nlm_version1 = { |
582 | .number = 1, |
583 | .nrprocs = ARRAY_SIZE(nlm_procedures), |
584 | .procs = nlm_procedures, |
585 | .counts = nlm_version1_counts, |
586 | }; |
587 | |
588 | static unsigned int nlm_version3_counts[ARRAY_SIZE(nlm_procedures)]; |
589 | static const struct rpc_version nlm_version3 = { |
590 | .number = 3, |
591 | .nrprocs = ARRAY_SIZE(nlm_procedures), |
592 | .procs = nlm_procedures, |
593 | .counts = nlm_version3_counts, |
594 | }; |
595 | |
596 | static const struct rpc_version *nlm_versions[] = { |
597 | [1] = &nlm_version1, |
598 | [3] = &nlm_version3, |
599 | #ifdef CONFIG_LOCKD_V4 |
600 | [4] = &nlm_version4, |
601 | #endif |
602 | }; |
603 | |
604 | static struct rpc_stat nlm_rpc_stats; |
605 | |
606 | const struct rpc_program nlm_program = { |
607 | .name = "lockd" , |
608 | .number = NLM_PROGRAM, |
609 | .nrvers = ARRAY_SIZE(nlm_versions), |
610 | .version = nlm_versions, |
611 | .stats = &nlm_rpc_stats, |
612 | }; |
613 | |