Blame view

fs/nfs/callback_xdr.c 24.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * linux/fs/nfs/callback_xdr.c
   *
   * Copyright (C) 2004 Trond Myklebust
   *
   * NFSv4 callback encode/decode procedures
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
  #include <linux/kernel.h>
  #include <linux/sunrpc/svc.h>
  #include <linux/nfs4.h>
  #include <linux/nfs_fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
12
  #include <linux/slab.h>
c36fca52f   Andy Adamson   NFS refactor nfs_...
13
  #include <linux/sunrpc/bc_xprt.h>
4ce79717c   Trond Myklebust   [PATCH] NFS: Head...
14
  #include "nfs4_fs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include "callback.h"
c36fca52f   Andy Adamson   NFS refactor nfs_...
16
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
  
  #define CB_OP_TAGLEN_MAXSZ	(512)
  #define CB_OP_HDR_RES_MAXSZ	(2 + CB_OP_TAGLEN_MAXSZ)
  #define CB_OP_GETATTR_BITMAP_MAXSZ	(4)
  #define CB_OP_GETATTR_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ + \
  				CB_OP_GETATTR_BITMAP_MAXSZ + \
  				2 + 2 + 3 + 3)
  #define CB_OP_RECALL_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
25
  #if defined(CONFIG_NFS_V4_1)
f2a625616   Fred Isaman   pnfs: CB_LAYOUTRE...
26
  #define CB_OP_LAYOUTRECALL_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
1be5683b0   Marc Eshel   pnfs: CB_NOTIFY_D...
27
  #define CB_OP_DEVICENOTIFY_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
28
29
  #define CB_OP_SEQUENCE_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ + \
  					4 + 1 + 3)
31f096077   Alexandros Batsakis   nfs41: V2 initial...
30
  #define CB_OP_RECALLANY_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
b9efa1b27   Andy Adamson   nfs41: implement ...
31
  #define CB_OP_RECALLSLOT_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
32
  #endif /* CONFIG_NFS_V4_1 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  #define NFSDBG_FACILITY NFSDBG_CALLBACK
31d2b4356   Andy Adamson   nfs41: fix wrong ...
34
35
  /* Internal error code */
  #define NFS4ERR_RESOURCE_HDR	11050
c36fca52f   Andy Adamson   NFS refactor nfs_...
36
37
  typedef __be32 (*callback_process_op_t)(void *, void *,
  					struct cb_process_state *);
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
38
39
  typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
  typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
47
48
49
  
  
  struct callback_op {
  	callback_process_op_t process_op;
  	callback_decode_arg_t decode_args;
  	callback_encode_res_t encode_res;
  	long res_maxsize;
  };
  
  static struct callback_op callback_ops[];
