BeRTOS
|
00001 00040 #include "battfs.h" 00041 #include "cfg/cfg_battfs.h" 00042 #include <cfg/debug.h> 00043 #include <cfg/macros.h> /* MIN, MAX */ 00044 #include <cfg/test.h> 00045 #include <cpu/byteorder.h> /* cpu_to_xx */ 00046 00047 #define LOG_LEVEL BATTFS_LOG_LEVEL 00048 #define LOG_FORMAT BATTFS_LOG_FORMAT 00049 #include <cfg/log.h> 00050 00051 #include <string.h> /* memset, memmove */ 00052 00053 #if LOG_LEVEL >= LOG_LVL_INFO 00054 static void dumpPageArray(struct BattFsSuper *disk) 00055 { 00056 kprintf("Page array dump, free_page_start %d:", disk->free_page_start); 00057 for (pgcnt_t i = 0; i < disk->dev->blk_cnt; i++) 00058 { 00059 if (!(i % 16)) 00060 kputchar('\n'); 00061 kprintf("%04d ", disk->page_array[i]); 00062 } 00063 kputchar('\n'); 00064 } 00065 #endif 00066 00071 INLINE void battfs_to_disk(struct BattFsPageHeader *hdr, uint8_t *buf) 00072 { 00073 STATIC_ASSERT(BATTFS_HEADER_LEN == 12); 00074 buf[0] = hdr->inode; 00075 00076 buf[1] = hdr->fill; 00077 buf[2] = hdr->fill >> 8; 00078 00079 buf[3] = hdr->pgoff; 00080 buf[4] = hdr->pgoff >> 8; 00081 00082 /* 00083 * Sequence number is 40 bits long. 00084 * No need to take care of wraparonds: the memory will die first! 00085 */ 00086 buf[5] = hdr->seq; 00087 buf[6] = hdr->seq >> 8; 00088 buf[7] = hdr->seq >> 16; 00089 buf[8] = hdr->seq >> 24; 00090 buf[9] = hdr->seq >> 32; 00091 00092 /* 00093 * This field must be the last one! 00094 * This is needed because if the page is only partially 00095 * written, we can use this to detect it. 00096 */ 00097 buf[10] = hdr->fcs; 00098 buf[11] = hdr->fcs >> 8; 00099 } 00100 00105 INLINE void disk_to_battfs(uint8_t *buf, struct BattFsPageHeader *hdr) 00106 { 00107 STATIC_ASSERT(BATTFS_HEADER_LEN == 12); 00108 hdr->inode = buf[0]; 00109 hdr->fill = buf[2] << 8 | buf[1]; 00110 hdr->pgoff = buf[4] << 8 | buf[3]; 00111 hdr->seq = (seq_t)buf[9] << 32 | (seq_t)buf[8] << 24 | (seq_t)buf[7] << 16 | buf[6] << 8 | buf[5]; 00112 hdr->fcs = buf[11] << 8 | buf[10]; 00113 } 00114 00118 static fcs_t computeFcs(struct BattFsPageHeader *hdr) 00119 { 00120 uint8_t buf[BATTFS_HEADER_LEN]; 00121 fcs_t cks; 00122 00123 battfs_to_disk(hdr, buf); 00124 rotating_init(&cks); 00125 /* fcs is at the end of whole header */ 00126 rotating_update(buf, BATTFS_HEADER_LEN - sizeof(fcs_t), &cks); 00127 return cks; 00128 } 00129 00134 static bool readHdr(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr) 00135 { 00136 uint8_t buf[BATTFS_HEADER_LEN]; 00137 00138 /* 00139 * Read header from disk. 00140 * Header is actually a footer, and so 00141 * resides at page end. 00142 */ 00143 if (kblock_read(disk->dev, page, buf, disk->data_size, BATTFS_HEADER_LEN) 00144 != BATTFS_HEADER_LEN) 00145 { 00146 LOG_ERR("page[%d]\n", page); 00147 return false; 00148 } 00149 00150 /* Fill header */ 00151 disk_to_battfs(buf, hdr); 00152 00153 return true; 00154 } 00155 00156 static bool writeHdr(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr) 00157 { 00158 uint8_t buf[BATTFS_HEADER_LEN]; 00159 00160 #warning FIXME:refactor computeFcs to save time and stack 00161 hdr->fcs = computeFcs(hdr); 00162 /* Fill buffer */ 00163 battfs_to_disk(hdr, buf); 00164 00165 /* 00166 * write header to disk. 00167 * Header is actually a footer, and so 00168 * resides at page end. 00169 */ 00170 if (kblock_write(disk->dev, page, buf, disk->data_size, BATTFS_HEADER_LEN) 00171 != BATTFS_HEADER_LEN) 00172 { 00173 LOG_ERR("writing to buffer\n"); 00174 return false; 00175 } 00176 return true; 00177 } 00178 00179 00184 static pgcnt_t countPages(pgoff_t *filelen_table, inode_t inode) 00185 { 00186 pgcnt_t cnt = 0; 00187 00188 for (inode_t i = 0; i < inode; i++) 00189 cnt += filelen_table[i]; 00190 00191 return cnt; 00192 } 00193 00198 static void movePages(struct BattFsSuper *disk, pgcnt_t src, int offset) 00199 { 00200 pgcnt_t dst = src + offset; 00201 LOG_INFO("src %d, offset %d, size %d\n", src, offset, (unsigned int)((disk->dev->blk_cnt - MAX(dst, src)) * sizeof(pgcnt_t))); 00202 memmove(&disk->page_array[dst], &disk->page_array[src], (disk->dev->blk_cnt - MAX(dst, src)) * sizeof(pgcnt_t)); 00203 00204 if (offset < 0) 00205 { 00206 /* Fill empty space in array with sentinel */ 00207 for (pgcnt_t page = disk->dev->blk_cnt + offset; page < disk->dev->blk_cnt; page++) 00208 disk->page_array[page] = PAGE_UNSET_SENTINEL; 00209 } 00210 } 00211 00221 static bool countDiskFilePages(struct BattFsSuper *disk, pgoff_t *filelen_table) 00222 { 00223 BattFsPageHeader hdr; 00224 disk->free_page_start = 0; 00225 00226 /* Count the number of disk page per file */ 00227 for (pgcnt_t page = 0; page < disk->dev->blk_cnt; page++) 00228 { 00229 if (!readHdr(disk, page, &hdr)) 00230 return false; 00231 00232 /* Increase free space */ 00233 disk->free_bytes += disk->data_size; 00234 00235 /* Check header FCS */ 00236 if (hdr.fcs == computeFcs(&hdr)) 00237 { 00238 ASSERT(hdr.fill <= disk->data_size); 00239 00240 /* Page is valid and is owned by a file */ 00241 filelen_table[hdr.inode]++; 00242 00243 /* Keep trace of free space */ 00244 disk->free_bytes -= hdr.fill; 00245 disk->free_page_start++; 00246 } 00247 } 00248 LOG_INFO("free_bytes:%ld, free_page_start:%d\n", (long)disk->free_bytes, disk->free_page_start); 00249 00250 return true; 00251 } 00252 00267 static bool fillPageArray(struct BattFsSuper *disk, pgoff_t *filelen_table) 00268 { 00269 BattFsPageHeader hdr; 00270 pgcnt_t curr_free_page = disk->free_page_start; 00271 /* Fill page allocation array */ 00272 for (pgcnt_t page = 0; page < disk->dev->blk_cnt; page++) 00273 { 00274 if (!readHdr(disk, page, &hdr)) 00275 return false; 00276 00277 /* Check header FCS */ 00278 if (hdr.fcs == computeFcs(&hdr)) 00279 { 00280 /* Compute array position */ 00281 pgcnt_t array_pos = countPages(filelen_table, hdr.inode); 00282 array_pos += hdr.pgoff; 00283 00284 00285 /* Check if position is already used by another page of the same file */ 00286 if (disk->page_array[array_pos] == PAGE_UNSET_SENTINEL) 00287 disk->page_array[array_pos] = page; 00288 else 00289 { 00290 BattFsPageHeader hdr_prv; 00291 00292 if (!readHdr(disk, disk->page_array[array_pos], &hdr_prv)) 00293 return false; 00294 00295 /* Check header FCS */ 00296 ASSERT(hdr_prv.fcs == computeFcs(&hdr_prv)); 00297 00298 /* Only the very same page with a different seq number can be here */ 00299 ASSERT(hdr.inode == hdr_prv.inode); 00300 ASSERT(hdr.pgoff == hdr_prv.pgoff); 00301 ASSERT(hdr.seq != hdr_prv.seq); 00302 00303 pgcnt_t new_page, old_page; 00304 fill_t old_fill; 00305 00306 /* 00307 * Sequence number comparison: since 00308 * seq is 40 bits wide, it wraps once 00309 * every 1.1E12 times. 00310 * The memory will not live enough to 00311 * see a wraparound, so we can use a simple 00312 * compare here. 00313 */ 00314 if (hdr.seq > hdr_prv.seq) 00315 { 00316 /* Current header is newer than the previuos one */ 00317 old_page = disk->page_array[array_pos]; 00318 new_page = page; 00319 old_fill = hdr_prv.fill; 00320 } 00321 else 00322 { 00323 /* Previous header is newer than the current one */ 00324 old_page = page; 00325 new_page = disk->page_array[array_pos]; 00326 old_fill = hdr.fill; 00327 } 00328 00329 /* Set new page */ 00330 disk->page_array[array_pos] = new_page; 00331 /* Add free space */ 00332 disk->free_bytes += old_fill; 00333 /* Shift all array one position to the left, overwriting duplicate page */ 00334 array_pos -= hdr.pgoff; 00335 array_pos += filelen_table[hdr.inode]; 00336 movePages(disk, array_pos, -1); 00337 /* Move back all indexes */ 00338 filelen_table[hdr.inode]--; 00339 disk->free_page_start--; 00340 curr_free_page--; 00341 /* Set old page as free */ 00342 ASSERT(disk->page_array[curr_free_page] == PAGE_UNSET_SENTINEL); 00343 disk->page_array[curr_free_page++] = old_page; 00344 00345 } 00346 } 00347 else 00348 { 00349 /* Invalid page, keep as free */ 00350 ASSERT(disk->page_array[curr_free_page] == PAGE_UNSET_SENTINEL); 00351 //LOG_INFO("Page %d invalid, keeping as free\n", page); 00352 disk->page_array[curr_free_page++] = page; 00353 } 00354 } 00355 return true; 00356 } 00357 00358 00364 bool battfs_mount(struct BattFsSuper *disk, struct KBlock *dev, pgcnt_t *page_array, size_t array_size) 00365 { 00366 pgoff_t filelen_table[BATTFS_MAX_FILES]; 00367 00368 ASSERT(dev); 00369 ASSERT(kblock_partialWrite(dev)); 00370 disk->dev = dev; 00371 00372 ASSERT(disk->dev->blk_size > BATTFS_HEADER_LEN); 00373 /* Fill page_size with the usable space */ 00374 disk->data_size = disk->dev->blk_size - BATTFS_HEADER_LEN; 00375 ASSERT(disk->dev->blk_cnt); 00376 ASSERT(disk->dev->blk_cnt < PAGE_UNSET_SENTINEL - 1); 00377 ASSERT(page_array); 00378 disk->page_array = page_array; 00379 ASSERT(array_size >= disk->dev->blk_cnt * sizeof(pgcnt_t)); 00380 00381 memset(filelen_table, 0, BATTFS_MAX_FILES * sizeof(pgoff_t)); 00382 00383 disk->free_bytes = 0; 00384 disk->disk_size = (disk_size_t)disk->data_size * disk->dev->blk_cnt; 00385 00386 /* Count pages per file */ 00387 if (!countDiskFilePages(disk, filelen_table)) 00388 { 00389 LOG_ERR("counting file pages\n"); 00390 return false; 00391 } 00392 00393 /* Once here, we have filelen_table filled with file lengths */ 00394 00395 /* Fill page array with sentinel */ 00396 for (pgcnt_t page = 0; page < disk->dev->blk_cnt; page++) 00397 disk->page_array[page] = PAGE_UNSET_SENTINEL; 00398 00399 /* Fill page allocation array using filelen_table */ 00400 if (!fillPageArray(disk, filelen_table)) 00401 { 00402 LOG_ERR("filling page array\n"); 00403 return false; 00404 } 00405 #if LOG_LEVEL >= LOG_LVL_INFO 00406 dumpPageArray(disk); 00407 #endif 00408 #if CONFIG_BATTFS_SHUFFLE_FREE_PAGES 00409 SHUFFLE(&disk->page_array[disk->free_page_start], disk->dev->blk_cnt - disk->free_page_start); 00410 00411 LOG_INFO("Page array after shuffle:\n"); 00412 #if LOG_LEVEL >= LOG_LVL_INFO 00413 dumpPageArray(disk); 00414 #endif 00415 #endif 00416 /* Init list for opened files. */ 00417 LIST_INIT(&disk->file_opened_list); 00418 return true; 00419 } 00420 00425 bool battfs_fsck(struct BattFsSuper *disk) 00426 { 00427 #define FSCHECK(cond) do { if(!(cond)) { LOG_ERR("\"" #cond "\"\n"); return false; } } while (0) 00428 00429 FSCHECK(disk->free_page_start <= disk->dev->blk_cnt); 00430 FSCHECK(disk->data_size < disk->dev->blk_size); 00431 FSCHECK(disk->free_bytes <= disk->disk_size); 00432 00433 disk_size_t free_bytes = 0; 00434 BattFsPageHeader hdr, prev_hdr; 00435 inode_t files = 0; 00436 pgcnt_t page_used = 0; 00437 00438 bool start = true; 00439 00440 /* Uneeded, the first time will be overwritten but useful to silence 00441 * the warning for uninitialized value */ 00442 FSCHECK(readHdr(disk, 0, &prev_hdr)); 00443 for (pgcnt_t page = 0; page < disk->dev->blk_cnt; page++) 00444 { 00445 FSCHECK(readHdr(disk, disk->page_array[page], &hdr)); 00446 free_bytes += disk->data_size; 00447 00448 if (page < disk->free_page_start) 00449 { 00450 FSCHECK(computeFcs(&hdr) == hdr.fcs); 00451 page_used++; 00452 free_bytes -= hdr.fill; 00453 if (hdr.inode != prev_hdr.inode || start) 00454 { 00455 if (LIKELY(!start)) 00456 FSCHECK(hdr.inode > prev_hdr.inode); 00457 else 00458 start = false; 00459 00460 FSCHECK(hdr.pgoff == 0); 00461 files++; 00462 } 00463 else 00464 { 00465 FSCHECK(hdr.fill != 0); 00466 FSCHECK(prev_hdr.fill == disk->data_size); 00467 FSCHECK(hdr.pgoff == prev_hdr.pgoff + 1); 00468 } 00469 prev_hdr = hdr; 00470 } 00471 } 00472 00473 FSCHECK(page_used == disk->free_page_start); 00474 FSCHECK(free_bytes == disk->free_bytes); 00475 00476 return true; 00477 } 00478 00483 static int battfs_flush(struct KFile *fd) 00484 { 00485 BattFs *fdb = BATTFS_CAST(fd); 00486 00487 if (kblock_flush(fdb->disk->dev) == 0) 00488 return 0; 00489 else 00490 { 00491 fdb->errors |= BATTFS_DISK_FLUSHBUF_ERR; 00492 return EOF; 00493 } 00494 } 00495 00500 static int battfs_fileclose(struct KFile *fd) 00501 { 00502 BattFs *fdb = BATTFS_CAST(fd); 00503 00504 if (battfs_flush(fd) == 0) 00505 { 00506 REMOVE(&fdb->link); 00507 return 0; 00508 } 00509 else 00510 return EOF; 00511 } 00512 00513 #define NO_SPACE PAGE_UNSET_SENTINEL 00514 00515 static pgcnt_t allocateNewPage(struct BattFsSuper *disk, pgcnt_t new_pos, inode_t inode) 00516 { 00517 if (SPACE_OVER(disk)) 00518 { 00519 LOG_ERR("No disk space available!\n"); 00520 return NO_SPACE; 00521 } 00522 00523 LOG_INFO("Getting new page %d, pos %d\n", disk->page_array[disk->free_page_start], new_pos); 00524 pgcnt_t new_page = disk->page_array[disk->free_page_start++]; 00525 memmove(&disk->page_array[new_pos + 1], &disk->page_array[new_pos], (disk->free_page_start - new_pos - 1) * sizeof(pgcnt_t)); 00526 00527 Node *n; 00528 /* Move following file start point one position ahead. */ 00529 FOREACH_NODE(n, &disk->file_opened_list) 00530 { 00531 BattFs *file = containerof(n, BattFs, link); 00532 if (file->inode > inode) 00533 { 00534 LOG_INFO("Move file %d start pos\n", file->inode); 00535 file->start++; 00536 } 00537 } 00538 00539 disk->page_array[new_pos] = new_page; 00540 return new_page; 00541 } 00542 00543 static pgcnt_t renewPage(struct BattFsSuper *disk, pgcnt_t old_pos) 00544 { 00545 if (SPACE_OVER(disk)) 00546 { 00547 LOG_ERR("No disk space available!\n"); 00548 return NO_SPACE; 00549 } 00550 00551 /* Get a free page */ 00552 pgcnt_t new_page = disk->page_array[disk->free_page_start]; 00553 movePages(disk, disk->free_page_start + 1, -1); 00554 00555 /* Insert previous page in free blocks list */ 00556 LOG_INFO("Setting page %d as free\n", old_pos); 00557 disk->page_array[disk->dev->blk_cnt - 1] = old_pos; 00558 return new_page; 00559 } 00560 00565 static size_t battfs_write(struct KFile *fd, const void *_buf, size_t size) 00566 { 00567 BattFs *fdb = BATTFS_CAST(fd); 00568 BattFsSuper *disk = fdb->disk; 00569 const uint8_t *buf = (const uint8_t *)_buf; 00570 00571 size_t total_write = 0; 00572 pgoff_t pg_offset; 00573 pgaddr_t addr_offset; 00574 pgaddr_t wr_len; 00575 BattFsPageHeader curr_hdr; 00576 pgcnt_t new_page; 00577 00578 if (fd->seek_pos < 0) 00579 { 00580 fdb->errors |= BATTFS_NEGATIVE_SEEK_ERR; 00581 return total_write; 00582 } 00583 00584 if (fd->seek_pos > fd->size) 00585 { 00586 if (!readHdr(disk, fdb->start[fdb->max_off], &curr_hdr)) 00587 { 00588 fdb->errors |= BATTFS_DISK_READ_ERR; 00589 return total_write; 00590 } 00591 00592 /* 00593 * Renew page only if is not in cache. 00594 * This avoids rewriting the same page continuously 00595 * if the user code keeps writing in the same portion 00596 * of the file. 00597 */ 00598 if (kblock_buffered(disk->dev) 00599 && ((fdb->start[fdb->max_off] != kblock_cachedBlock(disk->dev)) || !kblock_cacheDirty(disk->dev))) 00600 { 00601 new_page = renewPage(disk, fdb->start[fdb->max_off]); 00602 if (new_page == NO_SPACE) 00603 { 00604 fdb->errors |= BATTFS_DISK_SPACEOVER_ERR; 00605 return total_write; 00606 } 00607 00608 kblock_copy(disk->dev, fdb->start[fdb->max_off], new_page); 00609 fdb->start[fdb->max_off] = new_page; 00610 } 00611 else 00612 new_page = fdb->start[fdb->max_off]; 00613 00614 /* Fill unused space of first page with 0s */ 00615 uint8_t dummy = 0; 00616 // TODO: write in blocks to speed up things 00617 pgaddr_t zero_bytes = MIN(fd->seek_pos - fd->size, (kfile_off_t)(disk->data_size - curr_hdr.fill)); 00618 while (zero_bytes--) 00619 { 00620 if (kblock_write(disk->dev, new_page, &dummy, curr_hdr.fill, 1) != 1) 00621 { 00622 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00623 return total_write; 00624 } 00625 curr_hdr.fill++; 00626 fd->size++; 00627 disk->free_bytes--; 00628 } 00629 curr_hdr.seq++; 00630 if (!writeHdr(disk, new_page, &curr_hdr)) 00631 { 00632 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00633 return total_write; 00634 } 00635 00636 /* Allocate the missing pages first. */ 00637 pgoff_t missing_pages = fd->seek_pos / disk->data_size - fdb->max_off; 00638 00639 LOG_INFO("missing pages: %d\n", missing_pages); 00640 00641 while (missing_pages--) 00642 { 00643 zero_bytes = MIN((kfile_off_t)disk->data_size, fd->seek_pos - fd->size); 00644 00645 new_page = allocateNewPage(disk, (fdb->start - disk->page_array) + fdb->max_off + 1, fdb->inode); 00646 if (new_page == NO_SPACE) 00647 { 00648 fdb->errors |= BATTFS_DISK_SPACEOVER_ERR; 00649 return total_write; 00650 } 00651 00652 00653 // TODO: write in blocks to speed up things 00654 /* Fill page buffer with 0 to avoid filling unused pages with garbage */ 00655 for (pgaddr_t off = 0; off < disk->data_size; off++) 00656 { 00657 if (kblock_write(disk->dev, new_page, &dummy, off, 1) != 1) 00658 { 00659 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00660 return total_write; 00661 } 00662 } 00663 curr_hdr.inode = fdb->inode; 00664 curr_hdr.pgoff = ++fdb->max_off; 00665 curr_hdr.fill = zero_bytes; 00666 curr_hdr.seq = 0; 00667 00668 if (!writeHdr(disk, new_page, &curr_hdr)) 00669 { 00670 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00671 return total_write; 00672 } 00673 00674 /* Update size and free space left */ 00675 fd->size += zero_bytes; 00676 disk->free_bytes -= zero_bytes; 00677 } 00678 } 00679 00680 while (size) 00681 { 00682 pg_offset = fd->seek_pos / disk->data_size; 00683 addr_offset = fd->seek_pos % disk->data_size; 00684 wr_len = MIN(size, (size_t)(disk->data_size - addr_offset)); 00685 00686 /* Handle write outside EOF */ 00687 if (pg_offset > fdb->max_off) 00688 { 00689 LOG_INFO("New page needed, pg_offset %d, pos %d\n", pg_offset, (int)((fdb->start - disk->page_array) + pg_offset)); 00690 00691 new_page = allocateNewPage(disk, (fdb->start - disk->page_array) + pg_offset, fdb->inode); 00692 if (new_page == NO_SPACE) 00693 { 00694 fdb->errors |= BATTFS_DISK_SPACEOVER_ERR; 00695 return total_write; 00696 } 00697 00698 curr_hdr.inode = fdb->inode; 00699 curr_hdr.pgoff = pg_offset; 00700 curr_hdr.fill = 0; 00701 curr_hdr.seq = 0; 00702 fdb->max_off = pg_offset; 00703 } 00704 else 00705 { 00706 if (!readHdr(disk, fdb->start[pg_offset], &curr_hdr)) 00707 { 00708 fdb->errors |= BATTFS_DISK_READ_ERR; 00709 return total_write; 00710 } 00711 00712 /* Renew page only if is not in cache. */ 00713 if (kblock_buffered(disk->dev) 00714 && ((fdb->start[fdb->max_off] != kblock_cachedBlock(disk->dev)) || !kblock_cacheDirty(disk->dev))) 00715 { 00716 new_page = renewPage(disk, fdb->start[pg_offset]); 00717 if (new_page == NO_SPACE) 00718 { 00719 fdb->errors |= BATTFS_DISK_SPACEOVER_ERR; 00720 return total_write; 00721 } 00722 00723 LOG_INFO("Re-writing page %d to %d\n", fdb->start[pg_offset], new_page); 00724 if (kblock_copy(disk->dev, fdb->start[pg_offset], new_page) != 0) 00725 { 00726 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00727 return total_write; 00728 } 00729 fdb->start[pg_offset] = new_page; 00730 } 00731 else 00732 { 00733 LOG_INFO("Using cached block %d\n", fdb->start[pg_offset]); 00734 new_page = fdb->start[pg_offset]; 00735 } 00736 00737 curr_hdr.seq++; 00738 } 00739 //LOG_INFO("writing to buffer for page %d, offset %d, size %d\n", disk->curr_page, addr_offset, wr_len); 00740 if (kblock_write(disk->dev, new_page, buf, addr_offset, wr_len) != wr_len) 00741 { 00742 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00743 return total_write; 00744 } 00745 00746 size -= wr_len; 00747 fd->seek_pos += wr_len; 00748 total_write += wr_len; 00749 buf += wr_len; 00750 fill_t fill_delta = MAX((int32_t)(addr_offset + wr_len) - curr_hdr.fill, (int32_t)0); 00751 disk->free_bytes -= fill_delta; 00752 fd->size += fill_delta; 00753 curr_hdr.fill += fill_delta; 00754 00755 if (!writeHdr(disk, new_page, &curr_hdr)) 00756 { 00757 fdb->errors |= BATTFS_DISK_WRITE_ERR; 00758 return total_write; 00759 } 00760 00761 //LOG_INFO("free_bytes %d, seek_pos %d, size %d, curr_hdr.fill %d\n", disk->free_bytes, fd->seek_pos, fd->size, curr_hdr.fill); 00762 } 00763 return total_write; 00764 } 00765 00766 00771 static size_t battfs_read(struct KFile *fd, void *_buf, size_t size) 00772 { 00773 BattFs *fdb = BATTFS_CAST(fd); 00774 BattFsSuper *disk = fdb->disk; 00775 uint8_t *buf = (uint8_t *)_buf; 00776 00777 size_t total_read = 0; 00778 pgoff_t pg_offset; 00779 pgaddr_t addr_offset; 00780 pgaddr_t read_len; 00781 00782 if (fd->seek_pos < 0) 00783 { 00784 fdb->errors |= BATTFS_NEGATIVE_SEEK_ERR; 00785 return total_read; 00786 } 00787 00788 size = MIN((kfile_off_t)size, MAX(fd->size - fd->seek_pos, (kfile_off_t)0)); 00789 00790 while (size) 00791 { 00792 pg_offset = fd->seek_pos / disk->data_size; 00793 addr_offset = fd->seek_pos % disk->data_size; 00794 read_len = MIN(size, (size_t)(disk->data_size - addr_offset)); 00795 00796 //LOG_INFO("reading from page %d, offset %d, size %d\n", fdb->start[pg_offset], addr_offset, read_len); 00797 /* Read from disk */ 00798 if (kblock_read(disk->dev, fdb->start[pg_offset], buf, addr_offset, read_len) != read_len) 00799 { 00800 fdb->errors |= BATTFS_DISK_READ_ERR; 00801 return total_read; 00802 } 00803 00804 #ifdef _DEBUG 00805 BattFsPageHeader hdr; 00806 readHdr(disk, fdb->start[pg_offset], &hdr); 00807 ASSERT(hdr.inode == fdb->inode); 00808 #endif 00809 00810 size -= read_len; 00811 fd->seek_pos += read_len; 00812 total_read += read_len; 00813 buf += read_len; 00814 } 00815 return total_read; 00816 } 00817 00818 00827 static bool findFile(BattFsSuper *disk, inode_t inode, pgcnt_t *last) 00828 { 00829 BattFsPageHeader hdr; 00830 pgcnt_t first = 0, page; 00831 *last = disk->free_page_start; 00832 fcs_t fcs; 00833 00834 while (first < *last) 00835 { 00836 page = (first + *last) / 2; 00837 LOG_INFO("first %d, last %d, page %d\n", first, *last, page); 00838 if (!readHdr(disk, disk->page_array[page], &hdr)) 00839 return false; 00840 LOG_INFO("inode read: %d\n", hdr.inode); 00841 fcs = computeFcs(&hdr); 00842 if (hdr.fcs == fcs && hdr.inode == inode) 00843 { 00844 *last = page - hdr.pgoff; 00845 LOG_INFO("Found: %d\n", *last); 00846 return true; 00847 } 00848 else if (hdr.fcs == fcs && hdr.inode < inode) 00849 first = page + 1; 00850 else 00851 *last = page; 00852 } 00853 LOG_INFO("Not found: last %d\n", *last); 00854 return false; 00855 } 00856 00860 bool battfs_fileExists(BattFsSuper *disk, inode_t inode) 00861 { 00862 pgcnt_t dummy; 00863 return findFile(disk, inode, &dummy); 00864 } 00865 00871 static file_size_t countFileSize(BattFsSuper *disk, pgcnt_t *start, inode_t inode) 00872 { 00873 file_size_t size = 0; 00874 BattFsPageHeader hdr; 00875 00876 while (start < &disk->page_array[disk->free_page_start]) 00877 { 00878 if (!readHdr(disk, *start++, &hdr)) 00879 return EOF; 00880 if (hdr.fcs == computeFcs(&hdr) && hdr.inode == inode) 00881 size += hdr.fill; 00882 else 00883 break; 00884 } 00885 return size; 00886 } 00887 00888 static int battfs_error(struct KFile *fd) 00889 { 00890 BattFs *fdb = BATTFS_CAST(fd); 00891 return fdb->errors; 00892 } 00893 00894 00895 static void battfs_clearerr(struct KFile *fd) 00896 { 00897 BattFs *fdb = BATTFS_CAST(fd); 00898 fdb->errors = 0; 00899 } 00900 00906 bool battfs_fileopen(BattFsSuper *disk, BattFs *fd, inode_t inode, filemode_t mode) 00907 { 00908 Node *n; 00909 00910 memset(fd, 0, sizeof(*fd)); 00911 00912 /* Search file start point in disk page array */ 00913 pgcnt_t start_pos; 00914 if (!findFile(disk, inode, &start_pos)) 00915 { 00916 LOG_INFO("file %d not found\n", inode); 00917 if (!(mode & BATTFS_CREATE)) 00918 { 00919 fd->errors |= BATTFS_FILE_NOT_FOUND_ERR; 00920 return false; 00921 } 00922 /* Create the file */ 00923 BattFsPageHeader hdr; 00924 00925 if (allocateNewPage(disk, start_pos, inode) == NO_SPACE) 00926 { 00927 fd->errors |= BATTFS_DISK_SPACEOVER_ERR; 00928 return false; 00929 } 00930 00931 hdr.inode = inode; 00932 hdr.pgoff = 0; 00933 hdr.fill = 0; 00934 hdr.seq = 0; 00935 if (!writeHdr(disk, disk->page_array[start_pos], &hdr)) 00936 { 00937 fd->errors |= BATTFS_DISK_WRITE_ERR; 00938 return false; 00939 } 00940 } 00941 fd->start = &disk->page_array[start_pos]; 00942 LOG_INFO("Start pos %d\n", start_pos); 00943 00944 /* Fill file size */ 00945 if ((fd->fd.size = countFileSize(disk, fd->start, inode)) == EOF) 00946 { 00947 fd->errors |= BATTFS_DISK_READ_ERR; 00948 return false; 00949 } 00950 fd->max_off = fd->fd.size / disk->data_size; 00951 00952 /* Reset seek position */ 00953 fd->fd.seek_pos = 0; 00954 00955 /* Insert file handle in list, ordered by inode, ascending. */ 00956 FOREACH_NODE(n, &disk->file_opened_list) 00957 { 00958 BattFs *file = containerof(n, BattFs, link); 00959 if (file->inode >= inode) 00960 break; 00961 } 00962 INSERT_BEFORE(&fd->link, n); 00963 00964 /* Fill in data */ 00965 fd->inode = inode; 00966 fd->mode = mode; 00967 fd->disk = disk; 00968 00969 fd->fd.close = battfs_fileclose; 00970 fd->fd.flush = battfs_flush; 00971 fd->fd.read = battfs_read; 00972 fd->fd.reopen = kfile_genericReopen; 00973 fd->fd.seek = kfile_genericSeek; 00974 fd->fd.write = battfs_write; 00975 00976 fd->fd.error = battfs_error; 00977 fd->fd.clearerr = battfs_clearerr; 00978 00979 DB(fd->fd._type = KFT_BATTFS); 00980 00981 return true; 00982 } 00983 00984 00988 bool battfs_umount(struct BattFsSuper *disk) 00989 { 00990 Node *n; 00991 int res = 0; 00992 00993 /* Close all open files */ 00994 FOREACH_NODE(n, &disk->file_opened_list) 00995 { 00996 BattFs *file = containerof(n, BattFs, link); 00997 res += battfs_fileclose(&file->fd); 00998 } 00999 01000 /* Close disk */ 01001 return (kblock_flush(disk->dev) == 0) && (kblock_close(disk->dev) == 0) && (res == 0); 01002 } 01003 01004 #if UNIT_TEST 01005 01006 void battfs_writeTestBlock(KBlock *dev, pgcnt_t page, inode_t inode, seq_t seq, fill_t fill, pgoff_t pgoff) 01007 { 01008 uint8_t buf[BATTFS_HEADER_LEN]; 01009 battfs_eraseBlock(dev, page); 01010 01011 BattFsPageHeader hdr; 01012 hdr.inode = inode; 01013 hdr.fill = fill; 01014 hdr.pgoff = pgoff; 01015 hdr.seq = seq; 01016 hdr.fcs = computeFcs(&hdr); 01017 01018 battfs_to_disk(&hdr, buf); 01019 01020 ASSERT(kblock_write(dev, page, buf, dev->blk_size - BATTFS_HEADER_LEN, BATTFS_HEADER_LEN) == BATTFS_HEADER_LEN); 01021 ASSERT(kblock_flush(dev) == 0); 01022 } 01023 01024 void battfs_eraseBlock(KBlock *dev, pgcnt_t page) 01025 { 01026 /* Reset page to all 0xff */ 01027 uint8_t buf[dev->blk_size]; 01028 memset(buf, 0xFF, dev->blk_size); 01029 ASSERT(kblock_write(dev, page, buf, 0, dev->blk_size) == dev->blk_size); 01030 ASSERT(kblock_flush(dev) == 0); 01031 } 01032 01033 #endif