aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/linux-2.6/xfs_ioctl32.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2009-01-18 20:02:57 -0500
committerLachlan McIlroy <lachlan@sgi.com>2009-01-18 22:43:18 -0500
commitab596ad8972f314ace538799734c7e1bdd1da2ff (patch)
tree40c35f802be9307a4739ff3b22260d3a0788c2f0 /fs/xfs/linux-2.6/xfs_ioctl32.c
parentf3b8436ad9a8ad36b3c9fa1fe030c7f38e5d3d0b (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.c175
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/* 343STATIC struct dentry *
344 * Convert userspace handle data into inode. 344xfs_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 */
352STATIC int
353xfs_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
423STATIC int 352STATIC int
424xfs_compat_attrlist_by_handle( 353xfs_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
475STATIC int 402STATIC int
476xfs_compat_attrmulti_by_handle( 403xfs_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
562STATIC int 487STATIC int
563xfs_compat_fssetdm_by_handle( 488xfs_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
597out: 520out:
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 }