7111c66e4   Al Viro   [PATCH] fix svc_p...
50
  static __be32 nfs4_callback_null(struct svc_rqst *rqstp, void *argp, void *resp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
  {
  	return htonl(NFS4_OK);
  }
5704fdeb4   Al Viro   [PATCH] xdr annot...
54
  static int nfs4_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
  {
  	return xdr_argsize_check(rqstp, p);
  }
5704fdeb4   Al Viro   [PATCH] xdr annot...
58
  static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
61
  {
  	return xdr_ressize_check(rqstp, p);
  }
5704fdeb4   Al Viro   [PATCH] xdr annot...
62
  static __be32 *read_buf(struct xdr_stream *xdr, int nbytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
64
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
  
  	p = xdr_inline_decode(xdr, nbytes);
  	if (unlikely(p == NULL))
  		printk(KERN_WARNING "NFSv4 callback reply buffer overflowed!
  ");
  	return p;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
72
  static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
74
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	*len = ntohl(*p);
  
  	if (*len != 0) {
  		p = read_buf(xdr, *len);
  		if (unlikely(p == NULL))
  			return htonl(NFS4ERR_RESOURCE);
  		*str = (const char *)p;
  	} else
  		*str = NULL;
  
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
91
  static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
93
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	fh->size = ntohl(*p);
  	if (fh->size > NFS4_FHSIZE)
  		return htonl(NFS4ERR_BADHANDLE);
  	p = read_buf(xdr, fh->size);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	memcpy(&fh->data[0], p, fh->size);
  	memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
108
  static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
110
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  	unsigned int attrlen;
  
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	attrlen = ntohl(*p);
  	p = read_buf(xdr, attrlen << 2);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	if (likely(attrlen > 0))
  		bitmap[0] = ntohl(*p++);
  	if (attrlen > 1)
  		bitmap[1] = ntohl(*p);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
126
  static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
128
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
135
  
  	p = read_buf(xdr, 16);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	memcpy(stateid->data, p, 16);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
136
  static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
138
  	__be32 *p;
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
139
  	__be32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
  
  	status = decode_string(xdr, &hdr->taglen, &hdr->tag);
  	if (unlikely(status != 0))
  		return status;
  	/* We do not like overly long tags! */
5cce428d9   Chuck Lever   NFS: Remove an un...
145
  	if (hdr->taglen > CB_OP_TAGLEN_MAXSZ - 12) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
  		printk("NFSv4 CALLBACK %s: client sent tag of length %u
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
148
  				__func__, hdr->taglen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
  		return htonl(NFS4ERR_RESOURCE);
  	}
  	p = read_buf(xdr, 12);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
b8f2ef84b   Benny Halevy   nfs41: store mino...
154
  	hdr->minorversion = ntohl(*p++);
48a9e2d22   Benny Halevy   nfs41: decode min...
155
156
  	/* Check minor version is zero or one. */
  	if (hdr->minorversion <= 1) {
c36fca52f   Andy Adamson   NFS refactor nfs_...
157
  		hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
48a9e2d22   Benny Halevy   nfs41: decode min...
158
  	} else {
b8f2ef84b   Benny Halevy   nfs41: store mino...
159
160
161
162
  		printk(KERN_WARNING "%s: NFSv4 server callback with "
  			"illegal minor version %u!
  ",
  			__func__, hdr->minorversion);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
  		return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  	hdr->nops = ntohl(*p);
b8f2ef84b   Benny Halevy   nfs41: store mino...
166
167
168
  	dprintk("%s: minorversion %d nops %d
  ", __func__,
  		hdr->minorversion, hdr->nops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
171
  static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
173
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
31d2b4356   Andy Adamson   nfs41: fix wrong ...
176
  		return htonl(NFS4ERR_RESOURCE_HDR);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  	*op = ntohl(*p);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
180
  static __be32 decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_getattrargs *args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  {
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
182
  	__be32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
  
  	status = decode_fh(xdr, &args->fh);
  	if (unlikely(status != 0))
  		goto out;
671beed7e   Chuck Lever   NFS: Change cb_ge...
187
  	args->addr = svc_addr(rqstp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  	status = decode_bitmap(xdr, args->bitmap);
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
190
191
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	return status;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
194
  static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
196
  	__be32 *p;
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
197
  	__be32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198

c1d358665   Chuck Lever   NFS: Change cb_re...
199
  	args->addr = svc_addr(rqstp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
  	status = decode_stateid(xdr, &args->stateid);
  	if (unlikely(status != 0))
  		goto out;
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL)) {
  		status = htonl(NFS4ERR_RESOURCE);
  		goto out;
  	}
  	args->truncate = ntohl(*p);
  	status = decode_fh(xdr, &args->fh);
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
211
212
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
3873bc50e   Alexey Dobriyan   NFSv4: really ret...
213
  	return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  }
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
215
  #if defined(CONFIG_NFS_V4_1)
f2a625616   Fred Isaman   pnfs: CB_LAYOUTRE...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
  				       struct xdr_stream *xdr,
  				       struct cb_layoutrecallargs *args)
  {
  	__be32 *p;
  	__be32 status = 0;
  	uint32_t iomode;
  
  	args->cbl_addr = svc_addr(rqstp);
  	p = read_buf(xdr, 4 * sizeof(uint32_t));
  	if (unlikely(p == NULL)) {
  		status = htonl(NFS4ERR_BADXDR);
  		goto out;
  	}
  
  	args->cbl_layout_type = ntohl(*p++);
  	/* Depite the spec's xdr, iomode really belongs in the FILE switch,
25985edce   Lucas De Marchi   Fix common misspe...
233
  	 * as it is unusable and ignored with the other types.
f2a625616   Fred Isaman   pnfs: CB_LAYOUTRE...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  	 */
  	iomode = ntohl(*p++);
  	args->cbl_layoutchanged = ntohl(*p++);
  	args->cbl_recall_type = ntohl(*p++);
  
  	if (args->cbl_recall_type == RETURN_FILE) {
  		args->cbl_range.iomode = iomode;
  		status = decode_fh(xdr, &args->cbl_fh);
  		if (unlikely(status != 0))
  			goto out;
  
  		p = read_buf(xdr, 2 * sizeof(uint64_t));
  		if (unlikely(p == NULL)) {
  			status = htonl(NFS4ERR_BADXDR);
  			goto out;
  		}
  		p = xdr_decode_hyper(p, &args->cbl_range.offset);
  		p = xdr_decode_hyper(p, &args->cbl_range.length);
  		status = decode_stateid(xdr, &args->cbl_stateid);
  		if (unlikely(status != 0))
  			goto out;
  	} else if (args->cbl_recall_type == RETURN_FSID) {
  		p = read_buf(xdr, 2 * sizeof(uint64_t));
  		if (unlikely(p == NULL)) {
  			status = htonl(NFS4ERR_BADXDR);
  			goto out;
  		}
  		p = xdr_decode_hyper(p, &args->cbl_fsid.major);
  		p = xdr_decode_hyper(p, &args->cbl_fsid.minor);
  	} else if (args->cbl_recall_type != RETURN_ALL) {
  		status = htonl(NFS4ERR_BADXDR);
  		goto out;
  	}
  	dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d
  ",
  		__func__,
  		args->cbl_layout_type, iomode,
  		args->cbl_layoutchanged, args->cbl_recall_type);
  out:
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
  	return status;
  }
1be5683b0   Marc Eshel   pnfs: CB_NOTIFY_D...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  static
  __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
  				struct xdr_stream *xdr,
  				struct cb_devicenotifyargs *args)
  {
  	__be32 *p;
  	__be32 status = 0;
  	u32 tmp;
  	int n, i;
  	args->ndevs = 0;
  
  	/* Num of device notifications */
  	p = read_buf(xdr, sizeof(uint32_t));
  	if (unlikely(p == NULL)) {
  		status = htonl(NFS4ERR_BADXDR);
  		goto out;
  	}
  	n = ntohl(*p++);
  	if (n <= 0)
  		goto out;
363e0df05   Dan Carpenter   nfs: check for in...
297
298
299
300
  	if (n > ULONG_MAX / sizeof(*args->devs)) {
  		status = htonl(NFS4ERR_BADXDR);
  		goto out;
  	}
1be5683b0   Marc Eshel   pnfs: CB_NOTIFY_D...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
  
  	args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL);
  	if (!args->devs) {
  		status = htonl(NFS4ERR_DELAY);
  		goto out;
  	}
  
  	/* Decode each dev notification */
  	for (i = 0; i < n; i++) {
  		struct cb_devicenotifyitem *dev = &args->devs[i];
  
  		p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE);
  		if (unlikely(p == NULL)) {
  			status = htonl(NFS4ERR_BADXDR);
  			goto err;
  		}
  
  		tmp = ntohl(*p++);	/* bitmap size */
  		if (tmp != 1) {
  			status = htonl(NFS4ERR_INVAL);
  			goto err;
  		}
  		dev->cbd_notify_type = ntohl(*p++);
  		if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE &&
  		    dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) {
  			status = htonl(NFS4ERR_INVAL);
  			goto err;
  		}
  
  		tmp = ntohl(*p++);	/* opaque size */
  		if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) &&
  		     (tmp != NFS4_DEVICEID4_SIZE + 8)) ||
  		    ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) &&
  		     (tmp != NFS4_DEVICEID4_SIZE + 4))) {
  			status = htonl(NFS4ERR_INVAL);
  			goto err;
  		}
  		dev->cbd_layout_type = ntohl(*p++);
  		memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE);
  		p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
  
  		if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) {
  			p = read_buf(xdr, sizeof(uint32_t));
  			if (unlikely(p == NULL)) {
  				status = htonl(NFS4ERR_BADXDR);
  				goto err;
  			}
  			dev->cbd_immediate = ntohl(*p++);
  		} else {
  			dev->cbd_immediate = 0;
  		}
  
  		args->ndevs++;
  
  		dprintk("%s: type %d layout 0x%x immediate %d
  ",
  			__func__, dev->cbd_notify_type, dev->cbd_layout_type,
  			dev->cbd_immediate);
  	}
  out:
  	dprintk("%s: status %d ndevs %d
  ",
  		__func__, ntohl(status), args->ndevs);
  	return status;
  err:
  	kfree(args->devs);
  	goto out;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
