1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Process version 3 NFS requests. |
4 | * |
5 | * Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de> |
6 | */ |
7 | |
8 | #include <linux/fs.h> |
9 | #include <linux/ext2_fs.h> |
10 | #include <linux/magic.h> |
11 | #include <linux/namei.h> |
12 | |
13 | #include "cache.h" |
14 | #include "xdr3.h" |
15 | #include "vfs.h" |
16 | #include "filecache.h" |
17 | |
18 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
19 | |
20 | static int nfs3_ftypes[] = { |
21 | 0, /* NF3NON */ |
22 | S_IFREG, /* NF3REG */ |
23 | S_IFDIR, /* NF3DIR */ |
24 | S_IFBLK, /* NF3BLK */ |
25 | S_IFCHR, /* NF3CHR */ |
26 | S_IFLNK, /* NF3LNK */ |
27 | S_IFSOCK, /* NF3SOCK */ |
28 | S_IFIFO, /* NF3FIFO */ |
29 | }; |
30 | |
31 | /* |
32 | * NULL call. |
33 | */ |
34 | static __be32 |
35 | nfsd3_proc_null(struct svc_rqst *rqstp) |
36 | { |
37 | return rpc_success; |
38 | } |
39 | |
40 | /* |
41 | * Get a file's attributes |
42 | */ |
43 | static __be32 |
44 | nfsd3_proc_getattr(struct svc_rqst *rqstp) |
45 | { |
46 | struct nfsd_fhandle *argp = rqstp->rq_argp; |
47 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
48 | |
49 | dprintk("nfsd: GETATTR(3) %s\n" , |
50 | SVCFH_fmt(&argp->fh)); |
51 | |
52 | fh_copy(dst: &resp->fh, src: &argp->fh); |
53 | resp->status = fh_verify(rqstp, &resp->fh, 0, |
54 | NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); |
55 | if (resp->status != nfs_ok) |
56 | goto out; |
57 | |
58 | resp->status = fh_getattr(fh: &resp->fh, stat: &resp->stat); |
59 | out: |
60 | return rpc_success; |
61 | } |
62 | |
63 | /* |
64 | * Set a file's attributes |
65 | */ |
66 | static __be32 |
67 | nfsd3_proc_setattr(struct svc_rqst *rqstp) |
68 | { |
69 | struct nfsd3_sattrargs *argp = rqstp->rq_argp; |
70 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
71 | struct nfsd_attrs attrs = { |
72 | .na_iattr = &argp->attrs, |
73 | }; |
74 | const struct timespec64 *guardtime = NULL; |
75 | |
76 | dprintk("nfsd: SETATTR(3) %s\n" , |
77 | SVCFH_fmt(&argp->fh)); |
78 | |
79 | fh_copy(dst: &resp->fh, src: &argp->fh); |
80 | if (argp->check_guard) |
81 | guardtime = &argp->guardtime; |
82 | resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime); |
83 | return rpc_success; |
84 | } |
85 | |
86 | /* |
87 | * Look up a path name component |
88 | */ |
89 | static __be32 |
90 | nfsd3_proc_lookup(struct svc_rqst *rqstp) |
91 | { |
92 | struct nfsd3_diropargs *argp = rqstp->rq_argp; |
93 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
94 | |
95 | dprintk("nfsd: LOOKUP(3) %s %.*s\n" , |
96 | SVCFH_fmt(&argp->fh), |
97 | argp->len, |
98 | argp->name); |
99 | |
100 | fh_copy(dst: &resp->dirfh, src: &argp->fh); |
101 | fh_init(fhp: &resp->fh, NFS3_FHSIZE); |
102 | |
103 | resp->status = nfsd_lookup(rqstp, &resp->dirfh, |
104 | argp->name, argp->len, |
105 | &resp->fh); |
106 | return rpc_success; |
107 | } |
108 | |
109 | /* |
110 | * Check file access |
111 | */ |
112 | static __be32 |
113 | nfsd3_proc_access(struct svc_rqst *rqstp) |
114 | { |
115 | struct nfsd3_accessargs *argp = rqstp->rq_argp; |
116 | struct nfsd3_accessres *resp = rqstp->rq_resp; |
117 | |
118 | dprintk("nfsd: ACCESS(3) %s 0x%x\n" , |
119 | SVCFH_fmt(&argp->fh), |
120 | argp->access); |
121 | |
122 | fh_copy(dst: &resp->fh, src: &argp->fh); |
123 | resp->access = argp->access; |
124 | resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); |
125 | return rpc_success; |
126 | } |
127 | |
128 | /* |
129 | * Read a symlink. |
130 | */ |
131 | static __be32 |
132 | nfsd3_proc_readlink(struct svc_rqst *rqstp) |
133 | { |
134 | struct nfsd_fhandle *argp = rqstp->rq_argp; |
135 | struct nfsd3_readlinkres *resp = rqstp->rq_resp; |
136 | |
137 | dprintk("nfsd: READLINK(3) %s\n" , SVCFH_fmt(&argp->fh)); |
138 | |
139 | /* Read the symlink. */ |
140 | fh_copy(dst: &resp->fh, src: &argp->fh); |
141 | resp->len = NFS3_MAXPATHLEN; |
142 | resp->pages = rqstp->rq_next_page++; |
143 | resp->status = nfsd_readlink(rqstp, &resp->fh, |
144 | page_address(*resp->pages), &resp->len); |
145 | return rpc_success; |
146 | } |
147 | |
148 | /* |
149 | * Read a portion of a file. |
150 | */ |
151 | static __be32 |
152 | nfsd3_proc_read(struct svc_rqst *rqstp) |
153 | { |
154 | struct nfsd3_readargs *argp = rqstp->rq_argp; |
155 | struct nfsd3_readres *resp = rqstp->rq_resp; |
156 | |
157 | dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n" , |
158 | SVCFH_fmt(&argp->fh), |
159 | (unsigned long) argp->count, |
160 | (unsigned long long) argp->offset); |
161 | |
162 | argp->count = min_t(u32, argp->count, svc_max_payload(rqstp)); |
163 | argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen); |
164 | if (argp->offset > (u64)OFFSET_MAX) |
165 | argp->offset = (u64)OFFSET_MAX; |
166 | if (argp->offset + argp->count > (u64)OFFSET_MAX) |
167 | argp->count = (u64)OFFSET_MAX - argp->offset; |
168 | |
169 | resp->pages = rqstp->rq_next_page; |
170 | |
171 | /* Obtain buffer pointer for payload. |
172 | * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof) |
173 | * + 1 (xdr opaque byte count) = 26 |
174 | */ |
175 | resp->count = argp->count; |
176 | svc_reserve_auth(rqstp, space: ((1 + NFS3_POST_OP_ATTR_WORDS + 3) << 2) + |
177 | resp->count + 4); |
178 | |
179 | fh_copy(dst: &resp->fh, src: &argp->fh); |
180 | resp->status = nfsd_read(rqstp, fhp: &resp->fh, offset: argp->offset, |
181 | count: &resp->count, eof: &resp->eof); |
182 | return rpc_success; |
183 | } |
184 | |
185 | /* |
186 | * Write data to a file |
187 | */ |
188 | static __be32 |
189 | nfsd3_proc_write(struct svc_rqst *rqstp) |
190 | { |
191 | struct nfsd3_writeargs *argp = rqstp->rq_argp; |
192 | struct nfsd3_writeres *resp = rqstp->rq_resp; |
193 | unsigned long cnt = argp->len; |
194 | unsigned int nvecs; |
195 | |
196 | dprintk("nfsd: WRITE(3) %s %d bytes at %Lu%s\n" , |
197 | SVCFH_fmt(&argp->fh), |
198 | argp->len, |
199 | (unsigned long long) argp->offset, |
200 | argp->stable ? " stable" : "" ); |
201 | |
202 | resp->status = nfserr_fbig; |
203 | if (argp->offset > (u64)OFFSET_MAX || |
204 | argp->offset + argp->len > (u64)OFFSET_MAX) |
205 | return rpc_success; |
206 | |
207 | fh_copy(dst: &resp->fh, src: &argp->fh); |
208 | resp->committed = argp->stable; |
209 | nvecs = svc_fill_write_vector(rqstp, payload: &argp->payload); |
210 | |
211 | resp->status = nfsd_write(rqstp, &resp->fh, argp->offset, |
212 | rqstp->rq_vec, nvecs, &cnt, |
213 | stable: resp->committed, verf: resp->verf); |
214 | resp->count = cnt; |
215 | return rpc_success; |
216 | } |
217 | |
218 | /* |
219 | * Implement NFSv3's unchecked, guarded, and exclusive CREATE |
220 | * semantics for regular files. Except for the created file, |
221 | * this operation is stateless on the server. |
222 | * |
223 | * Upon return, caller must release @fhp and @resfhp. |
224 | */ |
225 | static __be32 |
226 | nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, |
227 | struct svc_fh *resfhp, struct nfsd3_createargs *argp) |
228 | { |
229 | struct iattr *iap = &argp->attrs; |
230 | struct dentry *parent, *child; |
231 | struct nfsd_attrs attrs = { |
232 | .na_iattr = iap, |
233 | }; |
234 | __u32 v_mtime, v_atime; |
235 | struct inode *inode; |
236 | __be32 status; |
237 | int host_err; |
238 | |
239 | if (isdotent(argp->name, argp->len)) |
240 | return nfserr_exist; |
241 | if (!(iap->ia_valid & ATTR_MODE)) |
242 | iap->ia_mode = 0; |
243 | |
244 | status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); |
245 | if (status != nfs_ok) |
246 | return status; |
247 | |
248 | parent = fhp->fh_dentry; |
249 | inode = d_inode(dentry: parent); |
250 | |
251 | host_err = fh_want_write(fh: fhp); |
252 | if (host_err) |
253 | return nfserrno(errno: host_err); |
254 | |
255 | inode_lock_nested(inode, subclass: I_MUTEX_PARENT); |
256 | |
257 | child = lookup_one_len(argp->name, parent, argp->len); |
258 | if (IS_ERR(ptr: child)) { |
259 | status = nfserrno(errno: PTR_ERR(ptr: child)); |
260 | goto out; |
261 | } |
262 | |
263 | if (d_really_is_negative(dentry: child)) { |
264 | status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); |
265 | if (status != nfs_ok) |
266 | goto out; |
267 | } |
268 | |
269 | status = fh_compose(resfhp, fhp->fh_export, child, fhp); |
270 | if (status != nfs_ok) |
271 | goto out; |
272 | |
273 | v_mtime = 0; |
274 | v_atime = 0; |
275 | if (argp->createmode == NFS3_CREATE_EXCLUSIVE) { |
276 | u32 *verifier = (u32 *)argp->verf; |
277 | |
278 | /* |
279 | * Solaris 7 gets confused (bugid 4218508) if these have |
280 | * the high bit set, as do xfs filesystems without the |
281 | * "bigtime" feature. So just clear the high bits. |
282 | */ |
283 | v_mtime = verifier[0] & 0x7fffffff; |
284 | v_atime = verifier[1] & 0x7fffffff; |
285 | } |
286 | |
287 | if (d_really_is_positive(dentry: child)) { |
288 | status = nfs_ok; |
289 | |
290 | switch (argp->createmode) { |
291 | case NFS3_CREATE_UNCHECKED: |
292 | if (!d_is_reg(dentry: child)) |
293 | break; |
294 | iap->ia_valid &= ATTR_SIZE; |
295 | goto set_attr; |
296 | case NFS3_CREATE_GUARDED: |
297 | status = nfserr_exist; |
298 | break; |
299 | case NFS3_CREATE_EXCLUSIVE: |
300 | if (inode_get_mtime_sec(inode: d_inode(dentry: child)) == v_mtime && |
301 | inode_get_atime_sec(inode: d_inode(dentry: child)) == v_atime && |
302 | d_inode(dentry: child)->i_size == 0) { |
303 | break; |
304 | } |
305 | status = nfserr_exist; |
306 | } |
307 | goto out; |
308 | } |
309 | |
310 | if (!IS_POSIXACL(inode)) |
311 | iap->ia_mode &= ~current_umask(); |
312 | |
313 | status = fh_fill_pre_attrs(fhp); |
314 | if (status != nfs_ok) |
315 | goto out; |
316 | host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true); |
317 | if (host_err < 0) { |
318 | status = nfserrno(errno: host_err); |
319 | goto out; |
320 | } |
321 | fh_fill_post_attrs(fhp); |
322 | |
323 | /* A newly created file already has a file size of zero. */ |
324 | if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) |
325 | iap->ia_valid &= ~ATTR_SIZE; |
326 | if (argp->createmode == NFS3_CREATE_EXCLUSIVE) { |
327 | iap->ia_valid = ATTR_MTIME | ATTR_ATIME | |
328 | ATTR_MTIME_SET | ATTR_ATIME_SET; |
329 | iap->ia_mtime.tv_sec = v_mtime; |
330 | iap->ia_atime.tv_sec = v_atime; |
331 | iap->ia_mtime.tv_nsec = 0; |
332 | iap->ia_atime.tv_nsec = 0; |
333 | } |
334 | |
335 | set_attr: |
336 | status = nfsd_create_setattr(rqstp, fhp, resfhp, iap: &attrs); |
337 | |
338 | out: |
339 | inode_unlock(inode); |
340 | if (child && !IS_ERR(ptr: child)) |
341 | dput(child); |
342 | fh_drop_write(fh: fhp); |
343 | return status; |
344 | } |
345 | |
346 | static __be32 |
347 | nfsd3_proc_create(struct svc_rqst *rqstp) |
348 | { |
349 | struct nfsd3_createargs *argp = rqstp->rq_argp; |
350 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
351 | svc_fh *dirfhp, *newfhp; |
352 | |
353 | dprintk("nfsd: CREATE(3) %s %.*s\n" , |
354 | SVCFH_fmt(&argp->fh), |
355 | argp->len, |
356 | argp->name); |
357 | |
358 | dirfhp = fh_copy(dst: &resp->dirfh, src: &argp->fh); |
359 | newfhp = fh_init(fhp: &resp->fh, NFS3_FHSIZE); |
360 | |
361 | resp->status = nfsd3_create_file(rqstp, fhp: dirfhp, resfhp: newfhp, argp); |
362 | return rpc_success; |
363 | } |
364 | |
365 | /* |
366 | * Make directory. This operation is not idempotent. |
367 | */ |
368 | static __be32 |
369 | nfsd3_proc_mkdir(struct svc_rqst *rqstp) |
370 | { |
371 | struct nfsd3_createargs *argp = rqstp->rq_argp; |
372 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
373 | struct nfsd_attrs attrs = { |
374 | .na_iattr = &argp->attrs, |
375 | }; |
376 | |
377 | dprintk("nfsd: MKDIR(3) %s %.*s\n" , |
378 | SVCFH_fmt(&argp->fh), |
379 | argp->len, |
380 | argp->name); |
381 | |
382 | argp->attrs.ia_valid &= ~ATTR_SIZE; |
383 | fh_copy(dst: &resp->dirfh, src: &argp->fh); |
384 | fh_init(fhp: &resp->fh, NFS3_FHSIZE); |
385 | resp->status = nfsd_create(rqstp, &resp->dirfh, name: argp->name, len: argp->len, |
386 | attrs: &attrs, S_IFDIR, rdev: 0, res: &resp->fh); |
387 | return rpc_success; |
388 | } |
389 | |
390 | static __be32 |
391 | nfsd3_proc_symlink(struct svc_rqst *rqstp) |
392 | { |
393 | struct nfsd3_symlinkargs *argp = rqstp->rq_argp; |
394 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
395 | struct nfsd_attrs attrs = { |
396 | .na_iattr = &argp->attrs, |
397 | }; |
398 | |
399 | if (argp->tlen == 0) { |
400 | resp->status = nfserr_inval; |
401 | goto out; |
402 | } |
403 | if (argp->tlen > NFS3_MAXPATHLEN) { |
404 | resp->status = nfserr_nametoolong; |
405 | goto out; |
406 | } |
407 | |
408 | argp->tname = svc_fill_symlink_pathname(rqstp, first: &argp->first, |
409 | page_address(rqstp->rq_arg.pages[0]), |
410 | total: argp->tlen); |
411 | if (IS_ERR(ptr: argp->tname)) { |
412 | resp->status = nfserrno(errno: PTR_ERR(ptr: argp->tname)); |
413 | goto out; |
414 | } |
415 | |
416 | dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n" , |
417 | SVCFH_fmt(&argp->ffh), |
418 | argp->flen, argp->fname, |
419 | argp->tlen, argp->tname); |
420 | |
421 | fh_copy(dst: &resp->dirfh, src: &argp->ffh); |
422 | fh_init(fhp: &resp->fh, NFS3_FHSIZE); |
423 | resp->status = nfsd_symlink(rqstp, &resp->dirfh, name: argp->fname, |
424 | len: argp->flen, path: argp->tname, attrs: &attrs, res: &resp->fh); |
425 | kfree(objp: argp->tname); |
426 | out: |
427 | return rpc_success; |
428 | } |
429 | |
430 | /* |
431 | * Make socket/fifo/device. |
432 | */ |
433 | static __be32 |
434 | nfsd3_proc_mknod(struct svc_rqst *rqstp) |
435 | { |
436 | struct nfsd3_mknodargs *argp = rqstp->rq_argp; |
437 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
438 | struct nfsd_attrs attrs = { |
439 | .na_iattr = &argp->attrs, |
440 | }; |
441 | int type; |
442 | dev_t rdev = 0; |
443 | |
444 | dprintk("nfsd: MKNOD(3) %s %.*s\n" , |
445 | SVCFH_fmt(&argp->fh), |
446 | argp->len, |
447 | argp->name); |
448 | |
449 | fh_copy(dst: &resp->dirfh, src: &argp->fh); |
450 | fh_init(fhp: &resp->fh, NFS3_FHSIZE); |
451 | |
452 | if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) { |
453 | rdev = MKDEV(argp->major, argp->minor); |
454 | if (MAJOR(rdev) != argp->major || |
455 | MINOR(rdev) != argp->minor) { |
456 | resp->status = nfserr_inval; |
457 | goto out; |
458 | } |
459 | } else if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO) { |
460 | resp->status = nfserr_badtype; |
461 | goto out; |
462 | } |
463 | |
464 | type = nfs3_ftypes[argp->ftype]; |
465 | resp->status = nfsd_create(rqstp, &resp->dirfh, name: argp->name, len: argp->len, |
466 | attrs: &attrs, type, rdev, res: &resp->fh); |
467 | out: |
468 | return rpc_success; |
469 | } |
470 | |
471 | /* |
472 | * Remove file/fifo/socket etc. |
473 | */ |
474 | static __be32 |
475 | nfsd3_proc_remove(struct svc_rqst *rqstp) |
476 | { |
477 | struct nfsd3_diropargs *argp = rqstp->rq_argp; |
478 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
479 | |
480 | dprintk("nfsd: REMOVE(3) %s %.*s\n" , |
481 | SVCFH_fmt(&argp->fh), |
482 | argp->len, |
483 | argp->name); |
484 | |
485 | /* Unlink. -S_IFDIR means file must not be a directory */ |
486 | fh_copy(dst: &resp->fh, src: &argp->fh); |
487 | resp->status = nfsd_unlink(rqstp, &resp->fh, type: -S_IFDIR, |
488 | name: argp->name, len: argp->len); |
489 | return rpc_success; |
490 | } |
491 | |
492 | /* |
493 | * Remove a directory |
494 | */ |
495 | static __be32 |
496 | nfsd3_proc_rmdir(struct svc_rqst *rqstp) |
497 | { |
498 | struct nfsd3_diropargs *argp = rqstp->rq_argp; |
499 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
500 | |
501 | dprintk("nfsd: RMDIR(3) %s %.*s\n" , |
502 | SVCFH_fmt(&argp->fh), |
503 | argp->len, |
504 | argp->name); |
505 | |
506 | fh_copy(dst: &resp->fh, src: &argp->fh); |
507 | resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, |
508 | name: argp->name, len: argp->len); |
509 | return rpc_success; |
510 | } |
511 | |
512 | static __be32 |
513 | nfsd3_proc_rename(struct svc_rqst *rqstp) |
514 | { |
515 | struct nfsd3_renameargs *argp = rqstp->rq_argp; |
516 | struct nfsd3_renameres *resp = rqstp->rq_resp; |
517 | |
518 | dprintk("nfsd: RENAME(3) %s %.*s ->\n" , |
519 | SVCFH_fmt(&argp->ffh), |
520 | argp->flen, |
521 | argp->fname); |
522 | dprintk("nfsd: -> %s %.*s\n" , |
523 | SVCFH_fmt(&argp->tfh), |
524 | argp->tlen, |
525 | argp->tname); |
526 | |
527 | fh_copy(dst: &resp->ffh, src: &argp->ffh); |
528 | fh_copy(dst: &resp->tfh, src: &argp->tfh); |
529 | resp->status = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen, |
530 | &resp->tfh, argp->tname, argp->tlen); |
531 | return rpc_success; |
532 | } |
533 | |
534 | static __be32 |
535 | nfsd3_proc_link(struct svc_rqst *rqstp) |
536 | { |
537 | struct nfsd3_linkargs *argp = rqstp->rq_argp; |
538 | struct nfsd3_linkres *resp = rqstp->rq_resp; |
539 | |
540 | dprintk("nfsd: LINK(3) %s ->\n" , |
541 | SVCFH_fmt(&argp->ffh)); |
542 | dprintk("nfsd: -> %s %.*s\n" , |
543 | SVCFH_fmt(&argp->tfh), |
544 | argp->tlen, |
545 | argp->tname); |
546 | |
547 | fh_copy(dst: &resp->fh, src: &argp->ffh); |
548 | fh_copy(dst: &resp->tfh, src: &argp->tfh); |
549 | resp->status = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen, |
550 | &resp->fh); |
551 | return rpc_success; |
552 | } |
553 | |
554 | static void nfsd3_init_dirlist_pages(struct svc_rqst *rqstp, |
555 | struct nfsd3_readdirres *resp, |
556 | u32 count) |
557 | { |
558 | struct xdr_buf *buf = &resp->dirlist; |
559 | struct xdr_stream *xdr = &resp->xdr; |
560 | unsigned int sendbuf = min_t(unsigned int, rqstp->rq_res.buflen, |
561 | svc_max_payload(rqstp)); |
562 | |
563 | memset(buf, 0, sizeof(*buf)); |
564 | |
565 | /* Reserve room for the NULL ptr & eof flag (-2 words) */ |
566 | buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), sendbuf); |
567 | buf->buflen -= XDR_UNIT * 2; |
568 | buf->pages = rqstp->rq_next_page; |
569 | rqstp->rq_next_page += (buf->buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; |
570 | |
571 | xdr_init_encode_pages(xdr, buf, pages: buf->pages, NULL); |
572 | } |
573 | |
574 | /* |
575 | * Read a portion of a directory. |
576 | */ |
577 | static __be32 |
578 | nfsd3_proc_readdir(struct svc_rqst *rqstp) |
579 | { |
580 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; |
581 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
582 | loff_t offset; |
583 | |
584 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n" , |
585 | SVCFH_fmt(&argp->fh), |
586 | argp->count, (u32) argp->cookie); |
587 | |
588 | nfsd3_init_dirlist_pages(rqstp, resp, count: argp->count); |
589 | |
590 | fh_copy(dst: &resp->fh, src: &argp->fh); |
591 | resp->common.err = nfs_ok; |
592 | resp->cookie_offset = 0; |
593 | resp->rqstp = rqstp; |
594 | offset = argp->cookie; |
595 | resp->status = nfsd_readdir(rqstp, &resp->fh, &offset, |
596 | &resp->common, nfs3svc_encode_entry3); |
597 | memcpy(resp->verf, argp->verf, 8); |
598 | nfs3svc_encode_cookie3(resp, offset); |
599 | |
600 | /* Recycle only pages that were part of the reply */ |
601 | rqstp->rq_next_page = resp->xdr.page_ptr + 1; |
602 | |
603 | return rpc_success; |
604 | } |
605 | |
606 | /* |
607 | * Read a portion of a directory, including file handles and attrs. |
608 | * For now, we choose to ignore the dircount parameter. |
609 | */ |
610 | static __be32 |
611 | nfsd3_proc_readdirplus(struct svc_rqst *rqstp) |
612 | { |
613 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; |
614 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
615 | loff_t offset; |
616 | |
617 | dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n" , |
618 | SVCFH_fmt(&argp->fh), |
619 | argp->count, (u32) argp->cookie); |
620 | |
621 | nfsd3_init_dirlist_pages(rqstp, resp, count: argp->count); |
622 | |
623 | fh_copy(dst: &resp->fh, src: &argp->fh); |
624 | resp->common.err = nfs_ok; |
625 | resp->cookie_offset = 0; |
626 | resp->rqstp = rqstp; |
627 | offset = argp->cookie; |
628 | |
629 | resp->status = fh_verify(rqstp, &resp->fh, S_IFDIR, NFSD_MAY_NOP); |
630 | if (resp->status != nfs_ok) |
631 | goto out; |
632 | |
633 | if (resp->fh.fh_export->ex_flags & NFSEXP_NOREADDIRPLUS) { |
634 | resp->status = nfserr_notsupp; |
635 | goto out; |
636 | } |
637 | |
638 | resp->status = nfsd_readdir(rqstp, &resp->fh, &offset, |
639 | &resp->common, nfs3svc_encode_entryplus3); |
640 | memcpy(resp->verf, argp->verf, 8); |
641 | nfs3svc_encode_cookie3(resp, offset); |
642 | |
643 | /* Recycle only pages that were part of the reply */ |
644 | rqstp->rq_next_page = resp->xdr.page_ptr + 1; |
645 | |
646 | out: |
647 | return rpc_success; |
648 | } |
649 | |
650 | /* |
651 | * Get file system stats |
652 | */ |
653 | static __be32 |
654 | nfsd3_proc_fsstat(struct svc_rqst *rqstp) |
655 | { |
656 | struct nfsd_fhandle *argp = rqstp->rq_argp; |
657 | struct nfsd3_fsstatres *resp = rqstp->rq_resp; |
658 | |
659 | dprintk("nfsd: FSSTAT(3) %s\n" , |
660 | SVCFH_fmt(&argp->fh)); |
661 | |
662 | resp->status = nfsd_statfs(rqstp, &argp->fh, &resp->stats, access: 0); |
663 | fh_put(&argp->fh); |
664 | return rpc_success; |
665 | } |
666 | |
667 | /* |
668 | * Get file system info |
669 | */ |
670 | static __be32 |
671 | nfsd3_proc_fsinfo(struct svc_rqst *rqstp) |
672 | { |
673 | struct nfsd_fhandle *argp = rqstp->rq_argp; |
674 | struct nfsd3_fsinfores *resp = rqstp->rq_resp; |
675 | u32 max_blocksize = svc_max_payload(rqstp); |
676 | |
677 | dprintk("nfsd: FSINFO(3) %s\n" , |
678 | SVCFH_fmt(&argp->fh)); |
679 | |
680 | resp->f_rtmax = max_blocksize; |
681 | resp->f_rtpref = max_blocksize; |
682 | resp->f_rtmult = PAGE_SIZE; |
683 | resp->f_wtmax = max_blocksize; |
684 | resp->f_wtpref = max_blocksize; |
685 | resp->f_wtmult = PAGE_SIZE; |
686 | resp->f_dtpref = max_blocksize; |
687 | resp->f_maxfilesize = ~(u32) 0; |
688 | resp->f_properties = NFS3_FSF_DEFAULT; |
689 | |
690 | resp->status = fh_verify(rqstp, &argp->fh, 0, |
691 | NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); |
692 | |
693 | /* Check special features of the file system. May request |
694 | * different read/write sizes for file systems known to have |
695 | * problems with large blocks */ |
696 | if (resp->status == nfs_ok) { |
697 | struct super_block *sb = argp->fh.fh_dentry->d_sb; |
698 | |
699 | /* Note that we don't care for remote fs's here */ |
700 | if (sb->s_magic == MSDOS_SUPER_MAGIC) { |
701 | resp->f_properties = NFS3_FSF_BILLYBOY; |
702 | } |
703 | resp->f_maxfilesize = sb->s_maxbytes; |
704 | } |
705 | |
706 | fh_put(&argp->fh); |
707 | return rpc_success; |
708 | } |
709 | |
710 | /* |
711 | * Get pathconf info for the specified file |
712 | */ |
713 | static __be32 |
714 | nfsd3_proc_pathconf(struct svc_rqst *rqstp) |
715 | { |
716 | struct nfsd_fhandle *argp = rqstp->rq_argp; |
717 | struct nfsd3_pathconfres *resp = rqstp->rq_resp; |
718 | |
719 | dprintk("nfsd: PATHCONF(3) %s\n" , |
720 | SVCFH_fmt(&argp->fh)); |
721 | |
722 | /* Set default pathconf */ |
723 | resp->p_link_max = 255; /* at least */ |
724 | resp->p_name_max = 255; /* at least */ |
725 | resp->p_no_trunc = 0; |
726 | resp->p_chown_restricted = 1; |
727 | resp->p_case_insensitive = 0; |
728 | resp->p_case_preserving = 1; |
729 | |
730 | resp->status = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); |
731 | |
732 | if (resp->status == nfs_ok) { |
733 | struct super_block *sb = argp->fh.fh_dentry->d_sb; |
734 | |
735 | /* Note that we don't care for remote fs's here */ |
736 | switch (sb->s_magic) { |
737 | case EXT2_SUPER_MAGIC: |
738 | resp->p_link_max = EXT2_LINK_MAX; |
739 | resp->p_name_max = EXT2_NAME_LEN; |
740 | break; |
741 | case MSDOS_SUPER_MAGIC: |
742 | resp->p_case_insensitive = 1; |
743 | resp->p_case_preserving = 0; |
744 | break; |
745 | } |
746 | } |
747 | |
748 | fh_put(&argp->fh); |
749 | return rpc_success; |
750 | } |
751 | |
752 | /* |
753 | * Commit a file (range) to stable storage. |
754 | */ |
755 | static __be32 |
756 | nfsd3_proc_commit(struct svc_rqst *rqstp) |
757 | { |
758 | struct nfsd3_commitargs *argp = rqstp->rq_argp; |
759 | struct nfsd3_commitres *resp = rqstp->rq_resp; |
760 | struct nfsd_file *nf; |
761 | |
762 | dprintk("nfsd: COMMIT(3) %s %u@%Lu\n" , |
763 | SVCFH_fmt(&argp->fh), |
764 | argp->count, |
765 | (unsigned long long) argp->offset); |
766 | |
767 | fh_copy(dst: &resp->fh, src: &argp->fh); |
768 | resp->status = nfsd_file_acquire_gc(rqstp, fhp: &resp->fh, NFSD_MAY_WRITE | |
769 | NFSD_MAY_NOT_BREAK_LEASE, nfp: &nf); |
770 | if (resp->status) |
771 | goto out; |
772 | resp->status = nfsd_commit(rqst: rqstp, fhp: &resp->fh, nf, offset: argp->offset, |
773 | count: argp->count, verf: resp->verf); |
774 | nfsd_file_put(nf); |
775 | out: |
776 | return rpc_success; |
777 | } |
778 | |
779 | |
780 | /* |
781 | * NFSv3 Server procedures. |
782 | * Only the results of non-idempotent operations are cached. |
783 | */ |
784 | #define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat |
785 | #define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat |
786 | #define nfsd3_mkdirargs nfsd3_createargs |
787 | #define nfsd3_readdirplusargs nfsd3_readdirargs |
788 | #define nfsd3_fhandleargs nfsd_fhandle |
789 | #define nfsd3_attrstatres nfsd3_attrstat |
790 | #define nfsd3_wccstatres nfsd3_attrstat |
791 | #define nfsd3_createres nfsd3_diropres |
792 | |
793 | #define ST 1 /* status*/ |
794 | #define FH 17 /* filehandle with length */ |
795 | #define AT 21 /* attributes */ |
796 | #define pAT (1+AT) /* post attributes - conditional */ |
797 | #define WC (7+pAT) /* WCC attributes */ |
798 | |
799 | static const struct svc_procedure nfsd_procedures3[22] = { |
800 | [NFS3PROC_NULL] = { |
801 | .pc_func = nfsd3_proc_null, |
802 | .pc_decode = nfssvc_decode_voidarg, |
803 | .pc_encode = nfssvc_encode_voidres, |
804 | .pc_argsize = sizeof(struct nfsd_voidargs), |
805 | .pc_argzero = sizeof(struct nfsd_voidargs), |
806 | .pc_ressize = sizeof(struct nfsd_voidres), |
807 | .pc_cachetype = RC_NOCACHE, |
808 | .pc_xdrressize = ST, |
809 | .pc_name = "NULL" , |
810 | }, |
811 | [NFS3PROC_GETATTR] = { |
812 | .pc_func = nfsd3_proc_getattr, |
813 | .pc_decode = nfs3svc_decode_fhandleargs, |
814 | .pc_encode = nfs3svc_encode_getattrres, |
815 | .pc_release = nfs3svc_release_fhandle, |
816 | .pc_argsize = sizeof(struct nfsd_fhandle), |
817 | .pc_argzero = sizeof(struct nfsd_fhandle), |
818 | .pc_ressize = sizeof(struct nfsd3_attrstatres), |
819 | .pc_cachetype = RC_NOCACHE, |
820 | .pc_xdrressize = ST+AT, |
821 | .pc_name = "GETATTR" , |
822 | }, |
823 | [NFS3PROC_SETATTR] = { |
824 | .pc_func = nfsd3_proc_setattr, |
825 | .pc_decode = nfs3svc_decode_sattrargs, |
826 | .pc_encode = nfs3svc_encode_wccstatres, |
827 | .pc_release = nfs3svc_release_fhandle, |
828 | .pc_argsize = sizeof(struct nfsd3_sattrargs), |
829 | .pc_argzero = sizeof(struct nfsd3_sattrargs), |
830 | .pc_ressize = sizeof(struct nfsd3_wccstatres), |
831 | .pc_cachetype = RC_REPLBUFF, |
832 | .pc_xdrressize = ST+WC, |
833 | .pc_name = "SETATTR" , |
834 | }, |
835 | [NFS3PROC_LOOKUP] = { |
836 | .pc_func = nfsd3_proc_lookup, |
837 | .pc_decode = nfs3svc_decode_diropargs, |
838 | .pc_encode = nfs3svc_encode_lookupres, |
839 | .pc_release = nfs3svc_release_fhandle2, |
840 | .pc_argsize = sizeof(struct nfsd3_diropargs), |
841 | .pc_argzero = sizeof(struct nfsd3_diropargs), |
842 | .pc_ressize = sizeof(struct nfsd3_diropres), |
843 | .pc_cachetype = RC_NOCACHE, |
844 | .pc_xdrressize = ST+FH+pAT+pAT, |
845 | .pc_name = "LOOKUP" , |
846 | }, |
847 | [NFS3PROC_ACCESS] = { |
848 | .pc_func = nfsd3_proc_access, |
849 | .pc_decode = nfs3svc_decode_accessargs, |
850 | .pc_encode = nfs3svc_encode_accessres, |
851 | .pc_release = nfs3svc_release_fhandle, |
852 | .pc_argsize = sizeof(struct nfsd3_accessargs), |
853 | .pc_argzero = sizeof(struct nfsd3_accessargs), |
854 | .pc_ressize = sizeof(struct nfsd3_accessres), |
855 | .pc_cachetype = RC_NOCACHE, |
856 | .pc_xdrressize = ST+pAT+1, |
857 | .pc_name = "ACCESS" , |
858 | }, |
859 | [NFS3PROC_READLINK] = { |
860 | .pc_func = nfsd3_proc_readlink, |
861 | .pc_decode = nfs3svc_decode_fhandleargs, |
862 | .pc_encode = nfs3svc_encode_readlinkres, |
863 | .pc_release = nfs3svc_release_fhandle, |
864 | .pc_argsize = sizeof(struct nfsd_fhandle), |
865 | .pc_argzero = sizeof(struct nfsd_fhandle), |
866 | .pc_ressize = sizeof(struct nfsd3_readlinkres), |
867 | .pc_cachetype = RC_NOCACHE, |
868 | .pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4, |
869 | .pc_name = "READLINK" , |
870 | }, |
871 | [NFS3PROC_READ] = { |
872 | .pc_func = nfsd3_proc_read, |
873 | .pc_decode = nfs3svc_decode_readargs, |
874 | .pc_encode = nfs3svc_encode_readres, |
875 | .pc_release = nfs3svc_release_fhandle, |
876 | .pc_argsize = sizeof(struct nfsd3_readargs), |
877 | .pc_argzero = sizeof(struct nfsd3_readargs), |
878 | .pc_ressize = sizeof(struct nfsd3_readres), |
879 | .pc_cachetype = RC_NOCACHE, |
880 | .pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4, |
881 | .pc_name = "READ" , |
882 | }, |
883 | [NFS3PROC_WRITE] = { |
884 | .pc_func = nfsd3_proc_write, |
885 | .pc_decode = nfs3svc_decode_writeargs, |
886 | .pc_encode = nfs3svc_encode_writeres, |
887 | .pc_release = nfs3svc_release_fhandle, |
888 | .pc_argsize = sizeof(struct nfsd3_writeargs), |
889 | .pc_argzero = sizeof(struct nfsd3_writeargs), |
890 | .pc_ressize = sizeof(struct nfsd3_writeres), |
891 | .pc_cachetype = RC_REPLBUFF, |
892 | .pc_xdrressize = ST+WC+4, |
893 | .pc_name = "WRITE" , |
894 | }, |
895 | [NFS3PROC_CREATE] = { |
896 | .pc_func = nfsd3_proc_create, |
897 | .pc_decode = nfs3svc_decode_createargs, |
898 | .pc_encode = nfs3svc_encode_createres, |
899 | .pc_release = nfs3svc_release_fhandle2, |
900 | .pc_argsize = sizeof(struct nfsd3_createargs), |
901 | .pc_argzero = sizeof(struct nfsd3_createargs), |
902 | .pc_ressize = sizeof(struct nfsd3_createres), |
903 | .pc_cachetype = RC_REPLBUFF, |
904 | .pc_xdrressize = ST+(1+FH+pAT)+WC, |
905 | .pc_name = "CREATE" , |
906 | }, |
907 | [NFS3PROC_MKDIR] = { |
908 | .pc_func = nfsd3_proc_mkdir, |
909 | .pc_decode = nfs3svc_decode_mkdirargs, |
910 | .pc_encode = nfs3svc_encode_createres, |
911 | .pc_release = nfs3svc_release_fhandle2, |
912 | .pc_argsize = sizeof(struct nfsd3_mkdirargs), |
913 | .pc_argzero = sizeof(struct nfsd3_mkdirargs), |
914 | .pc_ressize = sizeof(struct nfsd3_createres), |
915 | .pc_cachetype = RC_REPLBUFF, |
916 | .pc_xdrressize = ST+(1+FH+pAT)+WC, |
917 | .pc_name = "MKDIR" , |
918 | }, |
919 | [NFS3PROC_SYMLINK] = { |
920 | .pc_func = nfsd3_proc_symlink, |
921 | .pc_decode = nfs3svc_decode_symlinkargs, |
922 | .pc_encode = nfs3svc_encode_createres, |
923 | .pc_release = nfs3svc_release_fhandle2, |
924 | .pc_argsize = sizeof(struct nfsd3_symlinkargs), |
925 | .pc_argzero = sizeof(struct nfsd3_symlinkargs), |
926 | .pc_ressize = sizeof(struct nfsd3_createres), |
927 | .pc_cachetype = RC_REPLBUFF, |
928 | .pc_xdrressize = ST+(1+FH+pAT)+WC, |
929 | .pc_name = "SYMLINK" , |
930 | }, |
931 | [NFS3PROC_MKNOD] = { |
932 | .pc_func = nfsd3_proc_mknod, |
933 | .pc_decode = nfs3svc_decode_mknodargs, |
934 | .pc_encode = nfs3svc_encode_createres, |
935 | .pc_release = nfs3svc_release_fhandle2, |
936 | .pc_argsize = sizeof(struct nfsd3_mknodargs), |
937 | .pc_argzero = sizeof(struct nfsd3_mknodargs), |
938 | .pc_ressize = sizeof(struct nfsd3_createres), |
939 | .pc_cachetype = RC_REPLBUFF, |
940 | .pc_xdrressize = ST+(1+FH+pAT)+WC, |
941 | .pc_name = "MKNOD" , |
942 | }, |
943 | [NFS3PROC_REMOVE] = { |
944 | .pc_func = nfsd3_proc_remove, |
945 | .pc_decode = nfs3svc_decode_diropargs, |
946 | .pc_encode = nfs3svc_encode_wccstatres, |
947 | .pc_release = nfs3svc_release_fhandle, |
948 | .pc_argsize = sizeof(struct nfsd3_diropargs), |
949 | .pc_argzero = sizeof(struct nfsd3_diropargs), |
950 | .pc_ressize = sizeof(struct nfsd3_wccstatres), |
951 | .pc_cachetype = RC_REPLBUFF, |
952 | .pc_xdrressize = ST+WC, |
953 | .pc_name = "REMOVE" , |
954 | }, |
955 | [NFS3PROC_RMDIR] = { |
956 | .pc_func = nfsd3_proc_rmdir, |
957 | .pc_decode = nfs3svc_decode_diropargs, |
958 | .pc_encode = nfs3svc_encode_wccstatres, |
959 | .pc_release = nfs3svc_release_fhandle, |
960 | .pc_argsize = sizeof(struct nfsd3_diropargs), |
961 | .pc_argzero = sizeof(struct nfsd3_diropargs), |
962 | .pc_ressize = sizeof(struct nfsd3_wccstatres), |
963 | .pc_cachetype = RC_REPLBUFF, |
964 | .pc_xdrressize = ST+WC, |
965 | .pc_name = "RMDIR" , |
966 | }, |
967 | [NFS3PROC_RENAME] = { |
968 | .pc_func = nfsd3_proc_rename, |
969 | .pc_decode = nfs3svc_decode_renameargs, |
970 | .pc_encode = nfs3svc_encode_renameres, |
971 | .pc_release = nfs3svc_release_fhandle2, |
972 | .pc_argsize = sizeof(struct nfsd3_renameargs), |
973 | .pc_argzero = sizeof(struct nfsd3_renameargs), |
974 | .pc_ressize = sizeof(struct nfsd3_renameres), |
975 | .pc_cachetype = RC_REPLBUFF, |
976 | .pc_xdrressize = ST+WC+WC, |
977 | .pc_name = "RENAME" , |
978 | }, |
979 | [NFS3PROC_LINK] = { |
980 | .pc_func = nfsd3_proc_link, |
981 | .pc_decode = nfs3svc_decode_linkargs, |
982 | .pc_encode = nfs3svc_encode_linkres, |
983 | .pc_release = nfs3svc_release_fhandle2, |
984 | .pc_argsize = sizeof(struct nfsd3_linkargs), |
985 | .pc_argzero = sizeof(struct nfsd3_linkargs), |
986 | .pc_ressize = sizeof(struct nfsd3_linkres), |
987 | .pc_cachetype = RC_REPLBUFF, |
988 | .pc_xdrressize = ST+pAT+WC, |
989 | .pc_name = "LINK" , |
990 | }, |
991 | [NFS3PROC_READDIR] = { |
992 | .pc_func = nfsd3_proc_readdir, |
993 | .pc_decode = nfs3svc_decode_readdirargs, |
994 | .pc_encode = nfs3svc_encode_readdirres, |
995 | .pc_release = nfs3svc_release_fhandle, |
996 | .pc_argsize = sizeof(struct nfsd3_readdirargs), |
997 | .pc_argzero = sizeof(struct nfsd3_readdirargs), |
998 | .pc_ressize = sizeof(struct nfsd3_readdirres), |
999 | .pc_cachetype = RC_NOCACHE, |
1000 | .pc_name = "READDIR" , |
1001 | }, |
1002 | [NFS3PROC_READDIRPLUS] = { |
1003 | .pc_func = nfsd3_proc_readdirplus, |
1004 | .pc_decode = nfs3svc_decode_readdirplusargs, |
1005 | .pc_encode = nfs3svc_encode_readdirres, |
1006 | .pc_release = nfs3svc_release_fhandle, |
1007 | .pc_argsize = sizeof(struct nfsd3_readdirplusargs), |
1008 | .pc_argzero = sizeof(struct nfsd3_readdirplusargs), |
1009 | .pc_ressize = sizeof(struct nfsd3_readdirres), |
1010 | .pc_cachetype = RC_NOCACHE, |
1011 | .pc_name = "READDIRPLUS" , |
1012 | }, |
1013 | [NFS3PROC_FSSTAT] = { |
1014 | .pc_func = nfsd3_proc_fsstat, |
1015 | .pc_decode = nfs3svc_decode_fhandleargs, |
1016 | .pc_encode = nfs3svc_encode_fsstatres, |
1017 | .pc_argsize = sizeof(struct nfsd3_fhandleargs), |
1018 | .pc_argzero = sizeof(struct nfsd3_fhandleargs), |
1019 | .pc_ressize = sizeof(struct nfsd3_fsstatres), |
1020 | .pc_cachetype = RC_NOCACHE, |
1021 | .pc_xdrressize = ST+pAT+2*6+1, |
1022 | .pc_name = "FSSTAT" , |
1023 | }, |
1024 | [NFS3PROC_FSINFO] = { |
1025 | .pc_func = nfsd3_proc_fsinfo, |
1026 | .pc_decode = nfs3svc_decode_fhandleargs, |
1027 | .pc_encode = nfs3svc_encode_fsinfores, |
1028 | .pc_argsize = sizeof(struct nfsd3_fhandleargs), |
1029 | .pc_argzero = sizeof(struct nfsd3_fhandleargs), |
1030 | .pc_ressize = sizeof(struct nfsd3_fsinfores), |
1031 | .pc_cachetype = RC_NOCACHE, |
1032 | .pc_xdrressize = ST+pAT+12, |
1033 | .pc_name = "FSINFO" , |
1034 | }, |
1035 | [NFS3PROC_PATHCONF] = { |
1036 | .pc_func = nfsd3_proc_pathconf, |
1037 | .pc_decode = nfs3svc_decode_fhandleargs, |
1038 | .pc_encode = nfs3svc_encode_pathconfres, |
1039 | .pc_argsize = sizeof(struct nfsd3_fhandleargs), |
1040 | .pc_argzero = sizeof(struct nfsd3_fhandleargs), |
1041 | .pc_ressize = sizeof(struct nfsd3_pathconfres), |
1042 | .pc_cachetype = RC_NOCACHE, |
1043 | .pc_xdrressize = ST+pAT+6, |
1044 | .pc_name = "PATHCONF" , |
1045 | }, |
1046 | [NFS3PROC_COMMIT] = { |
1047 | .pc_func = nfsd3_proc_commit, |
1048 | .pc_decode = nfs3svc_decode_commitargs, |
1049 | .pc_encode = nfs3svc_encode_commitres, |
1050 | .pc_release = nfs3svc_release_fhandle, |
1051 | .pc_argsize = sizeof(struct nfsd3_commitargs), |
1052 | .pc_argzero = sizeof(struct nfsd3_commitargs), |
1053 | .pc_ressize = sizeof(struct nfsd3_commitres), |
1054 | .pc_cachetype = RC_NOCACHE, |
1055 | .pc_xdrressize = ST+WC+2, |
1056 | .pc_name = "COMMIT" , |
1057 | }, |
1058 | }; |
1059 | |
1060 | static DEFINE_PER_CPU_ALIGNED(unsigned long, |
1061 | nfsd_count3[ARRAY_SIZE(nfsd_procedures3)]); |
1062 | const struct svc_version nfsd_version3 = { |
1063 | .vs_vers = 3, |
1064 | .vs_nproc = ARRAY_SIZE(nfsd_procedures3), |
1065 | .vs_proc = nfsd_procedures3, |
1066 | .vs_dispatch = nfsd_dispatch, |
1067 | .vs_count = nfsd_count3, |
1068 | .vs_xdrsize = NFS3_SVC_XDRSIZE, |
1069 | }; |
1070 | |