diff options
author | npiggin@suse.de <npiggin@suse.de> | 2010-05-26 11:05:37 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-05-27 22:15:57 -0400 |
commit | 737f2e93b9724a3554f9d0b248c22cd407313f86 (patch) | |
tree | 205ceb526d32c5fcc6ad84a85b05afa9c7fc9902 /fs | |
parent | 3889e6e76f66b7de208a1709d0fe530b21a2d384 (diff) |
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>
Diffstat (limited to 'fs')
-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 | } |