Contiki-NG
Loading...
Searching...
No Matches
cfs-coffee.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2008, 2009, 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 * This file is part of the Contiki operating system.
30 *
31 */
32
33/**
34 * \file
35 * Coffee: A file system for a variety of storage types in
36 * memory-constrained devices.
37 *
38 * For further information, see "Enabling Large-Scale Storage in
39 * Sensor Networks with the Coffee File System" in the proceedings
40 * of ACM/IEEE IPSN 2009.
41 *
42 * \author
43 * Nicolas Tsiftes <nvt@sics.se>
44 */
45
46#include <limits.h>
47#include <string.h>
48
49#define DEBUG 0
50#if DEBUG
51#include <stdio.h>
52#define PRINTF(...) printf(__VA_ARGS__)
53#else
54#define PRINTF(...)
55#endif
56
57#include "contiki.h"
58#include "cfs/cfs.h"
59#include "cfs-coffee-arch.h"
60#include "cfs/cfs-coffee.h"
61
62/* Micro logs enable modifications on storage types that do not support
63 in-place updates. This applies primarily to flash memories. */
64#ifndef COFFEE_MICRO_LOGS
65#define COFFEE_MICRO_LOGS 1
66#endif
67
68/* If the files are expected to be appended to only, this parameter
69 can be set to save some code space. */
70#ifndef COFFEE_APPEND_ONLY
71#define COFFEE_APPEND_ONLY 0
72#endif
73
74#if COFFEE_MICRO_LOGS && COFFEE_APPEND_ONLY
75#error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set."
76#endif
77
78/*
79 * Prevent sectors from being erased directly after file removal.
80 * This will level the wear across sectors better, but may lead
81 * to longer garbage collection procedures.
82 */
83#ifndef COFFEE_EXTENDED_WEAR_LEVELLING
84#define COFFEE_EXTENDED_WEAR_LEVELLING 1
85#endif
86
87#if COFFEE_START & (COFFEE_SECTOR_SIZE - 1)
88#error COFFEE_START must point to the first byte in a sector.
89#endif
90
91/* File descriptor flags. */
92#define COFFEE_FD_FREE 0x0
93#define COFFEE_FD_READ 0x1
94#define COFFEE_FD_WRITE 0x2
95#define COFFEE_FD_APPEND 0x4
96
97/* File object flags. */
98#define COFFEE_FILE_MODIFIED 0x1
99
100/* Internal Coffee markers. */
101#define INVALID_PAGE ((coffee_page_t)-1)
102#define UNKNOWN_OFFSET ((cfs_offset_t)-1)
103
104/* File removal actions. They can have the same values because
105 they are passed as separate parameters. */
106#define REMOVE_LOG 1
107#define CLOSE_FDS 1
108#define ALLOW_GC 1
109
110/* "Greedy" garbage collection erases as many sectors as possible. */
111#define GC_GREEDY 0
112/* "Reluctant" garbage collection stops after erasing one sector. */
113#define GC_RELUCTANT 1
114
115/* File descriptor macros. */
116#define FD_VALID(fd) ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
117 coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
118#define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ)
119#define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE)
120#define FD_APPENDABLE(fd) (coffee_fd_set[(fd)].flags & CFS_APPEND)
121
122/* File object macros. */
123#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
124#define FILE_FREE(file) ((file)->max_pages == 0)
125#define FILE_UNREFERENCED(file) ((file)->references == 0)
126
127/* File header flags. */
128#define HDR_FLAG_VALID 0x01 /* Completely written header. */
129#define HDR_FLAG_ALLOCATED 0x02 /* Allocated file. */
130#define HDR_FLAG_OBSOLETE 0x04 /* File marked for GC. */
131#define HDR_FLAG_MODIFIED 0x08 /* Modified file, log exists. */
132#define HDR_FLAG_LOG 0x10 /* Log file. */
133#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
134
135/* File header macros. */
136#define CHECK_FLAG(hdr, flag) ((hdr).flags & (flag))
137#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
138#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
139#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
140#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
141#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
142#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
143#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
144#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
145 !HDR_OBSOLETE(hdr) && \
146 !HDR_ISOLATED(hdr))
147
148/* Shortcuts derived from the hardware-dependent configuration of Coffee. */
149#define COFFEE_SECTOR_COUNT \
150 (coffee_page_t)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
151#define COFFEE_PAGE_COUNT \
152 ((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
153#define COFFEE_PAGES_PER_SECTOR \
154 ((coffee_page_t)(COFFEE_SECTOR_SIZE / COFFEE_PAGE_SIZE))
155
156/* This structure is used for garbage collection statistics. */
157struct sector_status {
158 coffee_page_t active;
159 coffee_page_t obsolete;
160 coffee_page_t free;
161};
162
163/* The structure of cached file objects. */
164struct file {
165 cfs_offset_t end;
166 coffee_page_t page;
167 coffee_page_t max_pages;
168 int16_t record_count;
169 uint8_t references;
170 uint8_t flags;
171};
172
173/* The file descriptor structure. */
174struct file_desc {
175 cfs_offset_t offset;
176 struct file *file;
177 uint8_t flags;
178 uint8_t io_flags;
179};
180
181/* The file header structure mimics the representation of file headers
182 in the physical storage medium. */
183struct file_header {
184 coffee_page_t log_page;
185 uint16_t log_records;
186 uint16_t log_record_size;
187 coffee_page_t max_pages;
188 uint8_t deprecated_eof_hint;
189 uint8_t flags;
190 char name[COFFEE_NAME_LENGTH];
191};
192
193/* This is needed because of a buggy compiler. */
194struct log_param {
195 cfs_offset_t offset;
196 char *buf;
197 uint16_t size;
198};
199
200/*
201 * Variables that keep track of opened files and internal
202 * optimization information for Coffee.
203 */
204static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
205static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
206static coffee_page_t next_free;
207static char gc_wait;
208
209/*---------------------------------------------------------------------------*/
210static void
211write_header(struct file_header *hdr, coffee_page_t page)
212{
213 hdr->flags |= HDR_FLAG_VALID;
214 COFFEE_WRITE(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
215}
216/*---------------------------------------------------------------------------*/
217static void
218read_header(struct file_header *hdr, coffee_page_t page)
219{
220 COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
221 if(DEBUG && HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
222 PRINTF("Coffee: Invalid header at page %u!\n", (unsigned)page);
223 }
224}
225/*---------------------------------------------------------------------------*/
226static cfs_offset_t
227absolute_offset(coffee_page_t page, cfs_offset_t offset)
228{
229 return page * COFFEE_PAGE_SIZE + sizeof(struct file_header) + offset;
230}
231/*---------------------------------------------------------------------------*/
232static coffee_page_t
233get_sector_status(coffee_page_t sector, struct sector_status *stats)
234{
235 static coffee_page_t skip_pages;
236 static char last_pages_are_active;
237 struct file_header hdr;
238 coffee_page_t active, obsolete, free;
239 coffee_page_t sector_start, sector_end;
240 coffee_page_t page;
241
242 memset(stats, 0, sizeof(*stats));
243 active = obsolete = free = 0;
244
245 /*
246 * get_sector_status() is an iterative function using local static
247 * state. It therefore requires that the caller starts iterating from
248 * sector 0 in order to reset the internal state.
249 */
250 if(sector == 0) {
251 skip_pages = 0;
252 last_pages_are_active = 0;
253 }
254
255 sector_start = sector * COFFEE_PAGES_PER_SECTOR;
256 sector_end = sector_start + COFFEE_PAGES_PER_SECTOR;
257
258 /*
259 * Account for pages belonging to a file starting in a previous
260 * segment that extends into this segment. If the whole segment is
261 * covered, we do not need to continue counting pages in this iteration.
262 */
263 if(last_pages_are_active) {
264 if(skip_pages >= COFFEE_PAGES_PER_SECTOR) {
265 stats->active = COFFEE_PAGES_PER_SECTOR;
266 skip_pages -= COFFEE_PAGES_PER_SECTOR;
267 return 0;
268 }
269 active = skip_pages;
270 } else {
271 if(skip_pages >= COFFEE_PAGES_PER_SECTOR) {
272 stats->obsolete = COFFEE_PAGES_PER_SECTOR;
273 skip_pages -= COFFEE_PAGES_PER_SECTOR;
274 return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages;
275 }
276 obsolete = skip_pages;
277 }
278
279 /* Determine the amount of pages of each type that have not been
280 accounted for yet in the current sector. */
281 for(page = sector_start + skip_pages; page < sector_end;) {
282 read_header(&hdr, page);
283 last_pages_are_active = 0;
284 if(HDR_ACTIVE(hdr)) {
285 last_pages_are_active = 1;
286 page += hdr.max_pages;
287 active += hdr.max_pages;
288 } else if(HDR_ISOLATED(hdr)) {
289 page++;
290 obsolete++;
291 } else if(HDR_OBSOLETE(hdr)) {
292 page += hdr.max_pages;
293 obsolete += hdr.max_pages;
294 } else {
295 free = sector_end - page;
296 break;
297 }
298 }
299
300 /*
301 * Determine the amount of pages in the following sectors that
302 * should be remembered for the next iteration. This is necessary
303 * because no file page except the first contains information
304 * about what type of page it is. A side effect of remembering this
305 * amount is that there is no need to read in the headers of each
306 * of these pages from the storage.
307 */
308 skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR;
309 if(skip_pages > 0) {
310 if(last_pages_are_active) {
311 active = COFFEE_PAGES_PER_SECTOR - obsolete;
312 } else {
313 obsolete = COFFEE_PAGES_PER_SECTOR - active;
314 }
315 }
316
317 stats->active = active;
318 stats->obsolete = obsolete;
319 stats->free = free;
320
321 /*
322 * To avoid unnecessary page isolation, we notify the caller that
323 * "skip_pages" pages should be isolated only if the current file extent
324 * ends in the next sector. If the file extent ends in a more distant
325 * sector, however, the garbage collection can free the next sector
326 * immediately without requiring page isolation.
327 */
328 return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ?
329 0 : skip_pages;
330}
331/*---------------------------------------------------------------------------*/
332static void
333isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
334{
335 struct file_header hdr;
336 coffee_page_t page;
337
338 /* Split an obsolete file starting in the previous sector and mark
339 the following pages as isolated. */
340 memset(&hdr, 0, sizeof(hdr));
341 hdr.flags = HDR_FLAG_ALLOCATED | HDR_FLAG_ISOLATED;
342
343 /* Isolation starts from the next sector. */
344 for(page = 0; page < skip_pages; page++) {
345 write_header(&hdr, start + page);
346 }
347 PRINTF("Coffee: Isolated %u pages starting in sector %d\n",
348 (unsigned)skip_pages, (int)start / COFFEE_PAGES_PER_SECTOR);
349}
350/*---------------------------------------------------------------------------*/
351static void
352collect_garbage(int mode)
353{
354 coffee_page_t sector;
355 struct sector_status stats;
356 coffee_page_t first_page, isolation_count;
357
358 PRINTF("Coffee: Running the garbage collector in %s mode\n",
359 mode == GC_RELUCTANT ? "reluctant" : "greedy");
360 /*
361 * The garbage collector erases as many sectors as possible. A sector is
362 * erasable if there are only free or obsolete pages in it.
363 */
364 for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
365 isolation_count = get_sector_status(sector, &stats);
366 PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n",
367 (unsigned)sector, (unsigned)stats.active,
368 (unsigned)stats.obsolete, (unsigned)stats.free);
369
370 if(stats.active > 0) {
371 continue;
372 }
373
374 if((mode == GC_RELUCTANT && stats.free == 0) ||
375 (mode == GC_GREEDY && stats.obsolete > 0)) {
376 first_page = sector * COFFEE_PAGES_PER_SECTOR;
377 if(first_page < next_free) {
378 next_free = first_page;
379 }
380
381 if(isolation_count > 0) {
382 isolate_pages(first_page + COFFEE_PAGES_PER_SECTOR, isolation_count);
383 }
384
385 COFFEE_ERASE(sector);
386 PRINTF("Coffee: Erased sector %d!\n", sector);
387
388 if(mode == GC_RELUCTANT && isolation_count > 0) {
389 break;
390 }
391 }
392 }
393}
394/*---------------------------------------------------------------------------*/
395static coffee_page_t
396next_file(coffee_page_t page, struct file_header *hdr)
397{
398 /*
399 * The quick-skip algorithm for finding file extents is the most
400 * essential part of Coffee. The file allocation rules enable this
401 * algorithm to quickly jump over free areas and allocated extents
402 * after reading single headers and determining their status.
403 *
404 * The worst-case performance occurs when we encounter multiple long
405 * sequences of isolated pages, but such sequences are uncommon and
406 * always shorter than a sector.
407 */
408 if(HDR_FREE(*hdr)) {
409 return (page + COFFEE_PAGES_PER_SECTOR) & ~(COFFEE_PAGES_PER_SECTOR - 1);
410 } else if(HDR_ISOLATED(*hdr)) {
411 return page + 1;
412 }
413 return page + hdr->max_pages;
414}
415/*---------------------------------------------------------------------------*/
416static struct file *
417load_file(coffee_page_t start, struct file_header *hdr)
418{
419 int i, unreferenced, free;
420 struct file *file;
421
422 /*
423 * We prefer to overwrite a free slot since unreferenced ones
424 * contain usable data. Free slots are designated by the page
425 * value INVALID_PAGE.
426 */
427 for(i = 0, unreferenced = free = -1; i < COFFEE_MAX_OPEN_FILES; i++) {
428 if(FILE_FREE(&coffee_files[i])) {
429 free = i;
430 break;
431 } else if(FILE_UNREFERENCED(&coffee_files[i])) {
432 unreferenced = i;
433 }
434 }
435
436 if(free == -1) {
437 if(unreferenced != -1) {
438 i = unreferenced;
439 } else {
440 return NULL;
441 }
442 }
443
444 file = &coffee_files[i];
445 file->page = start;
446 file->end = UNKNOWN_OFFSET;
447 file->max_pages = hdr->max_pages;
448 file->flags = HDR_MODIFIED(*hdr) ? COFFEE_FILE_MODIFIED : 0;
449 /* We don't know the amount of records yet. */
450 file->record_count = -1;
451
452 return file;
453}
454/*---------------------------------------------------------------------------*/
455static struct file *
456find_file(const char *name)
457{
458 int i;
459 struct file_header hdr;
460 coffee_page_t page;
461
462 /* First check if the file metadata is cached. */
463 for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) {
464 if(FILE_FREE(&coffee_files[i])) {
465 continue;
466 }
467
468 read_header(&hdr, coffee_files[i].page);
469 if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) {
470 return &coffee_files[i];
471 }
472 }
473
474 /* Scan the flash memory sequentially otherwise. */
475 for(page = 0; page < COFFEE_PAGE_COUNT; page = next_file(page, &hdr)) {
476 read_header(&hdr, page);
477 if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) {
478 return load_file(page, &hdr);
479 }
480 }
481
482 return NULL;
483}
484/*---------------------------------------------------------------------------*/
485static cfs_offset_t
486file_end(coffee_page_t start)
487{
488 struct file_header hdr;
489 unsigned char buf[COFFEE_PAGE_SIZE];
490 coffee_page_t page;
491 int i;
492
493 read_header(&hdr, start);
494
495 /*
496 * Move from the end of the range towards the beginning and look for
497 * a byte that has been modified.
498 *
499 * An important implication of this is that if the last written bytes
500 * are zeroes, then these are skipped from the calculation.
501 */
502
503 for(page = hdr.max_pages - 1; page >= 0; page--) {
504 COFFEE_READ(buf, sizeof(buf), (start + page) * COFFEE_PAGE_SIZE);
505 for(i = COFFEE_PAGE_SIZE - 1; i >= 0; i--) {
506 if(buf[i] != 0) {
507 if(page == 0 && i < sizeof(hdr)) {
508 return 0;
509 }
510 return 1 + i + (page * COFFEE_PAGE_SIZE) - sizeof(hdr);
511 }
512 }
513 }
514
515 /* All bytes are writable. */
516 return 0;
517}
518/*---------------------------------------------------------------------------*/
519static coffee_page_t
520find_contiguous_pages(coffee_page_t amount)
521{
522 coffee_page_t page, start;
523 struct file_header hdr;
524
525 start = INVALID_PAGE;
526 for(page = next_free; page < COFFEE_PAGE_COUNT;) {
527 read_header(&hdr, page);
528 if(HDR_FREE(hdr)) {
529 if(start == INVALID_PAGE) {
530 start = page;
531 if(start + amount >= COFFEE_PAGE_COUNT) {
532 /* We can stop immediately if the remaining pages are not enough. */
533 break;
534 }
535 }
536
537 /* All remaining pages in this sector are free --
538 jump to the next sector. */
539 page = next_file(page, &hdr);
540
541 if(start + amount <= page) {
542 if(start == next_free) {
543 next_free = start + amount;
544 }
545 return start;
546 }
547 } else {
548 start = INVALID_PAGE;
549 page = next_file(page, &hdr);
550 }
551 }
552 return INVALID_PAGE;
553}
554/*---------------------------------------------------------------------------*/
555static int
556remove_by_page(coffee_page_t page, int remove_log,
557 int close_fds, int gc_allowed)
558{
559 struct file_header hdr;
560 int i;
561
562 read_header(&hdr, page);
563 if(!HDR_ACTIVE(hdr)) {
564 return -1;
565 }
566
567 if(remove_log && HDR_MODIFIED(hdr)) {
568 if(remove_by_page(hdr.log_page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) {
569 return -1;
570 }
571 }
572
573 hdr.flags |= HDR_FLAG_OBSOLETE;
574 write_header(&hdr, page);
575
576 gc_wait = 0;
577
578 /* Close all file descriptors that reference the removed file. */
579 if(close_fds) {
580 for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
581 if(coffee_fd_set[i].file != NULL && coffee_fd_set[i].file->page == page) {
582 coffee_fd_set[i].flags = COFFEE_FD_FREE;
583 }
584 }
585 }
586
587 for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) {
588 if(coffee_files[i].page == page) {
589 coffee_files[i].page = INVALID_PAGE;
590 coffee_files[i].references = 0;
591 coffee_files[i].max_pages = 0;
592 }
593 }
594
595 if(!COFFEE_EXTENDED_WEAR_LEVELLING && gc_allowed) {
596 collect_garbage(GC_RELUCTANT);
597 }
598
599 return 0;
600}
601/*---------------------------------------------------------------------------*/
602static coffee_page_t
603page_count(cfs_offset_t size)
604{
605 return (size + sizeof(struct file_header) + COFFEE_PAGE_SIZE - 1) /
607}
608/*---------------------------------------------------------------------------*/
609static struct file *
610reserve(const char *name, coffee_page_t pages,
611 int allow_duplicates, unsigned flags)
612{
613 struct file_header hdr;
614 coffee_page_t page;
615 struct file *file;
616
617 if(!allow_duplicates && find_file(name) != NULL) {
618 return NULL;
619 }
620
621 page = find_contiguous_pages(pages);
622 if(page == INVALID_PAGE) {
623 if(gc_wait) {
624 return NULL;
625 }
626 collect_garbage(GC_GREEDY);
627 page = find_contiguous_pages(pages);
628 if(page == INVALID_PAGE) {
629 gc_wait = 1;
630 return NULL;
631 }
632 }
633
634 memset(&hdr, 0, sizeof(hdr));
635 strncpy(hdr.name, name, sizeof(hdr.name) - 1);
636 hdr.max_pages = pages;
637 hdr.flags = HDR_FLAG_ALLOCATED | flags;
638 write_header(&hdr, page);
639
640 PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n",
641 (unsigned)pages, (unsigned)page, name);
642
643 file = load_file(page, &hdr);
644 if(file != NULL) {
645 file->end = 0;
646 }
647
648 return file;
649}
650/*---------------------------------------------------------------------------*/
651#if COFFEE_MICRO_LOGS
652static void
653adjust_log_config(struct file_header *hdr,
654 uint16_t *log_record_size, uint16_t *log_records)
655{
656 *log_record_size = hdr->log_record_size == 0 ?
657 COFFEE_PAGE_SIZE : hdr->log_record_size;
658 *log_records = hdr->log_records == 0 ?
659 COFFEE_LOG_SIZE / *log_record_size : hdr->log_records;
660}
661#endif /* COFFEE_MICRO_LOGS */
662/*---------------------------------------------------------------------------*/
663#if COFFEE_MICRO_LOGS
664static uint16_t
665modify_log_buffer(uint16_t log_record_size,
666 cfs_offset_t *offset, uint16_t *size)
667{
668 uint16_t region;
669
670 region = *offset / log_record_size;
671 *offset %= log_record_size;
672
673 if(*size > log_record_size - *offset) {
674 *size = log_record_size - *offset;
675 }
676
677 return region;
678}
679#endif /* COFFEE_MICRO_LOGS */
680/*---------------------------------------------------------------------------*/
681#if COFFEE_MICRO_LOGS
682static int
683get_record_index(coffee_page_t log_page, uint16_t search_records,
684 uint16_t region)
685{
686 cfs_offset_t base;
687 uint16_t processed;
688 uint16_t batch_size;
689 int16_t match_index, i;
690
691 base = absolute_offset(log_page, sizeof(uint16_t) * search_records);
692 batch_size = search_records > COFFEE_LOG_TABLE_LIMIT ?
693 COFFEE_LOG_TABLE_LIMIT : search_records;
694 processed = 0;
695 match_index = -1;
696
697 {
698 uint16_t indices[batch_size];
699
700 while(processed < search_records && match_index < 0) {
701 if(batch_size + processed > search_records) {
702 batch_size = search_records - processed;
703 }
704
705 base -= batch_size * sizeof(indices[0]);
706 COFFEE_READ(&indices, sizeof(indices[0]) * batch_size, base);
707
708 for(i = batch_size - 1; i >= 0; i--) {
709 if(indices[i] - 1 == region) {
710 match_index = search_records - processed - (batch_size - i);
711 break;
712 }
713 }
714
715 processed += batch_size;
716 }
717 }
718
719 return match_index;
720}
721#endif /* COFFEE_MICRO_LOGS */
722/*---------------------------------------------------------------------------*/
723#if COFFEE_MICRO_LOGS
724static int
725read_log_page(struct file_header *hdr, int16_t record_count,
726 struct log_param *lp)
727{
728 uint16_t region;
729 int16_t match_index;
730 uint16_t log_record_size;
731 uint16_t log_records;
732 cfs_offset_t base;
733 uint16_t search_records;
734
735 adjust_log_config(hdr, &log_record_size, &log_records);
736 region = modify_log_buffer(log_record_size, &lp->offset, &lp->size);
737
738 search_records = record_count < 0 ? log_records : record_count;
739 match_index = get_record_index(hdr->log_page, search_records, region);
740 if(match_index < 0) {
741 return -1;
742 }
743
744 base = absolute_offset(hdr->log_page, log_records * sizeof(region));
745 base += (cfs_offset_t)match_index * log_record_size;
746 base += lp->offset;
747 COFFEE_READ(lp->buf, lp->size, base);
748
749 return lp->size;
750}
751#endif /* COFFEE_MICRO_LOGS */
752/*---------------------------------------------------------------------------*/
753#if COFFEE_MICRO_LOGS
754static coffee_page_t
755create_log(struct file *file, struct file_header *hdr)
756{
757 uint16_t log_record_size, log_records;
758 cfs_offset_t size;
759 struct file *log_file;
760
761 adjust_log_config(hdr, &log_record_size, &log_records);
762
763 /* Log index size + log data size. */
764 size = log_records * (sizeof(uint16_t) + log_record_size);
765
766 log_file = reserve(hdr->name, page_count(size), 1, HDR_FLAG_LOG);
767 if(log_file == NULL) {
768 return INVALID_PAGE;
769 }
770
771 hdr->flags |= HDR_FLAG_MODIFIED;
772 hdr->log_page = log_file->page;
773 write_header(hdr, file->page);
774
775 file->flags |= COFFEE_FILE_MODIFIED;
776 return log_file->page;
777}
778#endif /* COFFEE_MICRO_LOGS */
779/*---------------------------------------------------------------------------*/
780static int
781merge_log(coffee_page_t file_page, int extend)
782{
783 struct file_header hdr, hdr2;
784 int fd, n;
785 cfs_offset_t offset;
786 coffee_page_t max_pages;
787 struct file *new_file;
788 int i;
789
790 read_header(&hdr, file_page);
791
792 fd = cfs_open(hdr.name, CFS_READ);
793 if(fd < 0) {
794 return -1;
795 }
796
797 /*
798 * The reservation function adds extra space for the header, which has
799 * already been accounted for in the previous reservation.
800 */
801 max_pages = hdr.max_pages << extend;
802 new_file = reserve(hdr.name, max_pages, 1, 0);
803 if(new_file == NULL) {
804 cfs_close(fd);
805 return -1;
806 }
807
808 offset = 0;
809 do {
810 char buf[hdr.log_record_size == 0 ? COFFEE_PAGE_SIZE : hdr.log_record_size];
811 n = cfs_read(fd, buf, sizeof(buf));
812 if(n < 0) {
813 remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, ALLOW_GC);
814 cfs_close(fd);
815 return -1;
816 } else if(n > 0) {
817 COFFEE_WRITE(buf, n, absolute_offset(new_file->page, offset));
818 offset += n;
819 }
820 } while(n != 0);
821
822 for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
823 if(coffee_fd_set[i].flags != COFFEE_FD_FREE &&
824 coffee_fd_set[i].file->page == file_page) {
825 coffee_fd_set[i].file = new_file;
826 new_file->references++;
827 }
828 }
829
830 if(remove_by_page(file_page, REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) {
831 remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC);
832 cfs_close(fd);
833 return -1;
834 }
835
836 /* Copy the log configuration. */
837 read_header(&hdr2, new_file->page);
838 hdr2.log_record_size = hdr.log_record_size;
839 hdr2.log_records = hdr.log_records;
840 write_header(&hdr2, new_file->page);
841
842 new_file->flags &= ~COFFEE_FILE_MODIFIED;
843 new_file->end = offset;
844
845 cfs_close(fd);
846
847 return 0;
848}
849/*---------------------------------------------------------------------------*/
850#if COFFEE_MICRO_LOGS
851static int
852find_next_record(struct file *file, coffee_page_t log_page,
853 int log_records)
854{
855 int log_record, preferred_batch_size;
856
857 if(file->record_count >= 0) {
858 return file->record_count;
859 }
860
861 preferred_batch_size = log_records > COFFEE_LOG_TABLE_LIMIT ?
862 COFFEE_LOG_TABLE_LIMIT : log_records;
863 {
864 /* The next log record is unknown at this point; search for it. */
865 uint16_t indices[preferred_batch_size];
866 uint16_t processed;
867 uint16_t batch_size;
868
869 log_record = log_records;
870 for(processed = 0; processed < log_records; processed += batch_size) {
871 batch_size = log_records - processed >= preferred_batch_size ?
872 preferred_batch_size : log_records - processed;
873
874 COFFEE_READ(&indices, batch_size * sizeof(indices[0]),
875 absolute_offset(log_page, processed * sizeof(indices[0])));
876 for(log_record = 0; log_record < batch_size; log_record++) {
877 if(indices[log_record] == 0) {
878 log_record += processed;
879 break;
880 }
881 }
882 }
883 }
884
885 return log_record;
886}
887#endif /* COFFEE_MICRO_LOGS */
888/*---------------------------------------------------------------------------*/
889#if COFFEE_MICRO_LOGS
890static int
891write_log_page(struct file *file, struct log_param *lp)
892{
893 struct file_header hdr;
894 uint16_t region;
895 coffee_page_t log_page;
896 int16_t log_record;
897 uint16_t log_record_size;
898 uint16_t log_records;
899 cfs_offset_t offset;
900 struct log_param lp_out;
901
902 read_header(&hdr, file->page);
903
904 adjust_log_config(&hdr, &log_record_size, &log_records);
905 region = modify_log_buffer(log_record_size, &lp->offset, &lp->size);
906
907 log_page = 0;
908 if(HDR_MODIFIED(hdr)) {
909 /* A log structure has already been created. */
910 log_page = hdr.log_page;
911 log_record = find_next_record(file, log_page, log_records);
912 if(log_record >= log_records) {
913 /* The log is full; merge the log. */
914 PRINTF("Coffee: Merging the file %s with its log\n", hdr.name);
915 return merge_log(file->page, 0);
916 }
917 } else {
918 /* Create a log structure. */
919 log_page = create_log(file, &hdr);
920 if(log_page == INVALID_PAGE) {
921 return -1;
922 }
923 PRINTF("Coffee: Created a log structure for file %s at page %u\n",
924 hdr.name, (unsigned)log_page);
925 hdr.log_page = log_page;
926 log_record = 0;
927 }
928
929 {
930 char copy_buf[log_record_size];
931
932 lp_out.offset = offset = region * log_record_size;
933 lp_out.buf = copy_buf;
934 lp_out.size = log_record_size;
935
936 if((lp->offset > 0 || lp->size != log_record_size) &&
937 read_log_page(&hdr, log_record, &lp_out) < 0) {
938 COFFEE_READ(copy_buf, sizeof(copy_buf),
939 absolute_offset(file->page, offset));
940 }
941
942 memcpy(&copy_buf[lp->offset], lp->buf, lp->size);
943
944 /*
945 * Write the region number in the region index table.
946 * The region number is incremented to avoid values of zero.
947 */
948 offset = absolute_offset(log_page, 0);
949 ++region;
950 COFFEE_WRITE(&region, sizeof(region),
951 offset + log_record * sizeof(region));
952
953 offset += log_records * sizeof(region);
954 COFFEE_WRITE(copy_buf, sizeof(copy_buf),
955 offset + log_record * log_record_size);
956 file->record_count = log_record + 1;
957 }
958
959 return lp->size;
960}
961#endif /* COFFEE_MICRO_LOGS */
962/*---------------------------------------------------------------------------*/
963static int
964get_available_fd(void)
965{
966 int i;
967
968 for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
969 if(coffee_fd_set[i].flags == COFFEE_FD_FREE) {
970 return i;
971 }
972 }
973 return -1;
974}
975/*---------------------------------------------------------------------------*/
976int
977cfs_open(const char *name, int flags)
978{
979 int fd;
980 struct file_desc *fdp;
981
982 fd = get_available_fd();
983 if(fd < 0) {
984 PRINTF("Coffee: Failed to allocate a new file descriptor!\n");
985 return -1;
986 }
987
988 fdp = &coffee_fd_set[fd];
989 fdp->flags = 0;
990 fdp->io_flags = 0;
991
992 fdp->file = find_file(name);
993 if(fdp->file == NULL) {
994 if((flags & (CFS_READ | CFS_WRITE)) == CFS_READ) {
995 return -1;
996 }
997 fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0);
998 if(fdp->file == NULL) {
999 return -1;
1000 }
1001 fdp->file->end = 0;
1002 } else if(fdp->file->end == UNKNOWN_OFFSET) {
1003 fdp->file->end = file_end(fdp->file->page);
1004 }
1005
1006 fdp->flags |= flags;
1007 fdp->offset = flags & CFS_APPEND ? fdp->file->end : 0;
1008 fdp->file->references++;
1009
1010 return fd;
1011}
1012/*---------------------------------------------------------------------------*/
1013void
1015{
1016 if(FD_VALID(fd)) {
1017 coffee_fd_set[fd].flags = COFFEE_FD_FREE;
1018 coffee_fd_set[fd].file->references--;
1019 coffee_fd_set[fd].file = NULL;
1020 }
1021}
1022/*---------------------------------------------------------------------------*/
1024cfs_seek(int fd, cfs_offset_t offset, int whence)
1025{
1026 struct file_desc *fdp;
1027 cfs_offset_t new_offset;
1028
1029 if(!FD_VALID(fd)) {
1030 return -1;
1031 }
1032 fdp = &coffee_fd_set[fd];
1033
1034 if(whence == CFS_SEEK_SET) {
1035 new_offset = offset;
1036 } else if(whence == CFS_SEEK_END) {
1037 new_offset = fdp->file->end + offset;
1038 } else if(whence == CFS_SEEK_CUR) {
1039 new_offset = fdp->offset + offset;
1040 } else {
1041 return (cfs_offset_t)-1;
1042 }
1043
1044 if(new_offset < 0 || new_offset > fdp->file->max_pages * COFFEE_PAGE_SIZE) {
1045 return -1;
1046 }
1047
1048 if(fdp->file->end < new_offset) {
1049 if(FD_WRITABLE(fd)) {
1050 fdp->file->end = new_offset;
1051 } else {
1052 /* Disallow seeking past the end of the file for read only FDs */
1053 return (cfs_offset_t)-1;
1054 }
1055 }
1056
1057 return fdp->offset = new_offset;
1058}
1059/*---------------------------------------------------------------------------*/
1060int
1061cfs_remove(const char *name)
1062{
1063 struct file *file;
1064
1065 /*
1066 * Coffee removes files by marking them as obsolete. The space
1067 * is not guaranteed to be reclaimed immediately, but must be
1068 * sweeped by the garbage collector. The garbage collector is
1069 * called once a file reservation request cannot be granted.
1070 */
1071 file = find_file(name);
1072 if(file == NULL) {
1073 return -1;
1074 }
1075
1076 return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
1077}
1078/*---------------------------------------------------------------------------*/
1079int
1080cfs_read(int fd, void *buf, unsigned size)
1081{
1082 struct file_desc *fdp;
1083 struct file *file;
1084#if COFFEE_MICRO_LOGS
1085 struct file_header hdr;
1086 struct log_param lp;
1087 unsigned bytes_left;
1088 int r;
1089#endif
1090
1091 if(!(FD_VALID(fd) && FD_READABLE(fd))) {
1092 return -1;
1093 }
1094
1095 fdp = &coffee_fd_set[fd];
1096 file = fdp->file;
1097
1098 if(fdp->io_flags & CFS_COFFEE_IO_ENSURE_READ_LENGTH) {
1099 while(fdp->offset + size > file->end) {
1100 ((char *)buf)[--size] = '\0';
1101 }
1102 } else if(fdp->offset + size > file->end) {
1103 size = file->end - fdp->offset;
1104 }
1105
1106 /* If the file is not modified, read directly from the file extent. */
1107 if(!FILE_MODIFIED(file)) {
1108 COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
1109 fdp->offset += size;
1110 return size;
1111 }
1112
1113#if COFFEE_MICRO_LOGS
1114 read_header(&hdr, file->page);
1115
1116 /*
1117 * Copy the contents of the most recent log record. If there is
1118 * no log record for the file area to read from, we simply read
1119 * from the original file extent.
1120 */
1121 for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
1122 lp.offset = fdp->offset;
1123 lp.buf = buf;
1124 lp.size = bytes_left;
1125 r = read_log_page(&hdr, file->record_count, &lp);
1126
1127 /* Read from the original file if we cannot find the data in the log. */
1128 if(r < 0) {
1129 COFFEE_READ(buf, lp.size, absolute_offset(file->page, fdp->offset));
1130 r = lp.size;
1131 }
1132 fdp->offset += r;
1133 buf = (char *)buf + r;
1134 }
1135#endif /* COFFEE_MICRO_LOGS */
1136
1137 return size;
1138}
1139/*---------------------------------------------------------------------------*/
1140int
1141cfs_write(int fd, const void *buf, unsigned size)
1142{
1143 struct file_desc *fdp;
1144 struct file *file;
1145#if COFFEE_MICRO_LOGS
1146 int i;
1147 struct log_param lp;
1148 cfs_offset_t bytes_left;
1149 int8_t need_dummy_write;
1150 const char dummy[1] = { 0xff };
1151#endif
1152
1153 if(!(FD_VALID(fd) && FD_WRITABLE(fd))) {
1154 return -1;
1155 }
1156
1157 fdp = &coffee_fd_set[fd];
1158 file = fdp->file;
1159
1160 /* Attempt to extend the file if we try to write past the end. */
1161 if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
1162 while(size + fdp->offset + sizeof(struct file_header) >
1163 (file->max_pages * COFFEE_PAGE_SIZE)) {
1164 if(merge_log(file->page, 1) < 0) {
1165 return -1;
1166 }
1167 file = fdp->file;
1168 PRINTF("Extended the file at page %u\n", (unsigned)file->page);
1169 }
1170 }
1171
1172#if COFFEE_MICRO_LOGS
1173 if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
1174 (FILE_MODIFIED(file) || fdp->offset < file->end)) {
1175 need_dummy_write = 0;
1176 for(bytes_left = size; bytes_left > 0;) {
1177 lp.offset = fdp->offset;
1178 lp.buf = (void *)buf;
1179 lp.size = bytes_left;
1180 i = write_log_page(file, &lp);
1181 if(i < 0) {
1182 /* Return -1 if we wrote nothing because the log write failed. */
1183 if(size == bytes_left) {
1184 return -1;
1185 }
1186 break;
1187 } else if(i == 0) {
1188 /* The file was merged with the log. */
1189 file = fdp->file;
1190 } else {
1191 /* A log record was written. */
1192 bytes_left -= i;
1193 fdp->offset += i;
1194 buf = (char *)buf + i;
1195
1196 /* Update the file end for a potential log merge that might
1197 occur while writing log records. */
1198 if(fdp->offset > file->end) {
1199 file->end = fdp->offset;
1200 need_dummy_write = 1;
1201 }
1202 }
1203 }
1204
1205 if(need_dummy_write) {
1206 /*
1207 * A log record has been written at an offset beyond the original
1208 * extent's end. Consequently, we need to write a dummy value at the
1209 * corresponding end offset in the original extent to ensure that
1210 * the correct file size is calculated when opening the file again.
1211 */
1212 COFFEE_WRITE(dummy, 1, absolute_offset(file->page, fdp->offset - 1));
1213 }
1214 } else {
1215#endif /* COFFEE_MICRO_LOGS */
1216 if(COFFEE_APPEND_ONLY && fdp->offset < file->end) {
1217 return -1;
1218 }
1219
1220 COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
1221 fdp->offset += size;
1222#if COFFEE_MICRO_LOGS
1223 }
1224#endif /* COFFEE_MICRO_LOGS */
1225
1226 if(fdp->offset > file->end) {
1227 file->end = fdp->offset;
1228 }
1229
1230 return size;
1231}
1232/*---------------------------------------------------------------------------*/
1233int
1234cfs_opendir(struct cfs_dir *dir, const char *name)
1235{
1236 /*
1237 * Coffee is only guaranteed to support the directory names "/" and ".",
1238 * but it does not enforce this currently.
1239 */
1240 memset(dir->state, 0, sizeof(coffee_page_t));
1241 return 0;
1242}
1243/*---------------------------------------------------------------------------*/
1244int
1245cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
1246{
1247 struct file_header hdr;
1248 coffee_page_t page;
1249 coffee_page_t next_page;
1250
1251 memcpy(&page, dir->state, sizeof(coffee_page_t));
1252
1253 while(page < COFFEE_PAGE_COUNT) {
1254 read_header(&hdr, page);
1255 if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
1256 memcpy(record->name,
1257 hdr.name,
1258 MIN(sizeof(record->name), sizeof(hdr.name)));
1259 record->name[MIN(sizeof(record->name), sizeof(hdr.name)) - 1] = '\0';
1260 record->size = file_end(page);
1261
1262 next_page = next_file(page, &hdr);
1263 memcpy(dir->state, &next_page, sizeof(coffee_page_t));
1264 return 0;
1265 }
1266 page = next_file(page, &hdr);
1267 }
1268
1269 return -1;
1270}
1271/*---------------------------------------------------------------------------*/
1272void
1273cfs_closedir(struct cfs_dir *dir)
1274{
1275 return;
1276}
1277/*---------------------------------------------------------------------------*/
1278int
1279cfs_coffee_reserve(const char *name, cfs_offset_t size)
1280{
1281 return reserve(name, page_count(size), 0, 0) == NULL ? -1 : 0;
1282}
1283/*---------------------------------------------------------------------------*/
1284int
1285cfs_coffee_configure_log(const char *filename, unsigned log_size,
1286 unsigned log_record_size)
1287{
1288 struct file *file;
1289 struct file_header hdr;
1290
1291 if(log_record_size == 0 || log_record_size > COFFEE_PAGE_SIZE ||
1292 log_size < log_record_size) {
1293 return -1;
1294 }
1295
1296 file = find_file(filename);
1297 if(file == NULL) {
1298 return -1;
1299 }
1300
1301 read_header(&hdr, file->page);
1302 if(HDR_MODIFIED(hdr)) {
1303 /* Too late to customize the log. */
1304 return -1;
1305 }
1306
1307 hdr.log_records = log_size / log_record_size;
1308 hdr.log_record_size = log_record_size;
1309 write_header(&hdr, file->page);
1310
1311 return 0;
1312}
1313/*---------------------------------------------------------------------------*/
1314int
1315cfs_coffee_set_io_semantics(int fd, unsigned flags)
1316{
1317 if(!FD_VALID(fd)) {
1318 return -1;
1319 }
1320
1321 coffee_fd_set[fd].io_flags |= flags;
1322
1323 return 0;
1324}
1325/*---------------------------------------------------------------------------*/
1326int
1328{
1329 coffee_page_t i;
1330
1331 PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
1332
1333 for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
1334 COFFEE_ERASE(i);
1335 PRINTF(".");
1336 }
1337
1338 /* Formatting invalidates the file information. */
1339 memset(&coffee_files, 0, sizeof(coffee_files));
1340 memset(&coffee_fd_set, 0, sizeof(coffee_fd_set));
1341 next_free = 0;
1342 gc_wait = 1;
1343
1344 PRINTF(" done!\n");
1345
1346 return 0;
1347}
1348/*---------------------------------------------------------------------------*/
Header for the Coffee file system.
CFS header file.
int16_t coffee_page_t
Page.
#define COFFEE_ERASE(sector)
Erase.
#define COFFEE_MAX_OPEN_FILES
Number of file cache entries.
#define COFFEE_WRITE(buf, size, offset)
Write.
#define COFFEE_DYN_SIZE
Default reserved file size.
#define COFFEE_READ(buf, size, offset)
Read.
#define COFFEE_FD_SET_SIZE
Number of file descriptor entries.
#define COFFEE_LOG_SIZE
Default micro-log size.
#define COFFEE_NAME_LENGTH
Maximal filename length.
#define COFFEE_LOG_TABLE_LIMIT
Maximal amount of log table entries read in one batch.
#define COFFEE_PAGE_SIZE
Logical page size.
int cfs_coffee_configure_log(const char *filename, unsigned log_size, unsigned log_record_size)
Configure the on-demand log file.
#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_format(void)
Format the storage area assigned to Coffee.
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.
int cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
Read a directory entry.
int cfs_open(const char *name, int flags)
Open a file.
Definition cfs-coffee.c:977
#define CFS_COFFEE_IO_FIRM_SIZE
Instruct Coffee not to attempt to extend the file upon a request to write past the reserved file size...
Definition cfs-coffee.h:64
#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
#define CFS_SEEK_CUR
Specify that cfs_seek() should compute the offset from the current position of the file pointer.
Definition cfs.h:141
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
#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_closedir(struct cfs_dir *dir)
Close a directory opened with cfs_opendir().
void cfs_close(int fd)
Close an open file.
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
#define CFS_COFFEE_IO_ENSURE_READ_LENGTH
Instruct Coffee to set unused bytes in the destination buffer to zero.
Definition cfs-coffee.h:73
int cfs_opendir(struct cfs_dir *dir, const char *name)
Open a directory for reading directory entries.
static void start(void)
Start measurement.