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 | }; | ||
