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
btstack_tlv_windows_append_tag(btstack_tlv_windows_t * self,uint32_t tag,const uint8_t * data,uint32_t data_size)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
btstack_tlv_windows_find_entry(btstack_tlv_windows_t * self,uint32_t tag)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 */
btstack_tlv_windows_delete_tag(void * context,uint32_t tag)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 */
btstack_tlv_windows_get_tag(void * context,uint32_t tag,uint8_t * buffer,uint32_t buffer_size)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 */
btstack_tlv_windows_store_tag(void * context,uint32_t tag,const uint8_t * data,uint32_t data_size)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
btstack_tlv_windows_read_db(btstack_tlv_windows_t * self)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 */
btstack_tlv_windows_init_instance(btstack_tlv_windows_t * self,const char * db_path)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 */
btstack_tlv_windows_deinit(btstack_tlv_windows_t * self)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