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