X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Ffs%2Fbattfs.c;h=9c72f74928c024bf331df8bde3d5118ba3d81482;hb=8c60fefb5bbf501b3930bd40c9187b37798d7909;hp=73e3536af56b7f7d026cfd68fa6afbaeb282f2e7;hpb=54cdb63007e4a3dac8cd50ecdb9e601e0302e62b;p=bertos.git diff --git a/bertos/fs/battfs.c b/bertos/fs/battfs.c index 73e3536a..9c72f749 100644 --- a/bertos/fs/battfs.c +++ b/bertos/fs/battfs.c @@ -156,8 +156,8 @@ static bool battfs_writeHeader(struct BattFsSuper *disk, pgcnt_t page, struct Ba * Header is actually a footer, and so * resides at page end. */ - if (disk->write(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN) - != BATTFS_HEADER_LEN) + if (!(disk->bufferWrite(disk, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN) + == BATTFS_HEADER_LEN && disk->save(disk, page))) { LOG_ERR("Error: page[%d]\n", page); return false; @@ -246,11 +246,10 @@ static bool countDiskFilePages(struct BattFsSuper *disk, pgoff_t *filelen_table) * inside file. * e.g. : at page array[0] you will find page address of the first page * of the first file (if present). - * Free blocks are allocated after the last file, starting from invalid ones - * and continuing with the marked free ones. + * Free blocks are allocated after the last file. * * \return true if ok, false on disk read errors. - * \note The whole disk is scanned once. + * \note The whole disk is scanned at max twice. */ static bool fillPageArray(struct BattFsSuper *disk, pgoff_t *filelen_table) { @@ -343,222 +342,48 @@ static bool fillPageArray(struct BattFsSuper *disk, pgoff_t *filelen_table) return true; } -#if 0 -/** - * Find the latest version of a page, starting from the - * page supplied by \a page_array. - * The pages are read from the disk until a different - * inode or page offset is found. - * The lastest version of the page is moved in the first - * position of \a page_array. - * \return the number of old versions of the page or PAGE_ERROR - * on disk read errors. - */ -static pgcnt_t findLastVersion(struct BattFsSuper *disk, pgcnt_t *page_array) -{ - pgcnt_t *array_start = page_array; - BattFsPageHeader hdr; - if (!battfs_readHeader(disk, *page_array++, &hdr)) - return PAGE_ERROR; - - /* Free space: early bailout */ - if (hdr.fcs != computeFcs(&hdr)) - return 0; - - /* - * If the first page is valid, - * inode and pg_off in the array are taken - * as the current page markers. - */ - inode_t curr_inode = hdr.inode; - pgoff_t curr_pgoff = hdr.pgoff; - - /* Temps used to find the sequence number range */ - seq_t minl = HALF_SEQ - 1; - seq_t maxl = 0; - seq_t minh = MAX_SEQ; - seq_t maxh = MAX_SEQ; - pgcnt_t lpos = 0, hpos = 0, dup_cnt = 0; - - /* - * Find min and max values for the two - * half of seq_num range. - * With this we can find seqnum wraparounds. - * seq_t is a type that has at least 1 bit more than - * pgaddr_t. So all version of a page blocks can be numbered using - * at most half numbers of a seq_t type. - * The sequence number algorithm increments by 1 the previous seq_num - * every time a page is rewritten. So the sequence is - * guaranteed to be countiguous. - * Only wrap arounds may happen, but due to half size sequence limitation, - * there are only 4 possible situations: - * - * \verbatim - * |------lower half------|-------upper half-------| - * - * 1) |------minl*****maxl---|------------------------| - * 2) |------minl********maxl|minh******maxh----------| - * 3) |----------------------|----minh*******maxh-----| - * 4) |minl******maxl--------|------------minh****maxh| - * \endverbatim - * - * Situations 1 and 3 are easy to detect, while 2 and 4 require more care. - */ - do - { - if (hdr.seq < HALF_SEQ) - { - minl = MIN(minl, hdr.seq); - if (hdr.seq > maxl) - { - maxl = hdr.seq; - lpos = dup_cnt; - } - } - else - { - minh = MIN(minh, hdr.seq); - if (hdr.seq > maxh) - { - maxh = hdr.seq; - hpos = dup_cnt; - } - } - - if (!battfs_readHeader(disk, *page_array++, &hdr)) - return PAGE_ERROR; - dup_cnt++; - } - while (curr_inode == hdr.inode && curr_pgoff == hdr.pgoff && hdr.fcs == computeFcs(&hdr)); - - - /* Return early if there is only one version of the current page */ - if (dup_cnt == 1) - return 0; - - /* Find the position in the array of the last version of the page */ - pgcnt_t last_ver = hpos; - if (maxl >= minl) - { - /* Valid interval found in lower half */ - if (maxh >= minh) - { - /* Valid interval also found in upper half */ - if (maxl != minh - 1) - { - /* Interval starts in upper half and ends in lower */ - ASSERT(minl == 0); - ASSERT(maxh == MAX_SEQ); - - last_ver = lpos; - } - } - else - /* - * Upper interval is invalid. - * Use lower values. - */ - last_ver = lpos; - } - - /* Put last page version at array start position */ - SWAP(array_start[0], array_start[last_ver]); - - return dup_cnt - 1; -} /** - * Collect old pages, removing empty spaces from \a pg_array, for a maximum len of \a pg_len. - * Once the collect task is completed, copy \a old_cnt pages from \a old_pages at the - * end of free space in pg_array. + * Flush the current \a disk buffer. + * \return true if ok, false on errors. */ -void collectOldPages(pgcnt_t *pg_array, pgcnt_t pg_len, pgcnt_t *old_pages, pgcnt_t old_cnt) +static bool battfs_flushBuffer(struct BattFsSuper *disk) { - bool copy = false; - pgcnt_t gap = 0; - - for (pgcnt_t curr_page = 0; curr_page < pg_len; curr_page++) + if (disk->cache_dirty) { - if (!copy) - { - if (pg_array[curr_page] == PAGE_UNSET_SENTINEL) - gap++; - else - { - pg_array[curr_page - gap] = pg_array[curr_page]; - copy = true; - } - } - else - { - if (pg_array[curr_page] != PAGE_UNSET_SENTINEL) - pg_array[curr_page - gap] = pg_array[curr_page]; - else - { - gap++; - copy = false; - } - } - } - ASSERT(gap == old_cnt); - pg_array += pg_len - old_cnt; + LOG_INFO("Flushing to disk page %d\n", disk->curr_page); + if (!disk->erase(disk, disk->curr_page)) + return false; - memcpy(pg_array, old_pages, old_cnt * sizeof(pgcnt_t)); + if (!disk->save(disk, disk->curr_page)) + return false; + disk->cache_dirty = false; + } + return true; } /** - * This function scan the page array of \a disk looking for - * old versions of the same page. - * - * Only the last version is kept as valid, the old ones are inserted - * in the free blocks heap. - * \return true if ok, false on disk read errors. - * \note The whole disk is scanned once. + * Load \a new_page from \a disk in disk page buffer. + * If a previuos page is still dirty in the buffer, will be + * flushed first. + * \return true if ok, false on errors. */ -static bool dropOldPages(struct BattFsSuper *disk) +static bool battfs_loadPage(struct BattFsSuper *disk, pgcnt_t new_page) { - #define OLD_PAGE_BUFLEN 64 - pgcnt_t old_pages[OLD_PAGE_BUFLEN]; - pgcnt_t old_cnt = 0; - - pgcnt_t *curr_page = disk->page_array; - pgcnt_t *collect_start = disk->page_array; - pgcnt_t collect_len = disk->page_count; - pgcnt_t dup_pages; - - do - { - dup_pages = findLastVersion(disk, curr_page); - if (dup_pages == PAGE_ERROR) - return false; - /* The first page is the last version */ - curr_page++; - while (dup_pages--) - { - if (old_cnt >= OLD_PAGE_BUFLEN) - { - collectOldPages(collect_start, collect_len, old_pages, old_cnt); - collect_len -= old_cnt; - disk->free_bytes += old_cnt * (disk->page_size - BATTFS_HEADER_LEN); - disk->free_page_start -= old_cnt; - curr_page -= old_cnt; - collect_start = curr_page; - old_cnt = 0; - } + LOG_INFO("Loading page %d\n", new_page); + if (disk->curr_page == new_page) + return true; - old_pages[old_cnt++] = *curr_page; - *curr_page++ = PAGE_UNSET_SENTINEL; - } - } - while (curr_page < disk->page_array + disk->free_page_start); + if (!battfs_flushBuffer(disk)) + return false; - collectOldPages(collect_start, collect_len, old_pages, old_cnt); - disk->free_bytes += old_cnt * (disk->page_size - BATTFS_HEADER_LEN); - disk->free_page_start -= old_cnt; + if (!disk->load(disk, new_page)) + return false; + disk->curr_page = new_page; return true; } -#endif + /** * Initialize and mount disk described by @@ -581,7 +406,9 @@ bool battfs_init(struct BattFsSuper *disk) /* Disk open must set all of these */ ASSERT(disk->read); - ASSERT(disk->write); + ASSERT(disk->load); + ASSERT(disk->bufferWrite); + ASSERT(disk->save); ASSERT(disk->erase); ASSERT(disk->close); ASSERT(disk->page_size); @@ -613,6 +440,12 @@ bool battfs_init(struct BattFsSuper *disk) LOG_ERR("error filling page array\n"); return false; } + #warning TODO: shuffle free blocks + + /* Initialize page buffer cache */ + disk->cache_dirty = false; + disk->curr_page = 0; + disk->load(disk, disk->curr_page); /* Init list for opened files. */ LIST_INIT(&disk->file_opened_list); @@ -625,9 +458,12 @@ bool battfs_init(struct BattFsSuper *disk) */ static int battfs_flush(struct KFile *fd) { - (void)fd; - #warning TODO - return 0; + BattFs *fdb = BATTFS_CAST(fd); + + if (battfs_flushBuffer(fdb->disk)) + return 0; + else + return EOF; } /** @@ -643,6 +479,69 @@ static int battfs_fileclose(struct KFile *fd) return 0; } + +/** + * Write to file \a fd \a size bytes from \a buf. + * \return The number of bytes written. + */ +static size_t battfs_write(struct KFile *fd, const void *_buf, size_t size) +{ + BattFs *fdb = BATTFS_CAST(fd); + const uint8_t *buf = (const uint8_t *)_buf; + + size_t total_write = 0; + pgoff_t pg_offset; + pgaddr_t addr_offset; + pgaddr_t wr_len; + + size = MIN((kfile_off_t)size, fd->size - fd->seek_pos); + + while (size) + { + #warning TODO: outside EOF? + + pg_offset = fd->seek_pos / (fdb->disk->page_size - BATTFS_HEADER_LEN); + addr_offset = fd->seek_pos % (fdb->disk->page_size - BATTFS_HEADER_LEN); + wr_len = MIN(size, (size_t)(fdb->disk->page_size - BATTFS_HEADER_LEN - addr_offset)); + + + if (fdb->start[pg_offset] != fdb->disk->curr_page) + { + if (!battfs_loadPage(fdb->disk, fdb->start[pg_offset])) + { + #warning TODO set error? + return total_write; + } + + /* Get a free page */ + fdb->disk->curr_page = fdb->disk->page_array[fdb->disk->free_page_start]; + movePages(fdb->disk, fdb->disk->free_page_start + 1, -1); + + /* Insert previous page in free blocks list */ + fdb->disk->page_array[fdb->disk->page_count - 1] = fdb->start[pg_offset]; + /* Assign new page */ + fdb->start[pg_offset] = fdb->disk->curr_page; + #warning TODO: hdr have to be updated! + } + + + if (fdb->disk->bufferWrite(fdb->disk, addr_offset, buf, wr_len) != wr_len) + { + #warning TODO set error? + } + fdb->disk->cache_dirty = true; + + size -= wr_len; + fd->seek_pos += wr_len; + total_write += wr_len; + buf += wr_len; + #warning TODO: hdr have to be updated! + } + return total_write; + +} + + /** * Read from file \a fd \a size bytes in \a buf. * \return The number of bytes read. @@ -665,6 +564,10 @@ static size_t battfs_read(struct KFile *fd, void *_buf, size_t size) addr_offset = fd->seek_pos % (fdb->disk->page_size - BATTFS_HEADER_LEN); read_len = MIN(size, (size_t)(fdb->disk->page_size - BATTFS_HEADER_LEN - addr_offset)); + /* Flush current page if needed */ + if (fdb->start[pg_offset] == fdb->disk->curr_page) + battfs_flushBuffer(fdb->disk); + /* Read from disk */ if (fdb->disk->read(fdb->disk, fdb->start[pg_offset], addr_offset, buf, read_len) != read_len) { @@ -688,10 +591,10 @@ static size_t battfs_read(struct KFile *fd, void *_buf, size_t size) static pgcnt_t *findFile(BattFsSuper *disk, inode_t inode) { BattFsPageHeader hdr; - pgcnt_t first = 0, page, last = disk->page_count -1; + pgcnt_t first = 0, page, last = disk->free_page_start; fcs_t fcs; - while (first <= last) + while (first < last) { page = (first + last) / 2; @@ -723,19 +626,19 @@ bool battfs_fileExists(BattFsSuper *disk, inode_t inode) * in disk->page_array. Size is written in \a size. * \return true if all s ok, false on disk read errors. */ -static bool countFileSize(BattFsSuper *disk, pgcnt_t *start, inode_t inode, file_size_t *size) +static file_size_t countFileSize(BattFsSuper *disk, pgcnt_t *start, inode_t inode) { - *size = 0; + file_size_t size = 0; BattFsPageHeader hdr; for (;;) { if (!battfs_readHeader(disk, *start++, &hdr)) - return false; + return EOF; if (hdr.fcs == computeFcs(&hdr) && hdr.inode == inode) - *size += hdr.fill; + size += hdr.fill; else - return true; + return size; } } @@ -768,7 +671,7 @@ bool battfs_fileopen(BattFsSuper *disk, BattFs *fd, inode_t inode, filemode_t mo } /* Fill file size */ - if (!countFileSize(disk, fd->start, inode, &fd->fd.size)) + if ((fd->fd.size = countFileSize(disk, fd->start, inode)) == EOF) return false; /* Reset seek position */ @@ -793,10 +696,10 @@ bool battfs_fileopen(BattFsSuper *disk, BattFs *fd, inode_t inode, filemode_t mo fd->fd.read = battfs_read; fd->fd.reopen = kfile_genericReopen; fd->fd.seek = kfile_genericSeek; + fd->fd.write = battfs_write; -#warning TODO battfs_write, battfs_error, battfs_clearerr +#warning TODO battfs_error, battfs_clearerr #if 0 - fd->fd.write = battfs_write; fd->fd.error = battfs_error; fd->fd.clearerr = battfs_clearerr; #endif @@ -825,10 +728,16 @@ bool battfs_close(struct BattFsSuper *disk) return disk->close(disk) && (res == 0); } +#if UNIT_TEST bool battfs_writeTestBlock(struct BattFsSuper *disk, pgcnt_t page, inode_t inode, seq_t seq, fill_t fill, pgoff_t pgoff) { BattFsPageHeader hdr; + /* Reset page to all 0xff */ + uint8_t buf[disk->page_size]; + memset(buf, 0xFF, disk->page_size); + disk->bufferWrite(disk, 0, buf, disk->page_size); + hdr.inode = inode; hdr.fill = fill; hdr.pgoff = pgoff; @@ -843,3 +752,4 @@ bool battfs_writeTestBlock(struct BattFsSuper *disk, pgcnt_t page, inode_t inode return true; } +#endif