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
btstack_tlv_posix_append_tag(btstack_tlv_posix_t * self,uint32_t tag,const uint8_t * data,uint32_t data_size)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
btstack_tlv_posix_find_entry(btstack_tlv_posix_t * self,uint32_t tag)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 */
btstack_tlv_posix_delete_tag(void * context,uint32_t tag)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 */
btstack_tlv_posix_get_tag(void * context,uint32_t tag,uint8_t * buffer,uint32_t buffer_size)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 */
btstack_tlv_posix_store_tag(void * context,uint32_t tag,const uint8_t * data,uint32_t data_size)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
btstack_tlv_posix_read_db(btstack_tlv_posix_t * self)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 */
btstack_tlv_posix_init_instance(btstack_tlv_posix_t * self,const char * db_path)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
btstack_tlv_posix_set_read_only(void)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 */
btstack_tlv_posix_deinit(btstack_tlv_posix_t * self)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