| /* Copyright 2020 The Fuchsia Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include "common.h" |
| |
| #include <factory_boot_kvs.h> |
| #include <cbor/cbor.h> |
| #include <cn-cbor/cn-cbor.h> |
| |
| static cn_cbor* s_cbor_head = NULL; |
| static uint8_t s_buf[FACTORY_BOOT_KVS_SIZE] __attribute__ ((aligned(16))) = {0}; |
| |
| FbKvsResult FbKvsInit(FbKvsOps* op) |
| { |
| cn_cbor_errback err; |
| if ((op == NULL) || (op->read == NULL)) { |
| FB_KVS_ERROR("INVALID args.\n"); |
| return kFbKvsResultInvalidArg; |
| } |
| |
| if (s_cbor_head != NULL) { |
| FB_KVS_ERROR("factory boot is already initialized\n"); |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult ret = op->read(s_buf, FACTORY_BOOT_KVS_SIZE); |
| if (ret != kFbKvsResultOk) { |
| FB_KVS_ERROR("failed to read data: %d\n", ret); |
| return kFbKvsResultInvalid; |
| } |
| |
| s_cbor_head = cn_cbor_decode(s_buf, FACTORY_BOOT_KVS_SIZE, &err); |
| if (s_cbor_head == NULL) { |
| /* check if the call failed because there is a padding at the end of data. |
| * Re-run decoding in this case |
| */ |
| if ((err.err == CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED) && (err.pos != 0)) { |
| s_cbor_head = cn_cbor_decode(s_buf, err.pos, &err); |
| } |
| |
| if (s_cbor_head == NULL) { |
| FB_KVS_ERROR("failed to parse data: position %zu, error %d\n", err.pos, err.err); |
| return kFbKvsResultInvalidData; |
| } |
| } |
| |
| return kFbKvsResultOk; |
| } |
| |
| void FbKvsDeinit(void) |
| { |
| if (s_cbor_head == NULL) { |
| cn_cbor_free(s_cbor_head); |
| s_cbor_head = NULL; |
| } |
| } |
| |
| FbKvsResult FbKvsGetNumberOfPairs(size_t* size) |
| { |
| cn_cbor* cp = NULL; |
| size_t num = 0; |
| |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if (size == NULL) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| *size = 0; |
| |
| /* counts keys and values together. should be even number */ |
| for (cp = s_cbor_head->first_child; cp; cp = cp->next) { |
| num++; |
| } |
| |
| /* fail if there are odd number of items */ |
| if (num & 1) { |
| return kFbKvsResultInvalid; |
| } |
| |
| *size = num/2; |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetKeyByIndex(uint32_t index, char* key, size_t size) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (size == 0)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| /* get a key info */ |
| cn_cbor* k = cn_cbor_index(s_cbor_head, index*2); |
| if (k == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| if (k->type != CN_CBOR_TEXT) { |
| return kFbKvsResultInvalidData; |
| } |
| |
| if ((size_t)(k->length + 1) > size) { |
| return kFbKvsResultBufTooSmall; |
| } |
| |
| memcpy(key, k->v.str, k->length); |
| key[k->length] = '\0'; |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetValueSize(const char* key, size_t* size) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (size == NULL)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| switch (cb->type) { |
| case CN_CBOR_UINT: |
| *size = sizeof(uint64_t); |
| break; |
| case CN_CBOR_INT: |
| *size = sizeof(int64_t); |
| break; |
| case CN_CBOR_BYTES: |
| *size = cb->length; |
| break; |
| case CN_CBOR_TEXT: |
| *size = cb->length + 1; |
| break; |
| default: |
| return kFbKvsResultInvalidData; |
| } |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetValueType(const char* key, FbKvsValueType* type) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (type == NULL)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| switch (cb->type) { |
| case CN_CBOR_UINT: |
| *type = kFbKvsTypeULong; |
| break; |
| case CN_CBOR_INT: |
| *type = kFbKvsTypeLong; |
| break; |
| case CN_CBOR_BYTES: |
| *type = kFbKvsTypeData; |
| break; |
| case CN_CBOR_TEXT: |
| *type = kFbKvsTypeString; |
| break; |
| default: |
| return kFbKvsResultInvalid; |
| } |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetString(const char* key, char* value, size_t* size) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (value == NULL) || (size == NULL) || (*size == 0)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| if (cb->type != CN_CBOR_TEXT) { |
| return kFbKvsResultTypeMismatch; |
| } |
| |
| if ((size_t)(cb->length + 1) > *size) { |
| return kFbKvsResultBufTooSmall; |
| } |
| |
| memcpy(value, cb->v.str, cb->length); |
| value[cb->length] = '\0'; |
| *size = cb->length + 1; |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetLong(const char* key, int64_t* value) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (value == NULL)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| switch (cb->type) { |
| case CN_CBOR_INT: |
| *value = cb->v.sint; |
| break; |
| default: |
| return kFbKvsResultTypeMismatch; |
| } |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetULong(const char* key, uint64_t* value) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (value == NULL)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| switch (cb->type) { |
| case CN_CBOR_UINT: |
| *value = cb->v.uint; |
| break; |
| default: |
| return kFbKvsResultTypeMismatch; |
| } |
| |
| return kFbKvsResultOk; |
| } |
| |
| FbKvsResult FbKvsGetData(const char* key, uint8_t* value, size_t* size) |
| { |
| if (s_cbor_head == NULL) { |
| return kFbKvsResultErrorNotInitialized; |
| } |
| |
| if ((key == NULL) || (value == NULL) || (size == NULL) || (*size == 0)) { |
| return kFbKvsResultInvalidArg; |
| } |
| |
| cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key); |
| if (cb == NULL) { |
| return kFbKvsResultNotFound; |
| } |
| |
| if (cb->type != CN_CBOR_BYTES) { |
| return kFbKvsResultTypeMismatch; |
| } |
| |
| if ((size_t)cb->length > *size) { |
| return kFbKvsResultBufTooSmall; |
| } |
| |
| memcpy(value, cb->v.bytes, cb->length); |
| *size = cb->length; |
| |
| return kFbKvsResultOk; |
| } |