diff options
author | Christoph Hellwig <hch@lst.de> | 2009-01-18 20:02:57 -0500 |
---|---|---|
committer | Lachlan McIlroy <lachlan@sgi.com> | 2009-01-18 22:43:18 -0500 |
commit | ab596ad8972f314ace538799734c7e1bdd1da2ff (patch) | |
tree | 40c35f802be9307a4739ff3b22260d3a0788c2f0 /fs/xfs/linux-2.6/xfs_ioctl32.c | |
parent | f3b8436ad9a8ad36b3c9fa1fe030c7f38e5d3d0b (diff) |
xfs: fix dentry aliasing issues in open_by_handle
Open by handle just grabs an inode by handle and then creates itself
a dentry for it. While this works for regular files it is horribly
broken for directories, where the VFS locking relies on the fact that
there is only just one single dentry for a given inode, and that
these are always connected to the root of the filesystem so that
it's locking algorithms work (see Documentations/filesystems/Locking)
Remove all the existing open by handle code and replace it with a small
wrapper around the exportfs code which deals with all these issues.
At the same time we also make the checks for a valid handle strict
enough to reject all not perfectly well formed handles - given that
we never hand out others that's okay and simplifies the code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_ioctl32.c')
-rw-r--r-- | fs/xfs/linux-2.6/xfs_ioctl32.c | 175 |
1 files changed, 49 insertions, 126 deletions
diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 50903ad3182e..fd4362063f25 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c | |||
@@ -340,96 +340,24 @@ xfs_compat_handlereq_copyin( | |||
340 | return 0; | 340 | return 0; |
341 | } | 341 | } |
342 | 342 | ||
343 | /* | 343 | STATIC struct dentry * |
344 | * Convert userspace handle data into inode. | 344 | xfs_compat_handlereq_to_dentry( |
345 | * | 345 | struct file *parfilp, |
346 | * We use the fact that all the fsop_handlereq ioctl calls have a data | 346 | compat_xfs_fsop_handlereq_t *hreq) |
347 | * structure argument whose first component is always a xfs_fsop_handlereq_t, | ||
348 | * so we can pass that sub structure into this handy, shared routine. | ||
349 | * | ||
350 | * If no error, caller must always iput the returned inode. | ||
351 | */ | ||
352 | STATIC int | ||
353 | xfs_vget_fsop_handlereq_compat( | ||
354 | xfs_mount_t *mp, | ||
355 | struct inode *parinode, /* parent inode pointer */ | ||
356 | compat_xfs_fsop_handlereq_t *hreq, | ||
357 | struct inode **inode) | ||
358 | { | 347 | { |
359 | void __user *hanp; | 348 | return xfs_handle_to_dentry(parfilp, |
360 | size_t hlen; | 349 | compat_ptr(hreq->ihandle), hreq->ihandlen); |
361 | xfs_fid_t *xfid; | ||
362 | xfs_handle_t *handlep; | ||
363 | xfs_handle_t handle; | ||
364 | xfs_inode_t *ip; | ||
365 | xfs_ino_t ino; | ||
366 | __u32 igen; | ||
367 | int error; | ||
368 | |||
369 | /* | ||
370 | * Only allow handle opens under a directory. | ||
371 | */ | ||
372 | if (!S_ISDIR(parinode->i_mode)) | ||
373 | return XFS_ERROR(ENOTDIR); | ||
374 | |||
375 | hanp = compat_ptr(hreq->ihandle); | ||
376 | hlen = hreq->ihandlen; | ||
377 | handlep = &handle; | ||
378 | |||
379 | if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep)) | ||
380 | return XFS_ERROR(EINVAL); | ||
381 | if (copy_from_user(handlep, hanp, hlen)) | ||
382 | return XFS_ERROR(EFAULT); | ||
383 | if (hlen < sizeof(*handlep)) | ||
384 | memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen); | ||
385 | if (hlen > sizeof(handlep->ha_fsid)) { | ||
386 | if (handlep->ha_fid.fid_len != | ||
387 | (hlen - sizeof(handlep->ha_fsid) - | ||
388 | sizeof(handlep->ha_fid.fid_len)) || | ||
389 | handlep->ha_fid.fid_pad) | ||
390 | return XFS_ERROR(EINVAL); | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * Crack the handle, obtain the inode # & generation # | ||
395 | */ | ||
396 | xfid = (struct xfs_fid *)&handlep->ha_fid; | ||
397 | if (xfid->fid_len == sizeof(*xfid) - sizeof(xfid->fid_len)) { | ||
398 | ino = xfid->fid_ino; | ||
399 | igen = xfid->fid_gen; | ||
400 | } else { | ||
401 | return XFS_ERROR(EINVAL); | ||
402 | } | ||
403 | |||
404 | /* | ||
405 | * Get the XFS inode, building a Linux inode to go with it. | ||
406 | */ | ||
407 | error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0); | ||
408 | if (error) | ||
409 | return error; | ||
410 | if (ip == NULL) | ||
411 | return XFS_ERROR(EIO); | ||
412 | if (ip->i_d.di_gen != igen) { | ||
413 | xfs_iput_new(ip, XFS_ILOCK_SHARED); | ||
414 | return XFS_ERROR(ENOENT); | ||
415 | } | ||
416 | |||
417 | xfs_iunlock(ip, XFS_ILOCK_SHARED); | ||
418 | |||
419 | *inode = VFS_I(ip); | ||
420 | return 0; | ||
421 | } | 350 | } |
422 | 351 | ||
423 | STATIC int | 352 | STATIC int |
424 | xfs_compat_attrlist_by_handle( | 353 | xfs_compat_attrlist_by_handle( |
425 | xfs_mount_t *mp, | 354 | struct file *parfilp, |
426 | void __user *arg, | 355 | void __user *arg) |
427 | struct inode *parinode) | ||
428 | { | 356 | { |
429 | int error; | 357 | int error; |
430 | attrlist_cursor_kern_t *cursor; | 358 | attrlist_cursor_kern_t *cursor; |
431 | compat_xfs_fsop_attrlist_handlereq_t al_hreq; | 359 | compat_xfs_fsop_attrlist_handlereq_t al_hreq; |
432 | struct inode *inode; | 360 | struct dentry *dentry; |
433 | char *kbuf; | 361 | char *kbuf; |
434 | 362 | ||
435 | if (!capable(CAP_SYS_ADMIN)) | 363 | if (!capable(CAP_SYS_ADMIN)) |
@@ -446,17 +374,17 @@ xfs_compat_attrlist_by_handle( | |||
446 | if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE)) | 374 | if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE)) |
447 | return -XFS_ERROR(EINVAL); | 375 | return -XFS_ERROR(EINVAL); |
448 | 376 | ||
449 | error = xfs_vget_fsop_handlereq_compat(mp, parinode, &al_hreq.hreq, | 377 | dentry = xfs_compat_handlereq_to_dentry(parfilp, &al_hreq.hreq); |
450 | &inode); | 378 | if (IS_ERR(dentry)) |
451 | if (error) | 379 | return PTR_ERR(dentry); |
452 | goto out; | ||
453 | 380 | ||
381 | error = -ENOMEM; | ||
454 | kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL); | 382 | kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL); |
455 | if (!kbuf) | 383 | if (!kbuf) |
456 | goto out_vn_rele; | 384 | goto out_dput; |
457 | 385 | ||
458 | cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; | 386 | cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; |
459 | error = xfs_attr_list(XFS_I(inode), kbuf, al_hreq.buflen, | 387 | error = -xfs_attr_list(XFS_I(dentry->d_inode), kbuf, al_hreq.buflen, |
460 | al_hreq.flags, cursor); | 388 | al_hreq.flags, cursor); |
461 | if (error) | 389 | if (error) |
462 | goto out_kfree; | 390 | goto out_kfree; |
@@ -466,22 +394,20 @@ xfs_compat_attrlist_by_handle( | |||
466 | 394 | ||
467 | out_kfree: | 395 | out_kfree: |
468 | kfree(kbuf); | 396 | kfree(kbuf); |
469 | out_vn_rele: | 397 | out_dput: |
470 | iput(inode); | 398 | dput(dentry); |
471 | out: | 399 | return error; |
472 | return -error; | ||
473 | } | 400 | } |
474 | 401 | ||
475 | STATIC int | 402 | STATIC int |
476 | xfs_compat_attrmulti_by_handle( | 403 | xfs_compat_attrmulti_by_handle( |
477 | xfs_mount_t *mp, | 404 | struct file *parfilp, |
478 | void __user *arg, | 405 | void __user *arg) |
479 | struct inode *parinode) | ||
480 | { | 406 | { |
481 | int error; | 407 | int error; |
482 | compat_xfs_attr_multiop_t *ops; | 408 | compat_xfs_attr_multiop_t *ops; |
483 | compat_xfs_fsop_attrmulti_handlereq_t am_hreq; | 409 | compat_xfs_fsop_attrmulti_handlereq_t am_hreq; |
484 | struct inode *inode; | 410 | struct dentry *dentry; |
485 | unsigned int i, size; | 411 | unsigned int i, size; |
486 | char *attr_name; | 412 | char *attr_name; |
487 | 413 | ||
@@ -491,20 +417,19 @@ xfs_compat_attrmulti_by_handle( | |||
491 | sizeof(compat_xfs_fsop_attrmulti_handlereq_t))) | 417 | sizeof(compat_xfs_fsop_attrmulti_handlereq_t))) |
492 | return -XFS_ERROR(EFAULT); | 418 | return -XFS_ERROR(EFAULT); |
493 | 419 | ||
494 | error = xfs_vget_fsop_handlereq_compat(mp, parinode, &am_hreq.hreq, | 420 | dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq); |
495 | &inode); | 421 | if (IS_ERR(dentry)) |
496 | if (error) | 422 | return PTR_ERR(dentry); |
497 | goto out; | ||
498 | 423 | ||
499 | error = E2BIG; | 424 | error = E2BIG; |
500 | size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t); | 425 | size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t); |
501 | if (!size || size > 16 * PAGE_SIZE) | 426 | if (!size || size > 16 * PAGE_SIZE) |
502 | goto out_vn_rele; | 427 | goto out_dput; |
503 | 428 | ||
504 | error = ENOMEM; | 429 | error = ENOMEM; |
505 | ops = kmalloc(size, GFP_KERNEL); | 430 | ops = kmalloc(size, GFP_KERNEL); |
506 | if (!ops) | 431 | if (!ops) |
507 | goto out_vn_rele; | 432 | goto out_dput; |
508 | 433 | ||
509 | error = EFAULT; | 434 | error = EFAULT; |
510 | if (copy_from_user(ops, compat_ptr(am_hreq.ops), size)) | 435 | if (copy_from_user(ops, compat_ptr(am_hreq.ops), size)) |
@@ -527,20 +452,21 @@ xfs_compat_attrmulti_by_handle( | |||
527 | 452 | ||
528 | switch (ops[i].am_opcode) { | 453 | switch (ops[i].am_opcode) { |
529 | case ATTR_OP_GET: | 454 | case ATTR_OP_GET: |
530 | ops[i].am_error = xfs_attrmulti_attr_get(inode, | 455 | ops[i].am_error = xfs_attrmulti_attr_get( |
531 | attr_name, | 456 | dentry->d_inode, attr_name, |
532 | compat_ptr(ops[i].am_attrvalue), | 457 | compat_ptr(ops[i].am_attrvalue), |
533 | &ops[i].am_length, ops[i].am_flags); | 458 | &ops[i].am_length, ops[i].am_flags); |
534 | break; | 459 | break; |
535 | case ATTR_OP_SET: | 460 | case ATTR_OP_SET: |
536 | ops[i].am_error = xfs_attrmulti_attr_set(inode, | 461 | ops[i].am_error = xfs_attrmulti_attr_set( |
537 | attr_name, | 462 | dentry->d_inode, attr_name, |
538 | compat_ptr(ops[i].am_attrvalue), | 463 | compat_ptr(ops[i].am_attrvalue), |
539 | ops[i].am_length, ops[i].am_flags); | 464 | ops[i].am_length, ops[i].am_flags); |
540 | break; | 465 | break; |
541 | case ATTR_OP_REMOVE: | 466 | case ATTR_OP_REMOVE: |
542 | ops[i].am_error = xfs_attrmulti_attr_remove(inode, | 467 | ops[i].am_error = xfs_attrmulti_attr_remove( |
543 | attr_name, ops[i].am_flags); | 468 | dentry->d_inode, attr_name, |
469 | ops[i].am_flags); | ||
544 | break; | 470 | break; |
545 | default: | 471 | default: |
546 | ops[i].am_error = EINVAL; | 472 | ops[i].am_error = EINVAL; |
@@ -553,22 +479,20 @@ xfs_compat_attrmulti_by_handle( | |||
553 | kfree(attr_name); | 479 | kfree(attr_name); |
554 | out_kfree_ops: | 480 | out_kfree_ops: |
555 | kfree(ops); | 481 | kfree(ops); |
556 | out_vn_rele: | 482 | out_dput: |
557 | iput(inode); | 483 | dput(dentry); |
558 | out: | ||
559 | return -error; | 484 | return -error; |
560 | } | 485 | } |
561 | 486 | ||
562 | STATIC int | 487 | STATIC int |
563 | xfs_compat_fssetdm_by_handle( | 488 | xfs_compat_fssetdm_by_handle( |
564 | xfs_mount_t *mp, | 489 | struct file *parfilp, |
565 | void __user *arg, | 490 | void __user *arg) |
566 | struct inode *parinode) | ||
567 | { | 491 | { |
568 | int error; | 492 | int error; |
569 | struct fsdmidata fsd; | 493 | struct fsdmidata fsd; |
570 | compat_xfs_fsop_setdm_handlereq_t dmhreq; | 494 | compat_xfs_fsop_setdm_handlereq_t dmhreq; |
571 | struct inode *inode; | 495 | struct dentry *dentry; |
572 | 496 | ||
573 | if (!capable(CAP_MKNOD)) | 497 | if (!capable(CAP_MKNOD)) |
574 | return -XFS_ERROR(EPERM); | 498 | return -XFS_ERROR(EPERM); |
@@ -576,12 +500,11 @@ xfs_compat_fssetdm_by_handle( | |||
576 | sizeof(compat_xfs_fsop_setdm_handlereq_t))) | 500 | sizeof(compat_xfs_fsop_setdm_handlereq_t))) |
577 | return -XFS_ERROR(EFAULT); | 501 | return -XFS_ERROR(EFAULT); |
578 | 502 | ||
579 | error = xfs_vget_fsop_handlereq_compat(mp, parinode, &dmhreq.hreq, | 503 | dentry = xfs_compat_handlereq_to_dentry(parfilp, &dmhreq.hreq); |
580 | &inode); | 504 | if (IS_ERR(dentry)) |
581 | if (error) | 505 | return PTR_ERR(dentry); |
582 | return -error; | ||
583 | 506 | ||
584 | if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { | 507 | if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode)) { |
585 | error = -XFS_ERROR(EPERM); | 508 | error = -XFS_ERROR(EPERM); |
586 | goto out; | 509 | goto out; |
587 | } | 510 | } |
@@ -591,11 +514,11 @@ xfs_compat_fssetdm_by_handle( | |||
591 | goto out; | 514 | goto out; |
592 | } | 515 | } |
593 | 516 | ||
594 | error = -xfs_set_dmattrs(XFS_I(inode), fsd.fsd_dmevmask, | 517 | error = -xfs_set_dmattrs(XFS_I(dentry->d_inode), fsd.fsd_dmevmask, |
595 | fsd.fsd_dmstate); | 518 | fsd.fsd_dmstate); |
596 | 519 | ||
597 | out: | 520 | out: |
598 | iput(inode); | 521 | dput(dentry); |
599 | return error; | 522 | return error; |
600 | } | 523 | } |
601 | 524 | ||
@@ -722,21 +645,21 @@ xfs_file_compat_ioctl( | |||
722 | 645 | ||
723 | if (xfs_compat_handlereq_copyin(&hreq, arg)) | 646 | if (xfs_compat_handlereq_copyin(&hreq, arg)) |
724 | return -XFS_ERROR(EFAULT); | 647 | return -XFS_ERROR(EFAULT); |
725 | return xfs_open_by_handle(mp, &hreq, filp, inode); | 648 | return xfs_open_by_handle(filp, &hreq); |
726 | } | 649 | } |
727 | case XFS_IOC_READLINK_BY_HANDLE_32: { | 650 | case XFS_IOC_READLINK_BY_HANDLE_32: { |
728 | struct xfs_fsop_handlereq hreq; | 651 | struct xfs_fsop_handlereq hreq; |
729 | 652 | ||
730 | if (xfs_compat_handlereq_copyin(&hreq, arg)) | 653 | if (xfs_compat_handlereq_copyin(&hreq, arg)) |
731 | return -XFS_ERROR(EFAULT); | 654 | return -XFS_ERROR(EFAULT); |
732 | return xfs_readlink_by_handle(mp, &hreq, inode); | 655 | return xfs_readlink_by_handle(filp, &hreq); |
733 | } | 656 | } |
734 | case XFS_IOC_ATTRLIST_BY_HANDLE_32: | 657 | case XFS_IOC_ATTRLIST_BY_HANDLE_32: |
735 | return xfs_compat_attrlist_by_handle(mp, arg, inode); | 658 | return xfs_compat_attrlist_by_handle(filp, arg); |
736 | case XFS_IOC_ATTRMULTI_BY_HANDLE_32: | 659 | case XFS_IOC_ATTRMULTI_BY_HANDLE_32: |
737 | return xfs_compat_attrmulti_by_handle(mp, arg, inode); | 660 | return xfs_compat_attrmulti_by_handle(filp, arg); |
738 | case XFS_IOC_FSSETDM_BY_HANDLE_32: | 661 | case XFS_IOC_FSSETDM_BY_HANDLE_32: |
739 | return xfs_compat_fssetdm_by_handle(mp, arg, inode); | 662 | return xfs_compat_fssetdm_by_handle(filp, arg); |
740 | default: | 663 | default: |
741 | return -XFS_ERROR(ENOIOCTLCMD); | 664 | return -XFS_ERROR(ENOIOCTLCMD); |
742 | } | 665 | } |