/*---------------------------------------------------------------------------*/ static struct file * find_file(const char *name) { int i; struct file_header hdr; coffee_page_t page; /* First check if the file metadata is cached. */ for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) { if(FILE_FREE(&coffee_files[i])) { continue; } read_header(&hdr, coffee_files[i].page); if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) { return &coffee_files[i]; } } /* Scan the flash memory sequentially otherwise. */ for(page = 0; page < COFFEE_PAGE_COUNT; page = next_file(page, &hdr)) { read_header(&hdr, page); if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr) && strcmp(name, hdr.name) == 0) { return load_file(page, &hdr); } } return NULL; }
/*---------------------------------------------------------------------------*/ int cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record) { struct file_header hdr; coffee_page_t page; memcpy(&page, dir->dummy_space, sizeof(coffee_page_t)); while(page < COFFEE_PAGE_COUNT) { read_header(&hdr, page); if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) { coffee_page_t next_page; memcpy(record->name, hdr.name, sizeof(record->name)); record->name[sizeof(record->name) - 1] = '\0'; record->size = file_end(page); next_page = next_file(page, &hdr); memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t)); return 0; } page = next_file(page, &hdr); } return -1; }
/*---------------------------------------------------------------------------*/ static void read_header(struct file_header *hdr, coffee_page_t page) { COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE); #if DEBUG if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) { PRINTF("Invalid header at page %u!\n", (unsigned)page); } #endif }
/*---------------------------------------------------------------------------*/ static int remove_by_page(coffee_page_t page, int remove_log, int close_fds, int gc_allowed) { struct file_header hdr; int i; read_header(&hdr, page); if(!HDR_ACTIVE(hdr)) { return -1; } if(remove_log && HDR_MODIFIED(hdr)) { if(remove_by_page(hdr.log_page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) { return -1; } } hdr.flags |= HDR_FLAG_OBSOLETE; write_header(&hdr, page); *gc_wait = 0; /* Close all file descriptors that reference the removed file. */ if(close_fds) { for(i = 0; i < COFFEE_FD_SET_SIZE; i++) { if(coffee_fd_set[i].file != NULL && coffee_fd_set[i].file->page == page) { coffee_fd_set[i].flags = COFFEE_FD_FREE; } } } for(i = 0; i < COFFEE_MAX_OPEN_FILES; i++) { if(coffee_files[i].page == page) { coffee_files[i].page = INVALID_PAGE; coffee_files[i].references = 0; coffee_files[i].max_pages = 0; } } #if !COFFEE_EXTENDED_WEAR_LEVELLING if(gc_allowed) { collect_garbage(GC_RELUCTANT); } #endif return 0; }
/*---------------------------------------------------------------------------*/ int cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record) { struct file_header hdr; coffee_page_t page; for(page = *(coffee_page_t *)dir->dummy_space; page < COFFEE_PAGE_COUNT;) { watchdog_periodic(); read_header(&hdr, page); if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) { memcpy(record->name, hdr.name, sizeof(record->name)); record->name[sizeof(record->name) - 1] = '\0'; record->size = file_end(page); *(coffee_page_t *)dir->dummy_space = next_file(page, &hdr); return 0; } page = next_file(page, &hdr); } return -1; }
/*---------------------------------------------------------------------------*/ static coffee_page_t get_sector_status(uint16_t sector, struct sector_status *stats) { static coffee_page_t skip_pages; static char last_pages_are_active; struct file_header hdr; coffee_page_t active, obsolete, free; coffee_page_t sector_start, sector_end; coffee_page_t page; memset(stats, 0, sizeof(*stats)); active = obsolete = free = 0; /* * get_sector_status() is an iterative function using local static * state. It therefore requires that the caller starts iterating from * sector 0 in order to reset the internal state. */ if(sector == 0) { skip_pages = 0; last_pages_are_active = 0; } sector_start = sector * COFFEE_PAGES_PER_SECTOR; sector_end = sector_start + COFFEE_PAGES_PER_SECTOR; /* * Account for pages belonging to a file starting in a previous * segment that extends into this segment. If the whole segment is * covered, we do not need to continue counting pages in this iteration. */ if(last_pages_are_active) { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->active = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return 0; } active = skip_pages; } else { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->obsolete = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages; } obsolete = skip_pages; } /* Determine the amount of pages of each type that have not been accounted for yet in the current sector. */ for(page = sector_start + skip_pages; page < sector_end;) { read_header(&hdr, page); last_pages_are_active = 0; if(HDR_ACTIVE(hdr)) { last_pages_are_active = 1; page += hdr.max_pages; active += hdr.max_pages; } else if(HDR_ISOLATED(hdr)) { page++; obsolete++; } else if(HDR_OBSOLETE(hdr)) { page += hdr.max_pages; obsolete += hdr.max_pages; } else { free = sector_end - page; break; } } /* * Determine the amount of pages in the following sectors that * should be remembered for the next iteration. This is necessary * because no page except the first of a file contains information * about what type of page it is. A side effect of remembering this * amount is that there is no need to read in the headers of each * of these pages from the storage. */ skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR; if(skip_pages > 0) { if(last_pages_are_active) { active = COFFEE_PAGES_PER_SECTOR - obsolete; } else { obsolete = COFFEE_PAGES_PER_SECTOR - active; } } stats->active = active; stats->obsolete = obsolete; stats->free = free; /* * To avoid unnecessary page isolation, we notify the caller that * "skip_pages" pages should be isolated only if the current file extent * ends in the next sector. If the file extent ends in a more distant * sector, however, the garbage collection can free the next sector * immediately without requiring page isolation. */ return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ? 0 : skip_pages; }
/*---------------------------------------------------------------------------*/ static coffee_page_t get_sector_status(uint16_t sector, struct sector_status *stats) { static coffee_page_t skip_pages; static char last_pages_are_active; struct file_header hdr; coffee_page_t active, obsolete, free; coffee_page_t sector_start, sector_end; coffee_page_t page; memset(stats, 0, sizeof(*stats)); active = obsolete = free = 0; if(sector == 0) { skip_pages = 0; last_pages_are_active = 0; } sector_start = sector * COFFEE_PAGES_PER_SECTOR; sector_end = sector_start + COFFEE_PAGES_PER_SECTOR; if(last_pages_are_active) { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->active = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return 0; } active = skip_pages; } else { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->obsolete = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages; } obsolete = skip_pages; } for(page = sector_start + skip_pages; page < sector_end;) { read_header(&hdr, page); last_pages_are_active = 0; if(HDR_ACTIVE(hdr)) { last_pages_are_active = 1; page += hdr.max_pages; active += hdr.max_pages; } else if(HDR_ISOLATED(hdr)) { page++; obsolete++; } else if(HDR_OBSOLETE(hdr)) { page += hdr.max_pages; obsolete += hdr.max_pages; } else { free = sector_end - page; break; } } skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR; if(skip_pages > 0) { if(last_pages_are_active) { active = COFFEE_PAGES_PER_SECTOR - obsolete; } else { obsolete = COFFEE_PAGES_PER_SECTOR - active; } } stats->active = active; stats->obsolete = obsolete; stats->free = free; return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ? 0 : skip_pages; }