Whamcloud - gitweb
LU-16369 ldiskfs: do not check enc context at lookup
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / rhel8 / ext4-filename-encode.patch
1 diff -wur /dev/null b/fs/ext4/critical_encode.h
2 --- /dev/null
3 +++ b/fs/ext4/critical_encode.h
4 @@ -0,0 +1,166 @@
5 +/*
6 + *  critical_encode.h
7 + *
8 + *  Copyright (c) 2022 Whamcloud
9 + */
10 +
11 +#ifndef _CRITICAL_ENCODE_H
12 +#define _CRITICAL_ENCODE_H
13 +
14 +#include <linux/ctype.h>
15 +
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.
25 + */
26 +static inline int critical_encode(const u8 *src, int len, char *dst)
27 +{
28 +       u8 *p = (u8 *)src, *q = dst;
29 +
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 == '=')) {
34 +                       *(q++) = '=';
35 +                       *(q++) = *(p++) + 64;
36 +               } else {
37 +                       *(q++) = *(p++);
38 +               }
39 +       }
40 +
41 +       return (char *)q - dst;
42 +}
43 +
44 +/* returns the number of chars encoding would produce */
45 +static inline int critical_chars(const u8 *src, int len)
46 +{
47 +       u8 *p = (u8 *)src;
48 +       int newlen = len;
49 +
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 == '='))
54 +                       newlen++;
55 +               p++;
56 +       }
57 +
58 +       return newlen;
59 +}
60 +
61 +/* decoding routine - returns the number of chars in output */
62 +static inline int critical_decode(const u8 *src, int len, char *dst)
63 +{
64 +       u8 *p = (u8 *)src, *q = dst;
65 +
66 +       while (p - src < len) {
67 +               if (unlikely(*p == '=')) {
68 +                       *(q++) = *(++p) - 64;
69 +                       p++;
70 +               } else {
71 +                       *(q++) = *(p++);
72 +               }
73 +       }
74 +
75 +       return (char *)q - dst;
76 +}
77 +
78 +#define fscrypt_get_encryption_info(inode) \
79 +       (unlikely(!IS_LUSTRE_MOUNT(inode->i_sb)) ? 0 : -EOPNOTSUPP)
80 +
81 +static inline int ext4_has_permitted_context(struct inode *parent,
82 +                                       struct inode *child)
83 +{
84 +       if (unlikely(!IS_LUSTRE_MOUNT(parent->i_sb)))
85 +               return 1;
86 +       return fscrypt_has_permitted_context(parent, child);
87 +}
88 +
89 +static inline int ext4_prepare_lookup(struct inode *dir,
90 +                                         struct dentry *dentry,
91 +                                         unsigned int flags)
92 +{
93 +       if (unlikely(!IS_LUSTRE_MOUNT(dir->i_sb)))
94 +               return 0;
95 +        return fscrypt_prepare_lookup(dir, dentry, flags);
96 +}
97 +
98 +static inline int ext4_fname_alloc_buffer(const struct inode *inode,
99 +                                            u32 max_encrypted_len,
100 +                                            struct fscrypt_str *crypto_str)
101 +{
102 +       crypto_str->name = kmalloc(max_encrypted_len + 1, GFP_NOFS);
103 +       if (!crypto_str->name)
104 +               return -ENOMEM;
105 +       crypto_str->len = max_encrypted_len;
106 +       return 0;
107 +}
108 +
109 +static inline void ext4_fname_free_buffer(struct fscrypt_str *crypto_str)
110 +{
111 +       if (!crypto_str)
112 +               return;
113 +       kfree(crypto_str->name);
114 +       crypto_str->name = NULL;
115 +}
116 +
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)
121 +{
122 +       int presented_len;
123 +
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
128 +                */
129 +               presented_len = NAME_MAX;
130 +               if (oname->name[presented_len - 1] == '=')
131 +                       presented_len--;
132 +               oname->len = presented_len;
133 +       }
134 +       oname->name[presented_len] = '\0';
135 +
136 +       return 0;
137 +}
138 +
139 +static inline int ext4_setup_filename(struct inode *dir,
140 +                                        const struct qstr *iname,
141 +                                        int lookup,
142 +                                        struct ext4_filename *fname)
143 +{
144 +       fname->usr_fname = iname;
145 +
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 '='.
151 +                */
152 +               int len = iname->len;
153 +               char *buf;
154 +
155 +               buf = kmalloc(len, GFP_NOFS);
156 +               if (!buf)
157 +                       return -ENOMEM;
158 +
159 +               len = critical_decode(iname->name, len, buf);
160 +               fname->disk_name.name = (unsigned char *)buf;
161 +               fname->disk_name.len = len;
162 +               return 0;
163 +       }
164 +
165 +       fname->disk_name.name = (unsigned char *) iname->name;
166 +       fname->disk_name.len = iname->len;
167 +       return 0;
168 +}
169 +
170 +#endif /* _CRITICAL_ENCODE_H */
171 diff -wur a/fs/ext4/dir.c b/fs/ext4/dir.c
172 --- a/fs/ext4/dir.c
173 +++ b/fs/ext4/dir.c
174 @@ -28,6 +28,7 @@
175  #include <linux/iversion.h>
176  #include "ext4.h"
177  #include "xattr.h"
178 +#include "critical_encode.h"
179  
180  static int ext4_dx_readdir(struct file *, struct dir_context *);
181  
182 @@ -144,7 +145,8 @@ static int ext4_readdir(struct file *
183                         return err;
184         }
185  
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);
190                 if (err < 0)
191                         return err;
192 @@ -258,22 +259,33 @@ static int ext4_readdir(struct file *
193                                             get_dtype(sb, de->file_type)))
194                                                 goto done;
195                                 } else {
196 -                                       int save_len = fstr.len;
197                                         struct fscrypt_str de_name =
198                                                         FSTR_INIT(de->name,
199                                                                 de->name_len);
200 +                                       int presented_len;
201  
202                                         /* Directory is encrypted */
203 -                                       err = fscrypt_fname_disk_to_usr(inode,
204 +                                       presented_len = critical_chars(de->name,
205 +                                                                 de->name_len);
206 +                                       err = ext4_fname_alloc_buffer(inode,
207 +                                                                 presented_len,
208 +                                                                 &fstr);
209 +                                       if (err)
210 +                                               goto errout;
211 +
212 +                                       err = ext4_fname_disk_to_usr(inode,
213                                                 0, 0, &de_name, &fstr);
214                                         de_name = fstr;
215 -                                       fstr.len = save_len;
216 -                                       if (err)
217 +                                       if (err) {
218 +                                               ext4_fname_free_buffer(&fstr);
219                                                 goto errout;
220 -                                       if (!dir_emit(ctx,
221 +                                       }
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);
228 +                                       if (!err)
229                                                 goto done;
230                                 }
231                         }
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
235 @@ -30,6 +30,7 @@
236  #include "ext4_jbd2.h"
237  #include "xattr.h"
238  #include "acl.h"
239 +#include "critical_encode.h"
240  
241  #include <trace/events/ext4.h>
242  
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
246 @@ -40,6 +40,7 @@
247  
248  #include "xattr.h"
249  #include "acl.h"
250 +#include "critical_encode.h"
251  
252  #include <trace/events/ext4.h>
253  /*
254 @@ -1368,22 +1369,31 @@ static int htree_dirblock_to_tree(struct
255                                    hinfo->hash, hinfo->minor_hash, de,
256                                    &tmp_str);
257                 } else {
258 -                       int save_len = fname_crypto_str.len;
259                         struct fscrypt_str de_name = FSTR_INIT(de->name,
260                                                                 de->name_len);
261 +                       int presented_len;
262  
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);
268 +                       if (err) {
269 +                               count = err;
270 +                               goto errout;
271 +                       }
272 +
273 +                       err = ext4_fname_disk_to_usr(dir, hinfo->hash,
274                                         hinfo->minor_hash, &de_name,
275                                         &fname_crypto_str);
276                         if (err) {
277 +                               ext4_fname_free_buffer(&fname_crypto_str);
278                                 count = err;
279                                 goto errout;
280                         }
281                         err = ext4_htree_store_dirent(dir_file,
282                                    hinfo->hash, hinfo->minor_hash, de,
283                                         &fname_crypto_str);
284 -                       fname_crypto_str.len = save_len;
285 +                       ext4_fname_free_buffer(&fname_crypto_str);
286                 }
287                 if (err != 0) {
288                         count = err;
289 @@ -1614,7 +1614,7 @@ static void dx_insert_block(struct dx_fr
290   * Return: %true if the directory entry matches, otherwise %false.
291   */
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)
295  {
296         struct fscrypt_name f;
297  
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;
301  #endif
302 -       return fscrypt_match_name(&f, de->name, de->name_len);
303 +       return fscrypt_match_name(&f, de->name, denamelen);
304  }
305  
306  /*
307 @@ -1637,16 +1637,30 @@ int ext4_search_dir(struct buffer_hea
308                     unsigned int offset, struct ext4_dir_entry_2 **res_dir)
309  {
310         struct ext4_dir_entry_2 * de;
311 +       bool probablytrunc;
312         char * dlimit;
313 -       int de_len;
314 +       int de_len, denamelen;
315  
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
320 +        */
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.
332 +                        */
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
338                          * a full check */
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)
342                 return NULL;
343  
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)
347                 return NULL;
348         if (retval)
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);
356         return ret;
357  }
358  EXPORT_SYMBOL(__ext4_find_entry);
359 @@ -1900,7 +1911,7 @@ static struct dentry *ext4_lookup(str
360         struct buffer_head *bh;
361         int err;
362  
363 -       err = fscrypt_prepare_lookup(dir, dentry, flags);
364 +       err = ext4_prepare_lookup(dir, dentry, flags);
365         if (err)
366                 return ERR_PTR(err);
367  
368 @@ -1957,7 +1957,7 @@ static struct dentry *ext4_lookup(struct
369                 }
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))
383                         return -EEXIST;
384                 nlen = EXT4_DIR_ENTRY_LEN(de);
385                 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);