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