Blame view

fs/nfsd/nfsproc.c 21.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
   * Process version 2 NFS requests.
   *
   * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
  #include <linux/namei.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7

9a74af213   Boaz Harrosh   nfsd: Move privat...
8
9
  #include "cache.h"
  #include "xdr.h"
0a3adadee   J. Bruce Fields   nfsd: make fs/nfs...
10
  #include "vfs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
  
  typedef struct svc_rqst	svc_rqst;
  typedef struct svc_buf	svc_buf;
  
  #define NFSDDBG_FACILITY		NFSDDBG_PROC
7111c66e4   Al Viro   [PATCH] fix svc_p...
16
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
  nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
  {
  	return nfs_ok;
  }
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
21
22
  static __be32
  nfsd_return_attrs(__be32 err, struct nfsd_attrstat *resp)
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
23
24
  {
  	if (err) return err;
3dadecce2   Al Viro   switch vfs_getatt...
25
  	return fh_getattr(&resp->fh, &resp->stat);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
26
  }
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
27
28
  static __be32
  nfsd_return_dirop(__be32 err, struct nfsd_diropres *resp)
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
29
30
  {
  	if (err) return err;
3dadecce2   Al Viro   switch vfs_getatt...
31
  	return fh_getattr(&resp->fh, &resp->stat);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
32
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
  /*
   * Get a file's attributes
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
37
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
  nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
  					  struct nfsd_attrstat *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
41
  	__be32 nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
  	dprintk("nfsd: GETATTR  %s
  ", SVCFH_fmt(&argp->fh));
  
  	fh_copy(&resp->fh, &argp->fh);
04716e662   J. Bruce Fields   nfsd: permit unau...
46
47
  	nfserr = fh_verify(rqstp, &resp->fh, 0,
  			NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
48
  	return nfsd_return_attrs(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
54
  }
  
  /*
   * Set a file's attributes
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
55
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
  nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
  					  struct nfsd_attrstat  *resp)
  {
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
59
60
  	struct iattr *iap = &argp->attrs;
  	struct svc_fh *fhp;
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
61
  	__be32 nfserr;
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
62

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
  	dprintk("nfsd: SETATTR  %s, valid=%x, size=%ld
  ",
  		SVCFH_fmt(&argp->fh),
  		argp->attrs.ia_valid, (long) argp->attrs.ia_size);
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
67
68
69
70
71
72
73
  	fhp = fh_copy(&resp->fh, &argp->fh);
  
  	/*
  	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
  	 * which only requires access, and "set-[ac]time-to-X" which
  	 * requires ownership.
  	 * So if it looks like it might be "set both to the same time which
31051c85b   Jan Kara   fs: Give dentry t...
74
  	 * is close to now", and if setattr_prepare fails, then we
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
75
76
  	 * convert to "set to now" instead of "set to explicit time"
  	 *
31051c85b   Jan Kara   fs: Give dentry t...
77
  	 * We only call setattr_prepare as the last test as technically
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  	 * it is not an interface that we should be using.
  	 */
  #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
  #define	MAX_TOUCH_TIME_ERROR (30*60)
  	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
  	    iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
  		/*
  		 * Looks probable.
  		 *
  		 * Now just make sure time is in the right ballpark.
  		 * Solaris, at least, doesn't seem to care what the time
  		 * request is.  We require it be within 30 minutes of now.
  		 */
  		time_t delta = iap->ia_atime.tv_sec - get_seconds();
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
92
93
94
95
  
  		nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
  		if (nfserr)
  			goto done;
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
96
97
98
99
  
  		if (delta < 0)
  			delta = -delta;
  		if (delta < MAX_TOUCH_TIME_ERROR &&
31051c85b   Jan Kara   fs: Give dentry t...
100
  		    setattr_prepare(fhp->fh_dentry, iap) != 0) {
cc265089c   Andreas Gruenbacher   nfsd: Disable NFS...
101
102
103
104
105
106
107
108
109
110
111
  			/*
  			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
  			 * This will cause notify_change to set these times
  			 * to "now"
  			 */
  			iap->ia_valid &= ~BOTH_TIME_SET;
  		}
  	}
  
  	nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time_t)0);
  done:
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
112
  	return nfsd_return_attrs(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
  }
  
  /*
   * Look up a path name component
   * Note: the dentry in the resp->fh may be negative if the file
   * doesn't exist yet.
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
121
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
  nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
  					 struct nfsd_diropres  *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
125
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
135
  
  	dprintk("nfsd: LOOKUP   %s %.*s
  ",
  		SVCFH_fmt(&argp->fh), argp->len, argp->name);
  
  	fh_init(&resp->fh, NFS_FHSIZE);
  	nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
  				 &resp->fh);
  
  	fh_put(&argp->fh);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
136
  	return nfsd_return_dirop(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
  }
  
  /*
   * Read a symlink.
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
142
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
  nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_readlinkargs *argp,
  					   struct nfsd_readlinkres *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
146
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  
  	dprintk("nfsd: READLINK %s
  ", SVCFH_fmt(&argp->fh));
  
  	/* Read the symlink. */
  	resp->len = NFS_MAXPATHLEN;
  	nfserr = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len);
  
  	fh_put(&argp->fh);
  	return nfserr;
  }
  
  /*
   * Read a portion of a file.
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
163
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
  nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
  				       struct nfsd_readres  *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
167
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
175
176
  
  	dprintk("nfsd: READ    %s %d bytes at %d
  ",
  		SVCFH_fmt(&argp->fh),
  		argp->count, argp->offset);
  
  	/* Obtain buffer pointer for payload. 19 is 1 word for
  	 * status, 17 words for fattr, and 1 word for the byte count.
  	 */
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
177
  	if (NFSSVC_MAXBLKSIZE_V2 < argp->count) {
ad06e4bd6   Chuck Lever   [PATCH] knfsd: SU...
178
  		char buf[RPC_MAX_ADDRBUFLEN];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  		printk(KERN_NOTICE
ad06e4bd6   Chuck Lever   [PATCH] knfsd: SU...
180
181
182
  			"oversized read request from %s (%d bytes)
  ",
  				svc_print_addr(rqstp, buf, sizeof(buf)),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  				argp->count);
7adae489f   Greg Banks   [PATCH] knfsd: Pr...
184
  		argp->count = NFSSVC_MAXBLKSIZE_V2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	}
