Blame view

fs/nfsd/nfs3xdr.c 26.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
   * XDR support for nfsd/protocol version 3.
   *
   * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
   *
   * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
  #include <linux/namei.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
9
  #include "xdr3.h"
2e8138a27   J. Bruce Fields   nfsd: move nfsd/a...
10
  #include "auth.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  
  #define NFSDDBG_FACILITY		NFSDDBG_XDR
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  
  /*
   * Mapping of S_IF* types to NFS file types
   */
  static u32	nfs3_ftypes[] = {
  	NF3NON,  NF3FIFO, NF3CHR, NF3BAD,
  	NF3DIR,  NF3BAD,  NF3BLK, NF3BAD,
  	NF3REG,  NF3BAD,  NF3LNK, NF3BAD,
  	NF3SOCK, NF3BAD,  NF3LNK, NF3BAD,
  };
  
  /*
   * XDR functions for basic NFS types
   */
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
27
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
28
  encode_time3(__be32 *p, struct timespec *time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  {
  	*p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
  	return p;
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
33
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
34
  decode_time3(__be32 *p, struct timespec *time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
39
  {
  	time->tv_sec = ntohl(*p++);
  	time->tv_nsec = ntohl(*p++);
  	return p;
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
40
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
41
  decode_fh(__be32 *p, struct svc_fh *fhp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
50
51
52
  {
  	unsigned int size;
  	fh_init(fhp, NFS3_FHSIZE);
  	size = ntohl(*p++);
  	if (size > NFS3_FHSIZE)
  		return NULL;
  
  	memcpy(&fhp->fh_handle.fh_base, p, size);
  	fhp->fh_handle.fh_size = size;
  	return p + XDR_QUADLEN(size);
  }
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
53
  /* Helper function for NFSv3 ACL code */
91f07168c   Al Viro   [PATCH] xdr annot...
54
  __be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp)
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
55
56
57
  {
  	return decode_fh(p, fhp);
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
58
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
59
  encode_fh(__be32 *p, struct svc_fh *fhp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
69
70
71
  {
  	unsigned int size = fhp->fh_handle.fh_size;
  	*p++ = htonl(size);
  	if (size) p[XDR_QUADLEN(size)-1]=0;
  	memcpy(p, &fhp->fh_handle.fh_base, size);
  	return p + XDR_QUADLEN(size);
  }
  
  /*
   * Decode a file name and make sure that the path contains
   * no slashes or null bytes.
   */
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
72
  static __be32 *
ee1a95b3b   Chuck Lever   NFSD: Use unsigne...
73
  decode_filename(__be32 *p, char **namp, unsigned int *lenp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  {
  	char		*name;
ee1a95b3b   Chuck Lever   NFSD: Use unsigne...
76
  	unsigned int	i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
86
  
  	if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
  		for (i = 0, name = *namp; i < *lenp; i++, name++) {
  			if (*name == '\0' || *name == '/')
  				return NULL;
  		}
  	}
  
  	return p;
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
87
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
88
  decode_sattr3(__be32 *p, struct iattr *iap)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
95
96
97
98
99
100
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
  {
  	u32	tmp;
  
  	iap->ia_valid = 0;
  
  	if (*p++) {
  		iap->ia_valid |= ATTR_MODE;
  		iap->ia_mode = ntohl(*p++);
  	}
  	if (*p++) {
  		iap->ia_valid |= ATTR_UID;
  		iap->ia_uid = ntohl(*p++);
  	}
  	if (*p++) {
  		iap->ia_valid |= ATTR_GID;
  		iap->ia_gid = ntohl(*p++);
  	}
  	if (*p++) {
  		u64	newsize;
  
  		iap->ia_valid |= ATTR_SIZE;
  		p = xdr_decode_hyper(p, &newsize);
  		if (newsize <= NFS_OFFSET_MAX)
  			iap->ia_size = newsize;
  		else
  			iap->ia_size = NFS_OFFSET_MAX;
  	}
  	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
  		iap->ia_valid |= ATTR_ATIME;
  	} else if (tmp == 2) {		/* set to client time */
  		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
  		iap->ia_atime.tv_sec = ntohl(*p++);
  		iap->ia_atime.tv_nsec = ntohl(*p++);
  	}
  	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
  		iap->ia_valid |= ATTR_MTIME;
  	} else if (tmp == 2) {		/* set to client time */
  		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
  		iap->ia_mtime.tv_sec = ntohl(*p++);
  		iap->ia_mtime.tv_nsec = ntohl(*p++);
  	}
  	return p;
  }
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
  {
  	u64 f;
  	switch(fsid_source(fhp)) {
  	default:
  	case FSIDSOURCE_DEV:
  		p = xdr_encode_hyper(p, (u64)huge_encode_dev
  				     (fhp->fh_dentry->d_inode->i_sb->s_dev));
  		break;
  	case FSIDSOURCE_FSID:
  		p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
  		break;
  	case FSIDSOURCE_UUID:
  		f = ((u64*)fhp->fh_export->ex_uuid)[0];
  		f ^= ((u64*)fhp->fh_export->ex_uuid)[1];
  		p = xdr_encode_hyper(p, f);
  		break;
  	}
  	return p;
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
152
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
153
  encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
a334de286   David Shaw   [PATCH] knfsd: ch...
154
  	      struct kstat *stat)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  {
a334de286   David Shaw   [PATCH] knfsd: ch...
156
157
158
159
160
161
  	*p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
  	*p++ = htonl((u32) stat->mode);
  	*p++ = htonl((u32) stat->nlink);
  	*p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid));
  	*p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid));
  	if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
  		p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
  	} else {
a334de286   David Shaw   [PATCH] knfsd: ch...
164
  		p = xdr_encode_hyper(p, (u64) stat->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  	}
a334de286   David Shaw   [PATCH] knfsd: ch...
166
167
168
  	p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
  	*p++ = htonl((u32) MAJOR(stat->rdev));
  	*p++ = htonl((u32) MINOR(stat->rdev));
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
169
  	p = encode_fsid(p, fhp);
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
170
  	p = xdr_encode_hyper(p, stat->ino);
a334de286   David Shaw   [PATCH] knfsd: ch...
171
  	p = encode_time3(p, &stat->atime);
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
172
  	p = encode_time3(p, &stat->mtime);
a334de286   David Shaw   [PATCH] knfsd: ch...
173
  	p = encode_time3(p, &stat->ctime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
  
  	return p;
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
177
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
178
  encode_saved_post_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  	/* Attributes to follow */
  	*p++ = xdr_one;
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
182
  	return encode_fattr3(rqstp, p, fhp, &fhp->fh_post_attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
189
  }
  
  /*
   * Encode post-operation attributes.
   * The inode may be NULL if the call failed because of a stale file
   * handle. In this case, no attributes are returned.
   */
91f07168c   Al Viro   [PATCH] xdr annot...
190
191
  static __be32 *
  encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  {
  	struct dentry *dentry = fhp->fh_dentry;
072f62ed8   NeilBrown   knfsd: various nf...
194
  	if (dentry && dentry->d_inode) {
a334de286   David Shaw   [PATCH] knfsd: ch...
195
196
  	        int err;
  		struct kstat stat;
547754916   Jan Blunck   Use struct path i...
197
  		err = vfs_getattr(fhp->fh_export->ex_path.mnt, dentry, &stat);
a334de286   David Shaw   [PATCH] knfsd: ch...
198
199
  		if (!err) {
  			*p++ = xdr_one;		/* attributes follow */
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
200
  			lease_get_mtime(dentry->d_inode, &stat.mtime);
a334de286   David Shaw   [PATCH] knfsd: ch...
201
202
  			return encode_fattr3(rqstp, p, fhp, &stat);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
  	}
  	*p++ = xdr_zero;
  	return p;
  }
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
207
  /* Helper for NFSv3 ACLs */
91f07168c   Al Viro   [PATCH] xdr annot...
208
209
  __be32 *
  nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
210
211
212
  {
  	return encode_post_op_attr(rqstp, p, fhp);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
  /*
   * Enocde weak cache consistency data
   */
91f07168c   Al Viro   [PATCH] xdr annot...
216
217
  static __be32 *
  encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  {
  	struct dentry	*dentry = fhp->fh_dentry;
  
  	if (dentry && dentry->d_inode && fhp->fh_post_saved) {
  		if (fhp->fh_pre_saved) {
  			*p++ = xdr_one;
  			p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
  			p = encode_time3(p, &fhp->fh_pre_mtime);
  			p = encode_time3(p, &fhp->fh_pre_ctime);
  		} else {
  			*p++ = xdr_zero;
  		}
  		return encode_saved_post_attr(rqstp, p, fhp);
  	}
  	/* no pre- or post-attrs */
  	*p++ = xdr_zero;
  	return encode_post_op_attr(rqstp, p, fhp);
  }
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
236
237
238
239
240
241
242
243
244
245
  /*
   * Fill in the post_op attr for the wcc data
   */
  void fill_post_wcc(struct svc_fh *fhp)
  {
  	int err;
  
  	if (fhp->fh_post_saved)
  		printk("nfsd: inode locked twice during operation.
  ");
547754916   Jan Blunck   Use struct path i...
246
  	err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry,
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
247
  			&fhp->fh_post_attr);
c654b8a9c   J. Bruce Fields   nfsd: support ext...
248
  	fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version;
c1ac3ffcd   Neil Brown   nfsd: Fix possibl...
249
  	if (err) {
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
250
  		fhp->fh_post_saved = 0;
c1ac3ffcd   Neil Brown   nfsd: Fix possibl...
251
252
253
  		/* Grab the ctime anyway - set_change_info might use it */
  		fhp->fh_post_attr.ctime = fhp->fh_dentry->d_inode->i_ctime;
  	} else
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
254
255
  		fhp->fh_post_saved = 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
  
  /*
   * XDR decode functions
   */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
261
  nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
  {
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
269
  nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
  					struct nfsd3_sattrargs *args)
  {
072f62ed8   NeilBrown   knfsd: various nf...
272
  	if (!(p = decode_fh(p, &args->fh)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  		return 0;
072f62ed8   NeilBrown   knfsd: various nf...
274
  	p = decode_sattr3(p, &args->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
284
285
  
  	if ((args->check_guard = ntohl(*p++)) != 0) { 
  		struct timespec time; 
  		p = decode_time3(p, &time);
  		args->guardtime = time.tv_sec;
  	}
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
286
  nfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
296
  					struct nfsd3_diropargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh))
  	 || !(p = decode_filename(p, &args->name, &args->len)))
  		return 0;
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
297
  nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
  					struct nfsd3_accessargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
  	args->access = ntohl(*p++);
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
308
  nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
  					struct nfsd3_readargs *args)
  {
  	unsigned int len;
  	int v,pn;
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
313
  	u32 max_blocksize = svc_max_payload(rqstp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314

072f62ed8   NeilBrown   knfsd: various nf...
315
  	if (!(p = decode_fh(p, &args->fh)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  		return 0;
072f62ed8   NeilBrown   knfsd: various nf...
317
  	p = xdr_decode_hyper(p, &args->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
  
  	len = args->count = ntohl(*p++);
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
320
321
  	if (len > max_blocksize)
  		len = max_blocksize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
  
  	/* set up the kvec */
  	v=0;
  	while (len > 0) {
445243594   NeilBrown   [PATCH] knfsd: Re...
326
  		pn = rqstp->rq_resused++;
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
327
328
329
  		rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
  		rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
  		len -= rqstp->rq_vec[v].iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
  		v++;
  	}
  	args->vlen = v;
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
337
  nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  					struct nfsd3_writeargs *args)
  {
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
340
  	unsigned int len, v, hdr, dlen;
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
341
  	u32 max_blocksize = svc_max_payload(rqstp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342

072f62ed8   NeilBrown   knfsd: various nf...
343
  	if (!(p = decode_fh(p, &args->fh)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  		return 0;
072f62ed8   NeilBrown   knfsd: various nf...
345
  	p = xdr_decode_hyper(p, &args->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
  
  	args->count = ntohl(*p++);
  	args->stable = ntohl(*p++);
  	len = args->len = ntohl(*p++);
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
350
351
352
353
354
  	/*
  	 * The count must equal the amount of data passed.
  	 */
  	if (args->count != args->len)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355

f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
356
357
358
  	/*
  	 * Check to make sure that we got the right number of
  	 * bytes.
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
359
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  	hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
072f62ed8   NeilBrown   knfsd: various nf...
361
362
  	dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len
  		- hdr;
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
363
364
365
366
  	/*
  	 * Round the length of the data which was specified up to
  	 * the next multiple of XDR units and then compare that
  	 * against the length which was actually received.
ba67a39ef   NeilBrown   knfsd: Allow NFSv...
367
368
369
  	 * Note that when RPCSEC/GSS (for example) is used, the
  	 * data buffer can be padded so dlen might be larger
  	 * than required.  It must never be smaller.
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
370
  	 */
ba67a39ef   NeilBrown   knfsd: Allow NFSv...
371
  	if (dlen < XDR_QUADLEN(len)*4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  		return 0;
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
373
374
375
376
  	if (args->count > max_blocksize) {
  		args->count = max_blocksize;
  		len = args->len = max_blocksize;
  	}
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
377
378
  	rqstp->rq_vec[0].iov_base = (void*)p;
  	rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
379
  	v = 0;
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
380
381
  	while (len > rqstp->rq_vec[v].iov_len) {
  		len -= rqstp->rq_vec[v].iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  		v++;
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
383
384
  		rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
  		rqstp->rq_vec[v].iov_len = PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  	}
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
386
  	rqstp->rq_vec[v].iov_len = len;
f34b95689   Peter Staubach   The NFSv2/NFSv3 s...
387
388
  	args->vlen = v + 1;
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
392
  nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
397
398
399
400
401
  					struct nfsd3_createargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh))
  	 || !(p = decode_filename(p, &args->name, &args->len)))
  		return 0;
  
  	switch (args->createmode = ntohl(*p++)) {
  	case NFS3_CREATE_UNCHECKED:
  	case NFS3_CREATE_GUARDED:
072f62ed8   NeilBrown   knfsd: various nf...
402
  		p = decode_sattr3(p, &args->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
411
412
413
414
  		break;
  	case NFS3_CREATE_EXCLUSIVE:
  		args->verf = p;
  		p += 2;
  		break;
  	default:
  		return 0;
  	}
  
  	return xdr_argsize_check(rqstp, p);
  }
  int
91f07168c   Al Viro   [PATCH] xdr annot...
415
  nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
  					struct nfsd3_createargs *args)
  {
072f62ed8   NeilBrown   knfsd: various nf...
418
419
  	if (!(p = decode_fh(p, &args->fh)) ||
  	    !(p = decode_filename(p, &args->name, &args->len)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  		return 0;
072f62ed8   NeilBrown   knfsd: various nf...
421
  	p = decode_sattr3(p, &args->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
426
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
427
  nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
  					struct nfsd3_symlinkargs *args)
  {
a628f6675   Chuck Lever   NFSD: Fix mixed s...
430
  	unsigned int len, avail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
  	char *old, *new;
  	struct kvec *vec;
072f62ed8   NeilBrown   knfsd: various nf...
433
434
  	if (!(p = decode_fh(p, &args->ffh)) ||
  	    !(p = decode_filename(p, &args->fname, &args->flen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
  		)
  		return 0;
072f62ed8   NeilBrown   knfsd: various nf...
437
  	p = decode_sattr3(p, &args->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
  	/* now decode the pathname, which might be larger than the first page.
  	 * As we have to check for nul's anyway, we copy it into a new page
  	 * This page appears in the rq_res.pages list, but as pages_len is always
  	 * 0, it won't get in the way
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
  	len = ntohl(*p++);
  	if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
  		return 0;
445243594   NeilBrown   [PATCH] knfsd: Re...
446
447
  	args->tname = new =
  		page_address(rqstp->rq_respages[rqstp->rq_resused++]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
458
459
460
  	args->tlen = len;
  	/* first copy and check from the first page */
  	old = (char*)p;
  	vec = &rqstp->rq_arg.head[0];
  	avail = vec->iov_len - (old - (char*)vec->iov_base);
  	while (len && avail && *old) {
  		*new++ = *old++;
  		len--;
  		avail--;
  	}
  	/* now copy next page if there is one */
  	if (len && !avail && rqstp->rq_arg.page_len) {
  		avail = rqstp->rq_arg.page_len;
a628f6675   Chuck Lever   NFSD: Fix mixed s...
461
462
  		if (avail > PAGE_SIZE)
  			avail = PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
  		old = page_address(rqstp->rq_arg.pages[0]);
  	}
  	while (len && avail && *old) {
  		*new++ = *old++;
  		len--;
  		avail--;
  	}
  	*new = '\0';
  	if (len)
  		return 0;
  
  	return 1;
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
478
  nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
481
482
483
484
485
486
487
  					struct nfsd3_mknodargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh))
  	 || !(p = decode_filename(p, &args->name, &args->len)))
  		return 0;
  
  	args->ftype = ntohl(*p++);
  
  	if (args->ftype == NF3BLK  || args->ftype == NF3CHR
072f62ed8   NeilBrown   knfsd: various nf...
488
489
  	 || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
  		p = decode_sattr3(p, &args->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
494
495
496
497
498
499
  
  	if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
  		args->major = ntohl(*p++);
  		args->minor = ntohl(*p++);
  	}
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
500
  nfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
508
509
510
511
512
  					struct nfsd3_renameargs *args)
  {
  	if (!(p = decode_fh(p, &args->ffh))
  	 || !(p = decode_filename(p, &args->fname, &args->flen))
  	 || !(p = decode_fh(p, &args->tfh))
  	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
  		return 0;
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
513
  nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
517
  					struct nfsd3_readlinkargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
445243594   NeilBrown   [PATCH] knfsd: Re...
518
519
  	args->buffer =
  		page_address(rqstp->rq_respages[rqstp->rq_resused++]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
523
524
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
525
  nfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
528
529
530
531
532
533
534
535
536
  					struct nfsd3_linkargs *args)
  {
  	if (!(p = decode_fh(p, &args->ffh))
  	 || !(p = decode_fh(p, &args->tfh))
  	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
  		return 0;
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
537
  nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
540
541
542
543
544
545
546
547
548
  					struct nfsd3_readdirargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
  	p = xdr_decode_hyper(p, &args->cookie);
  	args->verf   = p; p += 2;
  	args->dircount = ~0;
  	args->count  = ntohl(*p++);
  
  	if (args->count > PAGE_SIZE)
  		args->count = PAGE_SIZE;
445243594   NeilBrown   [PATCH] knfsd: Re...
549
550
  	args->buffer =
  		page_address(rqstp->rq_respages[rqstp->rq_resused++]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
556
  nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
  					struct nfsd3_readdirargs *args)
  {
  	int len, pn;
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
560
  	u32 max_blocksize = svc_max_payload(rqstp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
563
564
565
566
567
  
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
  	p = xdr_decode_hyper(p, &args->cookie);
  	args->verf     = p; p += 2;
  	args->dircount = ntohl(*p++);
  	args->count    = ntohl(*p++);
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
568
  	len = (args->count > max_blocksize) ? max_blocksize :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
  						  args->count;
  	args->count = len;
  
  	while (len > 0) {
445243594   NeilBrown   [PATCH] knfsd: Re...
573
  		pn = rqstp->rq_resused++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
  		if (!args->buffer)
  			args->buffer = page_address(rqstp->rq_respages[pn]);
  		len -= PAGE_SIZE;
  	}
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
583
  nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  					struct nfsd3_commitargs *args)
  {
  	if (!(p = decode_fh(p, &args->fh)))
  		return 0;
  	p = xdr_decode_hyper(p, &args->offset);
  	args->count = ntohl(*p++);
  
  	return xdr_argsize_check(rqstp, p);
  }
  
  /*
   * XDR encode functions
   */
  /*
   * There must be an encoding function for void results so svc_process
   * will work properly.
   */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
602
  nfs3svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
607
608
  {
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* GETATTR */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
609
  nfs3svc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
  					struct nfsd3_attrstat *resp)
  {
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
612
613
614
  	if (resp->status == 0) {
  		lease_get_mtime(resp->fh.fh_dentry->d_inode,
  				&resp->stat.mtime);
a334de286   David Shaw   [PATCH] knfsd: ch...
615
  		p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
616
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* SETATTR, REMOVE, RMDIR */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
622
  nfs3svc_encode_wccstat(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
626
627
628
629
630
  					struct nfsd3_attrstat *resp)
  {
  	p = encode_wcc_data(rqstp, p, &resp->fh);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* LOOKUP */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
631
  nfs3svc_encode_diropres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
635
636
637
638
639
640
641
642
643
  					struct nfsd3_diropres *resp)
  {
  	if (resp->status == 0) {
  		p = encode_fh(p, &resp->fh);
  		p = encode_post_op_attr(rqstp, p, &resp->fh);
  	}
  	p = encode_post_op_attr(rqstp, p, &resp->dirfh);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* ACCESS */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
644
  nfs3svc_encode_accessres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
647
648
649
650
651
652
653
654
  					struct nfsd3_accessres *resp)
  {
  	p = encode_post_op_attr(rqstp, p, &resp->fh);
  	if (resp->status == 0)
  		*p++ = htonl(resp->access);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* READLINK */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
655
  nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
663
664
  					struct nfsd3_readlinkres *resp)
  {
  	p = encode_post_op_attr(rqstp, p, &resp->fh);
  	if (resp->status == 0) {
  		*p++ = htonl(resp->len);
  		xdr_ressize_check(rqstp, p);
  		rqstp->rq_res.page_len = resp->len;
  		if (resp->len & 3) {
  			/* need to pad the tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
  			rqstp->rq_res.tail[0].iov_base = p;
  			*p = 0;
  			rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
  		}
  		return 1;
  	} else
  		return xdr_ressize_check(rqstp, p);
  }
  
  /* READ */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
676
  nfs3svc_encode_readres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
682
683
684
  					struct nfsd3_readres *resp)
  {
  	p = encode_post_op_attr(rqstp, p, &resp->fh);
  	if (resp->status == 0) {
  		*p++ = htonl(resp->count);
  		*p++ = htonl(resp->eof);
  		*p++ = htonl(resp->count);	/* xdr opaque count */
  		xdr_ressize_check(rqstp, p);
25985edce   Lucas De Marchi   Fix common misspe...
685
  		/* now update rqstp->rq_res to reflect data as well */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
  		rqstp->rq_res.page_len = resp->count;
  		if (resp->count & 3) {
  			/* need to pad the tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
692
693
694
695
696
697
698
699
  			rqstp->rq_res.tail[0].iov_base = p;
  			*p = 0;
  			rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
  		}
  		return 1;
  	} else
  		return xdr_ressize_check(rqstp, p);
  }
  
  /* WRITE */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
700
  nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
704
705
706
707
708
709
710
711
712
713
714
  					struct nfsd3_writeres *resp)
  {
  	p = encode_wcc_data(rqstp, p, &resp->fh);
  	if (resp->status == 0) {
  		*p++ = htonl(resp->count);
  		*p++ = htonl(resp->committed);
  		*p++ = htonl(nfssvc_boot.tv_sec);
  		*p++ = htonl(nfssvc_boot.tv_usec);
  	}
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* CREATE, MKDIR, SYMLINK, MKNOD */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
715
  nfs3svc_encode_createres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
717
718
719
720
721
722
723
724
725
726
727
728
  					struct nfsd3_diropres *resp)
  {
  	if (resp->status == 0) {
  		*p++ = xdr_one;
  		p = encode_fh(p, &resp->fh);
  		p = encode_post_op_attr(rqstp, p, &resp->fh);
  	}
  	p = encode_wcc_data(rqstp, p, &resp->dirfh);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* RENAME */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
729
  nfs3svc_encode_renameres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
736
737
738
  					struct nfsd3_renameres *resp)
  {
  	p = encode_wcc_data(rqstp, p, &resp->ffh);
  	p = encode_wcc_data(rqstp, p, &resp->tfh);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* LINK */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
739
  nfs3svc_encode_linkres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
745
746
747
748
  					struct nfsd3_linkres *resp)
  {
  	p = encode_post_op_attr(rqstp, p, &resp->fh);
  	p = encode_wcc_data(rqstp, p, &resp->tfh);
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* READDIR */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
749
  nfs3svc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
752
753
754
755
756
757
758
759
760
761
762
  					struct nfsd3_readdirres *resp)
  {
  	p = encode_post_op_attr(rqstp, p, &resp->fh);
  
  	if (resp->status == 0) {
  		/* stupid readdir cookie */
  		memcpy(p, resp->verf, 8); p += 2;
  		xdr_ressize_check(rqstp, p);
  		if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
  			return 1; /*No room for trailer */
  		rqstp->rq_res.page_len = (resp->count) << 2;
  
  		/* add the 'tail' to the end of the 'head' page - page 0. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
767
768
769
770
  		rqstp->rq_res.tail[0].iov_base = p;
  		*p++ = 0;		/* no more entries */
  		*p++ = htonl(resp->common.err == nfserr_eof);
  		rqstp->rq_res.tail[0].iov_len = 2<<2;
  		return 1;
  	} else
  		return xdr_ressize_check(rqstp, p);
  }
3ee6f61ca   Adrian Bunk   [PATCH] remove NF...
771
  static __be32 *
91f07168c   Al Viro   [PATCH] xdr annot...
772
  encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name,
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
773
  	     int namlen, u64 ino)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
  {
  	*p++ = xdr_one;				 /* mark entry present */
  	p    = xdr_encode_hyper(p, ino);	 /* file id */
  	p    = xdr_encode_array(p, name, namlen);/* name length & name */
  
  	cd->offset = p;				/* remember pointer */
  	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
  
  	return p;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
787
788
789
790
791
792
793
  static int
  compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
  		const char *name, int namlen)
  {
  	struct svc_export	*exp;
  	struct dentry		*dparent, *dchild;
  	int rv = 0;
  
  	dparent = cd->fh.fh_dentry;
  	exp  = cd->fh.fh_export;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
797
798
799
  	if (isdotent(name, namlen)) {
  		if (namlen == 2) {
  			dchild = dget_parent(dparent);
  			if (dchild == dparent) {
  				/* filesystem root - cannot return filehandle for ".." */
  				dput(dchild);
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
800
  				return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
803
804
805
806
  			}
  		} else
  			dchild = dget(dparent);
  	} else
  		dchild = lookup_one_len(name, dparent, namlen);
  	if (IS_ERR(dchild))
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
807
808
809
810
811
812
813
814
815
816
817
  		return -ENOENT;
  	rv = -ENOENT;
  	if (d_mountpoint(dchild))
  		goto out;
  	rv = fh_compose(fhp, exp, dchild, &cd->fh);
  	if (rv)
  		goto out;
  	if (!dchild->d_inode)
  		goto out;
  	rv = 0;
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
  	dput(dchild);
  	return rv;
  }
c47d832bc   Daniel Mack   nfsd: make local ...
821
  static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen)
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
822
823
824
  {
  	struct svc_fh	fh;
  	int err;
aed100faf   J. Bruce Fields   nfsd: fix leak on...
825
  	fh_init(&fh, NFS3_FHSIZE);
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
826
827
828
829
  	err = compose_entry_fh(cd, &fh, name, namlen);
  	if (err) {
  		*p++ = 0;
  		*p++ = 0;
aed100faf   J. Bruce Fields   nfsd: fix leak on...
830
  		goto out;
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
831
832
833
834
  	}
  	p = encode_post_op_attr(cd->rqstp, p, &fh);
  	*p++ = xdr_one;			/* yes, a file handle follows */
  	p = encode_fh(p, &fh);
aed100faf   J. Bruce Fields   nfsd: fix leak on...
835
  out:
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
836
837
838
  	fh_put(&fh);
  	return p;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
841
842
843
844
845
846
847
848
849
850
851
  /*
   * Encode a directory entry. This one works for both normal readdir
   * and readdirplus.
   * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
   * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
   * 
   * The readdirplus baggage is 1+21 words for post_op_attr, plus the
   * file handle.
   */
  
  #define NFS3_ENTRY_BAGGAGE	(2 + 1 + 2 + 1)
  #define NFS3_ENTRYPLUS_BAGGAGE	(1 + 21 + 1 + (NFS3_FHSIZE >> 2))
  static int
598b9a563   NeilBrown   [PATCH] knfsd: al...
852
  encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
40ee5dc6a   Peter Staubach   knfsd: 64 bit ino...
853
  	     loff_t offset, u64 ino, unsigned int d_type, int plus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
856
  {
  	struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
  		       					common);
91f07168c   Al Viro   [PATCH] xdr annot...
857
  	__be32		*p = cd->buffer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  	caddr_t		curr_page_addr = NULL;
  	int		pn;		/* current page number */
  	int		slen;		/* string (name) length */
  	int		elen;		/* estimated entry length in words */
  	int		num_entry_words = 0;	/* actual number of words */
  
  	if (cd->offset) {
  		u64 offset64 = offset;
  
  		if (unlikely(cd->offset1)) {
  			/* we ended up with offset on a page boundary */
  			*cd->offset = htonl(offset64 >> 32);
  			*cd->offset1 = htonl(offset64 & 0xffffffff);
  			cd->offset1 = NULL;
  		} else {
598b9a563   NeilBrown   [PATCH] knfsd: al...
873
  			xdr_encode_hyper(cd->offset, offset64);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
  		}
  	}
  
  	/*
  	dprintk("encode_entry(%.*s @%ld%s)
  ",
  		namlen, name, (long) offset, plus? " plus" : "");
  	 */
  
  	/* truncate filename if too long */
  	if (namlen > NFS3_MAXNAMLEN)
  		namlen = NFS3_MAXNAMLEN;
  
  	slen = XDR_QUADLEN(namlen);
  	elen = slen + NFS3_ENTRY_BAGGAGE
  		+ (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
  
  	if (cd->buflen < elen) {
  		cd->common.err = nfserr_toosmall;
  		return -EINVAL;
  	}
  
  	/* determine which page in rq_respages[] we are currently filling */
  	for (pn=1; pn < cd->rqstp->rq_resused; pn++) {
  		curr_page_addr = page_address(cd->rqstp->rq_respages[pn]);
  
  		if (((caddr_t)cd->buffer >= curr_page_addr) &&
  		    ((caddr_t)cd->buffer <  curr_page_addr + PAGE_SIZE))
  			break;
  	}
  
  	if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
  		/* encode entry in current page */
  
  		p = encode_entry_baggage(cd, p, name, namlen, ino);
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
909
910
  		if (plus)
  			p = encode_entryplus_baggage(cd, p, name, namlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
913
914
  		num_entry_words = p - cd->buffer;
  	} else if (cd->rqstp->rq_respages[pn+1] != NULL) {
  		/* temporarily encode entry into next page, then move back to
  		 * current and next page in rq_respages[] */
91f07168c   Al Viro   [PATCH] xdr annot...
915
  		__be32 *p1, *tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
917
918
919
920
921
  		int len1, len2;
  
  		/* grab next page for temporary storage of entry */
  		p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]);
  
  		p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
8177e6d6d   J. Bruce Fields   nfsd: clean up re...
922
  		if (plus)
479c2553a   Petr Vandrovec   Fix memory corrup...
923
  			p1 = encode_entryplus_baggage(cd, p1, name, namlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
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
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
  
  		/* determine entry word length and lengths to go in pages */
  		num_entry_words = p1 - tmp;
  		len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
  		if ((num_entry_words << 2) < len1) {
  			/* the actual number of words in the entry is less
  			 * than elen and can still fit in the current page
  			 */
  			memmove(p, tmp, num_entry_words << 2);
  			p += num_entry_words;
  
  			/* update offset */
  			cd->offset = cd->buffer + (cd->offset - tmp);
  		} else {
  			unsigned int offset_r = (cd->offset - tmp) << 2;
  
  			/* update pointer to offset location.
  			 * This is a 64bit quantity, so we need to
  			 * deal with 3 cases:
  			 *  -	entirely in first page
  			 *  -	entirely in second page
  			 *  -	4 bytes in each page
  			 */
  			if (offset_r + 8 <= len1) {
  				cd->offset = p + (cd->offset - tmp);
  			} else if (offset_r >= len1) {
  				cd->offset -= len1 >> 2;
  			} else {
  				/* sitting on the fence */
  				BUG_ON(offset_r != len1 - 4);
  				cd->offset = p + (cd->offset - tmp);
  				cd->offset1 = tmp;
  			}
  
  			len2 = (num_entry_words << 2) - len1;
  
  			/* move from temp page to current and next pages */
  			memmove(p, tmp, len1);
  			memmove(tmp, (caddr_t)tmp+len1, len2);
  
  			p = tmp + (len2 >> 2);
  		}
  	}
  	else {
  		cd->common.err = nfserr_toosmall;
  		return -EINVAL;
  	}
  
  	cd->buflen -= num_entry_words;
  	cd->buffer = p;
  	cd->common.err = nfs_ok;
  	return 0;
  
  }
  
  int
a0ad13ef6   NeilBrown   [PATCH] knfsd: Fi...
980
981
  nfs3svc_encode_entry(void *cd, const char *name,
  		     int namlen, loff_t offset, u64 ino, unsigned int d_type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
983
984
985
986
  {
  	return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
  }
  
  int
a0ad13ef6   NeilBrown   [PATCH] knfsd: Fi...
987
988
989
  nfs3svc_encode_entry_plus(void *cd, const char *name,
  			  int namlen, loff_t offset, u64 ino,
  			  unsigned int d_type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
993
994
995
  {
  	return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
  }
  
  /* FSSTAT */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
996
  nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
  					struct nfsd3_fsstatres *resp)
  {
  	struct kstatfs	*s = &resp->stats;
  	u64		bs = s->f_bsize;
  
  	*p++ = xdr_zero;	/* no post_op_attr */
  
  	if (resp->status == 0) {
  		p = xdr_encode_hyper(p, bs * s->f_blocks);	/* total bytes */
  		p = xdr_encode_hyper(p, bs * s->f_bfree);	/* free bytes */
  		p = xdr_encode_hyper(p, bs * s->f_bavail);	/* user available bytes */
  		p = xdr_encode_hyper(p, s->f_files);	/* total inodes */
  		p = xdr_encode_hyper(p, s->f_ffree);	/* free inodes */
  		p = xdr_encode_hyper(p, s->f_ffree);	/* user available inodes */
  		*p++ = htonl(resp->invarsec);	/* mean unchanged time */
  	}
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* FSINFO */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
1018
  nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  					struct nfsd3_fsinfores *resp)
  {
  	*p++ = xdr_zero;	/* no post_op_attr */
  
  	if (resp->status == 0) {
  		*p++ = htonl(resp->f_rtmax);
  		*p++ = htonl(resp->f_rtpref);
  		*p++ = htonl(resp->f_rtmult);
  		*p++ = htonl(resp->f_wtmax);
  		*p++ = htonl(resp->f_wtpref);
  		*p++ = htonl(resp->f_wtmult);
  		*p++ = htonl(resp->f_dtpref);
  		p = xdr_encode_hyper(p, resp->f_maxfilesize);
  		*p++ = xdr_one;
  		*p++ = xdr_zero;
  		*p++ = htonl(resp->f_properties);
  	}
  
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* PATHCONF */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
1042
  nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  					struct nfsd3_pathconfres *resp)
  {
  	*p++ = xdr_zero;	/* no post_op_attr */
  
  	if (resp->status == 0) {
  		*p++ = htonl(resp->p_link_max);
  		*p++ = htonl(resp->p_name_max);
  		*p++ = htonl(resp->p_no_trunc);
  		*p++ = htonl(resp->p_chown_restricted);
  		*p++ = htonl(resp->p_case_insensitive);
  		*p++ = htonl(resp->p_case_preserving);
  	}
  
  	return xdr_ressize_check(rqstp, p);
  }
  
  /* COMMIT */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
1061
  nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
  					struct nfsd3_commitres *resp)
  {
  	p = encode_wcc_data(rqstp, p, &resp->fh);
  	/* Write verifier */
  	if (resp->status == 0) {
  		*p++ = htonl(nfssvc_boot.tv_sec);
  		*p++ = htonl(nfssvc_boot.tv_usec);
  	}
  	return xdr_ressize_check(rqstp, p);
  }
  
  /*
   * XDR release functions
   */
  int
91f07168c   Al Viro   [PATCH] xdr annot...
1077
  nfs3svc_release_fhandle(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
1083
1084
  					struct nfsd3_attrstat *resp)
  {
  	fh_put(&resp->fh);
  	return 1;
  }
  
  int
91f07168c   Al Viro   [PATCH] xdr annot...
1085
  nfs3svc_release_fhandle2(struct svc_rqst *rqstp, __be32 *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
1089
1090
1091
  					struct nfsd3_fhandle_pair *resp)
  {
  	fh_put(&resp->fh1);
  	fh_put(&resp->fh2);
  	return 1;
  }