This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / cachefiles / cf-key.c
1 /* cf-key.c: Key to pathname encoder
2  *
3  * Copyright (C) 2006 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 License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, 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 /*
31  * turn the raw key into something cooked
32  * - the raw key should include the length in the two bytes at the front
33  * - the key may be up to 514 bytes in length (including the length word)
34  *   - "base64" encode the strange keys, mapping 3 bytes of raw to four of
35  *     cooked
36  *   - need to cut the cooked key into 252 char lengths (189 raw bytes)
37  */
38 char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type)
39 {
40         unsigned char csum, ch;
41         unsigned int acc;
42         char *key;
43         int loop, len, max, seg, mark, print;
44
45         _enter(",%d", keylen);
46
47         BUG_ON(keylen < 2 || keylen > 514);
48
49         csum = raw[0] + raw[1];
50         print = 1;
51         for (loop = 2; loop < keylen; loop++) {
52                 ch = raw[loop];
53                 csum += ch;
54                 print &= cachefiles_filecharmap[ch];
55         }
56
57         if (print) {
58                 /* if the path is usable ASCII, then we render it directly */
59                 max = keylen - 2;
60                 max += 2;       /* two base64'd length chars on the front */
61                 max += 5;       /* @checksum/M */
62                 max += 3 * 2;   /* maximum number of segment dividers (".../M")
63                                  * is ((514 + 251) / 252) = 3
64                                  */
65                 max += 1;       /* NUL on end */
66         } else {
67                 /* calculate the maximum length of the cooked key */
68                 keylen = (keylen + 2) / 3;
69
70                 max = keylen * 4;
71                 max += 5;       /* @checksum/M */
72                 max += 3 * 2;   /* maximum number of segment dividers (".../M")
73                                  * is ((514 + 188) / 189) = 3
74                                  */
75                 max += 1;       /* NUL on end */
76         }
77
78         _debug("max: %d", max);
79
80         key = kmalloc(max, GFP_KERNEL);
81         if (!key)
82                 return NULL;
83
84         len = 0;
85
86         /* build the cooked key */
87         sprintf(key, "@%02x/+", (unsigned) csum);
88         len = 5;
89         mark = len - 1;
90
91         if (print) {
92                 acc = *(uint16_t *) raw;
93                 raw += 2;
94
95                 key[len + 1] = cachefiles_charmap[acc & 63];
96                 acc >>= 6;
97                 key[len] = cachefiles_charmap[acc & 63];
98                 len += 2;
99
100                 seg = 250;
101                 for (loop = keylen; loop > 0; loop--) {
102                         if (seg <= 0) {
103                                 key[len++] = '/';
104                                 mark = len;
105                                 key[len++] = '+';
106                                 seg = 252;
107                         }
108
109                         key[len++] = *raw++;
110                         ASSERT(len < max);
111                 }
112
113                 switch (type) {
114                 case FSCACHE_COOKIE_TYPE_INDEX:         type = 'I';     break;
115                 case FSCACHE_COOKIE_TYPE_DATAFILE:      type = 'D';     break;
116                 default:                                type = 'S';     break;
117                 }
118         } else {
119                 seg = 252;
120                 for (loop = keylen; loop > 0; loop--) {
121                         if (seg <= 0) {
122                                 key[len++] = '/';
123                                 mark = len;
124                                 key[len++] = '+';
125                                 seg = 252;
126                         }
127
128                         acc = *raw++;
129                         acc |= *raw++ << 8;
130                         acc |= *raw++ << 16;
131
132                         _debug("acc: %06x", acc);
133
134                         key[len++] = cachefiles_charmap[acc & 63];
135                         acc >>= 6;
136                         key[len++] = cachefiles_charmap[acc & 63];
137                         acc >>= 6;
138                         key[len++] = cachefiles_charmap[acc & 63];
139                         acc >>= 6;
140                         key[len++] = cachefiles_charmap[acc & 63];
141
142                         ASSERT(len < max);
143                 }
144
145                 switch (type) {
146                 case FSCACHE_COOKIE_TYPE_INDEX:         type = 'J';     break;
147                 case FSCACHE_COOKIE_TYPE_DATAFILE:      type = 'E';     break;
148                 default:                                type = 'T';     break;
149                 }
150         }
151
152         key[mark] = type;
153         key[len] = 0;
154
155         _leave(" = %p %d:[%s]", key, len, key);
156         return key;
157 }