From e10487a19576fb660453c106674553972a52b519 Mon Sep 17 00:00:00 2001 From: Shaun Tancheff Date: Wed, 18 Oct 2023 04:37:21 -0500 Subject: [PATCH] LU-17131 ldiskfs: el9.2 encdata and filename-encode Add encryption support for el9.2 Test-Parameters: serverdistro=el9.2 testlist=sanity Test-Parameters: serverdistro=el9.2 testlist=conf-sanity Signed-off-by: Shaun Tancheff Signed-off-by: Sebastien Buisson Change-Id: Iacda4c0e4107bccb57aece2e8d9cee12a4bcd09b Reviewed-by: Andreas Dilger Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/53077 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin --- .../patches/rhel9.2/ext4-encdata.patch | 441 +++++++++++++++++++++ .../patches/rhel9.2/ext4-filename-encode.patch | 409 +++++++++++++++++++ .../series/ldiskfs-5.14-rhel9.2.series | 2 + 3 files changed, 852 insertions(+) create mode 100644 ldiskfs/kernel_patches/patches/rhel9.2/ext4-encdata.patch create mode 100644 ldiskfs/kernel_patches/patches/rhel9.2/ext4-filename-encode.patch diff --git a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-encdata.patch b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-encdata.patch new file mode 100644 index 0000000..a7a71c8 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-encdata.patch @@ -0,0 +1,441 @@ +diff --git a/fs/ext4/encdata.h b/fs/ext4/encdata.h +new file mode 100644 +index 00000000..aa83832f +--- /dev/null ++++ b/fs/ext4/encdata.h +@@ -0,0 +1,128 @@ ++/* ++ * encdata.h ++ * ++ * Copyright (c) 2022 Whamcloud ++ */ ++ ++#ifndef _ENCDATA_H ++#define _ENCDATA_H ++ ++/* Define a fixed 4096-byte encryption unit size */ ++/* Must be identical to LUSTRE_ENCRYPTION_UNIT_SIZE */ ++#define EXT4_ENCRYPTION_BLOCKBITS 12 ++#define EXT4_ENCRYPTION_UNIT_SIZE ((size_t)1 << EXT4_ENCRYPTION_BLOCKBITS) ++#define EXT4_ENCRYPTION_MASK (~(EXT4_ENCRYPTION_UNIT_SIZE - 1)) ++#define LLCRYPT_SET_CONTEXT_MAX_SIZE 40 ++#define ENCDATA_XATTR_FMT_1 "{ encoding: " ++#define ENCDATA_XATTR_FMT_2 ", size: " ++#define ENCDATA_XATTR_FMT_3 ", enc_ctx: " ++#define ENCDATA_XATTR_FMT_4 ", enc_name: " ++#define ENCDATA_XATTR_FMT_END " }" ++#define ENCDATA_XATTR_FMT_COMP ENCDATA_XATTR_FMT_1 ENCDATA_XATTR_FMT_2 \ ++ ENCDATA_XATTR_FMT_3 ENCDATA_XATTR_FMT_4 \ ++ ENCDATA_XATTR_FMT_END ++ ++extern char encdata_xattr_fmt[NAME_MAX]; ++ ++/* ++ * base64url encoding, lifted from fs/crypto/fname.c. ++ */ ++ ++static const char base64url_table[] = ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; ++ ++#define BASE64URL_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) ++ ++/** ++ * base64url_encode() - base64url-encode some binary data ++ * @src: the binary data to encode ++ * @srclen: the length of @src in bytes ++ * @dst: (output) the base64url-encoded string. Not NUL-terminated. ++ * ++ * Encodes data using base64url encoding, i.e. the "Base 64 Encoding with URL ++ * and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't used, ++ * as it's unneeded and not required by the RFC. base64url is used instead of ++ * base64 to avoid the '/' character, which isn't allowed in filenames. ++ * ++ * Return: the length of the resulting base64url-encoded string in bytes. ++ * This will be equal to LLCRYPT_BASE64URL_CHARS(srclen). ++ */ ++static inline int base64url_encode(const u8 *src, int srclen, char *dst) ++{ ++ u32 ac = 0; ++ int bits = 0; ++ int i; ++ char *cp = dst; ++ ++ for (i = 0; i < srclen; i++) { ++ ac = (ac << 8) | src[i]; ++ bits += 8; ++ do { ++ bits -= 6; ++ *cp++ = base64url_table[(ac >> bits) & 0x3f]; ++ } while (bits >= 6); ++ } ++ if (bits) ++ *cp++ = base64url_table[(ac << (6 - bits)) & 0x3f]; ++ return cp - dst; ++} ++ ++/** ++ * base64url_decode() - base64url-decode a string ++ * @src: the string to decode. Doesn't need to be NUL-terminated. ++ * @srclen: the length of @src in bytes ++ * @dst: (output) the decoded binary data ++ * ++ * Decodes a string using base64url encoding, i.e. the "Base 64 Encoding with ++ * URL and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't ++ * accepted, nor are non-encoding characters such as whitespace. ++ * ++ * This implementation hasn't been optimized for performance. ++ * ++ * Return: the length of the resulting decoded binary data in bytes, ++ * or -1 if the string isn't a valid base64url string. ++ */ ++static inline int base64url_decode(const char *src, int srclen, u8 *dst) ++{ ++ u32 ac = 0; ++ int bits = 0; ++ int i; ++ u8 *bp = dst; ++ ++ for (i = 0; i < srclen; i++) { ++ const char *p = strchr(base64url_table, src[i]); ++ ++ if (p == NULL || src[i] == 0) ++ return -1; ++ ac = (ac << 6) | (p - base64url_table); ++ bits += 6; ++ if (bits >= 8) { ++ bits -= 8; ++ *bp++ = (u8)(ac >> bits); ++ } ++ } ++ if (ac & ((1 << bits) - 1)) ++ return -1; ++ return bp - dst; ++} ++ ++/* This version of the code uses base64url encoding for binary data. */ ++#define ENCDATA_ENCODING "base64url" ++ ++/* Wrappers to support various encodings. Add new methods in there. ++ */ ++static inline int encode(const u8 *src, int srclen, char *dst, char *encoding) ++{ ++ if (!strcmp(encoding, "base64url")) ++ return base64url_encode(src, srclen, dst); ++ return -EINVAL; ++} ++ ++static inline int decode(const char *src, int srclen, u8 *dst, char *encoding) ++{ ++ if (!strcmp(encoding, "base64url")) ++ return base64url_decode(src, srclen, dst); ++ return -EINVAL; ++} ++ ++#endif /* _ENCDATA_H */ +diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c +index fb500d12..769eabce 100644 +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -46,6 +46,7 @@ + #include "xattr.h" + #include "acl.h" + #include "truncate.h" ++#include "encdata.h" + + #include + +@@ -5620,6 +5621,12 @@ int ext4_getattr(struct user_namespace *mnt_userns, const struct path *path, + STATX_ATTR_VERITY); + + generic_fillattr(mnt_userns, inode, stat); ++ ++ if (flags & EXT4_ENCRYPT_FL && ++ unlikely(!IS_LUSTRE_MOUNT(inode->i_sb))) ++ stat->size = round_up(stat->size, ++ EXT4_ENCRYPTION_UNIT_SIZE); ++ + return 0; + } + +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index 89d609c9..fd066751 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -55,6 +55,7 @@ + #include "acl.h" + #include "mballoc.h" + #include "fsmap.h" ++#include "encdata.h" + + #define CREATE_TRACE_POINTS + #include +@@ -7260,6 +7261,7 @@ MODULE_ALIAS_FS("ext4"); + + /* Shared across all ext4 file systems */ + wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ]; ++char encdata_xattr_fmt[NAME_MAX]; + + static int __init ext4_init_fs(void) + { +@@ -7313,6 +7315,12 @@ static int __init ext4_init_fs(void) + if (err) + goto out; + ++ snprintf(encdata_xattr_fmt, sizeof(encdata_xattr_fmt), ++ ENCDATA_XATTR_FMT_1"%%%u[^,]"ENCDATA_XATTR_FMT_2"%%llu" ++ ENCDATA_XATTR_FMT_3"%%%us"ENCDATA_XATTR_FMT_4"%%%us", ++ NAME_MAX, BASE64URL_CHARS(LLCRYPT_SET_CONTEXT_MAX_SIZE), ++ BASE64URL_CHARS(NAME_MAX)); ++ + return 0; + out: + ext4_fc_destroy_dentry_cache(); +diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h +index 824faf0b..1e8aa6f2 100644 +--- a/fs/ext4/xattr.h ++++ b/fs/ext4/xattr.h +@@ -140,6 +140,8 @@ extern const struct xattr_handler ext4_xattr_security_handler; + extern const struct xattr_handler ext4_xattr_hurd_handler; + + #define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c" ++#define EXT4_XATTR_NAME_ENCDATA "encdata" ++#define EXT4_XATTR_NAME_RAWENCNAME "rawencname" + + /* + * The EXT4_STATE_NO_EXPAND is overloaded and used for two purposes. +diff --git a/fs/ext4/xattr_security.c b/fs/ext4/xattr_security.c +index 8213f66f..6fb465a8 100644 +--- a/fs/ext4/xattr_security.c ++++ b/fs/ext4/xattr_security.c +@@ -10,13 +10,217 @@ + #include + #include "ext4_jbd2.h" + #include "ext4.h" ++#include "critical_encode.h" ++#include "encdata.h" + #include "xattr.h" + ++/* security.encdata is a virtual xattr containing information related ++ * to encrypted files. It is expressed as ASCII text with a "key: value" ++ * format, and space as field separator. For instance: ++ * ++ * { encoding: base64url, size: 3012, enc_ctx: YWJjZGVmZ2hpamtsbW ++ * 5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbg, enc_name: ZmlsZXdpdGh2ZX ++ * J5bG9uZ25hbWVmaWxld2l0aHZlcnlsb25nbmFtZWZpbGV3aXRodmVyeWxvbmdu ++ * YW1lZmlsZXdpdGg } ++ * ++ * 'encoding' is the encoding method used for binary data, assume name ++ * can be up to 255 chars. ++ * 'size' is the clear text file data length in bytes. ++ * 'enc_ctx' is encoded encryption context, 40 bytes for v2. ++ * 'enc_name' is encoded encrypted name, 256 bytes max. ++ * So on overall, this xattr is at most 727 chars plus terminating '\0'. ++ */ ++static int ext4_build_xattr_encdata(struct dentry *dentry, ++ struct inode *inode, ++ void *buffer, size_t size) ++{ ++ char encoded_enc_ctx[BASE64URL_CHARS(LLCRYPT_SET_CONTEXT_MAX_SIZE) + 1]; ++ unsigned char enc_ctx[LLCRYPT_SET_CONTEXT_MAX_SIZE]; ++ char encoded_name[BASE64URL_CHARS(NAME_MAX) + 1]; ++ struct ext4_filename fname = { 0 }; ++ struct inode *parent = NULL; ++ int encoded_enc_ctx_len = 0; ++ int encoded_name_len = 0; ++ char size_str[32]; ++ int retval; ++ ++ if (!IS_ENCRYPTED(inode)) { ++ retval = -ENODATA; ++ goto out; ++ } ++ ++ /* get size */ ++ retval = snprintf(size_str, sizeof(size_str), "%llu", ++ S_ISDIR(inode->i_mode) ? 0 : inode->i_size); ++ if (retval >= sizeof(size_str)) { ++ retval = -ERANGE; ++ goto out; ++ } ++ ++ /* get raw name */ ++ if (dentry && dentry->d_parent) ++ parent = dentry->d_parent->d_inode; ++ ++ retval = ext4_setup_filename(parent, &dentry->d_name, 1, &fname); ++ if (retval) ++ goto out; ++ ++ /* base64url-encode raw name */ ++ encoded_name_len = encode(fname.disk_name.name, fname.disk_name.len, ++ encoded_name, ENCDATA_ENCODING); ++ if (encoded_name_len == -EINVAL) { ++ retval = -EINVAL; ++ goto out; ++ } ++ encoded_name[encoded_name_len] = '\0'; ++ ++ if (!buffer) { ++ /* Return exact xattr length we would return if called with ++ * non-NULL buffer. ++ */ ++ retval = sizeof(ENCDATA_XATTR_FMT_COMP) - 1 + ++ sizeof(ENCDATA_ENCODING) - 1 + strlen(size_str) + ++ BASE64URL_CHARS(LLCRYPT_SET_CONTEXT_MAX_SIZE) + ++ encoded_name_len; ++ goto out; ++ } ++ ++ /* get encryption context */ ++ retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, ++ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ++ enc_ctx, sizeof(enc_ctx)); ++ ++ if (retval < 0) ++ goto out; ++ ++ /* base64url-encode encryption context */ ++ encoded_enc_ctx_len = encode(enc_ctx, retval, encoded_enc_ctx, ++ ENCDATA_ENCODING); ++ if (encoded_enc_ctx_len == -EINVAL) { ++ retval = -EINVAL; ++ goto out; ++ } ++ encoded_enc_ctx[encoded_enc_ctx_len] = '\0'; ++ ++ /* write EXT4_XATTR_ENCDATA info into buffer */ ++ retval = snprintf(buffer, size, ++ ENCDATA_XATTR_FMT_1 ENCDATA_ENCODING ++ ENCDATA_XATTR_FMT_2"%s"ENCDATA_XATTR_FMT_3"%s" ++ ENCDATA_XATTR_FMT_4"%s"ENCDATA_XATTR_FMT_END, ++ size_str, encoded_enc_ctx, ++ encoded_name_len ? encoded_name : ""); ++ if (retval >= size) ++ retval = -ERANGE; ++ ++out: ++ if (fname.disk_name.name != dentry->d_name.name) ++ kfree(fname.disk_name.name); ++ ++ return retval; ++} ++ ++static int ext4_process_xattr_encdata(struct inode *inode, ++ const void *value, size_t size, ++ int flags) ++{ ++ char encoded_enc_ctx[BASE64URL_CHARS(LLCRYPT_SET_CONTEXT_MAX_SIZE) + 1]; ++ unsigned char enc_ctx[LLCRYPT_SET_CONTEXT_MAX_SIZE]; ++ char encoded_name[BASE64URL_CHARS(NAME_MAX) + 1]; ++ char encoding[NAME_MAX + 1]; ++ char name[NAME_MAX + 1]; ++ loff_t disk_size = 0; ++ char *buffer = NULL; ++ int enc_ctx_len = 0; ++ int name_len = 0; ++ int retval = 0; ++ ++ if (IS_ENCRYPTED(inode) || !value || flags & XATTR_REPLACE) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ buffer = kmalloc(size + 1, GFP_NOFS); ++ if (!buffer) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ memcpy(buffer, value, size); ++ buffer[size] = '\0'; ++ ++ retval = sscanf(buffer, encdata_xattr_fmt, ++ encoding, &disk_size, encoded_enc_ctx, encoded_name); ++ if (retval < 4) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ /* get former encryption context: should not exist */ ++ retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, ++ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0); ++ if (retval != -ENODATA) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ if (strlen(encoded_enc_ctx) > ++ BASE64URL_CHARS(LLCRYPT_SET_CONTEXT_MAX_SIZE)) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ /* base64url-decode encryption context */ ++ retval = decode(encoded_enc_ctx, strlen(encoded_enc_ctx), ++ enc_ctx, encoding); ++ if (retval < 0) { ++ retval = -EINVAL; ++ goto out; ++ } ++ enc_ctx_len = retval; ++ ++ /* set encryption context, this will set encryption flag */ ++ retval = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION, ++ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ++ enc_ctx, enc_ctx_len, XATTR_CREATE); ++ if (retval < 0) ++ goto out; ++ ++ if (disk_size) { ++ /* set size on inode */ ++ spin_lock(&inode->i_lock); ++ i_size_write(inode, disk_size); ++ EXT4_I(inode)->i_disksize = disk_size; ++ spin_unlock(&inode->i_lock); ++ mark_inode_dirty(inode); ++ } ++ ++ /* put raw encrypted name in EXT4_XATTR_NAME_RAWENCNAME xattr, ++ * for later use, but base64url-decode first ++ */ ++ retval = decode(encoded_name, strlen(encoded_name), name, encoding); ++ if (retval < 0) { ++ retval = -EINVAL; ++ goto out; ++ } ++ name_len = retval; ++ ++ retval = ext4_xattr_set(inode, EXT4_XATTR_INDEX_LUSTRE, ++ EXT4_XATTR_NAME_RAWENCNAME, ++ name, name_len, XATTR_CREATE); ++ ++out: ++ kfree(buffer); ++ ++ return retval; ++} ++ + static int + ext4_xattr_security_get(const struct xattr_handler *handler, +- struct dentry *unused, struct inode *inode, ++ struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) + { ++ if (!strncmp(name, EXT4_XATTR_NAME_ENCDATA, strlen(name))) ++ return ext4_build_xattr_encdata(dentry, inode, buffer, size); ++ + return ext4_xattr_get(inode, EXT4_XATTR_INDEX_SECURITY, + name, buffer, size); + } +@@ -28,6 +232,9 @@ ext4_xattr_security_set(const struct xattr_handler *handler, + const char *name, const void *value, + size_t size, int flags) + { ++ if (!strncmp(name, EXT4_XATTR_NAME_ENCDATA, strlen(name))) ++ return ext4_process_xattr_encdata(inode, value, size, flags); ++ + return ext4_xattr_set(inode, EXT4_XATTR_INDEX_SECURITY, + name, value, size, flags); + } +-- +2.25.1 + diff --git a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-filename-encode.patch b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-filename-encode.patch new file mode 100644 index 0000000..5ddf314 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-filename-encode.patch @@ -0,0 +1,409 @@ +--- /dev/null ++++ b/fs/ext4/critical_encode.h +@@ -0,0 +1,173 @@ ++/* ++ * critical_encode.h ++ * ++ * Copyright (c) 2022 Whamcloud ++ */ ++ ++#ifndef _CRITICAL_ENCODE_H ++#define _CRITICAL_ENCODE_H ++ ++#include ++ ++/* 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_has_permitted_context(struct inode *parent, ++ struct inode *child) ++{ ++ if (unlikely(!IS_LUSTRE_MOUNT(parent->i_sb))) ++ return 1; ++ return fscrypt_has_permitted_context(parent, child); ++} ++ ++struct ext4_filename; ++ ++static inline int ext4_prepare_readdir(struct inode *dir) ++{ ++ if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb))) ++ return 0; ++ return fscrypt_prepare_readdir(dir); ++} ++ ++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) ++{ ++ int err = 0; ++ ++ 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; ++ ++#ifdef CONFIG_UNICODE ++ err = ext4_fname_setup_ci_filename(dir, iname, fname); ++#endif ++ ++ return err; ++} ++ ++#endif /* _CRITICAL_ENCODE_H */ +--- a/fs/ext4/dir.c ++++ b/fs/ext4/dir.c +@@ -29,6 +29,7 @@ + #include + #include "ext4.h" + #include "xattr.h" ++#include "critical_encode.h" + + static int ext4_dx_readdir(struct file *, struct dir_context *); + +@@ -134,7 +135,7 @@ static int ext4_readdir(struct file *fil + struct buffer_head *bh = NULL; + struct fscrypt_str fstr = FSTR_INIT(NULL, 0); + +- err = fscrypt_prepare_readdir(inode); ++ err = ext4_prepare_readdir(inode); + if (err) + return err; + +@@ -161,7 +162,8 @@ static int ext4_readdir(struct file *fil + return err; + } + +- if (IS_ENCRYPTED(inode)) { ++ /* disable decryption of filename, present only escaped name */ ++ if (0 && IS_ENCRYPTED(inode)) { + err = fscrypt_fname_alloc_buffer(EXT4_NAME_LEN, &fstr); + if (err < 0) + return err; +@@ -275,24 +277,35 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) + 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, + EXT4_DIRENT_HASH(de), + EXT4_DIRENT_MINOR_HASH(de), + &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; + } + } +--- 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 + +--- a/fs/ext4/namei.c ++++ b/fs/ext4/namei.c +@@ -42,6 +42,7 @@ + + #include "xattr.h" + #include "acl.h" ++#include "critical_encode.h" + + #include + /* +@@ -1432,7 +1433,7 @@ static int htree_dirblock_to_tree(struct + ext4_dir_rec_len(0, + csum ? NULL : dir)); + /* Check if the directory is encrypted */ +- if (IS_ENCRYPTED(dir)) { ++ if (0 && IS_ENCRYPTED(dir)) { + err = fscrypt_prepare_readdir(dir); + if (err < 0) { + brelse(bh); +@@ -1478,22 +1479,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; +@@ -1818,7 +1828,7 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, + */ + static bool ext4_match(struct inode *parent, + const struct ext4_filename *fname, +- struct ext4_dir_entry_2 *de) ++ struct ext4_dir_entry_2 *de, int denamelen) + { + struct fscrypt_name f; + +@@ -1853,7 +1863,7 @@ static bool ext4_match(struct inode *parent, + } + #endif + +- return fscrypt_match_name(&f, de->name, de->name_len); ++ return fscrypt_match_name(&f, de->name, denamelen); + } + + /* +@@ -1864,16 +1874,30 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size, + 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 - EXT4_BASE_DIR_LEN) { + /* 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 (de->name + de->name_len <= dlimit && +- ext4_match(dir, fname, de)) { ++ ext4_match(dir, 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, +@@ -2075,7 +2099,7 @@ struct buffer_head *ext4_find_entry_locked(struct inode *dir, + struct ext4_filename fname; + struct buffer_head *bh; + +- err = ext4_fname_setup_filename(dir, d_name, 1, &fname); ++ err = ext4_setup_filename(dir, d_name, 1, &fname); + if (err == -ENOENT) + return NULL; + if (err) +@@ -2083,7 +2107,9 @@ struct buffer_head *ext4_find_entry_locked(struct inode *dir, + + bh = __ext4_find_entry(dir, &fname, res_dir, inlined, lck); + +- ext4_fname_free_filename(&fname); ++ if (fname.disk_name.name != d_name->name) ++ kfree(fname.disk_name.name); ++ + return bh; + } + +@@ -2097,7 +2123,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir, + struct ext4_filename fname; + struct buffer_head *bh; + +- err = ext4_fname_prepare_lookup(dir, dentry, &fname); ++ err = ext4_setup_filename(dir, &dentry->d_name, 1, &fname); + generic_set_encrypted_ci_d_ops(dentry); + if (err == -ENOENT) + return NULL; +@@ -2198,7 +2224,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi + } + if (!IS_ERR(inode) && IS_ENCRYPTED(dir) && + (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && +- !fscrypt_has_permitted_context(dir, inode)) { ++ !ext4_has_permitted_context(dir, inode)) { + ext4_warning(inode->i_sb, + "Inconsistent encryption contexts: %lu/%lu", + dir->i_ino, inode->i_ino); +@@ -2498,7 +2524,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, + if (ext4_check_dir_entry(dir, NULL, de, bh, + buf, buf_size, offset)) + return -EFSCORRUPTED; +- if (ext4_match(dir, fname, de)) ++ if (ext4_match(dir, fname, de, de->name_len)) + return -EEXIST; + nlen = EXT4_DIR_ENTRY_LEN(de, dir); + rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.2.series b/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.2.series index 243409e..078007e 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.2.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.2.series @@ -32,5 +32,7 @@ rhel8/ext4-ext-merge.patch linux-5.14/ext4-xattr-disable-credits-check.patch rhel9.2/ext4-fiemap-kernel-data.patch rhel8/ext4-old_ea_inodes_handling_fix.patch +rhel9.2/ext4-filename-encode.patch rhel9.1/ext4-enc-flag.patch +rhel9.2/ext4-encdata.patch rhel9/ext4-add-periodic-superblock-update.patch -- 1.8.3.1