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_windows.c" 33 34 #include "btstack_tlv.h" 35 #include "btstack_tlv_windows.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 static int btstack_tlv_windows_append_tag(btstack_tlv_windows_t * self, uint32_t tag, const uint8_t * data, uint32_t data_size){ 71 72 if (self->file == INVALID_HANDLE_VALUE) return 1; 73 74 log_info("append tag %04x, len %u", tag, data_size); 75 76 uint8_t header[8]; 77 big_endian_store_32(header, 0, tag); 78 big_endian_store_32(header, 4, data_size); 79 DWORD written_header = 0; 80 (void)WriteFile( 81 self->file, // file handle 82 &header, // start of data to write 83 sizeof(header), // number of bytes to write 84 &written_header, // number of bytes that were written 85 NULL); // no overlapped structure 86 87 if (written_header != sizeof(header)) return 1; 88 if (data_size > 0) { 89 DWORD written_value = 0; 90 (void)WriteFile( 91 self->file, // file handle 92 data, // start of data to write 93 data_size, // number of bytes to write 94 &written_value, // number of bytes that were written 95 NULL); // no overlapped structure 96 if (written_value != data_size) return 1; 97 } 98 return 1; 99 } 100 101 static tlv_entry_t * btstack_tlv_windows_find_entry(btstack_tlv_windows_t * self, uint32_t tag){ 102 btstack_linked_list_iterator_t it; 103 btstack_linked_list_iterator_init(&it, &self->entry_list); 104 while (btstack_linked_list_iterator_has_next(&it)){ 105 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 106 if (entry->tag != tag) continue; 107 return entry; 108 } 109 return NULL; 110 } 111 112 /** 113 * Delete Tag 114 * @param tag 115 */ 116 static void btstack_tlv_windows_delete_tag(void * context, uint32_t tag){ 117 btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; 118 btstack_linked_list_iterator_t it; 119 btstack_linked_list_iterator_init(&it, &self->entry_list); 120 while (btstack_linked_list_iterator_has_next(&it)){ 121 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 122 if (entry->tag != tag) continue; 123 btstack_linked_list_iterator_remove(&it); 124 free(entry); 125 btstack_tlv_windows_append_tag(self, tag, NULL, 0); 126 return; 127 } 128 } 129 130 /** 131 * Get Value for Tag 132 * @param tag 133 * @param buffer 134 * @param buffer_size 135 * @returns size of value 136 */ 137 static int btstack_tlv_windows_get_tag(void * context, uint32_t tag, uint8_t * buffer, uint32_t buffer_size){ 138 btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; 139 tlv_entry_t * entry = btstack_tlv_windows_find_entry(self, tag); 140 // not found 141 if (!entry) return 0; 142 // return len if buffer = NULL 143 if (!buffer) return entry->len; 144 // otherwise copy data into buffer 145 uint16_t bytes_to_copy = btstack_min(buffer_size, entry->len); 146 memcpy(buffer, &entry->value[0], bytes_to_copy); 147 return bytes_to_copy; 148 } 149 150 /** 151 * Store Tag 152 * @param tag 153 * @param data 154 * @param data_size 155 */ 156 static int btstack_tlv_windows_store_tag(void * context, uint32_t tag, const uint8_t * data, uint32_t data_size){ 157 btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; 158 159 // enforce arbitrary max value size 160 btstack_assert(data_size <= MAX_TLV_VALUE_SIZE); 161 162 // remove old entry 163 tlv_entry_t * old_entry = btstack_tlv_windows_find_entry(self, tag); 164 if (old_entry){ 165 btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); 166 free(old_entry); 167 } 168 169 // create new entry 170 uint32_t entry_size = sizeof(tlv_entry_t) - DUMMY_SIZE + data_size; 171 tlv_entry_t * new_entry = (tlv_entry_t *) malloc(entry_size); 172 if (!new_entry) return 0; 173 memset(new_entry, 0, entry_size); 174 new_entry->tag = tag; 175 new_entry->len = data_size; 176 memcpy(&new_entry->value[0], data, data_size); 177 178 // append new entry 179 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 180 181 // write new tag 182 btstack_tlv_windows_append_tag(self, tag, data, data_size); 183 184 return 0; 185 } 186 187 // returns 0 on success 188 static int btstack_tlv_windows_read_db(btstack_tlv_windows_t * self){ 189 // open file 190 log_info("open db %s", self->db_path); 191 self->file = CreateFile( 192 self->db_path, 193 GENERIC_READ | GENERIC_WRITE, 194 0, // do not share 195 NULL, // default security 196 OPEN_EXISTING, // only open if it exists 197 FILE_ATTRIBUTE_NORMAL, 198 NULL); 199 200 uint8_t header[BTSTACK_TLV_HEADER_LEN]; 201 if (self->file != INVALID_HANDLE_VALUE){ 202 // check header 203 bool ok = ReadFile(self->file, header, BTSTACK_TLV_HEADER_LEN, NULL, NULL); 204 int file_valid = 0; 205 if (ok){ 206 if (memcmp(header, btstack_tlv_header_magic, strlen(btstack_tlv_header_magic)) == 0){ 207 log_info("BTstack Magic Header found"); 208 // read entries 209 while (true){ 210 uint8_t entry[8]; 211 DWORD bytes_read; 212 ok = ReadFile(self->file, entry, sizeof(entry), &bytes_read, NULL); 213 if ( ok && (bytes_read == 0)) { 214 // EOF, we're good 215 file_valid = 1; 216 break; 217 } 218 219 if ( (ok == false) || (bytes_read != sizeof(entry))) { 220 break; 221 } 222 223 uint32_t tag = big_endian_read_32(entry, 0); 224 uint32_t len = big_endian_read_32(entry, 4); 225 226 // arbitrary safety check: values <= MAX_TLV_VALUE_SIZE 227 if (len > MAX_TLV_VALUE_SIZE) { 228 break; 229 } 230 231 // create new entry for regular tag 232 tlv_entry_t * new_entry = NULL; 233 if (len > 0) { 234 new_entry = (tlv_entry_t *) malloc(sizeof(tlv_entry_t) - DUMMY_SIZE + len); 235 if (!new_entry) return 0; 236 new_entry->tag = tag; 237 new_entry->len = len; 238 239 // read 240 DWORD value_read; 241 ok = ReadFile(self->file, &new_entry->value[0],len, &value_read, NULL); 242 if ((ok == false) || (value_read != len)) { 243 break; 244 } 245 } 246 247 // remove old entry 248 tlv_entry_t * old_entry = btstack_tlv_windows_find_entry(self, tag); 249 if (old_entry){ 250 btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); 251 free(old_entry); 252 } 253 254 // append new entry 255 if (new_entry){ 256 btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); 257 } 258 } 259 } 260 } 261 if (!file_valid) { 262 log_info("file invalid, re-create"); 263 CloseHandle(self->file); 264 self->file = INVALID_HANDLE_VALUE; 265 } 266 } 267 if (self->file == INVALID_HANDLE_VALUE){ 268 // create new file 269 self->file = CreateFile( 270 self->db_path, 271 GENERIC_WRITE, 272 0, // do not share 273 NULL, // default security 274 CREATE_ALWAYS, // create new file / replace old one 275 FILE_ATTRIBUTE_NORMAL, 276 NULL); 277 278 if (self->file == INVALID_HANDLE_VALUE) { 279 log_error("failed to create file"); 280 return -1; 281 } 282 memset(header, 0, sizeof(header)); 283 memcpy(header, btstack_tlv_header_magic, sizeof(btstack_tlv_header_magic)); 284 285 DWORD dwBytesWritten = 0; 286 (void)WriteFile( 287 self->file, // file handle 288 &header, // start of data to write 289 sizeof(header), // number of bytes to write 290 &dwBytesWritten, // number of bytes that were written 291 NULL); // no overlapped structure 292 UNUSED(dwBytesWritten); 293 294 // write out all valid entries (if any) 295 btstack_linked_list_iterator_t it; 296 btstack_linked_list_iterator_init(&it, &self->entry_list); 297 while (btstack_linked_list_iterator_has_next(&it)){ 298 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 299 btstack_tlv_windows_append_tag(self, entry->tag, &entry->value[0], entry->len); 300 } 301 } 302 return 0; 303 } 304 305 static const btstack_tlv_t btstack_tlv_windows = { 306 /* int (*get_tag)(..); */ &btstack_tlv_windows_get_tag, 307 /* int (*store_tag)(..); */ &btstack_tlv_windows_store_tag, 308 /* void (*delete_tag)(v..); */ &btstack_tlv_windows_delete_tag, 309 }; 310 311 /** 312 * Init Tag Length Value Store 313 */ 314 const btstack_tlv_t * btstack_tlv_windows_init_instance(btstack_tlv_windows_t * self, const char * db_path){ 315 memset(self, 0, sizeof(btstack_tlv_windows_t)); 316 self->db_path = db_path; 317 self->file = INVALID_HANDLE_VALUE; 318 319 // read DB 320 btstack_tlv_windows_read_db(self); 321 return &btstack_tlv_windows; 322 } 323 324 /** 325 * Free TLV entries 326 * @param self 327 */ 328 void btstack_tlv_windows_deinit(btstack_tlv_windows_t * self){ 329 // free all entries 330 btstack_linked_list_iterator_t it; 331 btstack_linked_list_iterator_init(&it, &self->entry_list); 332 while (btstack_linked_list_iterator_has_next(&it)){ 333 tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); 334 btstack_linked_list_iterator_remove(&it); 335 free(entry); 336 } 337 } 338