| /* |
| * Copyright (c) 2018 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <kvstore.h> |
| |
| // header, key, zero, value, zero |
| #define RECLEN(ksz, vsz) (2 + (ksz) + 1 + (vsz) + 1) |
| |
| void kvs_init(kvstore_t* kvs, void* buffer, size_t buflen) { |
| kvs->data = buffer; |
| kvs->datamax = buflen; |
| kvs->kvcount = 0; |
| if (buflen < sizeof(kvshdr_t)) { |
| kvs->datalen = kvs->datamax; |
| } else { |
| kvs->datalen = sizeof(kvshdr_t); |
| } |
| } |
| |
| int kvs_load(kvstore_t* kvs, void* buffer, size_t buflen) { |
| // initially configure kvstore as invalid to provide |
| // some protection against using after ignoring the |
| // return value of load |
| kvs->data = buffer; |
| kvs->datalen = buflen; |
| kvs->datamax = buflen; |
| kvs->kvcount = 0; |
| |
| kvshdr_t hdr; |
| if (buflen < sizeof(hdr)) { |
| return KVS_ERR_BAD_PARAM; |
| } |
| |
| memcpy(&hdr, buffer, sizeof(hdr)); |
| if ((hdr.version != KVSTORE_VERSION) || (hdr.length < sizeof(hdr))) { |
| return KVS_ERR_PARSE_HDR; |
| } |
| if (hdr.length > buflen) { |
| return KVS_ERR_PARSE_HDR; |
| } |
| if (hdr.flags != 0) { |
| return KVS_ERR_PARSE_HDR; |
| } |
| if (hdr.reserved != 0) { |
| return KVS_ERR_PARSE_HDR; |
| } |
| |
| uint32_t crc = crc32(0, buffer, sizeof(hdr) - sizeof(uint32_t)); |
| crc = crc32(crc, buffer + sizeof(hdr), hdr.length - sizeof(hdr)); |
| if (crc != hdr.crc) { |
| return KVS_ERR_PARSE_CRC; |
| } |
| |
| size_t count = 0; |
| uint8_t *kv = buffer + sizeof(hdr); |
| uint8_t *rec = kv; |
| size_t avail = hdr.length - sizeof(hdr); |
| while (avail > 0) { |
| if (avail < 2) { |
| return KVS_ERR_PARSE_REC; |
| } |
| size_t klen = rec[0]; |
| size_t vlen = rec[1]; |
| size_t reclen = RECLEN(klen, vlen); |
| if (avail < reclen) { |
| return KVS_ERR_PARSE_REC; |
| } |
| if (rec[2 + klen] != 0) { |
| return KVS_ERR_PARSE_REC; |
| } |
| if (rec[2 + klen + 1 + vlen] != 0) { |
| return KVS_ERR_PARSE_REC; |
| } |
| rec += reclen; |
| avail -= reclen; |
| count++; |
| } |
| |
| kvs->kvcount = count; |
| kvs->datalen = sizeof(hdr) + (rec - kv); |
| return KVS_OK; |
| } |
| |
| int kvs_save(kvstore_t* kvs) { |
| if (kvs->datamax < sizeof(kvshdr_t)) { |
| return KVS_ERR_OUT_OF_SPACE; |
| } |
| kvshdr_t hdr; |
| hdr.version = KVSTORE_VERSION; |
| hdr.flags = 0; |
| hdr.length = kvs->datalen; |
| hdr.reserved = 0; |
| hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t)); |
| hdr.crc = crc32(hdr.crc, kvs->data + sizeof(hdr), hdr.length - sizeof(hdr)); |
| memcpy(kvs->data, &hdr, sizeof(hdr)); |
| return KVS_OK; |
| } |
| |
| int kvs_addn(kvstore_t* kvs, const void* key, size_t klen, |
| const void* val, size_t vlen) { |
| // ensure valid parameters |
| if ((klen == 0) || (klen > 255) || (vlen > 255)) { |
| return KVS_ERR_BAD_PARAM; |
| } |
| |
| // ensure available space |
| size_t reclen = RECLEN(klen, vlen); |
| if (reclen > (kvs->datamax - kvs->datalen)) { |
| return KVS_ERR_OUT_OF_SPACE; |
| } |
| |
| uint8_t* rec = kvs->data + kvs->datalen; |
| *rec++ = klen; |
| *rec++ = vlen; |
| memcpy(rec, key, klen); |
| rec += klen; |
| *rec++ = 0; |
| memcpy(rec, val, vlen); |
| rec += vlen; |
| *rec++ = 0; |
| |
| kvs->datalen += reclen; |
| kvs->kvcount++; |
| return KVS_OK; |
| } |
| |
| int kvs_add(kvstore_t* kvs, const char* key, const char* value) { |
| return kvs_addn(kvs, key, strlen(key), value, strlen(value)); |
| } |
| |
| |
| int kvs_getn(kvstore_t* kvs, const void* key, size_t klen, |
| const void** val, size_t* vlen) { |
| uint8_t* rec = kvs->data + sizeof(kvshdr_t); |
| size_t count; |
| for (count = 0; count < kvs->kvcount; count++) { |
| size_t ksz = rec[0]; |
| size_t vsz = rec[1]; |
| if ((klen == ksz) && !memcmp(key, rec + 2, klen)) { |
| *val = rec + 2 + klen + 1; |
| if (vlen) { |
| *vlen = vsz; |
| } |
| return KVS_OK; |
| } |
| rec += RECLEN(ksz, vsz); |
| } |
| return KVS_ERR_NOT_FOUND; |
| } |
| |
| const char* kvs_get(kvstore_t* kvs, const char* key, const char* fallback) { |
| const void* val; |
| if (kvs_getn(kvs, key, strlen(key), &val, NULL) == KVS_OK) { |
| return (const char*) val; |
| } else { |
| return fallback; |
| } |
| } |
| |
| int kvs_foreach(kvstore_t* kvs, void *cookie, |
| int (*func)(void *cookie, const char* key, const char* val)) { |
| uint8_t* rec = kvs->data + sizeof(kvshdr_t); |
| size_t count; |
| for (count = 0; count < kvs->kvcount; count++) { |
| size_t ksz = rec[0]; |
| size_t vsz = rec[1]; |
| int r = func(cookie, (const char*) (rec + 2), (const char*) (rec + 2 + ksz + 1)); |
| if (r != KVS_OK) { |
| return r; |
| } |
| rec += RECLEN(ksz, vsz); |
| } |
| return KVS_OK; |
| } |