1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Key to pathname encoder
4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
8 #include <linux/slab.h>
11 static const char cachefiles_charmap
[64] =
12 "0123456789" /* 0 - 9 */
13 "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */
14 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */
18 static const char cachefiles_filecharmap
[256] = {
19 /* we skip space and tab and control chars */
20 [33 ... 46] = 1, /* '!' -> '.' */
21 /* we skip '/' as it's significant to pathwalk */
22 [48 ... 127] = 1, /* '0' -> '~' */
25 static inline unsigned int how_many_hex_digits(unsigned int x
)
27 return x
? round_up(ilog2(x
) + 1, 4) / 4 : 0;
31 * turn the raw key into something cooked
32 * - the key may be up to NAME_MAX in length (including the length word)
33 * - "base64" encode the strange keys, mapping 3 bytes of raw to four of
35 * - need to cut the cooked key into 252 char lengths (189 raw bytes)
37 bool cachefiles_cook_key(struct cachefiles_object
*object
)
39 const u8
*key
= fscache_get_key(object
->cookie
), *kend
;
41 unsigned int acc
, i
, n
, nle
, nbe
, keylen
= object
->cookie
->key_len
;
42 unsigned int b64len
, len
, print
, pad
;
45 _enter(",%u,%*phN", keylen
, keylen
, key
);
47 BUG_ON(keylen
> NAME_MAX
- 3);
50 for (i
= 0; i
< keylen
; i
++) {
52 print
&= cachefiles_filecharmap
[ch
];
55 /* If the path is usable ASCII, then we render it directly */
58 name
= kmalloc(len
+ 1, GFP_KERNEL
);
62 name
[0] = 'D'; /* Data object type, string encoding */
63 memcpy(name
+ 1, key
, keylen
);
67 /* See if it makes sense to encode it as "hex,hex,hex" for each 32-bit
68 * chunk. We rely on the key having been padded out to a whole number
71 n
= round_up(keylen
, 4);
73 for (i
= 0; i
< n
; i
+= 4) {
74 u32 be
= be32_to_cpu(*(__be32
*)(key
+ i
));
75 u32 le
= le32_to_cpu(*(__le32
*)(key
+ i
));
77 nbe
+= 1 + how_many_hex_digits(be
);
78 nle
+= 1 + how_many_hex_digits(le
);
81 b64len
= DIV_ROUND_UP(keylen
, 3);
82 pad
= b64len
* 3 - keylen
;
83 b64len
= 2 + b64len
* 4; /* Length if we base64-encode it */
84 _debug("len=%u nbe=%u nle=%u b64=%u", keylen
, nbe
, nle
, b64len
);
85 if (nbe
< b64len
|| nle
< b64len
) {
86 unsigned int nlen
= min(nbe
, nle
) + 1;
87 name
= kmalloc(nlen
, GFP_KERNEL
);
90 sep
= (nbe
<= nle
) ? 'S' : 'T'; /* Encoding indicator */
92 for (i
= 0; i
< n
; i
+= 4) {
95 x
= be32_to_cpu(*(__be32
*)(key
+ i
));
97 x
= le32_to_cpu(*(__le32
*)(key
+ i
));
100 len
+= snprintf(name
+ len
, nlen
- len
, "%x", x
);
106 /* We need to base64-encode it */
107 name
= kmalloc(b64len
+ 1, GFP_KERNEL
);
123 name
[len
++] = cachefiles_charmap
[acc
& 63];
125 name
[len
++] = cachefiles_charmap
[acc
& 63];
127 name
[len
++] = cachefiles_charmap
[acc
& 63];
129 name
[len
++] = cachefiles_charmap
[acc
& 63];
130 } while (key
< kend
);
134 object
->d_name
= name
;
135 object
->d_name_len
= len
;
136 _leave(" = %s", object
->d_name
);