comparison services/ffs/fsck.c @ 0:75a11d740a02

initial import of gsm-fw from freecalypso-sw rev 1033:5ab737ac3ad7
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 09 Jun 2016 00:02:41 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:75a11d740a02
1 /******************************************************************************
2 * Flash File System (ffs)
3 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
4 *
5 * FFS file system integrity checking, journalling, init and exit
6 *
7 * $Id: fsck.c 1.3.1.1.1.33 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
8 *
9 ******************************************************************************/
10
11 #include <string.h>
12 #include <assert.h>
13
14 #include "ffs.h"
15 #include "core.h"
16 #include "drv.h"
17 #include "ffstrace.h"
18
19 /******************************************************************************
20 * Functions
21 ******************************************************************************/
22
23 bref_t blocks_fsck(void);
24 iref_t inodes_fsck(void);
25
26 /******************************************************************************
27 * Init and Exit
28 ******************************************************************************/
29
30 effs_t ffs_initialize(void)
31 {
32 bref_t b;
33 struct inode_s *ip;
34 int i;
35
36 tlw(led_set(0));
37 tlw(led_on(LED_INIT));
38 ttw(str(TTrInit, "initialize {" NL));
39 tw(tr(TR_BEGIN, TrFsck, "ffs_initialize() {\n"));
40
41 // default to non-initialized ffs
42 fs.root = 0;
43 fs.debug[0] = fs.debug[1] = fs.debug[2] = fs.debug[3] = 0;
44 fs.testflags = 0;
45
46 tlw(led_on(LED_DRV_INIT));
47 fs.initerror = ffsdrv_init(); // read manufacturer and device ID
48 tlw(led_off(LED_DRV_INIT));
49 if (fs.initerror < 0) {
50 tlw(led_off(0));
51 tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
52 ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
53 return fs.initerror;
54 }
55
56 for (i = 0; i < 2; i++)
57 {
58 tlw(led_on(LED_BLOCKS_FSCK));
59 fs.initerror = EFFS_INVALID;
60 fs.initerror = b = blocks_fsck();
61 tlw(led_off(LED_BLOCKS_FSCK));
62 if (fs.initerror < 0) {
63 tlw(led_off(0));
64 tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
65 ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
66 return fs.initerror;
67 }
68
69 tlw(led_on(LED_INODES_FSCK));
70 fs.initerror = EFFS_INVALID;
71 fs.initerror = inodes_fsck();
72 tlw(led_off(LED_INODES_FSCK));
73 if (fs.initerror < 0) {
74 tlw(led_off(0));
75 tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
76 ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
77 return fs.initerror;
78 }
79
80 // parse the fs options in the root inode's name
81 ip = inode_addr(fs.root);
82 fs_params_init(addr2name(offset2addr(location2offset(ip->location))));
83
84 if ((fs.initerror = journal_init(fs.ijournal)) == 0)
85 break;
86 }
87
88 // Init all file_descriptors to zero
89 memset(fs.fd, 0, sizeof(struct file_descriptor_s) * fs.fd_max);
90
91 // If blocks_fsck() found a block that needs cleaning, we do it, now
92 // that all the file system has been initialized.
93 if (b > 0) {
94 block_clean(b - 1);
95 block_free(b - 1);
96 }
97
98 statistics_init();
99
100 // In target, we do this before entering the task event loop...
101 // Otherwise we would in some cases impose a long reboot delay if we did
102 // it here. If we test in target it is nessesary to call
103 // blocks_reclaim() anyway because we re-init ffs.
104
105 #if (TARGET == 1) //NOTEME: can this be done in another/better way?
106 #if (WITH_TFFS == 1)
107 blocks_reclaim();
108 #endif
109 #else
110 blocks_reclaim();
111 #endif
112 tlw(led_off(LED_INIT));
113 tw(tr(TR_END, TrFsck, "} %d\n", EFFS_OK));
114 ttw(str(TTrInit, "} 0" NL));
115
116 return EFFS_OK;
117 }
118
119 void fs_params_init(const char *p)
120 {
121 uint8 opt, digit;
122 uint32 n;
123 int numdatablocks;
124
125 tw(tr(TR_BEGIN, TrFsck, "fsparams_init('%s') {\n", p));
126
127 // Compiled default values
128 fs.filename_max = FFS_FILENAME_MAX;
129 fs.path_depth_max = FFS_PATH_DEPTH_MAX;
130 fs.fd_max = FFS_FD_MAX;
131 fs.journal_size = FFS_JOURNAL_SIZE_IN256THS;
132 fs.flags = 0;
133 fs.testflags = 0;
134
135 // Flag that it not has been changed by an input arg.
136 fs.block_files_max = 0;
137
138 // The default lost bytes percentage of a block before it is reclaimed
139 // is approx. 90%.
140 fs.lost_threshold = (256 - 256/10);
141
142 // If we only have two blocks, we cannot make any reclaims and thus we
143 // have a write-once FFS system.
144 fs.blocks_free_min = (dev.numblocks > 2 ? 1 : 0);
145
146 // Don't count free and inodes blocks
147 numdatablocks = dev.numblocks - fs.blocks_free_min - 1;
148
149 // Abselute max number of inodes.
150 fs.inodes_max = dev.blocksize / sizeof(struct inode_s);
151 if (fs.inodes_max > FFS_INODES_MAX)
152 fs.inodes_max = FFS_INODES_MAX;
153
154 // MUST be true: objects_max <= inodes_max - block_files_max, this is do
155 // to the fact that we always need to have block_files_max number of
156 // inodes left when we run a data reclaim.
157 fs.objects_max = fs.inodes_max / 2;
158
159 // Find a suitable chunk_size
160 if (dev.numblocks*dev.blocksize > 1024*1024)
161 fs.chunk_size_max = 8192;
162 else
163 fs.chunk_size_max = (2048 > (dev.blocksize / 8)
164 ? (dev.blocksize / 8)
165 : 2048);
166 fs.fd_buf_size = fs.chunk_size_max;
167
168 fs.journal_size = fs.journal_size * dev.blocksize / 256;
169 if (fs.journal_size < FFS_JOURNAL_SIZE_MIN)
170 fs.journal_size = FFS_JOURNAL_SIZE_MIN;
171
172 // Set it just below the same amount as entries in one journal file
173 fs.block_files_max = (fs.journal_size / sizeof(struct journal_s)
174 - FFS_JOURNAL_MARGIN - 2);
175
176 // MUST be true: block_files_max < objects_max / 2. But if we want
177 // to reach objects_max must block_files_max >= objects_max / number
178 // of datablocks, however a big block_files_max require higher
179 // reserved_space.
180 if (fs.block_files_max > fs.objects_max / 2)
181 fs.block_files_max = fs.objects_max / 2 - 4;
182
183 // Are we able to reach objects_max? If not then lower the number
184 if (fs.objects_max > numdatablocks * fs.block_files_max)
185 fs.objects_max = numdatablocks * fs.block_files_max + 10;
186
187 // Absolute minimum is RESERVED_LOW the rest is 'workspace' which is
188 // needed to have a reasonable performance.
189 fs.reserved_space = dev.blocksize / 2 +
190 numdatablocks * dev.blocksize / 16 + RESERVED_LOW;
191
192 // skip to first char following second slash in name
193 n = 0;
194 while (*p) {
195 if (*p++ == '/') {
196 n++;
197 if (n == 2)
198 break;
199 }
200 }
201 if (n == 2) {
202 // while still options to process...
203 while (*p) {
204 opt = *p++; // save option letter for later
205 // collect option value...
206 n = 0;
207 while ((digit = *p)) {
208 if (digit >= '0' && digit <= '9') {
209 n = 10 * n + digit - '0';
210 p++;
211 }
212 else
213 break;
214 }
215 switch (opt) {
216 case 'b': dev.numblocks = n; break;
217 case 'm': fs.blocks_free_min = n; break;
218 case 'i': fs.inodes_max = n; break;
219 case 'o': fs.objects_max = n; break;
220 case 'n': fs.filename_max = n; break;
221 case 'f': fs.block_files_max = n; break;
222 case 'd': fs.fd_max = n; break;
223 case 's': fs.fd_buf_size = n; break;
224 case 't': fs.lost_threshold = n; break;
225 case 'z': fs.flags = n; break;
226 case 'j': fs.journal_size = n; break;
227 case 'c': fs.chunk_size_max = n; break;
228 case 'r': fs.reserved_space = n; break;
229 // d = &fs.path_depth_max; // really necessary?
230 default:
231 break;
232 }
233 }
234 }
235
236 // Now recompute a few parameters based on adjusted values.
237
238 // No journal file thuse no reserved space.
239 if (fs.journal_size == 0) {
240 fs.block_files_max = fs.objects_max / 2;
241 fs.reserved_space = 0;
242 fs.block_files_reserved = 0;
243 }
244
245 else {
246 // If journal size is less than minimum must it have been changed by an
247 // input arg, recalculate.
248 if (fs.journal_size < FFS_JOURNAL_SIZE_MIN)
249 fs.journal_size = fs.journal_size * dev.blocksize / 256;
250
251 if (fs.reserved_space < RESERVED_LOW)
252 fs.reserved_space = fs.reserved_space * dev.blocksize / 256;
253
254 // Only one reserved is needed however we want a margin and set it to 2
255 fs.block_files_reserved = 2;
256 }
257
258 // Don't count free blocks, inode block, reserved space, block headers
259 // and the size of one filename.
260 fs.filesize_max = numdatablocks * dev.blocksize - fs.reserved_space -
261 numdatablocks * BHEADER_SIZE - FFS_FILENAME_MAX;
262
263 // Furthermore don't count the overhead from each chunk (alignment)
264 fs.filesize_max -= ((fs.filesize_max / fs.chunk_size_max) * dev.atomsize
265 + dev.atomsize);
266
267 // NOTEME: chunk_size_min is never used
268 fs.chunk_size_min = numdatablocks / fs.objects_max;
269
270 tw(tr(TR_FUNC, TrFsck, "dev.numblocks = %d\n", dev.numblocks));
271 tw(tr(TR_FUNC, TrFsck, "fs.blocks_free_min = %d\n", fs.blocks_free_min));
272 tw(tr(TR_FUNC, TrFsck, "fs.inodes_max = %d\n", fs.inodes_max));
273 tw(tr(TR_FUNC, TrFsck, "fs.objects_max = %d\n", fs.objects_max));
274 tw(tr(TR_FUNC, TrFsck, "fs.block_files_max = %d\n", fs.block_files_max));
275 tw(tr(TR_FUNC, TrFsck, "fs.block_files_reserved = %d\n", fs.block_files_reserved));
276 tw(tr(TR_FUNC, TrFsck, "fs.chunk_size_max = %d\n", fs.chunk_size_max));
277 tw(tr(TR_FUNC, TrFsck, "fs.filename_max = %d\n", fs.filename_max));
278 tw(tr(TR_FUNC, TrFsck, "fs.lost_threshold = %d\n", fs.lost_threshold));
279 tw(tr(TR_FUNC, TrFsck, "fs.path_depth_max = %d\n", fs.path_depth_max));
280 tw(tr(TR_FUNC, TrFsck, "fs.journal_size = %d\n", fs.journal_size));
281 tw(tr(TR_FUNC, TrFsck, "fs.reserved_space = %d\n", fs.reserved_space));
282 tw(tr(TR_FUNC, TrFsck, "fs.fd_max = %d\n", fs.fd_max));
283 tw(tr(TR_FUNC, TrFsck, "fs.fd_buf_size = 0x%02x\n", fs.fd_buf_size));
284 tw(tr(TR_FUNC, TrFsck, "fs.flags = 0x%02x\n", fs.flags));
285 tw(tr(TR_END, TrFsck, "}\n"));
286 }
287
288 // TODO: Finish pending commits/writes.
289 effs_t ffs_exit(void)
290 {
291 tw(tr(TR_FUNC, TrFsck, "exit() 0\n"));
292
293
294 return EFFS_OK;
295 }
296
297 #if 0 // Not used in this version
298 // Purely for core internal use; Read a file.
299 effs_t file_read_int(const char *path, void *src, int size)
300 {
301 if (fs.initerror != EFFS_OK)
302 return fs.initerror;
303
304 return object_read(path, src, size, 0);
305 }
306
307 // Purely for core internal use; Update a file.
308 effs_t file_update(const char *path, void *src, int size)
309 {
310 char *name;
311 iref_t i, dir;
312
313 if (fs.initerror != EFFS_OK)
314 return fs.initerror;
315
316 if ((i = object_lookup(path, &name, &dir)) < 0)
317 return i;
318
319 journal_begin(i);
320
321 if ((i = object_create(name, src, size, -dir)) < 0)
322 return i;
323
324 journal_end(0);
325
326 return EFFS_OK;
327 }
328 #endif
329
330 /******************************************************************************
331 * blocks_fsck()
332 ******************************************************************************/
333
334 blocksize_t block_used(bref_t b)
335 {
336 blocksize_t used;
337 uint32 *p, *q;
338
339 tlw(led_toggle(LED_BLOCKS_FSCK));
340
341 // We search backwards through block to find the last used byte and
342 // thus the total number of used bytes. Note that this code depends
343 // on the fact that an erased flash location is 0xFF!
344 p = (uint32 *) offset2addr(dev.binfo[b].offset);
345 for (q = p + dev.blocksize/4 - 4; q > p; q -= 4) {
346 if ( ~(q[0] & q[1] & q[2] & q[3]) )
347 break;
348 }
349
350 if ( ~(q[0] & q[1] & q[2] & q[3]) )
351 q += 4;
352 used = atomalign((char *) q - (char *) p);
353
354 tw(tr(TR_FUNC, TrFsckLow, "ffs_block_used(%d) %d\n", b, used));
355
356 return used;
357 }
358
359
360 age_t age_distance(age_t x, age_t y)
361 {
362 age_t a = x - y;
363
364 if (a > 0x8000)
365 a = -a;
366
367 tw(tr(TR_FUNC, TrFsckLow, "age_distance(%d, %d) %d\n", x, y, a));
368
369 return a;
370 }
371
372 // For each ffs block, we initialise the basic bstat array information,
373 // namely the number of used bytes. Also, we locate the inodes block and if
374 // a previous operation was interrupted by a powerfail, we clean it up.
375 //
376 // We return EFFS_OK if all is fine. If a positive integer is returned, it
377 // denotes a block that needs to be cleaned by block_clean() once FFS
378 // has been properly intialized (we actually return the block number + 1
379 // because otherwise it would clash with EFFS_OK return code). If no inodes
380 // block is found or another error occurs, we return the error code.
381 bref_t blocks_fsck(void)
382 {
383 bref_t b, b_to_clean, b_inode_lost;
384 int age_valid;
385 age_t age_min, age_max, age_dist, age_dist_min, age_dist_max;
386 struct block_header_s *bhp;
387 struct block_header_old_s *obhp;
388
389 ttw(str(TTrInitLow, "blocks_fsck {" NL));
390 tw(tr(TR_BEGIN, TrFsck, "blocks_fsck() {\n"));
391
392 // initialize ages to the illegal/unset value
393 age_min = age_max = age_dist = 0;
394
395 fs.format = 0;
396 fs.inodes = -1;
397 fs.newinodes = -1;
398 b_inode_lost = -1;
399 b_to_clean = EFFS_OK;
400
401 for (b = 0; b < dev.numblocks; b++)
402 {
403 tlw(led_toggle(LED_DRV_INIT));
404
405 // read block flags from flash
406 bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
407 obhp = (struct block_header_old_s *) bhp;
408
409 bstat[b].used = dev.blocksize;
410 bstat[b].lost = bstat[b].used;
411 bstat[b].flags = bhp->flags;
412 bstat[b].objects = 0;
413
414 age_valid = 0;
415
416 if (bhp->magic_low != BLOCK_MAGIC_LOW ||
417 bhp->magic_high != BLOCK_MAGIC_HIGH) {
418 // The block magic as bad! It *could* be because the flash
419 // memory map is incorrect or because another application has
420 // spuriously written to the flash or ... who knows what. First
421 // we check to see if the reason is that we are dealing with a
422 // (really) old ffs format version.
423 if (obhp->magic_low == OLD_BLOCK_MAGIC_LOW &&
424 obhp->magic_high == OLD_FFS_FORMAT_VERSION) {
425 tw(tr(TR_FUNC, TrFsck, "OLD "));
426 fs.format = obhp->magic_high;
427 // We simulate that all the blocks are data blocks, in order
428 // to have some well-defined state that preformat() can work
429 // on. Later we will return EFFS_BADFORMAT and otherwise
430 // leave everything as it is, *without* modifying anything!
431 bstat[b].flags = BF_IS_DATA;
432 }
433 else {
434 // Quickly test if block is in empty state. We do not make a
435 // full check with block_used() because that takes too
436 // long --- we let preformat() do that.
437 if (bhp->magic_low == FLASH_NULL16 &&
438 bhp->magic_high == FLASH_NULL16 &&
439 bhp->age == FLASH_NULL16 &&
440 bhp->version == FLASH_NULL16 &&
441 bhp->flags == FLASH_NULL16)
442 {
443 bstat[b].used = 0;
444 bstat[b].lost = 0;
445 bstat[b].flags = BF_IS_EMPTY;
446 tw(tr(TR_FUNC, TrFsck, "EMPTY "));
447 }
448 else {
449 // If the block is not free, it is probably corrupted.
450 // Thus we reset its age and free it.
451 tw(tr(TR_FUNC, TrFsck, "magic = 0x%08x\n",
452 bhp->magic_low | (bhp->magic_high << 16)));
453 ffsdrv.write_halfword(&bhp->age, 0);
454 block_free(b);
455 tw(tr(TR_FUNC, TrFsck, "BAD "));
456 }
457 }
458 }
459 else {
460 fs.format = bhp->version;
461 age_valid = 1;
462
463 if (!is_block(b, BF_IS_FREE)) {
464 bstat[b].used = block_used(b);
465 bstat[b].lost = bstat[b].used - BHEADER_SIZE;
466 }
467
468 if (is_block(b, BF_IS_FREE)) {
469 // The only case where we do not call block_used() is
470 // when the block is truly free.
471 bstat[b].used = 0;
472 bstat[b].lost = 0;
473 tw(tr(TR_FUNC, TrFsck, "FREE "));
474 ttw(ttr(TTrInitLow, "FREE" NL));
475
476 }
477 else if (is_block(b, BF_IS_DATA)) {
478 tw(tr(TR_FUNC, TrFsck, "DATA "));
479 ttw(ttr(TTrInitLow, "DATA" NL));
480 }
481 else if (is_block(b, BF_IS_CLEANING)) {
482 // Here we schedule a block_clean(). Note that we can
483 // and do not execute the block cleaning now, as the info
484 // that block_clean() needs is not at all ready at this
485 // point in the initialization. So we set a flag and then
486 // clean the block at the end of ffs_initialize()
487 tw(tr(TR_FUNC, TrFsck, "CLEANING "));
488 ttw(ttr(TTrInitLow, "CLEANING" NL));
489 b_to_clean = b + 1;
490 }
491 else if (is_block(b, BF_IS_COPYING)) {
492 tw(tr(TR_FUNC, TrFsck, "COPYING "));
493 ttw(ttr(TTrInitLow, "COPYING" NL));
494 fs.newinodes = b;
495 }
496 else if (is_block(b, BF_IS_INODES)) {
497 tw(tr(TR_FUNC, TrFsck, "INODES "));
498 ttw(ttr(TTrInitLow, "INODES" NL));
499 fs.inodes = b;
500 }
501 else if (is_block(b, BF_IS_INODES_LOST)) {
502 tw(tr(TR_FUNC, TrFsck, "INODESLOST"));
503 ttw(ttr(TTrInitLow, "INODESLOST" NL));
504 b_inode_lost = b;
505 }
506 else {
507 block_free(b);
508 tw(tr(TR_FUNC, TrFsck, "INVALID "));
509 ttw(ttr(TTrInitLow, "INVALID" NL));
510 }
511 }
512
513 tw(tr(TR_NULL, TrFsck, " %2d: (0x%05x) %02x, used = %6d\n",
514 b, dev.binfo[b].offset, bstat[b].flags & 0xFF, bstat[b].used));
515
516 if (age_valid) {
517 if (age_min == 0) {
518 // Initialize minimum and maximum block ages
519 age_min = age_max = bhp->age;
520 tw(tr(TR_FUNC, TrFsckLow, "age_min/max = %d\n", age_min));
521 }
522 else {
523 age_dist_min = age_distance(bhp->age, age_min);
524 age_dist_max = age_distance(bhp->age, age_max);
525 if (age_dist_min > age_dist ||
526 age_dist_max > age_dist) {
527 if (age_dist_max > age_dist_min) {
528 age_dist = age_dist_max;
529 age_min = bhp->age;
530 tw(tr(TR_FUNC, TrFsckLow, "age_min = %d (dist = %d)\n",
531 age_min, age_dist));
532 }
533 else {
534 age_dist = age_dist_min;
535 age_max = bhp->age;
536 tw(tr(TR_FUNC, TrFsckLow, "age_max = %d (dist = %d)\n",
537 age_max, age_dist));
538 }
539 }
540 }
541 }
542 }
543 tlw(led_off(LED_DRV_INIT));
544 tw(tr(TR_FUNC, TrFsck, "age min, max, max-min = %d, %d, %d\n",
545 age_min, age_max, (uint16) (age_max-age_min)));
546 // If age_max is untouched is is because all blocks were in the 'Empty'
547 // state. In this case we let the age be as it is (0xFFFF).
548 if (age_max == 0)
549 age_max = age_min = BLOCK_AGE_MAX;
550
551 // Handle age wrap around thus ensuring fs.age_max is set correctly. We
552 // have to type-cast the whole computation, otherwise it will be
553 // incorrect.
554 if ((age_t) (age_max - age_min) > 0x8000) {
555 age_dist = age_max;
556 age_max = age_min;
557 age_min = age_dist;
558 }
559
560 // save maximum age found for the case of a bad block that is going to
561 // be reclaimed later on by blocks_reclaim()
562 fs.age_max = age_max;
563
564 tw(tr(TR_FUNC, TrFsck, "fs.format = 0x%04x\n", fs.format));
565 tw(tr(TR_FUNC, TrFsck, "fs.inodes, newinodes = %d, %d\n",
566 fs.inodes, fs.newinodes));
567 ttw(ttr(TTrInit, "fs.inodes, newinodes = %d, %d" NL,
568 fs.inodes, fs.newinodes));
569 tw(tr(TR_FUNC, TrFsck, "age min, max = %d, %d\n", age_min, age_max));
570
571 // If any blocks were in the EMPTY state, now is the time to bring them
572 // into the FREE state. Note that we must only do this *after*
573 // fs.age_max has been initialized.
574 for (b = 0; b < dev.numblocks; b++) {
575 if (is_block(b, BF_IS_EMPTY)) {
576 if ((bstat[b].used = block_used(b)) == 0)
577 block_preformat(b, 0);
578 else
579 block_free(b);
580 }
581 }
582
583 if (fs.inodes >= 0) {
584 // The 'old' inode block is still valid thus we keep it.
585 if (fs.newinodes >= 0)
586 // The copying of inodes to the new block was not finished thus
587 // we free the block
588 block_free(fs.newinodes);
589 inodes_set(fs.inodes);
590 }
591 else {
592 // Copying must have been finished
593 if (fs.newinodes >= 0 && b_inode_lost >= 0) {
594 // The inode reclaim did finish but currently there is no valid
595 // inode block thus the operation must be finished by committing
596 // the new block as the valid inode block.
597 fs.inodes = b_inode_lost;
598 block_commit();
599 }
600 else {
601 // No old or new Inode block!
602 tw(tr(TR_END, TrFsck, "} %d\n", EFFS_NOFORMAT));
603 ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
604 return EFFS_NOFORMAT;
605 }
606 }
607
608 if ((fs.format >> 8) != (FFS_FORMAT_VERSION >> 8)) {
609 tw(tr(TR_END, TrFsck, "} %d\n", EFFS_BADFORMAT));
610 ttw(ttr(TTrInitLow, "} %d" NL, EFFS_BADFORMAT));
611 return EFFS_BADFORMAT;
612 }
613
614 // FIXME: Insert age sanity check; age distance must not be too big (> 2
615 // * FFS_AGE_DISTANCE)?
616
617 tw(tr(TR_END, TrFsck, "} %d\n", b_to_clean));
618 ttw(ttr(TTrInitLow, "} %d" NL, b_to_clean));
619
620 return b_to_clean;
621 }
622
623 // Set fs.inodes and fs.inodes_addr
624 void inodes_set(iref_t i)
625 {
626 fs.inodes = i;
627 fs.inodes_addr = (struct inode_s *)
628 (offset2addr(dev.binfo[fs.inodes].offset)
629 + dev.atomsize - sizeof(struct inode_s));
630 }
631
632
633 /******************************************************************************
634 * inodes_fsck()
635 ******************************************************************************/
636
637 // Now for each inode in the inodes block, update the bstat array
638 // information: free, used, objects. Also, locate the root inode. We could
639 // optimize this a little, because bstat[binodes].used gives an inidication
640 // of how many inodes are actually present in the system.
641 iref_t inodes_fsck(void)
642 {
643 iref_t i;
644 struct inode_s *ip;
645 char *addr;
646 bref_t block;
647
648 ttw(str(TTrInitLow, "inodes_fsck {" NL));
649 tw(tr(TR_BEGIN, TrFsck, "inodes_fsck() {\n"));
650 tw(tr(TR_FUNC, TrFsck, "inodes in block %d:\n", fs.inodes));
651
652 // the fields of the bstat entry for the inodes have the meaning:
653 // used = total number of used inodes (valid, erased, invalid)
654 // lost = total number of lost inodes (erased, invalid)
655 // objects = index of first free inode (used by inode_alloc())
656
657 fs.root = 0; // default to root inode not found
658 fs.ijournal = 0; // default to journal file inode not found
659 bstat[fs.inodes].objects = 1;
660 bstat[fs.inodes].used = 0;
661 bstat[fs.inodes].lost = 0;
662 fs.sequence = 0; // just for debug (fun)
663
664 // we must set some default value for this, so we set it to max possible!
665 fs.inodes_max = dev.blocksize / sizeof(struct inode_s);
666
667 ip = inode_addr(1);
668 tw(tr(TR_FUNC, TrFsck, " i addr cld sib seq upd flag size name\n"));
669 for (i = 1; i < fs.inodes_max; i++, ip++)
670 {
671 // just for debug (fun)
672 if (ip->sequence > fs.sequence)
673 fs.sequence = ip->sequence;
674
675 // compute block index and total data space occupied
676 block = offset2block(location2offset(ip->location));
677
678 // Only scan used inodes. blocks_fsck() accounted all used space as
679 // also being lost space, so now we subtract from the lost space,
680 // the space used by valid objects
681 if (ip->location != FLASH_NULL32)
682 {
683 bstat[fs.inodes].used++;
684
685 tw(tr(TR_FUNC, TrFsck, "%3d 0x%05X %3d %3d %4d %3d %s%s%s%s%s%s %6d %s\n",
686 i,
687 location2offset(ip->location),
688 ip->child, ip->sibling,
689 ip->sequence, ip->updates,
690 is_object(ip, OT_DIR) ? "d" : "",
691 is_object(ip, OT_LINK) ? "l" : "",
692 is_object(ip, OT_FILE) ? "f" : "",
693 is_object(ip, OT_SEGMENT) ? "s" : "",
694 is_object(ip, OT_ERASED) ? " " : "",
695 IS_BIT_SET(ip->flags, OF_READONLY) && !is_object(ip, OT_ERASED) ?
696 "r" : " ",
697 ip->size,
698 // Erased chunks do not have any name so we can not trace erased objects!
699 (ip->size && !is_object(ip, OT_SEGMENT) && !is_object(ip, OT_ERASED) ?
700 addr2name(offset2addr(location2offset(ip->location))) : "")
701 ));
702
703 if (is_object_valid(ip)) {
704 // This inode is valid, so we account the data space as used
705 // and the inode as used too.
706 bstat[block].lost -= ip->size;
707 bstat[block].objects++;
708 // test if this is the root inode. store index if it is.
709 if (!is_object(ip, OT_SEGMENT)) {
710 addr = addr2name(offset2addr(location2offset(ip->location)));
711 if (*addr == '/')
712 fs.root = i;
713 else if (*addr == '.' &&
714 ffs_strcmp(addr, FFS_JOURNAL_NAME) == 0) {
715 fs.ijournal = i;
716 }
717 }
718 }
719 else if (is_object(ip, OT_ERASED)) {
720 // this inode's data is deleted, so we account the data
721 // space as used and lost and the inode as lost too.
722 bstat[fs.inodes].lost++;
723 }
724 else {
725 // This is an invalid object, so we account the data space
726 // as used and lost and the inode as lost too. NOTEME: error
727 // what should we do? Perhaps we should record semi-lost
728 // inodes? Can we safely account for it here if this is an
729 // object to be recovered because another inode.copied is
730 // referring to this? Will used/lost etc. be updated
731 // correctly then?
732 bstat[fs.inodes].lost++;
733 tw(tr(TR_NULL, TrFsck, "(invalid = %d)\n", ip->flags & OT_MASK));
734 }
735 }
736 }
737 ttw(ttr(TTrInit, "fs.root=%d, journal=%d" NL, fs.root, fs.ijournal));
738 tw(tr(TR_END, TrFsck, "} used: %d, lost: %d, root: %d, journal: %d\n",
739 bstat[fs.inodes].used, bstat[fs.inodes].lost, fs.root, fs.ijournal));
740
741 fs.sequence++;
742
743 tw(tr_bstat());
744
745 if (fs.root == 0) {
746 ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
747 return EFFS_NOFORMAT;
748 }
749
750 ttw(str(TTrInitLow, "} 0" NL));
751
752 return EFFS_OK;
753 }
754
755
756 /******************************************************************************
757 * Preformat and format
758 ******************************************************************************/
759
760 // Prepare all blocks for fs_format(). Because ffs_is_formattable() has
761 // already been called prior to this function, we know that no sector erase
762 // is in progress! The blocks are prepared by putting them into the 'Free'
763 // state.
764 effs_t fs_preformat(void)
765 {
766 bref_t b;
767
768 ttw(str(TTrFormat, "preformat {" NL));
769 tw(tr(TR_BEGIN, TrFormat, "fs_preformat() {\n"));
770
771 // Mark ffs as being non-formatted from now on.
772 fs.root = 0;
773
774 // We must initialize bstat[fs.inodes].used and inodes_high, such that
775 // inodes_reclaim() isn't triggered in reclaim() on the following
776 // fs_format().
777 inodes_set(0);
778 bstat[fs.inodes].used = 0;
779 bstat[fs.inodes].lost = 0;
780 bstat[fs.inodes].objects = 0;
781
782 // While format is in progress, we make FFS inaccessible to other
783 // functions...
784 fs.initerror = EFFS_NOFORMAT;
785
786 if (dev.manufact == 0) {
787 b = EFFS_NODEVICE;
788 }
789 else {
790 for (b = 0; b < dev.numblocks; b++) {
791 if (is_block(b, BF_IS_EMPTY)) {
792 if ((bstat[b].used = block_used(b)) == 0)
793 block_preformat(b, 0);
794 else
795 block_free(b);
796 }
797 else if (!is_block(b, BF_IS_FREE)) {
798 block_free(b);
799 }
800 }
801 b = EFFS_OK;
802 }
803
804 tw(tr(TR_END, TrFormat, "} %d\n", b));
805 ttw(ttr(TTrFormat, "} %d" NL, b));
806
807 return b;
808 }
809
810 // Preformat a single block thus taking it from the 'Empty' state into
811 // 'Free' state.
812 void block_preformat(bref_t b, age_t age)
813 {
814 int set_age_max;
815 struct block_header_s *bhp =
816 (struct block_header_s *) offset2addr(dev.binfo[b].offset);
817
818 tw(tr(TR_BEGIN, TrFormat, "fs_block_preformat(%d, %d)\n", b, age));
819
820 if (age == 0) {
821 age = fs.age_max;
822 }
823 else {
824 // We schedule an update of fs.age_max. Due to proper handling of
825 // age wrap-around, we can not actually set it now.
826 set_age_max = (age == fs.age_max);
827 age++;
828 if (age == 0)
829 age++;
830 if (set_age_max) {
831 fs.age_max = age;
832 tw(tr(TR_FUNC, TrFormat, "new fs.age_max = %d\n", fs.age_max));
833 }
834 }
835
836 ffsdrv.write_halfword(&bhp->age, age);
837 ffsdrv.write_halfword(&bhp->version, FFS_FORMAT_VERSION);
838 ffsdrv.write_halfword(&bhp->magic_low, BLOCK_MAGIC_LOW);
839 ffsdrv.write_halfword(&bhp->magic_high, BLOCK_MAGIC_HIGH);
840
841 bstat[b].flags = BF_IS_EMPTY;
842 bstat[b].used = 0;
843 bstat[b].lost = 0;
844 bstat[b].objects = 0;
845
846 block_flags_write(b, BF_FREE);
847
848 tw(tr(TR_END, TrFormat, ""));
849 }
850
851 // After preformat() has erased two blocks, this function can be called to
852 // initialize ffs by writing fs data and metadata. Note that ffs_begin() is
853 // *not* called before this function in ffs.c. Otherwise we would never
854 // enter this function because fs.root is zero. NOTEME: this is also a bug
855 // as this means we risk that this operation is started while an erase (or a
856 // write) is in progress! How the flash device reacts to this is currently
857 // unknown.
858 effs_t fs_format(const char *name)
859 {
860 bref_t i, b;
861
862 ttw(str(TTrFormat, "format {" NL));
863 tw(tr(TR_BEGIN, TrFormat, "fs_format('%s') {\n", name));
864
865 // Initialize file system parameters. It should be safe to change these
866 // now, as the format cannot fail at this point onwards.
867 fs_params_init(name);
868
869 // Make the first block be the inodes block
870 if ((fs.inodes = block_alloc(1, BF_COPYING)) < 0)
871 return EFFS_AGAIN;
872 block_flags_write(fs.inodes, BF_INODES);
873 inodes_set(fs.inodes);
874
875 // Make all block as data blocks except from the free_min and inode block
876 for (i = 0; i < dev.numblocks - fs.blocks_free_min - 1; i++)
877 if ((b = block_alloc(0, BF_DATA)) < 0)
878 return EFFS_AGAIN;
879
880 // Restart object sequencing (debug feature only)
881 fs.sequence = 0;
882
883 // Create root directory
884 journal_begin(0);
885 if ((fs.root = object_create(name, 0, 0, 0)) < 0) {
886 tw(tr(TR_END, TrFormat, "} %d\n", fs.root));
887 return fs.root;
888 }
889 journal_commit(OT_DIR);
890
891 if ((fs.ijournal = journal_create(0)) < 0) {
892 tw(tr(TR_END, TrFormat, "} %d\n", fs.ijournal));
893 return fs.ijournal;
894 }
895
896 fs.initerror = ffs_initialize();
897
898 ttw(ttr(TTrFormat, "} %d" NL, fs.initerror));
899 tw(tr(TR_END, TrFormat, "} %d\n", fs.initerror));
900
901 return fs.initerror;
902 }
903
904 // Check if we are ready to preformat (flag = 0) or format (flag = 1)
905 //
906 // For a format, we must first ensure no blocks are valid e.g. a preformat
907 // has already been run. Next, we must ensure we have preformatted all
908 // blocks e.g. all blocks are in the 'Free' state. This is actually the same
909 // thing but it sure helps the user because it yields a more precise error
910 // code when the format fails. In future we might be able to start a format
911 // when only two blocks have been preformatted, but this is harder because
912 // we have to make sure not to read from the physical sector that we are
913 // erasing, and this is exactly what ffs_ffs_initialize() currently does
914 // (when it is called at the end of format()).
915 //
916 // For a preformat, we must ensure an erase is not in progress (because we
917 // don't know how the device will react to a new erase when an erase is
918 // currently suspended).
919 effs_t is_formattable(int8 flag)
920 {
921 bref_t i, free, valid;
922 effs_t error = EFFS_OK;
923
924 tw(tr(TR_FUNC, TrFormat, "is_formattable() "));
925
926 // Count the number of valid and free blocks. These numbers will later
927 // be checked to see if we are really ready for a (pre)format(). Note
928 // that we *only* read block flags from the bstat[] array. We must not
929 // read directly from the flash sectors because an erase might be in
930 // progress!
931 for (i = 0, free = 0, valid = 0; i < dev.numblocks; i++) {
932 if (is_block(i, BF_IS_DATA) || is_block(i, BF_IS_INODES))
933 valid++;
934 if (is_block(i, BF_IS_FREE))
935 free++;
936 }
937 if (flag == 0) {
938 // In the case of a preformat, ensure an erase is not in
939 // progress (because we don't know how the device will react to a new
940 // erase when an erase is currently suspended).
941 if (dev.state == DEV_ERASE || dev.state == DEV_ERASE_SUSPEND) {
942 tw(tr(TR_NULL, TrFormat, "(%d)\n", EFFS_AGAIN));
943 return EFFS_AGAIN;
944 }
945 }
946 else {
947 if (valid > 0)
948 // Ensure we have preformatted prior to a format.
949 error = EFFS_NOPREFORMAT;
950 else if (free < dev.numblocks)
951 // Ensure all blocks are free before a format(). If not, a
952 // preformat() is currently in progress.
953 error = EFFS_AGAIN;
954 }
955
956 tw(tr(TR_NULL, TrFormat, "(%d)\n", error));
957 return error;
958 }
959
960
961 /******************************************************************************
962 * Journalling
963 ******************************************************************************/
964
965 // The following matrix illustrates how the members of an inode change for
966 // the various (journalled) operations:
967 //
968 // | flags | size | loc | child | siblg | dir | oldi | updates
969 // ---------+-------+------+-----+-------+-------+-----+------+--------
970 // create | new | new | new | - | - | ins | n/a | 0
971 // fupdate | o | new | new | o | - | ins | del | old+1
972 // relocate | o | o | new | o | - | ins | del | old+1
973 // fctrl | new | o | o | o | - | ins | del | old+1
974 // remove | n/a | n/a | n/a | n/a | n/a | n/a | del | n/a
975 //
976 // - = leave empty (0xFFFF)
977 // ins = insert/append into directory
978 // o = old value
979 //
980 // We don't have to store child member in the journal entry because either
981 // it is EMPTY (fs.journal.oldi = 0) or it is retrieved from oldip->child.
982
983 // NOTEME: With journalling implemented, object_relocate might be able just
984 // to make a simple data copy!
985
986 // block_clean() is safe (without journalling), now that only ip->size is
987 // set to zero.
988
989 // Begin a new journal. Either a fresh object create (oldi == 0) or an
990 // update of an existing object (oldi == iref of old object)
991 void journal_begin(iref_t oldi)
992 {
993 tw(tr(TR_FUNC, TrJournal, "journal_begin(%d)\n", oldi));
994
995 fs.journal.i = 0;
996 fs.journal.state = JOURNAL_IS_EMPTY;
997 fs.journal.repli = 0;
998 fs.link_child = 1; //Default link child in journal_commit()
999
1000 if (oldi == 0) {
1001 fs.journal.flags = 0xFF;
1002 fs.journal.diri = 0;
1003 fs.journal.oldi = 0;
1004 fs.journal.location = 0;
1005 fs.journal.size = 0;
1006 }
1007 else {
1008 struct inode_s *oldip = inode_addr(oldi);
1009 fs.journal.flags = oldip->flags;
1010 fs.journal.diri = oldi;
1011 fs.journal.oldi = oldi;
1012 fs.journal.location = oldip->location;
1013 fs.journal.size = oldip->size;
1014 }
1015 }
1016
1017 // NOTEME: We have compressed the macro code because it will NOT compile on
1018 // Unix otherwise. So until we find out why, we use this as a work-around.
1019 #if (FFS_TEST == 1)
1020 #define JOURNAL_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_END, TrJournal, "} (" text ")\n")); return; }
1021 #else
1022 #define JOURNAL_TEST(testcase, text)
1023 #endif
1024
1025 // NOTEME: Should we empty journal file when we are anyway relocating it in
1026 // data_reclaim()?
1027 void journal_end(uint8 type)
1028 {
1029 struct inode_s *ip = inode_addr(fs.ijournal);
1030 struct journal_s *addr = (struct journal_s *)
1031 offset2addr(location2offset(ip->location) + fs.journal_pos);
1032
1033 tw(tr(TR_BEGIN, TrJournal, "journal_end(0x%x) {\n", type));
1034 tw(tr(TR_FUNC, TrJournal, "journal_pos = 0x%04x (%d)\n", fs.journal_pos,
1035 (fs.journal_pos - JOURNAL_POS_INITIAL) / sizeof(struct journal_s)));
1036
1037 // If this is a create, set the object type
1038 if (type != 0 && fs.journal.oldi == 0)
1039 fs.journal.flags = (fs.journal.flags & OF_MASK) | type;
1040
1041 // If there is no journal file, we can do without it, although we
1042 // certainly don't like it!
1043 if (fs.ijournal == 0) {
1044 journal_commit(0);
1045 tw(tr(TR_END, TrJournal, "} No jounal file\n"));
1046 return;
1047 }
1048
1049 JOURNAL_TEST(JOURNAL_TEST_EMPTY, "Oops in JOURNAL_IS_EMPTY");
1050
1051 // Write RAM journal to journal file.
1052 if (fs.journal.state == (uint8) JOURNAL_IS_EMPTY) {
1053 fs.journal.state = JOURNAL_IS_WRITING;
1054 ffsdrv.write(addr, &fs.journal, sizeof(fs.journal));
1055 }
1056
1057 JOURNAL_TEST(JOURNAL_TEST_WRITING, "Oops in JOURNAL_IS_WRITING");
1058
1059 // Advance journal file's state
1060 if (fs.journal.state == (uint8) JOURNAL_IS_WRITING) {
1061 fs.journal.state = JOURNAL_IS_READY;
1062 ffsdrv_write_byte(&addr->state, fs.journal.state);
1063 }
1064
1065 JOURNAL_TEST(JOURNAL_TEST_READY, "Oops in JOURNAL_IS_READY");
1066
1067 journal_commit(0);
1068
1069 JOURNAL_TEST(JOURNAL_TEST_COMMITTING, "Oops in JOURNAL_TEST_COMMITTING");
1070 JOURNAL_TEST(JOURNAL_TEST_COMMITTED, "Oops in JOURNAL_COMMITTED");
1071
1072 // Advance journal file's state
1073 ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);
1074
1075 JOURNAL_TEST(JOURNAL_TEST_DONE, "Oops in JOURNAL_IS_DONE");
1076
1077 // Advance journal
1078 fs.journal_pos += sizeof(struct journal_s);
1079
1080 // Unless we are currently relocating the journal file itself, check if
1081 // journal file is near full and relocate it if it is.
1082 if (fs.journal_pos >= fs.journal_size - FFS_JOURNAL_MARGIN *
1083 sizeof(struct journal_s) && fs.journal.oldi != fs.ijournal) {
1084 tw(tr(TR_FUNC, TrJournal, "Journal file (near) full!\n"));
1085 journal_create(fs.ijournal);
1086 }
1087
1088 // Check if we have just committed the journal file itself
1089 if (fs.journal.oldi == fs.ijournal) {
1090 fs.journal_pos = JOURNAL_POS_INITIAL;
1091 fs.ijournal = fs.journal.i;
1092 tw(tr(TR_FUNC, TrJournal, "Journal file re-created, fs.ijournal = %d\n",
1093 fs.ijournal));
1094 }
1095 tw(tr(TR_END, TrJournal, "}\n"));
1096 }
1097
1098 // Write contents of fs.journal to FFS meta data (inodes). Note that we do
1099 // NOT traverse ip->copied as we used to do in the old
1100 // object_update_commit(). Also, we do not check if object has been
1101 // erased after traversing ip->copied. All this code has been removed
1102 // because we will very soon have full callback functionality and thus the
1103 // code is redundant.
1104 void journal_commit(uint8 type)
1105 {
1106 struct inode_s *ip = inode_addr(fs.journal.i);
1107 struct inode_s *oldip = inode_addr(fs.journal.oldi);
1108 struct inode_s *dp;
1109 bref_t b;
1110
1111 tw(tr(TR_BEGIN, TrJournal, "journal_commit(%d) {\n", type));
1112 tw(tr(TR_FUNC, TrJournal, "i = %d\n", fs.journal.i));
1113 ttw(ttr(TTrObj, "jc(){" NL));
1114
1115 if (fs.journal.i)
1116 {
1117 // If this is a create, set the object type
1118 if (type != 0 && fs.journal.oldi == 0)
1119 fs.journal.flags = (fs.journal.flags & OF_MASK) | type;
1120
1121 tw(tr(TR_FUNC, TrJournal, "loc = 0x%04x, size = %d\n",
1122 fs.journal.location, fs.journal.size));
1123 ffsdrv.write((uint32 *) &ip->location, (uint32 *) &fs.journal.location, sizeof(location_t));
1124 ffsdrv.write_halfword((uint16 *) &ip->size, fs.journal.size);
1125
1126 if (fs.journal.oldi != 0 && fs.link_child != 0)
1127 // If this is an update, we copy the child member from old
1128 // inode. We must do this before we validate the new object,
1129 // otherwise an intermediate readdir() will detect an empty
1130 // directory!
1131 ffsdrv.write_halfword((uint16*) &ip->child, oldip->child);
1132
1133 tw(tr(TR_FUNC, TrJournal, "seq = %d\n", fs.sequence));
1134 // We must check if sequence is already written because if this
1135 // commit was inititiated by journal_init(), we don't know exactly
1136 // what was written
1137 if (ip->sequence == FLASH_NULL16)
1138 ffsdrv.write_halfword(&ip->sequence, fs.sequence++);
1139 if (fs.journal.oldi == 0)
1140 ffsdrv.write_halfword(&ip->updates, 0);
1141 else
1142 ffsdrv.write_halfword(&ip->updates, oldip->updates + 1);
1143
1144 JOURNAL_TEST(JOURNAL_TEST_COMMITTING, "Oops in JOURNAL_TEST_COMMITTING")
1145
1146 // Insert object into directory structure. We must do this before
1147 // deleting old object, otherwise an intermediate readdir() will
1148 // fail with EFFS_NOTFOUND. Note that when the root directory is
1149 // created, fs.journal.diri is zero --- thus the test!
1150 if (fs.journal.diri != 0) {
1151 tw(tr(TR_FUNC, TrJournal, "diri = %d ", fs.journal.diri));
1152 if (fs.journal.diri < 0) {
1153 tw(tr(TR_NULL, TrJournal, "child\n"));
1154 dp = inode_addr(-fs.journal.diri);
1155 ffsdrv.write_halfword((uint16 *) &dp->child, fs.journal.i);
1156 }
1157 else {
1158 tw(tr(TR_NULL, TrJournal, "sibling\n"));
1159 dp = inode_addr(fs.journal.diri);
1160 ffsdrv.write_halfword((uint16 *) &dp->sibling, fs.journal.i);
1161 }
1162 }
1163
1164 // The new object is validated before the old object is deleted.
1165 // This is in order to avoid an interrupting stat or read operation
1166 // to fail with EFFS_NOTFOUND
1167 tw(tr(TR_FUNC, TrJournal, "flags = 0x%02x\n", fs.journal.flags));
1168 ffsdrv_write_byte(&ip->flags, fs.journal.flags);
1169
1170 // Update bstat[] appropriately
1171 b = offset2block(location2offset(ip->location));
1172 bstat[b].objects++;
1173 tw(tr(TR_FUNC, TrJournal, "bstat[%d].objects = %d\n", b, bstat[b].objects));
1174 }
1175
1176 tw(tr(TR_FUNC, TrJournal, "oldi = %d\n", fs.journal.oldi));
1177 if (fs.journal.oldi != 0)
1178 {
1179 // If this is an update or an erase, we erase the old object
1180 ffsdrv_write_byte(&oldip->flags, OT_ERASED);
1181
1182 // Update bstat according to deletion of the old object.
1183 b = offset2block(location2offset(oldip->location));
1184 bstat[b].objects--;
1185 tw(tr(TR_FUNC, TrJournal, "bstat[%d].objects = %d\n", b, bstat[b].objects));
1186
1187 // If we moved the data (all cases, except fcontrol), update lost
1188 if (fs.journal.location != oldip->location)
1189 bstat[b].lost += oldip->size;
1190
1191 bstat[fs.inodes].lost++;
1192
1193 // If we renamed a file to an existing filename, remove the replaced file.
1194 if (fs.journal.repli > 0)
1195 object_remove(fs.journal.repli); // Ignore error!
1196 }
1197
1198 tw(tr(TR_END, TrJournal, "}\n"));
1199 ttw(ttr(TTrObj, "}" NL));
1200 }
1201
1202 // Save the current journal into "old" journal. We need this because an
1203 // object_create() can call data_reclaim() which can call object_relocate()
1204 // which uses the journal system.
1205 int journal_push(void)
1206 {
1207 memcpy(&fs.ojournal, &fs.journal, sizeof(struct journal_s));
1208 fs.journal_depth++;
1209 if (fs.journal_depth > 1) {
1210 tw(tr(TR_FUNC, TrAll, "FATAL: journal_push() to depth %d\n",
1211 fs.journal_depth));
1212 return -1;
1213 }
1214
1215 tw(tr(TR_FUNC, TrJournal, "journal_push() to depth %d\n",
1216 fs.journal_depth));
1217
1218 return EFFS_OK;
1219 }
1220
1221 // Recall "old" journal into current journal
1222 int journal_pop(void)
1223 {
1224 tw(tr(TR_FUNC, TrJournal, "journal_pop() from depth %d\n",
1225 fs.journal_depth));
1226
1227 fs.journal_depth--;
1228 if (fs.journal_depth < 0) {
1229 tw(tr(TR_FUNC, TrAll, "FATAL: journal_pop() to depth %d\n",
1230 fs.journal_depth));
1231 return -1;
1232 }
1233 memcpy(&fs.journal, &fs.ojournal, sizeof(struct journal_s));
1234
1235 return EFFS_OK;
1236 }
1237
1238 // Initialize the journalling system. Create journal file if it not already
1239 // exist. Commit/write pending journal if such exists --- return 1 in that
1240 // case. Otherwise, if journal file is clean (no journals pending) and all
1241 // is fine, return EFFS_OK.
1242 effs_t journal_init(iref_t i)
1243 {
1244 int j;
1245 struct inode_s *ip = inode_addr(i);
1246 struct journal_s *addr;
1247
1248 if (i == 0) {
1249 // Journal file does not exist, so create it
1250 if ((i = journal_create(0)) <= 0) {
1251 fs.ijournal = 0;
1252 return i;
1253 }
1254 }
1255
1256 fs.journal_depth = 0;
1257 fs.journal_pos = JOURNAL_POS_INITIAL;
1258
1259 addr = (struct journal_s *)
1260 offset2addr(location2offset(ip->location) + fs.journal_pos);
1261
1262 tw(tr(TR_BEGIN, TrJournal, "journal_init(%d) {\n", i));
1263
1264 fs.ijournal = i;
1265
1266 // Search for first non-completed journal entry.
1267 for (j = 0; /* FIXME: limit to end of journal */; j++, addr++) {
1268 if (addr->state != (uint8) JOURNAL_IS_DONE)
1269 break;
1270 }
1271 tw(tr(TR_FUNC, TrJournal, "entry %d is in state 0x%x\n", j, addr->state));
1272
1273 fs.journal_pos += j * sizeof(fs.journal);
1274 i = EFFS_OK;
1275
1276 if (addr->state == (uint8) JOURNAL_IS_EMPTY) {
1277 tw(tr(TR_FUNC, TrJournal, "Last journal is in EMPTY state\n"));
1278 // Journal file is proper, so just record position
1279 }
1280 else if (addr->state == (uint8) JOURNAL_IS_READY) {
1281 // Copy the entry into fs.journal.
1282 tw(tr(TR_FUNC, TrJournal, "Last journal is in READY state\n"));
1283 memcpy(&fs.journal, addr, sizeof(fs.journal));
1284 journal_end(0);
1285 i = 1;
1286 }
1287 else {
1288 // Journal entry wasn't finished, so just ignore it after updating
1289 // its state to JOURNAL_IS_DONE.
1290 tw(tr(TR_FUNC, TrJournal, "Last journal is between EMPTY and READY\n"));
1291 ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);
1292 fs.journal_pos += sizeof(fs.journal);
1293 }
1294
1295 if (ip->size != fs.journal_size + atomalign(sizeof(FFS_JOURNAL_NAME) + 1)) {
1296 tw(tr(TR_FUNC, TrJournal, "Wrong journal size, create new\n"));
1297 // Journal size do not match default size, so create new. This
1298 // should only happen if we use an old FFS image with a newer FFS
1299 // version.
1300 if ((i = journal_create(fs.ijournal)) <= 0) {
1301 fs.ijournal = 0;
1302 return i;
1303 }
1304 }
1305
1306 tw(tr(TR_FUNC, TrJournal, "journal_pos = 0x%04x\n", fs.journal_pos));
1307 tw(tr(TR_END, TrJournal, "} %d\n", i));
1308
1309 return i;
1310 }
1311
1312 // Create the journal file from scratch or relocate an existing one. It is
1313 // marked read-only just for clarity --- it cannot be deleted anyway!
1314 // fs_format() calls this function. Note that no data are written in
1315 // object_create() because the journal file is handled specially in that
1316 // function.
1317 iref_t journal_create(iref_t oldi)
1318 {
1319 iref_t i;
1320
1321 tw(tr(TR_BEGIN, TrJournal, "journal_create(%d) {\n", oldi));
1322 tw(tr(TR_FUNC, TrJournal, "journal file size = %d\n", fs.journal_size));
1323
1324 if (fs.journal_size == 0) {
1325 tw(tr(TR_FUNC, TrJournal, "Journal file creation aborted because fs.journal_size = 0 (No journal file wanted)\n"));
1326 tw(tr(TR_END, TrJournal, "} %d\n", 0));
1327 return 0;
1328 }
1329
1330 // If we are working on a write-once file system, we do not need a
1331 // journal.
1332 if (fs.blocks_free_min == 0) {
1333 tw(tr(TR_FUNC, TrJournal, "Journal file creation aborted because fs.blocks_free_min = 0 (write-once system)\n"));
1334 tw(tr(TR_END, TrJournal, "} %d\n", 0));
1335 return 0;
1336 }
1337
1338 journal_begin(oldi);
1339
1340 i = object_create(FFS_JOURNAL_NAME, 0, fs.journal_size, -fs.root);
1341 if (i < 0) {
1342 tw(tr(TR_END, TrJournal, "} %d\n", i));
1343 return i;
1344 }
1345 fs.journal.flags = BIT_SET(fs.journal.flags, OF_READONLY);
1346
1347 // commit the creation or relocation
1348 if (oldi != 0)
1349 journal_end(0);
1350 else {
1351 journal_commit(OT_FILE);
1352 fs.journal_pos = JOURNAL_POS_INITIAL;
1353 }
1354
1355 tw(tr(TR_END, TrJournal, "} %d\n", i));
1356
1357 return i;
1358 }
1359
1360 /******************************************************************************
1361 * FFS Begin and End
1362 ******************************************************************************/
1363
1364 // The following two functions should surround the code of every API
1365 // function in ffs.c (except preformat and format). The functions
1366 // ensures that the operation about to be executed can be made without
1367 // race-conditions or other problems.
1368 #if (TARGET == 0)
1369 int debug_suspend = 0;
1370 #endif
1371
1372
1373 // Check if ffs has been initialized. Suspend an erase operation.
1374 effs_t ffs_begin(void)
1375 {
1376 #if (TARGET == 0)
1377 if (debug_suspend > 0) {
1378 tw(tr(TR_FUNC, TrAll, "FATAL: Previous erase_suspend was not resumed\n"));
1379 return EFFS_CORRUPTED;
1380 }
1381 // tw(tr(TR_FUNC, TrHelper, "Set debug_suspend\n"));
1382 debug_suspend = 1;
1383 #endif
1384
1385 if (fs.initerror != EFFS_OK)
1386 return fs.initerror;
1387
1388 // Suspend an erase in progress (only applicable if we are using a
1389 // multi-bank device driver)
1390 if (dev.state == DEV_ERASE) {
1391 ffsdrv.erase_suspend();
1392 }
1393 else if (dev.state == DEV_WRITE) {
1394 ffsdrv.write_end();
1395 }
1396
1397 return EFFS_OK;
1398 }
1399
1400 // Resume an erase operation that was in progress.
1401 int ffs_end(int error)
1402 {
1403 #if (TARGET == 1)
1404 // Resume an erase in progress (only applicable if we are using a
1405 // multi-bank device driver)
1406 if (dev.state == DEV_ERASE_SUSPEND) {
1407 ffsdrv.erase_resume();
1408 }
1409 #else
1410 debug_suspend = 0;
1411 #endif
1412
1413 return error;
1414 }
1415
1416 /******************************************************************************
1417 * FFS Statistics functions
1418 ******************************************************************************/
1419
1420 // Not implemented:
1421 int statistics_file_create(void)
1422 {
1423 return 0;
1424 }
1425
1426 // Not implemented:
1427 // Rewrite the statistics file if it exists. Otherwise return error
1428 // code. The function is called after each data and inodes reclaim (after
1429 // writing the file that provoked the reclaim).
1430 int statistics_write(void)
1431 {
1432 return 0;
1433 }
1434
1435 // Read the statistics file if it exists. Otherwise reset all statistics to
1436 // zero and set the magic. This function is called from ffs_init().
1437 void statistics_init(void)
1438 {
1439 memset(&stats, 0, sizeof(struct ffs_stats_s));
1440 }
1441
1442 void statistics_update_drec(int valid, int lost, int candidate)
1443 {
1444 unsigned int old;
1445
1446 switch (candidate) {
1447 case MOST_LOST: stats.drec.most_lost++; break;
1448 case MOST_UNUSED: stats.drec.most_unused++; break;
1449 case YOUNGEST: stats.drec.youngest++; break;
1450 }
1451
1452 // Increment Most Significant Word if overflow is detected
1453 old = stats.drec.valid[0];
1454 stats.drec.valid[0] += valid;
1455 if (old > stats.drec.valid[0])
1456 stats.drec.valid[1]++;
1457
1458 old = stats.drec.lost[0];
1459 stats.drec.lost[0] += lost;
1460 if (old > stats.drec.lost[0])
1461 stats.drec.lost[1]++;
1462 }
1463
1464 void statistics_update_irec(int valid, int lost)
1465 {
1466 stats.irec.num++;
1467 stats.irec.valid += valid;
1468 stats.irec.lost += lost;
1469 }