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 BLUEKITCHEN GMBH 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 BLUEKITCHEN 21 * GMBH 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 58 #define MAX_TLV_VALUE_SIZE 2048 59 60 static const char * btstack_tlv_header_magic = "BTstack"; 61 62 #define DUMMY_SIZE 4 63 typedef struct tlv_entry { 64 void * next; 65 uint32_t tag; 66 uint32_t len; 67 uint8_t value[DUMMY_SIZE]; // dummy size 68 } tlv_entry_t; 69 70 // testing support 71 static bool btstack_tlv_posix_read_only = false; 72 73 static void btstack_tlv_posix_append_tag(btstack_tlv_posix_t * self, uint32_t tag, const uint8_t * data, uint32_t data_size){ 74 75 if (!self->file) return; 76 77 log_info("append tag %04x, len %u", tag, data_size); 78 79 uint8_t header[8]; 80 big_endian_store_32(header, 0, tag); 81 big_endian_store_32(header, 4, data_size); 82 size_t written_header = fwrite(header, 1, sizeof(header), self->file); 83 if (written_header != sizeof(header)) return; 84 if (data_size > 0) { 85 size_t written_value = fwrite(data, 1, data_size, self->file); 86 if (written_value != data_size) return; 87 } 88 fflush(self->file); 89 } 90 91 static tlv_entry_t * btstack_tlv_posix_find_entry(btstack_tlv_posix_t * self, uint32_t tag){ 92 btstack_linked_list_iterator_t it; 93 btstack_linked_list_iterator_init(&it, &self->entry_list); 94 while (btstack_linked_list_iterator_has_next(&it)){ 95 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 96 if (entry->tag != tag) continue; 97 return entry; 98 } 99 return NULL; 100 } 101 102 /** 103 * Delete Tag 104 * @param tag 105 */ 106 static void btstack_tlv_posix_delete_tag(void * context, uint32_t tag){ 107 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 108 btstack_linked_list_iterator_t it; 109 btstack_linked_list_iterator_init(&it, &self->entry_list); 110 while (btstack_linked_list_iterator_has_next(&it)){ 111 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 112 if (entry->tag != tag) continue; 113 btstack_linked_list_iterator_remove(&it); 114 free(entry); 115 btstack_tlv_posix_append_tag(self, tag, NULL, 0); 116 return; 117 } 118 } 119 120 /** 121 * Get Value for Tag 122 * @param tag 123 * @param buffer 124 * @param buffer_size 125 * @returns size of value 126 */ 127 static int btstack_tlv_posix_get_tag(void * context, uint32_t tag, uint8_t * buffer, uint32_t buffer_size){ 128 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 129 tlv_entry_t * entry = btstack_tlv_posix_find_entry(self, tag); 130 // not found 131 if (!entry) return 0; 132 // return len if buffer = NULL 133 if (!buffer) return entry->len; 134 // otherwise copy data into buffer 135 uint16_t bytes_to_copy = btstack_min(buffer_size, entry->len); 136 memcpy(buffer, &entry->value[0], bytes_to_copy); 137 return bytes_to_copy; 138 } 139 140 /** 141 * Store Tag 142 * @param tag 143 * @param data 144 * @param data_size 145 */ 146 static int btstack_tlv_posix_store_tag(void * context, uint32_t tag, const uint8_t * data, uint32_t data_size){ 147 btstack_tlv_posix_t * self = (btstack_tlv_posix_t *) context; 148 149 // enforce arbitrary max value size 150 btstack_assert(data_size <= MAX_TLV_VALUE_SIZE); 151 152 // remove old entry 153 tlv_entry_t * old_entry = btstack_tlv_posix_find_entry(self, tag); 154 if (old_entry){ 155 btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); 156 free(old_entry); 157 } 158 159 // create new entry 160 uint32_t entry_size = sizeof(tlv_entry_t) - DUMMY_SIZE + data_size; 161 tlv_entry_t * new_entry = (tlv_entry_t *) malloc(entry_size); 162 if (!new_entry) return 0; 163 memset(new_entry, 0, entry_size); 164 new_entry->tag = tag; 165 new_entry->len = data_size; 166 memcpy(&new_entry->value[0], data, data_size); 167 168 // append new entry 169 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 170 171 // write new tag 172 btstack_tlv_posix_append_tag(self, tag, data, data_size); 173 174 return 0; 175 } 176 177 // returns 0 on success 178 static int btstack_tlv_posix_read_db(btstack_tlv_posix_t * self){ 179 // open file 180 log_info("open db %s", self->db_path); 181 const char * mode = btstack_tlv_posix_read_only ? "r" : "r+"; 182 self->file = fopen(self->db_path, mode); 183 uint8_t header[BTSTACK_TLV_HEADER_LEN]; 184 if (self->file){ 185 // checker header 186 size_t objects_read = fread(header, 1, BTSTACK_TLV_HEADER_LEN, self->file ); 187 int file_valid = 0; 188 if (objects_read == BTSTACK_TLV_HEADER_LEN){ 189 if (memcmp(header, btstack_tlv_header_magic, strlen(btstack_tlv_header_magic)) == 0){ 190 log_info("BTstack Magic Header found"); 191 // read entries 192 while (true){ 193 uint8_t entry[8]; 194 size_t entries_read = fread(entry, 1, sizeof(entry), self->file); 195 if (entries_read == 0){ 196 // EOF, we're good 197 file_valid = 1; 198 break; 199 } 200 if (entries_read != sizeof(entry)) break; 201 202 uint32_t tag = big_endian_read_32(entry, 0); 203 uint32_t len = big_endian_read_32(entry, 4); 204 205 // arbitrary safety check: values <= MAX_TLV_VALUE_SIZE 206 if (len > MAX_TLV_VALUE_SIZE) break; 207 208 // create new entry for regular tag 209 tlv_entry_t * new_entry = NULL; 210 if (len > 0) { 211 new_entry = (tlv_entry_t *) malloc(sizeof(tlv_entry_t) - DUMMY_SIZE + len); 212 if (!new_entry) return 0; 213 new_entry->tag = tag; 214 new_entry->len = len; 215 216 // read 217 size_t value_read = fread(&new_entry->value[0], 1, len, self->file); 218 if (value_read != len) break; 219 } 220 221 // remove old entry 222 tlv_entry_t * old_entry = btstack_tlv_posix_find_entry(self, tag); 223 if (old_entry){ 224 btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); 225 free(old_entry); 226 } 227 228 // append new entry 229 if (new_entry){ 230 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 231 } 232 } 233 } 234 } 235 if (!file_valid) { 236 log_info("file invalid, re-create"); 237 fclose(self->file); 238 self->file = NULL; 239 } 240 } 241 242 // close file in read-only mode 243 if (btstack_tlv_posix_read_only && (self->file != NULL)){ 244 fclose(self->file); 245 self->file = NULL; 246 return 0; 247 } 248 249 if (!self->file){ 250 // create truncate file 251 self->file = fopen(self->db_path,"w+"); 252 if (!self->file) { 253 log_error("failed to create file"); 254 return -1; 255 } 256 memset(header, 0, sizeof(header)); 257 strcpy((char *)header, btstack_tlv_header_magic); 258 fwrite(header, 1, sizeof(header), self->file); 259 // write out all valid entries (if any) 260 btstack_linked_list_iterator_t it; 261 btstack_linked_list_iterator_init(&it, &self->entry_list); 262 while (btstack_linked_list_iterator_has_next(&it)){ 263 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 264 btstack_tlv_posix_append_tag(self, entry->tag, &entry->value[0], entry->len); 265 } 266 } 267 return 0; 268 } 269 270 static const btstack_tlv_t btstack_tlv_posix = { 271 /* int (*get_tag)(..); */ &btstack_tlv_posix_get_tag, 272 /* int (*store_tag)(..); */ &btstack_tlv_posix_store_tag, 273 /* void (*delete_tag)(v..); */ &btstack_tlv_posix_delete_tag, 274 }; 275 276 /** 277 * Init Tag Length Value Store 278 */ 279 const btstack_tlv_t * btstack_tlv_posix_init_instance(btstack_tlv_posix_t * self, const char * db_path){ 280 memset(self, 0, sizeof(btstack_tlv_posix_t)); 281 self->db_path = db_path; 282 283 // read DB 284 if (db_path != NULL){ 285 btstack_tlv_posix_read_db(self); 286 } 287 return &btstack_tlv_posix; 288 } 289 290 void btstack_tlv_posix_set_read_only(void){ 291 btstack_tlv_posix_read_only = true; 292 } 293 294 /** 295 * Free TLV entries 296 * @param self 297 */ 298 void btstack_tlv_posix_deinit(btstack_tlv_posix_t * self){ 299 // free all entries 300 btstack_linked_list_iterator_t it; 301 btstack_linked_list_iterator_init(&it, &self->entry_list); 302 while (btstack_linked_list_iterator_has_next(&it)){ 303 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 304 btstack_linked_list_iterator_remove(&it); 305 free(entry); 306 } 307 btstack_tlv_posix_read_only = true; 308 } 309