xref: /btstack/platform/windows/btstack_tlv_windows.c (revision ced70f9bfeafe291ec597a3a9cc862e39e0da3ce)
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