1 diff -wur /dev/null b/fs/ext4/critical_encode.h
3 +++ b/fs/ext4/critical_encode.h
8 + * Copyright (c) 2022 Whamcloud
11 +#ifndef _CRITICAL_ENCODE_H
12 +#define _CRITICAL_ENCODE_H
14 +#include <linux/ctype.h>
16 +/* Encoding/decoding routines inspired from yEnc principles.
17 + * We just take care of a few critical characters:
18 + * NULL, LF, CR, /, DEL and =.
19 + * If such a char is found, it is replaced with '=' followed by
20 + * the char value + 64.
21 + * All other chars are left untouched.
22 + * Efficiency of this encoding depends on the occurences of the
23 + * critical chars, but statistically on binary data it can be much higher
24 + * than base64 for instance.
26 +static inline int critical_encode(const u8 *src, int len, char *dst)
28 + u8 *p = (u8 *)src, *q = dst;
30 + while (p - src < len) {
31 + /* escape NULL, LF, CR, /, DEL and = */
32 + if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
33 + *p == '/' || *p == 0x7F || *p == '=')) {
35 + *(q++) = *(p++) + 64;
41 + return (char *)q - dst;
44 +/* returns the number of chars encoding would produce */
45 +static inline int critical_chars(const u8 *src, int len)
50 + while (p - src < len) {
51 + /* NULL, LF, CR, /, DEL and = cost an additional '=' */
52 + if (unlikely(*p == 0x0 || *p == 0xA || *p == 0xD ||
53 + *p == '/' || *p == 0x7F || *p == '='))
61 +/* decoding routine - returns the number of chars in output */
62 +static inline int critical_decode(const u8 *src, int len, char *dst)
64 + u8 *p = (u8 *)src, *q = dst;
66 + while (p - src < len) {
67 + if (unlikely(*p == '=')) {
68 + *(q++) = *(++p) - 64;
75 + return (char *)q - dst;
78 +#define fscrypt_get_encryption_info(inode) \
79 + (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) ? 0 : -EOPNOTSUPP)
81 +static inline int ext4_has_permitted_context(struct inode *parent,
82 + struct inode *child)
84 + if (unlikely(!IS_LUSTRE_MOUNT(parent->i_sb)))
86 + return fscrypt_has_permitted_context(parent, child);
89 +static inline int ext4_prepare_lookup(struct inode *dir,
90 + struct dentry *dentry,
93 + if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)))
95 + return fscrypt_prepare_lookup(dir, dentry, flags);
98 +static inline int ext4_fname_alloc_buffer(const struct inode *inode,
99 + u32 max_encrypted_len,
100 + struct fscrypt_str *crypto_str)
102 + crypto_str->name = kmalloc(max_encrypted_len + 1, GFP_NOFS);
103 + if (!crypto_str->name)
105 + crypto_str->len = max_encrypted_len;
109 +static inline void ext4_fname_free_buffer(struct fscrypt_str *crypto_str)
113 + kfree(crypto_str->name);
114 + crypto_str->name = NULL;
117 +static inline int ext4_fname_disk_to_usr(struct inode *inode,
118 + u32 hash, u32 minor_hash,
119 + const struct fscrypt_str *iname,
120 + struct fscrypt_str *oname)
124 + presented_len = critical_encode(iname->name, iname->len, oname->name);
125 + if (presented_len > NAME_MAX) {
126 + /* truncate at NAME_MAX,
127 + * or NAME_MAX-1 if name ends with '=' to avoid decoding issue
129 + presented_len = NAME_MAX;
130 + if (oname->name[presented_len - 1] == '=')
132 + oname->len = presented_len;
134 + oname->name[presented_len] = '\0';
139 +static inline int ext4_setup_filename(struct inode *dir,
140 + const struct qstr *iname,
142 + struct ext4_filename *fname)
144 + fname->usr_fname = iname;
146 + if (lookup && IS_ENCRYPTED(dir) &&
147 + unlikely(!IS_LUSTRE_MOUNT(dir->i_sb) &&
148 + strnchr(iname->name, iname->len, '='))) {
149 + /* Only proceed to critical decode if
150 + * iname contains escape char '='.
152 + int len = iname->len;
155 + buf = kmalloc(len, GFP_NOFS);
159 + len = critical_decode(iname->name, len, buf);
160 + fname->disk_name.name = (unsigned char *)buf;
161 + fname->disk_name.len = len;
165 + fname->disk_name.name = (unsigned char *) iname->name;
166 + fname->disk_name.len = iname->len;
170 +#endif /* _CRITICAL_ENCODE_H */
171 diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
175 #include <linux/iversion.h>
178 +#include "critical_encode.h"
180 static int ext4_dx_readdir(struct file *, struct dir_context *);
182 @@ -144,7 +145,8 @@ static int ext4_readdir(struct file *
186 - if (IS_ENCRYPTED(inode)) {
187 + /* disable decryption of filename, present only escaped name */
188 + if (0 && IS_ENCRYPTED(inode)) {
189 err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
192 @@ -258,22 +259,33 @@ static int ext4_readdir(struct file *
193 get_dtype(sb, de->file_type)))
196 - int save_len = fstr.len;
197 struct fscrypt_str de_name =
202 /* Directory is encrypted */
203 - err = fscrypt_fname_disk_to_usr(inode,
204 + presented_len = critical_chars(de->name,
206 + err = ext4_fname_alloc_buffer(inode,
212 + err = ext4_fname_disk_to_usr(inode,
213 0, 0, &de_name, &fstr);
215 - fstr.len = save_len;
218 + ext4_fname_free_buffer(&fstr);
222 + err = dir_emit(ctx,
223 de_name.name, de_name.len,
224 le32_to_cpu(de->inode),
225 - get_dtype(sb, de->file_type)))
226 + get_dtype(sb, de->file_type));
227 + ext4_fname_free_buffer(&fstr);
232 diff -wur a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
233 --- a/fs/ext4/ialloc.c
234 +++ b/fs/ext4/ialloc.c
236 #include "ext4_jbd2.h"
239 +#include "critical_encode.h"
241 #include <trace/events/ext4.h>
243 diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c
244 --- a/fs/ext4/namei.c
245 +++ b/fs/ext4/namei.c
250 +#include "critical_encode.h"
252 #include <trace/events/ext4.h>
254 @@ -1368,22 +1369,31 @@ static int htree_dirblock_to_tree(struct
255 hinfo->hash, hinfo->minor_hash, de,
258 - int save_len = fname_crypto_str.len;
259 struct fscrypt_str de_name = FSTR_INIT(de->name,
263 /* Directory is encrypted */
264 - err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
265 + presented_len = critical_chars(de->name, de->name_len);
266 + err = ext4_fname_alloc_buffer(dir, presented_len,
267 + &fname_crypto_str);
273 + err = ext4_fname_disk_to_usr(dir, hinfo->hash,
274 hinfo->minor_hash, &de_name,
277 + ext4_fname_free_buffer(&fname_crypto_str);
281 err = ext4_htree_store_dirent(dir_file,
282 hinfo->hash, hinfo->minor_hash, de,
284 - fname_crypto_str.len = save_len;
285 + ext4_fname_free_buffer(&fname_crypto_str);
289 @@ -1614,7 +1614,7 @@ static void dx_insert_block(struct dx_fr
290 * Return: %true if the directory entry matches, otherwise %false.
292 static inline bool ext4_match(const struct ext4_filename *fname,
293 - const struct ext4_dir_entry_2 *de)
294 + const struct ext4_dir_entry_2 *de, int denamelen)
296 struct fscrypt_name f;
298 @@ -1626,7 +1626,7 @@ static inline bool ext4_match(const s
299 #ifdef CONFIG_EXT4_FS_ENCRYPTION
300 f.crypto_buf = fname->crypto_buf;
302 - return fscrypt_match_name(&f, de->name, de->name_len);
303 + return fscrypt_match_name(&f, de->name, denamelen);
307 @@ -1637,16 +1637,30 @@ int ext4_search_dir(struct buffer_hea
308 unsigned int offset, struct ext4_dir_entry_2 **res_dir)
310 struct ext4_dir_entry_2 * de;
311 + bool probablytrunc;
314 + int de_len, denamelen;
316 de = (struct ext4_dir_entry_2 *)search_buf;
317 dlimit = search_buf + buf_size;
318 + /* fname is probably truncated if it is the decoded representation of
319 + * an encrypted filename not aligned on a 32-byte boundary
321 + probablytrunc = !IS_LUSTRE_MOUNT(dir->i_sb) && IS_ENCRYPTED(dir) &&
322 + fname->disk_name.len & 31;
323 while ((char *) de < dlimit) {
324 /* this code is executed quadratically often */
325 /* do minimal checking `by hand' */
326 + denamelen = de->name_len;
327 + if (unlikely(probablytrunc) &&
328 + de->name_len > fname->disk_name.len)
329 + /* Adjust name len to look for a partial match.
330 + * Since it is binary encrypted names, there
331 + * should not be any collision between names.
333 + denamelen = fname->disk_name.len;
334 if ((char *) de + de->name_len <= dlimit &&
335 - ext4_match(fname, de)) {
336 + ext4_match(fname, de, denamelen)) {
337 /* found a match - just to be sure, do
339 if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
340 @@ -1707,7 +1717,7 @@ struct buffer_head *__ext4_find_entry
341 if (namelen > EXT4_NAME_LEN)
344 - retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
345 + retval = ext4_setup_filename(dir, d_name, 1, &fname);
346 if (retval == -ENOENT)
349 @@ -1834,7 +1844,8 @@ cleanup_and_exit:
350 /* Clean up the read-ahead blocks */
351 for (; ra_ptr < ra_max; ra_ptr++)
352 brelse(bh_use[ra_ptr]);
353 - ext4_fname_free_filename(&fname);
354 + if (fname.disk_name.name != d_name->name)
355 + kfree(fname.disk_name.name);
358 EXPORT_SYMBOL(__ext4_find_entry);
359 @@ -1900,7 +1911,7 @@ static struct dentry *ext4_lookup(str
360 struct buffer_head *bh;
363 - err = fscrypt_prepare_lookup(dir, dentry, flags);
364 + err = ext4_prepare_lookup(dir, dentry, flags);
368 @@ -1957,7 +1957,7 @@ static struct dentry *ext4_lookup(struct
370 if (!IS_ERR(inode) && IS_ENCRYPTED(dir) &&
371 (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
372 - !fscrypt_has_permitted_context(dir, inode)) {
373 + !ext4_has_permitted_context(dir, inode)) {
374 ext4_warning(inode->i_sb,
375 "Inconsistent encryption contexts: %lu/%lu",
376 dir->i_ino, inode->i_ino);
377 @@ -2206,7 +2221,7 @@ int ext4_find_dest_de(struct inode *d
378 if (ext4_check_dir_entry(dir, NULL, de, bh,
379 buf, buf_size, offset))
380 return -EFSCORRUPTED;
381 - if (ext4_match(fname, de))
382 + if (ext4_match(fname, de, de->name_len))
384 nlen = EXT4_DIR_ENTRY_LEN(de);
385 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);