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