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 
14 static_assert(sizeof(kvshdr_t) == 6 * sizeof(uint32_t), "");
15 
kvs_init(kvstore_t * kvs,void * buffer,size_t buflen)16 void 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 
kvs_load(kvstore_t * kvs,void * buffer,size_t buflen)27 int 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 
kvs_save(kvstore_t * kvs)91 int 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 
kvs_addn(kvstore_t * kvs,const void * key,size_t klen,const void * val,size_t vlen)106 int 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 
kvs_add(kvstore_t * kvs,const char * key,const char * value)134 int 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 
kvs_getn(kvstore_t * kvs,const void * key,size_t klen,const void ** val,size_t * vlen)139 int 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 
kvs_get(kvstore_t * kvs,const char * key,const char * fallback)158 const 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 
kvs_foreach(kvstore_t * kvs,void * cookie,int (* func)(void * cookie,const char * key,const char * val))167 int 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