1 /* 2 * Copyright (C) 2017 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 21 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 24 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 27 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #define __BTSTACK_FILE__ "btstack_tlv_posix.c" 33 34 #include "btstack_tlv.h" 35 #include "btstack_tlv_posix.h" 36 #include "btstack_debug.h" 37 #include "btstack_util.h" 38 #include "btstack_debug.h" 39 #include "string.h" 40 41 #include <stdlib.h> 42 #include <string.h> 43 44 45 // Header: 46 // - Magic: 'BTstack' 47 // - Status: 48 // - bits 765432: reserved 49 // - bits 10: epoch 50 51 // Entries 52 // - Tag: 32 bit 53 // - Len: 32 bit 54 // - Value: Len in bytes 55 56 #define BTSTACK_TLV_HEADER_LEN 8 57 static const char * btstack_tlv_header_magic = "BTstack"; 58 59 #define DUMMY_SIZE 4 60 typedef struct tlv_entry { 61 void * next; 62 uint32_t tag; 63 uint32_t len; 64 uint8_t value[DUMMY_SIZE]; // dummy size 65 } tlv_entry_t; 66 67 static int btstack_tlv_posix_append_tag(btstack_tlv_posix_t * self, uint32_t tag, const uint8_t * data, uint32_t data_size){ 68 69 if (!self->file) return 1; 70 71 log_info("append tag %04x, len %u", tag, data_size); 72 73 uint8_t header[8]; 74 big_endian_store_32(header, 0, tag); 75 big_endian_store_32(header, 4, data_size); 76 size_t written_header = fwrite(header, 1, sizeof(header), self->file); 77 if (written_header != sizeof(header)) return 1; 78 size_t written_value = fwrite(data, 1, data_size, self->file); 79 if (written_value != data_size) return 1; 80 fflush(self->file); 81 return 1; 82 } 83 84 static tlv_entry_t * btstack_tlv_posix_find_entry(btstack_tlv_posix_t * self, uint32_t tag){ 85 btstack_linked_list_iterator_t it; 86 btstack_linked_list_iterator_init(&it, &self->entry_list); 87 while (btstack_linked_list_iterator_has_next(&it)){ 88 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 89 if (entry->tag != tag) continue; 90 return entry; 91 } 92 return NULL; 93 } 94 95 /** 96 * Delete Tag 97 * @param tag 98 */ 99 static void btstack_tlv_posix_delete_tag(void * context, uint32_t tag){ 100 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 101 btstack_linked_list_iterator_t it; 102 btstack_linked_list_iterator_init(&it, &self->entry_list); 103 while (btstack_linked_list_iterator_has_next(&it)){ 104 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 105 if (entry->tag != tag) continue; 106 btstack_linked_list_iterator_remove(&it); 107 btstack_tlv_posix_append_tag(self, tag, NULL, 0); 108 return; 109 } 110 } 111 112 /** 113 * Get Value for Tag 114 * @param tag 115 * @param buffer 116 * @param buffer_size 117 * @returns size of value 118 */ 119 static int btstack_tlv_posix_get_tag(void * context, uint32_t tag, uint8_t * buffer, uint32_t buffer_size){ 120 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 121 tlv_entry_t * entry = btstack_tlv_posix_find_entry(self, tag); 122 // not found 123 if (!entry) return 0; 124 // return len if buffer = NULL 125 if (!buffer) return entry->len; 126 // otherwise copy data into buffer 127 uint16_t bytes_to_copy = btstack_min(buffer_size, entry->len); 128 memcpy(buffer, &entry->value[0], bytes_to_copy); 129 return bytes_to_copy; 130 } 131 132 /** 133 * Store Tag 134 * @param tag 135 * @param data 136 * @param data_size 137 */ 138 static int btstack_tlv_posix_store_tag(void * context, uint32_t tag, const uint8_t * data, uint32_t data_size){ 139 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 140 141 // remove old entry 142 tlv_entry_t * old_entry = btstack_tlv_posix_find_entry(self, tag); 143 if (old_entry){ 144 btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); 145 free(old_entry); 146 } 147 148 // create new entry 149 uint32_t entry_size = sizeof(tlv_entry_t) - DUMMY_SIZE + data_size; 150 tlv_entry_t * new_entry = (tlv_entry_t *) malloc(entry_size); 151 if (!new_entry) return 0; 152 memset(new_entry, 0, entry_size); 153 new_entry->tag = tag; 154 new_entry->len = data_size; 155 memcpy(&new_entry->value[0], data, data_size); 156 157 // append new entry 158 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 159 160 // write new tag 161 btstack_tlv_posix_append_tag(self, tag, data, data_size); 162 163 return 0; 164 } 165 166 // returns 0 on success 167 static int btstack_tlv_posix_read_db(btstack_tlv_posix_t * self){ 168 // open file 169 log_info("open db %s", self->db_path); 170 self->file = fopen(self->db_path,"r+"); 171 uint8_t header[BTSTACK_TLV_HEADER_LEN]; 172 if (self->file){ 173 // checker header 174 size_t objects_read = fread(header, 1, BTSTACK_TLV_HEADER_LEN, self->file ); 175 int file_valid = 0; 176 if (objects_read == BTSTACK_TLV_HEADER_LEN){ 177 if (memcmp(header, btstack_tlv_header_magic, strlen(btstack_tlv_header_magic)) == 0){ 178 log_info("BTstack Magic Header found"); 179 // read entries 180 while (1){ 181 uint8_t entry[8]; 182 size_t entries_read = fread(entry, 1, sizeof(entry), self->file); 183 if (entries_read == 0){ 184 // EOF, we're good 185 file_valid = 1; 186 break; 187 } 188 if (entries_read == sizeof(entry)){ 189 uint32_t tag = big_endian_read_32(entry, 0); 190 uint32_t len = big_endian_read_32(entry, 4); 191 // arbitrary safetly check: values < 1000 bytes each 192 if (len > 1000) break; 193 tlv_entry_t * new_entry = (tlv_entry_t *) malloc(sizeof(tlv_entry_t) - DUMMY_SIZE + len); 194 if (!new_entry) return 0; 195 new_entry->tag = tag; 196 new_entry->len = len; 197 // read 198 size_t value_read = fread(&new_entry->value[0], 1, len, self->file); 199 if (value_read == len){ 200 // append new entry 201 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 202 } else { 203 // fail 204 free(new_entry); 205 break; 206 } 207 } 208 } 209 } 210 } 211 if (!file_valid) { 212 log_info("file invalid, re-create"); 213 fclose(self->file); 214 self->file = NULL; 215 } 216 } 217 if (!self->file){ 218 // create truncate file 219 self->file = fopen(self->db_path,"w+"); 220 memset(header, 0, sizeof(header)); 221 strcpy((char *)header, btstack_tlv_header_magic); 222 fwrite(header, 1, sizeof(header), self->file); 223 // write out all valid entries (if any) 224 btstack_linked_list_iterator_t it; 225 btstack_linked_list_iterator_init(&it, &self->entry_list); 226 while (btstack_linked_list_iterator_has_next(&it)){ 227 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 228 btstack_tlv_posix_append_tag(self, entry->tag, &entry->value[0], entry->len); 229 } 230 } 231 return 0; 232 } 233 234 static const btstack_tlv_t btstack_tlv_posix = { 235 /* int (*get_tag)(..); */ &btstack_tlv_posix_get_tag, 236 /* int (*store_tag)(..); */ &btstack_tlv_posix_store_tag, 237 /* void (*delete_tag)(v..); */ &btstack_tlv_posix_delete_tag, 238 }; 239 240 /** 241 * Init Tag Length Value Store 242 */ 243 const btstack_tlv_t * btstack_tlv_posix_init_instance(btstack_tlv_posix_t * self, const char * db_path){ 244 memset(self, 0, sizeof(btstack_tlv_posix_t)); 245 self->db_path = db_path; 246 247 // read DB 248 btstack_tlv_posix_read_db(self); 249 return &btstack_tlv_posix; 250 } 251 252