/*---------------------------------------------------------------------------*/ static void write_header(struct file_header *hdr, coffee_page_t page) { hdr->flags |= HDR_FLAG_VALID; COFFEE_WRITE(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE); PRINTF("hdr.flags = %x\n", hdr->flags); }
static int write_log_page(struct file *file, struct log_param *lp) { struct file_header hdr; uint16_t region; coffee_page_t log_page; int16_t log_record; uint16_t log_record_size; uint16_t log_records; cfs_offset_t offset; struct log_param lp_out; read_header(&hdr, file->page); adjust_log_config(&hdr, &log_record_size, &log_records); region = modify_log_buffer(log_record_size, &lp->offset, &lp->size); log_page = 0; if(HDR_MODIFIED(hdr)) { /* A log structure has already been created. */ log_page = hdr.log_page; log_record = find_next_record(file, log_page, log_records); if(log_record >= log_records) { /* The log is full; merge the log. */ PRINTF(("Coffee: Merging the file %s with its log\n", hdr.name)); return merge_log(file->page, 0); } } else { /* Create a log structure. */ log_page = create_log(file, &hdr); if(log_page == INVALID_PAGE) { return -1; } PRINTF(("Coffee: Created a log structure for file %s at page %u\n", hdr.name, (unsigned)log_page)); hdr.log_page = log_page; log_record = 0; } { char copy_buf[log_record_size]; lp_out.offset = offset = region * log_record_size; lp_out.buf = copy_buf; lp_out.size = log_record_size; if((lp->offset > 0 || lp->size != log_record_size) && read_log_page(&hdr, log_record, &lp_out) < 0) { COFFEE_READ(copy_buf, sizeof(copy_buf), absolute_offset(file->page, offset)); } memcpy(©_buf[lp->offset], lp->buf, lp->size); /* * Write the region number in the region index table. * The region number is incremented to avoid values of zero. */ offset = absolute_offset(log_page, 0); ++region; COFFEE_WRITE(®ion, sizeof(region), offset + log_record * sizeof(region)); offset += log_records * sizeof(region); COFFEE_WRITE(copy_buf, sizeof(copy_buf), offset + log_record * log_record_size); file->record_count = log_record + 1; } return lp->size; }
/*---------------------------------------------------------------------------*/ static int merge_log(coffee_page_t file_page, int extend) { struct file_header hdr, hdr2; int fd, n; cfs_offset_t offset; coffee_page_t max_pages; struct file *new_file; int i; read_header(&hdr, file_page); fd = cfs_open(hdr.name, CFS_READ); if(fd < 0) { return -1; } /* * The reservation function adds extra space for the header, which has * already been accounted for in the previous reservation. */ max_pages = hdr.max_pages << extend; new_file = reserve(hdr.name, max_pages, 1, 0); if(new_file == NULL) { cfs_close(fd); return -1; } offset = 0; do { char buf[hdr.log_record_size == 0 ? COFFEE_PAGE_SIZE : hdr.log_record_size]; n = cfs_read(fd, buf, sizeof(buf)); if(n < 0) { remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, ALLOW_GC); cfs_close(fd); return -1; } else if(n > 0) { COFFEE_WRITE(buf, n, absolute_offset(new_file->page, offset)); offset += n; } } while(n != 0); for(i = 0; i < COFFEE_FD_SET_SIZE; i++) { if(coffee_fd_set[i].flags != COFFEE_FD_FREE && coffee_fd_set[i].file->page == file_page) { coffee_fd_set[i].file = new_file; new_file->references++; } } if(remove_by_page(file_page, REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0) { remove_by_page(new_file->page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC); cfs_close(fd); return -1; } /* Copy the log configuration and the EOF hint. */ read_header(&hdr2, new_file->page); hdr2.log_record_size = hdr.log_record_size; hdr2.log_records = hdr.log_records; write_header(&hdr2, new_file->page); new_file->flags &= ~COFFEE_FILE_MODIFIED; new_file->end = offset; cfs_close(fd); return 0; }
/*---------------------------------------------------------------------------*/ int cfs_write(int fd, const void *buf, unsigned size) { struct file_desc *fdp; struct file *file; #if COFFEE_MICRO_LOGS int i; struct log_param lp; cfs_offset_t bytes_left; const char dummy[1] = { 0xff }; #endif if(!(FD_VALID(fd) && FD_WRITABLE(fd))) { return -1; } fdp = &coffee_fd_set[fd]; file = fdp->file; /* Attempt to extend the file if we try to write past the end. */ #if COFFEE_IO_SEMANTICS if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) { #endif while(size + fdp->offset + sizeof(struct file_header) > (file->max_pages * COFFEE_PAGE_SIZE)) { if(merge_log(file->page, 1) < 0) { return -1; } file = fdp->file; PRINTF(("Extended the file at page %u\n", (unsigned)file->page)); } #if COFFEE_IO_SEMANTICS } #endif #if COFFEE_MICRO_LOGS #if COFFEE_IO_SEMANTICS if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) && (FILE_MODIFIED(file) || fdp->offset < file->end)) { #else if(FILE_MODIFIED(file) || fdp->offset < file->end) { #endif for(bytes_left = size; bytes_left > 0;) { lp.offset = fdp->offset; lp.buf = buf; lp.size = bytes_left; i = write_log_page(file, &lp); if(i < 0) { /* Return -1 if we wrote nothing because the log write failed. */ if(size == bytes_left) { return -1; } break; } else if(i == 0) { /* The file was merged with the log. */ file = fdp->file; } else { /* A log record was written. */ bytes_left -= i; fdp->offset += i; buf = (char *)buf + i; /* Update the file end for a potential log merge that might occur while writing log records. */ if(fdp->offset > file->end) { file->end = fdp->offset; } } } if(fdp->offset > file->end) { /* Update the original file's end with a dummy write. */ COFFEE_WRITE(dummy, 1, absolute_offset(file->page, fdp->offset)); } } else { #endif /* COFFEE_MICRO_LOGS */ #if COFFEE_APPEND_ONLY if(fdp->offset < file->end) { return -1; } #endif /* COFFEE_APPEND_ONLY */ COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset)); fdp->offset += size; #if COFFEE_MICRO_LOGS } #endif /* COFFEE_MICRO_LOGS */ if(fdp->offset > file->end) { file->end = fdp->offset; } return size; } /*---------------------------------------------------------------------------*/ int cfs_opendir(struct cfs_dir *dir, const char *name) { /* * Coffee is only guaranteed to support "/" and ".", but it does not * currently enforce this. */ memset(dir->dummy_space, 0, sizeof(coffee_page_t)); return 0; }
/*---------------------------------------------------------------------------*/ int cfs_write(int fd, const void *buf, unsigned size) { struct file_desc *fdp; struct file *file; #if COFFEE_MICRO_LOGS int i; struct log_param lp; cfs_offset_t bytes_left; const char dummy[1] = { 0xff }; #endif if(!(FD_VALID(fd) && FD_WRITABLE(fd))) { return -1; } fdp = &coffee_fd_set[fd]; file = fdp->file; /* Attempt to extend the file if we try to write past the end. */ while(size + fdp->offset + sizeof(struct file_header) > (file->max_pages * COFFEE_PAGE_SIZE)) { if(merge_log(file->page, 1) < 0) { return -1; } file = fdp->file; PRINTF("Extended the file at page %u\n", (unsigned)file->page); } #if COFFEE_MICRO_LOGS if(FILE_MODIFIED(file) || fdp->offset < file->end) { bytes_left = size; while(bytes_left) { lp.offset = fdp->offset; lp.buf = buf; lp.size = bytes_left; i = write_log_page(file, &lp); if(i < 0) { /* Return -1 if we wrote nothing because the log write failed. */ if(size == bytes_left) { return -1; } break; } else if(i == 0) { /* The file was merged with the log. */ file = fdp->file; } else { /* A log record was written. */ bytes_left -= i; fdp->offset += i; buf += i; } } if(fdp->offset > file->end) { /* Update the original file's end with a dummy write. */ COFFEE_WRITE(dummy, 1, absolute_offset(file->page, fdp->offset)); } } else { #endif /* COFFEE_MICRO_LOGS */ #if COFFEE_APPEND_ONLY if(fdp->offset < file->end) { return -1; } #endif /* COFFEE_APPEND_ONLY */ COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset)); fdp->offset += size; #if COFFEE_MICRO_LOGS } #endif /* COFFEE_MICRO_LOGS */ if(fdp->offset > file->end) { file->end = fdp->offset; } return size; }
/*---------------------------------------------------------------------------*/ static void get_sector_status(uint16_t sector, uint16_t *active, uint16_t *free, uint16_t *obsolete) { uint32_t offset, sector_start; uint32_t end; struct file_header hdr; static int16_t skip_pages; static int last_pages_are_active; int i; *active = *free = *obsolete = 0; if(sector == 0) { skip_pages = 0; last_pages_are_active = 0; } else if(skip_pages > COFFEE_PAGES_PER_SECTOR) { skip_pages -= COFFEE_PAGES_PER_SECTOR; if(last_pages_are_active) { *active = COFFEE_PAGES_PER_SECTOR; } else { *obsolete = COFFEE_PAGES_PER_SECTOR; } return; } sector_start = sector * COFFEE_SECTOR_SIZE; if(last_pages_are_active) { *active = skip_pages; } else { *obsolete = skip_pages; /* Split an obsolete file starting in the previous sector and mark the following pages as isolated. */ offset = sector_start; for(i = 0; i < skip_pages; i++) { COFFEE_READ(&hdr, sizeof(hdr), offset); hdr.flags |= COFFEE_FLAG_ISOLATED; COFFEE_WRITE(&hdr, sizeof(hdr), offset); offset += COFFEE_PAGE_SIZE; } PRINTF("Coffee: Isolated %u pages starting in sector %d\n", (unsigned)skip_pages, (int)sector); } offset = sector_start + skip_pages * COFFEE_PAGE_SIZE; end = (sector + 1) * COFFEE_SECTOR_SIZE; while(offset < end) { COFFEE_READ(&hdr, sizeof(hdr), offset); if(COFFEE_PAGE_ACTIVE(hdr)) { last_pages_are_active = 1; offset += hdr.max_pages * COFFEE_PAGE_SIZE; *active += hdr.max_pages; } else if(COFFEE_PAGE_ISOLATED(hdr)) { last_pages_are_active = 0; offset += COFFEE_PAGE_SIZE; *obsolete++; } else if(COFFEE_PAGE_OBSOLETE(hdr)) { last_pages_are_active = 0; offset += hdr.max_pages * COFFEE_PAGE_SIZE; *obsolete += hdr.max_pages; } else if(COFFEE_PAGE_FREE(hdr)) { *free = (end - offset) / COFFEE_PAGE_SIZE; break; } } skip_pages = *active + *obsolete - 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; } } }