Add more tests; handle end of disk space correctly.
[bertos.git] / bertos / fs / battfs.c
index 87b540197661745f445983e9e149f09a179458be..7d30231f68726c0f4f03299780a27ba038f785e5 100644 (file)
@@ -508,6 +508,61 @@ bool battfs_init(struct BattFsSuper *disk)
        return true;
 }
 
+/**
+ * Check the filesystem.
+ * \return true if ok, false on errors.
+ */
+bool battfs_fsck(struct BattFsSuper *disk)
+{
+       #define FSCHECK(cond) do { if(!(cond)) { LOG_ERR("\"" #cond "\"\n"); return false; } } while (0)
+
+       FSCHECK(disk->free_page_start <= disk->page_count);
+       FSCHECK(disk->data_size < disk->page_size);
+       FSCHECK(disk->free_bytes <= disk->disk_size);
+
+       disk_size_t free_bytes = 0;
+       BattFsPageHeader hdr, prev_hdr;
+       inode_t files = 0;
+       pgcnt_t page_used = 0;
+
+       bool start = true;
+
+       for (pgcnt_t page = 0; page < disk->page_count; page++)
+       {
+               FSCHECK(readHdr(disk, disk->page_array[page], &hdr));
+               free_bytes += disk->data_size;
+
+               if (page < disk->free_page_start)
+               {
+                       FSCHECK(computeFcs(&hdr) == hdr.fcs);
+                       page_used++;
+                       free_bytes -= hdr.fill;
+                       if (hdr.inode != prev_hdr.inode || start)
+                       {
+                               if (LIKELY(!start))
+                                       FSCHECK(hdr.inode > prev_hdr.inode);
+                               else
+                                       start = false;
+
+                               FSCHECK(hdr.pgoff == 0);
+                               files++;
+                       }
+                       else
+                       {
+                               FSCHECK(hdr.fill != 0);
+                               FSCHECK(prev_hdr.fill == disk->data_size);
+                               FSCHECK(hdr.pgoff == prev_hdr.pgoff + 1);
+                       }
+                       prev_hdr = hdr;
+               }
+       }
+
+       FSCHECK(page_used == disk->free_page_start);
+       FSCHECK(free_bytes == disk->free_bytes);
+
+       return true;
+}
+
 /**
  * Flush file \a fd.
  * \return 0 if ok, EOF on errors.
@@ -538,10 +593,10 @@ static int battfs_fileclose(struct KFile *fd)
 
 static bool getNewPage(struct BattFsSuper *disk, pgcnt_t new_pos, inode_t inode, pgoff_t pgoff, BattFsPageHeader *new_hdr)
 {
-       if (disk->free_page_start >= disk->page_count)
+       if (SPACE_OVER(disk))
        {
                #warning TODO space over!
-               LOG_INFO("No disk space available\n");
+               LOG_ERR("No disk space available!\n");
                return false;
        }
        flushBuffer(disk);
@@ -589,51 +644,65 @@ static size_t battfs_write(struct KFile *fd, const void *_buf, size_t size)
        if (fd->seek_pos < 0)
                return total_write;
 
-       if ((fd->seek_pos / fdb->disk->data_size) > fdb->max_off)
+       if (fd->seek_pos > fd->size)
        {
-               /*
-                * Handle writing when seek pos if far over EOF,
-                * We need to allocate the missing pages first.
-                */
-               pgoff_t missing_pages = fd->seek_pos / fdb->disk->data_size - fdb->max_off;
-
-               LOG_INFO("missing pages: %d\n", missing_pages);
+               /* Handle writing when seek pos if far over EOF */
                if (!loadPage(fdb->disk, fdb->start[fdb->max_off], &curr_hdr))
                {
                                #warning TODO set error?
                                return total_write;
                }
 
