aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_iops.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_iops.c')
-rw-r--r--fs/xfs/xfs_iops.c46
1 files changed, 29 insertions, 17 deletions
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index f9babd179223..ab302539e5b9 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -750,6 +750,7 @@ xfs_setattr_size(
750 struct xfs_mount *mp = ip->i_mount; 750 struct xfs_mount *mp = ip->i_mount;
751 struct inode *inode = VFS_I(ip); 751 struct inode *inode = VFS_I(ip);
752 int mask = iattr->ia_valid; 752 int mask = iattr->ia_valid;
753 xfs_off_t oldsize, newsize;
753 struct xfs_trans *tp; 754 struct xfs_trans *tp;
754 int error; 755 int error;
755 uint lock_flags; 756 uint lock_flags;
@@ -777,11 +778,13 @@ xfs_setattr_size(
777 lock_flags |= XFS_IOLOCK_EXCL; 778 lock_flags |= XFS_IOLOCK_EXCL;
778 xfs_ilock(ip, lock_flags); 779 xfs_ilock(ip, lock_flags);
779 780
781 oldsize = inode->i_size;
782 newsize = iattr->ia_size;
783
780 /* 784 /*
781 * Short circuit the truncate case for zero length files. 785 * Short circuit the truncate case for zero length files.
782 */ 786 */
783 if (iattr->ia_size == 0 && 787 if (newsize == 0 && oldsize == 0 && ip->i_d.di_nextents == 0) {
784 ip->i_size == 0 && ip->i_d.di_nextents == 0) {
785 if (!(mask & (ATTR_CTIME|ATTR_MTIME))) 788 if (!(mask & (ATTR_CTIME|ATTR_MTIME)))
786 goto out_unlock; 789 goto out_unlock;
787 790
@@ -807,14 +810,14 @@ xfs_setattr_size(
807 * the inode to the transaction, because the inode cannot be unlocked 810 * the inode to the transaction, because the inode cannot be unlocked
808 * once it is a part of the transaction. 811 * once it is a part of the transaction.
809 */ 812 */
810 if (iattr->ia_size > ip->i_size) { 813 if (newsize > oldsize) {
811 /* 814 /*
812 * Do the first part of growing a file: zero any data in the 815 * Do the first part of growing a file: zero any data in the
813 * last block that is beyond the old EOF. We need to do this 816 * last block that is beyond the old EOF. We need to do this
814 * before the inode is joined to the transaction to modify 817 * before the inode is joined to the transaction to modify
815 * i_size. 818 * i_size.
816 */ 819 */
817 error = xfs_zero_eof(ip, iattr->ia_size, ip->i_size); 820 error = xfs_zero_eof(ip, newsize, oldsize);
818 if (error) 821 if (error)
819 goto out_unlock; 822 goto out_unlock;
820 } 823 }
@@ -833,8 +836,8 @@ xfs_setattr_size(
833 * here and prevents waiting for other data not within the range we 836 * here and prevents waiting for other data not within the range we
834 * care about here. 837 * care about here.
835 */ 838 */
836 if (ip->i_size != ip->i_d.di_size && iattr->ia_size > ip->i_d.di_size) { 839 if (oldsize != ip->i_d.di_size && newsize > ip->i_d.di_size) {
837 error = xfs_flush_pages(ip, ip->i_d.di_size, iattr->ia_size, 0, 840 error = xfs_flush_pages(ip, ip->i_d.di_size, newsize, 0,
838 FI_NONE); 841 FI_NONE);
839 if (error) 842 if (error)
840 goto out_unlock; 843 goto out_unlock;
@@ -845,8 +848,7 @@ xfs_setattr_size(
845 */ 848 */
846 inode_dio_wait(inode); 849 inode_dio_wait(inode);
847 850
848 error = -block_truncate_page(inode->i_mapping, iattr->ia_size, 851 error = -block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks);
849 xfs_get_blocks);
850 if (error) 852 if (error)
851 goto out_unlock; 853 goto out_unlock;
852 854
@@ -857,7 +859,7 @@ xfs_setattr_size(
857 if (error) 859 if (error)
858 goto out_trans_cancel; 860 goto out_trans_cancel;
859 861
860 truncate_setsize(inode, iattr->ia_size); 862 truncate_setsize(inode, newsize);
861 863
862 commit_flags = XFS_TRANS_RELEASE_LOG_RES; 864 commit_flags = XFS_TRANS_RELEASE_LOG_RES;
863 lock_flags |= XFS_ILOCK_EXCL; 865 lock_flags |= XFS_ILOCK_EXCL;
@@ -876,19 +878,29 @@ xfs_setattr_size(
876 * these flags set. For all other operations the VFS set these flags 878 * these flags set. For all other operations the VFS set these flags
877 * explicitly if it wants a timestamp update. 879 * explicitly if it wants a timestamp update.
878 */ 880 */
879 if (iattr->ia_size != ip->i_size && 881 if (newsize != oldsize && (!(mask & (ATTR_CTIME | ATTR_MTIME)))) {
880 (!(mask & (ATTR_CTIME | ATTR_MTIME)))) {
881 iattr->ia_ctime = iattr->ia_mtime = 882 iattr->ia_ctime = iattr->ia_mtime =
882 current_fs_time(inode->i_sb); 883 current_fs_time(inode->i_sb);
883 mask |= ATTR_CTIME | ATTR_MTIME; 884 mask |= ATTR_CTIME | ATTR_MTIME;
884 } 885 }
885 886
886 if (iattr->ia_size > ip->i_size) { 887 /*
887 ip->i_d.di_size = iattr->ia_size; 888 * The first thing we do is set the size to new_size permanently on
888 ip->i_size = iattr->ia_size; 889 * disk. This way we don't have to worry about anyone ever being able
889 } else if (iattr->ia_size <= ip->i_size || 890 * to look at the data being freed even in the face of a crash.
890 (iattr->ia_size == 0 && ip->i_d.di_nextents)) { 891 * What we're getting around here is the case where we free a block, it
891 error = xfs_itruncate_data(&tp, ip, iattr->ia_size); 892 * is allocated to another file, it is written to, and then we crash.
893 * If the new data gets written to the file but the log buffers
894 * containing the free and reallocation don't, then we'd end up with
895 * garbage in the blocks being freed. As long as we make the new size
896 * permanent before actually freeing any blocks it doesn't matter if
897 * they get written to.
898 */
899 ip->i_d.di_size = newsize;
900 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
901
902 if (newsize <= oldsize) {
903 error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, newsize);
892 if (error) 904 if (error)
893 goto out_trans_abort; 905 goto out_trans_abort;
894 906