cd123012d   Jeff Layton   RPC: add wrapper ...
186
  	svc_reserve_auth(rqstp, (19<<2) + argp->count + 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  
  	resp->count = argp->count;
039a87ca5   J. Bruce Fields   nfsd: minor nfsd ...
189
  	nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  				  argp->offset,
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
191
  			   	  rqstp->rq_vec, argp->vlen,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  				  &resp->count);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
193
  	if (nfserr) return nfserr;
3dadecce2   Al Viro   switch vfs_getatt...
194
  	return fh_getattr(&resp->fh, &resp->stat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
200
  }
  
  /*
   * Write data to a file
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
201
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
  nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
  					struct nfsd_attrstat  *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
205
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	int	stable = 1;
31dec2538   David Shaw   Short write in nf...
207
  	unsigned long cnt = argp->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
  
  	dprintk("nfsd: WRITE    %s %d bytes at %d
  ",
  		SVCFH_fmt(&argp->fh),
  		argp->len, argp->offset);
  
  	nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL,
  				   argp->offset,
3cc03b164   NeilBrown   [PATCH] knfsd: Av...
216
  				   rqstp->rq_vec, argp->vlen,
31dec2538   David Shaw   Short write in nf...
217
  			           &cnt,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  				   &stable);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
219
  	return nfsd_return_attrs(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
  }
  
  /*
   * CREATE processing is complicated. The keyword here is `overloaded.'
   * The parent directory is kept locked between the check for existence
   * and the actual create() call in compliance with VFS protocols.
   * N.B. After this call _both_ argp->fh and resp->fh need an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
228
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
236
  nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
  					 struct nfsd_diropres   *resp)
  {
  	svc_fh		*dirfhp = &argp->fh;
  	svc_fh		*newfhp = &resp->fh;
  	struct iattr	*attr = &argp->attrs;
  	struct inode	*inode;
  	struct dentry	*dchild;
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
237
238
  	int		type, mode;
  	__be32		nfserr;
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
239
  	int		hosterr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
  	dev_t		rdev = 0, wanted = new_decode_dev(attr->ia_size);
  
  	dprintk("nfsd: CREATE   %s %.*s
  ",
  		SVCFH_fmt(dirfhp), argp->len, argp->name);
  
  	/* First verify the parent file handle */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