369
  static __be32 decode_sessionid(struct xdr_stream *xdr,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
370
371
  				 struct nfs4_sessionid *sid)
  {
9733f0d92   Andy Adamson   nfs41: cleanup ca...
372
  	__be32 *p;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
373
374
375
376
  	int len = NFS4_MAX_SESSIONID_LEN;
  
  	p = read_buf(xdr, len);
  	if (unlikely(p == NULL))
a419aef8b   Joe Perches   trivial: remove u...
377
  		return htonl(NFS4ERR_RESOURCE);
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
378
379
380
381
  
  	memcpy(sid->data, p, len);
  	return 0;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
382
  static __be32 decode_rc_list(struct xdr_stream *xdr,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
383
384
  			       struct referring_call_list *rc_list)
  {
9733f0d92   Andy Adamson   nfs41: cleanup ca...
385
  	__be32 *p;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
386
  	int i;
9733f0d92   Andy Adamson   nfs41: cleanup ca...
387
  	__be32 status;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  
  	status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
  	if (status)
  		goto out;
  
  	status = htonl(NFS4ERR_RESOURCE);
  	p = read_buf(xdr, sizeof(uint32_t));
  	if (unlikely(p == NULL))
  		goto out;
  
  	rc_list->rcl_nrefcalls = ntohl(*p++);
  	if (rc_list->rcl_nrefcalls) {
  		p = read_buf(xdr,
  			     rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
  		if (unlikely(p == NULL))
  			goto out;
  		rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
  						sizeof(*rc_list->rcl_refcalls),
  						GFP_KERNEL);
  		if (unlikely(rc_list->rcl_refcalls == NULL))
  			goto out;
  		for (i = 0; i < rc_list->rcl_nrefcalls; i++) {
  			rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++);
  			rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++);
  		}
  	}
  	status = 0;
  
  out:
  	return status;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
419
  static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
