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_prepare_lookup(struct inode *dir,
82 + struct dentry *dentry,
85 + if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)))
87 + return fscrypt_prepare_lookup(dir, dentry, flags);
90 +static inline int ext4_fname_alloc_buffer(const struct inode *inode,
91 + u32 max_encrypted_len,
92 + struct fscrypt_str *crypto_str)
94 + crypto_str->name = kmalloc(max_encrypted_len + 1, GFP_NOFS);
95 + if (!crypto_str->name)
97 + crypto_str->len = max_encrypted_len;
101 +static inline void ext4_fname_free_buffer(struct fscrypt_str *crypto_str)
105 + kfree(crypto_str->name);
106 + crypto_str->name = NULL;
109 +static inline int ext4_fname_disk_to_usr(struct inode *inode,
110 + u32 hash, u32 minor_hash,
111 + const struct fscrypt_str *iname,
112 + struct fscrypt_str *oname)
116 + presented_len = critical_encode(iname->name, iname->len, oname->name);
117 + if (presented_len > NAME_MAX) {
118 + /* truncate at NAME_MAX,
119 + * or NAME_MAX-1 if name ends with '=' to avoid decoding issue
121 + presented_len = NAME_MAX;
122 + if (oname->name[presented_len - 1] == '=')
124 + oname->len = presented_len;
126 + oname->name[presented_len] = '\0';
131 +static inline int ext4_setup_filename(struct inode *dir,
132 + const struct qstr *iname,
134 + struct ext4_filename *fname)
136 + fname->usr_fname = iname;
138 + if (lookup && IS_ENCRYPTED(dir) &&
139 + unlikely(!IS_LUSTRE_MOUNT(dir->i_sb) &&
140 + strnchr(iname->name, iname->len, '='))) {
141 + /* Only proceed to critical decode if
142 + * iname contains escape char '='.
144 + int len = iname->len;
147 + buf = kmalloc(len, GFP_NOFS);
151 + len = critical_decode(iname->name, len, buf);
152 + fname->disk_name.name = (unsigned char *)buf;
153 + fname->disk_name.len = len;
157 + fname->disk_name.name = (unsigned char *) iname->name;
158 + fname->disk_name.len = iname->len;
162 +#endif /* _CRITICAL_ENCODE_H */
163 diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
167 #include <linux/iversion.h>
170 +#include "critical_encode.h"
172 static int ext4_dx_readdir(struct file *, struct dir_context *);
174 @@ -144,7 +145,8 @@ static int ext4_readdir(struct file *
178 - if (IS_ENCRYPTED(inode)) {
179 + /* disable decryption of filename, present only escaped name */
180 + if (0 && IS_ENCRYPTED(inode)) {
181 err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
184 @@ -258,22 +259,33 @@ static int ext4_readdir(struct file *
185 get_dtype(sb, de->file_type)))
188 - int save_len = fstr.len;
189 struct fscrypt_str de_name =
194 /* Directory is encrypted */
195 - err = fscrypt_fname_disk_to_usr(inode,
196 + presented_len = critical_chars(de->name,
198 + err = ext4_fname_alloc_buffer(inode,
204 + err = ext4_fname_disk_to_usr(inode,
205 0, 0, &de_name, &fstr);
207 - fstr.len = save_len;
210 + ext4_fname_free_buffer(&fstr);
214 + err = dir_emit(ctx,
215 de_name.name, de_name.len,
216 le32_to_cpu(de->inode),
217 - get_dtype(sb, de->file_type)))
218 + get_dtype(sb, de->file_type));
219 + ext4_fname_free_buffer(&fstr);
224 diff -wur a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
225 --- a/fs/ext4/ialloc.c
226 +++ b/fs/ext4/ialloc.c
228 #include "ext4_jbd2.h"
231 +#include "critical_encode.h"
233 #include <trace/events/ext4.h>
235 diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c
236 --- a/fs/ext4/namei.c
237 +++ b/fs/ext4/namei.c
242 +#include "critical_encode.h"
244 #include <trace/events/ext4.h>
246 @@ -1368,22 +1369,31 @@ static int htree_dirblock_to_tree(struct
247 hinfo->hash, hinfo->minor_hash, de,
250 - int save_len = fname_crypto_str.len;
251 struct fscrypt_str de_name = FSTR_INIT(de->name,
255 /* Directory is encrypted */
256 - err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
257 + presented_len = critical_chars(de->name, de->name_len);
258 + err = ext4_fname_alloc_buffer(dir, presented_len,
259 + &fname_crypto_str);
265 + err = ext4_fname_disk_to_usr(dir, hinfo->hash,
266 hinfo->minor_hash, &de_name,
269 + ext4_fname_free_buffer(&fname_crypto_str);
273 err = ext4_htree_store_dirent(dir_file,
274 hinfo->hash, hinfo->minor_hash, de,
276 - fname_crypto_str.len = save_len;
277 + ext4_fname_free_buffer(&fname_crypto_str);
281 @@ -1614,7 +1614,7 @@ static void dx_insert_block(struct dx_fr
282 * Return: %true if the directory entry matches, otherwise %false.
284 static inline bool ext4_match(const struct ext4_filename *fname,
285 - const struct ext4_dir_entry_2 *de)
286 + const struct ext4_dir_entry_2 *de, int denamelen)
288 struct fscrypt_name f;
290 @@ -1626,7 +1626,7 @@ static inline bool ext4_match(const s
291 #ifdef CONFIG_EXT4_FS_ENCRYPTION
292 f.crypto_buf = fname->crypto_buf;
294 - return fscrypt_match_name(&f, de->name, de->name_len);
295 + return fscrypt_match_name(&f, de->name, denamelen);
299 @@ -1637,16 +1637,30 @@ int ext4_search_dir(struct buffer_hea
300 unsigned int offset, struct ext4_dir_entry_2 **res_dir)
302 struct ext4_dir_entry_2 * de;
303 + bool probablytrunc;
306 + int de_len, denamelen;
308 de = (struct ext4_dir_entry_2 *)search_buf;
309 dlimit = search_buf + buf_size;
310 + /* fname is probably truncated if it is the decoded representation of
311 + * an encrypted filename not aligned on a 32-byte boundary
313 + probablytrunc = !IS_LUSTRE_MOUNT(dir->i_sb) && IS_ENCRYPTED(dir) &&
314 + fname->disk_name.len & 31;
315 while ((char *) de < dlimit) {
316 /* this code is executed quadratically often */
317 /* do minimal checking `by hand' */
318 + denamelen = de->name_len;
319 + if (unlikely(probablytrunc) &&
320 + de->name_len > fname->disk_name.len)
321 + /* Adjust name len to look for a partial match.
322 + * Since it is binary encrypted names, there
323 + * should not be any collision between names.
325 + denamelen = fname->disk_name.len;
326 if ((char *) de + de->name_len <= dlimit &&
327 - ext4_match(fname, de)) {
328 + ext4_match(fname, de, denamelen)) {
329 /* found a match - just to be sure, do
331 if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
332 @@ -1707,7 +1717,7 @@ struct buffer_head *__ext4_find_entry
333 if (namelen > EXT4_NAME_LEN)
336 - retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
337 + retval = ext4_setup_filename(dir, d_name, 1, &fname);
338 if (retval == -ENOENT)
341 @@ -1834,7 +1844,8 @@ cleanup_and_exit:
342 /* Clean up the read-ahead blocks */
343 for (; ra_ptr < ra_max; ra_ptr++)
344 brelse(bh_use[ra_ptr]);
345 - ext4_fname_free_filename(&fname);
346 + if (fname.disk_name.name != d_name->name)
347 + kfree(fname.disk_name.name);
350 EXPORT_SYMBOL(__ext4_find_entry);
351 @@ -1900,7 +1911,7 @@ static struct dentry *ext4_lookup(str
352 struct buffer_head *bh;
355 - err = fscrypt_prepare_lookup(dir, dentry, flags);
356 + err = ext4_prepare_lookup(dir, dentry, flags);
360 @@ -2206,7 +2221,7 @@ int ext4_find_dest_de(struct inode *d
361 if (ext4_check_dir_entry(dir, NULL, de, bh,
362 buf, buf_size, offset))
363 return -EFSCORRUPTED;
364 - if (ext4_match(fname, de))
365 + if (ext4_match(fname, de, de->name_len))
367 nlen = EXT4_DIR_ENTRY_LEN(de);
368 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);