BeRTOS
battfs.c
Go to the documentation of this file.
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