mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 14:04:54 +02:00
ANDROID: sync encrypt+casefold support with patches going upstream
The following patches are now queued in f2fs/dev for 5.11, but android-mainline and android12-5.4 have an old version of them: libfs: Add generic function for setting dentry_ops fscrypt: Have filesystems handle their d_ops f2fs: Handle casefolding with Encryption Get them up-to-date. Link: https://lore.kernel.org/linux-f2fs-devel/20201119060904.463807-1-drosen@google.com Bug: 161184936 Test: kvm-xfstests -c f2fs/default,f2fs/encrypt -g casefold Change-Id: I359bf4f23631c1b8175de8d5f12d0787fd7f42bd Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
parent
ffea51ec1f
commit
35693714b7
|
|
@ -3427,10 +3427,6 @@ static inline void ext4_unlock_group(struct super_block *sb,
|
|||
/* dir.c */
|
||||
extern const struct file_operations ext4_dir_operations;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
extern const struct dentry_operations ext4_dentry_ops;
|
||||
#endif
|
||||
|
||||
/* file.c */
|
||||
extern const struct inode_operations ext4_file_inode_operations;
|
||||
extern const struct file_operations ext4_file_operations;
|
||||
|
|
|
|||
|
|
@ -1697,7 +1697,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
|
|||
struct buffer_head *bh;
|
||||
|
||||
err = ext4_fname_prepare_lookup(dir, dentry, &fname);
|
||||
generic_set_encrypted_ci_d_ops(dir, dentry);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
return NULL;
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -207,8 +207,10 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir,
|
|||
/*
|
||||
* Test whether a case-insensitive directory entry matches the filename
|
||||
* being searched for.
|
||||
*
|
||||
* Returns 1 for a match, 0 for no match, and -errno on an error.
|
||||
*/
|
||||
static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
||||
static int f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
const struct super_block *sb = dir->i_sb;
|
||||
|
|
@ -222,11 +224,11 @@ static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
|||
FSTR_INIT((u8 *)de_name, de_name_len);
|
||||
|
||||
if (WARN_ON_ONCE(!fscrypt_has_encryption_key(dir)))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
|
||||
if (!decrypted_name.name)
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
res = fscrypt_fname_disk_to_usr(dir, 0, 0, &encrypted_name,
|
||||
&decrypted_name);
|
||||
if (res < 0)
|
||||
|
|
@ -236,23 +238,24 @@ static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
|||
}
|
||||
|
||||
res = utf8_strncasecmp_folded(um, name, &entry);
|
||||
if (res < 0) {
|
||||
/*
|
||||
* In strict mode, ignore invalid names. In non-strict mode,
|
||||
* fall back to treating them as opaque byte sequences.
|
||||
*/
|
||||
if (sb_has_strict_encoding(sb) || name->len != entry.len)
|
||||
res = 1;
|
||||
else
|
||||
res = memcmp(name->name, entry.name, name->len);
|
||||
/*
|
||||
* In strict mode, ignore invalid names. In non-strict mode,
|
||||
* fall back to treating them as opaque byte sequences.
|
||||
*/
|
||||
if (res < 0 && !sb_has_strict_encoding(sb)) {
|
||||
res = name->len == entry.len &&
|
||||
memcmp(name->name, entry.name, name->len) == 0;
|
||||
} else {
|
||||
/* utf8_strncasecmp_folded returns 0 on match */
|
||||
res = (res == 0);
|
||||
}
|
||||
out:
|
||||
kfree(decrypted_name.name);
|
||||
return res == 0;
|
||||
return res;
|
||||
}
|
||||
#endif /* CONFIG_UNICODE */
|
||||
|
||||
static inline bool f2fs_match_name(const struct inode *dir,
|
||||
static inline int f2fs_match_name(const struct inode *dir,
|
||||
const struct f2fs_filename *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
|
|
@ -279,6 +282,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
|
|||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
int max_len = 0;
|
||||
int res = 0;
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
|
|
@ -296,10 +300,15 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (de->hash_code == fname->hash &&
|
||||
f2fs_match_name(d->inode, fname, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len)))
|
||||
goto found;
|
||||
if (de->hash_code == fname->hash) {
|
||||
res = f2fs_match_name(d->inode, fname,
|
||||
d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len));
|
||||
if (res < 0)
|
||||
return ERR_PTR(res);
|
||||
if (res)
|
||||
goto found;
|
||||
}
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
*max_slots = max_len;
|
||||
|
|
@ -349,7 +358,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
|||
}
|
||||
|
||||
de = find_in_block(dir, dentry_page, fname, &max_slots);
|
||||
if (de) {
|
||||
if (IS_ERR(de)) {
|
||||
*res_page = ERR_CAST(de);
|
||||
de = NULL;
|
||||
break;
|
||||
} else if (de) {
|
||||
*res_page = dentry_page;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,10 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
|
|||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
de = f2fs_find_target_dentry(&d, fname, NULL);
|
||||
unlock_page(ipage);
|
||||
if (IS_ERR(de)) {
|
||||
*res_page = ERR_CAST(de);
|
||||
de = NULL;
|
||||
}
|
||||
if (de)
|
||||
*res_page = ipage;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
err = f2fs_prepare_lookup(dir, dentry, &fname);
|
||||
generic_set_encrypted_ci_d_ops(dir, dentry);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
goto out_splice;
|
||||
if (err)
|
||||
|
|
|
|||
46
fs/libfs.c
46
fs/libfs.c
|
|
@ -1464,7 +1464,7 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE) && IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && defined(CONFIG_UNICODE)
|
||||
static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
|
|
@ -1474,28 +1474,48 @@ static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
|
|||
|
||||
/**
|
||||
* generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry
|
||||
* @dir: parent of dentry whose ops to set
|
||||
* @dentry: detnry to set ops on
|
||||
* @dentry: dentry to set ops on
|
||||
*
|
||||
* This function sets the dentry ops for the given dentry to handle both
|
||||
* casefolding and encryption of the dentry name.
|
||||
* Casefolded directories need d_hash and d_compare set, so that the dentries
|
||||
* contained in them are handled case-insensitively. Note that these operations
|
||||
* are needed on the parent directory rather than on the dentries in it, and
|
||||
* while the casefolding flag can be toggled on and off on an empty directory,
|
||||
* dentry_operations can't be changed later. As a result, if the filesystem has
|
||||
* casefolding support enabled at all, we have to give all dentries the
|
||||
* casefolding operations even if their inode doesn't have the casefolding flag
|
||||
* currently (and thus the casefolding ops would be no-ops for now).
|
||||
*
|
||||
* Encryption works differently in that the only dentry operation it needs is
|
||||
* d_revalidate, which it only needs on dentries that have the no-key name flag.
|
||||
* The no-key flag can't be set "later", so we don't have to worry about that.
|
||||
*
|
||||
* Finally, to maximize compatibility with overlayfs (which isn't compatible
|
||||
* with certain dentry operations) and to avoid taking an unnecessary
|
||||
* performance hit, we use custom dentry_operations for each possible
|
||||
* combination rather than always installing all operations.
|
||||
*/
|
||||
void generic_set_encrypted_ci_d_ops(struct inode *dir, struct dentry *dentry)
|
||||
void generic_set_encrypted_ci_d_ops(struct dentry *dentry)
|
||||
{
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (dentry->d_flags & DCACHE_NOKEY_NAME) {
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (dir->i_sb->s_encoding) {
|
||||
d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops);
|
||||
return;
|
||||
}
|
||||
bool needs_encrypt_ops = dentry->d_flags & DCACHE_NOKEY_NAME;
|
||||
#endif
|
||||
#ifdef CONFIG_UNICODE
|
||||
bool needs_ci_ops = dentry->d_sb->s_encoding;
|
||||
#endif
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && defined(CONFIG_UNICODE)
|
||||
if (needs_encrypt_ops && needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (needs_encrypt_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (dir->i_sb->s_encoding) {
|
||||
if (needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_ci_dentry_ops);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,6 @@ static int dbg_check_name(const struct ubifs_info *c,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ubifs_set_d_ops(struct inode *dir, struct dentry *dentry);
|
||||
static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
|
@ -204,7 +203,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
|
||||
|
||||
err = fscrypt_prepare_lookup(dir, dentry, &nm);
|
||||
ubifs_set_d_ops(dir, dentry);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
return d_splice_alias(NULL, dentry);
|
||||
if (err)
|
||||
|
|
@ -1651,19 +1650,3 @@ const struct file_operations ubifs_dir_operations = {
|
|||
.compat_ioctl = ubifs_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
static const struct dentry_operations ubifs_encrypted_dentry_ops = {
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void ubifs_set_d_ops(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (dentry->d_flags & DCACHE_NOKEY_NAME) {
|
||||
d_set_d_op(dentry, &ubifs_encrypted_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3202,8 +3202,7 @@ extern int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
|
|||
extern int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
const char *str, const struct qstr *name);
|
||||
#endif
|
||||
extern void generic_set_encrypted_ci_d_ops(struct inode *dir,
|
||||
struct dentry *dentry);
|
||||
extern void generic_set_encrypted_ci_d_ops(struct dentry *dentry);
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
extern int buffer_migrate_page(struct address_space *,
|
||||
|
|
|
|||
|
|
@ -774,9 +774,11 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir,
|
|||
* directory's encryption key is available, then the lookup is assumed to be by
|
||||
* plaintext name; otherwise, it is assumed to be by no-key name.
|
||||
*
|
||||
* After calling this function, a filesystem should ensure that its dentry
|
||||
* operations contain fscrypt_d_revalidate if DCACHE_NOKEY_NAME was set,
|
||||
* so that the dentry can be invalidated if the key is later added.
|
||||
* This will set DCACHE_NOKEY_NAME on the dentry if the lookup is by no-key
|
||||
* name. In this case the filesystem must assign the dentry a dentry_operations
|
||||
* which contains fscrypt_d_revalidate (or contains a d_revalidate method that
|
||||
* calls fscrypt_d_revalidate), so that the dentry will be invalidated if the
|
||||
* directory's encryption key is later added.
|
||||
*
|
||||
* Return: 0 on success; -ENOENT if the directory's key is unavailable but the
|
||||
* filename isn't a valid no-key name, so a negative dentry should be created;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user