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