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