420
421
422
  					struct xdr_stream *xdr,
  					struct cb_sequenceargs *args)
  {
9733f0d92   Andy Adamson   nfs41: cleanup ca...
423
  	__be32 *p;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
424
  	int i;
9733f0d92   Andy Adamson   nfs41: cleanup ca...
425
  	__be32 status;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
426
427
428
429
430
431
432
433
434
  
  	status = decode_sessionid(xdr, &args->csa_sessionid);
  	if (status)
  		goto out;
  
  	status = htonl(NFS4ERR_RESOURCE);
  	p = read_buf(xdr, 5 * sizeof(uint32_t));
  	if (unlikely(p == NULL))
  		goto out;
65fc64e54   Ricardo Labiaga   nfs41: Backchanne...
435
  	args->csa_addr = svc_addr(rqstp);
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  	args->csa_sequenceid = ntohl(*p++);
  	args->csa_slotid = ntohl(*p++);
  	args->csa_highestslotid = ntohl(*p++);
  	args->csa_cachethis = ntohl(*p++);
  	args->csa_nrclists = ntohl(*p++);
  	args->csa_rclists = NULL;
  	if (args->csa_nrclists) {
  		args->csa_rclists = kmalloc(args->csa_nrclists *
  					    sizeof(*args->csa_rclists),
  					    GFP_KERNEL);
  		if (unlikely(args->csa_rclists == NULL))
  			goto out;
  
  		for (i = 0; i < args->csa_nrclists; i++) {
  			status = decode_rc_list(xdr, &args->csa_rclists[i]);
  			if (status)
  				goto out_free;
  		}
  	}
  	status = 0;
  
  	dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u "
  		"highestslotid %u cachethis %d nrclists %u
  ",
  		__func__,
  		((u32 *)&args->csa_sessionid)[0],
  		((u32 *)&args->csa_sessionid)[1],
  		((u32 *)&args->csa_sessionid)[2],
  		((u32 *)&args->csa_sessionid)[3],
  		args->csa_sequenceid, args->csa_slotid,
  		args->csa_highestslotid, args->csa_cachethis,
  		args->csa_nrclists);
  out:
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
  	return status;
  
  out_free:
  	for (i = 0; i < args->csa_nrclists; i++)
  		kfree(args->csa_rclists[i].rcl_refcalls);
  	kfree(args->csa_rclists);
  	goto out;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
479
  static __be32 decode_recallany_args(struct svc_rqst *rqstp,
31f096077   Alexandros Batsakis   nfs41: V2 initial...
480
481
482
  				      struct xdr_stream *xdr,
  				      struct cb_recallanyargs *args)
  {
d743c3c9c   Peng Tao   NFS4: fix cb_reca...
483
484
  	uint32_t bitmap[2];
  	__be32 *p, status;
31f096077   Alexandros Batsakis   nfs41: V2 initial...
485
486
487
488
489
490
  
  	args->craa_addr = svc_addr(rqstp);
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_BADXDR);
  	args->craa_objs_to_keep = ntohl(*p++);
d743c3c9c   Peng Tao   NFS4: fix cb_reca...
491
492
493
494
  	status = decode_bitmap(xdr, bitmap);
  	if (unlikely(status))
  		return status;
  	args->craa_type_mask = bitmap[0];
31f096077   Alexandros Batsakis   nfs41: V2 initial...
495
496
497
  
  	return 0;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
498
  static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
b9efa1b27   Andy Adamson   nfs41: implement ...
499
500
501
502
503
504
505
506
507
508
509
510
  					struct xdr_stream *xdr,
  					struct cb_recallslotargs *args)
  {
  	__be32 *p;
  
  	args->crsa_addr = svc_addr(rqstp);
  	p = read_buf(xdr, 4);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_BADXDR);
  	args->crsa_target_max_slots = ntohl(*p++);
  	return 0;
  }
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
511
  #endif /* CONFIG_NFS_V4_1 */
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
512
  static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
514
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
522
523
524
  
  	p = xdr_reserve_space(xdr, 4 + len);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	xdr_encode_opaque(p, str, len);
  	return 0;
  }
  
  #define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)
  #define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
5704fdeb4   Al Viro   [PATCH] xdr annot...
525
  static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, __be32 **savep)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
527
528
  	__be32 bm[2];
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  
  	bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
  	bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
  	if (bm[1] != 0) {
  		p = xdr_reserve_space(xdr, 16);
  		if (unlikely(p == NULL))
  			return htonl(NFS4ERR_RESOURCE);
  		*p++ = htonl(2);
  		*p++ = bm[0];
  		*p++ = bm[1];
  	} else if (bm[0] != 0) {
  		p = xdr_reserve_space(xdr, 12);
  		if (unlikely(p == NULL))
  			return htonl(NFS4ERR_RESOURCE);
  		*p++ = htonl(1);
  		*p++ = bm[0];
  	} else {
  		p = xdr_reserve_space(xdr, 8);
  		if (unlikely(p == NULL))
  			return htonl(NFS4ERR_RESOURCE);
  		*p++ = htonl(0);
  	}
  	*savep = p;
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
554
  static __be32 encode_attr_change(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t change)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
