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 } __attribute__((packed));
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   coffee_page_t log_page;
00803   struct file_header hdr, hdr2;
00804   int fd, n;
00805   cfs_offset_t offset;
00806   coffee_page_t max_pages;
00807   struct file *new_file;
00808   int i;
00809 
00810   read_header(&hdr, file_page);
00811   log_page = hdr.log_page;
00812 
00813   fd = cfs_open(hdr.name, CFS_READ);
00814   if(fd < 0) {
00815     return -1;
00816   }
00817 
00818   /*
00819    * The reservation function adds extra space for the header, which has
00820    * already been calculated with in the previous reservation.
00821    */
00822   max_pages = hdr.max_pages << extend;
00823   new_file = reserve(hdr.name, max_pages, 1, 0);
00824   if(new_file == NULL) {
00825     cfs_close(fd);
00826     return -1;
00827   }
00828 
00829   offset = 0;
00830   do {
00831     char buf[hdr.log_record_size == 0 ? COFFEE_PAGE_SIZE : hdr.log_record_size];
00832     n = cfs_read(fd, buf, sizeof(buf));
00833     if(n < 0) {
00834       remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, ALLOW_GC);
00835       cfs_close(fd);
00836       return -1;
00837     } else if(n > 0) {
00838       COFFEE_WRITE(buf, n, absolute_offset(new_file->page, offset));
00839       offset += n;
00840     }
00841   } while(n != 0);
00842 
00843   for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
00844     if(coffee_fd_set[i].flags != COFFEE_FD_FREE && 
00845        coffee_fd_set[i].file->page == file_page) {
00846       coffee_fd_set[i].file = new_file;
00847       new_file->references++;
00848     }
00849   }
00850 
00851   if(remove_by_page(file_page, REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) {
00852     remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC);
00853     cfs_close(fd);
00854     return -1;
00855   }
00856 
00857   /* Copy the log configuration and the EOF hint. */
00858   read_header(&hdr2, new_file->page);
00859   hdr2.log_record_size = hdr.log_record_size;
00860   hdr2.log_records = hdr.log_records;
00861   write_header(&hdr2, new_file->page);
00862 
00863   new_file->flags &= ~COFFEE_FILE_MODIFIED;
00864   new_file->end = offset;
00865 
00866   cfs_close(fd);
00867 
00868   return 0;
00869 }
00870 /*---------------------------------------------------------------------------*/
00871 #if COFFEE_MICRO_LOGS
00872 static int
00873 find_next_record(struct file *file, coffee_page_t log_page,
00874                 int log_records)
00875 {
00876   int log_record, preferred_batch_size;
00877 
00878   if(file->record_count >= 0) {
00879     return file->record_count;
00880   }
00881 
00882   preferred_batch_size = log_records > COFFEE_LOG_TABLE_LIMIT ?
00883                          COFFEE_LOG_TABLE_LIMIT : log_records;
00884   {
00885     /* The next log record is unknown at this point; search for it. */
00886     uint16_t indices[preferred_batch_size];
00887     uint16_t processed;
00888     uint16_t batch_size;
00889 
00890     log_record = log_records;
00891     for(processed = 0; processed < log_records; processed += batch_size) {
00892       batch_size = log_records - processed >= preferred_batch_size ?
00893         preferred_batch_size : log_records - processed;
00894 
00895       COFFEE_READ(&indices, batch_size * sizeof(indices[0]),
00896                   absolute_offset(log_page, processed * sizeof(indices[0])));
00897       for(log_record = 0; log_record < batch_size; log_record++) {
00898         if(indices[log_record] == 0) {
00899           log_record += processed;
00900           break;
00901         }
00902       }
00903     }
00904   }
00905 
00906   return log_record;
00907 }
00908 #endif /* COFFEE_MICRO_LOGS */
00909 /*---------------------------------------------------------------------------*/
00910 #if COFFEE_MICRO_LOGS
00911 static int
00912 write_log_page(struct file *file, struct log_param *lp)
00913 {
00914   struct file_header hdr;
00915   uint16_t region;
00916   coffee_page_t log_page;
00917   int16_t log_record;
00918   uint16_t log_record_size;
00919   uint16_t log_records;
00920   cfs_offset_t offset;
00921   struct log_param lp_out;
00922 
00923   read_header(&hdr, file->page);
00924 
00925   adjust_log_config(&hdr, &log_record_size, &log_records);
00926   region = modify_log_buffer(log_record_size, &lp->offset, &lp->size);
00927 
00928   log_page = 0;
00929   if(HDR_MODIFIED(hdr)) {
00930     /* A log structure has already been created. */
00931     log_page = hdr.log_page;
00932     log_record = find_next_record(file, log_page, log_records);
00933     if(log_record >= log_records) {
00934       /* The log is full; merge the log. */
00935       PRINTF("Coffee: Merging the file %s with its log\n", hdr.name);
00936       return merge_log(file->page, 0);
00937     }
00938   } else {
00939     /* Create a log structure. */
00940     log_page = create_log(file, &hdr);
00941     if(log_page == INVALID_PAGE) {
00942       return -1;
00943     }
00944     PRINTF("Coffee: Created a log structure for file %s at page %u\n",
00945         hdr.name, (unsigned)log_page);
00946     hdr.log_page = log_page;
00947     log_record = 0;
00948   }
00949 
00950   {
00951     char copy_buf[log_record_size];
00952 
00953     lp_out.offset = offset = region * log_record_size;
00954     lp_out.buf = copy_buf;
00955     lp_out.size = log_record_size;
00956 
00957     if((lp->offset > 0 || lp->size != log_record_size) &&
00958         read_log_page(&hdr, log_record, &lp_out) < 0) {
00959       COFFEE_READ(copy_buf, sizeof(copy_buf),
00960           absolute_offset(file->page, offset));
00961     }
00962 
00963     memcpy(&copy_buf[lp->offset], lp->buf, lp->size);
00964 
00965     /*
00966      * Write the region number in the region index table.
00967      * The region number is incremented to avoid values of zero.
00968      */
00969     offset = absolute_offset(log_page, 0);
00970     ++region;
00971     COFFEE_WRITE(&region, sizeof(region),
00972                  offset + log_record * sizeof(region));
00973 
00974     offset += log_records * sizeof(region);
00975     COFFEE_WRITE(copy_buf, sizeof(copy_buf),
00976                  offset + log_record * log_record_size);
00977     file->record_count = log_record + 1;
00978   }
00979 
00980   return lp->size;
00981 }
00982 #endif /* COFFEE_MICRO_LOGS */
00983 /*---------------------------------------------------------------------------*/
00984 static int
00985 get_available_fd(void)
00986 {
00987   int i;
00988 
00989   for(i = 0; i < COFFEE_FD_SET_SIZE; i++) {
00990     if(coffee_fd_set[i].flags == COFFEE_FD_FREE) {
00991       return i;
00992     }
00993   }
00994   return -1;
00995 }
00996 /*---------------------------------------------------------------------------*/
00997 int
00998 cfs_open(const char *name, int flags)
00999 {
01000   int fd;
01001   struct file_desc *fdp;
01002 
01003   fd = get_available_fd();
01004   if(fd < 0) {
01005     PRINTF("Coffee: Failed to allocate a new file descriptor!\n");
01006     return -1;
01007   }
01008 
01009   fdp = &coffee_fd_set[fd];
01010   fdp->flags = 0;
01011 
01012   fdp->file = find_file(name);
01013   if(fdp->file == NULL) {
01014     if((flags & (CFS_READ | CFS_WRITE)) == CFS_READ) {
01015       return -1;
01016     }
01017     fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0);
01018     if(fdp->file == NULL) {
01019       return -1;
01020     }
01021     fdp->file->end = 0;
01022   } else if(fdp->file->end == UNKNOWN_OFFSET) {
01023     fdp->file->end = file_end(fdp->file->page);
01024   }
01025 
01026   fdp->flags |= flags;
01027   fdp->offset = flags & CFS_APPEND ? fdp->file->end : 0;
01028   fdp->file->references++;
01029 
01030   return fd;
01031 }
01032 /*---------------------------------------------------------------------------*/
01033 void
01034 cfs_close(int fd)
01035 {
01036   if(FD_VALID(fd)) {
01037     coffee_fd_set[fd].flags = COFFEE_FD_FREE;
01038     coffee_fd_set[fd].file->references--;
01039     coffee_fd_set[fd].file = NULL;
01040   }
01041 }
01042 /*---------------------------------------------------------------------------*/
01043 cfs_offset_t
01044 cfs_seek(int fd, cfs_offset_t offset, int whence)
01045 {
01046   struct file_desc *fdp;
01047   cfs_offset_t new_offset;
01048 
01049   if(!FD_VALID(fd)) {
01050     return -1;
01051   }
01052   fdp = &coffee_fd_set[fd];
01053 
01054   if(whence == CFS_SEEK_SET) {
01055     new_offset = offset;
01056   } else if(whence == CFS_SEEK_END) {
01057     new_offset = fdp->file->end + offset;
01058   } else if(whence == CFS_SEEK_CUR) {
01059     new_offset = fdp->offset + offset;
01060   } else {
01061     return (cfs_offset_t)-1;
01062   }
01063 
01064   if(new_offset < 0 || new_offset > fdp->file->max_pages * COFFEE_PAGE_SIZE) {
01065     return -1;
01066   }
01067 
01068   if(fdp->file->end < new_offset) {
01069     fdp->file->end = new_offset;
01070   }
01071 
01072   return fdp->offset = new_offset;
01073 }
01074 /*---------------------------------------------------------------------------*/
01075 int
01076 cfs_remove(const char *name)
01077 {
01078   struct file *file;
01079 
01080   /*
01081    * Coffee removes files by marking them as obsolete. The space
01082    * is not guaranteed to be reclaimed immediately, but must be
01083    * sweeped by the garbage collector. The garbage collector is
01084    * called once a file reservation request cannot be granted.
01085    */
01086   file = find_file(name);
01087   if(file == NULL) {
01088     return -1;
01089   }
01090 
01091   return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
01092 }
01093 /*---------------------------------------------------------------------------*/
01094 int
01095 cfs_read(int fd, void *buf, unsigned size)
01096 {
01097   struct file_desc *fdp;
01098   struct file *file;
01099 #if COFFEE_MICRO_LOGS
01100   struct file_header hdr;
01101   struct log_param lp;
01102   unsigned bytes_left;
01103   int r;
01104 #endif
01105 
01106   if(!(FD_VALID(fd) && FD_READABLE(fd))) {
01107     return -1;
01108   }
01109 
01110   fdp = &coffee_fd_set[fd];
01111   file = fdp->file;
01112   if(fdp->offset + size > file->end) {
01113     size = file->end - fdp->offset;
01114   }
01115 
01116   /* If the file is allocated, read directly in the file. */
01117   if(!FILE_MODIFIED(file)) {
01118     COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
01119     fdp->offset += size;
01120     return size;
01121   }
01122 
01123 #if COFFEE_MICRO_LOGS
01124   read_header(&hdr, file->page);
01125 
01126   /*
01127    * Fill the buffer by copying from the log in first hand, or the
01128    * ordinary file if the page has no log record.
01129    */
01130   for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
01131     r = -1;
01132 
01133     lp.offset = fdp->offset;
01134     lp.buf = buf;
01135     lp.size = bytes_left;
01136     r = read_log_page(&hdr, file->record_count, &lp);
01137 
01138     /* Read from the original file if we cannot find the data in the log. */
01139     if(r < 0) {
01140       COFFEE_READ(buf, bytes_left, absolute_offset(file->page, fdp->offset));
01141       r = bytes_left;
01142     }
01143     fdp->offset += r;
01144     buf += r;
01145   }
01146 #endif /* COFFEE_MICRO_LOGS */
01147 
01148   return size;
01149 }
01150 /*---------------------------------------------------------------------------*/
01151 int
01152 cfs_write(int fd, const void *buf, unsigned size)
01153 {
01154   struct file_desc *fdp;
01155   struct file *file;
01156 #if COFFEE_MICRO_LOGS
01157   int i;
01158   struct log_param lp;
01159   cfs_offset_t bytes_left;
01160   const char dummy[1] = { 0xff };
01161 #endif
01162 
01163   if(!(FD_VALID(fd) && FD_WRITABLE(fd))) {
01164     return -1;
01165   }
01166 
01167   fdp = &coffee_fd_set[fd];
01168   file = fdp->file;
01169 
01170   /* Attempt to extend the file if we try to write past the end. */
01171 #if COFFEE_IO_SEMANTICS
01172   if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
01173 #endif
01174   while(size + fdp->offset + sizeof(struct file_header) >
01175      (file->max_pages * COFFEE_PAGE_SIZE)) {
01176     if(merge_log(file->page, 1) < 0) {
01177       return -1;
01178     }
01179     file = fdp->file;
01180     PRINTF("Extended the file at page %u\n", (unsigned)file->page);
01181   }
01182 #if COFFEE_IO_SEMANTICS
01183   }
01184 #endif
01185 
01186 #if COFFEE_MICRO_LOGS
01187 #if COFFEE_IO_SEMANTICS
01188   if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
01189      (FILE_MODIFIED(file) || fdp->offset < file->end)) {
01190 #else
01191   if(FILE_MODIFIED(file) || fdp->offset < file->end) {
01192 #endif
01193     for(bytes_left = size; bytes_left > 0;) {
01194       lp.offset = fdp->offset;
01195       lp.buf = buf;
01196       lp.size = bytes_left;
01197       i = write_log_page(file, &lp);
01198       if(i < 0) {
01199         /* Return -1 if we wrote nothing because the log write failed. */
01200         if(size == bytes_left) {
01201           return -1;
01202         }
01203         break;
01204       } else if(i == 0) {
01205         /* The file was merged with the log. */
01206         file = fdp->file;
01207       } else {
01208         /* A log record was written. */
01209         bytes_left -= i;
01210         fdp->offset += i;
01211         buf += i;
01212 
01213         /* Update the file end for a potential log merge that might
01214            occur while writing log records. */
01215         if(fdp->offset > file->end) {
01216           file->end = fdp->offset;
01217         }
01218       }
01219     }
01220 
01221     if(fdp->offset > file->end) {
01222       /* Update the original file's end with a dummy write. */
01223       COFFEE_WRITE(dummy, 1, absolute_offset(file->page, fdp->offset));
01224     }
01225   } else {
01226 #endif /* COFFEE_MICRO_LOGS */
01227 #if COFFEE_APPEND_ONLY
01228     if(fdp->offset < file->end) {
01229       return -1;
01230     }
01231 #endif /* COFFEE_APPEND_ONLY */
01232 
01233     COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
01234     fdp->offset += size;
01235 #if COFFEE_MICRO_LOGS
01236   }
01237 #endif /* COFFEE_MICRO_LOGS */
01238 
01239   if(fdp->offset > file->end) {
01240     file->end = fdp->offset;
01241   }
01242 
01243   return size;
01244 }
01245 /*---------------------------------------------------------------------------*/
01246 int
01247 cfs_opendir(struct cfs_dir *dir, const char *name)
01248 {
01249   /*
01250    * Coffee is only guaranteed to support "/" and ".", but it does not 
01251    * currently enforce this.
01252    */
01253   memset(dir->dummy_space, 0, sizeof(coffee_page_t));
01254   return 0;
01255 }
01256 /*---------------------------------------------------------------------------*/
01257 int
01258 cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
01259 {
01260   struct file_header hdr;
01261   coffee_page_t page;
01262 
01263   memcpy(&page, dir->dummy_space, sizeof(coffee_page_t));
01264 
01265   while(page < COFFEE_PAGE_COUNT) {
01266     read_header(&hdr, page);
01267     if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
01268       coffee_page_t next_page;
01269       memcpy(record->name, hdr.name, sizeof(record->name));
01270       record->name[sizeof(record->name) - 1] = '\0';
01271       record->size = file_end(page);
01272 
01273       next_page = next_file(page, &hdr);
01274       memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t));
01275       return 0;
01276     }
01277     page = next_file(page, &hdr);
01278   }
01279 
01280   return -1;
01281 }
01282 /*---------------------------------------------------------------------------*/
01283 void
01284 cfs_closedir(struct cfs_dir *dir)
01285 {
01286   return;
01287 }
01288 /*---------------------------------------------------------------------------*/
01289 int
01290 cfs_coffee_reserve(const char *name, cfs_offset_t size)
01291 {
01292   return reserve(name, page_count(size), 0, 0) == NULL ? -1 : 0;
01293 }
01294 /*---------------------------------------------------------------------------*/
01295 int
01296 cfs_coffee_configure_log(const char *filename, unsigned log_size,
01297                          unsigned log_record_size)
01298 {
01299   struct file *file;
01300   struct file_header hdr;
01301 
01302   if(log_record_size == 0 || log_record_size > COFFEE_PAGE_SIZE ||
01303      log_size < log_record_size) {
01304     return -1;
01305   }
01306 
01307   file = find_file(filename);
01308   if(file == NULL) {
01309     return -1;
01310   }
01311 
01312   read_header(&hdr, file->page);
01313   if(HDR_MODIFIED(hdr)) {
01314     /* Too late to customize the log. */
01315     return -1;
01316   }
01317 
01318   hdr.log_records = log_size / log_record_size;
01319   hdr.log_record_size = log_record_size;
01320   write_header(&hdr, file->page);
01321 
01322   return 0;
01323 }
01324 /*---------------------------------------------------------------------------*/
01325 #if COFFEE_IO_SEMANTICS
01326 int
01327 cfs_coffee_set_io_semantics(int fd, unsigned flags)
01328 {
01329   if(!FD_VALID(fd)) {
01330     return -1;
01331   }
01332 
01333   coffee_fd_set[fd].io_flags |= flags;
01334 
01335   return 0;
01336 }
01337 #endif
01338 /*---------------------------------------------------------------------------*/
01339 int
01340 cfs_coffee_format(void)
01341 {
01342   unsigned i;
01343 
01344   PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);
01345 
01346   *next_free = 0;
01347 
01348   for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
01349     COFFEE_ERASE(i);
01350     PRINTF(".");
01351   }
01352 
01353   /* Formatting invalidates the file information. */
01354   memset(&protected_mem, 0, sizeof(protected_mem));
01355 
01356   PRINTF(" done!\n");
01357 
01358   return 0;
01359 }
01360 /*---------------------------------------------------------------------------*/
01361 void *
01362 cfs_coffee_get_protected_mem(unsigned *size)
01363 {
01364   *size = sizeof(protected_mem);
01365   return &protected_mem;
01366 }

Generated on Mon Apr 11 14:23:26 2011 for Contiki 2.5 by  doxygen 1.6.1