-               uint8_t dummy = 0xff;
-               /* Add missing pages to reach current seek_pos */
-               while (missing_pages--)
+               /* Fill unused space of first page with 0s */
+               uint8_t dummy = 0;
+               pgaddr_t zero_bytes = MIN(fd->seek_pos - fd->size, fdb->disk->data_size - curr_hdr.fill);
+               while (zero_bytes--)
                {
-                       /* Update size and free space left */
-                       fd->size += fdb->disk->data_size - curr_hdr.fill;
-                       fdb->disk->free_bytes -= fdb->disk->data_size - curr_hdr.fill;
+                       if (fdb->disk->bufferWrite(fdb->disk, curr_hdr.fill, &dummy, 1) != 1)
+                       {
+                               #warning TODO set error?
+                       }
+                       curr_hdr.fill++;
+                       fd->size++;
+                       fdb->disk->free_bytes--;
+                       fdb->disk->cache_dirty = true;
+               }
+               setBufferHdr(fdb->disk, &curr_hdr);
 
-                       /* Fill empty space with 0xFF */
-                       for (addr_offset = curr_hdr.fill; addr_offset < fdb->disk->data_size; addr_offset++)
+               /* Allocate the missing pages first. */
+               pgoff_t missing_pages = fd->seek_pos / fdb->disk->data_size - fdb->max_off;
+
+               if (missing_pages)
+               {
+                       LOG_INFO("missing pages: %d\n", missing_pages);
+                       flushBuffer(fdb->disk);
+
+                       /* Fill page buffer with 0 to avoid filling unused pages with garbage */
+                       for (pgaddr_t off = 0; off < fdb->disk->data_size; off++)
                        {
-                               if (fdb->disk->bufferWrite(fdb->disk, addr_offset, &dummy, 1) != 1)
+                               if (fdb->disk->bufferWrite(fdb->disk, off, &dummy, 1) != 1)
                                {
                                        #warning TODO set error?
                                }
                        }
-                       curr_hdr.fill = fdb->disk->data_size;
-                       setBufferHdr(fdb->disk, &curr_hdr);
 
-                       /* Get the new page needed */
-                       if (!getNewPage(fdb->disk, (fdb->start - fdb->disk->page_array) + fdb->max_off + 1, fdb->inode, fdb->max_off + 1, &curr_hdr))
-                               return total_write;
+                       while (missing_pages--)
+                       {
+                               zero_bytes = MIN((kfile_off_t)fdb->disk->data_size, fd->seek_pos - fd->size);
+                               /* Get the new page needed */
+                               if (!getNewPage(fdb->disk, (fdb->start - fdb->disk->page_array) + fdb->max_off + 1, fdb->inode, fdb->max_off + 1, &curr_hdr))
+                                       return total_write;
 
-                       fdb->max_off++;
+                               /* Update size and free space left */
+                               fd->size += zero_bytes;
+                               fdb->disk->free_bytes -= zero_bytes;
+
+                               curr_hdr.fill = zero_bytes;
+                               setBufferHdr(fdb->disk, &curr_hdr);
+
+                               fdb->max_off++;
+                       }
                }
-               /* Fix sizes for the last new page (could be only partially full) */
-               curr_hdr.fill = fd->seek_pos % fdb->disk->data_size;
-               setBufferHdr(fdb->disk, &curr_hdr);
-               fd->size += curr_hdr.fill;
-               fdb->disk->free_bytes -= curr_hdr.fill;
        }
        else if (!getBufferHdr(fdb->disk, &curr_hdr))
                return total_write;
@@ -655,6 +724,11 @@ static size_t battfs_write(struct KFile *fd, const void *_buf, size_t size)
                /* Handle cache load of a new page*/
                else if (fdb->start[pg_offset] != fdb->disk->curr_page)
                {
+                       if (SPACE_OVER(fdb->disk))
+                       {
+                               LOG_ERR("No disk space available!\n");
+                               return total_write;
+                       }
                        LOG_INFO("Re-writing page %d to %d\n", fdb->start[pg_offset], fdb->disk->page_array[fdb->disk->free_page_start]);
                        if (!loadPage(fdb->disk, fdb->start[pg_offset], &curr_hdr))
                        {
@@ -674,7 +748,6 @@ static size_t battfs_write(struct KFile *fd, const void *_buf, size_t size)
                        curr_hdr.seq++;
                }
 
-
                //LOG_INFO("writing to buffer for page %d, offset %d, size %d\n", fdb->disk->curr_page, addr_offset, wr_len);
                if (fdb->disk->bufferWrite(fdb->disk, addr_offset, buf, wr_len) != wr_len)
                {