]>
Commit | Line | Data |
---|---|---|
c2a518a0 GKH |
1 | From foo@baz Fri Jan 18 09:16:11 CET 2019 |
2 | From: Chao Yu <chao2.yu@samsung.com> | |
3 | Date: Mon, 22 Feb 2016 18:29:18 +0800 | |
4 | Subject: f2fs: fix to convert inline directory correctly | |
5 | ||
6 | From: Chao Yu <chao2.yu@samsung.com> | |
7 | ||
8 | With below serials, we will lose parts of dirents: | |
9 | ||
10 | 1) mount f2fs with inline_dentry option | |
11 | 2) echo 1 > /sys/fs/f2fs/sdX/dir_level | |
12 | 3) mkdir dir | |
13 | 4) touch 180 files named [1-180] in dir | |
14 | 5) touch 181 in dir | |
15 | 6) echo 3 > /proc/sys/vm/drop_caches | |
16 | 7) ll dir | |
17 | ||
18 | ls: cannot access 2: No such file or directory | |
19 | ls: cannot access 4: No such file or directory | |
20 | ls: cannot access 5: No such file or directory | |
21 | ls: cannot access 6: No such file or directory | |
22 | ls: cannot access 8: No such file or directory | |
23 | ls: cannot access 9: No such file or directory | |
24 | ... | |
25 | total 360 | |
26 | drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./ | |
27 | drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../ | |
28 | -rw-r--r-- 1 root root 0 Feb 19 15:12 1 | |
29 | -rw-r--r-- 1 root root 0 Feb 19 15:12 10 | |
30 | -rw-r--r-- 1 root root 0 Feb 19 15:12 100 | |
31 | -????????? ? ? ? ? ? 101 | |
32 | -????????? ? ? ? ? ? 102 | |
33 | -????????? ? ? ? ? ? 103 | |
34 | ... | |
35 | ||
36 | The reason is: when doing the inline dir conversion, we didn't consider | |
37 | that directory has hierarchical hash structure which can be configured | |
38 | through sysfs interface 'dir_level'. | |
39 | ||
40 | By default, dir_level of directory inode is 0, it means we have one bucket | |
41 | in hash table located in first level, all dirents will be hashed in this | |
42 | bucket, so it has no problem for us to do the duplication simply between | |
43 | inline dentry page and converted normal dentry page. | |
44 | ||
45 | However, if we configured dir_level with the value N (greater than 0), it | |
46 | will expand the bucket number of first level hash table by 2^N - 1, it | |
47 | hashs dirents into different buckets according their hash value, if we | |
48 | still move all dirents to first bucket, it makes incorrent locating for | |
49 | inline dirents, the result is, although we can iterate all dirents through | |
50 | ->readdir, we can't stat some of them in ->lookup which based on hash | |
51 | table searching. | |
52 | ||
53 | This patch fixes this issue by rehashing dirents into correct position | |
54 | when converting inline directory. | |
55 | ||
56 | Signed-off-by: Chao Yu <chao2.yu@samsung.com> | |
57 | Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> | |
58 | [bwh: Backported to 4.4: | |
59 | - Keep using f2fs_crypto functions instead of generic fscrypt API | |
60 | - Use remove_dirty_dir_inode() instead of remove_dirty_inode() | |
61 | - Adjust context] | |
62 | Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> | |
63 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
64 | --- | |
65 | fs/f2fs/dir.c | 87 +++++++++++++++++++++++--------------------- | |
66 | fs/f2fs/f2fs.h | 4 +- | |
67 | fs/f2fs/inline.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++- | |
68 | include/linux/f2fs_fs.h | 2 + | |
69 | 4 files changed, 144 insertions(+), 43 deletions(-) | |
70 | ||
71 | --- a/fs/f2fs/dir.c | |
72 | +++ b/fs/f2fs/dir.c | |
73 | @@ -48,7 +48,6 @@ unsigned char f2fs_filetype_table[F2FS_F | |
74 | [F2FS_FT_SYMLINK] = DT_LNK, | |
75 | }; | |
76 | ||
77 | -#define S_SHIFT 12 | |
78 | static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { | |
79 | [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, | |
80 | [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, | |
81 | @@ -64,6 +63,13 @@ void set_de_type(struct f2fs_dir_entry * | |
82 | de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; | |
83 | } | |
84 | ||
85 | +unsigned char get_de_type(struct f2fs_dir_entry *de) | |
86 | +{ | |
87 | + if (de->file_type < F2FS_FT_MAX) | |
88 | + return f2fs_filetype_table[de->file_type]; | |
89 | + return DT_UNKNOWN; | |
90 | +} | |
91 | + | |
92 | static unsigned long dir_block_index(unsigned int level, | |
93 | int dir_level, unsigned int idx) | |
94 | { | |
95 | @@ -519,11 +525,7 @@ void f2fs_update_dentry(nid_t ino, umode | |
96 | test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); | |
97 | } | |
98 | ||
99 | -/* | |
100 | - * Caller should grab and release a rwsem by calling f2fs_lock_op() and | |
101 | - * f2fs_unlock_op(). | |
102 | - */ | |
103 | -int __f2fs_add_link(struct inode *dir, const struct qstr *name, | |
104 | +int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, | |
105 | struct inode *inode, nid_t ino, umode_t mode) | |
106 | { | |
107 | unsigned int bit_pos; | |
108 | @@ -536,28 +538,11 @@ int __f2fs_add_link(struct inode *dir, c | |
109 | struct f2fs_dentry_block *dentry_blk = NULL; | |
110 | struct f2fs_dentry_ptr d; | |
111 | struct page *page = NULL; | |
112 | - struct f2fs_filename fname; | |
113 | - struct qstr new_name; | |
114 | - int slots, err; | |
115 | - | |
116 | - err = f2fs_fname_setup_filename(dir, name, 0, &fname); | |
117 | - if (err) | |
118 | - return err; | |
119 | - | |
120 | - new_name.name = fname_name(&fname); | |
121 | - new_name.len = fname_len(&fname); | |
122 | - | |
123 | - if (f2fs_has_inline_dentry(dir)) { | |
124 | - err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); | |
125 | - if (!err || err != -EAGAIN) | |
126 | - goto out; | |
127 | - else | |
128 | - err = 0; | |
129 | - } | |
130 | + int slots, err = 0; | |
131 | ||
132 | level = 0; | |
133 | - slots = GET_DENTRY_SLOTS(new_name.len); | |
134 | - dentry_hash = f2fs_dentry_hash(&new_name, NULL); | |
135 | + slots = GET_DENTRY_SLOTS(new_name->len); | |
136 | + dentry_hash = f2fs_dentry_hash(new_name, NULL); | |
137 | ||
138 | current_depth = F2FS_I(dir)->i_current_depth; | |
139 | if (F2FS_I(dir)->chash == dentry_hash) { | |
140 | @@ -566,10 +551,8 @@ int __f2fs_add_link(struct inode *dir, c | |
141 | } | |
142 | ||
143 | start: | |
144 | - if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) { | |
145 | - err = -ENOSPC; | |
146 | - goto out; | |
147 | - } | |
148 | + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) | |
149 | + return -ENOSPC; | |
150 | ||
151 | /* Increase the depth, if required */ | |
152 | if (level == current_depth) | |
153 | @@ -583,10 +566,8 @@ start: | |
154 | ||
155 | for (block = bidx; block <= (bidx + nblock - 1); block++) { | |
156 | dentry_page = get_new_data_page(dir, NULL, block, true); | |
157 | - if (IS_ERR(dentry_page)) { | |
158 | - err = PTR_ERR(dentry_page); | |
159 | - goto out; | |
160 | - } | |
161 | + if (IS_ERR(dentry_page)) | |
162 | + return PTR_ERR(dentry_page); | |
163 | ||
164 | dentry_blk = kmap(dentry_page); | |
165 | bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, | |
166 | @@ -606,7 +587,7 @@ add_dentry: | |
167 | ||
168 | if (inode) { | |
169 | down_write(&F2FS_I(inode)->i_sem); | |
170 | - page = init_inode_metadata(inode, dir, &new_name, NULL); | |
171 | + page = init_inode_metadata(inode, dir, new_name, NULL); | |
172 | if (IS_ERR(page)) { | |
173 | err = PTR_ERR(page); | |
174 | goto fail; | |
175 | @@ -616,7 +597,7 @@ add_dentry: | |
176 | } | |
177 | ||
178 | make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); | |
179 | - f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos); | |
180 | + f2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos); | |
181 | ||
182 | set_page_dirty(dentry_page); | |
183 | ||
184 | @@ -638,7 +619,34 @@ fail: | |
185 | } | |
186 | kunmap(dentry_page); | |
187 | f2fs_put_page(dentry_page, 1); | |
188 | -out: | |
189 | + | |
190 | + return err; | |
191 | +} | |
192 | + | |
193 | +/* | |
194 | + * Caller should grab and release a rwsem by calling f2fs_lock_op() and | |
195 | + * f2fs_unlock_op(). | |
196 | + */ | |
197 | +int __f2fs_add_link(struct inode *dir, const struct qstr *name, | |
198 | + struct inode *inode, nid_t ino, umode_t mode) | |
199 | +{ | |
200 | + struct f2fs_filename fname; | |
201 | + struct qstr new_name; | |
202 | + int err; | |
203 | + | |
204 | + err = f2fs_fname_setup_filename(dir, name, 0, &fname); | |
205 | + if (err) | |
206 | + return err; | |
207 | + | |
208 | + new_name.name = fname_name(&fname); | |
209 | + new_name.len = fname_len(&fname); | |
210 | + | |
211 | + err = -EAGAIN; | |
212 | + if (f2fs_has_inline_dentry(dir)) | |
213 | + err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); | |
214 | + if (err == -EAGAIN) | |
215 | + err = f2fs_add_regular_entry(dir, &new_name, inode, ino, mode); | |
216 | + | |
217 | f2fs_fname_free_filename(&fname); | |
218 | return err; | |
219 | } | |
220 | @@ -792,10 +800,7 @@ bool f2fs_fill_dentries(struct dir_conte | |
221 | break; | |
222 | ||
223 | de = &d->dentry[bit_pos]; | |
224 | - if (de->file_type < F2FS_FT_MAX) | |
225 | - d_type = f2fs_filetype_table[de->file_type]; | |
226 | - else | |
227 | - d_type = DT_UNKNOWN; | |
228 | + d_type = get_de_type(de); | |
229 | ||
230 | de_name.name = d->filename[bit_pos]; | |
231 | de_name.len = le16_to_cpu(de->name_len); | |
232 | --- a/fs/f2fs/f2fs.h | |
233 | +++ b/fs/f2fs/f2fs.h | |
234 | @@ -1677,7 +1677,7 @@ struct dentry *f2fs_get_parent(struct de | |
235 | */ | |
236 | extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; | |
237 | void set_de_type(struct f2fs_dir_entry *, umode_t); | |
238 | - | |
239 | +unsigned char get_de_type(struct f2fs_dir_entry *); | |
240 | struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *, | |
241 | f2fs_hash_t, int *, struct f2fs_dentry_ptr *); | |
242 | bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, | |
243 | @@ -1698,6 +1698,8 @@ void f2fs_set_link(struct inode *, struc | |
244 | int update_dent_inode(struct inode *, struct inode *, const struct qstr *); | |
245 | void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, | |
246 | const struct qstr *, f2fs_hash_t , unsigned int); | |
247 | +int f2fs_add_regular_entry(struct inode *, const struct qstr *, | |
248 | + struct inode *, nid_t, umode_t); | |
249 | int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, | |
250 | umode_t); | |
251 | void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, | |
252 | --- a/fs/f2fs/inline.c | |
253 | +++ b/fs/f2fs/inline.c | |
254 | @@ -367,7 +367,7 @@ int make_empty_inline_dir(struct inode * | |
255 | * NOTE: ipage is grabbed by caller, but if any error occurs, we should | |
256 | * release ipage in this function. | |
257 | */ | |
258 | -static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, | |
259 | +static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, | |
260 | struct f2fs_inline_dentry *inline_dentry) | |
261 | { | |
262 | struct page *page; | |
263 | @@ -428,6 +428,98 @@ out: | |
264 | return err; | |
265 | } | |
266 | ||
267 | +static int f2fs_add_inline_entries(struct inode *dir, | |
268 | + struct f2fs_inline_dentry *inline_dentry) | |
269 | +{ | |
270 | + struct f2fs_dentry_ptr d; | |
271 | + unsigned long bit_pos = 0; | |
272 | + int err = 0; | |
273 | + | |
274 | + make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); | |
275 | + | |
276 | + while (bit_pos < d.max) { | |
277 | + struct f2fs_dir_entry *de; | |
278 | + struct qstr new_name; | |
279 | + nid_t ino; | |
280 | + umode_t fake_mode; | |
281 | + | |
282 | + if (!test_bit_le(bit_pos, d.bitmap)) { | |
283 | + bit_pos++; | |
284 | + continue; | |
285 | + } | |
286 | + | |
287 | + de = &d.dentry[bit_pos]; | |
288 | + new_name.name = d.filename[bit_pos]; | |
289 | + new_name.len = de->name_len; | |
290 | + | |
291 | + ino = le32_to_cpu(de->ino); | |
292 | + fake_mode = get_de_type(de) << S_SHIFT; | |
293 | + | |
294 | + err = f2fs_add_regular_entry(dir, &new_name, NULL, | |
295 | + ino, fake_mode); | |
296 | + if (err) | |
297 | + goto punch_dentry_pages; | |
298 | + | |
299 | + if (unlikely(!de->name_len)) | |
300 | + d.max = -1; | |
301 | + | |
302 | + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); | |
303 | + } | |
304 | + return 0; | |
305 | +punch_dentry_pages: | |
306 | + truncate_inode_pages(&dir->i_data, 0); | |
307 | + truncate_blocks(dir, 0, false); | |
308 | + remove_dirty_dir_inode(dir); | |
309 | + return err; | |
310 | +} | |
311 | + | |
312 | +static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, | |
313 | + struct f2fs_inline_dentry *inline_dentry) | |
314 | +{ | |
315 | + struct f2fs_inline_dentry *backup_dentry; | |
316 | + int err; | |
317 | + | |
318 | + backup_dentry = kmalloc(sizeof(struct f2fs_inline_dentry), | |
319 | + GFP_F2FS_ZERO); | |
320 | + if (!backup_dentry) | |
321 | + return -ENOMEM; | |
322 | + | |
323 | + memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); | |
324 | + truncate_inline_inode(ipage, 0); | |
325 | + | |
326 | + unlock_page(ipage); | |
327 | + | |
328 | + err = f2fs_add_inline_entries(dir, backup_dentry); | |
329 | + if (err) | |
330 | + goto recover; | |
331 | + | |
332 | + lock_page(ipage); | |
333 | + | |
334 | + stat_dec_inline_dir(dir); | |
335 | + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); | |
336 | + update_inode(dir, ipage); | |
337 | + kfree(backup_dentry); | |
338 | + return 0; | |
339 | +recover: | |
340 | + lock_page(ipage); | |
341 | + memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); | |
342 | + i_size_write(dir, MAX_INLINE_DATA); | |
343 | + update_inode(dir, ipage); | |
344 | + f2fs_put_page(ipage, 1); | |
345 | + | |
346 | + kfree(backup_dentry); | |
347 | + return err; | |
348 | +} | |
349 | + | |
350 | +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, | |
351 | + struct f2fs_inline_dentry *inline_dentry) | |
352 | +{ | |
353 | + if (!F2FS_I(dir)->i_dir_level) | |
354 | + return f2fs_move_inline_dirents(dir, ipage, inline_dentry); | |
355 | + else | |
356 | + return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry); | |
357 | +} | |
358 | + | |
359 | int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, | |
360 | struct inode *inode, nid_t ino, umode_t mode) | |
361 | { | |
362 | --- a/include/linux/f2fs_fs.h | |
363 | +++ b/include/linux/f2fs_fs.h | |
364 | @@ -497,4 +497,6 @@ enum { | |
365 | F2FS_FT_MAX | |
366 | }; | |
367 | ||
368 | +#define S_SHIFT 12 | |
369 | + | |
370 | #endif /* _LINUX_F2FS_FS_H */ |