diff options
| -rw-r--r-- | fs/ext2/ext2.h | 1 | ||||
| -rw-r--r-- | fs/ext2/file.c | 1 | ||||
| -rw-r--r-- | fs/ext2/inode.c | 153 |
3 files changed, 119 insertions, 36 deletions
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 8e917b686510..52b34f1d2738 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h | |||
| @@ -122,7 +122,6 @@ extern int ext2_write_inode (struct inode *, struct writeback_control *); | |||
| 122 | extern void ext2_delete_inode (struct inode *); | 122 | extern void ext2_delete_inode (struct inode *); |
| 123 | extern int ext2_sync_inode (struct inode *); | 123 | extern int ext2_sync_inode (struct inode *); |
| 124 | extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); | 124 | extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); |
| 125 | extern void ext2_truncate (struct inode *); | ||
| 126 | extern int ext2_setattr (struct dentry *, struct iattr *); | 125 | extern int ext2_setattr (struct dentry *, struct iattr *); |
| 127 | extern void ext2_set_inode_flags(struct inode *inode); | 126 | extern void ext2_set_inode_flags(struct inode *inode); |
| 128 | extern void ext2_get_inode_flags(struct ext2_inode_info *); | 127 | extern void ext2_get_inode_flags(struct ext2_inode_info *); |
diff --git a/fs/ext2/file.c b/fs/ext2/file.c index d82e7ca93455..49eec9456c5b 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c | |||
| @@ -95,7 +95,6 @@ const struct file_operations ext2_xip_file_operations = { | |||
| 95 | #endif | 95 | #endif |
| 96 | 96 | ||
| 97 | const struct inode_operations ext2_file_inode_operations = { | 97 | const struct inode_operations ext2_file_inode_operations = { |
| 98 | .truncate = ext2_truncate, | ||
| 99 | #ifdef CONFIG_EXT2_FS_XATTR | 98 | #ifdef CONFIG_EXT2_FS_XATTR |
| 100 | .setxattr = generic_setxattr, | 99 | .setxattr = generic_setxattr, |
| 101 | .getxattr = generic_getxattr, | 100 | .getxattr = generic_getxattr, |
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 527c46d9bc1f..19214435b752 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c | |||
| @@ -54,6 +54,18 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode) | |||
| 54 | inode->i_blocks - ea_blocks == 0); | 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 | * Called at the last iput() if i_nlink is zero. | 70 | * Called at the last iput() if i_nlink is zero. |
| 59 | */ | 71 | */ |
| @@ -71,7 +83,7 @@ void ext2_delete_inode (struct inode * inode) | |||
| 71 | 83 | ||
| 72 | inode->i_size = 0; | 84 | inode->i_size = 0; |
| 73 | if (inode->i_blocks) | 85 | if (inode->i_blocks) |
| 74 | ext2_truncate (inode); | 86 | ext2_truncate_blocks(inode, 0); |
| 75 | ext2_free_inode (inode); | 87 | ext2_free_inode (inode); |
| 76 | 88 | ||
| 77 | return; | 89 | return; |
| @@ -757,8 +769,8 @@ int __ext2_write_begin(struct file *file, struct address_space *mapping, | |||
| 757 | loff_t pos, unsigned len, unsigned flags, | 769 | loff_t pos, unsigned len, unsigned flags, |
| 758 | struct page **pagep, void **fsdata) | 770 | struct page **pagep, void **fsdata) |
| 759 | { | 771 | { |
| 760 | return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, | 772 | return block_write_begin_newtrunc(file, mapping, pos, len, flags, |
| 761 | ext2_get_block); | 773 | pagep, fsdata, ext2_get_block); |
| 762 | } | 774 | } |
| 763 | 775 | ||
| 764 | static int | 776 | static int |
| @@ -766,8 +778,25 @@ ext2_write_begin(struct file *file, struct address_space *mapping, | |||
| 766 | loff_t pos, unsigned len, unsigned flags, | 778 | loff_t pos, unsigned len, unsigned flags, |
| 767 | struct page **pagep, void **fsdata) | 779 | struct page **pagep, void **fsdata) |
| 768 | { | 780 | { |
| 781 | int ret; | ||
| 782 | |||
| 769 | *pagep = NULL; | 783 | *pagep = NULL; |
| 770 | return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata); | 784 | ret = __ext2_write_begin(file, mapping, pos, len, flags, pagep, fsdata); |
| 785 | if (ret < 0) | ||
| 786 | ext2_write_failed(mapping, pos + len); | ||
| 787 | return ret; | ||
| 788 | } | ||
| 789 | |||
| 790 | static int ext2_write_end(struct file *file, struct address_space *mapping, | ||
| 791 | loff_t pos, unsigned len, unsigned copied, | ||
| 792 | struct page *page, void *fsdata) | ||
| 793 | { | ||
| 794 | int ret; | ||
| 795 | |||
| 796 | ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); | ||
| 797 | if (ret < len) | ||
| 798 | ext2_write_failed(mapping, pos + len); | ||
| 799 | return ret; | ||
| 771 | } | 800 | } |
| 772 | 801 | ||
| 773 | static int | 802 | static int |
| @@ -775,13 +804,18 @@ ext2_nobh_write_begin(struct file *file, struct address_space *mapping, | |||
| 775 | loff_t pos, unsigned len, unsigned flags, | 804 | loff_t pos, unsigned len, unsigned flags, |
| 776 | struct page **pagep, void **fsdata) | 805 | struct page **pagep, void **fsdata) |
| 777 | { | 806 | { |
| 807 | int ret; | ||
| 808 | |||
| 778 | /* | 809 | /* |
| 779 | * Dir-in-pagecache still uses ext2_write_begin. Would have to rework | 810 | * Dir-in-pagecache still uses ext2_write_begin. Would have to rework |
| 780 | * directory handling code to pass around offsets rather than struct | 811 | * directory handling code to pass around offsets rather than struct |
| 781 | * pages in order to make this work easily. | 812 | * pages in order to make this work easily. |
| 782 | */ | 813 | */ |
| 783 | return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, | 814 | ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, pagep, |
| 784 | ext2_get_block); | 815 | fsdata, ext2_get_block); |
| 816 | if (ret < 0) | ||
| 817 | ext2_write_failed(mapping, pos + len); | ||
| 818 | return ret; | ||
| 785 | } | 819 | } |
| 786 | 820 | ||
| 787 | static int ext2_nobh_writepage(struct page *page, | 821 | static int ext2_nobh_writepage(struct page *page, |
| @@ -800,10 +834,15 @@ ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
| 800 | loff_t offset, unsigned long nr_segs) | 834 | loff_t offset, unsigned long nr_segs) |
| 801 | { | 835 | { |
| 802 | struct file *file = iocb->ki_filp; | 836 | struct file *file = iocb->ki_filp; |
| 803 | struct inode *inode = file->f_mapping->host; | 837 | struct address_space *mapping = file->f_mapping; |
| 804 | 838 | struct inode *inode = mapping->host; | |
| 805 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | 839 | ssize_t ret; |
| 806 | offset, nr_segs, ext2_get_block, NULL); | 840 | |
| 841 | ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev, | ||
| 842 | iov, offset, nr_segs, ext2_get_block, NULL); | ||
| 843 | if (ret < 0 && (rw & WRITE)) | ||
| 844 | ext2_write_failed(mapping, offset + iov_length(iov, nr_segs)); | ||
| 845 | return ret; | ||
| 807 | } | 846 | } |
| 808 | 847 | ||
| 809 | static int | 848 | static int |
| @@ -818,7 +857,7 @@ const struct address_space_operations ext2_aops = { | |||
| 818 | .writepage = ext2_writepage, | 857 | .writepage = ext2_writepage, |
| 819 | .sync_page = block_sync_page, | 858 | .sync_page = block_sync_page, |
| 820 | .write_begin = ext2_write_begin, | 859 | .write_begin = ext2_write_begin, |
| 821 | .write_end = generic_write_end, | 860 | .write_end = ext2_write_end, |
| 822 | .bmap = ext2_bmap, | 861 | .bmap = ext2_bmap, |
| 823 | .direct_IO = ext2_direct_IO, | 862 | .direct_IO = ext2_direct_IO, |
| 824 | .writepages = ext2_writepages, | 863 | .writepages = ext2_writepages, |
| @@ -1027,7 +1066,7 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de | |||
| 1027 | ext2_free_data(inode, p, q); | 1066 | ext2_free_data(inode, p, q); |
| 1028 | } | 1067 | } |
| 1029 | 1068 | ||
| 1030 | void ext2_truncate(struct inode *inode) | 1069 | static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) |
| 1031 | { | 1070 | { |
| 1032 | __le32 *i_data = EXT2_I(inode)->i_data; | 1071 | __le32 *i_data = EXT2_I(inode)->i_data; |
| 1033 | struct ext2_inode_info *ei = EXT2_I(inode); | 1072 | struct ext2_inode_info *ei = EXT2_I(inode); |
| @@ -1039,27 +1078,8 @@ void ext2_truncate(struct inode *inode) | |||
| 1039 | int n; | 1078 | int n; |
| 1040 | long iblock; | 1079 | long iblock; |
| 1041 | unsigned blocksize; | 1080 | unsigned blocksize; |
| 1042 | |||
| 1043 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
| 1044 | S_ISLNK(inode->i_mode))) | ||
| 1045 | return; | ||
| 1046 | if (ext2_inode_is_fast_symlink(inode)) | ||
| 1047 | return; | ||
| 1048 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
| 1049 | return; | ||
| 1050 | |||
| 1051 | blocksize = inode->i_sb->s_blocksize; | 1081 | blocksize = inode->i_sb->s_blocksize; |
| 1052 | iblock = (inode->i_size + blocksize-1) | 1082 | iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); |
| 1053 | >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); | ||
| 1054 | |||
| 1055 | if (mapping_is_xip(inode->i_mapping)) | ||
| 1056 | xip_truncate_page(inode->i_mapping, inode->i_size); | ||
| 1057 | else if (test_opt(inode->i_sb, NOBH)) | ||
| 1058 | nobh_truncate_page(inode->i_mapping, | ||
| 1059 | inode->i_size, ext2_get_block); | ||
| 1060 | else | ||
| 1061 | block_truncate_page(inode->i_mapping, | ||
| 1062 | inode->i_size, ext2_get_block); | ||
| 1063 | 1083 | ||
| 1064 | n = ext2_block_to_path(inode, iblock, offsets, NULL); | 1084 | n = ext2_block_to_path(inode, iblock, offsets, NULL); |
| 1065 | if (n == 0) | 1085 | if (n == 0) |
| @@ -1127,6 +1147,62 @@ do_indirects: | |||
| 1127 | ext2_discard_reservation(inode); | 1147 | ext2_discard_reservation(inode); |
| 1128 | 1148 | ||
| 1129 | mutex_unlock(&ei->truncate_mutex); | 1149 | mutex_unlock(&ei->truncate_mutex); |
| 1150 | } | ||
| 1151 | |||
| 1152 | static void ext2_truncate_blocks(struct inode *inode, loff_t offset) | ||
| 1153 | { | ||
| 1154 | /* | ||
| 1155 | * XXX: it seems like a bug here that we don't allow | ||
| 1156 | * IS_APPEND inode to have blocks-past-i_size trimmed off. | ||
| 1157 | * review and fix this. | ||
| 1158 | * | ||
| 1159 | * Also would be nice to be able to handle IO errors and such, | ||
| 1160 | * but that's probably too much to ask. | ||
| 1161 | */ | ||
| 1162 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
| 1163 | S_ISLNK(inode->i_mode))) | ||
| 1164 | return; | ||
| 1165 | if (ext2_inode_is_fast_symlink(inode)) | ||
| 1166 | return; | ||
| 1167 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
| 1168 | return; | ||
| 1169 | __ext2_truncate_blocks(inode, offset); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | int ext2_setsize(struct inode *inode, loff_t newsize) | ||
| 1173 | { | ||
| 1174 | loff_t oldsize; | ||
| 1175 | int error; | ||
| 1176 | |||
| 1177 | error = inode_newsize_ok(inode, newsize); | ||
| 1178 | if (error) | ||
| 1179 | return error; | ||
| 1180 | |||
| 1181 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
| 1182 | S_ISLNK(inode->i_mode))) | ||
| 1183 | return -EINVAL; | ||
| 1184 | if (ext2_inode_is_fast_symlink(inode)) | ||
| 1185 | return -EINVAL; | ||
| 1186 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
| 1187 | return -EPERM; | ||
| 1188 | |||
| 1189 | if (mapping_is_xip(inode->i_mapping)) | ||
| 1190 | error = xip_truncate_page(inode->i_mapping, newsize); | ||
| 1191 | else if (test_opt(inode->i_sb, NOBH)) | ||
| 1192 | error = nobh_truncate_page(inode->i_mapping, | ||
| 1193 | newsize, ext2_get_block); | ||
| 1194 | else | ||
| 1195 | error = block_truncate_page(inode->i_mapping, | ||
| 1196 | newsize, ext2_get_block); | ||
| 1197 | if (error) | ||
| 1198 | return error; | ||
| 1199 | |||
| 1200 | oldsize = inode->i_size; | ||
| 1201 | i_size_write(inode, newsize); | ||
| 1202 | truncate_pagecache(inode, oldsize, newsize); | ||
| 1203 | |||
| 1204 | __ext2_truncate_blocks(inode, newsize); | ||
| 1205 | |||
| 1130 | inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; | 1206 | inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; |
| 1131 | if (inode_needs_sync(inode)) { | 1207 | if (inode_needs_sync(inode)) { |
| 1132 | sync_mapping_buffers(inode->i_mapping); | 1208 | sync_mapping_buffers(inode->i_mapping); |
| @@ -1134,6 +1210,8 @@ do_indirects: | |||
| 1134 | } else { | 1210 | } else { |
| 1135 | mark_inode_dirty(inode); | 1211 | mark_inode_dirty(inode); |
| 1136 | } | 1212 | } |
| 1213 | |||
| 1214 | return 0; | ||
| 1137 | } | 1215 | } |
| 1138 | 1216 | ||
| 1139 | static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, | 1217 | static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, |
| @@ -1474,8 +1552,15 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) | |||
| 1474 | if (error) | 1552 | if (error) |
| 1475 | return error; | 1553 | return error; |
| 1476 | } | 1554 | } |
| 1477 | error = inode_setattr(inode, iattr); | 1555 | if (iattr->ia_valid & ATTR_SIZE) { |
| 1478 | if (!error && (iattr->ia_valid & ATTR_MODE)) | 1556 | error = ext2_setsize(inode, iattr->ia_size); |
| 1557 | if (error) | ||
| 1558 | return error; | ||
| 1559 | } | ||
| 1560 | generic_setattr(inode, iattr); | ||
| 1561 | if (iattr->ia_valid & ATTR_MODE) | ||
| 1479 | error = ext2_acl_chmod(inode); | 1562 | error = ext2_acl_chmod(inode); |
| 1563 | mark_inode_dirty(inode); | ||
| 1564 | |||
| 1480 | return error; | 1565 | return error; |
| 1481 | } | 1566 | } |
