diff options
Diffstat (limited to 'fs/xfs/xfs_ioctl.c')
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 130 |
1 files changed, 122 insertions, 8 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 45287419dc37..e448d735346b 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
@@ -641,7 +641,11 @@ xfs_ioc_space( | |||
641 | unsigned int cmd, | 641 | unsigned int cmd, |
642 | xfs_flock64_t *bf) | 642 | xfs_flock64_t *bf) |
643 | { | 643 | { |
644 | int attr_flags = 0; | 644 | struct xfs_mount *mp = ip->i_mount; |
645 | struct xfs_trans *tp; | ||
646 | struct iattr iattr; | ||
647 | bool setprealloc = false; | ||
648 | bool clrprealloc = false; | ||
645 | int error; | 649 | int error; |
646 | 650 | ||
647 | /* | 651 | /* |
@@ -661,17 +665,127 @@ xfs_ioc_space( | |||
661 | if (!S_ISREG(inode->i_mode)) | 665 | if (!S_ISREG(inode->i_mode)) |
662 | return -XFS_ERROR(EINVAL); | 666 | return -XFS_ERROR(EINVAL); |
663 | 667 | ||
664 | if (filp->f_flags & O_DSYNC) | ||
665 | attr_flags |= XFS_ATTR_SYNC; | ||
666 | |||
667 | if (ioflags & IO_INVIS) | ||
668 | attr_flags |= XFS_ATTR_DMI; | ||
669 | |||
670 | error = mnt_want_write_file(filp); | 668 | error = mnt_want_write_file(filp); |
671 | if (error) | 669 | if (error) |
672 | return error; | 670 | return error; |
671 | |||
673 | xfs_ilock(ip, XFS_IOLOCK_EXCL); | 672 | xfs_ilock(ip, XFS_IOLOCK_EXCL); |
674 | error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags); | 673 | |
674 | switch (bf->l_whence) { | ||
675 | case 0: /*SEEK_SET*/ | ||
676 | break; | ||
677 | case 1: /*SEEK_CUR*/ | ||
678 | bf->l_start += filp->f_pos; | ||
679 | break; | ||
680 | case 2: /*SEEK_END*/ | ||
681 | bf->l_start += XFS_ISIZE(ip); | ||
682 | break; | ||
683 | default: | ||
684 | error = XFS_ERROR(EINVAL); | ||
685 | goto out_unlock; | ||
686 | } | ||
687 | |||
688 | /* | ||
689 | * length of <= 0 for resv/unresv/zero is invalid. length for | ||
690 | * alloc/free is ignored completely and we have no idea what userspace | ||
691 | * might have set it to, so set it to zero to allow range | ||
692 | * checks to pass. | ||
693 | */ | ||
694 | switch (cmd) { | ||
695 | case XFS_IOC_ZERO_RANGE: | ||
696 | case XFS_IOC_RESVSP: | ||
697 | case XFS_IOC_RESVSP64: | ||
698 | case XFS_IOC_UNRESVSP: | ||
699 | case XFS_IOC_UNRESVSP64: | ||
700 | if (bf->l_len <= 0) { | ||
701 | error = XFS_ERROR(EINVAL); | ||
702 | goto out_unlock; | ||
703 | } | ||
704 | break; | ||
705 | default: | ||
706 | bf->l_len = 0; | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | if (bf->l_start < 0 || | ||
711 | bf->l_start > mp->m_super->s_maxbytes || | ||
712 | bf->l_start + bf->l_len < 0 || | ||
713 | bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) { | ||
714 | error = XFS_ERROR(EINVAL); | ||
715 | goto out_unlock; | ||
716 | } | ||
717 | |||
718 | switch (cmd) { | ||
719 | case XFS_IOC_ZERO_RANGE: | ||
720 | error = xfs_zero_file_space(ip, bf->l_start, bf->l_len); | ||
721 | if (!error) | ||
722 | setprealloc = true; | ||
723 | break; | ||
724 | case XFS_IOC_RESVSP: | ||
725 | case XFS_IOC_RESVSP64: | ||
726 | error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len, | ||
727 | XFS_BMAPI_PREALLOC); | ||
728 | if (!error) | ||
729 | setprealloc = true; | ||
730 | break; | ||
731 | case XFS_IOC_UNRESVSP: | ||
732 | case XFS_IOC_UNRESVSP64: | ||
733 | error = xfs_free_file_space(ip, bf->l_start, bf->l_len); | ||
734 | break; | ||
735 | case XFS_IOC_ALLOCSP: | ||
736 | case XFS_IOC_ALLOCSP64: | ||
737 | case XFS_IOC_FREESP: | ||
738 | case XFS_IOC_FREESP64: | ||
739 | if (bf->l_start > XFS_ISIZE(ip)) { | ||
740 | error = xfs_alloc_file_space(ip, XFS_ISIZE(ip), | ||
741 | bf->l_start - XFS_ISIZE(ip), 0); | ||
742 | if (error) | ||
743 | goto out_unlock; | ||
744 | } | ||
745 | |||
746 | iattr.ia_valid = ATTR_SIZE; | ||
747 | iattr.ia_size = bf->l_start; | ||
748 | |||
749 | error = xfs_setattr_size(ip, &iattr); | ||
750 | if (!error) | ||
751 | clrprealloc = true; | ||
752 | break; | ||
753 | default: | ||
754 | ASSERT(0); | ||
755 | error = XFS_ERROR(EINVAL); | ||
756 | } | ||
757 | |||
758 | if (error) | ||
759 | goto out_unlock; | ||
760 | |||
761 | tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID); | ||
762 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0); | ||
763 | if (error) { | ||
764 | xfs_trans_cancel(tp, 0); | ||
765 | goto out_unlock; | ||
766 | } | ||
767 | |||
768 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
769 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); | ||
770 | |||
771 | if (!(ioflags & IO_INVIS)) { | ||
772 | ip->i_d.di_mode &= ~S_ISUID; | ||
773 | if (ip->i_d.di_mode & S_IXGRP) | ||
774 | ip->i_d.di_mode &= ~S_ISGID; | ||
775 | xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
776 | } | ||
777 | |||
778 | if (setprealloc) | ||
779 | ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC; | ||
780 | else if (clrprealloc) | ||
781 | ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC; | ||
782 | |||
783 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
784 | if (filp->f_flags & O_DSYNC) | ||
785 | xfs_trans_set_sync(tp); | ||
786 | error = xfs_trans_commit(tp, 0); | ||
787 | |||
788 | out_unlock: | ||
675 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | 789 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); |
676 | mnt_drop_write_file(filp); | 790 | mnt_drop_write_file(filp); |
677 | return -error; | 791 | return -error; |