1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <assert.h> 6#include <string.h> 7 8#include <kvstore/kvstore.h> 9#include <lib/cksum.h> 10 11// header, key, zero, value, zero 12#define RECLEN(ksz, vsz) (2 + (ksz) + 1 + (vsz) + 1) 13 14static_assert(sizeof(kvshdr_t) == 6 * sizeof(uint32_t), ""); 15 16void kvs_init(kvstore_t* kvs, void* buffer, size_t buflen) { 17 kvs->data = buffer; 18 kvs->datamax = buflen; 19 kvs->kvcount = 0; 20 if (buflen < sizeof(kvshdr_t)) { 21 kvs->datalen = kvs->datamax; 22 } else { 23 kvs->datalen = sizeof(kvshdr_t); 24 } 25} 26 27int kvs_load(kvstore_t* kvs, void* buffer, size_t buflen) { 28 // initially configure kvstore as invalid to provide 29 // some protection against using after ignoring the 30 // return value of load 31 kvs->data = buffer; 32 kvs->datalen = buflen; 33 kvs->datamax = buflen; 34 kvs->kvcount = 0; 35 36 kvshdr_t hdr; 37 if (buflen < sizeof(hdr)) { 38 return KVS_ERR_BAD_PARAM; 39 } 40 41 memcpy(&hdr, buffer, sizeof(hdr)); 42 if ((hdr.version != KVSTORE_VERSION) || (hdr.length < sizeof(hdr))) { 43 return KVS_ERR_PARSE_HDR; 44 } 45 if (hdr.length > buflen) { 46 return KVS_ERR_PARSE_HDR; 47 } 48 if (hdr.flags != 0) { 49 return KVS_ERR_PARSE_HDR; 50 } 51 if (hdr.reserved != 0) { 52 return KVS_ERR_PARSE_HDR; 53 } 54 55 uint32_t crc = crc32(0, buffer, sizeof(hdr) - sizeof(uint32_t)); 56 crc = crc32(crc, buffer + sizeof(hdr), hdr.length - sizeof(hdr)); 57 if (crc != hdr.crc) { 58 return KVS_ERR_PARSE_CRC; 59 } 60 61 size_t count = 0; 62 uint8_t *kv = buffer + sizeof(hdr); 63 uint8_t *rec = kv; 64 size_t avail = hdr.length - sizeof(hdr); 65 while (avail > 0) { 66 if (avail < 2) { 67 return KVS_ERR_PARSE_REC; 68 } 69 size_t klen = rec[0]; 70 size_t vlen = rec[1]; 71 size_t reclen = RECLEN(klen, vlen); 72 if (avail < reclen) { 73 return KVS_ERR_PARSE_REC; 74 } 75 if (rec[2 + klen] != 0) { 76 return KVS_ERR_PARSE_REC; 77 } 78 if (rec[2 + klen + 1 + vlen] != 0) { 79 return KVS_ERR_PARSE_REC; 80 } 81 rec += reclen; 82 avail -= reclen; 83 count++; 84 } 85 86 kvs->kvcount = count; 87 kvs->datalen = sizeof(hdr) + (rec - kv); 88 return KVS_OK; 89} 90 91int kvs_save(kvstore_t* kvs) { 92 if (kvs->datamax < sizeof(kvshdr_t)) { 93 return KVS_ERR_OUT_OF_SPACE; 94 } 95 kvshdr_t hdr; 96 hdr.version = KVSTORE_VERSION; 97 hdr.flags = 0; 98 hdr.length = kvs->datalen; 99 hdr.reserved = 0; 100 hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t)); 101 hdr.crc = crc32(hdr.crc, kvs->data + sizeof(hdr), hdr.length - sizeof(hdr)); 102 memcpy(kvs->data, &hdr, sizeof(hdr)); 103 return KVS_OK; 104} 105 106int kvs_addn(kvstore_t* kvs, const void* key, size_t klen, 107 const void* val, size_t vlen) { 108 // ensure valid parameters 109 if ((klen == 0) || (klen > 255) || (vlen > 255)) { 110 return KVS_ERR_BAD_PARAM; 111 } 112 113 // ensure available space 114 size_t reclen = RECLEN(klen, vlen); 115 if (reclen > (kvs->datamax - kvs->datalen)) { 116 return KVS_ERR_OUT_OF_SPACE; 117 } 118 119 uint8_t* rec = kvs->data + kvs->datalen; 120 *rec++ = klen; 121 *rec++ = vlen; 122 memcpy(rec, key, klen); 123 rec += klen; 124 *rec++ = 0; 125 memcpy(rec, val, vlen); 126 rec += vlen; 127 *rec++ = 0; 128 129 kvs->datalen += reclen; 130 kvs->kvcount++; 131 return KVS_OK; 132} 133 134int kvs_add(kvstore_t* kvs, const char* key, const char* value) { 135 return kvs_addn(kvs, key, strlen(key), value, strlen(value)); 136} 137 138 139int kvs_getn(kvstore_t* kvs, const void* key, size_t klen, 140 const void** val, size_t* vlen) { 141 uint8_t* rec = kvs->data + sizeof(kvshdr_t); 142 size_t count; 143 for (count = 0; count < kvs->kvcount; count++) { 144 size_t ksz = rec[0]; 145 size_t vsz = rec[1]; 146 if ((klen == ksz) && !memcmp(key, rec + 2, klen)) { 147 *val = rec + 2 + klen + 1; 148 if (vlen) { 149 *vlen = vsz; 150 } 151 return KVS_OK; 152 } 153 rec += RECLEN(ksz, vsz); 154 } 155 return KVS_ERR_NOT_FOUND; 156} 157 158const char* kvs_get(kvstore_t* kvs, const char* key, const char* fallback) { 159 const void* val; 160 if (kvs_getn(kvs, key, strlen(key), &val, NULL) == KVS_OK) { 161 return (const char*) val; 162 } else { 163 return fallback; 164 } 165} 166 167int kvs_foreach(kvstore_t* kvs, void *cookie, 168 int (*func)(void *cookie, const char* key, const char* val)) { 169 uint8_t* rec = kvs->data + sizeof(kvshdr_t); 170 size_t count; 171 for (count = 0; count < kvs->kvcount; count++) { 172 size_t ksz = rec[0]; 173 size_t vsz = rec[1]; 174 int r = func(cookie, (const char*) (rec + 2), (const char*) (rec + 2 + ksz + 1)); 175 if (r != KVS_OK) { 176 return r; 177 } 178 rec += RECLEN(ksz, vsz); 179 } 180 return KVS_OK; 181} 182