xref: /openbmc/linux/fs/nfsd/nfsproc.c (revision 74be2d3b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Process version 2 NFS requests.
4  *
5  * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
6  */
7 
8 #include <linux/namei.h>
9 
10 #include "cache.h"
11 #include "xdr.h"
12 #include "vfs.h"
13 
14 typedef struct svc_rqst	svc_rqst;
15 typedef struct svc_buf	svc_buf;
16 
17 #define NFSDDBG_FACILITY		NFSDDBG_PROC
18 
19 
20 static __be32
21 nfsd_proc_null(struct svc_rqst *rqstp)
22 {
23 	return nfs_ok;
24 }
25 
26 static __be32
27 nfsd_return_attrs(__be32 err, struct nfsd_attrstat *resp)
28 {
29 	if (err) return err;
30 	return fh_getattr(&resp->fh, &resp->stat);
31 }
32 static __be32
33 nfsd_return_dirop(__be32 err, struct nfsd_diropres *resp)
34 {
35 	if (err) return err;
36 	return fh_getattr(&resp->fh, &resp->stat);
37 }
38 /*
39  * Get a file's attributes
40  * N.B. After this call resp->fh needs an fh_put
41  */
42 static __be32
43 nfsd_proc_getattr(struct svc_rqst *rqstp)
44 {
45 	struct nfsd_fhandle *argp = rqstp->rq_argp;
46 	struct nfsd_attrstat *resp = rqstp->rq_resp;
47 	__be32 nfserr;
48 	dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
49 
50 	fh_copy(&resp->fh, &argp->fh);
51 	nfserr = fh_verify(rqstp, &resp->fh, 0,
52 			NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
53 	return nfsd_return_attrs(nfserr, resp);
54 }
55 
56 /*
57  * Set a file's attributes
58  * N.B. After this call resp->fh needs an fh_put
59  */
60 static __be32
61 nfsd_proc_setattr(struct svc_rqst *rqstp)
62 {
63 	struct nfsd_sattrargs *argp = rqstp->rq_argp;
64 	struct nfsd_attrstat *resp = rqstp->rq_resp;
65 	struct iattr *iap = &argp->attrs;
66 	struct svc_fh *fhp;
67 	__be32 nfserr;
68 
69 	dprintk("nfsd: SETATTR  %s, valid=%x, size=%ld\n",
70 		SVCFH_fmt(&argp->fh),
71 		argp->attrs.ia_valid, (long) argp->attrs.ia_size);
72 
73 	fhp = fh_copy(&resp->fh, &argp->fh);
74 
75 	/*
76 	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
77 	 * which only requires access, and "set-[ac]time-to-X" which
78 	 * requires ownership.
79 	 * So if it looks like it might be "set both to the same time which
80 	 * is close to now", and if setattr_prepare fails, then we
81 	 * convert to "set to now" instead of "set to explicit time"
82 	 *
83 	 * We only call setattr_prepare as the last test as technically
84 	 * it is not an interface that we should be using.
85 	 */
86 #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
87 #define	MAX_TOUCH_TIME_ERROR (30*60)
88 	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
89 	    iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
90 		/*
91 		 * Looks probable.
92 		 *
93 		 * Now just make sure time is in the right ballpark.
94 		 * Solaris, at least, doesn't seem to care what the time
95 		 * request is.  We require it be within 30 minutes of now.
96 		 */
97 		time64_t delta = iap->ia_atime.tv_sec - ktime_get_real_seconds();
98 
99 		nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
100 		if (nfserr)
101 			goto done;
102 
103 		if (delta < 0)
104 			delta = -delta;
105 		if (delta < MAX_TOUCH_TIME_ERROR &&
106 		    setattr_prepare(fhp->fh_dentry, iap) != 0) {
107 			/*
108 			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
109 			 * This will cause notify_change to set these times
110 			 * to "now"
111 			 */
112 			iap->ia_valid &= ~BOTH_TIME_SET;
113 		}
114 	}
115 
116 	nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0);
117 done:
118 	return nfsd_return_attrs(nfserr, resp);
119 }
120 
121 /*
122  * Look up a path name component
123  * Note: the dentry in the resp->fh may be negative if the file
124  * doesn't exist yet.
125  * N.B. After this call resp->fh needs an fh_put
126  */
127 static __be32
128 nfsd_proc_lookup(struct svc_rqst *rqstp)
129 {
130 	struct nfsd_diropargs *argp = rqstp->rq_argp;
131 	struct nfsd_diropres *resp = rqstp->rq_resp;
132 	__be32	nfserr;
133 
134 	dprintk("nfsd: LOOKUP   %s %.*s\n",
135 		SVCFH_fmt(&argp->fh), argp->len, argp->name);
136 
137 	fh_init(&resp->fh, NFS_FHSIZE);
138 	nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
139 				 &resp->fh);
140 
141 	fh_put(&argp->fh);
142 	return nfsd_return_dirop(nfserr, resp);
143 }
144 
145 /*
146  * Read a symlink.
147  */
148 static __be32
149 nfsd_proc_readlink(struct svc_rqst *rqstp)
150 {
151 	struct nfsd_readlinkargs *argp = rqstp->rq_argp;
152 	struct nfsd_readlinkres *resp = rqstp->rq_resp;
153 	__be32	nfserr;
154 
155 	dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh));
156 
157 	/* Read the symlink. */
158 	resp->len = NFS_MAXPATHLEN;
159 	nfserr = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len);
160 
161 	fh_put(&argp->fh);
162 	return nfserr;
163 }
164 
165 /*
166  * Read a portion of a file.
167  * N.B. After this call resp->fh needs an fh_put
168  */
169 static __be32
170 nfsd_proc_read(struct svc_rqst *rqstp)
171 {
172 	struct nfsd_readargs *argp = rqstp->rq_argp;
173 	struct nfsd_readres *resp = rqstp->rq_resp;
174 	__be32	nfserr;
175 	u32 eof;
176 
177 	dprintk("nfsd: READ    %s %d bytes at %d\n",
178 		SVCFH_fmt(&argp->fh),
179 		argp->count, argp->offset);
180 
181 	/* Obtain buffer pointer for payload. 19 is 1 word for
182 	 * status, 17 words for fattr, and 1 word for the byte count.
183 	 */
184 
185 	if (NFSSVC_MAXBLKSIZE_V2 < argp->count) {
186 		char buf[RPC_MAX_ADDRBUFLEN];
187 		printk(KERN_NOTICE
188 			"oversized read request from %s (%d bytes)\n",
189 				svc_print_addr(rqstp, buf, sizeof(buf)),
190 				argp->count);
191 		argp->count = NFSSVC_MAXBLKSIZE_V2;
192 	}
193 	svc_reserve_auth(rqstp, (19<<2) + argp->count + 4);
194 
195 	resp->count = argp->count;
196 	nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
197 				  argp->offset,
198 			   	  rqstp->rq_vec, argp->vlen,
199 				  &resp->count,
200 				  &eof);
201 
202 	if (nfserr) return nfserr;
203 	return fh_getattr(&resp->fh, &resp->stat);
204 }
205 
206 /*
207  * Write data to a file
208  * N.B. After this call resp->fh needs an fh_put
209  */
210 static __be32
211 nfsd_proc_write(struct svc_rqst *rqstp)
212 {
213 	struct nfsd_writeargs *argp = rqstp->rq_argp;
214 	struct nfsd_attrstat *resp = rqstp->rq_resp;
215 	__be32	nfserr;
216 	unsigned long cnt = argp->len;
217 	unsigned int nvecs;
218 
219 	dprintk("nfsd: WRITE    %s %d bytes at %d\n",
220 		SVCFH_fmt(&argp->fh),
221 		argp->len, argp->offset);
222 
223 	nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages,
224 				      &argp->first, cnt);
225 	if (!nvecs)
226 		return nfserr_io;
227 	nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
228 			    argp->offset, rqstp->rq_vec, nvecs,
229 			    &cnt, NFS_DATA_SYNC, NULL);
230 	return nfsd_return_attrs(nfserr, resp);
231 }
232 
233 /*
234  * CREATE processing is complicated. The keyword here is `overloaded.'
235  * The parent directory is kept locked between the check for existence
236  * and the actual create() call in compliance with VFS protocols.
237  * N.B. After this call _both_ argp->fh and resp->fh need an fh_put
238  */
239 static __be32
240 nfsd_proc_create(struct svc_rqst *rqstp)
241 {
242 	struct nfsd_createargs *argp = rqstp->rq_argp;
243 	struct nfsd_diropres *resp = rqstp->rq_resp;
244 	svc_fh		*dirfhp = &argp->fh;
245 	svc_fh		*newfhp = &resp->fh;
246 	struct iattr	*attr = &argp->attrs;
247 	struct inode	*inode;
248 	struct dentry	*dchild;
249 	int		type, mode;
250 	__be32		nfserr;
251 	int		hosterr;
252 	dev_t		rdev = 0, wanted = new_decode_dev(attr->ia_size);
253 
254 	dprintk("nfsd: CREATE   %s %.*s\n",
255 		SVCFH_fmt(dirfhp), argp->len, argp->name);
256 
257 	/* First verify the parent file handle */
258 	nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC);
259 	if (nfserr)
260 		goto done; /* must fh_put dirfhp even on error */
261 
262 	/* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
263 
264 	nfserr = nfserr_exist;
265 	if (isdotent(argp->name, argp->len))
266 		goto done;
267 	hosterr = fh_want_write(dirfhp);
268 	if (hosterr) {
269 		nfserr = nfserrno(hosterr);
270 		goto done;
271 	}
272 
273 	fh_lock_nested(dirfhp, I_MUTEX_PARENT);
274 	dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
275 	if (IS_ERR(dchild)) {
276 		nfserr = nfserrno(PTR_ERR(dchild));
277 		goto out_unlock;
278 	}
279 	fh_init(newfhp, NFS_FHSIZE);
280 	nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
281 	if (!nfserr && d_really_is_negative(dchild))
282 		nfserr = nfserr_noent;
283 	dput(dchild);
284 	if (nfserr) {
285 		if (nfserr != nfserr_noent)
286 			goto out_unlock;
287 		/*
288 		 * If the new file handle wasn't verified, we can't tell
289 		 * whether the file exists or not. Time to bail ...
290 		 */
291 		nfserr = nfserr_acces;
292 		if (!newfhp->fh_dentry) {
293 			printk(KERN_WARNING
294 				"nfsd_proc_create: file handle not verified\n");
295 			goto out_unlock;
296 		}
297 	}
298 
299 	inode = d_inode(newfhp->fh_dentry);
300 
301 	/* Unfudge the mode bits */
302 	if (attr->ia_valid & ATTR_MODE) {
303 		type = attr->ia_mode & S_IFMT;
304 		mode = attr->ia_mode & ~S_IFMT;
305 		if (!type) {
306 			/* no type, so if target exists, assume same as that,
307 			 * else assume a file */
308 			if (inode) {
309 				type = inode->i_mode & S_IFMT;
310 				switch(type) {
311 				case S_IFCHR:
312 				case S_IFBLK:
313 					/* reserve rdev for later checking */
314 					rdev = inode->i_rdev;
315 					attr->ia_valid |= ATTR_SIZE;
316 
317 					/* FALLTHROUGH */
318 				case S_IFIFO:
319 					/* this is probably a permission check..
320 					 * at least IRIX implements perm checking on
321 					 *   echo thing > device-special-file-or-pipe
322 					 * by doing a CREATE with type==0
323 					 */
324 					nfserr = nfsd_permission(rqstp,
325 								 newfhp->fh_export,
326 								 newfhp->fh_dentry,
327 								 NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
328 					if (nfserr && nfserr != nfserr_rofs)
329 						goto out_unlock;
330 				}
331 			} else
332 				type = S_IFREG;
333 		}
334 	} else if (inode) {
335 		type = inode->i_mode & S_IFMT;
336 		mode = inode->i_mode & ~S_IFMT;
337 	} else {
338 		type = S_IFREG;
339 		mode = 0;	/* ??? */
340 	}
341 
342 	attr->ia_valid |= ATTR_MODE;
343 	attr->ia_mode = mode;
344 
345 	/* Special treatment for non-regular files according to the
346 	 * gospel of sun micro
347 	 */
348 	if (type != S_IFREG) {
349 		if (type != S_IFBLK && type != S_IFCHR) {
350 			rdev = 0;
351 		} else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
352 			/* If you think you've seen the worst, grok this. */
353 			type = S_IFIFO;
354 		} else {
355 			/* Okay, char or block special */
356 			if (!rdev)
357 				rdev = wanted;
358 		}
359 
360 		/* we've used the SIZE information, so discard it */
361 		attr->ia_valid &= ~ATTR_SIZE;
362 
363 		/* Make sure the type and device matches */
364 		nfserr = nfserr_exist;
365 		if (inode && type != (inode->i_mode & S_IFMT))
366 			goto out_unlock;
367 	}
368 
369 	nfserr = 0;
370 	if (!inode) {
371 		/* File doesn't exist. Create it and set attrs */
372 		nfserr = nfsd_create_locked(rqstp, dirfhp, argp->name,
373 					argp->len, attr, type, rdev, newfhp);
374 	} else if (type == S_IFREG) {
375 		dprintk("nfsd:   existing %s, valid=%x, size=%ld\n",
376 			argp->name, attr->ia_valid, (long) attr->ia_size);
377 		/* File already exists. We ignore all attributes except
378 		 * size, so that creat() behaves exactly like
379 		 * open(..., O_CREAT|O_TRUNC|O_WRONLY).
380 		 */
381 		attr->ia_valid &= ATTR_SIZE;
382 		if (attr->ia_valid)
383 			nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time64_t)0);
384 	}
385 
386 out_unlock:
387 	/* We don't really need to unlock, as fh_put does it. */
388 	fh_unlock(dirfhp);
389 	fh_drop_write(dirfhp);
390 done:
391 	fh_put(dirfhp);
392 	return nfsd_return_dirop(nfserr, resp);
393 }
394 
395 static __be32
396 nfsd_proc_remove(struct svc_rqst *rqstp)
397 {
398 	struct nfsd_diropargs *argp = rqstp->rq_argp;
399 	__be32	nfserr;
400 
401 	dprintk("nfsd: REMOVE   %s %.*s\n", SVCFH_fmt(&argp->fh),
402 		argp->len, argp->name);
403 
404 	/* Unlink. -SIFDIR means file must not be a directory */
405 	nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
406 	fh_put(&argp->fh);
407 	return nfserr;
408 }
409 
410 static __be32
411 nfsd_proc_rename(struct svc_rqst *rqstp)
412 {
413 	struct nfsd_renameargs *argp = rqstp->rq_argp;
414 	__be32	nfserr;
415 
416 	dprintk("nfsd: RENAME   %s %.*s -> \n",
417 		SVCFH_fmt(&argp->ffh), argp->flen, argp->fname);
418 	dprintk("nfsd:        ->  %s %.*s\n",
419 		SVCFH_fmt(&argp->tfh), argp->tlen, argp->tname);
420 
421 	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
422 				    &argp->tfh, argp->tname, argp->tlen);
423 	fh_put(&argp->ffh);
424 	fh_put(&argp->tfh);
425 	return nfserr;
426 }
427 
428 static __be32
429 nfsd_proc_link(struct svc_rqst *rqstp)
430 {
431 	struct nfsd_linkargs *argp = rqstp->rq_argp;
432 	__be32	nfserr;
433 
434 	dprintk("nfsd: LINK     %s ->\n",
435 		SVCFH_fmt(&argp->ffh));
436 	dprintk("nfsd:    %s %.*s\n",
437 		SVCFH_fmt(&argp->tfh),
438 		argp->tlen,
439 		argp->tname);
440 
441 	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
442 				  &argp->ffh);
443 	fh_put(&argp->ffh);
444 	fh_put(&argp->tfh);
445 	return nfserr;
446 }
447 
448 static __be32
449 nfsd_proc_symlink(struct svc_rqst *rqstp)
450 {
451 	struct nfsd_symlinkargs *argp = rqstp->rq_argp;
452 	struct svc_fh	newfh;
453 	__be32		nfserr;
454 
455 	if (argp->tlen > NFS_MAXPATHLEN)
456 		return nfserr_nametoolong;
457 
458 	argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
459 						page_address(rqstp->rq_arg.pages[0]),
460 						argp->tlen);
461 	if (IS_ERR(argp->tname))
462 		return nfserrno(PTR_ERR(argp->tname));
463 
464 	dprintk("nfsd: SYMLINK  %s %.*s -> %.*s\n",
465 		SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
466 		argp->tlen, argp->tname);
467 
468 	fh_init(&newfh, NFS_FHSIZE);
469 	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
470 						 argp->tname, &newfh);
471 
472 	kfree(argp->tname);
473 	fh_put(&argp->ffh);
474 	fh_put(&newfh);
475 	return nfserr;
476 }
477 
478 /*
479  * Make directory. This operation is not idempotent.
480  * N.B. After this call resp->fh needs an fh_put
481  */
482 static __be32
483 nfsd_proc_mkdir(struct svc_rqst *rqstp)
484 {
485 	struct nfsd_createargs *argp = rqstp->rq_argp;
486 	struct nfsd_diropres *resp = rqstp->rq_resp;
487 	__be32	nfserr;
488 
489 	dprintk("nfsd: MKDIR    %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
490 
491 	if (resp->fh.fh_dentry) {
492 		printk(KERN_WARNING
493 			"nfsd_proc_mkdir: response already verified??\n");
494 	}
495 
496 	argp->attrs.ia_valid &= ~ATTR_SIZE;
497 	fh_init(&resp->fh, NFS_FHSIZE);
498 	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
499 				    &argp->attrs, S_IFDIR, 0, &resp->fh);
500 	fh_put(&argp->fh);
501 	return nfsd_return_dirop(nfserr, resp);
502 }
503 
504 /*
505  * Remove a directory
506  */
507 static __be32
508 nfsd_proc_rmdir(struct svc_rqst *rqstp)
509 {
510 	struct nfsd_diropargs *argp = rqstp->rq_argp;
511 	__be32	nfserr;
512 
513 	dprintk("nfsd: RMDIR    %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
514 
515 	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
516 	fh_put(&argp->fh);
517 	return nfserr;
518 }
519 
520 /*
521  * Read a portion of a directory.
522  */
523 static __be32
524 nfsd_proc_readdir(struct svc_rqst *rqstp)
525 {
526 	struct nfsd_readdirargs *argp = rqstp->rq_argp;
527 	struct nfsd_readdirres *resp = rqstp->rq_resp;
528 	int		count;
529 	__be32		nfserr;
530 	loff_t		offset;
531 
532 	dprintk("nfsd: READDIR  %s %d bytes at %d\n",
533 		SVCFH_fmt(&argp->fh),
534 		argp->count, argp->cookie);
535 
536 	/* Shrink to the client read size */
537 	count = (argp->count >> 2) - 2;
538 
539 	/* Make sure we've room for the NULL ptr & eof flag */
540 	count -= 2;
541 	if (count < 0)
542 		count = 0;
543 
544 	resp->buffer = argp->buffer;
545 	resp->offset = NULL;
546 	resp->buflen = count;
547 	resp->common.err = nfs_ok;
548 	/* Read directory and encode entries on the fly */
549 	offset = argp->cookie;
550 	nfserr = nfsd_readdir(rqstp, &argp->fh, &offset,
551 			      &resp->common, nfssvc_encode_entry);
552 
553 	resp->count = resp->buffer - argp->buffer;
554 	if (resp->offset)
555 		*resp->offset = htonl(offset);
556 
557 	fh_put(&argp->fh);
558 	return nfserr;
559 }
560 
561 /*
562  * Get file system info
563  */
564 static __be32
565 nfsd_proc_statfs(struct svc_rqst *rqstp)
566 {
567 	struct nfsd_fhandle *argp = rqstp->rq_argp;
568 	struct nfsd_statfsres *resp = rqstp->rq_resp;
569 	__be32	nfserr;
570 
571 	dprintk("nfsd: STATFS   %s\n", SVCFH_fmt(&argp->fh));
572 
573 	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
574 			NFSD_MAY_BYPASS_GSS_ON_ROOT);
575 	fh_put(&argp->fh);
576 	return nfserr;
577 }
578 
579 /*
580  * NFSv2 Server procedures.
581  * Only the results of non-idempotent operations are cached.
582  */
583 struct nfsd_void { int dummy; };
584 
585 #define ST 1		/* status */
586 #define FH 8		/* filehandle */
587 #define	AT 18		/* attributes */
588 
589 static const struct svc_procedure nfsd_procedures2[18] = {
590 	[NFSPROC_NULL] = {
591 		.pc_func = nfsd_proc_null,
592 		.pc_decode = nfssvc_decode_void,
593 		.pc_encode = nfssvc_encode_void,
594 		.pc_argsize = sizeof(struct nfsd_void),
595 		.pc_ressize = sizeof(struct nfsd_void),
596 		.pc_cachetype = RC_NOCACHE,
597 		.pc_xdrressize = ST,
598 	},
599 	[NFSPROC_GETATTR] = {
600 		.pc_func = nfsd_proc_getattr,
601 		.pc_decode = nfssvc_decode_fhandle,
602 		.pc_encode = nfssvc_encode_attrstat,
603 		.pc_release = nfssvc_release_fhandle,
604 		.pc_argsize = sizeof(struct nfsd_fhandle),
605 		.pc_ressize = sizeof(struct nfsd_attrstat),
606 		.pc_cachetype = RC_NOCACHE,
607 		.pc_xdrressize = ST+AT,
608 	},
609 	[NFSPROC_SETATTR] = {
610 		.pc_func = nfsd_proc_setattr,
611 		.pc_decode = nfssvc_decode_sattrargs,
612 		.pc_encode = nfssvc_encode_attrstat,
613 		.pc_release = nfssvc_release_fhandle,
614 		.pc_argsize = sizeof(struct nfsd_sattrargs),
615 		.pc_ressize = sizeof(struct nfsd_attrstat),
616 		.pc_cachetype = RC_REPLBUFF,
617 		.pc_xdrressize = ST+AT,
618 	},
619 	[NFSPROC_ROOT] = {
620 		.pc_decode = nfssvc_decode_void,
621 		.pc_encode = nfssvc_encode_void,
622 		.pc_argsize = sizeof(struct nfsd_void),
623 		.pc_ressize = sizeof(struct nfsd_void),
624 		.pc_cachetype = RC_NOCACHE,
625 		.pc_xdrressize = ST,
626 	},
627 	[NFSPROC_LOOKUP] = {
628 		.pc_func = nfsd_proc_lookup,
629 		.pc_decode = nfssvc_decode_diropargs,
630 		.pc_encode = nfssvc_encode_diropres,
631 		.pc_release = nfssvc_release_fhandle,
632 		.pc_argsize = sizeof(struct nfsd_diropargs),
633 		.pc_ressize = sizeof(struct nfsd_diropres),
634 		.pc_cachetype = RC_NOCACHE,
635 		.pc_xdrressize = ST+FH+AT,
636 	},
637 	[NFSPROC_READLINK] = {
638 		.pc_func = nfsd_proc_readlink,
639 		.pc_decode = nfssvc_decode_readlinkargs,
640 		.pc_encode = nfssvc_encode_readlinkres,
641 		.pc_argsize = sizeof(struct nfsd_readlinkargs),
642 		.pc_ressize = sizeof(struct nfsd_readlinkres),
643 		.pc_cachetype = RC_NOCACHE,
644 		.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
645 	},
646 	[NFSPROC_READ] = {
647 		.pc_func = nfsd_proc_read,
648 		.pc_decode = nfssvc_decode_readargs,
649 		.pc_encode = nfssvc_encode_readres,
650 		.pc_release = nfssvc_release_fhandle,
651 		.pc_argsize = sizeof(struct nfsd_readargs),
652 		.pc_ressize = sizeof(struct nfsd_readres),
653 		.pc_cachetype = RC_NOCACHE,
654 		.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
655 	},
656 	[NFSPROC_WRITECACHE] = {
657 		.pc_decode = nfssvc_decode_void,
658 		.pc_encode = nfssvc_encode_void,
659 		.pc_argsize = sizeof(struct nfsd_void),
660 		.pc_ressize = sizeof(struct nfsd_void),
661 		.pc_cachetype = RC_NOCACHE,
662 		.pc_xdrressize = ST,
663 	},
664 	[NFSPROC_WRITE] = {
665 		.pc_func = nfsd_proc_write,
666 		.pc_decode = nfssvc_decode_writeargs,
667 		.pc_encode = nfssvc_encode_attrstat,
668 		.pc_release = nfssvc_release_fhandle,
669 		.pc_argsize = sizeof(struct nfsd_writeargs),
670 		.pc_ressize = sizeof(struct nfsd_attrstat),
671 		.pc_cachetype = RC_REPLBUFF,
672 		.pc_xdrressize = ST+AT,
673 	},
674 	[NFSPROC_CREATE] = {
675 		.pc_func = nfsd_proc_create,
676 		.pc_decode = nfssvc_decode_createargs,
677 		.pc_encode = nfssvc_encode_diropres,
678 		.pc_release = nfssvc_release_fhandle,
679 		.pc_argsize = sizeof(struct nfsd_createargs),
680 		.pc_ressize = sizeof(struct nfsd_diropres),
681 		.pc_cachetype = RC_REPLBUFF,
682 		.pc_xdrressize = ST+FH+AT,
683 	},
684 	[NFSPROC_REMOVE] = {
685 		.pc_func = nfsd_proc_remove,
686 		.pc_decode = nfssvc_decode_diropargs,
687 		.pc_encode = nfssvc_encode_void,
688 		.pc_argsize = sizeof(struct nfsd_diropargs),
689 		.pc_ressize = sizeof(struct nfsd_void),
690 		.pc_cachetype = RC_REPLSTAT,
691 		.pc_xdrressize = ST,
692 	},
693 	[NFSPROC_RENAME] = {
694 		.pc_func = nfsd_proc_rename,
695 		.pc_decode = nfssvc_decode_renameargs,
696 		.pc_encode = nfssvc_encode_void,
697 		.pc_argsize = sizeof(struct nfsd_renameargs),
698 		.pc_ressize = sizeof(struct nfsd_void),
699 		.pc_cachetype = RC_REPLSTAT,
700 		.pc_xdrressize = ST,
701 	},
702 	[NFSPROC_LINK] = {
703 		.pc_func = nfsd_proc_link,
704 		.pc_decode = nfssvc_decode_linkargs,
705 		.pc_encode = nfssvc_encode_void,
706 		.pc_argsize = sizeof(struct nfsd_linkargs),
707 		.pc_ressize = sizeof(struct nfsd_void),
708 		.pc_cachetype = RC_REPLSTAT,
709 		.pc_xdrressize = ST,
710 	},
711 	[NFSPROC_SYMLINK] = {
712 		.pc_func = nfsd_proc_symlink,
713 		.pc_decode = nfssvc_decode_symlinkargs,
714 		.pc_encode = nfssvc_encode_void,
715 		.pc_argsize = sizeof(struct nfsd_symlinkargs),
716 		.pc_ressize = sizeof(struct nfsd_void),
717 		.pc_cachetype = RC_REPLSTAT,
718 		.pc_xdrressize = ST,
719 	},
720 	[NFSPROC_MKDIR] = {
721 		.pc_func = nfsd_proc_mkdir,
722 		.pc_decode = nfssvc_decode_createargs,
723 		.pc_encode = nfssvc_encode_diropres,
724 		.pc_release = nfssvc_release_fhandle,
725 		.pc_argsize = sizeof(struct nfsd_createargs),
726 		.pc_ressize = sizeof(struct nfsd_diropres),
727 		.pc_cachetype = RC_REPLBUFF,
728 		.pc_xdrressize = ST+FH+AT,
729 	},
730 	[NFSPROC_RMDIR] = {
731 		.pc_func = nfsd_proc_rmdir,
732 		.pc_decode = nfssvc_decode_diropargs,
733 		.pc_encode = nfssvc_encode_void,
734 		.pc_argsize = sizeof(struct nfsd_diropargs),
735 		.pc_ressize = sizeof(struct nfsd_void),
736 		.pc_cachetype = RC_REPLSTAT,
737 		.pc_xdrressize = ST,
738 	},
739 	[NFSPROC_READDIR] = {
740 		.pc_func = nfsd_proc_readdir,
741 		.pc_decode = nfssvc_decode_readdirargs,
742 		.pc_encode = nfssvc_encode_readdirres,
743 		.pc_argsize = sizeof(struct nfsd_readdirargs),
744 		.pc_ressize = sizeof(struct nfsd_readdirres),
745 		.pc_cachetype = RC_NOCACHE,
746 	},
747 	[NFSPROC_STATFS] = {
748 		.pc_func = nfsd_proc_statfs,
749 		.pc_decode = nfssvc_decode_fhandle,
750 		.pc_encode = nfssvc_encode_statfsres,
751 		.pc_argsize = sizeof(struct nfsd_fhandle),
752 		.pc_ressize = sizeof(struct nfsd_statfsres),
753 		.pc_cachetype = RC_NOCACHE,
754 		.pc_xdrressize = ST+5,
755 	},
756 };
757 
758 
759 static unsigned int nfsd_count2[ARRAY_SIZE(nfsd_procedures2)];
760 const struct svc_version nfsd_version2 = {
761 	.vs_vers	= 2,
762 	.vs_nproc	= 18,
763 	.vs_proc	= nfsd_procedures2,
764 	.vs_count	= nfsd_count2,
765 	.vs_dispatch	= nfsd_dispatch,
766 	.vs_xdrsize	= NFS2_SVC_XDRSIZE,
767 };
768 
769 /*
770  * Map errnos to NFS errnos.
771  */
772 __be32
773 nfserrno (int errno)
774 {
775 	static struct {
776 		__be32	nfserr;
777 		int	syserr;
778 	} nfs_errtbl[] = {
779 		{ nfs_ok, 0 },
780 		{ nfserr_perm, -EPERM },
781 		{ nfserr_noent, -ENOENT },
782 		{ nfserr_io, -EIO },
783 		{ nfserr_nxio, -ENXIO },
784 		{ nfserr_fbig, -E2BIG },
785 		{ nfserr_acces, -EACCES },
786 		{ nfserr_exist, -EEXIST },
787 		{ nfserr_xdev, -EXDEV },
788 		{ nfserr_mlink, -EMLINK },
789 		{ nfserr_nodev, -ENODEV },
790 		{ nfserr_notdir, -ENOTDIR },
791 		{ nfserr_isdir, -EISDIR },
792 		{ nfserr_inval, -EINVAL },
793 		{ nfserr_fbig, -EFBIG },
794 		{ nfserr_nospc, -ENOSPC },
795 		{ nfserr_rofs, -EROFS },
796 		{ nfserr_mlink, -EMLINK },
797 		{ nfserr_nametoolong, -ENAMETOOLONG },
798 		{ nfserr_notempty, -ENOTEMPTY },
799 #ifdef EDQUOT
800 		{ nfserr_dquot, -EDQUOT },
801 #endif
802 		{ nfserr_stale, -ESTALE },
803 		{ nfserr_jukebox, -ETIMEDOUT },
804 		{ nfserr_jukebox, -ERESTARTSYS },
805 		{ nfserr_jukebox, -EAGAIN },
806 		{ nfserr_jukebox, -EWOULDBLOCK },
807 		{ nfserr_jukebox, -ENOMEM },
808 		{ nfserr_io, -ETXTBSY },
809 		{ nfserr_notsupp, -EOPNOTSUPP },
810 		{ nfserr_toosmall, -ETOOSMALL },
811 		{ nfserr_serverfault, -ESERVERFAULT },
812 		{ nfserr_serverfault, -ENFILE },
813 		{ nfserr_io, -EUCLEAN },
814 		{ nfserr_perm, -ENOKEY },
815 	};
816 	int	i;
817 
818 	for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
819 		if (nfs_errtbl[i].syserr == errno)
820 			return nfs_errtbl[i].nfserr;
821 	}
822 	WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
823 	return nfserr_io;
824 }
825 
826