Skip to content

Commit 91b587b

Browse files
Daniel LeeJaegeuk Kim
authored andcommitted
f2fs: Introduce linear search for dentries
This patch addresses an issue where some files in case-insensitive directories become inaccessible due to changes in how the kernel function, utf8_casefold(), generates case-folded strings from the commit 5c26d2f ("unicode: Don't special case ignorable code points"). F2FS uses these case-folded names to calculate hash values for locating dentries and stores them on disk. Since utf8_casefold() can produce different output across kernel versions, stored hash values and newly calculated hash values may differ. This results in affected files no longer being found via the hash-based lookup. To resolve this, the patch introduces a linear search fallback. If the initial hash-based search fails, F2FS will sequentially scan the directory entries. Fixes: 5c26d2f ("unicode: Don't special case ignorable code points") Link: https://bugzilla.kernel.org/show_bug.cgi?id=219586 Signed-off-by: Daniel Lee <[email protected]> Reviewed-by: Chao Yu <[email protected]> Signed-off-by: Jaegeuk Kim <[email protected]>
1 parent d217b5c commit 91b587b

File tree

3 files changed

+45
-19
lines changed

3 files changed

+45
-19
lines changed

fs/f2fs/dir.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,16 @@ static unsigned long dir_block_index(unsigned int level,
175175
static struct f2fs_dir_entry *find_in_block(struct inode *dir,
176176
struct page *dentry_page,
177177
const struct f2fs_filename *fname,
178-
int *max_slots)
178+
int *max_slots,
179+
bool use_hash)
179180
{
180181
struct f2fs_dentry_block *dentry_blk;
181182
struct f2fs_dentry_ptr d;
182183

183184
dentry_blk = (struct f2fs_dentry_block *)page_address(dentry_page);
184185

185186
make_dentry_ptr_block(dir, &d, dentry_blk);
186-
return f2fs_find_target_dentry(&d, fname, max_slots);
187+
return f2fs_find_target_dentry(&d, fname, max_slots, use_hash);
187188
}
188189

189190
static inline int f2fs_match_name(const struct inode *dir,
@@ -208,7 +209,8 @@ static inline int f2fs_match_name(const struct inode *dir,
208209
}
209210

210211
struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
211-
const struct f2fs_filename *fname, int *max_slots)
212+
const struct f2fs_filename *fname, int *max_slots,
213+
bool use_hash)
212214
{
213215
struct f2fs_dir_entry *de;
214216
unsigned long bit_pos = 0;
@@ -231,7 +233,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
231233
continue;
232234
}
233235

234-
if (de->hash_code == fname->hash) {
236+
if (!use_hash || de->hash_code == fname->hash) {
235237
res = f2fs_match_name(d->inode, fname,
236238
d->filename[bit_pos],
237239
le16_to_cpu(de->name_len));
@@ -258,11 +260,12 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
258260
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
259261
unsigned int level,
260262
const struct f2fs_filename *fname,
261-
struct page **res_page)
263+
struct page **res_page,
264+
bool use_hash)
262265
{
263266
int s = GET_DENTRY_SLOTS(fname->disk_name.len);
264267
unsigned int nbucket, nblock;
265-
unsigned int bidx, end_block;
268+
unsigned int bidx, end_block, bucket_no;
266269
struct page *dentry_page;
267270
struct f2fs_dir_entry *de = NULL;
268271
pgoff_t next_pgofs;
@@ -272,8 +275,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
272275
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
273276
nblock = bucket_blocks(level);
274277

278+
bucket_no = use_hash ? le32_to_cpu(fname->hash) % nbucket : 0;
279+
280+
start_find_bucket:
275281
bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,
276-
le32_to_cpu(fname->hash) % nbucket);
282+
bucket_no);
277283
end_block = bidx + nblock;
278284

279285
while (bidx < end_block) {
@@ -290,7 +296,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
290296
}
291297
}
292298

293-
de = find_in_block(dir, dentry_page, fname, &max_slots);
299+
de = find_in_block(dir, dentry_page, fname, &max_slots, use_hash);
294300
if (IS_ERR(de)) {
295301
*res_page = ERR_CAST(de);
296302
de = NULL;
@@ -307,12 +313,18 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
307313
bidx++;
308314
}
309315

