Contiki-NG
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. */
157 struct 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. */
164 struct 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. */
174 struct 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. */
183 struct 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. */
194 struct 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  */
204 static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
205 static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
206 static coffee_page_t next_free;
207 static char gc_wait;
208 
209 /*---------------------------------------------------------------------------*/
210 static void
211 write_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 /*---------------------------------------------------------------------------*/
217 static void
218 read_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 /*---------------------------------------------------------------------------*/
226 static cfs_offset_t
227 absolute_offset(coffee_page_t page, cfs_offset_t offset)
228 {
229  return page * COFFEE_PAGE_SIZE + sizeof(struct file_header) + offset;
230 }
231 /*---------------------------------------------------------------------------*/
232 static coffee_page_t
233 get_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 /*---------------------------------------------------------------------------*/
332 static void
333 isolate_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 /*---------------------------------------------------------------------------*/
351 static void
352 collect_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 /*---------------------------------------------------------------------------*/
395 static coffee_page_t
396 next_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 /*---------------------------------------------------------------------------*/
416 static struct file *
417 load_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 /*---------------------------------------------------------------------------*/
455 static struct file *
456 find_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 /*---------------------------------------------------------------------------*/
485 static cfs_offset_t
486 file_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 /*---------------------------------------------------------------------------*/
519 static coffee_page_t
520 find_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 /*---------------------------------------------------------------------------*/
555 static int
556 remove_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 /*---------------------------------------------------------------------------*/
602 static coffee_page_t
603 page_count(cfs_offset_t size)
604 {
605  return (size + sizeof(struct file_header) + COFFEE_PAGE_SIZE - 1) /
607 }
608 /*---------------------------------------------------------------------------*/
609 static struct file *
610 reserve(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
652 static void
653 adjust_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
664 static uint16_t
665 modify_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
682 static int
683 get_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
724 static int
725 read_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
754 static coffee_page_t
755 create_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 /*---------------------------------------------------------------------------*/
780 static int
781 merge_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
851 static int
852 find_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
890 static int
891 write_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 /*---------------------------------------------------------------------------*/
963 static int
964 get_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 /*---------------------------------------------------------------------------*/
976 int
977 cfs_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 /*---------------------------------------------------------------------------*/
1013 void
1014 cfs_close(int fd)
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 /*---------------------------------------------------------------------------*/
1023 cfs_offset_t
1024 cfs_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 /*---------------------------------------------------------------------------*/
1060 int
1061 cfs_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 /*---------------------------------------------------------------------------*/
1079 int
1080 cfs_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 /*---------------------------------------------------------------------------*/
1140 int
1141 cfs_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 /*---------------------------------------------------------------------------*/
1233 int
1234 cfs_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 /*---------------------------------------------------------------------------*/
1244 int
1245 cfs_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, hdr.name, sizeof(record->name));
1257  record->name[sizeof(record->name) - 1] = '\0';
1258  record->size = file_end(page);
1259 
1260  next_page = next_file(page, &hdr);
1261  memcpy(dir->state, &next_page, sizeof(coffee_page_t));
1262  return 0;
1263  }
1264  page = next_file(page, &hdr);
1265  }
1266 
1267  return -1;
1268 }
1269 /*---------------------------------------------------------------------------*/
1270 void
1271 cfs_closedir(struct cfs_dir *dir)
1272 {
1273  return;
1274 }
1275 /*---------------------------------------------------------------------------*/
1276 int
1277 cfs_coffee_reserve(const char *name, cfs_offset_t size)
1278 {
1279  return reserve(name, page_count(size), 0, 0) == NULL ? -1 : 0;
1280 }
1281 /*---------------------------------------------------------------------------*/
1282 int
1283 cfs_coffee_configure_log(const char *filename, unsigned log_size,
1284  unsigned log_record_size)
1285 {
1286  struct file *file;
1287  struct file_header hdr;
1288 
1289  if(log_record_size == 0 || log_record_size > COFFEE_PAGE_SIZE ||
1290  log_size < log_record_size) {
1291  return -1;
1292  }
1293 
1294  file = find_file(filename);
1295  if(file == NULL) {
1296  return -1;
1297  }
1298 
1299  read_header(&hdr, file->page);
1300  if(HDR_MODIFIED(hdr)) {
1301  /* Too late to customize the log. */
1302  return -1;
1303  }
1304 
1305  hdr.log_records = log_size / log_record_size;
1306  hdr.log_record_size = log_record_size;
1307  write_header(&hdr, file->page);
1308 
1309  return 0;
1310 }
1311 /*---------------------------------------------------------------------------*/
1312 int
1313 cfs_coffee_set_io_semantics(int fd, unsigned flags)
1314 {
1315  if(!FD_VALID(fd)) {
1316  return -1;
1317  }
1318 
1319  coffee_fd_set[fd].io_flags |= flags;
1320 
1321  return 0;
1322 }
1323 /*---------------------------------------------------------------------------*/
1324 int
1326 {
1327  coffee_page_t i;
1328 
1329  PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
1330 
1331  for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
1332  COFFEE_ERASE(i);
1333  PRINTF(".");
1334  }
1335 
1336  /* Formatting invalidates the file information. */
1337  memset(&coffee_files, 0, sizeof(coffee_files));
1338  memset(&coffee_fd_set, 0, sizeof(coffee_fd_set));
1339  next_free = 0;
1340  gc_wait = 1;
1341 
1342  PRINTF(" done!\n");
1343 
1344  return 0;
1345 }
1346 /*---------------------------------------------------------------------------*/
Header for the Coffee file system.
#define COFFEE_FD_SET_SIZE
Number of file descriptor entries.
#define COFFEE_READ(buf, size, offset)
Read.
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:977
int cfs_coffee_set_io_semantics(int fd, unsigned flags)
Set the I/O semantics for accessing a file.
Definition: cfs-coffee.c:1313
#define COFFEE_MAX_OPEN_FILES
Number of file cache entries.
int cfs_coffee_configure_log(const char *filename, unsigned log_size, unsigned log_record_size)
Configure the on-demand log file.
Definition: cfs-coffee.c:1283
#define COFFEE_LOG_SIZE
Default micro-log size.
#define COFFEE_APPEND_ONLY
Whether files are expected to be appended to only.
static void start(void)
Start measurement.
int cfs_remove(const char *name)
Remove a file.
Definition: cfs-coffee.c:1061
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-coffee.c:1024
int16_t coffee_page_t
Page.
#define CFS_APPEND
Specify that cfs_open() should append written data to the file rather than overwriting it...
Definition: cfs.h:120
int cfs_write(int fd, const void *buf, unsigned int len)
Write data to an open file.
Definition: cfs-sdcard.c:86
#define COFFEE_WRITE(buf, size, offset)
Write.
#define COFFEE_DYN_SIZE
Default reserved file size.
CFS header file.
#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_opendir(struct cfs_dir *dir, const char *name)
Open a directory for reading directory entries.
Definition: cfs-coffee.c:1234
void cfs_close(int fd)
Close an open file.
Definition: cfs-coffee.c:1014
#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
int cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
Read a directory entry.
Definition: cfs-coffee.c:1245
#define CFS_SEEK_END
Specify that cfs_seek() should compute the offset from the end of the file.
Definition: cfs.h:147
int cfs_read(int fd, void *buf, unsigned int len)
Read data from an open file.
Definition: cfs-sdcard.c:78
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:92
int cfs_coffee_reserve(const char *name, cfs_offset_t size)
Reserve space for a file.
Definition: cfs-coffee.c:1277
#define CFS_SEEK_CUR
Specify that cfs_seek() should compute the offset from the current position of the file pointer...
Definition: cfs.h:138
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:129
void cfs_closedir(struct cfs_dir *dir)
Close a directory opened with cfs_opendir().
Definition: cfs-coffee.c:1271
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition: cfs.h:106
int cfs_coffee_format(void)
Format the storage area assigned to Coffee.
Definition: cfs-coffee.c:1325
#define COFFEE_NAME_LENGTH
Maximal filename length.
#define COFFEE_ERASE(sector)
Erase.
#define COFFEE_LOG_TABLE_LIMIT
Maximal amount of log table entries read in one batch.
#define CFS_COFFEE_IO_ENSURE_READ_LENGTH
Instruct Coffee to set unused bytes in the destination buffer to zero.
Definition: cfs-coffee.h:73
#define COFFEE_PAGE_SIZE
Logical page size.