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