247
  	nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  	if (nfserr)
  		goto done; /* must fh_put dirfhp even on error */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
250
  	/* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
  	nfserr = nfserr_exist;
  	if (isdotent(argp->name, argp->len))
  		goto done;
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
255
256
257
258
259
  	hosterr = fh_want_write(dirfhp);
  	if (hosterr) {
  		nfserr = nfserrno(hosterr);
  		goto done;
  	}
7ed94296a   NeilBrown   [PATCH] knfsd: nf...
260
  	fh_lock_nested(dirfhp, I_MUTEX_PARENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
267
  	dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
  	if (IS_ERR(dchild)) {
  		nfserr = nfserrno(PTR_ERR(dchild));
  		goto out_unlock;
  	}
  	fh_init(newfhp, NFS_FHSIZE);
  	nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
2b0143b5c   David Howells   VFS: normal files...
268
  	if (!nfserr && d_really_is_negative(dchild))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  		nfserr = nfserr_noent;
  	dput(dchild);
  	if (nfserr) {
  		if (nfserr != nfserr_noent)
  			goto out_unlock;
  		/*
  		 * If the new file handle wasn't verified, we can't tell
  		 * whether the file exists or not. Time to bail ...
  		 */
  		nfserr = nfserr_acces;
  		if (!newfhp->fh_dentry) {
  			printk(KERN_WARNING 
  				"nfsd_proc_create: file handle not verified
  ");
  			goto out_unlock;
  		}
  	}
