From 10b173b54119de0df8c167710c91b200ac3262fa Mon Sep 17 00:00:00 2001 From: Shuichi Ihara Date: Mon, 10 Mar 2025 10:35:53 +0900 Subject: [PATCH] LU-18789 ldiskfs: ldiskfs patch adjustments for ubuntu 24.04.2 Ubuntu24.04.2 is based on linux-6.11.0 by default. Only a few ldiskfs patch adjustments are needed for it to build server modules properly. Test-Parameters: trivial Signed-off-by: Shuichi Ihara Change-Id: Ie476ef12568b8ecb94df38b48b51646dc42923da Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/58355 Tested-by: Maloo Tested-by: jenkins Reviewed-by: Jian Yu Reviewed-by: Shaun Tancheff Reviewed-by: Oleg Drokin --- config/lustre-build-ldiskfs.m4 | 5 +- .../patches/linux-6.11/ext4-filename-encode.patch | 466 +++++++++++++++++++++ .../kernel_patches/series/ldiskfs-6.11-ml.series | 2 +- 3 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 ldiskfs/kernel_patches/patches/linux-6.11/ext4-filename-encode.patch diff --git a/config/lustre-build-ldiskfs.m4 b/config/lustre-build-ldiskfs.m4 index 52f52d1..855ba48 100644 --- a/config/lustre-build-ldiskfs.m4 +++ b/config/lustre-build-ldiskfs.m4 @@ -98,6 +98,7 @@ AS_IF([test x$RHEL_KERNEL = xyes], [ ]) ], [test x$UBUNTU_KERNEL = xyes], [ BASEVER=$(echo $LINUXRELEASE | cut -d'-' -f1) + AS_VERSION_COMPARE([$BASEVER],[6.11.0],[ AS_VERSION_COMPARE([$BASEVER],[6.10.0],[ AS_VERSION_COMPARE([$BASEVER],[6.8.0],[ AS_VERSION_COMPARE([$BASEVER],[5.19.0],[ @@ -177,7 +178,9 @@ AS_IF([test x$RHEL_KERNEL = xyes], [ ], [LDISKFS_SERIES="6.7-ml.series"])], [LDISKFS_SERIES="6.10-ml.series"], - [LDISKFS_SERIES="6.10-ml.series"]) + [LDISKFS_SERIES="6.10-ml.series"])], + [LDISKFS_SERIES="6.11-ml.series"], + [LDISKFS_SERIES="6.11-ml.series"]) ], [test x$OPENEULER_KERNEL = xyes], [ case $OPENEULER_VERSION_NO in 2203.0) LDISKFS_SERIES="5.10.0-oe2203.series" ;; diff --git a/ldiskfs/kernel_patches/patches/linux-6.11/ext4-filename-encode.patch b/ldiskfs/kernel_patches/patches/linux-6.11/ext4-filename-encode.patch new file mode 100644 index 0000000..d98bf11 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/linux-6.11/ext4-filename-encode.patch @@ -0,0 +1,466 @@ +From d0a722cb8fb886380e24e8261e8efca09a3262d6 Mon Sep 17 00:00:00 2001 +From: Sebastien Buisson +Date: Tue, 20 Dec 2022 15:40:52 +0100 +Subject: [PATCH] LU-16374 ldiskfs: implement security.encdata xattr + +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'. + +On get, the value of the security.encdata xattr is computed from +encrypted file's information. +On set, encrypted file's information is restored from xattr value. +The encrypted name is stored temporarily in a dedicated xattr +LDISKFS_XATTR_NAME_RAWENCNAME, that will be used to set correct name +at linkat. + +Signed-off-by: Sebastien Buisson +Change-Id: Ia318c39d403b1c448e71bcd5b29862d022d05d0a +Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/49456 +Tested-by: jenkins +Tested-by: Maloo +Reviewed-by: Andreas Dilger +Reviewed-by: Li Dongyang +Reviewed-by: Oleg Drokin +--- + +=================================================================== +--- /dev/null ++++ linux-stage/fs/ext4/critical_encode.h +@@ -0,0 +1,170 @@ ++/* ++ * 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) ++{ ++ 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 ++ ext4_fname_setup_ci_filename(dir, iname, fname); ++#endif ++ return 0; ++} ++ ++#endif /* _CRITICAL_ENCODE_H */ +Index: linux-stage/fs/ext4/dir.c +=================================================================== +--- linux-stage.orig/fs/ext4/dir.c ++++ linux-stage/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,10 +277,10 @@ static int ext4_readdir(struct file *fil + 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; + u32 hash; + u32 minor_hash; + +@@ -291,16 +293,27 @@ static int ext4_readdir(struct file *fil + } + + /* Directory is encrypted */ +- err = fscrypt_fname_disk_to_usr(inode, +- hash, minor_hash, &de_name, &fstr); +- de_name = fstr; +- fstr.len = save_len; ++ presented_len = critical_chars(de->name, ++ de->name_len); ++ err = ext4_fname_alloc_buffer(inode, ++ presented_len, ++ &fstr); + if (err) + goto errout; +- if (!dir_emit(ctx, ++ ++ err = ext4_fname_disk_to_usr(inode, ++ 0, 0, &de_name, &fstr); ++ de_name = fstr; ++ if (err) { ++ ext4_fname_free_buffer(&fstr); ++ goto errout; ++ } ++ 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; + } + } +Index: linux-stage/fs/ext4/ialloc.c +=================================================================== +--- linux-stage.orig/fs/ext4/ialloc.c ++++ linux-stage/fs/ext4/ialloc.c +@@ -30,6 +30,7 @@ + #include "ext4_jbd2.h" + #include "xattr.h" + #include "acl.h" ++#include "critical_encode.h" + + #include + +Index: linux-stage/fs/ext4/namei.c +=================================================================== +--- linux-stage.orig/fs/ext4/namei.c ++++ linux-stage/fs/ext4/namei.c +@@ -41,6 +41,7 @@ + + #include "xattr.h" + #include "acl.h" ++#include "critical_encode.h" + + #include + /* +@@ -1442,7 +1443,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); +@@ -1493,22 +1494,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; +@@ -1787,7 +1797,7 @@ int ext4_fname_setup_ci_filename(struct + */ + 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; + +@@ -1829,7 +1839,7 @@ static bool ext4_match(struct inode *par + } + #endif + +- return fscrypt_match_name(&f, de->name, de->name_len); ++ return fscrypt_match_name(&f, de->name, denamelen); + } + + /* +@@ -1840,16 +1850,30 @@ int ext4_search_dir(struct buffer_head * + 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, +@@ -2052,7 +2076,7 @@ struct buffer_head *ext4_find_entry_lock + 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) +@@ -2060,7 +2084,9 @@ struct buffer_head *ext4_find_entry_lock + + 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; + } + +@@ -2074,7 +2100,7 @@ static struct buffer_head *ext4_lookup_e + 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); + if (err == -ENOENT) + return NULL; + if (err) +@@ -2082,7 +2108,9 @@ static struct buffer_head *ext4_lookup_e + + bh = __ext4_find_entry(dir, &fname, res_dir, NULL, NULL); + +- ext4_fname_free_filename(&fname); ++ if (fname.disk_name.name != dentry->d_name.name) ++ kfree(fname.disk_name.name); ++ + return bh; + } + +@@ -2174,7 +2202,7 @@ static struct dentry *ext4_lookup(struct + } + 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); +@@ -2466,7 +2494,7 @@ int ext4_find_dest_de(struct inode *dir, + 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-6.11-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-6.11-ml.series index c49a97c..bd5bb68 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-6.11-ml.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-6.11-ml.series @@ -32,7 +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 -linux-6.10/ext4-filename-encode.patch +linux-6.11/ext4-filename-encode.patch rhel9.1/ext4-enc-flag.patch linux-6.6/ext4-encdata.patch rhel9.4/ext4-add-IGET_NO_CHECKS-flag.patch -- 1.8.3.1