Blame view

fs/nfs/nfs2xdr.c 25.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * linux/fs/nfs/nfs2xdr.c
   *
   * XDR functions to encode/decode NFS RPC arguments and results.
   *
   * Copyright (C) 1992, 1993, 1994  Rick Sladkey
   * Copyright (C) 1996 Olaf Kirch
   * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
   * 		FIFO's need special handling in NFSv2
   */
  
  #include <linux/param.h>
  #include <linux/time.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
  #include <linux/errno.h>
  #include <linux/string.h>
  #include <linux/in.h>
  #include <linux/pagemap.h>
  #include <linux/proc_fs.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/nfs.h>
  #include <linux/nfs2.h>
  #include <linux/nfs_fs.h>
816724e65   Trond Myklebust   Merge branch 'mas...
24
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  
  #define NFSDBG_FACILITY		NFSDBG_XDR
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  /* Mapping from NFS error code to "errno" error code. */
  #define errno_NFSERR_IO		EIO
  
  /*
   * Declare the space requirements for NFS arguments and replies as
   * number of 32bit-words
   */
  #define NFS_fhandle_sz		(8)
  #define NFS_sattr_sz		(8)
  #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
  #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
  #define NFS_fattr_sz		(17)
  #define NFS_info_sz		(5)
  #define NFS_entry_sz		(NFS_filename_sz+3)
  
  #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
4fdc17b2a   Trond Myklebust   NFS: Introduce st...
44
  #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
51
  #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
  #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
  #define NFS_readargs_sz		(NFS_fhandle_sz+3)
  #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
  #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
  #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
  #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
94a6d7532   Chuck Lever   NFS: Use cached p...
52
  #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
60
61
62
  #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
  
  #define NFS_attrstat_sz		(1+NFS_fattr_sz)
  #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
  #define NFS_readlinkres_sz	(2)
  #define NFS_readres_sz		(1+NFS_fattr_sz+1)
  #define NFS_writeres_sz         (NFS_attrstat_sz)
  #define NFS_stat_sz		(1)
  #define NFS_readdirres_sz	(1)
  #define NFS_statfsres_sz	(1+NFS_info_sz)
25a0866cc   Chuck Lever   NFS: Introduce ne...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  
  /*
   * While encoding arguments, set up the reply buffer in advance to
   * receive reply data directly into the page cache.
   */
  static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
  				 unsigned int base, unsigned int len,
  				 unsigned int bufsize)
  {
  	struct rpc_auth	*auth = req->rq_cred->cr_auth;
  	unsigned int replen;
  
  	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
  	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
  }
f796f8b3a   Chuck Lever   NFS: Introduce ne...
78
79
80
81
82
83
84
85
86
87
  /*
   * Handle decode buffer overflows out-of-line.
   */
  static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
  {
  	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
  		"Remaining buffer length is %tu words.
  ",
  		func, xdr->end - xdr->p);
  }