2b0143b5c   David Howells   VFS: normal files...
286
  	inode = d_inode(newfhp->fh_dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  
  	/* Unfudge the mode bits */
  	if (attr->ia_valid & ATTR_MODE) {
  		type = attr->ia_mode & S_IFMT;
  		mode = attr->ia_mode & ~S_IFMT;
  		if (!type) {
  			/* no type, so if target exists, assume same as that,
  			 * else assume a file */
  			if (inode) {
  				type = inode->i_mode & S_IFMT;
  				switch(type) {
  				case S_IFCHR:
  				case S_IFBLK:
  					/* reserve rdev for later checking */
  					rdev = inode->i_rdev;
  					attr->ia_valid |= ATTR_SIZE;
  
  					/* FALLTHROUGH */
  				case S_IFIFO:
  					/* this is probably a permission check..
  					 * at least IRIX implements perm checking on
  					 *   echo thing > device-special-file-or-pipe
  					 * by doing a CREATE with type==0
  					 */
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
311
312
  					nfserr = nfsd_permission(rqstp,
  								 newfhp->fh_export,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  								 newfhp->fh_dentry,
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
314
  								 NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  					if (nfserr && nfserr != nfserr_rofs)
  						goto out_unlock;
  				}
  			} else
  				type = S_IFREG;
  		}
  	} else if (inode) {
  		type = inode->i_mode & S_IFMT;
  		mode = inode->i_mode & ~S_IFMT;
  	} else {
  		type = S_IFREG;
  		mode = 0;	/* ??? */
  	}
  
  	attr->ia_valid |= ATTR_MODE;
  	attr->ia_mode = mode;
  
  	/* Special treatment for non-regular files according to the
  	 * gospel of sun micro
  	 */
  	if (type != S_IFREG) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
  		if (type != S_IFBLK && type != S_IFCHR) {
  			rdev = 0;
  		} else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
  			/* If you think you've seen the worst, grok this. */
  			type = S_IFIFO;
  		} else {
  			/* Okay, char or block special */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  			if (!rdev)
  				rdev = wanted;
  		}
  
  		/* we've used the SIZE information, so discard it */
  		attr->ia_valid &= ~ATTR_SIZE;
  
  		/* Make sure the type and device matches */
  		nfserr = nfserr_exist;
  		if (inode && type != (inode->i_mode & S_IFMT))
  			goto out_unlock;
  	}
  
  	nfserr = 0;
  	if (!inode) {
  		/* File doesn't exist. Create it and set attrs */
b44061d0b   J. Bruce Fields   nfsd: reorganize ...
359
360
  		nfserr = nfsd_create_locked(rqstp, dirfhp, argp->name,
  					argp->len, attr, type, rdev, newfhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  	} else if (type == S_IFREG) {
  		dprintk("nfsd:   existing %s, valid=%x, size=%ld
  ",
  			argp->name, attr->ia_valid, (long) attr->ia_size);
  		/* File already exists. We ignore all attributes except
  		 * size, so that creat() behaves exactly like
  		 * open(..., O_CREAT|O_TRUNC|O_WRONLY).
  		 */
  		attr->ia_valid &= ATTR_SIZE;
  		if (attr->ia_valid)
  			nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time_t)0);
  	}
  
  out_unlock:
  	/* We don't really need to unlock, as fh_put does it. */
  	fh_unlock(dirfhp);
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
377
  	fh_drop_write(dirfhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  done:
  	fh_put(dirfhp);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
380
  	return nfsd_return_dirop(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  }
7111c66e4   Al Viro   [PATCH] fix svc_p...
382
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
  nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
  					 void		       *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
386
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
391
392
393
394
395
396
  
  	dprintk("nfsd: REMOVE   %s %.*s
  ", SVCFH_fmt(&argp->fh),
  		argp->len, argp->name);
  
  	/* Unlink. -SIFDIR means file must not be a directory */
  	nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
  	fh_put(&argp->fh);
  	return nfserr;
  }
7111c66e4   Al Viro   [PATCH] fix svc_p...
397
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
  nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
  				  	 void		        *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
401
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
407
408
409
410
411
412
413
414
415
  
  	dprintk("nfsd: RENAME   %s %.*s -> 
  ",
  		SVCFH_fmt(&argp->ffh), argp->flen, argp->fname);
  	dprintk("nfsd:        ->  %s %.*s
  ",
  		SVCFH_fmt(&argp->tfh), argp->tlen, argp->tname);
  
  	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
  				    &argp->tfh, argp->tname, argp->tlen);
  	fh_put(&argp->ffh);
  	fh_put(&argp->tfh);
  	return nfserr;
  }
7111c66e4   Al Viro   [PATCH] fix svc_p...
416
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
  nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
  				void			    *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
420
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  
  	dprintk("nfsd: LINK     %s ->
  ",
  		SVCFH_fmt(&argp->ffh));
  	dprintk("nfsd:    %s %.*s
  ",
  		SVCFH_fmt(&argp->tfh),
  		argp->tlen,
  		argp->tname);
  
  	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
  				  &argp->ffh);
  	fh_put(&argp->ffh);
  	fh_put(&argp->tfh);
  	return nfserr;
  }
