00001
00040 #include "battfs.h"
00041
00042 #include <cfg/debug.h>
00043 #include <cfg/macros.h>
00044 #include <mware/byteorder.h>
00045
00046
00047 #include <string.h>
00048
00049
00054 INLINE void battfs_to_disk(struct BattFsPageHeader *hdr, uint8_t *buf)
00055 {
00056 STATIC_ASSERT(BATTFS_HEADER_LEN == 12);
00057 buf[0] = hdr->inode;
00058
00059 buf[1] = hdr->fill;
00060 buf[2] = hdr->fill >> 8;
00061
00062 buf[3] = hdr->pgoff;
00063 buf[4] = hdr->pgoff >> 8;
00064
00065
00066
00067
00068
00069 buf[5] = hdr->mark;
00070 buf[6] = hdr->mark >> 8;
00071
00072
00073
00074
00075
00076
00077
00078 buf[7] = ((hdr->mark >> 16) & 0x01) | (hdr->seq << 6) | ~(BV(7) | BV(6) | BV(0));
00079
00080
00081
00082
00083 buf[8] = hdr->fcs_free;
00084 buf[9] = hdr->fcs_free >> 8;
00085
00086
00087
00088
00089
00090
00091 buf[10] = hdr->fcs;
00092 buf[11] = hdr->fcs >> 8;
00093 }
00094
00099 INLINE void disk_to_battfs(uint8_t *buf, struct BattFsPageHeader *hdr)
00100 {
00101 STATIC_ASSERT(BATTFS_HEADER_LEN == 12);
00102 hdr->inode = buf[0];
00103 hdr->fill = buf[2] << 8 | buf[1];
00104 hdr->pgoff = buf[4] << 8 | buf[3];
00105 hdr->mark = (mark_t)(buf[7] & 0x01) << 16 | buf[6] << 8 | buf[5];
00106 hdr->seq = buf[7] >> 6;
00107 hdr->fcs_free = buf[9] << 8 | buf[8];
00108 hdr->fcs = buf[11] << 8 | buf[10];
00109 }
00110
00114 static fcs_t computeFcs(struct BattFsPageHeader *hdr)
00115 {
00116 uint8_t buf[BATTFS_HEADER_LEN];
00117 fcs_t cks;
00118
00119 battfs_to_disk(hdr, buf);
00120 rotating_init(&cks);
00121
00122 rotating_update(buf, BATTFS_HEADER_LEN - sizeof(fcs_t), &cks);
00123 return cks;
00124 }
00125
00129 static fcs_t computeFcsFree(struct BattFsPageHeader *hdr)
00130 {
00131 uint8_t buf[BATTFS_HEADER_LEN];
00132 fcs_t cks;
00133
00134 battfs_to_disk(hdr, buf);
00135 rotating_init(&cks);
00136
00137 rotating_update(buf, BATTFS_HEADER_LEN - 2 * sizeof(fcs_t), &cks);
00138 return cks;
00139 }
00140
00141
00147 static bool battfs_readHeader(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr)
00148 {
00149 uint8_t buf[BATTFS_HEADER_LEN];
00150
00151
00152
00153
00154
00155 if (disk->read(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN)
00156 != BATTFS_HEADER_LEN)
00157 {
00158 TRACEMSG("Error: page[%d]\n", page);
00159 return false;
00160 }
00161
00162
00163 disk_to_battfs(buf, hdr);
00164
00165 return true;
00166 }
00167
00173 static bool battfs_writeHeader(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr)
00174 {
00175 uint8_t buf[BATTFS_HEADER_LEN];
00176
00177
00178 battfs_to_disk(hdr, buf);
00179
00180
00181
00182
00183
00184
00185 if (disk->write(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN)
00186 != BATTFS_HEADER_LEN)
00187 {
00188 TRACEMSG("Error: page[%d]\n", page);
00189 return false;
00190 }
00191 return true;
00192 }
00193
00198 static pgcnt_t countPages(pgoff_t *filelen_table, inode_t inode)
00199 {
00200 pgcnt_t cnt = 0;
00201
00202 for (inode_t i = 0; i < inode; i++)
00203 cnt += filelen_table[i];
00204
00205 return cnt;
00206 }
00207
00212 static void movePages(struct BattFsSuper *disk, pgcnt_t src, int offset)
00213 {
00214 pgcnt_t dst = src + offset;
00215 memmove(&disk->page_array[dst], &disk->page_array[src], (disk->page_count - MAX(dst, src)) * sizeof(pgcnt_t));
00216
00217 if (offset < 0)
00218 {
00219
00220 for (pgcnt_t page = disk->page_count + offset; page < disk->page_count; page++)
00221 disk->page_array[page] = PAGE_UNSET_SENTINEL;
00222 }
00223 }
00224
00229 static void insertFreePage(struct BattFsSuper *disk, mark_t mark, pgcnt_t page)
00230 {
00231 ASSERT(mark - disk->free_start < disk->free_next - disk->free_start);
00232
00233 pgcnt_t free_pos = disk->page_count - disk->free_next + mark;
00234 ASSERT(free_pos < disk->page_count);
00235
00236 TRACEMSG("mark:%u, page:%u, free_start:%u, free_next:%u, free_pos:%u\n",
00237 mark, page, disk->free_start, disk->free_next, free_pos);
00238
00239 ASSERT(disk->page_array[free_pos] == PAGE_UNSET_SENTINEL);
00240 disk->page_array[free_pos] = page;
00241 }
00242
00248 static bool battfs_markFree(struct BattFsSuper *disk, struct BattFsPageHeader *hdr, pgcnt_t page)
00249 {
00250 uint8_t buf[BATTFS_HEADER_LEN];
00251
00252 hdr->mark = disk->free_next;
00253 hdr->fcs_free = computeFcsFree(hdr);
00254 battfs_to_disk(hdr, buf);
00255
00256 if (!disk->write(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN))
00257 {
00258 TRACEMSG("error marking page [%d]\n", page);
00259 return false;
00260 }
00261 else
00262 {
00263 disk->free_next++;
00264 return true;
00265 }
00266 }
00267
00292 static void findFreeStartNext(struct BattFsSuper *disk, mark_t minl, mark_t maxl, mark_t minh, mark_t maxh)
00293 {
00294
00295 if (maxl >= minl)
00296 {
00297
00298 if (maxh >= minh)
00299 {
00300
00301 if (maxl == minh - 1)
00302 {
00303
00304 disk->free_start = minl;
00305 disk->free_next = maxh;
00306 }
00307 else
00308 {
00309
00310 ASSERT(minl == 0);
00311 ASSERT(maxh == (MAX_PAGE_ADDR | MARK_HALF_SIZE));
00312
00313 disk->free_start = minh;
00314 disk->free_next = maxl;
00315 }
00316 }
00317 else
00318 {
00319
00320
00321
00322
00323
00324 disk->free_start = minl;
00325 disk->free_next = maxl;
00326 }
00327 }
00328 else if (maxh >= minh)
00329 {
00330
00331
00332
00333
00334 disk->free_start = minh;
00335 disk->free_next = maxh;
00336 }
00337 else
00338 {
00339
00340
00341
00342
00343 TRACEMSG("No valid marked free block found, new disk or disk full\n");
00344 disk->free_start = 0;
00345 disk->free_next = -1;
00346 }
00347
00348
00349 disk->free_next++;
00350
00351 TRACEMSG("Free markers:\n minl %u\n maxl %u\n minh %u\n maxh %u\n free_start %u\n free_next %u\n",
00352 minl, maxl, minh, maxh, disk->free_start, disk->free_next);
00353 }
00354
00364 static bool countDiskFilePages(struct BattFsSuper *disk, pgoff_t *filelen_table)
00365 {
00366 BattFsPageHeader hdr;
00367 mark_t minl, maxl, minh, maxh;
00368
00369
00370 minl = MAX_PAGE_ADDR;
00371 maxl = 0;
00372 minh = MAX_PAGE_ADDR | MARK_HALF_SIZE;
00373 maxh = 0 | MARK_HALF_SIZE;
00374
00375
00376
00377 for (pgcnt_t page = 0; page < disk->page_count; page++)
00378 {
00379 if (!battfs_readHeader(disk, page, &hdr))
00380 return false;
00381
00382
00383 if (hdr.fcs == computeFcs(&hdr))
00384 {
00385 ASSERT(hdr.mark == MARK_PAGE_VALID);
00386 ASSERT(hdr.fcs_free == FCS_FREE_VALID);
00387 ASSERT(hdr.fill <= disk->page_size - BATTFS_HEADER_LEN);
00388
00389
00390 filelen_table[hdr.inode]++;
00391
00392
00393 disk->free_bytes += disk->page_size - BATTFS_HEADER_LEN - hdr.fill;
00394 }
00395 else
00396 {
00397
00398 disk->free_bytes += disk->page_size - BATTFS_HEADER_LEN;
00399
00400
00401 if (hdr.fcs_free == computeFcsFree(&hdr))
00402 {
00403
00404
00405
00406
00407 if (hdr.mark < MARK_HALF_SIZE)
00408 {
00409 minl = MIN(minl, hdr.mark);
00410 maxl = MAX(maxl, hdr.mark);
00411 }
00412 else
00413 {
00414 minh = MIN(minh, hdr.mark);
00415 maxh = MAX(maxh, hdr.mark);
00416 }
00417 }
00418 else
00419 TRACEMSG("page [%d] invalid, keeping as free\n", page);
00420 }
00421 }
00422 findFreeStartNext(disk, minl, maxl, minh, maxh);
00423 return true;
00424 }
00425
00441 static bool fillPageArray(struct BattFsSuper *disk, pgoff_t *filelen_table)
00442 {
00443 BattFsPageHeader hdr;
00444
00445 for (pgcnt_t page = 0; page < disk->page_count; page++)
00446 {
00447 if (!battfs_readHeader(disk, page, &hdr))
00448 return false;
00449
00450
00451 if (hdr.fcs == computeFcs(&hdr))
00452 {
00453
00454 ASSERT(hdr.mark == MARK_PAGE_VALID);
00455 ASSERT(hdr.fcs_free == FCS_FREE_VALID);
00456
00457
00458 pgcnt_t array_pos = countPages(filelen_table, hdr.inode);
00459 array_pos += hdr.pgoff;
00460
00461
00462 if (LIKELY(disk->page_array[array_pos] == PAGE_UNSET_SENTINEL))
00463 disk->page_array[array_pos] = page;
00464 else
00465 {
00466 BattFsPageHeader hdr_old;
00467
00468 if (!battfs_readHeader(disk, disk->page_array[array_pos], &hdr_old))
00469 return false;
00470
00471
00472 ASSERT(hdr_old.fcs == computeFcs(&hdr_old));
00473
00474
00475 ASSERT(hdr.inode == hdr_old.inode);
00476 ASSERT(hdr.pgoff == hdr_old.pgoff);
00477 ASSERT(hdr.mark == hdr_old.mark);
00478 ASSERT(hdr.fcs_free == hdr_old.fcs_free);
00479 ASSERT(hdr.seq != hdr_old.seq);
00480
00481 pgcnt_t new_page, old_page;
00482 fill_t old_fill;
00483
00484
00485 if (((hdr.seq - hdr_old.seq) & 0x03) < 2)
00486 {
00487
00488 old_page = disk->page_array[array_pos];
00489 new_page = page;
00490 old_fill = hdr_old.fill;
00491 }
00492 else
00493 {
00494
00495 old_page = page;
00496 new_page = disk->page_array[array_pos];
00497 old_fill = hdr.fill;
00498 }
00499
00500
00501 disk->page_array[array_pos] = new_page;
00502
00503
00504 disk->free_bytes += old_fill;
00505
00506
00507 array_pos -= hdr.pgoff;
00508 array_pos += filelen_table[hdr.inode];
00509 movePages(disk, array_pos, -1);
00510
00511
00512 filelen_table[hdr.inode]--;
00513
00514
00515 if (!battfs_markFree(disk, &hdr, old_page))
00516 return false;
00517
00518 insertFreePage(disk, hdr.mark, old_page);
00519 }
00520 }
00521 else
00522 {
00523
00524 if (hdr.fcs_free != computeFcsFree(&hdr))
00525
00526 hdr.mark = --disk->free_start;
00527
00528 insertFreePage(disk, hdr.mark, page);
00529 }
00530 }
00531 return true;
00532 }
00533
00539 bool battfs_init(struct BattFsSuper *disk)
00540 {
00541 pgoff_t filelen_table[BATTFS_MAX_FILES];
00542
00543
00544 ASSERT(disk->open);
00545
00546
00547 if (!disk->open(disk))
00548 {
00549 TRACEMSG("open error\n");
00550 return false;
00551 }
00552
00553
00554 ASSERT(disk->read);
00555 ASSERT(disk->write);
00556 ASSERT(disk->erase);
00557 ASSERT(disk->close);
00558 ASSERT(disk->page_size);
00559 ASSERT(disk->page_count);
00560 ASSERT(disk->page_count < PAGE_UNSET_SENTINEL - 1);
00561 ASSERT(disk->page_array);
00562
00563 memset(filelen_table, 0, BATTFS_MAX_FILES * sizeof(pgoff_t));
00564
00565 disk->free_bytes = 0;
00566 disk->disk_size = (disk_size_t)(disk->page_size - BATTFS_HEADER_LEN) * disk->page_count;
00567
00568
00569 if (!countDiskFilePages(disk, filelen_table))
00570 {
00571 TRACEMSG("error counting file pages\n");
00572 return false;
00573 }
00574
00575
00576
00577
00578 for (pgcnt_t page = 0; page < disk->page_count; page++)
00579 disk->page_array[page] = PAGE_UNSET_SENTINEL;
00580
00581
00582 if (!fillPageArray(disk, filelen_table))
00583 {
00584 TRACEMSG("error filling page array\n");
00585 return false;
00586 }
00587
00588
00589 LIST_INIT(&disk->file_opened_list);
00590 return true;
00591 }
00592
00597 static int battfs_flush(struct KFile *fd)
00598 {
00599 (void)fd;
00600 #warning TODO
00601 return 0;
00602 }
00603
00608 static int battfs_fileclose(struct KFile *fd)
00609 {
00610 KFileBattFs *fdb = KFILEBATTFS(fd);
00611
00612 battfs_flush(fd);
00613 REMOVE(&fdb->link);
00614 return 0;
00615 }
00616
00621 static size_t battfs_read(struct KFile *fd, void *_buf, size_t size)
00622 {
00623 KFileBattFs *fdb = KFILEBATTFS(fd);
00624 uint8_t *buf = (uint8_t *)_buf;
00625
00626 size_t total_read = 0;
00627 pgoff_t pg_offset;
00628 pgaddr_t addr_offset;
00629 pgaddr_t read_len;
00630
00631 size = MIN(size, fd->size - fd->seek_pos);
00632
00633 while (size)
00634 {
00635 pg_offset = fd->seek_pos / (fdb->disk->page_size - BATTFS_HEADER_LEN);
00636 addr_offset = fd->seek_pos % (fdb->disk->page_size - BATTFS_HEADER_LEN);
00637 read_len = MIN(size, (size_t)(fdb->disk->page_size - BATTFS_HEADER_LEN - addr_offset));
00638
00639
00640 if (fdb->disk->read(fdb->disk, fdb->start[pg_offset], addr_offset, buf, read_len) != read_len)
00641 {
00642 #warning TODO set error?
00643 }
00644
00645 size -= read_len;
00646 fd->seek_pos += read_len;
00647 total_read += read_len;
00648 buf += read_len;
00649 }
00650 return total_read;
00651 }
00652
00653
00659 static pgcnt_t *findFile(BattFsSuper *disk, inode_t inode)
00660 {
00661 BattFsPageHeader hdr;
00662 pgcnt_t first = 0, page, last = disk->page_count -1;
00663 fcs_t fcs;
00664
00665 while (first <= last)
00666 {
00667 page = (first + last) / 2;
00668
00669 if (!battfs_readHeader(disk, disk->page_array[page], &hdr))
00670 return NULL;
00671
00672 fcs = computeFcs(&hdr);
00673 if (hdr.fcs == fcs && hdr.inode == inode)
00674 return (&disk->page_array[page]) - hdr.pgoff;
00675 else if (hdr.fcs == fcs && hdr.inode < inode)
00676 first = page + 1;
00677 else
00678 last = page - 1;
00679 }
00680
00681 return NULL;
00682 }
00683
00687 bool battfs_fileExists(BattFsSuper *disk, inode_t inode)
00688 {
00689 return findFile(disk, inode) != NULL;
00690 }
00691
00697 static bool countFileSize(BattFsSuper *disk, pgcnt_t *start, inode_t inode, file_size_t *size)
00698 {
00699 *size = 0;
00700 BattFsPageHeader hdr;
00701
00702 for (;;)
00703 {
00704 if (!battfs_readHeader(disk, *start++, &hdr))
00705 return false;
00706 if (hdr.fcs == computeFcs(&hdr) && hdr.inode == inode)
00707 *size += hdr.fill;
00708 else
00709 return true;
00710 }
00711 }
00712
00718 bool battfs_fileopen(BattFsSuper *disk, KFileBattFs *fd, inode_t inode, filemode_t mode)
00719 {
00720 Node *n;
00721
00722 memset(fd, 0, sizeof(*fd));
00723
00724
00725 fd->start = findFile(disk, inode);
00726 if (fd->start == NULL)
00727 {
00728 if (!(mode & BATTFS_CREATE))
00729 return false;
00730
00731
00732 BattFsPageHeader hdr;
00733 hdr.inode = inode;
00734 hdr.seq = 0;
00735 hdr.fill = 0;
00736 hdr.pgoff = 0;
00737 hdr.mark = MARK_PAGE_VALID;
00738 hdr.fcs_free = FCS_FREE_VALID;
00739 hdr.fcs = computeFcs(&hdr);
00740 #warning TODO: get a free block and write on disk!
00741 }
00742
00743
00744 if (!countFileSize(disk, fd->start, inode, &fd->fd.size))
00745 return false;
00746
00747
00748 fd->fd.seek_pos = 0;
00749
00750
00751 FOREACH_NODE(n, &disk->file_opened_list)
00752 {
00753 KFileBattFs *file = containerof(n, KFileBattFs, link);
00754 if (file->inode >= inode)
00755 break;
00756 }
00757 INSERT_BEFORE(&fd->link, n);
00758
00759
00760 fd->inode = inode;
00761 fd->mode = mode;
00762 fd->disk = disk;
00763
00764 fd->fd.close = battfs_fileclose;
00765 fd->fd.flush = battfs_flush;
00766 fd->fd.read = battfs_read;
00767 fd->fd.reopen = kfile_genericReopen;
00768 fd->fd.seek = kfile_genericSeek;
00769
00770 #warning TODO battfs_write, battfs_error, battfs_clearerr
00771 #if 0
00772 fd->fd.write = battfs_write;
00773 fd->fd.error = battfs_error;
00774 fd->fd.clearerr = battfs_clearerr;
00775 #endif
00776
00777 DB(fd->fd._type = KFT_BATTFS);
00778
00779 return true;
00780 }
00781
00785 bool battfs_close(struct BattFsSuper *disk)
00786 {
00787 Node *n;
00788 int res = 0;
00789
00790
00791 FOREACH_NODE(n, &disk->file_opened_list)
00792 {
00793 KFileBattFs *file = containerof(n, KFileBattFs, link);
00794 res += battfs_fileclose(&file->fd);
00795 }
00796
00797
00798 return disk->close(disk) && (res == 0);
00799 }
00800
00801
00802 bool battfs_writeTestBlock(struct BattFsSuper *disk, pgcnt_t page, inode_t inode, seq_t seq, fill_t fill, pgoff_t pgoff, mark_t mark)
00803 {
00804 BattFsPageHeader hdr;
00805
00806 hdr.inode = inode;
00807 hdr.seq = seq;
00808 hdr.fill = fill;
00809 hdr.pgoff = pgoff;
00810 hdr.mark = MARK_PAGE_VALID;
00811 hdr.fcs_free = FCS_FREE_VALID;
00812 hdr.fcs = computeFcs(&hdr);
00813 if (mark != MARK_PAGE_VALID)
00814 {
00815 hdr.mark = mark;
00816 hdr.fcs_free = computeFcsFree(&hdr);
00817 }
00818
00819 if (!battfs_writeHeader(disk, page, &hdr))
00820 {
00821 TRACEMSG("error writing hdr\n");
00822 return false;
00823 }
00824
00825 return true;
00826 }