linux/fs/cachefiles/key.c
<<
>>
Prefs
   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}
 160