Remove dead code; Use LOG_ERR; Fix 2 typo.
[bertos.git] / bertos / fs / battfs.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief BattFS: a filesystem for embedded platforms (implementation).
34  *
35  * \version $Id:$
36  *
37  * \author Francesco Sacchi <batt@develer.com>
38  *
39  */
40
41 #include "battfs.h"
42
43 #include <cfg/debug.h>
44 #include <cfg/macros.h> /* MIN, MAX */
45 #include <cpu/byteorder.h> /* cpu_to_xx */
46
47 #define LOG_LEVEL       LOG_LVL_INFO
48 #define LOG_FORMAT      LOG_FMT_VERBOSE
49 #include <cfg/log.h>
50
51 #include <string.h> /* memset, memmove */
52
53
54 /**
55  * Convert from memory representation to disk structure.
56  * \note filesystem is in little-endian format.
57  */
58 INLINE void battfs_to_disk(struct BattFsPageHeader *hdr, uint8_t *buf)
59 {
60         STATIC_ASSERT(BATTFS_HEADER_LEN == 10);
61         buf[0] = hdr->inode;
62
63         buf[1] = hdr->fill;
64         buf[2] = hdr->fill >> 8;
65
66         buf[3] = hdr->pgoff;
67         buf[4] = hdr->pgoff >> 8;
68
69         /*
70          * Sequence number is at least 1 bit longer than page address.
71          * Needed to take care of wraparonds.
72          */
73         buf[5] = hdr->seq;
74         buf[6] = hdr->seq >> 8;
75
76         /*
77          * First bit used by seq.
78          * Unused bits are set to 1.
79          */
80         buf[7] = (hdr->seq >> 16) ? 0xFF : 0xFE;
81
82         /*
83          * This field must be the last one!
84          * This is needed because if the page is only partially
85          * written, we can use this to detect it.
86          */
87         buf[8] = hdr->fcs;
88         buf[9] = hdr->fcs >> 8;
89 }
90
91 /**
92  * Convert from disk structure to memory representation.
93  * \note filesystem is in little-endian format.
94  */
95 INLINE void disk_to_battfs(uint8_t *buf, struct BattFsPageHeader *hdr)
96 {
97         STATIC_ASSERT(BATTFS_HEADER_LEN == 10);
98         hdr->inode = buf[0];
99         hdr->fill = buf[2] << 8 | buf[1];
100         hdr->pgoff = buf[4] << 8 | buf[3];
101         hdr->seq = (seq_t)(buf[7] & 0x01) << 16 | buf[6] << 8 | buf[5];
102         hdr->fcs = buf[9] << 8 | buf[8];
103 }
104
105 /**
106  * Compute the fcs of the header.
107  */
108 static fcs_t computeFcs(struct BattFsPageHeader *hdr)
109 {
110         uint8_t buf[BATTFS_HEADER_LEN];
111         fcs_t cks;
112
113         battfs_to_disk(hdr, buf);
114         rotating_init(&cks);
115         /* fcs is at the end of whole header */
116         rotating_update(buf, BATTFS_HEADER_LEN - sizeof(fcs_t), &cks);
117         return cks;
118 }
119
120
121 /**
122  * Read header of page \a page.
123  * \return true on success, false otherwise.
124  */
125 static bool battfs_readHeader(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr)
126 {
127         uint8_t buf[BATTFS_HEADER_LEN];
128         /*
129          * Read header from disk.
130          * Header is actually a footer, and so
131          * resides at page end.
132          */
133         if (disk->read(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN)
134             != BATTFS_HEADER_LEN)
135         {
136                 LOG_ERR("Error: page[%d]\n", page);
137                 return false;
138         }
139
140         /* Fill header */
141         disk_to_battfs(buf, hdr);
142
143         return true;
144 }
145
146 /**
147  * Write header of page \a page.
148  * \return true on success, false otherwise.
149  */
150 static bool battfs_writeHeader(struct BattFsSuper *disk, pgcnt_t page, struct BattFsPageHeader *hdr)
151 {
152         uint8_t buf[BATTFS_HEADER_LEN];
153
154         /* Fill buffer */
155         battfs_to_disk(hdr, buf);
156
157         /*
158          * write header to disk.
159          * Header is actually a footer, and so
160          * resides at page end.
161          */
162         if (disk->write(disk, page, disk->page_size - BATTFS_HEADER_LEN, buf, BATTFS_HEADER_LEN)
163             != BATTFS_HEADER_LEN)
164         {
165                 LOG_ERR("Error: page[%d]\n", page);
166                 return false;
167         }
168         return true;
169 }
170
171 /**
172  * Count the number of pages from
173  * inode 0 to \a inode in \a filelen_table.
174  */
175 static pgcnt_t countPages(pgoff_t *filelen_table, inode_t inode)
176 {
177         pgcnt_t cnt = 0;
178
179         for (inode_t i = 0; i < inode; i++)
180                 cnt += filelen_table[i];
181
182         return cnt;
183 }
184
185 /**
186  * Move all pages in page allocation array from \a src to \a src + \a offset.
187  * The number of pages moved is page_count - MAX(dst, src).
188  */
189 static void movePages(struct BattFsSuper *disk, pgcnt_t src, int offset)
190 {
191         pgcnt_t dst = src + offset;
192         memmove(&disk->page_array[dst], &disk->page_array[src], (disk->page_count - MAX(dst, src)) * sizeof(pgcnt_t));
193
194         if (offset < 0)
195         {
196                 /* Fill empty space in array with sentinel */
197                 for (pgcnt_t page = disk->page_count + offset; page < disk->page_count; page++)
198                         disk->page_array[page] = PAGE_UNSET_SENTINEL;
199         }
200 }
201
202 /**
203  * Count number of pages per file on \a disk.
204  * This information is registered in \a filelen_table.
205  * Array index represent file inode, while value contained
206  * is the number of pages used by that file.
207  *
208  * \return true if ok, false on disk read errors.
209  * \note The whole disk is scanned once.
210  */
211 static bool countDiskFilePages(struct BattFsSuper *disk, pgoff_t *filelen_table)
212 {
213         BattFsPageHeader hdr;
214         disk->free_page_start = 0;
215
216         /* Count the number of disk page per file */
217         for (pgcnt_t page = 0; page < disk->page_count; page++)
218         {
219                 if (!battfs_readHeader(disk, page, &hdr))
220                         return false;
221
222                 /* Increase free space */
223                 disk->free_bytes += disk->page_size - BATTFS_HEADER_LEN;
224
225                 /* Check header FCS */
226                 if (hdr.fcs == computeFcs(&hdr))
227                 {
228                         ASSERT(hdr.fill <= disk->page_size - BATTFS_HEADER_LEN);
229
230                         /* Page is valid and is owned by a file */
231                         filelen_table[hdr.inode]++;
232
233                         /* Keep trace of free space */
234                         disk->free_bytes -= hdr.fill;
235                         disk->free_page_start++;
236                 }
237         }
238         LOG_INFO("free_bytes:%d, free_page_start:%d\n", disk->free_bytes, disk->free_page_start);
239
240         return true;
241 }
242
243 /**
244  * Fill page allocation array of \a disk
245  * using file lenghts in \a filelen_table.
246  *
247  * The page allocation array is an array containings all file infos.
248  * Is ordered by file, and within each file is ordered by page offset
249  * inside file.
250  * e.g. : at page array[0] you will find page address of the first page
251  * of the first file (if present).
252  * Free blocks are allocated after the last file, starting from invalid ones
253  * and continuing with the marked free ones.
254  *
255  * \return true if ok, false on disk read errors.
256  * \note The whole disk is scanned once.
257  */
258 static bool fillPageArray(struct BattFsSuper *disk, pgoff_t *filelen_table)
259 {
260         BattFsPageHeader hdr;
261         pgcnt_t curr_free_page = disk->free_page_start;
262         /* Fill page allocation array */
263         for (pgcnt_t page = 0; page < disk->page_count; page++)
264         {
265                 if (!battfs_readHeader(disk, page, &hdr))
266                         return false;
267
268                 /* Check header FCS */
269                 if (hdr.fcs == computeFcs(&hdr))
270                 {
271                         /* Compute array position */
272                         pgcnt_t array_pos_start = countPages(filelen_table, hdr.inode);
273                         pgcnt_t array_pos = array_pos_start + hdr.pgoff;
274
275                         /* Find the first free position */
276                         while (disk->page_array[array_pos] != PAGE_UNSET_SENTINEL)
277                         {
278                                 ASSERT(array_pos < array_pos_start + filelen_table[hdr.inode] + filelen_table[hdr.inode + 1]);
279                                 array_pos++;
280                         }
281
282                         disk->page_array[array_pos] = page;
283                 }
284                 else
285                 {
286                         /* Invalid page, keep as free */
287                         ASSERT(disk->page_array[curr_free_page] == PAGE_UNSET_SENTINEL);
288                         LOG_INFO("Page %d invalid, keeping as free\n", page);
289                         disk->page_array[curr_free_page++] = page;
290                 }
291         }
292         return true;
293 }
294
295 /**
296  * Find the latest version of a page, starting from the
297  * page supplied by \a page_array.
298  * The pages are read from the disk until a different
299  * inode or page offset is found.
300  * The lastest version of the page is moved in the first
301  * position of \a page_array.
302  * \return the number of old versions of the page or PAGE_ERROR
303  *         on disk read errors.
304  */
305 static pgcnt_t findLastVersion(struct BattFsSuper *disk, pgcnt_t *page_array)
306 {
307         pgcnt_t *array_start = page_array;
308         BattFsPageHeader hdr;
309         if (!battfs_readHeader(disk, *page_array++, &hdr))
310                 return PAGE_ERROR;
311
312         /* Free space: early bailout */
313         if (hdr.fcs != computeFcs(&hdr))
314                 return 0;
315
316         /*
317          * If the first page is valid,
318          * inode and pg_off in the array are taken
319          * as the current page markers.
320          */
321         inode_t curr_inode = hdr.inode;
322         pgoff_t curr_pgoff = hdr.pgoff;
323
324         /* Temps used to find the sequence number range */
325         seq_t minl = HALF_SEQ - 1;
326         seq_t maxl = 0;
327         seq_t minh = MAX_SEQ;
328         seq_t maxh = MAX_SEQ;
329         pgcnt_t lpos = 0, hpos = 0, dup_cnt = 0;
330
331         /*
332          * Find min and max values for the two
333          * half of seq_num range.
334          * With this we can find seqnum wraparounds.
335          * seq_t is a type that has at least 1 bit more than
336          * pgaddr_t. So all version of a page blocks can be numbered using
337          * at most half numbers of a seq_t type.
338          * The sequence number algorithm increments by 1 the previous seq_num
339          * every time a page is rewritten. So the sequence is
340          * guaranteed to be countiguous.
341          * Only wrap arounds may happen, but due to half size sequence limitation,
342          * there are only 4 possible situations:
343          *
344          * \verbatim
345          *    |------lower half------|-------upper half-------|
346          *
347          * 1) |------minl*****maxl---|------------------------|
348          * 2) |------minl********maxl|minh******maxh----------|
349          * 3) |----------------------|----minh*******maxh-----|
350          * 4) |minl******maxl--------|------------minh****maxh|
351          * \endverbatim
352          *
353          * Situations 1 and 3 are easy to detect, while 2 and 4 require more care.
354          */
355         do
356         {
357                 if (hdr.seq < HALF_SEQ)
358                 {
359                         minl = MIN(minl, hdr.seq);
360                         if (hdr.seq > maxl)
361                         {
362                                 maxl = hdr.seq;
363                                 lpos = dup_cnt;
364                         }
365                 }
366                 else
367                 {
368                         minh = MIN(minh, hdr.seq);
369                         if (hdr.seq > maxh)
370                         {
371                                 maxh = hdr.seq;
372                                 hpos = dup_cnt;
373                         }
374                 }
375
376                 if (!battfs_readHeader(disk, *page_array++, &hdr))
377                         return PAGE_ERROR;
378                 dup_cnt++;
379         }
380         while (curr_inode == hdr.inode && curr_pgoff == hdr.pgoff && hdr.fcs == computeFcs(&hdr));
381
382
383         /* Return early if there is only one version of the current page */
384         if (dup_cnt == 1)
385                 return 0;
386
387         /* Find the position in the array of the last version of the page */
388         pgcnt_t last_ver = hpos;
389         if (maxl >= minl)
390         {
391                 /* Valid interval found in lower half */
392                 if (maxh >= minh)
393                 {
394                         /* Valid interval also found in upper half */
395                         if (maxl != minh - 1)
396                         {
397                                 /* Interval starts in upper half and ends in lower */
398                                 ASSERT(minl == 0);
399                                 ASSERT(maxh == MAX_SEQ);
400
401                                 last_ver = lpos;
402                         }
403                 }
404                 else
405                         /*
406                          * Upper interval is invalid.
407                          * Use lower values.
408                          */
409                         last_ver = lpos;
410         }
411
412         /* Put last page version at array start position */
413         SWAP(array_start[0], array_start[last_ver]);
414
415         return dup_cnt - 1;
416 }
417
418 /**
419  * Collect old pages, removing empty spaces from \a pg_array, for a maximum len of \a pg_len.
420  * Once the collect task is completed, copy \a old_cnt pages from \a old_pages at the
421  * end of free space in pg_array.
422  */
423 void collectOldPages(pgcnt_t *pg_array, pgcnt_t pg_len, pgcnt_t *old_pages, pgcnt_t old_cnt)
424 {
425         bool copy = false;
426         pgcnt_t gap = 0;
427
428         for (pgcnt_t curr_page = 0; curr_page < pg_len; curr_page++)
429         {
430                 if (!copy)
431                 {
432                         if (pg_array[curr_page] == PAGE_UNSET_SENTINEL)
433                                 gap++;
434                         else
435                         {
436                                 pg_array[curr_page - gap] = pg_array[curr_page];
437                                 copy = true;
438                         }
439                 }
440                 else
441                 {
442                         if (pg_array[curr_page] != PAGE_UNSET_SENTINEL)
443                                 pg_array[curr_page - gap] = pg_array[curr_page];
444                         else
445                         {
446                                 gap++;
447                                 copy = false;
448                         }
449                 }
450         }
451         ASSERT(gap == old_cnt);
452         pg_array += pg_len - old_cnt;
453
454         memcpy(pg_array, old_pages, old_cnt * sizeof(pgcnt_t));
455 }
456
457 /**
458  * This function scan the page array of \a disk looking for
459  * old versions of the same page.
460  *
461  * Only the last version is kept as valid, the old ones are inserted
462  * in the free blocks heap.
463  * \return true if ok, false on disk read errors.
464  * \note The whole disk is scanned once.
465  */
466 static bool dropOldPages(struct BattFsSuper *disk)
467 {
468         #define OLD_PAGE_BUFLEN 64
469         pgcnt_t old_pages[OLD_PAGE_BUFLEN];
470         pgcnt_t old_cnt = 0;
471
472         pgcnt_t *curr_page = disk->page_array;
473         pgcnt_t *collect_start = disk->page_array;
474         pgcnt_t collect_len = disk->page_count;
475         pgcnt_t dup_pages;
476
477         do
478         {
479                 dup_pages = findLastVersion(disk, curr_page);
480                 if (dup_pages == PAGE_ERROR)
481                         return false;
482                 /* The first page is the last version */
483                 curr_page++;
484                 while (dup_pages--)
485                 {
486                         if (old_cnt >= OLD_PAGE_BUFLEN)
487                         {
488                                 collectOldPages(collect_start, collect_len, old_pages, old_cnt);
489                                 collect_len -= old_cnt;
490                                 disk->free_bytes += old_cnt * (disk->page_size - BATTFS_HEADER_LEN);
491                                 disk->free_page_start -= old_cnt;
492                                 curr_page -= old_cnt;
493                                 collect_start = curr_page;
494                                 old_cnt = 0;
495                         }
496
497                         old_pages[old_cnt++] = *curr_page;
498                         *curr_page++ = PAGE_UNSET_SENTINEL;
499                 }
500         }
501         while (curr_page < disk->page_array + disk->free_page_start);
502
503         collectOldPages(collect_start, collect_len, old_pages, old_cnt);
504         disk->free_bytes += old_cnt * (disk->page_size - BATTFS_HEADER_LEN);
505         disk->free_page_start -= old_cnt;
506
507         return true;
508 }
509
510
511 /**
512  * Initialize and mount disk described by
513  * \a disk.
514  * \return false on errors, true otherwise.
515  */
516 bool battfs_init(struct BattFsSuper *disk)
517 {
518         pgoff_t filelen_table[BATTFS_MAX_FILES];
519
520         /* Sanity check */
521         ASSERT(disk->open);
522
523         /* Init disk device */
524         if (!disk->open(disk))
525         {
526                 LOG_ERR("open error\n");
527                 return false;
528         }
529
530         /* Disk open must set all of these */
531         ASSERT(disk->read);
532         ASSERT(disk->write);
533         ASSERT(disk->erase);
534         ASSERT(disk->close);
535         ASSERT(disk->page_size);
536         ASSERT(disk->page_count);
537         ASSERT(disk->page_count < PAGE_UNSET_SENTINEL - 1);
538         ASSERT(disk->page_array);
539
540         memset(filelen_table, 0, BATTFS_MAX_FILES * sizeof(pgoff_t));
541
542         disk->free_bytes = 0;
543         disk->disk_size = (disk_size_t)(disk->page_size - BATTFS_HEADER_LEN) * disk->page_count;
544
545         /* Count pages per file */
546         if (!countDiskFilePages(disk, filelen_table))
547         {
548                 LOG_ERR("error counting file pages\n");
549                 return false;
550         }
551
552         /* Once here, we have filelen_table filled with file lengths */
553
554         /* Fill page array with sentinel */
555         for (pgcnt_t page = 0; page < disk->page_count; page++)
556                 disk->page_array[page] = PAGE_UNSET_SENTINEL;
557
558         /* Fill page allocation array using filelen_table */
559         if (!fillPageArray(disk, filelen_table))
560         {
561                 LOG_ERR("error filling page array\n");
562                 return false;
563         }
564
565         if (!dropOldPages(disk))
566         {
567                 LOG_ERR("error dropping old pages\n");
568                 return false;
569         }
570
571         /* Init list for opened files. */
572         LIST_INIT(&disk->file_opened_list);
573         return true;
574 }
575
576 /**
577  * Flush file \a fd.
578  * \return 0 if ok, EOF on errors.
579  */
580 static int battfs_flush(struct KFile *fd)
581 {
582         (void)fd;
583         #warning TODO
584         return 0;
585 }
586
587 /**
588  * Close file \a fd.
589  * \return 0 if ok, EOF on errors.
590  */
591 static int battfs_fileclose(struct KFile *fd)
592 {
593         BattFs *fdb = BATTFS_CAST(fd);
594
595         battfs_flush(fd);
596         REMOVE(&fdb->link);
597         return 0;
598 }
599
600 /**
601  * Read from file \a fd \a size bytes in \a buf.
602  * \return The number of bytes read.
603  */
604 static size_t battfs_read(struct KFile *fd, void *_buf, size_t size)
605 {
606         BattFs *fdb = BATTFS_CAST(fd);
607         uint8_t *buf = (uint8_t *)_buf;
608
609         size_t total_read = 0;
610         pgoff_t pg_offset;
611         pgaddr_t addr_offset;
612         pgaddr_t read_len;
613
614         size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
615
616         while (size)
617         {
618                 pg_offset = fd->seek_pos / (fdb->disk->page_size - BATTFS_HEADER_LEN);
619                 addr_offset = fd->seek_pos % (fdb->disk->page_size - BATTFS_HEADER_LEN);
620                 read_len = MIN(size, (size_t)(fdb->disk->page_size - BATTFS_HEADER_LEN - addr_offset));
621
622                 /* Read from disk */
623                 if (fdb->disk->read(fdb->disk, fdb->start[pg_offset], addr_offset, buf, read_len) != read_len)
624                 {
625                         #warning TODO set error?
626                 }
627
628                 size -= read_len;
629                 fd->seek_pos += read_len;
630                 total_read += read_len;
631                 buf += read_len;
632         }
633         return total_read;
634 }
635
636
637 /**
638  * Search file \a inode in \a disk using a binary search.
639  * \return pointer to file start in disk->page_array
640  * if file exists, NULL otherwise.
641  */
642 static pgcnt_t *findFile(BattFsSuper *disk, inode_t inode)
643 {
644         BattFsPageHeader hdr;
645         pgcnt_t first = 0, page, last = disk->page_count -1;
646         fcs_t fcs;
647
648         while (first <= last)
649         {
650                 page = (first + last) / 2;
651
652                 if (!battfs_readHeader(disk, disk->page_array[page], &hdr))
653                         return NULL;
654
655                 fcs = computeFcs(&hdr);
656                 if (hdr.fcs == fcs && hdr.inode == inode)
657                         return (&disk->page_array[page]) - hdr.pgoff;
658                 else if (hdr.fcs == fcs && hdr.inode < inode)
659                         first = page + 1;
660                 else
661                         last = page - 1;
662         }
663
664         return NULL;
665 }
666
667 /**
668  * \return true if file \a inode exists on \a disk, false otherwise.
669  */
670 bool battfs_fileExists(BattFsSuper *disk, inode_t inode)
671 {
672         return findFile(disk, inode) != NULL;
673 }
674
675 /**
676  * Count size of file \a inode on \a disk, starting at pointer \a start
677  * in disk->page_array. Size is written in \a size.
678  * \return true if all s ok, false on disk read errors.
679  */
680 static bool countFileSize(BattFsSuper *disk, pgcnt_t *start, inode_t inode, file_size_t *size)
681 {
682         *size = 0;
683         BattFsPageHeader hdr;
684
685         for (;;)
686         {
687                 if (!battfs_readHeader(disk, *start++, &hdr))
688                         return false;
689                 if (hdr.fcs == computeFcs(&hdr) && hdr.inode == inode)
690                         *size += hdr.fill;
691                 else
692                         return true;
693         }
694 }
695
696 /**
697  * Open file \a inode from \a disk in \a mode.
698  * File context is stored in \a fd.
699  * \return true if ok, false otherwise.
700  */
701 bool battfs_fileopen(BattFsSuper *disk, BattFs *fd, inode_t inode, filemode_t mode)
702 {
703         Node *n;
704
705         memset(fd, 0, sizeof(*fd));
706
707         /* Search file start point in disk page array */
708         fd->start = findFile(disk, inode);
709         if (fd->start == NULL)
710         {
711                 if (!(mode & BATTFS_CREATE))
712                         return false;
713
714                 /* File does not exist, create it */
715                 BattFsPageHeader hdr;
716                 hdr.inode = inode;
717                 hdr.seq = 0;
718                 hdr.fill = 0;
719                 hdr.pgoff = 0;
720                 hdr.fcs = computeFcs(&hdr);
721                 #warning TODO: get a free block and write on disk!
722         }
723
724         /* Fill file size */
725         if (!countFileSize(disk, fd->start, inode, &fd->fd.size))
726                 return false;
727
728         /* Reset seek position */
729         fd->fd.seek_pos = 0;
730
731         /* Insert file handle in list, ordered by inode, ascending. */
732         FOREACH_NODE(n, &disk->file_opened_list)
733         {
734                 BattFs *file = containerof(n, BattFs, link);
735                 if (file->inode >= inode)
736                         break;
737         }
738         INSERT_BEFORE(&fd->link, n);
739
740         /* Fill in data */
741         fd->inode = inode;
742         fd->mode = mode;
743         fd->disk = disk;
744
745         fd->fd.close = battfs_fileclose;
746         fd->fd.flush = battfs_flush;
747         fd->fd.read = battfs_read;
748         fd->fd.reopen = kfile_genericReopen;
749         fd->fd.seek = kfile_genericSeek;
750
751 #warning TODO battfs_write, battfs_error, battfs_clearerr
752 #if 0
753         fd->fd.write = battfs_write;
754         fd->fd.error = battfs_error;
755         fd->fd.clearerr = battfs_clearerr;
756 #endif
757
758         DB(fd->fd._type = KFT_BATTFS);
759
760         return true;
761 }
762
763 /**
764  * Close \a disk.
765  */
766 bool battfs_close(struct BattFsSuper *disk)
767 {
768         Node *n;
769         int res = 0;
770
771         /* Close all open files */
772         FOREACH_NODE(n, &disk->file_opened_list)
773         {
774                 BattFs *file = containerof(n, BattFs, link);
775                 res += battfs_fileclose(&file->fd);
776         }
777
778         /* Close disk */
779         return disk->close(disk) && (res == 0);
780 }
781
782 bool battfs_writeTestBlock(struct BattFsSuper *disk, pgcnt_t page, inode_t inode, seq_t seq, fill_t fill, pgoff_t pgoff)
783 {
784         BattFsPageHeader hdr;
785
786         hdr.inode = inode;
787         hdr.fill = fill;
788         hdr.pgoff = pgoff;
789         hdr.seq = seq;
790         hdr.fcs = computeFcs(&hdr);
791
792         if (!battfs_writeHeader(disk, page, &hdr))
793         {
794                 LOG_ERR("error writing hdr\n");
795                 return false;
796         }
797
798         return true;
799 }