xref: /OK3568_Linux_fs/kernel/fs/cachefiles/key.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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