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