Skip to content

Commit 737f2e9

Browse files
npiggin@suse.deAl Viro
authored andcommitted
ext2: convert to use the new truncate convention.
I also have commented a possible bug in existing ext2 code, marked with XXX. Cc: [email protected] Cc: Christoph Hellwig <[email protected]> Signed-off-by: Nick Piggin <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 3889e6e commit 737f2e9

File tree

3 files changed

+119
-36
lines changed

3 files changed

+119
-36
lines changed

fs/ext2/ext2.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ extern int ext2_write_inode (struct inode *, struct writeback_control *);
122122
extern void ext2_delete_inode (struct inode *);
123123
extern int ext2_sync_inode (struct inode *);
124124
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
125-
extern void ext2_truncate (struct inode *);
126125
extern int ext2_setattr (struct dentry *, struct iattr *);
127126
extern void ext2_set_inode_flags(struct inode *inode);
128127
extern void ext2_get_inode_flags(struct ext2_inode_info *);

fs/ext2/file.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ const struct file_operations ext2_xip_file_operations = {
9595
#endif
9696

9797
const struct inode_operations ext2_file_inode_operations = {
98-
.truncate = ext2_truncate,
9998
#ifdef CONFIG_EXT2_FS_XATTR
10099
.setxattr = generic_setxattr,
101100
.getxattr = generic_getxattr,

fs/ext2/inode.c

Lines changed: 119 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
5454
inode->i_blocks - ea_blocks == 0);
5555
}
5656

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+
5769
/*
5870
* Called at the last iput() if i_nlink is zero.
5971
*/
@@ -71,7 +83,7 @@ void ext2_delete_inode (struct inode * inode)
7183

7284
inode->i_size = 0;
7385
if (inode->i_blocks)
74-
ext2_truncate (inode);
86+
ext2_truncate_blocks(inode, 0);
7587
ext2_free_inode (inode);
7688

7789
return;
@@ -757,31 +769,53 @@ int __ext2_write_begin(struct file *file, struct address_space *mapping,
757769
loff_t pos, unsigned len, unsigned flags,
758770
struct page **pagep, void **fsdata)
759771
{
760-
return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
761-
ext2_get_block);
772+
return block_write_begin_newtrunc(file, mapping, pos, len, flags,
773+
pagep, fsdata, ext2_get_block);
762774
}
763775

764776
static int
765777
ext2_write_begin(struct file *file, struct address_space *mapping,
766778
loff_t pos, unsigned len, unsigned flags,
767779
struct page **pagep, void **fsdata)
768780
{
781+
int ret;
782+
769783
*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;
771800
}
772801

773802
static int
774803
ext2_nobh_write_begin(struct file *file, struct address_space *mapping,
775804
loff_t pos, unsigned len, unsigned flags,
776805
struct page **pagep, void **fsdata)
777806
{
807+
int ret;
808+
778809
/*
779810
* Dir-in-pagecache still uses ext2_write_begin. Would have to rework
780811
* directory handling code to pass around offsets rather than struct
781812
* pages in order to make this work easily.
782813
*/
783-
return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
784-
ext2_get_block);
814+
ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, pagep,
815+
fsdata, ext2_get_block);
816+
if (ret < 0)
817+
ext2_write_failed(mapping, pos + len);
818+
return ret;
785819
}
786820

787821
static int ext2_nobh_writepage(struct page *page,
@@ -800,10 +834,15 @@ ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
800834
loff_t offset, unsigned long nr_segs)
801835
{
802836
struct file *file = iocb->ki_filp;
803-
struct inode *inode = file->f_mapping->host;
804-
805-
return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
806-
offset, nr_segs, ext2_get_block, NULL);
837+
struct address_space *mapping = file->f_mapping;
838+
struct inode *inode = mapping->host;
839+
ssize_t ret;
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;
807846
}
808847

809848
static int
@@ -818,7 +857,7 @@ const struct address_space_operations ext2_aops = {
818857
.writepage = ext2_writepage,
819858
.sync_page = block_sync_page,
820859
.write_begin = ext2_write_begin,
821-
.write_end = generic_write_end,
860+
.write_end = ext2_write_end,
822861
.bmap = ext2_bmap,
823862
.direct_IO = ext2_direct_IO,
824863
.writepages = ext2_writepages,
@@ -1027,7 +1066,7 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de
10271066
ext2_free_data(inode, p, q);
10281067
}
10291068

1030-
void ext2_truncate(struct inode *inode)
1069+
static void __ext2_truncate_blocks(struct inode *inode, loff_t offset)
10311070
{
10321071
__le32 *i_data = EXT2_I(inode)->i_data;
10331072
struct ext2_inode_info *ei = EXT2_I(inode);
@@ -1039,27 +1078,8 @@ void ext2_truncate(struct inode *inode)
10391078
int n;
10401079
long iblock;
10411080
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-
10511081
blocksize = inode->i_sb->s_blocksize;
1052-
iblock = (inode->i_size + blocksize-1)
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);
1082+
iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
10631083

10641084
n = ext2_block_to_path(inode, iblock, offsets, NULL);
10651085
if (n == 0)
@@ -1127,13 +1147,71 @@ void ext2_truncate(struct inode *inode)
11271147
ext2_discard_reservation(inode);
11281148

11291149
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+
11301206
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
11311207
if (inode_needs_sync(inode)) {
11321208
sync_mapping_buffers(inode->i_mapping);
11331209
ext2_sync_inode (inode);
11341210
} else {
11351211
mark_inode_dirty(inode);
11361212
}
1213+
1214+
return 0;
11371215
}
11381216

11391217
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)
14741552
if (error)
14751553
return error;
14761554
}
1477-
error = inode_setattr(inode, iattr);
1478-
if (!error && (iattr->ia_valid & ATTR_MODE))
1555+
if (iattr->ia_valid & ATTR_SIZE) {
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)
14791562
error = ext2_acl_chmod(inode);
1563+
mark_inode_dirty(inode);
1564+
14801565
return error;
14811566
}

0 commit comments

Comments
 (0)