Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ext2: convert to use the new truncate convention.

I also have commented a possible bug in existing ext2 code, marked with XXX.

Cc: linux-ext4@vger.kernel.org
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

npiggin@suse.de and committed by
Al Viro
737f2e93 3889e6e7

+118 -35
-1
fs/ext2/ext2.h
··· 122 122 extern void ext2_delete_inode (struct inode *); 123 123 extern int ext2_sync_inode (struct inode *); 124 124 extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); 125 - extern void ext2_truncate (struct inode *); 126 125 extern int ext2_setattr (struct dentry *, struct iattr *); 127 126 extern void ext2_set_inode_flags(struct inode *inode); 128 127 extern void ext2_get_inode_flags(struct ext2_inode_info *);
-1
fs/ext2/file.c
··· 95 95 #endif 96 96 97 97 const struct inode_operations ext2_file_inode_operations = { 98 - .truncate = ext2_truncate, 99 98 #ifdef CONFIG_EXT2_FS_XATTR 100 99 .setxattr = generic_setxattr, 101 100 .getxattr = generic_getxattr,
+118 -33
fs/ext2/inode.c
··· 54 54 inode->i_blocks - ea_blocks == 0); 55 55 } 56 56 57 + static void ext2_truncate_blocks(struct inode *inode, loff_t offset); 58 + 59 + static void ext2_write_failed(struct address_space *mapping, loff_t to) 60 + { 61 + struct inode *inode = mapping->host; 62 + 63 + if (to > inode->i_size) { 64 + truncate_pagecache(inode, to, inode->i_size); 65 + ext2_truncate_blocks(inode, inode->i_size); 66 + } 67 + } 68 + 57 69 /* 58 70 * Called at the last iput() if i_nlink is zero. 59 71 */ ··· 83 71 84 72 inode->i_size = 0; 85 73 if (inode->i_blocks) 86 - ext2_truncate (inode); 74 + ext2_truncate_blocks(inode, 0); 87 75 ext2_free_inode (inode); 88 76 89 77 return; ··· 769 757 loff_t pos, unsigned len, unsigned flags, 770 758 struct page **pagep, void **fsdata) 771 759 { 772 - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, 773 - ext2_get_block); 760 + return block_write_begin_newtrunc(file, mapping, pos, len, flags, 761 + pagep, fsdata, ext2_get_block); 774 762 } 775 763 776 764 static int ··· 778 766 loff_t pos, unsigned len, unsigned flags, 779 767 struct page **pagep, void **fsdata) 780 768 { 769 + int ret; 770 + 781 771 *pagep = NULL; 782 - return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata); 772 + ret = __ext2_write_begin(file, mapping, pos, len, flags, pagep, fsdata); 773 + if (ret < 0) 774 + ext2_write_failed(mapping, pos + len); 775 + return ret; 776 + } 777 + 778 + static int ext2_write_end(struct file *file, struct address_space *mapping, 779 + loff_t pos, unsigned len, unsigned copied, 780 + struct page *page, void *fsdata) 781 + { 782 + int ret; 783 + 784 + ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); 785 + if (ret < len) 786 + ext2_write_failed(mapping, pos + len); 787 + return ret; 783 788 } 784 789 785 790 static int ··· 804 775 loff_t pos, unsigned len, unsigned flags, 805 776 struct page **pagep, void **fsdata) 806 777 { 778 + int ret; 779 + 807 780 /* 808 781 * Dir-in-pagecache still uses ext2_write_begin. Would have to rework 809 782 * directory handling code to pass around offsets rather than struct 810 783 * pages in order to make this work easily. 811 784 */ 812 - return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, 813 - ext2_get_block); 785 + ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, pagep, 786 + fsdata, ext2_get_block); 787 + if (ret < 0) 788 + ext2_write_failed(mapping, pos + len); 789 + return ret; 814 790 } 815 791 816 792 static int ext2_nobh_writepage(struct page *page, ··· 834 800 loff_t offset, unsigned long nr_segs) 835 801 { 836 802 struct file *file = iocb->ki_filp; 837 - struct inode *inode = file->f_mapping->host; 803 + struct address_space *mapping = file->f_mapping; 804 + struct inode *inode = mapping->host; 805 + ssize_t ret; 838 806 839 - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, 840 - offset, nr_segs, ext2_get_block, NULL); 807 + ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev, 808 + iov, offset, nr_segs, ext2_get_block, NULL); 809 + if (ret < 0 && (rw & WRITE)) 810 + ext2_write_failed(mapping, offset + iov_length(iov, nr_segs)); 811 + return ret; 841 812 } 842 813 843 814 static int ··· 857 818 .writepage = ext2_writepage, 858 819 .sync_page = block_sync_page, 859 820 .write_begin = ext2_write_begin, 860 - .write_end = generic_write_end, 821 + .write_end = ext2_write_end, 861 822 .bmap = ext2_bmap, 862 823 .direct_IO = ext2_direct_IO, 863 824 .writepages = ext2_writepages, ··· 1066 1027 ext2_free_data(inode, p, q); 1067 1028 } 1068 1029 1069 - void ext2_truncate(struct inode *inode) 1030 + static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) 1070 1031 { 1071 1032 __le32 *i_data = EXT2_I(inode)->i_data; 1072 1033 struct ext2_inode_info *ei = EXT2_I(inode); ··· 1078 1039 int n; 1079 1040 long iblock; 1080 1041 unsigned blocksize; 1081 - 1082 - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || 1083 - S_ISLNK(inode->i_mode))) 1084 - return; 1085 - if (ext2_inode_is_fast_symlink(inode)) 1086 - return; 1087 - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 1088 - return; 1089 - 1090 1042 blocksize = inode->i_sb->s_blocksize; 1091 - iblock = (inode->i_size + blocksize-1) 1092 - >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); 1093 - 1094 - if (mapping_is_xip(inode->i_mapping)) 1095 - xip_truncate_page(inode->i_mapping, inode->i_size); 1096 - else if (test_opt(inode->i_sb, NOBH)) 1097 - nobh_truncate_page(inode->i_mapping, 1098 - inode->i_size, ext2_get_block); 1099 - else 1100 - block_truncate_page(inode->i_mapping, 1101 - inode->i_size, ext2_get_block); 1043 + iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); 1102 1044 1103 1045 n = ext2_block_to_path(inode, iblock, offsets, NULL); 1104 1046 if (n == 0) ··· 1147 1127 ext2_discard_reservation(inode); 1148 1128 1149 1129 mutex_unlock(&ei->truncate_mutex); 1130 + } 1131 + 1132 + static void ext2_truncate_blocks(struct inode *inode, loff_t offset) 1133 + { 1134 + /* 1135 + * XXX: it seems like a bug here that we don't allow 1136 + * IS_APPEND inode to have blocks-past-i_size trimmed off. 1137 + * review and fix this. 1138 + * 1139 + * Also would be nice to be able to handle IO errors and such, 1140 + * but that's probably too much to ask. 1141 + */ 1142 + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || 1143 + S_ISLNK(inode->i_mode))) 1144 + return; 1145 + if (ext2_inode_is_fast_symlink(inode)) 1146 + return; 1147 + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 1148 + return; 1149 + __ext2_truncate_blocks(inode, offset); 1150 + } 1151 + 1152 + int ext2_setsize(struct inode *inode, loff_t newsize) 1153 + { 1154 + loff_t oldsize; 1155 + int error; 1156 + 1157 + error = inode_newsize_ok(inode, newsize); 1158 + if (error) 1159 + return error; 1160 + 1161 + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || 1162 + S_ISLNK(inode->i_mode))) 1163 + return -EINVAL; 1164 + if (ext2_inode_is_fast_symlink(inode)) 1165 + return -EINVAL; 1166 + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 1167 + return -EPERM; 1168 + 1169 + if (mapping_is_xip(inode->i_mapping)) 1170 + error = xip_truncate_page(inode->i_mapping, newsize); 1171 + else if (test_opt(inode->i_sb, NOBH)) 1172 + error = nobh_truncate_page(inode->i_mapping, 1173 + newsize, ext2_get_block); 1174 + else 1175 + error = block_truncate_page(inode->i_mapping, 1176 + newsize, ext2_get_block); 1177 + if (error) 1178 + return error; 1179 + 1180 + oldsize = inode->i_size; 1181 + i_size_write(inode, newsize); 1182 + truncate_pagecache(inode, oldsize, newsize); 1183 + 1184 + __ext2_truncate_blocks(inode, newsize); 1185 + 1150 1186 inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; 1151 1187 if (inode_needs_sync(inode)) { 1152 1188 sync_mapping_buffers(inode->i_mapping); ··· 1210 1134 } else { 1211 1135 mark_inode_dirty(inode); 1212 1136 } 1137 + 1138 + return 0; 1213 1139 } 1214 1140 1215 1141 static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, ··· 1552 1474 if (error) 1553 1475 return error; 1554 1476 } 1555 - error = inode_setattr(inode, iattr); 1556 - if (!error && (iattr->ia_valid & ATTR_MODE)) 1477 + if (iattr->ia_valid & ATTR_SIZE) { 1478 + error = ext2_setsize(inode, iattr->ia_size); 1479 + if (error) 1480 + return error; 1481 + } 1482 + generic_setattr(inode, iattr); 1483 + if (iattr->ia_valid & ATTR_MODE) 1557 1484 error = ext2_acl_chmod(inode); 1485 + mark_inode_dirty(inode); 1486 + 1558 1487 return error; 1559 1488 }