Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[linux-2.6] / fs / cachefiles / key.c
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
15 static const char cachefiles_charmap[64] =
16         "0123456789"                    /* 0 - 9 */
17         "abcdefghijklmnopqrstuvwxyz"    /* 10 - 35 */
18         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"    /* 36 - 61 */
19         "_-"                            /* 62 - 63 */
20         ;
21
22 static 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  */
37 char *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 }