linux/fs/cachefiles/key.c
<<
>>
Prefs
   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(" = %s %d", key, len);
 154        return key;
 155}
 156