f2fs: split f2fs_d_compare() from f2fs_match_name()
Sharing f2fs_ci_compare() between comparing cached dentries (f2fs_d_compare()) and comparing on-disk dentries (f2fs_match_name()) doesn't work as well as intended, as these actions fundamentally differ in several ways (e.g. whether the task may sleep, whether the directory is stable, whether the casefolded name was precomputed, whether the dentry will need to be decrypted once we allow casefold+encrypt, etc.) Just make f2fs_d_compare() implement what it needs directly, and rework f2fs_ci_compare() to be specialized for f2fs_match_name(). Signed-off-by: Eric Biggers <ebiggers@google.com> Reviewed-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
ff5f85c8d6
commit
f874fa1c7c
|
@ -107,36 +107,28 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir,
|
||||||
/*
|
/*
|
||||||
* Test whether a case-insensitive directory entry matches the filename
|
* Test whether a case-insensitive directory entry matches the filename
|
||||||
* being searched for.
|
* being searched for.
|
||||||
*
|
|
||||||
* Returns: 0 if the directory entry matches, more than 0 if it
|
|
||||||
* doesn't match or less than zero on error.
|
|
||||||
*/
|
*/
|
||||||
int f2fs_ci_compare(const struct inode *parent, const struct qstr *name,
|
static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
||||||
const struct qstr *entry, bool quick)
|
const struct qstr *entry, bool quick)
|
||||||
{
|
{
|
||||||
const struct f2fs_sb_info *sbi = F2FS_SB(parent->i_sb);
|
const struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||||
const struct unicode_map *um = sbi->s_encoding;
|
const struct unicode_map *um = sbi->s_encoding;
|
||||||
int ret;
|
int res;
|
||||||
|
|
||||||
if (quick)
|
if (quick)
|
||||||
ret = utf8_strncasecmp_folded(um, name, entry);
|
res = utf8_strncasecmp_folded(um, name, entry);
|
||||||
else
|
else
|
||||||
ret = utf8_strncasecmp(um, name, entry);
|
res = utf8_strncasecmp(um, name, entry);
|
||||||
|
if (res < 0) {
|
||||||
if (ret < 0) {
|
/*
|
||||||
/* Handle invalid character sequence as either an error
|
* In strict mode, ignore invalid names. In non-strict mode,
|
||||||
* or as an opaque byte sequence.
|
* fall back to treating them as opaque byte sequences.
|
||||||
*/
|
*/
|
||||||
if (f2fs_has_strict_mode(sbi))
|
if (f2fs_has_strict_mode(sbi) || name->len != entry->len)
|
||||||
return -EINVAL;
|
return false;
|
||||||
|
return !memcmp(name->name, entry->name, name->len);
|
||||||
if (name->len != entry->len)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return !!memcmp(name->name, entry->name, name->len);
|
|
||||||
}
|
}
|
||||||
|
return res == 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void f2fs_fname_setup_ci_filename(struct inode *dir,
|
static void f2fs_fname_setup_ci_filename(struct inode *dir,
|
||||||
|
@ -188,10 +180,10 @@ static inline bool f2fs_match_name(struct f2fs_dentry_ptr *d,
|
||||||
if (cf_str->name) {
|
if (cf_str->name) {
|
||||||
struct qstr cf = {.name = cf_str->name,
|
struct qstr cf = {.name = cf_str->name,
|
||||||
.len = cf_str->len};
|
.len = cf_str->len};
|
||||||
return !f2fs_ci_compare(parent, &cf, &entry, true);
|
return f2fs_match_ci_name(parent, &cf, &entry, true);
|
||||||
}
|
}
|
||||||
return !f2fs_ci_compare(parent, fname->usr_fname, &entry,
|
return f2fs_match_ci_name(parent, fname->usr_fname, &entry,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (fscrypt_match_name(fname, d->filename[bit_pos],
|
if (fscrypt_match_name(fname, d->filename[bit_pos],
|
||||||
|
@ -1080,17 +1072,25 @@ const struct file_operations f2fs_dir_operations = {
|
||||||
static int f2fs_d_compare(const struct dentry *dentry, unsigned int len,
|
static int f2fs_d_compare(const struct dentry *dentry, unsigned int len,
|
||||||
const char *str, const struct qstr *name)
|
const char *str, const struct qstr *name)
|
||||||
{
|
{
|
||||||
struct qstr qstr = {.name = str, .len = len };
|
|
||||||
const struct dentry *parent = READ_ONCE(dentry->d_parent);
|
const struct dentry *parent = READ_ONCE(dentry->d_parent);
|
||||||
const struct inode *inode = READ_ONCE(parent->d_inode);
|
const struct inode *dir = READ_ONCE(parent->d_inode);
|
||||||
|
const struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
|
||||||
|
struct qstr entry = QSTR_INIT(str, len);
|
||||||
|
int res;
|
||||||
|
|
||||||
if (!inode || !IS_CASEFOLDED(inode)) {
|
if (!dir || !IS_CASEFOLDED(dir))
|
||||||
if (len != name->len)
|
goto fallback;
|
||||||
return -1;
|
|
||||||
return memcmp(str, name->name, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return f2fs_ci_compare(inode, name, &qstr, false);
|
res = utf8_strncasecmp(sbi->s_encoding, name, &entry);
|
||||||
|
if (res >= 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (f2fs_has_strict_mode(sbi))
|
||||||
|
return -EINVAL;
|
||||||
|
fallback:
|
||||||
|
if (len != name->len)
|
||||||
|
return 1;
|
||||||
|
return !!memcmp(str, name->name, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_d_hash(const struct dentry *dentry, struct qstr *str)
|
static int f2fs_d_hash(const struct dentry *dentry, struct qstr *str)
|
||||||
|
|
|
@ -3142,11 +3142,6 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name,
|
||||||
bool hot, bool set);
|
bool hot, bool set);
|
||||||
struct dentry *f2fs_get_parent(struct dentry *child);
|
struct dentry *f2fs_get_parent(struct dentry *child);
|
||||||
|
|
||||||
extern int f2fs_ci_compare(const struct inode *parent,
|
|
||||||
const struct qstr *name,
|
|
||||||
const struct qstr *entry,
|
|
||||||
bool quick);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dir.c
|
* dir.c
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue