diff options
author | Christoph Hellwig <hch@infradead.org> | 2013-11-18 08:07:30 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2013-11-18 12:06:47 -0500 |
commit | 818e5a22e907fbae75e9c1fd78233baec9fa64b6 (patch) | |
tree | ef16a881deb91089522a7dba9d658bed01524226 /fs/nfsd/vfs.c | |
parent | 2d3c627502f2a9b0a7de06a5a2df2365542a72c9 (diff) |
nfsd: split up nfsd_setattr
Split out two helpers to make the code more readable and easier to verify
for correctness.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: stable@vger.kernel.org
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 144 |
1 files changed, 84 insertions, 60 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 94b5f5d2bfed..c3f57cf14a63 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp) | |||
298 | } | 298 | } |
299 | 299 | ||
300 | /* | 300 | /* |
301 | * Set various file attributes. | 301 | * Go over the attributes and take care of the small differences between |
302 | * N.B. After this call fhp needs an fh_put | 302 | * NFS semantics and what Linux expects. |
303 | */ | 303 | */ |
304 | __be32 | 304 | static void |
305 | nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | 305 | nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) |
306 | int check_guard, time_t guardtime) | ||
307 | { | 306 | { |
308 | struct dentry *dentry; | ||
309 | struct inode *inode; | ||
310 | int accmode = NFSD_MAY_SATTR; | ||
311 | umode_t ftype = 0; | ||
312 | __be32 err; | ||
313 | int host_err; | ||
314 | int size_change = 0; | ||
315 | |||
316 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) | ||
317 | accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; | ||
318 | if (iap->ia_valid & ATTR_SIZE) | ||
319 | ftype = S_IFREG; | ||
320 | |||
321 | /* Get inode */ | ||
322 | err = fh_verify(rqstp, fhp, ftype, accmode); | ||
323 | if (err) | ||
324 | goto out; | ||
325 | |||
326 | dentry = fhp->fh_dentry; | ||
327 | inode = dentry->d_inode; | ||
328 | |||
329 | /* Ignore any mode updates on symlinks */ | ||
330 | if (S_ISLNK(inode->i_mode)) | ||
331 | iap->ia_valid &= ~ATTR_MODE; | ||
332 | |||
333 | if (!iap->ia_valid) | ||
334 | goto out; | ||
335 | |||
336 | /* | 307 | /* |
337 | * NFSv2 does not differentiate between "set-[ac]time-to-now" | 308 | * NFSv2 does not differentiate between "set-[ac]time-to-now" |
338 | * which only requires access, and "set-[ac]time-to-X" which | 309 | * which only requires access, and "set-[ac]time-to-X" which |
@@ -342,8 +313,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | |||
342 | * convert to "set to now" instead of "set to explicit time" | 313 | * convert to "set to now" instead of "set to explicit time" |
343 | * | 314 | * |
344 | * We only call inode_change_ok as the last test as technically | 315 | * We only call inode_change_ok as the last test as technically |
345 | * it is not an interface that we should be using. It is only | 316 | * it is not an interface that we should be using. |
346 | * valid if the filesystem does not define it's own i_op->setattr. | ||
347 | */ | 317 | */ |
348 | #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) | 318 | #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) |
349 | #define MAX_TOUCH_TIME_ERROR (30*60) | 319 | #define MAX_TOUCH_TIME_ERROR (30*60) |
@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | |||
369 | iap->ia_valid &= ~BOTH_TIME_SET; | 339 | iap->ia_valid &= ~BOTH_TIME_SET; |
370 | } | 340 | } |
371 | } | 341 | } |
372 | |||
373 | /* | ||
374 | * The size case is special. | ||
375 | * It changes the file as well as the attributes. | ||
376 | */ | ||
377 | if (iap->ia_valid & ATTR_SIZE) { | ||
378 | if (iap->ia_size < inode->i_size) { | ||
379 | err = nfsd_permission(rqstp, fhp->fh_export, dentry, | ||
380 | NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE); | ||
381 | if (err) | ||
382 | goto out; | ||
383 | } | ||
384 | |||
385 | host_err = get_write_access(inode); | ||
386 | if (host_err) | ||
387 | goto out_nfserr; | ||
388 | |||
389 | size_change = 1; | ||
390 | host_err = locks_verify_truncate(inode, NULL, iap->ia_size); | ||
391 | if (host_err) { | ||
392 | put_write_access(inode); | ||
393 | goto out_nfserr; | ||
394 | } | ||
395 | } | ||
396 | 342 | ||
397 | /* sanitize the mode change */ | 343 | /* sanitize the mode change */ |
398 | if (iap->ia_valid & ATTR_MODE) { | 344 | if (iap->ia_valid & ATTR_MODE) { |
@@ -415,8 +361,86 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | |||
415 | iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); | 361 | iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); |
416 | } | 362 | } |
417 | } | 363 | } |
364 | } | ||
418 | 365 | ||
419 | /* Change the attributes. */ | 366 | static __be32 |
367 | nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
368 | struct iattr *iap) | ||
369 | { | ||
370 | struct inode *inode = fhp->fh_dentry->d_inode; | ||
371 | int host_err; | ||
372 | |||
373 | if (iap->ia_size < inode->i_size) { | ||
374 | __be32 err; | ||
375 | |||
376 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, | ||
377 | NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); | ||
378 | if (err) | ||
379 | return err; | ||
380 | } | ||
381 | |||
382 | host_err = get_write_access(inode); | ||
383 | if (host_err) | ||
384 | goto out_nfserrno; | ||
385 | |||
386 | host_err = locks_verify_truncate(inode, NULL, iap->ia_size); | ||
387 | if (host_err) | ||
388 | goto out_put_write_access; | ||
389 | return 0; | ||
390 | |||
391 | out_put_write_access: | ||
392 | put_write_access(inode); | ||
393 | out_nfserrno: | ||
394 | return nfserrno(host_err); | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * Set various file attributes. After this call fhp needs an fh_put. | ||
399 | */ | ||
400 | __be32 | ||
401 | nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | ||
402 | int check_guard, time_t guardtime) | ||
403 | { | ||
404 | struct dentry *dentry; | ||
405 | struct inode *inode; | ||
406 | int accmode = NFSD_MAY_SATTR; | ||
407 | umode_t ftype = 0; | ||
408 | __be32 err; | ||
409 | int host_err; | ||
410 | int size_change = 0; | ||
411 | |||
412 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) | ||
413 | accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; | ||
414 | if (iap->ia_valid & ATTR_SIZE) | ||
415 | ftype = S_IFREG; | ||
416 | |||
417 | /* Get inode */ | ||
418 | err = fh_verify(rqstp, fhp, ftype, accmode); | ||
419 | if (err) | ||
420 | goto out; | ||
421 | |||
422 | dentry = fhp->fh_dentry; | ||
423 | inode = dentry->d_inode; | ||
424 | |||
425 | /* Ignore any mode updates on symlinks */ | ||
426 | if (S_ISLNK(inode->i_mode)) | ||
427 | iap->ia_valid &= ~ATTR_MODE; | ||
428 | |||
429 | if (!iap->ia_valid) | ||
430 | goto out; | ||
431 | |||
432 | nfsd_sanitize_attrs(inode, iap); | ||
433 | |||
434 | /* | ||
435 | * The size case is special, it changes the file in addition to the | ||
436 | * attributes. | ||
437 | */ | ||
438 | if (iap->ia_valid & ATTR_SIZE) { | ||
439 | err = nfsd_get_write_access(rqstp, fhp, iap); | ||
440 | if (err) | ||
441 | goto out; | ||
442 | size_change = 1; | ||
443 | } | ||
420 | 444 | ||
421 | iap->ia_valid |= ATTR_CTIME; | 445 | iap->ia_valid |= ATTR_CTIME; |
422 | 446 | ||