backfs_sources := $(filter-out %.mod.c %/inode-test.c,$(wildcard @EXT4_SRC_DIR@/*.c))
-new_sources := mmp.c htree_lock.c
+new_sources := mmp.c htree_lock.c critical_encode.h
new_headers :=
ldiskfs_patched_sources := $(notdir $(backfs_sources) $(backfs_headers)) $(new_sources) $(new_headers)
mkdir -p linux trace/events
@echo -n "Replacing 'ext4' with 'ldiskfs':"
for i in $(notdir $(backfs_headers) $(backfs_sources)) $(new_sources) ; do \
+ [ -f linux-stage/fs/ext4/$$i ] || continue; \
echo -n " $$i" ; \
sed $(strip $(ldiskfs_sed_flags)) \
linux-stage/fs/ext4/$$i > $$i ; \
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/inode.c b/fs/ext4/inode.c
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
new_fl |= S_DAX;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
new_fl |= S_ENCRYPTED;
if (flags & EXT4_CASEFOLD_FL)
new_fl |= S_CASEFOLD;
stat->attributes |= STATX_ATTR_COMPRESSED;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & EXT4_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/inode.c b/fs/ext4/inode.c
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
new_fl |= S_ENCRYPTED;
if (flags & EXT4_CASEFOLD_FL)
new_fl |= S_CASEFOLD;
stat->attributes |= STATX_ATTR_COMPRESSED;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & EXT4_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/xattr.c b/fs/ext4/xattr.c
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
static int
ext4_xattr_list_entries(struct dentry *dentry, struct ext4_xattr_entry *entry,
-@@ -1197,6 +1197,7 @@
+@@ -1197,14 +1197,19 @@
+ ext4_handle_sync(handle);
+ }
+
++ if (!error && name_index == EXT4_XATTR_INDEX_ENCRYPTION &&
++ strcmp(name, "c") == 0)
++ EXT4_I(inode)->i_flags |= EXT4_ENCRYPT_FL;
++
+ cleanup:
+ brelse(is.iloc.bh);
+ brelse(bs.bh);
+ if (no_expand == 0)
+ ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
up_write(&EXT4_I(inode)->xattr_sem);
return error;
}
extern const struct xattr_handler ext4_xattr_acl_default_handler;
extern const struct xattr_handler ext4_xattr_security_handler;
-+#define LDISKFS_XATTR_INDEX_ENCRYPTION 9
-+#define LDISKFS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
++#define EXT4_XATTR_INDEX_ENCRYPTION 9
++#define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c"
+
extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);
extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -381,7 +378,8 @@ struct flex_groups {
+ #define EXT4_DIRTY_FL 0x00000100
+ #define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
+ #define EXT4_NOCOMPR_FL 0x00000400 /* Don't compress */
+-#define EXT4_ECOMPR_FL 0x00000800 /* Compression error */
++/* nb: was previously EXT4_ECOMPR_FL */
++#define EXT4_ENCRYPT_FL 0x00000800 /* encrypted file */
+ /* End compression flags --- maybe not all used */
+ #define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */
+ #define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */
+@@ -447,7 +445,7 @@ enum {
+ EXT4_INODE_DIRTY = 8,
+ EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */
+ EXT4_INODE_NOCOMPR = 10, /* Don't compress */
+- EXT4_INODE_ECOMPR = 11, /* Compression error */
++ EXT4_INODE_ENCRYPT = 11, /* Encrypted file */
+ /* End compression flags --- maybe not all used */
+ EXT4_INODE_INDEX = 12, /* hash-indexed directory */
+ EXT4_INODE_IMAGIC = 13, /* AFS directory */
+@@ -493,7 +491,7 @@ static inline void ext4_check_flag_va
+ CHECK_FLAG_VALUE(DIRTY);
+ CHECK_FLAG_VALUE(COMPRBLK);
+ CHECK_FLAG_VALUE(NOCOMPR);
+- CHECK_FLAG_VALUE(ECOMPR);
++ CHECK_FLAG_VALUE(ENCRYPT);
+ CHECK_FLAG_VALUE(INDEX);
+ CHECK_FLAG_VALUE(IMAGIC);
+ CHECK_FLAG_VALUE(JOURNAL_DATA);
--- /dev/null
+diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
+--- a/fs/ext4/dir.c
++++ b/fs/ext4/dir.c
+@@ -28,6 +28,7 @@
+ #include <linux/rbtree.h>
+ #include "ext4.h"
+ #include "xattr.h"
++#include "critical_encode.h"
+
+ static int ext4_dx_readdir(struct file *filp,
+ void *dirent, filldir_t filldir);
+@@ -243,12 +244,41 @@ revalidate:
+ * during the copy operation.
+ */
+ u64 version = filp->f_version;
++ int presented_len = de->name_len;
++ char *buf = de->name;
+
+- error = filldir(dirent, de->name,
+- de->name_len,
++ if (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) &&
++ EXT4_I(inode)->i_flags & EXT4_ENCRYPT_FL)
++ presented_len = critical_chars(de->name,
++ de->name_len);
++ if (unlikely(de->name_len != presented_len)) {
++ buf = kmalloc(presented_len + 1,
++ GFP_NOFS);
++ if (!buf) {
++ error = -ENOMEM;
++ break;
++ }
++ critical_encode(de->name,
++ de->name_len, buf);
++ if (presented_len > NAME_MAX) {
++ /* truncate at NAME_MAX, or
++ * NAME_MAX-1 if name ends with
++ * '=' to avoid decoding issue
++ */
++ presented_len = NAME_MAX;
++ if (buf[NAME_MAX - 1] == '=')
++ presented_len--;
++ }
++ buf[presented_len] = '\0';
++ }
++
++ error = filldir(dirent, buf,
++ presented_len,
+ filp->f_pos,
+ le32_to_cpu(de->inode),
+ get_dtype(sb, de->file_type));
++ if (unlikely(de->name_len != presented_len))
++ kfree(buf);
+ if (error)
+ break;
+ if (version != filp->f_version)
+@@ -516,10 +542,38 @@ static int call_filldir(struct file *fil
+ }
+ curr_pos = hash2pos(filp, fname->hash, fname->minor_hash);
+ while (fname) {
+- error = filldir(dirent, fname->name,
+- fname->name_len, curr_pos,
++ int presented_len = fname->name_len;
++ char *buf = fname->name;
++
++ if (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) &&
++ EXT4_I(inode)->i_flags & EXT4_ENCRYPT_FL)
++ presented_len = critical_chars(fname->name,
++ fname->name_len);
++ if (unlikely(fname->name_len != presented_len)) {
++ buf = kmalloc(presented_len + 1, GFP_NOFS);
++ if (!buf) {
++ filp->f_pos = curr_pos;
++ info->extra_fname = fname;
++ return -ENOMEM;
++ }
++ critical_encode(fname->name,
++ fname->name_len, buf);
++ if (presented_len > NAME_MAX) {
++ /* truncate at NAME_MAX, or NAME_MAX-1 if
++ * name ends with '=' to avoid decoding issue
++ */
++ presented_len = NAME_MAX;
++ if (buf[presented_len - 1] == '=')
++ presented_len--;
++ }
++ buf[presented_len] = '\0';
++ }
++ error = filldir(dirent, buf,
++ presented_len, curr_pos,
+ fname->inode,
+ get_dtype(sb, fname->file_type));
++ if (unlikely(fname->name_len != presented_len))
++ kfree(buf);
+ if (error) {
+ filp->f_pos = curr_pos;
+ info->extra_fname = fname;
+diff -wur /dev/null b/fs/ext4/critical_encode.h
+--- /dev/null
++++ b/fs/ext4/critical_encode.h
+@@ -0,0 +1,74 @@
++/*
++ * critical_encode.h
++ *
++ * Copyright (c) 2022 Whamcloud
++ */
++
++#ifndef _CRITICAL_ENCODE_H
++#define _CRITICAL_ENCODE_H
++
++#include <linux/ctype.h>
++
++/* Encoding/decoding routines inspired from yEnc principles.
++ * We just take care of a few critical characters:
++ * NULL, LF, CR, /, DEL and =.
++ * If such a char is found, it is replaced with '=' followed by
++ * the char value + 64.
++ * All other chars are left untouched.
++ * Efficiency of this encoding depends on the occurences of the
++ * critical chars, but statistically on binary data it can be much higher
++ * than base64 for instance.
++ */
++static inline int critical_encode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ /* escape NULL, LF, CR, /, DEL and = */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '=')) {
++ *(q++) = '=';
++ *(q++) = *(p++) + 64;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++/* returns the number of chars encoding would produce */
++static inline int critical_chars(const u8 *src, int len)
++{
++ u8 *p = (u8 *)src;
++ int newlen = len;
++
++ while (p - src < len) {
++ /* NULL, LF, CR, /, DEL and = cost an additional '=' */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '='))
++ newlen++;
++ p++;
++ }
++
++ return newlen;
++}
++
++/* decoding routine - returns the number of chars in output */
++static inline int critical_decode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ if (unlikely(*p == '=')) {
++ *(q++) = *(++p) - 64;
++ p++;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++#endif /* _CRITICAL_ENCODE_H */
+diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -39,6 +39,7 @@
+
+ #include "xattr.h"
+ #include "acl.h"
++#include "critical_encode.h"
+
+ #include <trace/events/ext4.h>
+ /*
+@@ -1494,9 +1494,9 @@ static void dx_insert_block(struct dx_fr
+ * `de != NULL' is guaranteed by caller.
+ */
+ static inline int ext4_match (int len, const char * const name,
+- struct ext4_dir_entry_2 * de)
++ struct ext4_dir_entry_2 * de, int denamelen)
+ {
+- if (len != de->name_len)
++ if (len != denamelen)
+ return 0;
+ if (!de->inode)
+ return 0;
+@@ -1516,18 +1516,30 @@ int search_dir(struct buffer_head *bh,
+ {
+ struct ext4_dir_entry_2 * de;
+ char * dlimit;
+- int de_len;
++ int de_len, denamelen;
+ const char *name = d_name->name;
+ int namelen = d_name->len;
++ bool probablytrunc;
+
+ de = (struct ext4_dir_entry_2 *)search_buf;
+ dlimit = search_buf + buf_size;
++ /* fname is probably truncated if it is the decoded representation of
++ * an encrypted filename not aligned on a 32-byte boundary
++ */
++ probablytrunc = !IS_LUSTRE_MOUNT(dir->i_sb) &&
++ EXT4_I(dir)->i_flags & EXT4_ENCRYPT_FL && namelen & 31;
+ while ((char *) de < dlimit) {
+ /* this code is executed quadratically often */
+ /* do minimal checking `by hand' */
+-
++ denamelen = de->name_len;
++ if (unlikely(probablytrunc) && de->name_len > namelen)
++ /* Adjust name len to look for a partial match.
++ * Since it is binary encrypted names, there
++ * should not be any collision between names.
++ */
++ denamelen = namelen;
+ if ((char *) de + namelen <= dlimit &&
+- ext4_match (namelen, name, de)) {
++ ext4_match(namelen, name, de, denamelen)) {
+ /* found a match - just to be sure, do a full check */
+ if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+ bh->b_size, offset))
+@@ -1588,8 +1589,10 @@ struct buffer_head *__ext4_find_entry
+ buffer */
+ int num = 0;
+ ext4_lblk_t nblocks;
++ char *buf = NULL;
+ int i, err = 0;
+ int namelen;
++ struct qstr qstr;
+
+ *res_dir = NULL;
+ sb = dir->i_sb;
+@@ -1597,6 +1600,24 @@ struct buffer_head *__ext4_find_entry
+ if (namelen > EXT4_NAME_LEN)
+ return NULL;
+
++ if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)) &&
++ EXT4_I(dir)->i_flags & EXT4_ENCRYPT_FL &&
++ strnchr(d_name->name, d_name->len, '=')) {
++ /* Only proceed to critical decode if
++ * iname contains escape char '='.
++ */
++ int len = d_name->len;
++
++ buf = kmalloc(len, GFP_NOFS);
++ if (!buf)
++ return ERR_PTR(-ENOMEM);
++
++ len = critical_decode(d_name->name, len, buf);
++ qstr.name = buf;
++ qstr.len = len;
++ d_name = &qstr;
++ }
++
+ if (ext4_has_inline_data(dir)) {
+ int has_inline_data = 1;
+ ret = ext4_find_inline_entry(dir, d_name, res_dir,
+@@ -1604,6 +1624,6 @@ struct buffer_head *__ext4_find_entry
+ if (has_inline_data) {
+ if (inlined)
+ *inlined = 1;
+- return ret;
++ goto out_free;
+ }
+ }
+@@ -1625,12 +1647,18 @@ struct buffer_head *__ext4_find_entry
+ * return. Otherwise, fall back to doing a search the
+ * old fashioned way.
+ */
+- if (err == -ENOENT)
+- return NULL;
++ if (err == -ENOENT) {
++ ret = NULL;
++ goto out_free;
++ }
+- if (err && err != ERR_BAD_DX_DIR)
+- return ERR_PTR(err);
++ if (err && err != ERR_BAD_DX_DIR) {
++ ret = ERR_PTR(err);
++ goto out_free;
++ }
+- if (bh)
+- return bh;
++ if (bh) {
++ ret = bh;
++ goto out_free;
++ }
+ dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
+ "falling back\n"));
+ ext4_htree_safe_relock(lck);
+@@ -1667,8 +1698,10 @@ restart:
+ num++;
+ bh = ext4_getblk(NULL, dir, b++, 0, &err);
+ if (unlikely(err)) {
+- if (ra_max == 0)
+- return ERR_PTR(err);
++ if (ra_max == 0) {
++ ret = ERR_PTR(err);
++ goto out_free;
++ }
+ break;
+ }
+ bh_use[ra_max] = bh;
+@@ -1729,6 +1763,9 @@ cleanup_and_exit:
+ /* Clean up the read-ahead blocks */
+ for (; ra_ptr < ra_max; ra_ptr++)
+ brelse(bh_use[ra_ptr]);
++out_free:
++ if (buf)
++ kfree(buf);
+ return ret;
+ }
+ EXPORT_SYMBOL(__ext4_find_entry);
+@@ -2121,7 +2128,7 @@ int ext4_find_dest_de(struct inode *d
+ if (ext4_check_dir_entry(dir, NULL, de, bh,
+ buf, buf_size, offset))
+ return -EIO;
+- if (ext4_match(namelen, name, de))
++ if (ext4_match(namelen, name, de, de->name_len))
+ return -EEXIST;
+ nlen = EXT4_DIR_REC_LEN(de);
+ rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/inode.c b/fs/ext4/inode.c
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
new_fl |= S_ENCRYPTED;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX|
stat->attributes |= STATX_ATTR_COMPRESSED;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & EXT4_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/inode.c b/fs/ext4/inode.c
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
new_fl |= S_ENCRYPTED;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX|
stat->attributes |= STATX_ATTR_COMPRESSED;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & EXT4_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
--- /dev/null
+diff -wur /dev/null b/fs/ext4/critical_encode.h
+--- /dev/null
++++ b/fs/ext4/critical_encode.h
+@@ -0,0 +1,158 @@
++/*
++ * critical_encode.h
++ *
++ * Copyright (c) 2022 Whamcloud
++ */
++
++#ifndef _CRITICAL_ENCODE_H
++#define _CRITICAL_ENCODE_H
++
++#include <linux/ctype.h>
++
++/* Encoding/decoding routines inspired from yEnc principles.
++ * We just take care of a few critical characters:
++ * NULL, LF, CR, /, DEL and =.
++ * If such a char is found, it is replaced with '=' followed by
++ * the char value + 64.
++ * All other chars are left untouched.
++ * Efficiency of this encoding depends on the occurences of the
++ * critical chars, but statistically on binary data it can be much higher
++ * than base64 for instance.
++ */
++static inline int critical_encode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ /* escape NULL, LF, CR, /, DEL and = */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '=')) {
++ *(q++) = '=';
++ *(q++) = *(p++) + 64;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++/* returns the number of chars encoding would produce */
++static inline int critical_chars(const u8 *src, int len)
++{
++ u8 *p = (u8 *)src;
++ int newlen = len;
++
++ while (p - src < len) {
++ /* NULL, LF, CR, /, DEL and = cost an additional '=' */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '='))
++ newlen++;
++ p++;
++ }
++
++ return newlen;
++}
++
++/* decoding routine - returns the number of chars in output */
++static inline int critical_decode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ if (unlikely(*p == '=')) {
++ *(q++) = *(++p) - 64;
++ p++;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++#define fscrypt_get_encryption_info(inode) \
++ (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) ? 0 : -EOPNOTSUPP)
++
++static inline int ext4_prepare_lookup(struct inode *dir,
++ struct dentry *dentry,
++ unsigned int flags)
++{
++ if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)))
++ return 0;
++ return fscrypt_prepare_lookup(dir, dentry, flags);
++}
++
++static inline int ext4_fname_alloc_buffer(const struct inode *inode,
++ u32 max_encrypted_len,
++ struct fscrypt_str *crypto_str)
++{
++ crypto_str->name = kmalloc(max_encrypted_len + 1, GFP_NOFS);
++ if (!crypto_str->name)
++ return -ENOMEM;
++ crypto_str->len = max_encrypted_len;
++ return 0;
++}
++
++static inline void ext4_fname_free_buffer(struct fscrypt_str *crypto_str)
++{
++ if (!crypto_str)
++ return;
++ kfree(crypto_str->name);
++ crypto_str->name = NULL;
++}
++
++static inline int ext4_fname_disk_to_usr(struct inode *inode,
++ u32 hash, u32 minor_hash,
++ const struct fscrypt_str *iname,
++ struct fscrypt_str *oname)
++{
++ int presented_len;
++
++ presented_len = critical_encode(iname->name, iname->len, oname->name);
++ if (presented_len > NAME_MAX) {
++ /* truncate at NAME_MAX,
++ * or NAME_MAX-1 if name ends with '=' to avoid decoding issue
++ */
++ presented_len = NAME_MAX;
++ if (oname->name[presented_len - 1] == '=')
++ presented_len--;
++ oname->len = presented_len;
++ }
++ oname->name[presented_len] = '\0';
++
++ return 0;
++}
++
++static inline int ext4_setup_filename(struct inode *dir,
++ const struct qstr *iname,
++ int lookup,
++ struct ext4_filename *fname)
++{
++ fname->usr_fname = iname;
++
++ if (lookup && IS_ENCRYPTED(dir) &&
++ unlikely(!IS_LUSTRE_MOUNT(dir->i_sb) &&
++ strnchr(iname->name, iname->len, '='))) {
++ /* Only proceed to critical decode if
++ * iname contains escape char '='.
++ */
++ int len = iname->len;
++ char *buf;
++
++ buf = kmalloc(len, GFP_NOFS);
++ if (!buf)
++ return -ENOMEM;
++
++ len = critical_decode(iname->name, len, buf);
++ fname->disk_name.name = (unsigned char *)buf;
++ fname->disk_name.len = len;
++ return 0;
++ }
++
++ fname->disk_name.name = (unsigned char *) iname->name;
++ fname->disk_name.len = iname->len;
++ return 0;
++}
++
++#endif /* _CRITICAL_ENCODE_H */
+diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
+--- a/fs/ext4/dir.c
++++ b/fs/ext4/dir.c
+@@ -28,6 +28,7 @@
+ #include <linux/iversion.h>
+ #include "ext4.h"
+ #include "xattr.h"
++#include "critical_encode.h"
+
+ static int ext4_dx_readdir(struct file *, struct dir_context *);
+
+@@ -144,7 +145,8 @@ static int ext4_readdir(struct file *
+ return err;
+ }
+
+- if (IS_ENCRYPTED(inode)) {
++ /* disable decryption of filename, present only escaped name */
++ if (0 && IS_ENCRYPTED(inode)) {
+ err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
+ if (err < 0)
+ return err;
+@@ -258,22 +259,33 @@ static int ext4_readdir(struct file *
+ get_dtype(sb, de->file_type)))
+ goto done;
+ } else {
+- int save_len = fstr.len;
+ struct fscrypt_str de_name =
+ FSTR_INIT(de->name,
+ de->name_len);
++ int presented_len;
+
+ /* Directory is encrypted */
+- err = fscrypt_fname_disk_to_usr(inode,
++ presented_len = critical_chars(de->name,
++ de->name_len);
++ err = ext4_fname_alloc_buffer(inode,
++ presented_len,
++ &fstr);
++ if (err)
++ goto errout;
++
++ err = ext4_fname_disk_to_usr(inode,
+ 0, 0, &de_name, &fstr);
+ de_name = fstr;
+- fstr.len = save_len;
+- if (err)
++ if (err) {
++ ext4_fname_free_buffer(&fstr);
+ goto errout;
+- if (!dir_emit(ctx,
++ }
++ err = dir_emit(ctx,
+ de_name.name, de_name.len,
+ le32_to_cpu(de->inode),
+- get_dtype(sb, de->file_type)))
++ get_dtype(sb, de->file_type));
++ ext4_fname_free_buffer(&fstr);
++ if (!err)
+ goto done;
+ }
+ }
+diff -wur a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
+--- a/fs/ext4/ialloc.c
++++ b/fs/ext4/ialloc.c
+@@ -30,6 +30,7 @@
+ #include "ext4_jbd2.h"
+ #include "xattr.h"
+ #include "acl.h"
++#include "critical_encode.h"
+
+ #include <trace/events/ext4.h>
+
+diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -40,6 +40,7 @@
+
+ #include "xattr.h"
+ #include "acl.h"
++#include "critical_encode.h"
+
+ #include <trace/events/ext4.h>
+ /*
+@@ -1368,22 +1369,31 @@ static int htree_dirblock_to_tree(struct
+ hinfo->hash, hinfo->minor_hash, de,
+ &tmp_str);
+ } else {
+- int save_len = fname_crypto_str.len;
+ struct fscrypt_str de_name = FSTR_INIT(de->name,
+ de->name_len);
++ int presented_len;
+
+ /* Directory is encrypted */
+- err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
++ presented_len = critical_chars(de->name, de->name_len);
++ err = ext4_fname_alloc_buffer(dir, presented_len,
++ &fname_crypto_str);
++ if (err) {
++ count = err;
++ goto errout;
++ }
++
++ err = ext4_fname_disk_to_usr(dir, hinfo->hash,
+ hinfo->minor_hash, &de_name,
+ &fname_crypto_str);
+ if (err) {
++ ext4_fname_free_buffer(&fname_crypto_str);
+ count = err;
+ goto errout;
+ }
+ err = ext4_htree_store_dirent(dir_file,
+ hinfo->hash, hinfo->minor_hash, de,
+ &fname_crypto_str);
+- fname_crypto_str.len = save_len;
++ ext4_fname_free_buffer(&fname_crypto_str);
+ }
+ if (err != 0) {
+ count = err;
+@@ -1614,7 +1614,7 @@ static void dx_insert_block(struct dx_fr
+ * Return: %true if the directory entry matches, otherwise %false.
+ */
+ static inline bool ext4_match(const struct ext4_filename *fname,
+- const struct ext4_dir_entry_2 *de)
++ const struct ext4_dir_entry_2 *de, int denamelen)
+ {
+ struct fscrypt_name f;
+
+@@ -1626,7 +1626,7 @@ static inline bool ext4_match(const s
+ #ifdef CONFIG_EXT4_FS_ENCRYPTION
+ f.crypto_buf = fname->crypto_buf;
+ #endif
+- return fscrypt_match_name(&f, de->name, de->name_len);
++ return fscrypt_match_name(&f, de->name, denamelen);
+ }
+
+ /*
+@@ -1637,16 +1637,30 @@ int ext4_search_dir(struct buffer_hea
+ unsigned int offset, struct ext4_dir_entry_2 **res_dir)
+ {
+ struct ext4_dir_entry_2 * de;
++ bool probablytrunc;
+ char * dlimit;
+- int de_len;
++ int de_len, denamelen;
+
+ de = (struct ext4_dir_entry_2 *)search_buf;
+ dlimit = search_buf + buf_size;
++ /* fname is probably truncated if it is the decoded representation of
++ * an encrypted filename not aligned on a 32-byte boundary
++ */
++ probablytrunc = !IS_LUSTRE_MOUNT(dir->i_sb) && IS_ENCRYPTED(dir) &&
++ fname->disk_name.len & 31;
+ while ((char *) de < dlimit) {
+ /* this code is executed quadratically often */
+ /* do minimal checking `by hand' */
++ denamelen = de->name_len;
++ if (unlikely(probablytrunc) &&
++ de->name_len > fname->disk_name.len)
++ /* Adjust name len to look for a partial match.
++ * Since it is binary encrypted names, there
++ * should not be any collision between names.
++ */
++ denamelen = fname->disk_name.len;
+ if ((char *) de + de->name_len <= dlimit &&
+- ext4_match(fname, de)) {
++ ext4_match(fname, de, denamelen)) {
+ /* found a match - just to be sure, do
+ * a full check */
+ if (ext4_check_dir_entry(dir, NULL, de, bh, search_buf,
+@@ -1707,7 +1717,7 @@ struct buffer_head *__ext4_find_entry
+ if (namelen > EXT4_NAME_LEN)
+ return NULL;
+
+- retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
++ retval = ext4_setup_filename(dir, d_name, 1, &fname);
+ if (retval == -ENOENT)
+ return NULL;
+ if (retval)
+@@ -1834,7 +1844,8 @@ cleanup_and_exit:
+ /* Clean up the read-ahead blocks */
+ for (; ra_ptr < ra_max; ra_ptr++)
+ brelse(bh_use[ra_ptr]);
+- ext4_fname_free_filename(&fname);
++ if (fname.disk_name.name != d_name->name)
++ kfree(fname.disk_name.name);
+ return ret;
+ }
+ EXPORT_SYMBOL(__ext4_find_entry);
+@@ -1900,7 +1911,7 @@ static struct dentry *ext4_lookup(str
+ struct buffer_head *bh;
+ int err;
+
+- err = fscrypt_prepare_lookup(dir, dentry, flags);
++ err = ext4_prepare_lookup(dir, dentry, flags);
+ if (err)
+ return ERR_PTR(err);
+
+@@ -2206,7 +2221,7 @@ int ext4_find_dest_de(struct inode *d
+ if (ext4_check_dir_entry(dir, NULL, de, bh,
+ buf, buf_size, offset))
+ return -EFSCORRUPTED;
+- if (ext4_match(fname, de))
++ if (ext4_match(fname, de, de->name_len))
+ return -EEXIST;
+ nlen = EXT4_DIR_ENTRY_LEN(de);
+ rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+diff -wur a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1154,6 +1154,9 @@ struct ext4_inode_info {
+ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */
+
++/* we know this is a Lustre mount thanks to the DIRDATA flag */
++#define IS_LUSTRE_MOUNT(sb) test_opt((sb), DIRDATA)
++
+ /*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
diff -wur a/fs/ext4/inode.c b/fs/ext4/inode.c
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
new_fl |= S_DAX;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
new_fl |= S_ENCRYPTED;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX|
stat->attributes |= STATX_ATTR_COMPRESSED;
- if (flags & EXT4_ENCRYPT_FL)
+ if (flags & EXT4_ENCRYPT_FL &&
-+ unlikely(test_opt(inode->i_sb, DIRDATA) != EXT4_MOUNT_DIRDATA))
++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & EXT4_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
--- /dev/null
+diff -wur /dev/null b/fs/ext4/critical_encode.h
+--- /dev/null
++++ b/fs/ext4/critical_encode.h
+@@ -0,0 +1,158 @@
++/*
++ * critical_encode.h
++ *
++ * Copyright (c) 2022 Whamcloud
++ */
++
++#ifndef _CRITICAL_ENCODE_H
++#define _CRITICAL_ENCODE_H
++
++#include <linux/ctype.h>
++
++/* Encoding/decoding routines inspired from yEnc principles.
++ * We just take care of a few critical characters:
++ * NULL, LF, CR, /, DEL and =.
++ * If such a char is found, it is replaced with '=' followed by
++ * the char value + 64.
++ * All other chars are left untouched.
++ * Efficiency of this encoding depends on the occurences of the
++ * critical chars, but statistically on binary data it can be much higher
++ * than base64 for instance.
++ */
++static inline int critical_encode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ /* escape NULL, LF, CR, /, DEL and = */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '=')) {
++ *(q++) = '=';
++ *(q++) = *(p++) + 64;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++/* returns the number of chars encoding would produce */
++static inline int critical_chars(const u8 *src, int len)
++{
++ u8 *p = (u8 *)src;
++ int newlen = len;
++
++ while (p - src < len) {
++ /* NULL, LF, CR, /, DEL and = cost an additional '=' */
++ if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
++ *p == '/' || *p == 0x7F || *p == '='))
++ newlen++;
++ p++;
++ }
++
++ return newlen;
++}
++
++/* decoding routine - returns the number of chars in output */
++static inline int critical_decode(const u8 *src, int len, char *dst)
++{
++ u8 *p = (u8 *)src, *q = dst;
++
++ while (p - src < len) {
++ if (unlikely(*p == '=')) {
++ *(q++) = *(++p) - 64;
++ p++;
++ } else {
++ *(q++) = *(p++);
++ }
++ }
++
++ return (char *)q - dst;
++}
++
++#define fscrypt_get_encryption_info(inode) \
++ (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) ? 0 : -EOPNOTSUPP)
++
++static inline int ext4_prepare_lookup(struct inode *dir,
++ struct dentry *dentry,
++ unsigned int flags)
++{
++ if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)))
++ return 0;
++ return fscrypt_prepare_lookup(dir, dentry, flags);
++}
++
++static inline int ext4_fname_alloc_buffer(const struct inode *inode,
++ u32 max_encrypted_len,
++ struct fscrypt_str *crypto_str)
++{
++ crypto_str->name = kmalloc(max_encrypted_len + 1, GFP_NOFS);
++ if (!crypto_str->name)
++ return -ENOMEM;
++ crypto_str->len = max_encrypted_len;
++ return 0;
++}
++
++static inline void ext4_fname_free_buffer(struct fscrypt_str *crypto_str)
++{
++ if (!crypto_str)
++ return;
++ kfree(crypto_str->name);
++ crypto_str->name = NULL;
++}
++
++static inline int ext4_fname_disk_to_usr(struct inode *inode,
++ u32 hash, u32 minor_hash,
++ const struct fscrypt_str *iname,
++ struct fscrypt_str *oname)
++{
++ int presented_len;
++
++ presented_len = critical_encode(iname->name, iname->len, oname->name);
++ if (presented_len > NAME_MAX) {
++ /* truncate at NAME_MAX,
++ * or NAME_MAX-1 if name ends with '=' to avoid decoding issue
++ */
++ presented_len = NAME_MAX;
++ if (oname->name[presented_len - 1] == '=')
++ presented_len--;
++ oname->len = presented_len;
++ }
++ oname->name[presented_len] = '\0';
++
++ return 0;
++}
++
++static inline int ext4_setup_filename(struct inode *dir,
++ const struct qstr *iname,
++ int lookup,
++ struct ext4_filename *fname)
++{
++ fname->usr_fname = iname;
++
++ if (lookup && IS_ENCRYPTED(dir) &&
++ unlikely(!IS_LUSTRE_MOUNT(dir->i_sb) &&
++ strnchr(iname->name, iname->len, '='))) {
++ /* Only proceed to critical decode if
++ * iname contains escape char '='.
++ */
++ int len = iname->len;
++ char *buf;
++
++ buf = kmalloc(len, GFP_NOFS);
++ if (!buf)
++ return -ENOMEM;
++
++ len = critical_decode(iname->name, len, buf);
++ fname->disk_name.name = (unsigned char *)buf;
++ fname->disk_name.len = len;
++ return 0;
++ }
++
++ fname->disk_name.name = (unsigned char *) iname->name;
++ fname->disk_name.len = iname->len;
++ return 0;
++}
++
++#endif /* _CRITICAL_ENCODE_H */
+diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
+--- a/fs/ext4/dir.c
++++ b/fs/ext4/dir.c
+@@ -28,6 +28,7 @@
+ #include <linux/iversion.h>
+ #include "ext4.h"
+ #include "xattr.h"
++#include "critical_encode.h"
+
+ static int ext4_dx_readdir(struct file *, struct dir_context *);
+
+@@ -144,7 +145,8 @@ static int ext4_readdir(struct file *
+ return err;
+ }
+
+- if (IS_ENCRYPTED(inode)) {
++ /* disable decryption of filename, present only escaped name */
++ if (0 && IS_ENCRYPTED(inode)) {
+ err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
+ if (err < 0)
+ return err;
+@@ -258,22 +259,33 @@ static int ext4_readdir(struct file *
+ get_dtype(sb, de->file_type)))
+ goto done;
+ } else {
+- int save_len = fstr.len;
+ struct fscrypt_str de_name =
+ FSTR_INIT(de->name,
+ de->name_len);
++ int presented_len;
+
+ /* Directory is encrypted */
+- err = fscrypt_fname_disk_to_usr(inode,
++ presented_len = critical_chars(de->name,
++ de->name_len);
++ err = ext4_fname_alloc_buffer(inode,
++ presented_len,
++ &fstr);
++ if (err)
++ goto errout;
++
++ err = ext4_fname_disk_to_usr(inode,
+ 0, 0, &de_name, &fstr);
+ de_name = fstr;
+- fstr.len = save_len;
+- if (err)
++ if (err) {
++ ext4_fname_free_buffer(&fstr);
+ goto errout;
+- if (!dir_emit(ctx,
++ }
++ err = dir_emit(ctx,
+ de_name.name, de_name.len,
+ le32_to_cpu(de->inode),
+- get_dtype(sb, de->file_type)))
++ get_dtype(sb, de->file_type));
++ ext4_fname_free_buffer(&fstr);
++ if (!err)
+ goto done;
+ }
+ }
+diff -wur a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
+--- a/fs/ext4/ialloc.c
++++ b/fs/ext4/ialloc.c
+@@ -30,6 +30,7 @@
+ #include "ext4_jbd2.h"
+ #include "xattr.h"
+ #include "acl.h"
++#include "critical_encode.h"
+
+ #include <trace/events/ext4.h>
+
+diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -40,6 +40,7 @@
+
+ #include "xattr.h"
+ #include "acl.h"
++#include "critical_encode.h"
+
+ #include <trace/events/ext4.h>
+ /*
+@@ -1368,22 +1369,31 @@ static int htree_dirblock_to_tree(struct
+ hinfo->hash, hinfo->minor_hash, de,
+ &tmp_str);
+ } else {
+- int save_len = fname_crypto_str.len;
+ struct fscrypt_str de_name = FSTR_INIT(de->name,
+ de->name_len);
++ int presented_len;
+
+ /* Directory is encrypted */
+- err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
++ presented_len = critical_chars(de->name, de->name_len);
++ err = ext4_fname_alloc_buffer(dir, presented_len,
++ &fname_crypto_str);
++ if (err) {
++ count = err;
++ goto errout;
++ }
++
++ err = ext4_fname_disk_to_usr(dir, hinfo->hash,
+ hinfo->minor_hash, &de_name,
+ &fname_crypto_str);
+ if (err) {
++ ext4_fname_free_buffer(&fname_crypto_str);
+ count = err;
+ goto errout;
+ }
+ err = ext4_htree_store_dirent(dir_file,
+ hinfo->hash, hinfo->minor_hash, de,
+ &fname_crypto_str);
+- fname_crypto_str.len = save_len;
++ ext4_fname_free_buffer(&fname_crypto_str);
+ }
+ if (err != 0) {
+ count = err;
+@@ -1614,7 +1614,7 @@ static void dx_insert_block(struct dx_fr
+ * Return: %true if the directory entry matches, otherwise %false.
+ */
+ static inline bool ext4_match(const struct ext4_filename *fname,
+- const struct ext4_dir_entry_2 *de)
++ const struct ext4_dir_entry_2 *de, int denamelen)
+ {
+ struct fscrypt_name f;
+
+@@ -1626,7 +1626,7 @@ static inline bool ext4_match(const s
+ #ifdef CONFIG_EXT4_FS_ENCRYPTION
+ f.crypto_buf = fname->crypto_buf;
+ #endif
+- return fscrypt_match_name(&f, de->name, de->name_len);
++ return fscrypt_match_name(&f, de->name, denamelen);
+ }
+
+ /*
+@@ -1637,16 +1637,30 @@ int ext4_search_dir(struct buffer_hea
+ unsigned int offset, struct ext4_dir_entry_2 **res_dir)
+ {
+ struct ext4_dir_entry_2 * de;
++ bool probablytrunc;
+ char * dlimit;
+- int de_len;
++ int de_len, denamelen;
+
+ de = (struct ext4_dir_entry_2 *)search_buf;
+ dlimit = search_buf + buf_size;
++ /* fname is probably truncated if it is the decoded representation of
++ * an encrypted filename not aligned on a 32-byte boundary
++ */
++ probablytrunc = !IS_LUSTRE_MOUNT(dir->i_sb) && IS_ENCRYPTED(dir) &&
++ fname->disk_name.len & 31;
+ while ((char *) de < dlimit) {
+ /* this code is executed quadratically often */
+ /* do minimal checking `by hand' */
++ denamelen = de->name_len;
++ if (unlikely(probablytrunc) &&
++ de->name_len > fname->disk_name.len)
++ /* Adjust name len to look for a partial match.
++ * Since it is binary encrypted names, there
++ * should not be any collision between names.
++ */
++ denamelen = fname->disk_name.len;
+ if ((char *) de + de->name_len <= dlimit &&
+- ext4_match(fname, de)) {
++ ext4_match(fname, de, denamelen)) {
+ /* found a match - just to be sure, do
+ * a full check */
+ if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+@@ -1707,7 +1717,7 @@ struct buffer_head *__ext4_find_entry
+ if (namelen > EXT4_NAME_LEN)
+ return NULL;
+
+- retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
++ retval = ext4_setup_filename(dir, d_name, 1, &fname);
+ if (retval == -ENOENT)
+ return NULL;
+ if (retval)
+@@ -1834,7 +1844,8 @@ cleanup_and_exit:
+ /* Clean up the read-ahead blocks */
+ for (; ra_ptr < ra_max; ra_ptr++)
+ brelse(bh_use[ra_ptr]);
+- ext4_fname_free_filename(&fname);
++ if (fname.disk_name.name != d_name->name)
++ kfree(fname.disk_name.name);
+ return ret;
+ }
+ EXPORT_SYMBOL(__ext4_find_entry);
+@@ -1900,7 +1911,7 @@ static struct dentry *ext4_lookup(str
+ struct buffer_head *bh;
+ int err;
+
+- err = fscrypt_prepare_lookup(dir, dentry, flags);
++ err = ext4_prepare_lookup(dir, dentry, flags);
+ if (err)
+ return ERR_PTR(err);
+
+@@ -2206,7 +2221,7 @@ int ext4_find_dest_de(struct inode *d
+ if (ext4_check_dir_entry(dir, NULL, de, bh,
+ buf, buf_size, offset))
+ return -EFSCORRUPTED;
+- if (ext4_match(fname, de))
++ if (ext4_match(fname, de, de->name_len))
+ return -EEXIST;
+ nlen = EXT4_DIR_ENTRY_LEN(de);
+ rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
rhel7.7/ext4-ialloc-uid-gid-and-pass-owner-down.patch
rhel7.6/ext4-projid-xattrs.patch
rhel7.9/ext4-enc-flag.patch
+rhel7.9/ext4-filename-encode.patch
rhel7.7/ext4-ialloc-uid-gid-and-pass-owner-down.patch
rhel7.6/ext4-projid-xattrs.patch
rhel7.9/ext4-enc-flag.patch
+rhel7.9/ext4-filename-encode.patch
rhel7.7/ext4-ialloc-uid-gid-and-pass-owner-down.patch
rhel7.6/ext4-projid-xattrs.patch
rhel7.9/ext4-enc-flag.patch
+rhel7.9/ext4-filename-encode.patch
rhel7.7/ext4-ialloc-uid-gid-and-pass-owner-down.patch
rhel7.6/ext4-projid-xattrs.patch
rhel7.9/ext4-enc-flag.patch
+rhel7.9/ext4-filename-encode.patch
base/ext4-projid-xattrs.patch
rhel8/linux-5.4/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8/ext4-filename-encode.patch
base/ext4-projid-xattrs.patch
rhel8/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8/ext4-filename-encode.patch
base/ext4-projid-xattrs.patch
rhel8/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8/ext4-filename-encode.patch
base/ext4-projid-xattrs.patch
rhel8.4/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8.5/ext4-filename-encode.patch
base/ext4-projid-xattrs.patch
rhel8.5/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8.5/ext4-filename-encode.patch
ubuntu18/ext4-projid-xattrs.patch
rhel8/ext4-enc-flag.patch
base/ext4-delayed-iput.patch
+rhel8/ext4-filename-encode.patch