7111c66e4   Al Viro   [PATCH] fix svc_p...
437
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
  nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
  				          void			  *resp)
  {
  	struct svc_fh	newfh;
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
442
  	__be32		nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
446
447
448
449
450
  
  	dprintk("nfsd: SYMLINK  %s %.*s -> %.*s
  ",
  		SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
  		argp->tlen, argp->tname);
  
  	fh_init(&newfh, NFS_FHSIZE);
  	/*
0aeae33f5   J. Bruce Fields   nfsd: make NFSv2 ...
451
452
453
  	 * Crazy hack: the request fits in a page, and already-decoded
  	 * attributes follow argp->tname, so it's safe to just write a
  	 * null to ensure it's null-terminated:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  	 */
0aeae33f5   J. Bruce Fields   nfsd: make NFSv2 ...
455
  	argp->tname[argp->tlen] = '\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
1e444f5bc   Kinglong Mee   NFSD: Remove iatt...
457
  						 argp->tname, &newfh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
461
462
463
464
465
466
467
  
  	fh_put(&argp->ffh);
  	fh_put(&newfh);
  	return nfserr;
  }
  
  /*
   * Make directory. This operation is not idempotent.
   * N.B. After this call resp->fh needs an fh_put
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
468
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
  nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
  					struct nfsd_diropres   *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
472
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  
  	dprintk("nfsd: MKDIR    %s %.*s
  ", SVCFH_fmt(&argp->fh), argp->len, argp->name);
  
  	if (resp->fh.fh_dentry) {
  		printk(KERN_WARNING
  			"nfsd_proc_mkdir: response already verified??
  ");
  	}
  
  	argp->attrs.ia_valid &= ~ATTR_SIZE;
  	fh_init(&resp->fh, NFS_FHSIZE);
  	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
  				    &argp->attrs, S_IFDIR, 0, &resp->fh);
  	fh_put(&argp->fh);
846f2fcd7   David Shaw   [PATCH] knfsd: Pr...
488
  	return nfsd_return_dirop(nfserr, resp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
  }
  
  /*
   * Remove a directory
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
494
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
  nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
  				 	void		      *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
498
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
503
504
505
506
507
508
509
510
  
  	dprintk("nfsd: RMDIR    %s %.*s
  ", SVCFH_fmt(&argp->fh), argp->len, argp->name);
  
  	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
  	fh_put(&argp->fh);
  	return nfserr;
  }
  
  /*
   * Read a portion of a directory.
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
511
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
  nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
  					  struct nfsd_readdirres  *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
515
516
  	int		count;
  	__be32		nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
545
546
547
548
549
550
551
  	loff_t		offset;
  
  	dprintk("nfsd: READDIR  %s %d bytes at %d
  ",
  		SVCFH_fmt(&argp->fh),		
  		argp->count, argp->cookie);
  
  	/* Shrink to the client read size */
  	count = (argp->count >> 2) - 2;
  
  	/* Make sure we've room for the NULL ptr & eof flag */
  	count -= 2;
  	if (count < 0)
  		count = 0;
  
  	resp->buffer = argp->buffer;
  	resp->offset = NULL;
  	resp->buflen = count;
  	resp->common.err = nfs_ok;
  	/* Read directory and encode entries on the fly */
  	offset = argp->cookie;
  	nfserr = nfsd_readdir(rqstp, &argp->fh, &offset, 
  			      &resp->common, nfssvc_encode_entry);
  
  	resp->count = resp->buffer - argp->buffer;
  	if (resp->offset)
  		*resp->offset = htonl(offset);
  
  	fh_put(&argp->fh);
  	return nfserr;
  }
  
  /*
   * Get file system info
   */
