battfs.c

Go to the documentation of this file.
00001 
00040 #include "battfs.h"
00041 
00042 #include <cfg/debug.h>
00043 #include <cfg/macros.h> /* MIN, MAX */
00044 #include <mware/byteorder.h> /* cpu_to_xx */
00045 
00046 
00047 #include <string.h> /* memset, memmove */
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      * Mark is at least 1 bit longer than page address.
00067      * Needed to take care of wraparonds.
00068      */
00069     buf[5] = hdr->mark;
00070     buf[6] = hdr->mark >> 8;
00071 
00072     /*
00073      * First bit used by mark, last 2 bits used by seq.
00074      * Since only 2 pages with the same inode and pgoff
00075      * can exist at the same time, 2 bit for seq are enough.
00076      * Unused bits are set to 1.
00077      */
00078     buf[7] = ((hdr->mark >> 16) & 0x01) | (hdr->seq << 6) | ~(BV(7) | BV(6) | BV(0));
00079 
00080     /*
00081      * This field must be the before the last one!
00082      */
00083     buf[8] = hdr->fcs_free;
00084     buf[9] = hdr->fcs_free >> 8;
00085 
00086     /*
00087      * This field must be the last one!
00088      * This is needed because if the page is only partially
00089      * written, we can use this to detect it.
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     /* fcs is at the end of whole header */
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     /* fcs_free is just before fcs of whole header */
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      * Read header from disk.
00152      * Header is actually a footer, and so
00153      * resides at page end.
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     /* Fill header */
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     /* Fill buffer */
00178     battfs_to_disk(hdr, buf);
00179 
00180     /*
00181      * write header to disk.
00182      * Header is actually a footer, and so
00183      * resides at page end.
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         /* Fill empty space in array with sentinel */
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     /* Determine free_start & free_next */
00295     if (maxl >= minl)
00296     {
00297         /* Valid interval found in lower half */
00298         if (maxh >= minh)
00299         {
00300             /* Valid interval also found in upper half */
00301             if (maxl == minh - 1)
00302             {
00303                 /* Interval starts in lower half and ends in upper */
00304                 disk->free_start = minl;
00305                 disk->free_next = maxh;
00306             }
00307             else
00308             {
00309                 /* Interval starts in upper half and ends in lower */
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              * Upper interval is invalid.
00321              * Use lower values.
00322              */
00323             
00324             disk->free_start = minl;
00325             disk->free_next = maxl;
00326         }
00327     }
00328     else if (maxh >= minh)
00329     {
00330         /*
00331          * Lower interval is invalid.
00332          * Use upper values.
00333          */
00334         disk->free_start = minh;
00335         disk->free_next = maxh;
00336     }
00337     else
00338     {
00339         /*
00340          * No valid interval found.
00341          * Hopefully the disk is brand new (or full).
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; //to be increased later
00346     }
00347 
00348     /* free_next should contain the first usable address */
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     /* Initialize min and max counters to keep trace od free blocks */
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     /* Count the number of disk page per file */
00377     for (pgcnt_t page = 0; page < disk->page_count; page++)
00378     {
00379         if (!battfs_readHeader(disk, page, &hdr))
00380             return false;
00381 
00382         /* Check header FCS */
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             /* Page is valid and is owned by a file */
00390             filelen_table[hdr.inode]++;
00391 
00392             /* Keep trace of free space */
00393             disk->free_bytes += disk->page_size - BATTFS_HEADER_LEN - hdr.fill;
00394         }
00395         else
00396         {
00397             /* Increase free space */
00398             disk->free_bytes += disk->page_size - BATTFS_HEADER_LEN;
00399             
00400             /* Check if page is marked free */
00401             if (hdr.fcs_free == computeFcsFree(&hdr))
00402             {
00403                 /*
00404                  * This page is a valid and marked free page.
00405                  * Update min and max free page markers.
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     /* Fill page allocation array */
00445     for (pgcnt_t page = 0; page < disk->page_count; page++)
00446     {
00447         if (!battfs_readHeader(disk, page, &hdr))
00448             return false;
00449 
00450         /* Check header FCS */
00451         if (hdr.fcs == computeFcs(&hdr))
00452         {
00453             /* Page is valid and is owned by a file */
00454             ASSERT(hdr.mark == MARK_PAGE_VALID);
00455             ASSERT(hdr.fcs_free == FCS_FREE_VALID);
00456 
00457             /* Compute array position */
00458             pgcnt_t array_pos = countPages(filelen_table, hdr.inode);
00459             array_pos += hdr.pgoff;
00460 
00461             /* Check if position is already used by another page of the same file */
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                 /* Check header FCS */
00472                 ASSERT(hdr_old.fcs == computeFcs(&hdr_old));
00473 
00474                 /* Only the very same page with a different seq number can be here */
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                 /* Fancy check to handle seq wraparound (2 bits only) */
00485                 if (((hdr.seq - hdr_old.seq) & 0x03) < 2)
00486                 {
00487                     /* Current header is newer than the previuos one */
00488                     old_page = disk->page_array[array_pos];
00489                     new_page = page;
00490                     old_fill = hdr_old.fill;
00491                 }
00492                 else
00493                 {
00494                     /* Previous header is newer than the current one */
00495                     old_page = page;
00496                     new_page = disk->page_array[array_pos];
00497                     old_fill = hdr.fill;
00498                 }
00499 
00500                 /* Set new page */
00501                 disk->page_array[array_pos] = new_page;
00502 
00503                 /* Add free space */
00504                 disk->free_bytes += old_fill;
00505 
00506                 /* Shift all array one position to the left, overwriting duplicate page */
00507                 array_pos -= hdr.pgoff;
00508                 array_pos += filelen_table[hdr.inode];
00509                 movePages(disk, array_pos, -1);
00510                 
00511                 /* Decrease file page count */
00512                 filelen_table[hdr.inode]--;
00513 
00514                 /* Add old page to free pages pool */
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             /* Check if page is free */
00524             if (hdr.fcs_free != computeFcsFree(&hdr))
00525                 /* Page is not a valid marked page, insert at list beginning */
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     /* Sanity check */
00544     ASSERT(disk->open);
00545 
00546     /* Init disk device */
00547     if (!disk->open(disk))
00548     {
00549         TRACEMSG("open error\n");
00550         return false;
00551     }
00552 
00553     /* Disk open must set all of these */
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     /* Count pages per file */
00569     if (!countDiskFilePages(disk, filelen_table))
00570     {
00571         TRACEMSG("error counting file pages\n");
00572         return false;
00573     }
00574 
00575     /* Once here, we have filelen_table filled with file lengths */
00576 
00577     /* Fill page array with sentinel */
00578     for (pgcnt_t page = 0; page < disk->page_count; page++)
00579         disk->page_array[page] = PAGE_UNSET_SENTINEL;
00580 
00581     /* Fill page allocation array using filelen_table */
00582     if (!fillPageArray(disk, filelen_table))
00583     {
00584         TRACEMSG("error filling page array\n");
00585         return false;
00586     }
00587 
00588     /* Init list for opened files. */
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         /* Read from disk */
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     /* Search file start point in disk page array */
00725     fd->start = findFile(disk, inode);
00726     if (fd->start == NULL)
00727     {
00728         if (!(mode & BATTFS_CREATE))
00729             return false;
00730 
00731         /* File does not exist, create it */
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     /* Fill file size */
00744     if (!countFileSize(disk, fd->start, inode, &fd->fd.size))
00745         return false;
00746 
00747     /* Reset seek position */
00748     fd->fd.seek_pos = 0;
00749 
00750     /* Insert file handle in list, ordered by inode, ascending. */
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     /* Fill in data */
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     /* Close all open files */
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     /* Close disk */
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 }