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