Linux Audio

Check our new training course

In-person Linux kernel drivers training

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