556
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
  
  	if (!(bitmap[0] & FATTR4_WORD0_CHANGE))
  		return 0;
  	p = xdr_reserve_space(xdr, 8);
90dc7d279   Harvey Harrison   nfs: fix sparse w...
561
  	if (unlikely(!p))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
  		return htonl(NFS4ERR_RESOURCE);
  	p = xdr_encode_hyper(p, change);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
566
  static __be32 encode_attr_size(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
568
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
  
  	if (!(bitmap[0] & FATTR4_WORD0_SIZE))
  		return 0;
  	p = xdr_reserve_space(xdr, 8);
90dc7d279   Harvey Harrison   nfs: fix sparse w...
573
  	if (unlikely(!p))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
  		return htonl(NFS4ERR_RESOURCE);
  	p = xdr_encode_hyper(p, size);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
578
  static __be32 encode_attr_time(struct xdr_stream *xdr, const struct timespec *time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
580
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
  
  	p = xdr_reserve_space(xdr, 12);
90dc7d279   Harvey Harrison   nfs: fix sparse w...
583
  	if (unlikely(!p))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
  		return htonl(NFS4ERR_RESOURCE);
  	p = xdr_encode_hyper(p, time->tv_sec);
  	*p = htonl(time->tv_nsec);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
589
  static __be32 encode_attr_ctime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
  {
  	if (!(bitmap[1] & FATTR4_WORD1_TIME_METADATA))
  		return 0;
  	return encode_attr_time(xdr,time);
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
595
  static __be32 encode_attr_mtime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
  {
  	if (!(bitmap[1] & FATTR4_WORD1_TIME_MODIFY))
  		return 0;
  	return encode_attr_time(xdr,time);
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
601
  static __be32 encode_compound_hdr_res(struct xdr_stream *xdr, struct cb_compound_hdr_res *hdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  {
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
603
  	__be32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
609
610
611
612
613
614
615
  
  	hdr->status = xdr_reserve_space(xdr, 4);
  	if (unlikely(hdr->status == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	status = encode_string(xdr, hdr->taglen, hdr->tag);
  	if (unlikely(status != 0))
  		return status;
  	hdr->nops = xdr_reserve_space(xdr, 4);
  	if (unlikely(hdr->nops == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
616
  static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
618
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
  	
  	p = xdr_reserve_space(xdr, 8);
  	if (unlikely(p == NULL))
31d2b4356   Andy Adamson   nfs41: fix wrong ...
622
  		return htonl(NFS4ERR_RESOURCE_HDR);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
626
  	*p++ = htonl(op);
  	*p = res;
  	return 0;
  }
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
627
  static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  {
5704fdeb4   Al Viro   [PATCH] xdr annot...
629
  	__be32 *savep = NULL;
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
630
  	__be32 status = res->status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  	
  	if (unlikely(status != 0))
  		goto out;
  	status = encode_attr_bitmap(xdr, res->bitmap, &savep);
  	if (unlikely(status != 0))
  		goto out;
  	status = encode_attr_change(xdr, res->bitmap, res->change_attr);
  	if (unlikely(status != 0))
  		goto out;
  	status = encode_attr_size(xdr, res->bitmap, res->size);
  	if (unlikely(status != 0))
  		goto out;
  	status = encode_attr_ctime(xdr, res->bitmap, &res->ctime);
  	if (unlikely(status != 0))
  		goto out;
  	status = encode_attr_mtime(xdr, res->bitmap, &res->mtime);
  	*savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1)));
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
649
650
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
  	return status;
  }
34bc47c94   Benny Halevy   nfs41: consider m...
653
  #if defined(CONFIG_NFS_V4_1)
9733f0d92   Andy Adamson   nfs41: cleanup ca...
654
  static __be32 encode_sessionid(struct xdr_stream *xdr,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
655
656
  				 const struct nfs4_sessionid *sid)
  {
9733f0d92   Andy Adamson   nfs41: cleanup ca...
657
  	__be32 *p;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
658
659
660
661
662
663
664
665
666
  	int len = NFS4_MAX_SESSIONID_LEN;
  
  	p = xdr_reserve_space(xdr, len);
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  
  	memcpy(p, sid, len);
  	return 0;
  }
9733f0d92   Andy Adamson   nfs41: cleanup ca...
667
  static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
668
669
670
  				       struct xdr_stream *xdr,
  				       const struct cb_sequenceres *res)
  {
9733f0d92   Andy Adamson   nfs41: cleanup ca...
671
  	__be32 *p;
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  	unsigned status = res->csr_status;
  
  	if (unlikely(status != 0))
  		goto out;
  
  	encode_sessionid(xdr, &res->csr_sessionid);
  
  	p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t));
  	if (unlikely(p == NULL))
  		return htonl(NFS4ERR_RESOURCE);
  
  	*p++ = htonl(res->csr_sequenceid);
  	*p++ = htonl(res->csr_slotid);
  	*p++ = htonl(res->csr_highestslotid);
  	*p++ = htonl(res->csr_target_highestslotid);
  out:
  	dprintk("%s: exit with status = %d
  ", __func__, ntohl(status));
  	return status;
  }
34bc47c94   Benny Halevy   nfs41: consider m...
692
693
694
  static __be32
  preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
  {
281fe15dc   Benny Halevy   nfs41: verify CB_...
695
696
697
698
699
700
701
  	if (op_nr == OP_CB_SEQUENCE) {
  		if (nop != 0)
  			return htonl(NFS4ERR_SEQUENCE_POS);
  	} else {
  		if (nop == 0)
  			return htonl(NFS4ERR_OP_NOT_IN_SESSION);
  	}
34bc47c94   Benny Halevy   nfs41: consider m...
702
703
704
  	switch (op_nr) {
  	case OP_CB_GETATTR:
  	case OP_CB_RECALL:
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
705
  	case OP_CB_SEQUENCE:
31f096077   Alexandros Batsakis   nfs41: V2 initial...
706
  	case OP_CB_RECALL_ANY:
b9efa1b27   Andy Adamson   nfs41: implement ...
707
  	case OP_CB_RECALL_SLOT:
f2a625616   Fred Isaman   pnfs: CB_LAYOUTRE...
708
  	case OP_CB_LAYOUTRECALL:
1be5683b0   Marc Eshel   pnfs: CB_NOTIFY_D...
709
  	case OP_CB_NOTIFY_DEVICEID:
34bc47c94   Benny Halevy   nfs41: consider m...
710
711
  		*op = &callback_ops[op_nr];
  		break;
34bc47c94   Benny Halevy   nfs41: consider m...
712
713
  	case OP_CB_NOTIFY:
  	case OP_CB_PUSH_DELEG:
34bc47c94   Benny Halevy   nfs41: consider m...
714
  	case OP_CB_RECALLABLE_OBJ_AVAIL:
34bc47c94   Benny Halevy   nfs41: consider m...
715
716
717
718
719
720
721
722
723
724
  	case OP_CB_WANTS_CANCELLED:
  	case OP_CB_NOTIFY_LOCK:
  		return htonl(NFS4ERR_NOTSUPP);
  
  	default:
  		return htonl(NFS4ERR_OP_ILLEGAL);
  	}
  
  	return htonl(NFS_OK);
  }
42acd0218   Andy Adamson   NFS add session b...
725
726
727
728
729
730
731
732
733
  static void nfs4_callback_free_slot(struct nfs4_session *session)
  {
  	struct nfs4_slot_table *tbl = &session->bc_slot_table;
  
  	spin_lock(&tbl->slot_tbl_lock);
  	/*
  	 * Let the state manager know callback processing done.
  	 * A single slot, so highest used slotid is either 0 or -1
  	 */
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
734
  	tbl->highest_used_slotid = -1;
42acd0218   Andy Adamson   NFS add session b...
735
736
737
  	nfs4_check_drain_bc_complete(session);
  	spin_unlock(&tbl->slot_tbl_lock);
  }
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
738
  static void nfs4_cb_free_slot(struct cb_process_state *cps)
42acd0218   Andy Adamson   NFS add session b...
739
  {
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
740
741
  	if (cps->slotid != -1)
  		nfs4_callback_free_slot(cps->clp->cl_session);
42acd0218   Andy Adamson   NFS add session b...
742
  }
34bc47c94   Benny Halevy   nfs41: consider m...
743
744
745
746
747
748
749
  #else /* CONFIG_NFS_V4_1 */
  
  static __be32
  preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
  {
  	return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
  }
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
750
  static void nfs4_cb_free_slot(struct cb_process_state *cps)
42acd0218   Andy Adamson   NFS add session b...
751
752
  {
  }
34bc47c94   Benny Halevy   nfs41: consider m...
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  #endif /* CONFIG_NFS_V4_1 */
  
  static __be32
  preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
  {
  	switch (op_nr) {
  	case OP_CB_GETATTR:
  	case OP_CB_RECALL:
  		*op = &callback_ops[op_nr];
  		break;
  	default:
  		return htonl(NFS4ERR_OP_ILLEGAL);
  	}
  
  	return htonl(NFS_OK);
  }
  
  static __be32 process_op(uint32_t minorversion, int nop,
  		struct svc_rqst *rqstp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
  		struct xdr_stream *xdr_in, void *argp,
c36fca52f   Andy Adamson   NFS refactor nfs_...
773
774
  		struct xdr_stream *xdr_out, void *resp,
  		struct cb_process_state *cps)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
  {
a162a6b80   Trond Myklebust   NFSv4: Kill brain...
776
  	struct callback_op *op = &callback_ops[0];
31d2b4356   Andy Adamson   nfs41: fix wrong ...
777
  	unsigned int op_nr;
34bc47c94   Benny Halevy   nfs41: consider m...
778
  	__be32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
  	long maxlen;
e6f684f64   Al Viro   [PATCH] fs/nfs/ca...
780
  	__be32 res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781

3110ff804   Harvey Harrison   nfs: replace rema...
782
783
  	dprintk("%s: start
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
  	status = decode_op_hdr(xdr_in, &op_nr);
31d2b4356   Andy Adamson   nfs41: fix wrong ...
785
786
  	if (unlikely(status))
  		return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787

34bc47c94   Benny Halevy   nfs41: consider m...
788
789
790
791
792
793
794
795
  	dprintk("%s: minorversion=%d nop=%d op_nr=%u
  ",
  		__func__, minorversion, nop, op_nr);
  
  	status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
  				preprocess_nfs4_op(op_nr, &op);
  	if (status == htonl(NFS4ERR_OP_ILLEGAL))
  		op_nr = OP_CB_ILLEGAL;
b92b30190   Andy Adamson   nfs41: directly e...
796
797
  	if (status)
  		goto encode_hdr;
31d2b4356   Andy Adamson   nfs41: fix wrong ...
798

c36fca52f   Andy Adamson   NFS refactor nfs_...
799
800
  	if (cps->drc_status) {
  		status = cps->drc_status;
4911096f1   Andy Adamson   nfs41: back chann...
801
802
  		goto encode_hdr;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
  	maxlen = xdr_out->end - xdr_out->p;
  	if (maxlen > 0 && maxlen < PAGE_SIZE) {
e95e60dae   Andy Adamson   nfs41: remove une...
805
806
  		status = op->decode_args(rqstp, xdr_in, argp);
  		if (likely(status == 0))
c36fca52f   Andy Adamson   NFS refactor nfs_...
807
  			status = op->process_op(argp, resp, cps);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
  	} else
  		status = htonl(NFS4ERR_RESOURCE);
b92b30190   Andy Adamson   nfs41: directly e...
810
  encode_hdr:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  	res = encode_op_hdr(xdr_out, op_nr, status);
31d2b4356   Andy Adamson   nfs41: fix wrong ...
812
813
  	if (unlikely(res))
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
  	if (op->encode_res != NULL && status == 0)
  		status = op->encode_res(rqstp, xdr_out, resp);
3110ff804   Harvey Harrison   nfs: replace rema...
816
817
  	dprintk("%s: done, status = %d
  ", __func__, ntohl(status));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
821
822
823
  	return status;
  }
  
  /*
   * Decode, process and encode a COMPOUND
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
824
  static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
  {
3a6258e1f   Trond Myklebust   NFSv4: Check the ...
826
827
  	struct cb_compound_hdr_arg hdr_arg = { 0 };
  	struct cb_compound_hdr_res hdr_res = { NULL };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
  	struct xdr_stream xdr_in, xdr_out;
c36fca52f   Andy Adamson   NFS refactor nfs_...
829
830
831
832
  	__be32 *p, status;
  	struct cb_process_state cps = {
  		.drc_status = 0,
  		.clp = NULL,
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
833
  		.slotid = -1,
c36fca52f   Andy Adamson   NFS refactor nfs_...
834
  	};
3a6258e1f   Trond Myklebust   NFSv4: Check the ...
835
  	unsigned int nops = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836

3110ff804   Harvey Harrison   nfs: replace rema...
837
838
  	dprintk("%s: start
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
  
  	xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
5704fdeb4   Al Viro   [PATCH] xdr annot...
841
  	p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
  	xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
3a6258e1f   Trond Myklebust   NFSv4: Check the ...
843
844
845
  	status = decode_compound_hdr_arg(&xdr_in, &hdr_arg);
  	if (status == __constant_htonl(NFS4ERR_RESOURCE))
  		return rpc_garbage_args;
c36fca52f   Andy Adamson   NFS refactor nfs_...
846
847
  	if (hdr_arg.minorversion == 0) {
  		cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident);
778be232a   Andy Adamson   NFS do not find c...
848
  		if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
c36fca52f   Andy Adamson   NFS refactor nfs_...
849
  			return rpc_drop_reply;
778be232a   Andy Adamson   NFS do not find c...
850
  	}
c36fca52f   Andy Adamson   NFS refactor nfs_...
851

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
  	hdr_res.taglen = hdr_arg.taglen;
  	hdr_res.tag = hdr_arg.tag;
3a6258e1f   Trond Myklebust   NFSv4: Check the ...
854
855
  	if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
  		return rpc_system_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856

3a6258e1f   Trond Myklebust   NFSv4: Check the ...
857
  	while (status == 0 && nops != hdr_arg.nops) {
4911096f1   Andy Adamson   nfs41: back chann...
858
  		status = process_op(hdr_arg.minorversion, nops, rqstp,
c36fca52f   Andy Adamson   NFS refactor nfs_...
859
  				    &xdr_in, argp, &xdr_out, resp, &cps);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
  		nops++;
  	}
3a6258e1f   Trond Myklebust   NFSv4: Check the ...
862

31d2b4356   Andy Adamson   nfs41: fix wrong ...
863
864
865
866
867
868
  	/* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return
  	* resource error in cb_compound status without returning op */
  	if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) {
  		status = htonl(NFS4ERR_RESOURCE);
  		nops--;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
  	*hdr_res.status = status;
  	*hdr_res.nops = htonl(nops);
55a673990   Trond Myklebust   NFSv4.1: Fix the ...
871
  	nfs4_cb_free_slot(&cps);
c36fca52f   Andy Adamson   NFS refactor nfs_...
872
  	nfs_put_client(cps.clp);
3110ff804   Harvey Harrison   nfs: replace rema...
873
874
  	dprintk("%s: done, status = %u
  ", __func__, ntohl(status));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
  	return rpc_success;
  }
  
  /*
   * Define NFS4 callback COMPOUND ops.
   */
  static struct callback_op callback_ops[] = {
  	[0] = {
  		.res_maxsize = CB_OP_HDR_RES_MAXSZ,
  	},
  	[OP_CB_GETATTR] = {
  		.process_op = (callback_process_op_t)nfs4_callback_getattr,
  		.decode_args = (callback_decode_arg_t)decode_getattr_args,
  		.encode_res = (callback_encode_res_t)encode_getattr_res,
  		.res_maxsize = CB_OP_GETATTR_RES_MAXSZ,
  	},
  	[OP_CB_RECALL] = {
  		.process_op = (callback_process_op_t)nfs4_callback_recall,
  		.decode_args = (callback_decode_arg_t)decode_recall_args,
  		.res_maxsize = CB_OP_RECALL_RES_MAXSZ,
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
895
896
  	},
  #if defined(CONFIG_NFS_V4_1)
f2a625616   Fred Isaman   pnfs: CB_LAYOUTRE...
897
898
899
900
901
902
  	[OP_CB_LAYOUTRECALL] = {
  		.process_op = (callback_process_op_t)nfs4_callback_layoutrecall,
  		.decode_args =
  			(callback_decode_arg_t)decode_layoutrecall_args,
  		.res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
  	},
1be5683b0   Marc Eshel   pnfs: CB_NOTIFY_D...
903
904
905
906
907
908
  	[OP_CB_NOTIFY_DEVICEID] = {
  		.process_op = (callback_process_op_t)nfs4_callback_devicenotify,
  		.decode_args =
  			(callback_decode_arg_t)decode_devicenotify_args,
  		.res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ,
  	},
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
909
910
911
912
913
914
  	[OP_CB_SEQUENCE] = {
  		.process_op = (callback_process_op_t)nfs4_callback_sequence,
  		.decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
  		.encode_res = (callback_encode_res_t)encode_cb_sequence_res,
  		.res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ,
  	},
31f096077   Alexandros Batsakis   nfs41: V2 initial...
915
916
917
918
919
  	[OP_CB_RECALL_ANY] = {
  		.process_op = (callback_process_op_t)nfs4_callback_recallany,
  		.decode_args = (callback_decode_arg_t)decode_recallany_args,
  		.res_maxsize = CB_OP_RECALLANY_RES_MAXSZ,
  	},
b9efa1b27   Andy Adamson   nfs41: implement ...
920
921
922
923
924
  	[OP_CB_RECALL_SLOT] = {
  		.process_op = (callback_process_op_t)nfs4_callback_recallslot,
  		.decode_args = (callback_decode_arg_t)decode_recallslot_args,
  		.res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ,
  	},
4aece6a19   Benny Halevy   nfs41: cb_sequenc...
925
  #endif /* CONFIG_NFS_V4_1 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
  };
  
  /*
   * Define NFS4 callback procedures
   */
  static struct svc_procedure nfs4_callback_procedures1[] = {
  	[CB_NULL] = {
  		.pc_func = nfs4_callback_null,
  		.pc_decode = (kxdrproc_t)nfs4_decode_void,
  		.pc_encode = (kxdrproc_t)nfs4_encode_void,
  		.pc_xdrressize = 1,
  	},
  	[CB_COMPOUND] = {
  		.pc_func = nfs4_callback_compound,
  		.pc_encode = (kxdrproc_t)nfs4_encode_void,
  		.pc_argsize = 256,
  		.pc_ressize = 256,
  		.pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
  	}
  };
  
  struct svc_version nfs4_callback_version1 = {
  	.vs_vers = 1,
  	.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
  	.vs_proc = nfs4_callback_procedures1,
  	.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
  	.vs_dispatch = NULL,
49697ee79   Steve Dickson   nfs4: Make the v4...
953
  	.vs_hidden = 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
  };
07bccc2dd   Alexandros Batsakis   nfs41: add suppor...
955
956
957
958
959
960
  struct svc_version nfs4_callback_version4 = {
  	.vs_vers = 4,
  	.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
  	.vs_proc = nfs4_callback_procedures1,
  	.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
  	.vs_dispatch = NULL,
6070295ef   Jeff Layton   nfs: set vs_hidde...
961
  	.vs_hidden = 1,
07bccc2dd   Alexandros Batsakis   nfs41: add suppor...
962
  };