Contiki 2.6

cfs-coffee.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, 2009, Swedish Institute of Computer Science
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * This file is part of the Contiki operating system.
00030  *
00031  */
00032 
00033 /**
00034  * \file
00035  *      Coffee: A file system for a variety of storage types in
00036  *              memory-constrained devices.
00037  *
00038  *      For further information, see "Enabling Large-Scale Storage in 
00039  *      Sensor Networks with the Coffee File System" in the proceedings 
00040  *      of ACM/IEEE IPSN 2009.
00041  *
00042  * \author
00043  *      Nicolas Tsiftes <nvt@sics.se>
00044  */
00045 
00046 #include <limits.h>
00047 #include <string.h>
00048 
00049 #define DEBUG 0
00050 #if DEBUG
00051 #include <stdio.h>
00052 #define PRINTF(...) printf(__VA_ARGS__)
00053 #else
00054 #define PRINTF(...)
00055 #endif
00056 
00057 #include "contiki-conf.h"
00058 #include "cfs/cfs.h"
00059 #include "cfs-coffee-arch.h"
00060 #include "cfs/cfs-coffee.h"
00061 
00062 /* Micro logs enable modifications on storage types that do not support
00063    in-place updates. This applies primarily to flash memories. */
00064 #ifndef COFFEE_MICRO_LOGS
00065 #define COFFEE_MICRO_LOGS       1
00066 #endif
00067 
00068 /* If the files are expected to be appended to only, this parameter 
00069    can be set to save some code space. */
00070 #ifndef COFFEE_APPEND_ONLY
00071 #define COFFEE_APPEND_ONLY      0
00072 #endif
00073 
00074 #if COFFEE_MICRO_LOGS && COFFEE_APPEND_ONLY
00075 #error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set."
00076 #endif
00077 
00078 /* I/O semantics can be set on file descriptors in order to optimize 
00079    file access on certain storage types. */
00080 #ifndef COFFEE_IO_SEMANTICS
00081 #define COFFEE_IO_SEMANTICS     0
00082 #endif
00083 
00084 /*
00085  * Prevent sectors from being erased directly after file removal.
00086  * This will level the wear across sectors better, but may lead
00087  * to longer garbage collection procedures.
00088  */
00089 #ifndef COFFEE_EXTENDED_WEAR_LEVELLING
00090 #define COFFEE_EXTENDED_WEAR_LEVELLING  1
00091 #endif
00092 
00093 #if COFFEE_START & (COFFEE_SECTOR_SIZE - 1)
00094 #error COFFEE_START must point to the first byte in a sector.
00095 #endif
00096 
00097 #define COFFEE_FD_FREE          0x0
00098 #define COFFEE_FD_READ          0x1
00099 #define COFFEE_FD_WRITE         0x2
00100 #define COFFEE_FD_APPEND        0x4
00101 
00102 #define COFFEE_FILE_MODIFIED    0x1
00103 
00104 #define INVALID_PAGE            ((coffee_page_t)-1)
00105 #define UNKNOWN_OFFSET          ((cfs_offset_t)-1)
00106 
00107 #define REMOVE_LOG              1
00108 #define CLOSE_FDS               1
00109 #define ALLOW_GC                1
00110 
00111 /* "Greedy" garbage collection erases as many sectors as possible. */
00112 #define GC_GREEDY               0
00113 /* "Reluctant" garbage collection stops after erasing one sector. */
00114 #define GC_RELUCTANT            1
00115 
00116 /* File descriptor macros. */
00117 #define FD_VALID(fd)                                    \
00118         ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE &&      \
00119         coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
00120 #define FD_READABLE(fd)         (coffee_fd_set[(fd)].flags & CFS_READ)
00121 #define FD_WRITABLE(fd)         (coffee_fd_set[(fd)].flags & CFS_WRITE)
00122 #define FD_APPENDABLE(fd)       (coffee_fd_set[(fd)].flags & CFS_APPEND)
00123 
00124 /* File object macros. */
00125 #define FILE_MODIFIED(file)     ((file)->flags & COFFEE_FILE_MODIFIED)
00126 #define FILE_FREE(file)         ((file)->max_pages == 0)
00127 #define FILE_UNREFERENCED(file) ((file)->references == 0)
00128 
00129 /* File header flags. */
00130 #define HDR_FLAG_VALID          0x1     /* Completely written header. */
00131 #define HDR_FLAG_ALLOCATED      0x2     /* Allocated file. */
00132 #define HDR_FLAG_OBSOLETE       0x4     /* File marked for GC. */
00133 #define HDR_FLAG_MODIFIED       0x8     /* Modified file, log exists. */
00134 #define HDR_FLAG_LOG            0x10    /* Log file. */
00135 #define HDR_FLAG_ISOLATED       0x20    /* Isolated page. */
00136 
00137 /* File header macros. */
00138 #define CHECK_FLAG(hdr, flag)   ((hdr).flags & (flag))
00139 #define HDR_VALID(hdr)          CHECK_FLAG(hdr, HDR_FLAG_VALID)
00140 #define HDR_ALLOCATED(hdr)      CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
00141 #define HDR_FREE(hdr)           !HDR_ALLOCATED(hdr)
00142 #define HDR_LOG(hdr)            CHECK_FLAG(hdr, HDR_FLAG_LOG)
00143 #define HDR_MODIFIED(hdr)       CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
00144 #define HDR_ISOLATED(hdr)       CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
00145 #define HDR_OBSOLETE(hdr)       CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
00146 #define HDR_ACTIVE(hdr)         (HDR_ALLOCATED(hdr) && \
00147                                 !HDR_OBSOLETE(hdr)  && \
00148                                 !HDR_ISOLATED(hdr))
00149 
00150 /* Shortcuts derived from the hardware-dependent configuration of Coffee. */
00151 #define COFFEE_SECTOR_COUNT     (unsigned)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
00152 #define COFFEE_PAGE_COUNT       \
00153         ((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
00154 #define COFFEE_PAGES_PER_SECTOR \
00155         ((coffee_page_t)(COFFEE_SECTOR_SIZE / COFFEE_PAGE_SIZE))
00156 
00157 /* This structure is used for garbage collection statistics. */
00158 struct sector_status {
00159   coffee_page_t active;
00160   coffee_page_t obsolete;
00161   coffee_page_t free;
00162 };
00163 
00164 /* The structure of cached file objects. */
00165 struct file {
00166   cfs_offset_t end;
00167   coffee_page_t page;
00168   coffee_page_t max_pages;
00169   int16_t record_count;
00170   uint8_t references;
00171   uint8_t flags;
00172 };
00173 
00174 /* The file descriptor structure. */
00175 struct file_desc {
00176   cfs_offset_t offset;
00177   struct file *file;
00178   uint8_t flags;
00179 #if COFFEE_IO_SEMANTICS
00180   uint8_t io_flags;
00181 #endif
00182 };
00183 
00184 /* The file header structure mimics the representation of file headers 
00185    in the physical storage medium. */
00186 struct file_header {
00187   coffee_page_t log_page;
00188   uint16_t log_records;
00189   uint16_t log_record_size;
00190   coffee_page_t max_pages;
00191   uint8_t deprecated_eof_hint;
00192   uint8_t flags;
00193   char name[COFFEE_NAME_LENGTH];
00194 };
00195 
00196 /* This is needed because of a buggy compiler. */
00197 struct log_param {
00198   cfs_offset_t offset;
00199   const char *buf;
00200   uint16_t size;
00201 };
00202 
00203 /*
00204  * The protected memory consists of structures that should not be 
00205  * overwritten during system checkpointing because they may be used by 
00206  * the checkpointing implementation. These structures need not be 
00207  * protected if checkpointing is not used.
00208  */
00209 static struct protected_mem_t {
00210   struct file coffee_files[COFFEE_MAX_OPEN_FILES];
00211   struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
00212   coffee_page_t next_free;
00213   char gc_wait;
00214 } protected_mem;
00215 static struct file * const coffee_files = protected_mem.coffee_files;
00216 static struct file_desc * const coffee_fd_set = protected_mem.coffee_fd_set;
00217 static coffee_page_t * const next_free = &protected_mem.next_free;
00218 static char * const gc_wait = &protected_mem.gc_wait;
00219 
00220 /*---------------------------------------------------------------------------*/
00221 static void
00222 write_header(struct file_header *hdr, coffee_page_t page)
00223 {
00224   hdr->flags |= HDR_FLAG_VALID;
00225   COFFEE_WRITE(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
00226 }
00227 /*---------------------------------------------------------------------------*/
00228 static void
00229 read_header(struct file_header *hdr, coffee_page_t page)
00230 {
00231   COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
00232 #if DEBUG
00233   if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
00234     PRINTF("Invalid header at page %u!\n", (unsigned)page);
00235   }
00236 #endif
00237 }
00238 /*---------------------------------------------------------------------------*/
00239 static cfs_offset_t
00240 absolute_offset(coffee_page_t page, cfs_offset_t offset)
00241 {
00242   return page * COFFEE_PAGE_SIZE + sizeof(struct file_header) + offset;
00243 }
00244 /*---------------------------------------------------------------------------*/
00245 static coffee_page_t
00246 get_sector_status(uint16_t sector, struct sector_status *stats)
00247 {
00248   static coffee_page_t skip_pages;
00249   static char last_pages_are_active;
00250   struct file_header hdr;
00251   coffee_page_t active, obsolete, free;
00252   coffee_page_t sector_start, sector_end;
00253   coffee_page_t page;
00254 
00255   memset(stats, 0, sizeof(*stats));
00256   active = obsolete = free = 0;
00257 
00258   /*
00259    * get_sector_status() is an iterative function using local static 
00260    * state. It therefore requires the the caller loops starts from 
00261    * sector 0 in order to reset the internal state.
00262    */
00263   if(sector == 0) {
00264     skip_pages = 0;
00265     last_pages_are_active = 0;
00266   }
00267 
00268   sector_start = sector * COFFEE_PAGES_PER_SECTOR;
00269   sector_end = sector_start + COFFEE_PAGES_PER_SECTOR;
00270 
00271   /*
00272    * Account for pages belonging to a file starting in a previous 
00273    * segment that extends into this segment. If the whole segment is 
00274    * covered, we do not need to continue counting pages in this iteration.
00275    */
00276   if(last_pages_are_active) {
00277     if(skip_pages >= COFFEE_PAGES_PER_SECTOR) {
00278       stats->active = COFFEE_PAGES_PER_SECTOR;
00279       skip_pages -= COFFEE_PAGES_PER_SECTOR;
00280       return 0;
00281     }
00282     active = skip_pages;
00283   } else {
00284     if(skip_pages >= COFFEE_PAGES_PER_SECTOR) {
00285       stats->obsolete = COFFEE_PAGES_PER_SECTOR;
00286       skip_pages -= COFFEE_PAGES_PER_SECTOR;
00287       return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages;
00288     }
00289     obsolete = skip_pages;
00290   }
00291 
00292   /* Determine the amount of pages of each type that have not been 
00293      accounted for yet in the current sector. */
00294   for(page = sector_start + skip_pages; page < sector_end;) {
00295     read_header(&hdr, page);
00296     last_pages_are_active = 0;
00297     if(HDR_ACTIVE(hdr)) {
00298       last_pages_are_active = 1;
00299       page += hdr.max_pages;
00300       active += hdr.max_pages;
00301     } else if(HDR_ISOLATED(hdr)) {
00302       page++;
00303       obsolete++;
00304     } else if(HDR_OBSOLETE(hdr)) {
00305       page += hdr.max_pages;
00306       obsolete += hdr.max_pages;
00307     } else {
00308       free = sector_end - page;
00309       break;
00310     }
00311   }
00312 
00313   /*
00314    * Determine the amount of pages in the following sectors that
00315    * should be remembered for the next iteration. This is necessary 
00316    * because no page except the first of a file contains information 
00317    * about what type of page it is. A side effect of remembering this
00318    * amount is that there is no need to read in the headers of each 
00319    * of these pages from the storage.
00320    */
00321   skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR;
00322   if(skip_pages > 0) {
00323     if(last_pages_are_active) {
00324       active = COFFEE_PAGES_PER_SECTOR - obsolete;
00325     } else {
00326       obsolete = COFFEE_PAGES_PER_SECTOR - active;
00327     }
00328   }
00329 
00330   stats->active = active;
00331   stats->obsolete = obsolete;
00332   stats->free = free;
00333 
00334   /*
00335    * To avoid unnecessary page isolation, we notify the callee that 
00336    * "skip_pages" pages should be isolated only if the current file extent 
00337    * ends in the next sector. If the file extent ends in a more distant 
00338    * sector, however, the garbage collection can free the next sector 
00339    * immediately without requiring page isolation. 
00340    */
00341   return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ?
00342         0 : skip_pages;
00343 }
00344 /*---------------------------------------------------------------------------*/
00345 static void
00346 isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
00347 {
00348   struct file_header hdr;
00349   coffee_page_t page;
00350 
00351   /* Split an obsolete file starting in the previous sector and mark
00352      the following pages as isolated. */
00353   memset(&hdr, 0, sizeof(hdr));
00354   hdr.flags = HDR_FLAG_ALLOCATED | HDR_FLAG_ISOLATED;
00355 
00356   /* Isolation starts from the next sector. */
00357   for(page = 0; page < skip_pages; page++) {
00358     write_header(&hdr, start + page);
00359   }
00360   PRINTF("Coffee: Isolated %u pages starting in sector %d\n",
00361          (unsigned)skip_pages, (int)start / COFFEE_PAGES_PER_SECTOR);
00362 
00363 }
00364 /*---------------------------------------------------------------------------*/
00365 static void
00366 collect_garbage(int mode)
00367 {
00368   uint16_t sector;
00369   struct sector_status stats;
00370   coffee_page_t first_page, isolation_count;
00371 
00372   PRINTF("Coffee: Running the file system garbage collector in %s mode\n",
00373          mode == GC_RELUCTANT ? "reluctant" : "greedy");
00374   /*
00375    * The garbage collector erases as many sectors as possible. A sector is
00376    * erasable if there are only free or obsolete pages in it.
00377    */
00378   for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
00379     isolation_count = get_sector_status(sector, &stats);
00380     PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n",
00381         sector, (unsigned)stats.active,
00382         (unsigned)stats.obsolete, (unsigned)stats.free);
00383 
00384     if(stats.active > 0) {
00385       continue;
00386     }
00387 
00388     if((mode == GC_RELUCTANT && stats.free == 0) ||
00389        (mode == GC_GREEDY && stats.obsolete > 0)) {
00390       first_page = sector * COFFEE_PAGES_PER_SECTOR;
00391       if(first_page < *next_free) {
00392         *next_free = first_page;
00393       }
00394 
00395       if(isolation_count > 0) {
00396         isolate_pages(first_page + COFFEE_PAGES_PER_SECTOR, isolation_count);
00397       }
00398 
00399       COFFEE_ERASE(sector);
00400       PRINTF("Coffee: Erased sector %d!\n", sector);
00401 
00402       if(mode == GC_RELUCTANT && isolation_count > 0) {
00403         break;
00404       }
00405     }
00406   }
00407 }
00408 /*---------------------------------------------------------------------------*/
00409 static coffee_page_t
00410 next_file(coffee_page_t page, struct file_header *hdr)
00411 {
00412   /*
00413    * The quick-skip algorithm for finding file extents is the most 
00414    * essential part of Coffee. The file allocation rules enables this 
00415    * algorithm to quickly jump over free areas and allocated extents 
00416    * after reading single headers and determining their status.
00417    *
00418    * The worst-case performance occurs when we encounter multiple long 
00419    * sequences of isolated pages, but such sequences are uncommon and 
00420    * always shorter than a sector.
00421    */
00422   if(HDR_FREE(*hdr)) {
00423     return (page + COFFEE_PAGES_PER_SECTOR) & ~(COFFEE_PAGES_PER_SECTOR - 1);
00424   } else if(HDR_ISOLATED(*hdr)) {
00425     return page + 1;
00426   }
00427   return page + hdr->max_pages;    
00428 }
00429 /*---------------------------------------------------------------------------*/
00430 static struct file *
00431 load_file(coffee_page_t start, struct file_header *hdr)
00432 {
00433   int i, unreferenced, free;
00434   struct file *file;
00435 
00436   /*
00437    * We prefer to overwrite a free slot since unreferenced ones
00438    * contain usable data. Free slots are designated by the page
00439    * value INVALID_PAGE.
00440    */
00441   for(i = 0, unreferenced = free = -1; i < COFFEE_MAX_OPEN_FILES; i++) {
00442     if(FILE_FREE(&coffee_files[i])) {
00443       free = i;
00444       break;
00445     } else if(FILE_UNREFERENCED(&coffee_files[i])) {
00446       unreferenced = i;
00447     }
00448   }
00449 
00450   if(free == -1) {
00451     if(unreferenced != -1) {
00452       i = unreferenced;
00453     } else {
00454       return NULL;
00455     }
00456   }
00457 
00458   file = &coffee_files[i];
00459   file->page = start;
00460   file->end = UNKNOWN_OFFSET;
00461   file->max_pages = hdr->max_pages;
00462   file->flags = 0;
00463   if(HDR_MODIFIED(*hdr)) {
00464     file->flags |= COFFEE_FILE_MODIFIED;
00465   }
00466   /* We don't know the amount of records yet. */
00467   file->record_count = -1;
00468 
00469   return file;
00470 }
00471 /*---------------------------------------------------------------------------*/
00472 static struct file *
00473 find_file(const char *name)
00474 {
00475   int i;
00476   struct file_header hdr;
00477   coffee_page_t page;
00478   
00479   /* First check if the file metadata is cached. */
00480   for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) {
00481     if(FILE_FREE(&coffee_files[i])) {
00482       continue;
00483     }
00484 
00485     read_header(&hdr, coffee_files[i].page);
00486     if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) {
00487       return &coffee_files[i];
00488     }
00489   }
00490   
00491   /* Scan the flash memory sequentially otherwise. */
00492   for(page = 0; page < COFFEE_PAGE_COUNT; page = next_file(page, &hdr)) {
00493     read_header(&hdr, page);
00494     if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) {
00495       return load_file(page, &hdr);
00496     }
00497   }
00498 
00499   return NULL;
00500 }
00501 /*---------------------------------------------------------------------------*/
00502 static cfs_offset_t
00503 file_end(coffee_page_t start)
00504 {
00505   struct file_header hdr;
00506   unsigned char buf[COFFEE_PAGE_SIZE];
00507   coffee_page_t page;
00508   int i;
00509 
00510   read_header(&hdr, start);
00511 
00512   /*
00513    * Move from the end of the range towards the beginning and look for
00514    * a byte that has been modified.
00515    *
00516    * An important implication of this is that if the last written bytes
00517    * are zeroes, then these are skipped from the calculation.
00518    */
00519 
00520   for(page = hdr.max_pages - 1; page >= 0; page--) {
00521     COFFEE_READ(buf, sizeof(buf), (start + page) * COFFEE_PAGE_SIZE);
00522     for(i = COFFEE_PAGE_SIZE - 1; i >= 0; i--) {
00523       if(buf[i] != 0) {
00524         if(page == 0 && i < sizeof(hdr)) {
00525           return 0;
00526         }
00527         return 1 + i + (page * COFFEE_PAGE_SIZE) - sizeof(hdr);
00528       }
00529     }
00530   }
00531 
00532   /* All bytes are writable. */
00533   return 0;
00534 }
00535 /*---------------------------------------------------------------------------*/
00536 static coffee_page_t
00537 find_contiguous_pages(coffee_page_t amount)
00538 {
00539   coffee_page_t page, start;
00540   struct file_header hdr;
00541 
00542   start = INVALID_PAGE;
00543   for(page = *next_free; page < COFFEE_PAGE_COUNT;) {
00544     read_header(&hdr, page);
00545     if(HDR_FREE(hdr)) {
00546       if(start == INVALID_PAGE) {
00547         start = page;
00548         if(start + amount >= COFFEE_PAGE_COUNT) {
00549           /* We can stop immediately if the remaining pages are not enough. */
00550           break;
00551         }
00552       }
00553 
00554       /* All remaining pages in this sector are free --
00555          jump to the next sector. */
00556       page = next_file(page, &hdr);
00557 
00558       if(start + amount <= page) {
00559         if(start == *next_free) {
00560           *next_free = start + amount;
00561         }
00562         return start;
00563       }
00564     } else {
00565       start = INVALID_PAGE;
00566       page = next_file(page, &hdr);
00567     }
00568   }
00569   return INVALID_PAGE;
00570 }
00571 /*---------------------------------------------------------------------------*/
00572 static int
00573 remove_by_page(coffee_page_t page, int remove_log, int close_fds,
00574                int gc_allowed)
00575 {
00576   struct file_header hdr;
00577   int i;
00578 
00579   read_header(&hdr, page);
00580   if(!HDR_ACTIVE(hdr)) {
00581     return -1;
00582   }
00583 
00584   if(remove_log && HDR_MODIFIED(hdr)) {
00585     if(remove_by_page(hdr.log_page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) {
00586       return -1;
00587     }
00588   }
00589 
00590   hdr.flags |= HDR_FLAG_OBSOLETE;
00591   write_header(&hdr, page);
00592 
00593   *gc_wait = 0;
00594 
00595   /* Close all file descriptors that reference the removed file. */
00596   if(close_fds) {
00597     for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
00598       if(coffee_fd_set[i].file != NULL && coffee_fd_set[i].file->page == page) {
00599         coffee_fd_set[i].flags = COFFEE_FD_FREE;
00600       }
00601     }
00602   }
00603 
00604   for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) {
00605     if(coffee_files[i].page == page) {
00606       coffee_files[i].page = INVALID_PAGE;
00607       coffee_files[i].references = 0;
00608       coffee_files[i].max_pages = 0;
00609     }
00610   }
00611 
00612 #if !COFFEE_EXTENDED_WEAR_LEVELLING
00613   if(gc_allowed) {
00614     collect_garbage(GC_RELUCTANT);
00615   }
00616 #endif
00617 
00618   return 0;
00619 }
00620 /*---------------------------------------------------------------------------*/
00621 static coffee_page_t
00622 page_count(cfs_offset_t size)
00623 {
00624   return (size + sizeof(struct file_header) + COFFEE_PAGE_SIZE - 1) /
00625                 COFFEE_PAGE_SIZE;
00626 }
00627 /*---------------------------------------------------------------------------*/
00628 static struct file *
00629 reserve(const char *name, coffee_page_t pages,
00630         int allow_duplicates, unsigned flags)
00631 {
00632   struct file_header hdr;
00633   coffee_page_t page;
00634   struct file *file;
00635 
00636   if(!allow_duplicates && find_file(name) != NULL) {
00637     return NULL;
00638   }
00639 
00640   page = find_contiguous_pages(pages);
00641   if(page == INVALID_PAGE) {
00642     if(*gc_wait) {
00643       return NULL;
00644     }
00645     collect_garbage(GC_GREEDY);
00646     page = find_contiguous_pages(pages);
00647     if(page == INVALID_PAGE) {
00648       *gc_wait = 1;
00649       return NULL;
00650     }
00651   }
00652 
00653   memset(&hdr, 0, sizeof(hdr));
00654   memcpy(hdr.name, name, sizeof(hdr.name) - 1);
00655   hdr.max_pages = pages;
00656   hdr.flags = HDR_FLAG_ALLOCATED | flags;
00657   write_header(&hdr, page);
00658 
00659   PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n",
00660       pages, page, name);
00661 
00662   file = load_file(page, &hdr);
00663   if(file != NULL) {
00664     file->end = 0;
00665   }
00666 
00667   return file;
00668 }
00669 /*---------------------------------------------------------------------------*/
00670 #if COFFEE_MICRO_LOGS
00671 static void
00672 adjust_log_config(struct file_header *hdr,
00673                   uint16_t *log_record_size, uint16_t *log_records)
00674 {
00675   *log_record_size = hdr->log_record_size == 0 ?
00676                      COFFEE_PAGE_SIZE : hdr->log_record_size;
00677   *log_records = hdr->log_records == 0 ?
00678                      COFFEE_LOG_SIZE / *log_record_size : hdr->log_records;
00679 }
00680 #endif /* COFFEE_MICRO_LOGS */
00681 /*---------------------------------------------------------------------------*/
00682 #if COFFEE_MICRO_LOGS
00683 static uint16_t
00684 modify_log_buffer(uint16_t log_record_size,
00685                   cfs_offset_t *offset, uint16_t *size)
00686 {
00687   uint16_t region;
00688 
00689   region = *offset / log_record_size;
00690   *offset %= log_record_size;
00691 
00692   if(*size > log_record_size - *offset) {
00693     *size = log_record_size - *offset;
00694   }
00695 
00696   return region;
00697 }
00698 #endif /* COFFEE_MICRO_LOGS */
00699 /*---------------------------------------------------------------------------*/
00700 #if COFFEE_MICRO_LOGS
00701 static int
00702 get_record_index(coffee_page_t log_page, uint16_t search_records,
00703                  uint16_t region)
00704 {
00705   cfs_offset_t base;
00706   uint16_t processed;
00707   uint16_t batch_size;
00708   int16_t match_index, i;
00709 
00710   base = absolute_offset(log_page, sizeof(uint16_t) * search_records);
00711   batch_size = search_records > COFFEE_LOG_TABLE_LIMIT ?
00712                 COFFEE_LOG_TABLE_LIMIT : search_records;
00713   processed = 0;
00714   match_index = -1;
00715 
00716   {
00717   uint16_t indices[batch_size];
00718 
00719   while(processed < search_records && match_index < 0) {
00720     if(batch_size + processed > search_records) {
00721       batch_size = search_records - processed;
00722     }
00723 
00724     base -= batch_size * sizeof(indices[0]);
00725     COFFEE_READ(&indices, sizeof(indices[0]) * batch_size, base);
00726 
00727     for(i = batch_size - 1; i >= 0; i--) {
00728       if(indices[i] - 1 == region) {
00729         match_index = search_records - processed - (batch_size - i);
00730         break;
00731       }
00732     }
00733 
00734     processed += batch_size;
00735   }
00736   }
00737 
00738   return match_index;
00739 }
00740 #endif /* COFFEE_MICRO_LOGS */
00741 /*---------------------------------------------------------------------------*/
00742 #if COFFEE_MICRO_LOGS
00743 static int
00744 read_log_page(struct file_header *hdr, int16_t record_count,
00745               struct log_param *lp)
00746 {
00747   uint16_t region;
00748   int16_t match_index;
00749   uint16_t log_record_size;
00750   uint16_t log_records;
00751   cfs_offset_t base;
00752   uint16_t search_records;
00753 
00754   adjust_log_config(hdr, &log_record_size, &log_records);
00755   region = modify_log_buffer(log_record_size, &lp->offset, &lp->size);
00756 
00757   search_records = record_count < 0 ? log_records : record_count;
00758   match_index = get_record_index(hdr->log_page, search_records, region);
00759   if(match_index < 0) {
00760     return -1;
00761   }
00762 
00763   base = absolute_offset(hdr->log_page, log_records * sizeof(region));
00764   base += (cfs_offset_t)match_index * log_record_size;
00765   base += lp->offset;
00766   COFFEE_READ(lp->buf, lp->size, base);
00767 
00768   return lp->size;
00769 }
00770 #endif /* COFFEE_MICRO_LOGS */
00771 /*---------------------------------------------------------------------------*/
00772 #if COFFEE_MICRO_LOGS
00773 static coffee_page_t
00774 create_log(struct file *file, struct file_header *hdr)
00775 {
00776   uint16_t log_record_size, log_records;
00777   cfs_offset_t size;
00778   struct file *log_file;
00779 
00780   adjust_log_config(hdr, &log_record_size, &log_records);
00781 
00782   /* Log index size + log data size. */
00783   size = log_records * (sizeof(uint16_t) + log_record_size);
00784 
00785   log_file = reserve(hdr->name, page_count(size), 1, HDR_FLAG_LOG);
00786   if(log_file == NULL) {
00787     return INVALID_PAGE;
00788   }
00789 
00790   hdr->flags |= HDR_FLAG_MODIFIED;
00791   hdr->log_page = log_file->page;
00792   write_header(hdr, file->page);
00793 
00794   file->flags |= COFFEE_FILE_MODIFIED;
00795   return log_file->page;
00796 }
00797 #endif /* COFFEE_MICRO_LOGS */
00798 /*---------------------------------------------------------------------------*/
00799 static int
00800 merge_log(coffee_page_t file_page, int extend)
00801 {
00802   struct file_header hdr, hdr2;
00803   int fd, n;
00804   cfs_offset_t offset;
00805   coffee_page_t max_pages;
00806   struct file *new_file;
00807   int i;
00808 
00809   read_header(&hdr, file_page);
00810 
00811   fd = cfs_open(hdr.name, CFS_READ);
00812   if(fd < 0) {
00813     return -1;
00814   }
00815 
00816   /*
00817    * The reservation function adds extra space for the header, which has
00818    * already been calculated with in the previous reservation.
00819    */
00820   max_pages = hdr.max_pages << extend;
00821   new_file = reserve(hdr.name, max_pages, 1, 0);
00822   if(new_file == NULL) {
00823     cfs_close(fd);
00824     return -1;
00825   }
00826 
00827   offset = 0;
00828   do {
00829     char buf[hdr.log_record_size == 0 ? COFFEE_PAGE_SIZE : hdr.log_record_size];
00830     n = cfs_read(fd, buf, sizeof(buf));
00831     if(n < 0) {
00832       remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, ALLOW_GC);
00833       cfs_close(fd);
00834       return -1;
00835     } else if(n > 0) {
00836       COFFEE_WRITE(buf, n, absolute_offset(new_file->page, offset));
00837       offset += n;
00838     }
00839   } while(n != 0);
00840 
00841   for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
00842     if(coffee_fd_set[i].flags != COFFEE_FD_FREE && 
00843        coffee_fd_set[i].file->page == file_page) {
00844       coffee_fd_set[i].file = new_file;
00845       new_file->references++;
00846     }
00847   }
00848 
00849   if(remove_by_page(file_page, REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) {
00850     remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC);
00851     cfs_close(fd);
00852     return -1;
00853   }
00854 
00855   /* Copy the log configuration and the EOF hint. */
00856   read_header(&hdr2, new_file->page);
00857   hdr2.log_record_size = hdr.log_record_size;
00858   hdr2.log_records = hdr.log_records;
00859   write_header(&hdr2, new_file->page);
00860 
00861   new_file->flags &= ~COFFEE_FILE_MODIFIED;
00862   new_file->end = offset;
00863 
00864   cfs_close(fd);
00865 
00866   return 0;
00867 }
00868 /*---------------------------------------------------------------------------*/
00869 #if COFFEE_MICRO_LOGS
00870 static int
00871 find_next_record(struct file *file, coffee_page_t log_page,
00872                 int log_records)
00873 {
00874   int log_record, preferred_batch_size;
00875 
00876   if(file->record_count >= 0) {
00877     return file->record_count;
00878   }
00879 
00880   preferred_batch_size = log_records > COFFEE_LOG_TABLE_LIMIT ?
00881                          COFFEE_LOG_TABLE_LIMIT : log_records;
00882   {
00883     /* The next log record is unknown at this point; search for it. */
00884     uint16_t indices[preferred_batch_size];
00885     uint16_t processed;
00886     uint16_t batch_size;
00887 
00888     log_record = log_records;
00889     for(processed = 0; processed < log_records; processed += batch_size) {
00890       batch_size = log_records - processed >= preferred_batch_size ?
00891         preferred_batch_size : log_records - processed;
00892 
00893       COFFEE_READ(&indices, batch_size * sizeof(indices[0]),
00894                   absolute_offset(log_page, processed * sizeof(indices[0])));
00895       for(log_record = 0; log_record < batch_size; log_record++) {
00896         if(indices[log_record] == 0) {
00897           log_record += processed;
00898           break;
00899         }
00900       }
00901     }
00902   }
00903 
00904   return log_record;
00905 }
00906 #endif /* COFFEE_MICRO_LOGS */
00907 /*---------------------------------------------------------------------------*/
00908 #if COFFEE_MICRO_LOGS
00909 static int
00910 write_log_page(struct file *file, struct log_param *lp)
00911 {
00912   struct file_header hdr;
00913   uint16_t region;
00914   coffee_page_t log_page;
00915   int16_t log_record;
00916   uint16_t log_record_size;
00917   uint16_t log_records;
00918   cfs_offset_t offset;
00919   struct log_param lp_out;
00920 
00921   read_header(&hdr, file->page);
00922 
00923   adjust_log_config(&hdr, &log_record_size, &log_records);
00924   region = modify_log_buffer(log_record_size, &lp->offset, &lp->size);
00925 
00926   log_page = 0;
00927   if(HDR_MODIFIED(hdr)) {
00928     /* A log structure has already been created. */
00929     log_page = hdr.log_page;
00930     log_record = find_next_record(file, log_page, log_records);
00931     if(log_record >= log_records) {
00932       /* The log is full; merge the log. */
00933       PRINTF("Coffee: Merging the file %s with its log\n", hdr.name);
00934       return merge_log(file->page, 0);
00935     }
00936   } else {
00937     /* Create a log structure. */
00938     log_page = create_log(file, &hdr);
00939     if(log_page == INVALID_PAGE) {
00940       return -1;
00941     }
00942     PRINTF("Coffee: Created a log structure for file %s at page %u\n",
00943         hdr.name, (unsigned)log_page);
00944     hdr.log_page = log_page;
00945     log_record = 0;
00946   }
00947 
00948   {
00949     char copy_buf[log_record_size];
00950 
00951     lp_out.offset = offset = region * log_record_size;
00952     lp_out.buf = copy_buf;
00953     lp_out.size = log_record_size;
00954 
00955     if((lp->offset > 0 || lp->size != log_record_size) &&
00956         read_log_page(&hdr, log_record, &lp_out) < 0) {
00957       COFFEE_READ(copy_buf, sizeof(copy_buf),
00958           absolute_offset(file->page, offset));
00959     }
00960 
00961     memcpy(&copy_buf[lp->offset], lp->buf, lp->size);
00962 
00963     /*
00964      * Write the region number in the region index table.
00965      * The region number is incremented to avoid values of zero.
00966      */
00967     offset = absolute_offset(log_page, 0);
00968     ++region;
00969     COFFEE_WRITE(&region, sizeof(region),
00970                  offset + log_record * sizeof(region));
00971 
00972     offset += log_records * sizeof(region);
00973     COFFEE_WRITE(copy_buf, sizeof(copy_buf),
00974                  offset + log_record * log_record_size);
00975     file->record_count = log_record + 1;
00976   }
00977 
00978   return lp->size;
00979 }
00980 #endif /* COFFEE_MICRO_LOGS */
00981 /*---------------------------------------------------------------------------*/
00982 static int
00983 get_available_fd(void)
00984 {
00985   int i;
00986 
00987   for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
00988     if(coffee_fd_set[i].flags == COFFEE_FD_FREE) {
00989       return i;
00990     }
00991   }
00992   return -1;
00993 }
00994 /*---------------------------------------------------------------------------*/
00995 int
00996 cfs_open(const char *name, int flags)
00997 {
00998   int fd;
00999   struct file_desc *fdp;
01000 
01001   fd = get_available_fd();
01002   if(fd < 0) {
01003     PRINTF("Coffee: Failed to allocate a new file descriptor!\n");
01004     return -1;
01005   }
01006 
01007   fdp = &coffee_fd_set[fd];
01008   fdp->flags = 0;
01009 
01010   fdp->file = find_file(name);
01011   if(fdp->file == NULL) {
01012     if((flags & (CFS_READ | CFS_WRITE)) == CFS_READ) {
01013       return -1;
01014     }
01015     fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0);
01016     if(fdp->file == NULL) {
01017       return -1;
01018     }
01019     fdp->file->end = 0;
01020   } else if(fdp->file->end == UNKNOWN_OFFSET) {
01021     fdp->file->end = file_end(fdp->file->page);
01022   }
01023 
01024   fdp->flags |= flags;
01025   fdp->offset = flags & CFS_APPEND ? fdp->file->end : 0;
01026   fdp->file->references++;
01027 
01028   return fd;
01029 }
01030 /*---------------------------------------------------------------------------*/
01031 void
01032 cfs_close(int fd)
01033 {
01034   if(FD_VALID(fd)) {
01035     coffee_fd_set[fd].flags = COFFEE_FD_FREE;
01036     coffee_fd_set[fd].file->references--;
01037     coffee_fd_set[fd].file = NULL;
01038   }
01039 }
01040 /*---------------------------------------------------------------------------*/
01041 cfs_offset_t
01042 cfs_seek(int fd, cfs_offset_t offset, int whence)
01043 {
01044   struct file_desc *fdp;
01045   cfs_offset_t new_offset;
01046 
01047   if(!FD_VALID(fd)) {
01048     return -1;
01049   }
01050   fdp = &coffee_fd_set[fd];
01051 
01052   if(whence == CFS_SEEK_SET) {
01053     new_offset = offset;
01054   } else if(whence == CFS_SEEK_END) {
01055     new_offset = fdp->file->end + offset;
01056   } else if(whence == CFS_SEEK_CUR) {
01057     new_offset = fdp->offset + offset;
01058   } else {
01059     return (cfs_offset_t)-1;
01060   }
01061 
01062   if(new_offset < 0 || new_offset > fdp->file->max_pages * COFFEE_PAGE_SIZE) {
01063     return -1;
01064   }
01065 
01066   if(fdp->file->end < new_offset) {
01067     fdp->file->end = new_offset;
01068   }
01069 
01070   return fdp->offset = new_offset;
01071 }
01072 /*---------------------------------------------------------------------------*/
01073 int
01074 cfs_remove(const char *name)
01075 {
01076   struct file *file;
01077 
01078   /*
01079    * Coffee removes files by marking them as obsolete. The space
01080    * is not guaranteed to be reclaimed immediately, but must be
01081    * sweeped by the garbage collector. The garbage collector is
01082    * called once a file reservation request cannot be granted.
01083    */
01084   file = find_file(name);
01085   if(file == NULL) {
01086     return -1;
01087   }
01088 
01089   return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
01090 }
01091 /*---------------------------------------------------------------------------*/
01092 int
01093 cfs_read(int fd, void *buf, unsigned size)
01094 {
01095   struct file_desc *fdp;
01096   struct file *file;
01097 #if COFFEE_MICRO_LOGS
01098   struct file_header hdr;
01099   struct log_param lp;
01100   unsigned bytes_left;
01101   int r;
01102 #endif
01103 
01104   if(!(FD_VALID(fd) && FD_READABLE(fd))) {
01105     return -1;
01106   }
01107 
01108   fdp = &coffee_fd_set[fd];
01109   file = fdp->file;
01110   if(fdp->offset + size > file->end) {
01111     size = file->end - fdp->offset;
01112   }
01113 
01114   /* If the file is allocated, read directly in the file. */
01115   if(!FILE_MODIFIED(file)) {
01116     COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
01117     fdp->offset += size;
01118     return size;
01119   }
01120 
01121 #if COFFEE_MICRO_LOGS
01122   read_header(&hdr, file->page);
01123 
01124   /*
01125    * Fill the buffer by copying from the log in first hand, or the
01126    * ordinary file if the page has no log record.
01127    */
01128   for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
01129     r = -1;
01130 
01131     lp.offset = fdp->offset;
01132     lp.buf = buf;
01133     lp.size = bytes_left;
01134     r = read_log_page(&hdr, file->record_count, &lp);
01135 
01136     /* Read from the original file if we cannot find the data in the log. */
01137     if(r < 0) {
01138       COFFEE_READ(buf, lp.size, absolute_offset(file->page, fdp->offset));
01139       r = lp.size;
01140     }
01141     fdp->offset += r;
01142     buf = (char *)buf + r;
01143   }
01144 #endif /* COFFEE_MICRO_LOGS */
01145 
01146   return size;
01147 }
01148 /*---------------------------------------------------------------------------*/
01149 int
01150 cfs_write(int fd, const void *buf, unsigned size)
01151 {
01152   struct file_desc *fdp;
01153   struct file *file;
01154 #if COFFEE_MICRO_LOGS
01155   int i;
01156   struct log_param lp;
01157   cfs_offset_t bytes_left;
01158   const char dummy[1] = { 0xff };
01159 #endif
01160 
01161   if(!(FD_VALID(fd) && FD_WRITABLE(fd))) {
01162     return -1;
01163   }
01164 
01165   fdp = &coffee_fd_set[fd];
01166   file = fdp->file;
01167 
01168   /* Attempt to extend the file if we try to write past the end. */
01169 #if COFFEE_IO_SEMANTICS
01170   if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
01171 #endif
01172   while(size + fdp->offset + sizeof(struct file_header) >
01173      (file->max_pages * COFFEE_PAGE_SIZE)) {
01174     if(merge_log(file->page, 1) < 0) {
01175       return -1;
01176     }
01177     file = fdp->file;
01178     PRINTF("Extended the file at page %u\n", (unsigned)file->page);
01179   }
01180 #if COFFEE_IO_SEMANTICS
01181   }
01182 #endif
01183 
01184 #if COFFEE_MICRO_LOGS
01185 #if COFFEE_IO_SEMANTICS
01186   if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
01187      (FILE_MODIFIED(file) || fdp->offset < file->end)) {
01188 #else
01189   if(FILE_MODIFIED(file) || fdp->offset < file->end) {
01190 #endif
01191     for(bytes_left = size; bytes_left > 0;) {
01192       lp.offset = fdp->offset;
01193       lp.buf = buf;
01194       lp.size = bytes_left;
01195       i = write_log_page(file, &lp);
01196       if(i < 0) {
01197         /* Return -1 if we wrote nothing because the log write failed. */
01198         if(size == bytes_left) {
01199           return -1;
01200         }
01201         break;
01202       } else if(i == 0) {
01203         /* The file was merged with the log. */
01204         file = fdp->file;
01205       } else {
01206         /* A log record was written. */
01207         bytes_left -= i;
01208         fdp->offset += i;
01209         buf = (char *)buf + i;
01210 
01211         /* Update the file end for a potential log merge that might
01212            occur while writing log records. */
01213         if(fdp->offset > file->end) {
01214           file->end = fdp->offset;
01215         }
01216       }
01217     }
01218 
01219     if(fdp->offset > file->end) {
01220       /* Update the original file's end with a dummy write. */
01221       COFFEE_WRITE(dummy, 1, absolute_offset(file->page, fdp->offset));
01222     }
01223   } else {
01224 #endif /* COFFEE_MICRO_LOGS */
01225 #if COFFEE_APPEND_ONLY
01226     if(fdp->offset < file->end) {
01227       return -1;
01228     }
01229 #endif /* COFFEE_APPEND_ONLY */
01230 
01231     COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
01232     fdp->offset += size;
01233 #if COFFEE_MICRO_LOGS
01234   }
01235 #endif /* COFFEE_MICRO_LOGS */
01236 
01237   if(fdp->offset > file->end) {
01238     file->end = fdp->offset;
01239   }
01240 
01241   return size;
01242 }
01243 /*---------------------------------------------------------------------------*/
01244 int
01245 cfs_opendir(struct cfs_dir *dir, const char *name)
01246 {
01247   /*
01248    * Coffee is only guaranteed to support "/" and ".", but it does not 
01249    * currently enforce this.
01250    */
01251   memset(dir->dummy_space, 0, sizeof(coffee_page_t));
01252   return 0;
01253 }
01254 /*---------------------------------------------------------------------------*/
01255 int
01256 cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
01257 {
01258   struct file_header hdr;
01259   coffee_page_t page;
01260 
01261   memcpy(&page, dir->dummy_space, sizeof(coffee_page_t));
01262 
01263   while(page < COFFEE_PAGE_COUNT) {
01264     read_header(&hdr, page);
01265     if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
01266       coffee_page_t next_page;
01267       memcpy(record->name, hdr.name, sizeof(record->name));
01268       record->name[sizeof(record->name) - 1] = '\0';
01269       record->size = file_end(page);
01270 
01271       next_page = next_file(page, &hdr);
01272       memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t));
01273       return 0;
01274     }
01275     page = next_file(page, &hdr);
01276   }
01277 
01278   return -1;
01279 }
01280 /*---------------------------------------------------------------------------*/
01281 void
01282 cfs_closedir(struct cfs_dir *dir)
01283 {
01284   return;
01285 }
01286 /*---------------------------------------------------------------------------*/
01287 int
01288 cfs_coffee_reserve(const char *name, cfs_offset_t size)
01289 {
01290   return reserve(name, page_count(size), 0, 0) == NULL ? -1 : 0;
01291 }
01292 /*---------------------------------------------------------------------------*/
01293 int
01294 cfs_coffee_configure_log(const char *filename, unsigned log_size,
01295                          unsigned log_record_size)
01296 {
01297   struct file *file;
01298   struct file_header hdr;
01299 
01300   if(log_record_size == 0 || log_record_size > COFFEE_PAGE_SIZE ||
01301      log_size < log_record_size) {
01302     return -1;
01303   }
01304 
01305   file = find_file(filename);
01306   if(file == NULL) {
01307     return -1;
01308   }
01309 
01310   read_header(&hdr, file->page);
01311   if(HDR_MODIFIED(hdr)) {
01312     /* Too late to customize the log. */
01313     return -1;
01314   }
01315 
01316   hdr.log_records = log_size / log_record_size;
01317   hdr.log_record_size = log_record_size;
01318   write_header(&hdr, file->page);
01319 
01320   return 0;
01321 }
01322 /*---------------------------------------------------------------------------*/
01323 #if COFFEE_IO_SEMANTICS
01324 int
01325 cfs_coffee_set_io_semantics(int fd, unsigned flags)
01326 {
01327   if(!FD_VALID(fd)) {
01328     return -1;
01329   }
01330 
01331   coffee_fd_set[fd].io_flags |= flags;
01332 
01333   return 0;
01334 }
01335 #endif
01336 /*---------------------------------------------------------------------------*/
01337 int
01338 cfs_coffee_format(void)
01339 {
01340   unsigned i;
01341 
01342   PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);
01343 
01344   *next_free = 0;
01345 
01346   for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
01347     COFFEE_ERASE(i);
01348     PRINTF(".");
01349   }
01350 
01351   /* Formatting invalidates the file information. */
01352   memset(&protected_mem, 0, sizeof(protected_mem));
01353 
01354   PRINTF(" done!\n");
01355 
01356   return 0;
01357 }
01358 /*---------------------------------------------------------------------------*/
01359 void *
01360 cfs_coffee_get_protected_mem(unsigned *size)
01361 {
01362   *size = sizeof(protected_mem);
01363   return &protected_mem;
01364 }