Contiki-NG
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 void
72merge_strings(char *dest, char *prefix, char *suffix)
73{
74 strcpy(dest, prefix);
75 strcat(dest, suffix);
76}
77
78char *
79storage_generate_file(char *prefix, unsigned long size)
80{
81 static char filename[ATTRIBUTE_NAME_LENGTH + sizeof(".ffff")];
82#if !DB_FEATURE_COFFEE
83 int fd;
84#endif
85
86 snprintf(filename, sizeof(filename), "%s.%x", prefix,
87 (unsigned)(random_rand() & 0xffff));
88
89#if DB_FEATURE_COFFEE
90 PRINTF("DB: Reserving %lu bytes in %s\n", size, filename);
91 if(cfs_coffee_reserve(filename, size) < 0) {
92 PRINTF("DB: Failed to reserve\n");
93 return NULL;
94 }
95 return filename;
96#else
97 fd = cfs_open(filename, CFS_WRITE);
98 cfs_close(fd);
99 return fd < 0 ? NULL : filename;
100#endif /* DB_FEATURE_COFFEE */
101}
102
103db_result_t
104storage_load(relation_t *rel)
105{
106 PRINTF("DB: Opening the tuple file %s\n", rel->tuple_filename);
107 rel->tuple_storage = cfs_open(rel->tuple_filename,
109 if(rel->tuple_storage < 0) {
110 PRINTF("DB: Failed to open the tuple file\n");
111 return DB_STORAGE_ERROR;
112 }
113
114 return DB_OK;
115}
116
117void
118storage_unload(relation_t *rel)
119{
120 if(RELATION_HAS_TUPLES(rel)) {
121 PRINTF("DB: Unload tuple file %s\n", rel->tuple_filename);
122
123 cfs_close(rel->tuple_storage);
124 rel->tuple_storage = -1;
125 }
126}
127
128db_result_t
129storage_get_relation(relation_t *rel, char *name)
130{
131 int fd;
132 int r;
133 int i;
134 struct attribute_record record;
135 db_result_t result;
136
137 fd = cfs_open(name, CFS_READ);
138 if(fd < 0) {
139 return DB_STORAGE_ERROR;
140 }
141
142 r = cfs_read(fd, rel->name, sizeof(rel->name));
143 if(r != sizeof(rel->name)) {
144 cfs_close(fd);
145 PRINTF("DB: Failed to read name, got %d of %d bytes\n",
146 r, sizeof(rel->name));
147 return DB_STORAGE_ERROR;
148 }
149
150 r = cfs_read(fd, rel->tuple_filename, sizeof(rel->tuple_filename));
151 if(r != sizeof(rel->name)) {
152 cfs_close(fd);
153 PRINTF("DB: Failed to read tuple filename\n");
154 return DB_STORAGE_ERROR;
155 }
156
157 rel->tuple_filename[sizeof(rel->tuple_filename) - 1] ^= ROW_XOR;
158
159 /* Read attribute records. */
160 result = DB_OK;
161 for(i = 0;; i++) {
162 r = cfs_read(fd, &record, sizeof(record));
163 if(r == 0) {
164 break;
165 }
166 if(r != sizeof(record)) {
167 PRINTF("DB: Failed to read attribute record %d (r = %d)\n", i, r);
168 result = DB_STORAGE_ERROR;
169 break;
170 }
171
172 if(relation_attribute_add(rel, DB_MEMORY, record.name,
173 record.domain, record.element_size) == NULL) {
174 PRINTF("DB: Failed to add the attribute %s\n", record.name);
175 result = DB_STORAGE_ERROR;
176 break;
177 }
178 }
179
180 PRINTF("DB: Read %d attributes\n", i);
181
182 cfs_close(fd);
183 return result;
184}
185
186db_result_t
187storage_put_relation(relation_t *rel)
188{
189 int fd;
190 int r;
191 char *str;
192 unsigned char *last_byte;
193
194 PRINTF("DB: put_relation(%s)\n", rel->name);
195
196 cfs_remove(rel->name);
197
198#if DB_FEATURE_COFFEE
199 cfs_coffee_reserve(rel->name, DB_COFFEE_CATALOG_SIZE);
200#endif
201
202 fd = cfs_open(rel->name, CFS_WRITE | CFS_READ);
203 if(fd < 0) {
204 return DB_STORAGE_ERROR;
205 }
206
207 r = cfs_write(fd, rel->name, sizeof(rel->name));
208 if(r != sizeof(rel->name)) {
209 cfs_close(fd);
210 cfs_remove(rel->name);
211 return DB_STORAGE_ERROR;
212 }
213
214 if(rel->tuple_filename[0] == '\0') {
215 str = storage_generate_file("tuple", DB_COFFEE_RESERVE_SIZE);
216 if(str == NULL) {
217 cfs_close(fd);
218 cfs_remove(rel->name);
219 return DB_STORAGE_ERROR;
220 }
221
222 strncpy(rel->tuple_filename, str, sizeof(rel->tuple_filename) - 1);
223 rel->tuple_filename[sizeof(rel->tuple_filename) - 1] = '\0';
224 }
225
226 /*
227 * Encode the last byte to ensure that the filename is not
228 * null-terminated. This will make the Coffee FS determine
229 * the correct length when re-opening the file.
230 */
231 last_byte = (unsigned char *)&rel->tuple_filename[sizeof(rel->tuple_filename) - 1];
232 *last_byte ^= ROW_XOR;
233
234 r = cfs_write(fd, rel->tuple_filename, sizeof(rel->tuple_filename));
235
236 *last_byte ^= ROW_XOR;
237
238 if(r != sizeof(rel->tuple_filename)) {
239 cfs_close(fd);
240 cfs_remove(rel->tuple_filename);
241 return DB_STORAGE_ERROR;
242 }
243
244 PRINTF("DB: Saved relation %s\n", rel->name);
245
246 cfs_close(fd);
247 return DB_OK;
248}
249
250db_result_t
251storage_put_attribute(relation_t *rel, attribute_t *attr)
252{
253 int fd;
254 struct attribute_record record;
255 int r;
256
257 PRINTF("DB: put_attribute(%s, %s)\n", rel->name, attr->name);
258
259 fd = cfs_open(rel->name, CFS_WRITE | CFS_APPEND);
260 if(fd < 0) {
261 return DB_STORAGE_ERROR;
262 }
263
264 memset(&record.name, 0, sizeof(record.name));
265 memcpy(record.name, attr->name, sizeof(record.name));
266 record.domain = attr->domain;
267 record.element_size = attr->element_size;
268 r = cfs_write(fd, &record, sizeof(record));
269 if(r != sizeof(record)) {
270 cfs_close(fd);
271 cfs_remove(rel->name);
272 return DB_STORAGE_ERROR;
273 }
274
275 cfs_close(fd);
276 return DB_OK;
277}
278
279db_result_t
280storage_drop_relation(relation_t *rel, int remove_tuples)
281{
282 if(remove_tuples && RELATION_HAS_TUPLES(rel)) {
283 cfs_remove(rel->tuple_filename);
284 }
285 return cfs_remove(rel->name) < 0 ? DB_STORAGE_ERROR : DB_OK;
286}
287
288#if DB_FEATURE_REMOVE
289db_result_t
290storage_rename_relation(char *old_name, char *new_name)
291{
292 db_result_t result;
293 int old_fd;
294 int new_fd;
295 int r;
296 char buf[64];
297
298 result = DB_STORAGE_ERROR;
299 old_fd = new_fd = -1;
300
301 old_fd = cfs_open(old_name, CFS_READ);
302 new_fd = cfs_open(new_name, CFS_WRITE);
303 if(old_fd < 0 || new_fd < 0) {
304 goto error;
305 }
306
307 for(;;) {
308 r = cfs_read(old_fd, buf, sizeof(buf));
309 if(r < 0) {
310 goto error;
311 } else if(r == 0) {
312 break;
313 }
314 if(cfs_write(new_fd, buf, r) != r) {
315 goto error;
316 }
317 };
318
319 cfs_remove(old_name);
320 result = DB_OK;
321
322error:
323 cfs_close(old_fd);
324 cfs_close(new_fd);
325
326 if(result != DB_OK) {
327 cfs_remove(new_name);
328 }
329 return result;
330}
331#endif /* DB_FEATURE_REMOVE */
332
333db_result_t
334storage_get_index(index_t *index, relation_t *rel, attribute_t *attr)
335{
336 char filename[INDEX_NAME_LENGTH + 1];
337 int fd;
338 int r;
339 struct index_record record;
340 db_result_t result;
341
342 merge_strings(filename, rel->name, INDEX_NAME_SUFFIX);
343
344 fd = cfs_open(filename, CFS_READ);
345 if(fd < 0) {
346 return DB_STORAGE_ERROR;
347 }
348
349 for(result = DB_STORAGE_ERROR;;) {
350 r = cfs_read(fd, &record, sizeof(record));
351 if(r < sizeof(record)) {
352 break;
353 }
354 if(strcmp(attr->name, record.attribute_name) == 0) {
355 PRINTF("DB: Found the index record for %s.%s: type %d, filename %s\n",
356 rel->name, attr->name, record.type, record.file_name);
357 index->type = record.type;
358 memcpy(index->descriptor_file, record.file_name,
359 sizeof(index->descriptor_file));
360 result = DB_OK;
361 }
362 }
363
364 cfs_close(fd);
365
366 return result;
367}
368
369db_result_t
370storage_put_index(index_t *index)
371{
372 char filename[INDEX_NAME_LENGTH + 1];
373 int fd;
374 int r;
375 struct index_record record;
376 db_result_t result;
377
378 merge_strings(filename, index->rel->name, INDEX_NAME_SUFFIX);
379
380 fd = cfs_open(filename, CFS_WRITE | CFS_APPEND);
381 if(fd < 0) {
382 return DB_STORAGE_ERROR;
383 }
384
385 strcpy(record.attribute_name, index->attr->name);
386 memcpy(record.file_name, index->descriptor_file, sizeof(record.file_name));
387 record.type = index->type;
388
389 result = DB_OK;
390 r = cfs_write(fd, &record, sizeof(record));
391 if(r < sizeof(record)) {
392 result = DB_STORAGE_ERROR;
393 } else {
394 PRINTF("DB: Wrote an index record for %s.%s, type %d\n",
395 index->rel->name, index->attr->name, record.type);
396 }
397
398 cfs_close(fd);
399
400 return result;
401}
402
403db_result_t
404storage_get_row(relation_t *rel, tuple_id_t *tuple_id, storage_row_t row)
405{
406 int r;
407 tuple_id_t nrows;
408
409 if(DB_ERROR(storage_get_row_amount(rel, &nrows))) {
410 return DB_STORAGE_ERROR;
411 }
412
413 if(*tuple_id >= nrows) {
414 return DB_FINISHED;
415 }
416
417 if(cfs_seek(rel->tuple_storage, *tuple_id * rel->row_length, CFS_SEEK_SET) ==
418 (cfs_offset_t)-1) {
419 return DB_STORAGE_ERROR;
420 }
421
422 r = cfs_read(rel->tuple_storage, row, rel->row_length);
423 if(r < 0) {
424 PRINTF("DB: Reading failed on fd %d\n", rel->tuple_storage);
425 return DB_STORAGE_ERROR;
426 } else if(r == 0) {
427 return DB_FINISHED;
428 } else if(r < rel->row_length) {
429 PRINTF("DB: Incomplete record: %d < %d\n", r, rel->row_length);
430 return DB_STORAGE_ERROR;
431 }
432
433 row[rel->row_length - 1] ^= ROW_XOR;
434
435 PRINTF("DB: Read %d bytes from relation %s\n", rel->row_length, rel->name);
436
437 return DB_OK;
438}
439
440db_result_t
441storage_put_row(relation_t *rel, storage_row_t row)
442{
443 cfs_offset_t end;
444 unsigned remaining;
445 int r;
446 unsigned char *last_byte;
447#if DB_FEATURE_INTEGRITY
448 int missing_bytes;
449 char buf[rel->row_length];
450#endif
451
452 end = cfs_seek(rel->tuple_storage, 0, CFS_SEEK_END);
453 if(end == (cfs_offset_t)-1) {
454 return DB_STORAGE_ERROR;
455 }
456
457#if DB_FEATURE_INTEGRITY
458 missing_bytes = end % rel->row_length;
459 if(missing_bytes > 0) {
460 memset(buf, 0xff, sizeof(buf));
461 r = cfs_write(rel->tuple_storage, buf, sizeof(buf));
462 if(r != missing_bytes) {
463 return DB_STORAGE_ERROR;
464 }
465 }
466#endif
467
468 /* Ensure that last written byte is separated from 0, to make file
469 lengths correct in Coffee. */
470 last_byte = row + rel->row_length - 1;
471 *last_byte ^= ROW_XOR;
472
473 remaining = rel->row_length;
474 do {
475 r = cfs_write(rel->tuple_storage, row, remaining);
476 if(r < 0) {
477 PRINTF("DB: Failed to store %u bytes\n", remaining);
478 *last_byte ^= ROW_XOR;
479 return DB_STORAGE_ERROR;
480 }
481 row += r;
482 remaining -= r;
483 } while(remaining > 0);
484
485 PRINTF("DB: Stored a of %d bytes\n", rel->row_length);
486
487 *last_byte ^= ROW_XOR;
488
489 return DB_OK;
490}
491
492db_result_t
493storage_get_row_amount(relation_t *rel, tuple_id_t *amount)
494{
495 cfs_offset_t offset;
496
497 if(rel->row_length == 0) {
498 *amount = 0;
499 } else {
500 offset = cfs_seek(rel->tuple_storage, 0, CFS_SEEK_END);
501 if(offset == (cfs_offset_t)-1) {
502 return DB_STORAGE_ERROR;
503 }
504
505 *amount = (tuple_id_t)(offset / rel->row_length);
506 }
507
508 return DB_OK;
509}
510
511db_storage_id_t
512storage_open(const char *filename)
513{
514 int fd;
515
516 fd = cfs_open(filename, CFS_WRITE | CFS_READ);
517#if DB_FEATURE_COFFEE
518 if(fd >= 0) {
520 }
521#endif
522 return fd;
523}
524
525void
526storage_close(db_storage_id_t fd)
527{
528 cfs_close(fd);
529}
530
531db_result_t
532storage_read(db_storage_id_t fd,
533 void *buffer, unsigned long offset, unsigned length)
534{
535 char *ptr;
536 int r;
537
538 /* Extend the file if necessary, so that previously unwritten bytes
539 will be read in as zeroes. */
540 if(cfs_seek(fd, offset + length, CFS_SEEK_SET) == (cfs_offset_t)-1) {
541 return DB_STORAGE_ERROR;
542 }
543
544 if(cfs_seek(fd, offset, CFS_SEEK_SET) == (cfs_offset_t)-1) {
545 return DB_STORAGE_ERROR;
546 }
547
548 ptr = buffer;
549 while(length > 0) {
550 r = cfs_read(fd, ptr, length);
551 if(r <= 0) {
552 return DB_STORAGE_ERROR;
553 }
554 ptr += r;
555 length -= r;
556 }
557
558 return DB_OK;
559}
560
561db_result_t
562storage_write(db_storage_id_t fd,
563 void *buffer, unsigned long offset, unsigned length)
564{
565 char *ptr;
566 int r;
567
568 if(cfs_seek(fd, offset, CFS_SEEK_SET) == (cfs_offset_t)-1) {
569 return DB_STORAGE_ERROR;
570 }
571
572 ptr = buffer;
573 while(length > 0) {
574 r = cfs_write(fd, ptr, length);
575 if(r <= 0) {
576 return DB_STORAGE_ERROR;
577 }
578 ptr += r;
579 length -= r;
580 }
581
582 return DB_OK;
583}
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 fd, void *buf, unsigned int len)
Read data from an open file.
Definition: cfs-sdcard.c:78
int cfs_coffee_reserve(const char *name, cfs_offset_t size)
Reserve space for a file.
Definition: cfs-coffee.c:1279
#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:150
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-sdcard.c:41
#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 fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-sdcard.c:94
#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 fd, const void *buf, unsigned int len)
Write data to an open file.
Definition: cfs-sdcard.c:86
void cfs_close(int fd)
Close an open file.
Definition: cfs-sdcard.c:69
int cfs_coffee_set_io_semantics(int fd, unsigned flags)
Set the I/O semantics for accessing a file.
Definition: cfs-coffee.c:1315
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.