// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2018 The Fuchsia Authors * */ #include // 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; }