]>
Commit | Line | Data |
---|---|---|
7d777456 GKH |
1 | From 9d176d321904553ab92a5df99e25ccb268a5560e Mon Sep 17 00:00:00 2001 |
2 | From: Jiaying Zhang <jiayingz@google.com> | |
3 | Date: Sun, 30 May 2010 22:49:29 -0400 | |
4 | Subject: ext4: Add flag to files with blocks intentionally past EOF | |
5 | ||
6 | commit c8d46e41bc744c8fa0092112af3942fcd46c8b18 upstream (as of v2.6.33-git11) | |
7 | ||
8 | fallocate() may potentially instantiate blocks past EOF, depending | |
9 | on the flags used when it is called. | |
10 | ||
11 | e2fsck currently has a test for blocks past i_size, and it | |
12 | sometimes trips up - noticeably on xfstests 013 which runs fsstress. | |
13 | ||
14 | This patch from Jiayang does fix it up - it (along with | |
15 | e2fsprogs updates and other patches recently from Aneesh) has | |
16 | survived many fsstress runs in a row. | |
17 | ||
18 | Signed-off-by: Eric Sandeen <sandeen@redhat.com> | |
19 | Signed-off-by: Jiaying Zhang <jiayingz@google.com> | |
20 | Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> | |
21 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
22 | --- | |
23 | fs/ext4/ext4.h | 6 ++++-- | |
24 | fs/ext4/extents.c | 22 +++++++++++++++++++++- | |
25 | fs/ext4/inode.c | 9 ++++++++- | |
26 | fs/ext4/ioctl.c | 9 +++++++++ | |
27 | 4 files changed, 42 insertions(+), 4 deletions(-) | |
28 | ||
29 | --- a/fs/ext4/ext4.h | |
30 | +++ b/fs/ext4/ext4.h | |
31 | @@ -284,10 +284,12 @@ struct flex_groups { | |
32 | #define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ | |
33 | #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ | |
34 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ | |
35 | +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ | |
36 | +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ | |
37 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ | |
38 | ||
39 | -#define EXT4_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ | |
40 | -#define EXT4_FL_USER_MODIFIABLE 0x000B80FF /* User modifiable flags */ | |
41 | +#define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ | |
42 | +#define EXT4_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ | |
43 | ||
44 | /* Flags that should be inherited by new inodes from their parent. */ | |
45 | #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ | |
46 | --- a/fs/ext4/extents.c | |
47 | +++ b/fs/ext4/extents.c | |
48 | @@ -3191,7 +3191,7 @@ int ext4_ext_get_blocks(handle_t *handle | |
49 | { | |
50 | struct ext4_ext_path *path = NULL; | |
51 | struct ext4_extent_header *eh; | |
52 | - struct ext4_extent newex, *ex; | |
53 | + struct ext4_extent newex, *ex, *last_ex; | |
54 | ext4_fsblk_t newblock; | |
55 | int err = 0, depth, ret, cache_type; | |
56 | unsigned int allocated = 0; | |
57 | @@ -3372,6 +3372,19 @@ int ext4_ext_get_blocks(handle_t *handle | |
58 | EXT4_STATE_DIO_UNWRITTEN); | |
59 | } | |
60 | } | |
61 | + | |
62 | + if (unlikely(EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) { | |
63 | + if (eh->eh_entries) { | |
64 | + last_ex = EXT_LAST_EXTENT(eh); | |
65 | + if (iblock + ar.len > le32_to_cpu(last_ex->ee_block) | |
66 | + + ext4_ext_get_actual_len(last_ex)) | |
67 | + EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL; | |
68 | + } else { | |
69 | + WARN_ON(eh->eh_entries == 0); | |
70 | + ext4_error(inode->i_sb, __func__, | |
71 | + "inode#%lu, eh->eh_entries = 0!", inode->i_ino); | |
72 | + } | |
73 | + } | |
74 | err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); | |
75 | if (err) { | |
76 | /* free data blocks we just allocated */ | |
77 | @@ -3505,6 +3518,13 @@ static void ext4_falloc_update_inode(str | |
78 | i_size_write(inode, new_size); | |
79 | if (new_size > EXT4_I(inode)->i_disksize) | |
80 | ext4_update_i_disksize(inode, new_size); | |
81 | + } else { | |
82 | + /* | |
83 | + * Mark that we allocate beyond EOF so the subsequent truncate | |
84 | + * can proceed even if the new size is the same as i_size. | |
85 | + */ | |
86 | + if (new_size > i_size_read(inode)) | |
87 | + EXT4_I(inode)->i_flags |= EXT4_EOFBLOCKS_FL; | |
88 | } | |
89 | ||
90 | } | |
91 | --- a/fs/ext4/inode.c | |
92 | +++ b/fs/ext4/inode.c | |
93 | @@ -4490,6 +4490,8 @@ void ext4_truncate(struct inode *inode) | |
94 | if (!ext4_can_truncate(inode)) | |
95 | return; | |
96 | ||
97 | + EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL; | |
98 | + | |
99 | if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC)) | |
100 | ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE); | |
101 | ||
102 | @@ -5345,7 +5347,9 @@ int ext4_setattr(struct dentry *dentry, | |
103 | } | |
104 | ||
105 | if (S_ISREG(inode->i_mode) && | |
106 | - attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { | |
107 | + attr->ia_valid & ATTR_SIZE && | |
108 | + (attr->ia_size < inode->i_size || | |
109 | + (EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))) { | |
110 | handle_t *handle; | |
111 | ||
112 | handle = ext4_journal_start(inode, 3); | |
113 | @@ -5376,6 +5380,9 @@ int ext4_setattr(struct dentry *dentry, | |
114 | goto err_out; | |
115 | } | |
116 | } | |
117 | + /* ext4_truncate will clear the flag */ | |
118 | + if ((EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) | |
119 | + ext4_truncate(inode); | |
120 | } | |
121 | ||
122 | rc = inode_setattr(inode, attr); | |
123 | --- a/fs/ext4/ioctl.c | |
124 | +++ b/fs/ext4/ioctl.c | |
125 | @@ -92,6 +92,15 @@ long ext4_ioctl(struct file *filp, unsig | |
126 | flags &= ~EXT4_EXTENTS_FL; | |
127 | } | |
128 | ||
129 | + if (flags & EXT4_EOFBLOCKS_FL) { | |
130 | + /* we don't support adding EOFBLOCKS flag */ | |
131 | + if (!(oldflags & EXT4_EOFBLOCKS_FL)) { | |
132 | + err = -EOPNOTSUPP; | |
133 | + goto flags_out; | |
134 | + } | |
135 | + } else if (oldflags & EXT4_EOFBLOCKS_FL) | |
136 | + ext4_truncate(inode); | |
137 | + | |
138 | handle = ext4_journal_start(inode, 1); | |
139 | if (IS_ERR(handle)) { | |
140 | err = PTR_ERR(handle); |