1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* Key to pathname encoder
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
5*4882a593Smuzhiyun * Written by David Howells (dhowells@redhat.com)
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun #include "internal.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun static const char cachefiles_charmap[64] =
12*4882a593Smuzhiyun "0123456789" /* 0 - 9 */
13*4882a593Smuzhiyun "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */
14*4882a593Smuzhiyun "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */
15*4882a593Smuzhiyun "_-" /* 62 - 63 */
16*4882a593Smuzhiyun ;
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun static const char cachefiles_filecharmap[256] = {
19*4882a593Smuzhiyun /* we skip space and tab and control chars */
20*4882a593Smuzhiyun [33 ... 46] = 1, /* '!' -> '.' */
21*4882a593Smuzhiyun /* we skip '/' as it's significant to pathwalk */
22*4882a593Smuzhiyun [48 ... 127] = 1, /* '0' -> '~' */
23*4882a593Smuzhiyun };
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /*
26*4882a593Smuzhiyun * turn the raw key into something cooked
27*4882a593Smuzhiyun * - the raw key should include the length in the two bytes at the front
28*4882a593Smuzhiyun * - the key may be up to 514 bytes in length (including the length word)
29*4882a593Smuzhiyun * - "base64" encode the strange keys, mapping 3 bytes of raw to four of
30*4882a593Smuzhiyun * cooked
31*4882a593Smuzhiyun * - need to cut the cooked key into 252 char lengths (189 raw bytes)
32*4882a593Smuzhiyun */
cachefiles_cook_key(const u8 * raw,int keylen,uint8_t type)33*4882a593Smuzhiyun char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun unsigned char csum, ch;
36*4882a593Smuzhiyun unsigned int acc;
37*4882a593Smuzhiyun char *key;
38*4882a593Smuzhiyun int loop, len, max, seg, mark, print;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun _enter(",%d", keylen);
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun BUG_ON(keylen < 2 || keylen > 514);
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun csum = raw[0] + raw[1];
45*4882a593Smuzhiyun print = 1;
46*4882a593Smuzhiyun for (loop = 2; loop < keylen; loop++) {
47*4882a593Smuzhiyun ch = raw[loop];
48*4882a593Smuzhiyun csum += ch;
49*4882a593Smuzhiyun print &= cachefiles_filecharmap[ch];
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun if (print) {
53*4882a593Smuzhiyun /* if the path is usable ASCII, then we render it directly */
54*4882a593Smuzhiyun max = keylen - 2;
55*4882a593Smuzhiyun max += 2; /* two base64'd length chars on the front */
56*4882a593Smuzhiyun max += 5; /* @checksum/M */
57*4882a593Smuzhiyun max += 3 * 2; /* maximum number of segment dividers (".../M")
58*4882a593Smuzhiyun * is ((514 + 251) / 252) = 3
59*4882a593Smuzhiyun */
60*4882a593Smuzhiyun max += 1; /* NUL on end */
61*4882a593Smuzhiyun } else {
62*4882a593Smuzhiyun /* calculate the maximum length of the cooked key */
63*4882a593Smuzhiyun keylen = (keylen + 2) / 3;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun max = keylen * 4;
66*4882a593Smuzhiyun max += 5; /* @checksum/M */
67*4882a593Smuzhiyun max += 3 * 2; /* maximum number of segment dividers (".../M")
68*4882a593Smuzhiyun * is ((514 + 188) / 189) = 3
69*4882a593Smuzhiyun */
70*4882a593Smuzhiyun max += 1; /* NUL on end */
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun max += 1; /* 2nd NUL on end */
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun _debug("max: %d", max);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun key = kmalloc(max, cachefiles_gfp);
78*4882a593Smuzhiyun if (!key)
79*4882a593Smuzhiyun return NULL;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun len = 0;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* build the cooked key */
84*4882a593Smuzhiyun sprintf(key, "@%02x%c+", (unsigned) csum, 0);
85*4882a593Smuzhiyun len = 5;
86*4882a593Smuzhiyun mark = len - 1;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (print) {
89*4882a593Smuzhiyun acc = *(uint16_t *) raw;
90*4882a593Smuzhiyun raw += 2;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun key[len + 1] = cachefiles_charmap[acc & 63];
93*4882a593Smuzhiyun acc >>= 6;
94*4882a593Smuzhiyun key[len] = cachefiles_charmap[acc & 63];
95*4882a593Smuzhiyun len += 2;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun seg = 250;
98*4882a593Smuzhiyun for (loop = keylen; loop > 0; loop--) {
99*4882a593Smuzhiyun if (seg <= 0) {
100*4882a593Smuzhiyun key[len++] = '\0';
101*4882a593Smuzhiyun mark = len;
102*4882a593Smuzhiyun key[len++] = '+';
103*4882a593Smuzhiyun seg = 252;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun key[len++] = *raw++;
107*4882a593Smuzhiyun ASSERT(len < max);
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun switch (type) {
111*4882a593Smuzhiyun case FSCACHE_COOKIE_TYPE_INDEX: type = 'I'; break;
112*4882a593Smuzhiyun case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'D'; break;
113*4882a593Smuzhiyun default: type = 'S'; break;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun } else {
116*4882a593Smuzhiyun seg = 252;
117*4882a593Smuzhiyun for (loop = keylen; loop > 0; loop--) {
118*4882a593Smuzhiyun if (seg <= 0) {
119*4882a593Smuzhiyun key[len++] = '\0';
120*4882a593Smuzhiyun mark = len;
121*4882a593Smuzhiyun key[len++] = '+';
122*4882a593Smuzhiyun seg = 252;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun acc = *raw++;
126*4882a593Smuzhiyun acc |= *raw++ << 8;
127*4882a593Smuzhiyun acc |= *raw++ << 16;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun _debug("acc: %06x", acc);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun key[len++] = cachefiles_charmap[acc & 63];
132*4882a593Smuzhiyun acc >>= 6;
133*4882a593Smuzhiyun key[len++] = cachefiles_charmap[acc & 63];
134*4882a593Smuzhiyun acc >>= 6;
135*4882a593Smuzhiyun key[len++] = cachefiles_charmap[acc & 63];
136*4882a593Smuzhiyun acc >>= 6;
137*4882a593Smuzhiyun key[len++] = cachefiles_charmap[acc & 63];
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun ASSERT(len < max);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun switch (type) {
143*4882a593Smuzhiyun case FSCACHE_COOKIE_TYPE_INDEX: type = 'J'; break;
144*4882a593Smuzhiyun case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'E'; break;
145*4882a593Smuzhiyun default: type = 'T'; break;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun key[mark] = type;
150*4882a593Smuzhiyun key[len++] = 0;
151*4882a593Smuzhiyun key[len] = 0;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun _leave(" = %p %d", key, len);
154*4882a593Smuzhiyun return key;
155*4882a593Smuzhiyun }
156