310-
if (!de && room && F2FS_I(dir)->chash != fname->hash) {
311-
F2FS_I(dir)->chash = fname->hash;
312-
F2FS_I(dir)->clevel = level;
313-
}
316+
if (de)
317+
return de;
314318

315-
return de;
319+
if (likely(use_hash)) {
320+
if (room && F2FS_I(dir)->chash != fname->hash) {
321+
F2FS_I(dir)->chash = fname->hash;
322+
F2FS_I(dir)->clevel = level;
323+
}
324+
} else if (++bucket_no < nbucket) {
325+
goto start_find_bucket;
326+
}
327+
return NULL;
316328
}
317329

318330
struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
@@ -323,11 +335,15 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
323335
struct f2fs_dir_entry *de = NULL;
324336
unsigned int max_depth;
325337
unsigned int level;
338+
bool use_hash = true;
326339

327340
*res_page = NULL;
328341

342+
#if IS_ENABLED(CONFIG_UNICODE)
343+
start_find_entry:
344+
#endif
329345
if (f2fs_has_inline_dentry(dir)) {
330-
de = f2fs_find_in_inline_dir(dir, fname, res_page);
346+
de = f2fs_find_in_inline_dir(dir, fname, res_page, use_hash);
331347
goto out;
332348
}
333349

@@ -343,11 +359,18 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
343359
}
344360

345361
for (level = 0; level < max_depth; level++) {
346-
de = find_in_level(dir, level, fname, res_page);
362+
de = find_in_level(dir, level, fname, res_page, use_hash);
347363
if (de || IS_ERR(*res_page))
348364
break;
349365
}
366+
350367
out:
368+
#if IS_ENABLED(CONFIG_UNICODE)
369+
if (IS_CASEFOLDED(dir) && !de && use_hash) {
370+
use_hash = false;
371+
goto start_find_entry;
372+
}
373+
#endif
351374
/* This is to increase the speed of f2fs_create */
352375
if (!de)
353376
F2FS_I(dir)->task = current;

fs/f2fs/f2fs.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3588,7 +3588,8 @@ int f2fs_prepare_lookup(struct inode *dir, struct dentry *dentry,
35883588
struct f2fs_filename *fname);
35893589
void f2fs_free_filename(struct f2fs_filename *fname);
35903590
struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
3591-
const struct f2fs_filename *fname, int *max_slots);
3591+
const struct f2fs_filename *fname, int *max_slots,
3592+
bool use_hash);
35923593
int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
35933594
unsigned int start_pos, struct fscrypt_str *fstr);
35943595
void f2fs_do_make_empty_dir(struct inode *inode, struct inode *parent,
@@ -4224,7 +4225,8 @@ int f2fs_write_inline_data(struct inode *inode, struct folio *folio);
42244225
int f2fs_recover_inline_data(struct inode *inode, struct page *npage);
42254226
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
42264227
const struct f2fs_filename *fname,
4227-
struct page **res_page);
4228+
struct page **res_page,
4229+
bool use_hash);
42284230
int f2fs_make_empty_inline_dir(struct inode *inode, struct inode *parent,
42294231
struct page *ipage);
42304232
int f2fs_add_inline_entry(struct inode *dir, const struct f2fs_filename *fname,

fs/f2fs/inline.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,8 @@ int f2fs_recover_inline_data(struct inode *inode, struct page *npage)
352352

353353
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
354354
const struct f2fs_filename *fname,
355-
struct page **res_page)
355+
struct page **res_page,
356+
bool use_hash)
356357
{
357358
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
358359
struct f2fs_dir_entry *de;
@@ -369,7 +370,7 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
369370
inline_dentry = inline_data_addr(dir, ipage);
370371

371372
make_dentry_ptr_inline(dir, &d, inline_dentry);
372-
de = f2fs_find_target_dentry(&d, fname, NULL);
373+
de = f2fs_find_target_dentry(&d, fname, NULL, use_hash);
373374
unlock_page(ipage);
374375
if (IS_ERR(de)) {
375376
*res_page = ERR_CAST(de);

0 commit comments

Comments
 (0)