battfs.c

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