7111c66e4   Al Viro   [PATCH] fix svc_p...
552
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
  nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
  					  struct nfsd_statfsres *resp)
  {
c4d987ba8   Al Viro   [PATCH] nfsd: NFS...
556
  	__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
  
  	dprintk("nfsd: STATFS   %s
  ", SVCFH_fmt(&argp->fh));
04716e662   J. Bruce Fields   nfsd: permit unau...
560
561
  	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
  			NFSD_MAY_BYPASS_GSS_ON_ROOT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
566
567
568
569
  	fh_put(&argp->fh);
  	return nfserr;
  }
  
  /*
   * NFSv2 Server procedures.
   * Only the results of non-idempotent operations are cached.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
  struct nfsd_void { int dummy; };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
  #define ST 1		/* status */
  #define FH 8		/* filehandle */
  #define	AT 18		/* attributes */
  
  static struct svc_procedure		nfsd_procedures2[18] = {
b9081d90f   Yu Zhiguo   NFS: kill off com...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
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
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  	[NFSPROC_NULL] = {
  		.pc_func = (svc_procfunc) nfsd_proc_null,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_void,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_void),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_GETATTR] = {
  		.pc_func = (svc_procfunc) nfsd_proc_getattr,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_fhandle),
  		.pc_ressize = sizeof(struct nfsd_attrstat),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST+AT,
  	},
  	[NFSPROC_SETATTR] = {
  		.pc_func = (svc_procfunc) nfsd_proc_setattr,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_sattrargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_sattrargs),
  		.pc_ressize = sizeof(struct nfsd_attrstat),
  		.pc_cachetype = RC_REPLBUFF,
  		.pc_xdrressize = ST+AT,
  	},
  	[NFSPROC_ROOT] = {
  		.pc_decode = (kxdrproc_t) nfssvc_decode_void,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_void),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_LOOKUP] = {
  		.pc_func = (svc_procfunc) nfsd_proc_lookup,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_diropargs),
  		.pc_ressize = sizeof(struct nfsd_diropres),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST+FH+AT,
  	},
  	[NFSPROC_READLINK] = {
  		.pc_func = (svc_procfunc) nfsd_proc_readlink,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_readlinkargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_readlinkres,
  		.pc_argsize = sizeof(struct nfsd_readlinkargs),
  		.pc_ressize = sizeof(struct nfsd_readlinkres),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
  	},
  	[NFSPROC_READ] = {
  		.pc_func = (svc_procfunc) nfsd_proc_read,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_readargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_readres,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_readargs),
  		.pc_ressize = sizeof(struct nfsd_readres),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
  	},
  	[NFSPROC_WRITECACHE] = {
  		.pc_decode = (kxdrproc_t) nfssvc_decode_void,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_void),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_WRITE] = {
  		.pc_func = (svc_procfunc) nfsd_proc_write,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_writeargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_writeargs),
  		.pc_ressize = sizeof(struct nfsd_attrstat),
  		.pc_cachetype = RC_REPLBUFF,
  		.pc_xdrressize = ST+AT,
  	},
  	[NFSPROC_CREATE] = {
  		.pc_func = (svc_procfunc) nfsd_proc_create,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_createargs),
  		.pc_ressize = sizeof(struct nfsd_diropres),
  		.pc_cachetype = RC_REPLBUFF,
  		.pc_xdrressize = ST+FH+AT,
  	},
  	[NFSPROC_REMOVE] = {
  		.pc_func = (svc_procfunc) nfsd_proc_remove,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_diropargs),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_REPLSTAT,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_RENAME] = {
  		.pc_func = (svc_procfunc) nfsd_proc_rename,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_renameargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_renameargs),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_REPLSTAT,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_LINK] = {
  		.pc_func = (svc_procfunc) nfsd_proc_link,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_linkargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_linkargs),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_REPLSTAT,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_SYMLINK] = {
  		.pc_func = (svc_procfunc) nfsd_proc_symlink,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_symlinkargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_symlinkargs),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_REPLSTAT,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_MKDIR] = {
  		.pc_func = (svc_procfunc) nfsd_proc_mkdir,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
  		.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
  		.pc_argsize = sizeof(struct nfsd_createargs),
  		.pc_ressize = sizeof(struct nfsd_diropres),
  		.pc_cachetype = RC_REPLBUFF,
  		.pc_xdrressize = ST+FH+AT,
  	},
  	[NFSPROC_RMDIR] = {
  		.pc_func = (svc_procfunc) nfsd_proc_rmdir,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_void,
  		.pc_argsize = sizeof(struct nfsd_diropargs),
  		.pc_ressize = sizeof(struct nfsd_void),
  		.pc_cachetype = RC_REPLSTAT,
  		.pc_xdrressize = ST,
  	},
  	[NFSPROC_READDIR] = {
  		.pc_func = (svc_procfunc) nfsd_proc_readdir,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_readdirargs,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_readdirres,
  		.pc_argsize = sizeof(struct nfsd_readdirargs),
  		.pc_ressize = sizeof(struct nfsd_readdirres),
  		.pc_cachetype = RC_NOCACHE,
  	},
  	[NFSPROC_STATFS] = {
  		.pc_func = (svc_procfunc) nfsd_proc_statfs,
  		.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
  		.pc_encode = (kxdrproc_t) nfssvc_encode_statfsres,
  		.pc_argsize = sizeof(struct nfsd_fhandle),
  		.pc_ressize = sizeof(struct nfsd_statfsres),
  		.pc_cachetype = RC_NOCACHE,
  		.pc_xdrressize = ST+5,
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
748
749
750
751
752
753
754
755
  };
  
  
  struct svc_version	nfsd_version2 = {
  		.vs_vers	= 2,
  		.vs_nproc	= 18,
  		.vs_proc	= nfsd_procedures2,
  		.vs_dispatch	= nfsd_dispatch,
  		.vs_xdrsize	= NFS2_SVC_XDRSIZE,
  };
  
  /*
   * Map errnos to NFS errnos.
   */
