Contiki-NG
Loading...
Searching...
No Matches
storage-cfs.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010, Swedish Institute of Computer Science
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/**
31 * \file
32 * Contiki File System (CFS) backend for the storage abstraction
33 * used by the database.
34 * \author
35 * Nicolas Tsiftes <nvt@sics.se>
36 */
37
38#include <stdio.h>
39#include <string.h>
40
41#include "cfs/cfs.h"
42#include "cfs/cfs-coffee.h"
43#include "lib/random.h"
44
45#define DEBUG DEBUG_NONE
46#include "net/ipv6/uip-debug.h"
47
48#include "db-options.h"
49#include "storage.h"
50
51struct attribute_record {
52 char name[ATTRIBUTE_NAME_LENGTH];
53 uint8_t domain;
54 uint8_t element_size;
55};
56
57struct index_record {
58 char attribute_name[ATTRIBUTE_NAME_LENGTH];
59 char file_name[DB_MAX_FILENAME_LENGTH];
60 uint8_t type;
61};
62
63#if DB_FEATURE_COFFEE
64#define DB_COFFEE_CATALOG_SIZE RELATION_NAME_LENGTH + \
65 (DB_MAX_ATTRIBUTES_PER_RELATION * \
66 sizeof(struct attribute_record))
67#endif
68
69#define ROW_XOR 0xf6U
70
71static bool
72merge_strings(char *dest, size_t dest_size, char *prefix, char *suffix)
73{
74 size_t prefix_len = strlen(prefix);
75 size_t suffix_len = strlen(suffix);
76
77 if(dest_size < prefix_len + suffix_len + 1) {
78 return false;
79 }
80
81 memcpy(dest, prefix, prefix_len);
82 memcpy(dest + prefix_len, suffix, suffix_len);
83 dest[prefix_len + suffix_len] = '\0';
84
85 return true;
86}
87
88char *
89storage_generate_file(char *prefix, unsigned long size)
90{
91 static char filename[ATTRIBUTE_NAME_LENGTH + sizeof(".ffff")];
92#if !DB_FEATURE_COFFEE
93 int fd;
94#endif
95
96 snprintf(filename, sizeof(filename), "%s.%x", prefix,
97 (unsigned)(random_rand() & 0xffff));
98
99#if DB_FEATURE_COFFEE
100 PRINTF("DB: Reserving %lu bytes in %s\n", size, filename);
101 if(cfs_coffee_reserve(filename, size) < 0) {
102 PRINTF("DB: Failed to reserve\n");
103 return NULL;
104 }
105 return filename;
106#else
107 fd = cfs_open(filename, CFS_WRITE);
108 cfs_close(fd);
109 return fd < 0 ? NULL : filename;
110#endif /* DB_FEATURE_COFFEE */
111}
112
113db_result_t
114storage_load(relation_t *rel)
115{
116 PRINTF("DB: Opening the tuple file %s\n", rel->tuple_filename);
117 rel->tuple_storage = cfs_open(rel->tuple_filename,
119 if(rel->tuple_storage < 0) {
120 PRINTF("DB: Failed to open the tuple file\n");
121 return DB_STORAGE_ERROR;
122 }
123
124 return DB_OK;
125}
126
127void
128storage_unload(relation_t *rel)
129{
130 if(RELATION_HAS_TUPLES(rel)) {
131 PRINTF("DB: Unload tuple file %s\n", rel->tuple_filename);
132
133 cfs_close(rel->tuple_storage);
134 rel->tuple_storage = -1;
135 }
136}
137
138db_result_t
139storage_get_relation(relation_t *rel, char *name)
140{
141 int fd;
142 int r;
143 int i;
144 struct attribute_record record;
145 db_result_t result;
146
147 fd = cfs_open(name, CFS_READ);
148 if(fd < 0) {
149 return DB_STORAGE_ERROR;
150 }
151
152 r = cfs_read(fd, rel->name, sizeof(rel->name));
153 if(r != sizeof(rel->name)) {
154 cfs_close(fd);
155 PRINTF("DB: Failed to read name, got %d of %d bytes\n",
156 r, sizeof(rel->name));
157 return DB_STORAGE_ERROR;
158 }
159
160 r = cfs_read(fd, rel->tuple_filename, sizeof(rel->tuple_filename));
161 if(r != sizeof(rel->name)) {
162 cfs_close(fd);
163 PRINTF("DB: Failed to read tuple filename\n");
164 return DB_STORAGE_ERROR;
165 }
166
167 rel->tuple_filename[sizeof(rel->tuple_filename) - 1] ^= ROW_XOR;
168
169 /* Read attribute records. */
170 result = DB_OK;
171 for(i = 0;; i++) {
172 r = cfs_read(fd, &record, sizeof(record));
173 if(r == 0) {
174 break;
175 }
176 if(r != sizeof(record)) {
177 PRINTF("DB: Failed to read attribute record %d (r = %d)\n", i, r);
178 result = DB_STORAGE_ERROR;
179 break;
180 }
181
182 if(relation_attribute_add(rel, DB_MEMORY, record.name,
183 record.domain, record.element_size) == NULL) {
184 PRINTF("DB: Failed to add the attribute %s\n", record.name);
185 result = DB_STORAGE_ERROR;
186 break;
187 }
188 }
189
190 (void)i;
191 PRINTF("DB: Read %d attributes\n", i);
192
193 cfs_close(fd);
194 return result;
195}
196
197db_result_t
198storage_put_relation(relation_t *rel)
199{
200 int fd;
201 int r;
202 char *str;
203 unsigned char *last_byte;
204
205 PRINTF("DB: put_relation(%s)\n", rel->name);
206
207 cfs_remove(rel->name);
208
209#if DB_FEATURE_COFFEE
210 cfs_coffee_reserve(rel->name, DB_COFFEE_CATALOG_SIZE);
211#endif
212
213 fd = cfs_open(rel->name, CFS_WRITE | CFS_READ);
214 if(fd < 0) {
215 return DB_STORAGE_ERROR;
216 }
217
218 r = cfs_write(fd, rel->name, sizeof(rel->name));
219 if(r != sizeof(rel->name)) {
220 cfs_close(fd);
221 cfs_remove(rel->name);
222 return DB_STORAGE_ERROR;
223 }
224
225 if(rel->tuple_filename[0] == '\0') {
226 str = storage_generate_file("tuple", DB_COFFEE_RESERVE_SIZE);
227 if(str == NULL) {
228 cfs_close(fd);
229 cfs_remove(rel->name);
230 return DB_STORAGE_ERROR;
231 }
232
233 strncpy(rel->tuple_filename, str, sizeof(rel->tuple_filename) - 1);
234 rel->tuple_filename[sizeof(rel->tuple_filename) - 1] = '\0';
235 }
236
237 /*
238 * Encode the last byte to ensure that the filename is not
239 * null-terminated. This will make the Coffee FS determine
240 * the correct length when re-opening the file.
241 */
242 last_byte = (unsigned char *)&rel->tuple_filename[sizeof(rel->tuple_filename) - 1];
243 *last_byte ^= ROW_XOR;
244
245 r = cfs_write(fd, rel->tuple_filename, sizeof(rel->tuple_filename));
246
247 *last_byte ^= ROW_XOR;
248
249 if(r != sizeof(rel->tuple_filename)) {
250 cfs_close(fd);
251 cfs_remove(rel->tuple_filename);
252 return DB_STORAGE_ERROR;
253 }
254
255 PRINTF("DB: Saved relation %s\n", rel->name);
256
257 cfs_close(fd);
258 return DB_OK;
259}
260
261db_result_t
262storage_put_attribute(relation_t *rel, attribute_t *attr)
263{
264 int fd;
265 struct attribute_record record;
266 int r;
267
268 PRINTF("DB: put_attribute(%s, %s)\n", rel->name, attr->name);
269
270 fd = cfs_open(rel->name, CFS_WRITE | CFS_APPEND);
271 if(fd < 0) {
272 return DB_STORAGE_ERROR;
273 }
274
275 memset(&record.name, 0, sizeof(record.name));
276 memcpy(record.name, attr->name, sizeof(record.name));
277 record.domain = attr->domain;
278 record.element_size = attr->element_size;
279 r = cfs_write(fd, &record, sizeof(record));
280 if(r != sizeof(record)) {
281 cfs_close(fd);
282 cfs_remove(rel->name);
283 return DB_STORAGE_ERROR;
284 }
285
286 cfs_close(fd);
287 return DB_OK;
288}
289
290db_result_t
291storage_drop_relation(relation_t *rel, int remove_tuples)
292{
293 if(remove_tuples && RELATION_HAS_TUPLES(rel)) {
294 cfs_remove(rel->tuple_filename);
295 }
296 return cfs_remove(rel->name) < 0 ? DB_STORAGE_ERROR : DB_OK;
297}
298
299#if DB_FEATURE_REMOVE
300db_result_t
301storage_rename_relation(char *old_name, char *new_name)
302{
303 db_result_t result;
304 int old_fd;
305 int new_fd;
306 int r;
307 char buf[64];
308
309 result = DB_STORAGE_ERROR;
310 old_fd = new_fd = -1;
311
312 old_fd = cfs_open(old_name, CFS_READ);
313 new_fd = cfs_open(new_name, CFS_WRITE);
314 if(old_fd < 0 || new_fd < 0) {
315 goto error;
316 }
317
318 for(;;) {
319 r = cfs_read(old_fd, buf, sizeof(buf));
320 if(r < 0) {
321 goto error;
322 } else if(r == 0) {
323 break;
324 }
325 if(cfs_write(new_fd, buf, r) != r) {
326 goto error;
327 }
328 };
329
330 cfs_remove(old_name);
331 result = DB_OK;
332
333error:
334 cfs_close(old_fd);
335 cfs_close(new_fd);
336
337 if(result != DB_OK) {
338 cfs_remove(new_name);
339 }
340 return result;
341}
342#endif /* DB_FEATURE_REMOVE */
343
344db_result_t
345storage_get_index(index_t *index, relation_t *rel, attribute_t *attr)
346{
347 char filename[INDEX_NAME_LENGTH + 1];
348 int fd;
349 int r;
350 struct index_record record;
351 db_result_t result;
352
353 if(!merge_strings(filename, sizeof(filename), rel->name, INDEX_NAME_SUFFIX)) {
354 return DB_NAME_ERROR;
355 }
356
357 fd = cfs_open(filename, CFS_READ);
358 if(fd < 0) {
359 return DB_STORAGE_ERROR;
360 }
361
362 for(result = DB_STORAGE_ERROR;;) {
363 r = cfs_read(fd, &record, sizeof(record));
364 if(r < sizeof(record)) {
365 break;
366 }
367 if(strcmp(attr->name, record.attribute_name) == 0) {
368 PRINTF("DB: Found the index record for %s.%s: type %d, filename %s\n",
369 rel->name, attr->name, record.type, record.file_name);
370 index->type = record.type;
371 memcpy(index->descriptor_file, record.file_name,
372 sizeof(index->descriptor_file));
373 result = DB_OK;
374 }
375 }
376
377 cfs_close(fd);
378
379 return result;
380}
381
382db_result_t
383storage_put_index(index_t *index)
384{
385 char filename[INDEX_NAME_LENGTH + 1];
386 int fd;
387 int r;
388 struct index_record record;
389 db_result_t result;
390
391 if(!merge_strings(filename, sizeof(filename), index->rel->name,
392 INDEX_NAME_SUFFIX)) {
393 return DB_NAME_ERROR;
394 }
395
396 fd = cfs_open(filename, CFS_WRITE | CFS_APPEND);
397 if(fd < 0) {
398 return DB_STORAGE_ERROR;
399 }
400
401 strcpy(record.attribute_name, index->attr->name);
402 memcpy(record.file_name, index->descriptor_file, sizeof(record.file_name));
403 record.type = index->type;
404
405 result = DB_OK;
406 r = cfs_write(fd, &record, sizeof(record));
407 if(r < sizeof(record)) {
408 result = DB_STORAGE_ERROR;
409 } else {
410 PRINTF("DB: Wrote an index record for %s.%s, type %d\n",
411 index->rel->name, index->attr->name, record.type);
412 }
413
414 cfs_close(fd);
415
416 return result;
417}
418
419db_result_t
420storage_get_row(relation_t *rel, tuple_id_t *tuple_id, storage_row_t row)
421{
422 int r;
423 tuple_id_t nrows;
424
425 if(DB_ERROR(storage_get_row_amount(rel, &nrows))) {
426 return DB_STORAGE_ERROR;
427 }
428
429 if(*tuple_id >= nrows) {
430 return DB_FINISHED;
431 }
432
433 if(cfs_seek(rel->tuple_storage, *tuple_id * rel->row_length, CFS_SEEK_SET) ==
434 (cfs_offset_t)-1) {
435 return DB_STORAGE_ERROR;
436 }
437
438 r = cfs_read(rel->tuple_storage, row, rel->row_length);
439 if(r < 0) {
440 PRINTF("DB: Reading failed on fd %d\n", rel->tuple_storage);
441 return DB_STORAGE_ERROR;
442 } else if(r == 0) {
443 return DB_FINISHED;
444 } else if(r < rel->row_length) {
445 PRINTF("DB: Incomplete record: %d < %d\n", r, rel->row_length);
446 return DB_STORAGE_ERROR;
447 }
448
449 row[rel->row_length - 1] ^= ROW_XOR;
450
451 PRINTF("DB: Read %d bytes from relation %s\n", rel->row_length, rel->name);
452
453 return DB_OK;
454}
455
456db_result_t
457storage_put_row(relation_t *rel, storage_row_t row)
458{
459 cfs_offset_t end;
460 unsigned remaining;
461 int r;
462 unsigned char *last_byte;
463#if DB_FEATURE_INTEGRITY
464 int missing_bytes;
465 char buf[rel->row_length];
466#endif
467
468 end = cfs_seek(rel->tuple_storage, 0, CFS_SEEK_END);
469 if(end == (cfs_offset_t)-1) {
470 return DB_STORAGE_ERROR;
471 }
472
473#if DB_FEATURE_INTEGRITY
474 missing_bytes = end % rel->row_length;
475 if(missing_bytes > 0) {
476 memset(buf, 0xff, sizeof(buf));
477 r = cfs_write(rel->tuple_storage, buf, sizeof(buf));
478 if(r != missing_bytes) {
479 return DB_STORAGE_ERROR;
480 }
481 }
482#endif
483
484 /* Ensure that last written byte is separated from 0, to make file
485 lengths correct in Coffee. */
486 last_byte = row + rel->row_length - 1;
487 *last_byte ^= ROW_XOR;
488
489 remaining = rel->row_length;
490 do {
491 r = cfs_write(rel->tuple_storage, row, remaining);
492 if(r < 0) {
493 PRINTF("DB: Failed to store %u bytes\n", remaining);
494 *last_byte ^= ROW_XOR;
495 return DB_STORAGE_ERROR;
496 }
497 row += r;
498 remaining -= r;
499 } while(remaining > 0);
500
501 PRINTF("DB: Stored a of %d bytes\n", rel->row_length);
502
503 *last_byte ^= ROW_XOR;
504
505 return DB_OK;
506}
507
508db_result_t
509storage_get_row_amount(relation_t *rel, tuple_id_t *amount)
510{
511 cfs_offset_t offset;
512
513 if(rel->row_length == 0) {
514 *amount = 0;
515 } else {
516 offset = cfs_seek(rel->tuple_storage, 0, CFS_SEEK_END);
517 if(offset == (cfs_offset_t)-1) {
518 return DB_STORAGE_ERROR;
519 }
520
521 *amount = (tuple_id_t)(offset / rel->row_length);
522 }
523
524 return DB_OK;
525}
526
527db_storage_id_t
528storage_open(const char *filename)
529{
530 int fd;
531
532 fd = cfs_open(filename, CFS_WRITE | CFS_READ);
533#if DB_FEATURE_COFFEE
534 if(fd >= 0) {
536 }
537#endif
538 return fd;
539}
540
541void
542storage_close(db_storage_id_t fd)
543{
544 cfs_close(fd);
545}
546
547db_result_t
548storage_read(db_storage_id_t fd,
549 void *buffer, unsigned long offset, unsigned length)
550{
551 char *ptr;
552 int r;
553
554 /* Extend the file if necessary, so that previously unwritten bytes
555 will be read in as zeroes. */
556 if(cfs_seek(fd, offset + length, CFS_SEEK_SET) == (cfs_offset_t)-1) {
557 return DB_STORAGE_ERROR;
558 }
559
560 if(cfs_seek(fd, offset, CFS_SEEK_SET) == (cfs_offset_t)-1) {
561 return DB_STORAGE_ERROR;
562 }
563
564 ptr = buffer;
565 while(length > 0) {
566 r = cfs_read(fd, ptr, length);
567 if(r <= 0) {
568 return DB_STORAGE_ERROR;
569 }
570 ptr += r;
571 length -= r;
572 }
573
574 return DB_OK;
575}
576
577db_result_t
578storage_write(db_storage_id_t fd,
579 void *buffer, unsigned long offset, unsigned length)
580{
581 char *ptr;
582 int r;
583
584 if(cfs_seek(fd, offset, CFS_SEEK_SET) == (cfs_offset_t)-1) {
585 return DB_STORAGE_ERROR;
586 }
587
588 ptr = buffer;
589 while(length > 0) {
590 r = cfs_write(fd, ptr, length);
591 if(r <= 0) {
592 return DB_STORAGE_ERROR;
593 }
594 ptr += r;
595 length -= r;
596 }
597
598 return DB_OK;
599}
Header for the Coffee file system.
CFS header file.
Database configuration options.
unsigned short random_rand(void)
Generates a new random number using the cc2538 RNG.
Definition random.c:58
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition cfs.h:95
int cfs_read(int f, void *buf, unsigned int len)
Read data from an open file.
Definition cfs-cooja.c:85
int cfs_coffee_reserve(const char *name, cfs_offset_t size)
Reserve space for a file.
#define CFS_APPEND
Specify that cfs_open() should append written data to the file rather than overwriting it.
Definition cfs.h:123
#define CFS_COFFEE_IO_FLASH_AWARE
Instruct Coffee that the access pattern to this file is adapted to flash I/O semantics by design,...
Definition cfs-coffee.h:53
int cfs_remove(const char *name)
Remove a file.
Definition cfs-cooja.c:147
int cfs_open(const char *n, int f)
Open a file.
Definition cfs-cooja.c:58
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file.
Definition cfs.h:132
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition cfs.h:109
cfs_offset_t cfs_seek(int f, cfs_offset_t o, int w)
Seek to a specified position in an open file.
Definition cfs-cooja.c:126
#define CFS_SEEK_END
Specify that cfs_seek() should compute the offset from the end of the file.
Definition cfs.h:150
int cfs_write(int f, const void *buf, unsigned int len)
Write data to an open file.
Definition cfs-cooja.c:102
void cfs_close(int f)
Close an open file.
Definition cfs-cooja.c:79
int cfs_coffee_set_io_semantics(int fd, unsigned flags)
Set the I/O semantics for accessing a file.
int cfs_offset_t
CFS directory entry name length.
Definition cfs.h:65
The storage interface used by the database.
A set of debugging macros for the IP stack.