25a0866cc   Chuck Lever   NFS: Introduce ne...
88

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
90
91
92
93
94
95
96
97
98
99
100
   * Encode/decode NFSv2 basic data types
   *
   * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
   * "NFS: Network File System Protocol Specification".
   *
   * Not all basic data types have their own encoding and decoding
   * functions.  For run-time efficiency, some data types are encoded
   * or decoded inline.
   */
  
  /*
f796f8b3a   Chuck Lever   NFS: Introduce ne...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
   *	typedef opaque	nfsdata<>;
   */
  static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
  {
  	u32 recvd, count;
  	size_t hdrlen;
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	count = be32_to_cpup(p);
  	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
  	recvd = xdr->buf->len - hdrlen;
  	if (unlikely(count > recvd))
  		goto out_cheating;
  out:
  	xdr_read_pages(xdr, count);
  	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
  	result->count = count;
  	return count;
  out_cheating:
  	dprintk("NFS: server cheating in read result: "
  		"count %u > recvd %u
  ", count, recvd);
  	count = recvd;
  	goto out;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  /*
   *	enum stat {
   *		NFS_OK = 0,
   *		NFSERR_PERM = 1,
   *		NFSERR_NOENT = 2,
   *		NFSERR_IO = 5,
   *		NFSERR_NXIO = 6,
   *		NFSERR_ACCES = 13,
   *		NFSERR_EXIST = 17,
   *		NFSERR_NODEV = 19,
   *		NFSERR_NOTDIR = 20,
   *		NFSERR_ISDIR = 21,
   *		NFSERR_FBIG = 27,
   *		NFSERR_NOSPC = 28,
   *		NFSERR_ROFS = 30,
   *		NFSERR_NAMETOOLONG = 63,
   *		NFSERR_NOTEMPTY = 66,
   *		NFSERR_DQUOT = 69,
   *		NFSERR_STALE = 70,
   *		NFSERR_WFLUSH = 99
   *	};
   */
  static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
  {
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	*status = be32_to_cpup(p);
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  /*
5f96e5e31   Chuck Lever   NFS: Move and upd...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
   * 2.3.2.  ftype
   *
   *	enum ftype {
   *		NFNON = 0,
   *		NFREG = 1,
   *		NFDIR = 2,
   *		NFBLK = 3,
   *		NFCHR = 4,
   *		NFLNK = 5
   *	};
   *
   */
  static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
  {
  	*type = be32_to_cpup(p++);
  	if (unlikely(*type > NF2FIFO))
  		*type = NFBAD;
  	return p;
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
191
192
193
194
195
196
197
198
199
200
201
202
   * 2.3.3.  fhandle
   *
   *	typedef opaque fhandle[FHSIZE];
   */
  static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
  {
  	__be32 *p;
  
  	BUG_ON(fh->size != NFS2_FHSIZE);
  	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
  	memcpy(p, fh->data, NFS2_FHSIZE);
  }
f796f8b3a   Chuck Lever   NFS: Introduce ne...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
  {
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	fh->size = NFS2_FHSIZE;
  	memcpy(fh->data, p, NFS2_FHSIZE);
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
25a0866cc   Chuck Lever   NFS: Introduce ne...
217
  /*
282ac2a57   Chuck Lever   NFS: Update xdr_e...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
   * 2.3.4.  timeval
   *
   *	struct timeval {
   *		unsigned int seconds;
   *		unsigned int useconds;
   *	};
   */
  static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
  {
  	*p++ = cpu_to_be32(timep->tv_sec);
  	if (timep->tv_nsec != 0)
  		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
  	else
  		*p++ = cpu_to_be32(0);
  	return p;
  }
  
  /*
   * Passing the invalid value useconds=1000000 is a Sun convention for
   * "set to current server time".  It's needed to make permissions checks
   * for the "touch" program across v2 mounts to Solaris and Irix servers
   * work correctly.  See description of sattr in section 6.1 of "NFS
   * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
   */
  static __be32 *xdr_encode_current_server_time(__be32 *p,
  					      const struct timespec *timep)
  {
  	*p++ = cpu_to_be32(timep->tv_sec);
  	*p++ = cpu_to_be32(1000000);
  	return p;
  }
5f96e5e31   Chuck Lever   NFS: Move and upd...
249
250
251
252
253
254
  static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
  {
  	timep->tv_sec = be32_to_cpup(p++);
  	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
  	return p;
  }
282ac2a57   Chuck Lever   NFS: Update xdr_e...
255
  /*
f796f8b3a   Chuck Lever   NFS: Introduce ne...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
   * 2.3.5.  fattr
   *
   *	struct fattr {
   *		ftype		type;
   *		unsigned int	mode;
   *		unsigned int	nlink;
   *		unsigned int	uid;
   *		unsigned int	gid;
   *		unsigned int	size;
   *		unsigned int	blocksize;
   *		unsigned int	rdev;
   *		unsigned int	blocks;
   *		unsigned int	fsid;
   *		unsigned int	fileid;
   *		timeval		atime;
   *		timeval		mtime;
   *		timeval		ctime;
   *	};
   *
   */
  static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
  {
5f96e5e31   Chuck Lever   NFS: Move and upd...
278
  	u32 rdev, type;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
279
280
281
282
283
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
  	if (unlikely(p == NULL))
  		goto out_overflow;
5f96e5e31   Chuck Lever   NFS: Move and upd...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  
  	fattr->valid |= NFS_ATTR_FATTR_V2;
  
  	p = xdr_decode_ftype(p, &type);
  
  	fattr->mode = be32_to_cpup(p++);
  	fattr->nlink = be32_to_cpup(p++);
  	fattr->uid = be32_to_cpup(p++);
  	fattr->gid = be32_to_cpup(p++);
  	fattr->size = be32_to_cpup(p++);
  	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
  
  	rdev = be32_to_cpup(p++);
  	fattr->rdev = new_decode_dev(rdev);
  	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
  		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
  		fattr->rdev = 0;
  	}
  
  	fattr->du.nfs2.blocks = be32_to_cpup(p++);
  	fattr->fsid.major = be32_to_cpup(p++);
  	fattr->fsid.minor = 0;
  	fattr->fileid = be32_to_cpup(p++);
  
  	p = xdr_decode_time(p, &fattr->atime);
  	p = xdr_decode_time(p, &fattr->mtime);
  	xdr_decode_time(p, &fattr->ctime);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
311
312
313
314
315
316
317
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
   * 2.3.6.  sattr
   *
   *	struct sattr {
   *		unsigned int	mode;
   *		unsigned int	uid;
   *		unsigned int	gid;
   *		unsigned int	size;
   *		timeval		atime;
   *		timeval		mtime;
   *	};
   */
  
  #define NFS2_SATTR_NOT_SET	(0xffffffff)
  
  static __be32 *xdr_time_not_set(__be32 *p)
  {
  	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  	return p;
  }
  
  static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
  {
  	__be32 *p;
  
  	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
  
  	if (attr->ia_valid & ATTR_MODE)
  		*p++ = cpu_to_be32(attr->ia_mode);
  	else
  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  	if (attr->ia_valid & ATTR_UID)
  		*p++ = cpu_to_be32(attr->ia_uid);
  	else
  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  	if (attr->ia_valid & ATTR_GID)
  		*p++ = cpu_to_be32(attr->ia_gid);
  	else
  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  	if (attr->ia_valid & ATTR_SIZE)
  		*p++ = cpu_to_be32((u32)attr->ia_size);
  	else
  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
  
  	if (attr->ia_valid & ATTR_ATIME_SET)
  		p = xdr_encode_time(p, &attr->ia_atime);
  	else if (attr->ia_valid & ATTR_ATIME)
  		p = xdr_encode_current_server_time(p, &attr->ia_atime);
  	else
  		p = xdr_time_not_set(p);
  	if (attr->ia_valid & ATTR_MTIME_SET)
  		xdr_encode_time(p, &attr->ia_mtime);
  	else if (attr->ia_valid & ATTR_MTIME)
  		xdr_encode_current_server_time(p, &attr->ia_mtime);
  	else
  		xdr_time_not_set(p);
  }
  
  /*
   * 2.3.7.  filename
   *
   *	typedef string filename<MAXNAMLEN>;
   */
  static void encode_filename(struct xdr_stream *xdr,
  			    const char *name, u32 length)
  {
  	__be32 *p;
  
  	BUG_ON(length > NFS2_MAXNAMLEN);
  	p = xdr_reserve_space(xdr, 4 + length);
  	xdr_encode_opaque(p, name, length);
  }
f796f8b3a   Chuck Lever   NFS: Introduce ne...
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
  static int decode_filename_inline(struct xdr_stream *xdr,
  				  const char **name, u32 *length)
  {
  	__be32 *p;
  	u32 count;
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	count = be32_to_cpup(p);
  	if (count > NFS3_MAXNAMLEN)
  		goto out_nametoolong;
  	p = xdr_inline_decode(xdr, count);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	*name = (const char *)p;
  	*length = count;
  	return 0;
  out_nametoolong:
  	dprintk("NFS: returned filename too long: %u
  ", count);
  	return -ENAMETOOLONG;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
25a0866cc   Chuck Lever   NFS: Introduce ne...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
  /*
   * 2.3.8.  path
   *
   *	typedef string path<MAXPATHLEN>;
   */
  static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
  {
  	__be32 *p;
  
  	BUG_ON(length > NFS2_MAXPATHLEN);
  	p = xdr_reserve_space(xdr, 4);
  	*p = cpu_to_be32(length);
  	xdr_write_pages(xdr, pages, 0, length);
  }
f796f8b3a   Chuck Lever   NFS: Introduce ne...
430
431
432
433
434
435
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
479
480
481
482
483
484
485
486
487
488
489
  static int decode_path(struct xdr_stream *xdr)
  {
  	u32 length, recvd;
  	size_t hdrlen;
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	length = be32_to_cpup(p);
  	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
  		goto out_size;
  	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
  	recvd = xdr->buf->len - hdrlen;
  	if (unlikely(length > recvd))
  		goto out_cheating;
  
  	xdr_read_pages(xdr, length);
  	xdr_terminate_string(xdr->buf, length);
  	return 0;
  out_size:
  	dprintk("NFS: returned pathname too long: %u
  ", length);
  	return -ENAMETOOLONG;
  out_cheating:
  	dprintk("NFS: server cheating in pathname result: "
  		"length %u > received %u
  ", length, recvd);
  	return -EIO;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  /*
   * 2.3.9.  attrstat
   *
   *	union attrstat switch (stat status) {
   *	case NFS_OK:
   *		fattr attributes;
   *	default:
   *		void;
   *	};
   */
  static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
  {
  	enum nfs_stat status;
  	int error;
  
  	error = decode_stat(xdr, &status);
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
  	error = decode_fattr(xdr, result);
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
25a0866cc   Chuck Lever   NFS: Introduce ne...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  /*
   * 2.3.10.  diropargs
   *
   *	struct diropargs {
   *		fhandle  dir;
   *		filename name;
   *	};
   */
  static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
  			     const char *name, u32 length)
  {
  	encode_fhandle(xdr, fh);
  	encode_filename(xdr, name, length);
  }
f796f8b3a   Chuck Lever   NFS: Introduce ne...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  /*
   * 2.3.11.  diropres
   *
   *	union diropres switch (stat status) {
   *	case NFS_OK:
   *		struct {
   *			fhandle file;
   *			fattr   attributes;
   *		} diropok;
   *	default:
   *		void;
   *	};
   */
  static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
  {
  	int error;
  
  	error = decode_fhandle(xdr, result->fh);
  	if (unlikely(error))
  		goto out;
  	error = decode_fattr(xdr, result->fattr);
  out:
  	return error;
  }
  
  static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
  {
  	enum nfs_stat status;
  	int error;
  
  	error = decode_stat(xdr, &status);
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
  	error = decode_diropok(xdr, result);
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
25a0866cc   Chuck Lever   NFS: Introduce ne...
545
546
  
  /*
2d70f533e   Chuck Lever   NFS: Remove old N...
547
548
549
550
   * NFSv2 XDR encode functions
   *
   * NFSv2 argument types are defined in section 2.2 of RFC 1094:
   * "NFS: Network File System Protocol Specification".
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552

9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
553
554
555
  static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
  				 struct xdr_stream *xdr,
  				 const struct nfs_fh *fh)
25a0866cc   Chuck Lever   NFS: Introduce ne...
556
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
557
  	encode_fhandle(xdr, fh);
25a0866cc   Chuck Lever   NFS: Introduce ne...
558
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
560
561
562
563
564
565
566
   * 2.2.3.  sattrargs
   *
   *	struct sattrargs {
   *		fhandle file;
   *		sattr attributes;
   *	};
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
567
568
569
  static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
  				   struct xdr_stream *xdr,
  				   const struct nfs_sattrargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
570
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
571
572
  	encode_fhandle(xdr, args->fh);
  	encode_sattr(xdr, args->sattr);
25a0866cc   Chuck Lever   NFS: Introduce ne...
573
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
574
575
576
  static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
  				   struct xdr_stream *xdr,
  				   const struct nfs_diropargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
577
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
578
  	encode_diropargs(xdr, args->fh, args->name, args->len);
25a0866cc   Chuck Lever   NFS: Introduce ne...
579
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
580
581
582
  static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
  				      struct xdr_stream *xdr,
  				      const struct nfs_readlinkargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
583
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
584
  	encode_fhandle(xdr, args->fh);
25a0866cc   Chuck Lever   NFS: Introduce ne...
585
586
  	prepare_reply_buffer(req, args->pages, args->pgbase,
  					args->pglen, NFS_readlinkres_sz);
25a0866cc   Chuck Lever   NFS: Introduce ne...
587
  }
4fdc17b2a   Trond Myklebust   NFS: Introduce st...
588
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
   * 2.2.7.  readargs
   *
   *	struct readargs {
   *		fhandle file;
   *		unsigned offset;
   *		unsigned count;
   *		unsigned totalcount;
   *	};
   */
  static void encode_readargs(struct xdr_stream *xdr,
  			    const struct nfs_readargs *args)
  {
  	u32 offset = args->offset;
  	u32 count = args->count;
  	__be32 *p;
  
  	encode_fhandle(xdr, args->fh);
  
  	p = xdr_reserve_space(xdr, 4 + 4 + 4);
  	*p++ = cpu_to_be32(offset);
  	*p++ = cpu_to_be32(count);
  	*p = cpu_to_be32(count);
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
612
613
614
  static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
  				  struct xdr_stream *xdr,
  				  const struct nfs_readargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
615
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
616
  	encode_readargs(xdr, args);
25a0866cc   Chuck Lever   NFS: Introduce ne...
617
618
619
  	prepare_reply_buffer(req, args->pages, args->pgbase,
  					args->count, NFS_readres_sz);
  	req->rq_rcv_buf.flags |= XDRBUF_READ;
25a0866cc   Chuck Lever   NFS: Introduce ne...
620
621
622
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
   * 2.2.9.  writeargs
   *
   *	struct writeargs {
   *		fhandle file;
   *		unsigned beginoffset;
   *		unsigned offset;
   *		unsigned totalcount;
   *		nfsdata data;
   *	};
   */
  static void encode_writeargs(struct xdr_stream *xdr,
  			     const struct nfs_writeargs *args)
  {
  	u32 offset = args->offset;
  	u32 count = args->count;
  	__be32 *p;
  
  	encode_fhandle(xdr, args->fh);
  
  	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
  	*p++ = cpu_to_be32(offset);
  	*p++ = cpu_to_be32(offset);
  	*p++ = cpu_to_be32(count);
  
  	/* nfsdata */
  	*p = cpu_to_be32(count);
  	xdr_write_pages(xdr, args->pages, args->pgbase, count);
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
651
652
653
  static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
  				   struct xdr_stream *xdr,
  				   const struct nfs_writeargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
654
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
655
656
  	encode_writeargs(xdr, args);
  	xdr->buf->flags |= XDRBUF_WRITE;
25a0866cc   Chuck Lever   NFS: Introduce ne...
657
658
659
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
660
661
662
663
664
665
666
   * 2.2.10.  createargs
   *
   *	struct createargs {
   *		diropargs where;
   *		sattr attributes;
   *	};
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
667
668
669
  static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
  				    struct xdr_stream *xdr,
  				    const struct nfs_createargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
670
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
671
672
  	encode_diropargs(xdr, args->fh, args->name, args->len);
  	encode_sattr(xdr, args->sattr);
25a0866cc   Chuck Lever   NFS: Introduce ne...
673
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
674
675
676
  static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
  				    struct xdr_stream *xdr,
  				    const struct nfs_removeargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
677
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
678
  	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
25a0866cc   Chuck Lever   NFS: Introduce ne...
679
680
681
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
682
683
684
685
686
687
688
   * 2.2.12.  renameargs
   *
   *	struct renameargs {
   *		diropargs from;
   *		diropargs to;
   *	};
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
689
690
691
  static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
  				    struct xdr_stream *xdr,
  				    const struct nfs_renameargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
692
693
694
  {
  	const struct qstr *old = args->old_name;
  	const struct qstr *new = args->new_name;
25a0866cc   Chuck Lever   NFS: Introduce ne...
695

9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
696
697
  	encode_diropargs(xdr, args->old_dir, old->name, old->len);
  	encode_diropargs(xdr, args->new_dir, new->name, new->len);
25a0866cc   Chuck Lever   NFS: Introduce ne...
698
699
700
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
701
702
703
704
705
706
707
   * 2.2.13.  linkargs
   *
   *	struct linkargs {
   *		fhandle from;
   *		diropargs to;
   *	};
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
708
709
710
  static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
  				  struct xdr_stream *xdr,
  				  const struct nfs_linkargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
711
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
712
713
  	encode_fhandle(xdr, args->fromfh);
  	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
25a0866cc   Chuck Lever   NFS: Introduce ne...
714
715
716
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
717
718
719
720
721
722
723
724
   * 2.2.14.  symlinkargs
   *
   *	struct symlinkargs {
   *		diropargs from;
   *		path to;
   *		sattr attributes;
   *	};
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
725
726
727
  static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
  				     struct xdr_stream *xdr,
  				     const struct nfs_symlinkargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
728
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
729
730
731
  	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
  	encode_path(xdr, args->pages, args->pathlen);
  	encode_sattr(xdr, args->sattr);
25a0866cc   Chuck Lever   NFS: Introduce ne...
732
733
734
  }
  
  /*
25a0866cc   Chuck Lever   NFS: Introduce ne...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
   * 2.2.17.  readdirargs
   *
   *	struct readdirargs {
   *		fhandle dir;
   *		nfscookie cookie;
   *		unsigned count;
   *	};
   */
  static void encode_readdirargs(struct xdr_stream *xdr,
  			       const struct nfs_readdirargs *args)
  {
  	__be32 *p;
  
  	encode_fhandle(xdr, args->fh);
  
  	p = xdr_reserve_space(xdr, 4 + 4);
  	*p++ = cpu_to_be32(args->cookie);
  	*p = cpu_to_be32(args->count);
  }
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
754
755
756
  static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
  				     struct xdr_stream *xdr,
  				     const struct nfs_readdirargs *args)
25a0866cc   Chuck Lever   NFS: Introduce ne...
757
  {
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
758
  	encode_readdirargs(xdr, args);
25a0866cc   Chuck Lever   NFS: Introduce ne...
759
760
  	prepare_reply_buffer(req, args->pages, 0,
  					args->count, NFS_readdirres_sz);
25a0866cc   Chuck Lever   NFS: Introduce ne...
761
762
763
  }
  
  /*
661ad4239   Chuck Lever   NFS: Replace old ...
764
765
766
767
   * NFSv2 XDR decode functions
   *
   * NFSv2 result types are defined in section 2.2 of RFC 1094:
   * "NFS: Network File System Protocol Specification".
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769

bf2695516   Chuck Lever   SUNRPC: New xdr_s...
770
  static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
771
772
  			     void *__unused)
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
773
774
  	enum nfs_stat status;
  	int error;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
775
  	error = decode_stat(xdr, &status);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
776
777
778
779
780
781
782
783
784
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
785
  static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
786
787
  				 struct nfs_fattr *result)
  {
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
788
  	return decode_attrstat(xdr, result);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
789
  }
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
790
  static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
791
792
  				 struct nfs_diropok *result)
  {
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
793
  	return decode_diropres(xdr, result);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
794
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
  /*
f796f8b3a   Chuck Lever   NFS: Introduce ne...
796
797
798
799
800
801
802
803
804
   * 2.2.6.  readlinkres
   *
   *	union readlinkres switch (stat status) {
   *	case NFS_OK:
   *		path data;
   *	default:
   *		void;
   *	};
   */
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
805
806
  static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
  				    struct xdr_stream *xdr, void *__unused)
f796f8b3a   Chuck Lever   NFS: Introduce ne...
807
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
808
809
  	enum nfs_stat status;
  	int error;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
810
  	error = decode_stat(xdr, &status);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
811
812
813
814
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
815
  	error = decode_path(xdr);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
  
  /*
   * 2.2.7.  readres
   *
   *	union readres switch (stat status) {
   *	case NFS_OK:
   *		fattr attributes;
   *		nfsdata data;
   *	default:
   *		void;
   *	};
   */
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
833
  static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
834
835
  				struct nfs_readres *result)
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
836
837
  	enum nfs_stat status;
  	int error;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
838
  	error = decode_stat(xdr, &status);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
839
840
841
842
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
843
  	error = decode_fattr(xdr, result->fattr);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
844
845
  	if (unlikely(error))
  		goto out;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
846
  	error = decode_nfsdata(xdr, result);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
847
848
849
850
851
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
852
  static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
853
854
  				 struct nfs_writeres *result)
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
855
856
  	/* All NFSv2 writes are "file sync" writes */
  	result->verf->committed = NFS_FILE_SYNC;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
857
  	return decode_attrstat(xdr, result->fattr);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
858
859
860
861
862
863
864
  }
  
  /**
   * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
   *                      the local page cache.
   * @xdr: XDR stream where entry resides
   * @entry: buffer to fill in with entry data
f796f8b3a   Chuck Lever   NFS: Introduce ne...
865
866
   * @plus: boolean indicating whether this should be a readdirplus entry
   *
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
867
868
   * Returns zero if successful, otherwise a negative errno value is
   * returned.
f796f8b3a   Chuck Lever   NFS: Introduce ne...
869
870
871
872
873
874
875
876
877
878
879
880
881
882
   *
   * This function is not invoked during READDIR reply decoding, but
   * rather whenever an application invokes the getdents(2) system call
   * on a directory already in our cache.
   *
   * 2.2.17.  entry
   *
   *	struct entry {
   *		unsigned	fileid;
   *		filename	name;
   *		nfscookie	cookie;
   *		entry		*nextentry;
   *	};
   */
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
883
884
  int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
  		       int plus)
f796f8b3a   Chuck Lever   NFS: Introduce ne...
885
886
887
888
889
890
891
892
893
894
895
896
  {
  	__be32 *p;
  	int error;
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	if (*p++ == xdr_zero) {
  		p = xdr_inline_decode(xdr, 4);
  		if (unlikely(p == NULL))
  			goto out_overflow;
  		if (*p++ == xdr_zero)
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
897
  			return -EAGAIN;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
898
  		entry->eof = 1;
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
899
  		return -EBADCOOKIE;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
900
901
902
903
904
905
906
907
908
  	}
  
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	entry->ino = be32_to_cpup(p);
  
  	error = decode_filename_inline(xdr, &entry->name, &entry->len);
  	if (unlikely(error))
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
909
  		return error;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
910
911
912
913
914
915
916
917
918
919
920
921
  
  	/*
  	 * The type (size and byte order) of nfscookie isn't defined in
  	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
  	 */
  	entry->prev_cookie = entry->cookie;
  	p = xdr_inline_decode(xdr, 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	entry->cookie = be32_to_cpup(p);
  
  	entry->d_type = DT_UNKNOWN;
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
922
  	return 0;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
923
924
925
  
  out_overflow:
  	print_overflow_msg(__func__, xdr);
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
926
  	return -EAGAIN;
f796f8b3a   Chuck Lever   NFS: Introduce ne...
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
953
954
955
956
957
958
959
960
961
962
963
964
965
  }
  
  /*
   * 2.2.17.  readdirres
   *
   *	union readdirres switch (stat status) {
   *	case NFS_OK:
   *		struct {
   *			entry *entries;
   *			bool eof;
   *		} readdirok;
   *	default:
   *		void;
   *	};
   *
   * Read the directory contents into the page cache, but don't
   * touch them.  The actual decoding is done by nfs2_decode_dirent()
   * during subsequent nfs_readdir() calls.
   */
  static int decode_readdirok(struct xdr_stream *xdr)
  {
  	u32 recvd, pglen;
  	size_t hdrlen;
  
  	pglen = xdr->buf->page_len;
  	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
  	recvd = xdr->buf->len - hdrlen;
  	if (unlikely(pglen > recvd))
  		goto out_cheating;
  out:
  	xdr_read_pages(xdr, pglen);
  	return pglen;
  out_cheating:
  	dprintk("NFS: server cheating in readdir result: "
  		"pglen %u > recvd %u
  ", pglen, recvd);
  	pglen = recvd;
  	goto out;
  }
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
966
967
  static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
  				   struct xdr_stream *xdr, void *__unused)
f796f8b3a   Chuck Lever   NFS: Introduce ne...
968
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
969
970
  	enum nfs_stat status;
  	int error;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
971
  	error = decode_stat(xdr, &status);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
972
973
974
975
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
976
  	error = decode_readdirok(xdr);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
977
978
979
980
981
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
  /*
f796f8b3a   Chuck Lever   NFS: Introduce ne...
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
   * 2.2.18.  statfsres
   *
   *	union statfsres (stat status) {
   *	case NFS_OK:
   *		struct {
   *			unsigned tsize;
   *			unsigned bsize;
   *			unsigned blocks;
   *			unsigned bfree;
   *			unsigned bavail;
   *		} info;
   *	default:
   *		void;
   *	};
   */
  static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
  {
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	result->tsize  = be32_to_cpup(p++);
  	result->bsize  = be32_to_cpup(p++);
  	result->blocks = be32_to_cpup(p++);
  	result->bfree  = be32_to_cpup(p++);
  	result->bavail = be32_to_cpup(p);
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
1015
  static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
f796f8b3a   Chuck Lever   NFS: Introduce ne...
1016
1017
  				  struct nfs2_fsstat *result)
  {
f796f8b3a   Chuck Lever   NFS: Introduce ne...
1018
1019
  	enum nfs_stat status;
  	int error;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
1020
  	error = decode_stat(xdr, &status);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
1021
1022
1023
1024
  	if (unlikely(error))
  		goto out;
  	if (status != NFS_OK)
  		goto out_default;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
1025
  	error = decode_info(xdr, result);
f796f8b3a   Chuck Lever   NFS: Introduce ne...
1026
1027
1028
1029
1030
1031
1032
1033
  out:
  	return error;
  out_default:
  	return nfs_stat_to_errno(status);
  }
  
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
1035
1036
   * We need to translate between nfs status return values and
   * the local errno values which may not be the same.
   */
858284932   Chuck Lever   NFS: Use the "nfs...
1037
  static const struct {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
1039
1040
1041
  	int stat;
  	int errno;
  } nfs_errtbl[] = {
  	{ NFS_OK,		0		},
856dff3d3   Benny Halevy   nfs: return negat...
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
  	{ NFSERR_PERM,		-EPERM		},
  	{ NFSERR_NOENT,		-ENOENT		},
  	{ NFSERR_IO,		-errno_NFSERR_IO},
  	{ NFSERR_NXIO,		-ENXIO		},
  /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
  	{ NFSERR_ACCES,		-EACCES		},
  	{ NFSERR_EXIST,		-EEXIST		},
  	{ NFSERR_XDEV,		-EXDEV		},
  	{ NFSERR_NODEV,		-ENODEV		},
  	{ NFSERR_NOTDIR,	-ENOTDIR	},
  	{ NFSERR_ISDIR,		-EISDIR		},
  	{ NFSERR_INVAL,		-EINVAL		},
  	{ NFSERR_FBIG,		-EFBIG		},
  	{ NFSERR_NOSPC,		-ENOSPC		},
  	{ NFSERR_ROFS,		-EROFS		},
  	{ NFSERR_MLINK,		-EMLINK		},
  	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
  	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
  	{ NFSERR_DQUOT,		-EDQUOT		},
  	{ NFSERR_STALE,		-ESTALE		},
  	{ NFSERR_REMOTE,	-EREMOTE	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
  #ifdef EWFLUSH
856dff3d3   Benny Halevy   nfs: return negat...
1064
  	{ NFSERR_WFLUSH,	-EWFLUSH	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
  #endif
856dff3d3   Benny Halevy   nfs: return negat...
1066
1067
1068
1069
1070
  	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
  	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
  	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
  	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
  	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
fdcb45777   Trond Myklebust   NFS: Fix the mapp...
1071
  	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
856dff3d3   Benny Halevy   nfs: return negat...
1072
1073
1074
  	{ NFSERR_BADTYPE,	-EBADTYPE	},
  	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
  	{ -1,			-EIO		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
  };
858284932   Chuck Lever   NFS: Use the "nfs...
1076
1077
1078
1079
1080
1081
  /**
   * nfs_stat_to_errno - convert an NFS status code to a local errno
   * @status: NFS status code to convert
   *
   * Returns a local errno value, or -EIO if the NFS status code is
   * not recognized.  This function is used jointly by NFSv2 and NFSv3.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
   */
858284932   Chuck Lever   NFS: Use the "nfs...
1083
  int nfs_stat_to_errno(enum nfs_stat status)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
1085
1086
1087
  {
  	int i;
  
  	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
858284932   Chuck Lever   NFS: Use the "nfs...
1088
  		if (nfs_errtbl[i].stat == (int)status)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
1090
  			return nfs_errtbl[i].errno;
  	}
858284932   Chuck Lever   NFS: Use the "nfs...
1091
1092
  	dprintk("NFS: Unrecognized nfs status value: %u
  ", status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1093
1094
  	return nfs_errtbl[i].errno;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
  #define PROC(proc, argtype, restype, timer)				\
  [NFSPROC_##proc] = {							\
  	.p_proc	    =  NFSPROC_##proc,					\
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
1098
  	.p_encode   =  (kxdreproc_t)nfs2_xdr_enc_##argtype,		\
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
1099
  	.p_decode   =  (kxdrdproc_t)nfs2_xdr_dec_##restype,		\
2bea90d43   Chuck Lever   SUNRPC: RPC buffe...
1100
1101
  	.p_arglen   =  NFS_##argtype##_sz,				\
  	.p_replen   =  NFS_##restype##_sz,				\
cc0175c1d   Chuck Lever   SUNRPC: display h...
1102
1103
1104
  	.p_timer    =  timer,						\
  	.p_statidx  =  NFSPROC_##proc,					\
  	.p_name     =  #proc,						\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
  	}
  struct rpc_procinfo	nfs_procedures[] = {
7d93bd71c   Chuck Lever   NFS: Repair white...
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
  	PROC(GETATTR,	fhandle,	attrstat,	1),
  	PROC(SETATTR,	sattrargs,	attrstat,	0),
  	PROC(LOOKUP,	diropargs,	diropres,	2),
  	PROC(READLINK,	readlinkargs,	readlinkres,	3),
  	PROC(READ,	readargs,	readres,	3),
  	PROC(WRITE,	writeargs,	writeres,	4),
  	PROC(CREATE,	createargs,	diropres,	0),
  	PROC(REMOVE,	removeargs,	stat,		0),
  	PROC(RENAME,	renameargs,	stat,		0),
  	PROC(LINK,	linkargs,	stat,		0),
  	PROC(SYMLINK,	symlinkargs,	stat,		0),
  	PROC(MKDIR,	createargs,	diropres,	0),
  	PROC(RMDIR,	diropargs,	stat,		0),
  	PROC(READDIR,	readdirargs,	readdirres,	3),
  	PROC(STATFS,	fhandle,	statfsres,	0),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1122
1123
1124
1125
  };
  
  struct rpc_version		nfs_version2 = {
  	.number			= 2,
e8c96f8c2   Tobias Klauser   [PATCH] fs: Use A...
1126
  	.nrprocs		= ARRAY_SIZE(nfs_procedures),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1127
1128
  	.procs			= nfs_procedures
  };