63f103111   Al Viro   [PATCH] nfsd: nfs...
756
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
759
  nfserrno (int errno)
  {
  	static struct {
63f103111   Al Viro   [PATCH] nfsd: nfs...
760
  		__be32	nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
763
764
765
766
767
  		int	syserr;
  	} nfs_errtbl[] = {
  		{ nfs_ok, 0 },
  		{ nfserr_perm, -EPERM },
  		{ nfserr_noent, -ENOENT },
  		{ nfserr_io, -EIO },
  		{ nfserr_nxio, -ENXIO },
62814d6a9   Jeff Layton   nfsd: add a nfser...
768
  		{ nfserr_fbig, -E2BIG },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
  		{ nfserr_acces, -EACCES },
  		{ nfserr_exist, -EEXIST },
  		{ nfserr_xdev, -EXDEV },
  		{ nfserr_mlink, -EMLINK },
  		{ 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 },
  #ifdef EDQUOT
  		{ nfserr_dquot, -EDQUOT },
  #endif
  		{ nfserr_stale, -ESTALE },
  		{ nfserr_jukebox, -ETIMEDOUT },
599eb3046   NeilBrown   knfsd: nfsd: Hand...
788
  		{ nfserr_jukebox, -ERESTARTSYS },
062304a81   J. Bruce Fields   nfsd: stop transl...
789
790
  		{ nfserr_jukebox, -EAGAIN },
  		{ nfserr_jukebox, -EWOULDBLOCK },
3beb6cd1d   J. Bruce Fields   nfsd: don't drop ...
791
  		{ nfserr_jukebox, -ENOMEM },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
  		{ nfserr_io, -ETXTBSY },
a838cc49d   Andreas Gruenbacher   [PATCH] NFSD: Add...
793
  		{ nfserr_notsupp, -EOPNOTSUPP },
b7aeda40d   Dean Hildebrand   nfsd: add etoosma...
794
  		{ nfserr_toosmall, -ETOOSMALL },
f39bde24b   J. Bruce Fields   nfsd4: fix error ...
795
  		{ nfserr_serverfault, -ESERVERFAULT },
b3fbfe0e7   Jeff Layton   nfsd: print statu...
796
  		{ nfserr_serverfault, -ENFILE },
42e616167   J. Bruce Fields   nfsd: handle EUCLEAN
797
  		{ nfserr_io, -EUCLEAN },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
  	};
  	int	i;
63f103111   Al Viro   [PATCH] nfsd: nfs...
800
  	for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
803
  		if (nfs_errtbl[i].syserr == errno)
  			return nfs_errtbl[i].nfserr;
  	}
ff30f08c3   J. Bruce Fields   nfsd: only WARN o...
804
805
  	WARN_ONCE(1, "nfsd: non-standard errno: %d
  ", errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
807
  	return nfserr_io;
  }