Contiki 2.6
|
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(©_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(®ion, 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 }