added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / workbench / devs / afs / validator.c
blob1f9758d32e87c9fed0e58966a5e6dd1cf25e319c
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id: validator.c 25132 2007-01-03 01:43:09Z neil $
4 */
6 /*
7 * -date------ -name------------------- -description-----------------------------
8 * 02-jan-2008 [Tomasz Wiszkowski] created disk validation procedures
9 * 03-jan-2008 [Tomasz Wiszkowski] updated procedure to strip fake dircache blocks
10 * no directory cache handling present here.
11 * 04-jan-2008 [Tomasz Wiszkowski] corrected procedure to *ignore* data block sizes
12 * for OFS volumes since DOSTYPE does not differentiate them,
13 * corrected tabulation.
14 * 07-jan-2008 [Tomasz Wiszkowski] partitioned procedures to prepare non-recursive scan
17 #include "validator.h"
18 #include "volumes.h"
20 #undef SDEBUG
21 #undef DEBUG
22 #define DEBUG 0
24 #ifdef __AROS__
25 #include <dos/dostags.h>
26 #include <utility/tagitem.h>
27 #include <exec/ports.h>
28 #include <exec/types.h>
29 #include "afsblocks.h"
30 #include "cache.h"
31 #include "error.h"
32 #include <exec/ports.h>
33 #include <exec/io.h>
34 #include <dos/dos.h>
35 #include <aros/debug.h>
36 #include "misc.h"
37 #endif
39 /*******************************************
40 Name : checkValid
41 Descr : verify whether disk is validated and therefore writable
42 since we need at least a stub function, it has to be
43 declared outside __AROS__ scope
44 Input : afsbase, volume
45 Output: 0 for unvalidated disc, 1 otherwise
46 Author: Tomasz Wiszkowski
47 ********************************************/
48 LONG checkValid(struct AFSBase *afs, struct Volume *vol)
50 #ifdef __AROS__
51 if (vol->state == ID_VALIDATED)
52 return 1;
54 if (showError(afs, ERR_DISKNOTVALID))
56 if (vr_OK == launchValidator(afs, vol))
57 return 1;
59 return 0;
60 #else
61 return 1;
62 #endif
65 /*******************************************
66 Name : launchValidator
67 Descr : launch validation process for specified medium
68 since we need at least a stub function, it has to be
69 declared outside __AROS__ scope
70 Input : volume
71 Output: none
72 Author: Tomasz Wiszkowski
73 ********************************************/
74 LONG launchValidator(struct AFSBase *afsbase, struct Volume *volume)
76 #ifdef __AROS__
77 D(bug("[afs]: flushing cache...\n"));
78 flushCache(afsbase, volume);
81 * initially this was meant to be a synchronous validation
82 * but due to obvious problems with IO commands idea was eventually given up
84 return validate(afsbase, volume);
85 #else
86 return 0;
87 #endif
93 /*****************************************
94 * this set is good only for AROS NATIVE
95 ****************************************/
96 #ifdef __AROS__
98 LONG validate(struct AFSBase *afs, struct Volume *vol)
100 DiskStructure ds;
101 ValidationResult res = vr_OK;
104 * fill in diskstructure. we will need it for the sake of validation.
106 ds.vol = vol;
107 ds.afs = afs;
108 ds.flags = 0;
111 * we could use some inhibiting here
113 if (0 == bm_allocate_bitmap(&ds))
115 inhibit(afs, vol, 1);
116 res = start_superblock(&ds);
117 inhibit(afs, vol, 0);
119 bm_free_bitmap(&ds);
121 switch (res)
123 case vr_OK:
124 D(bug("[afs validate]: Validation complete. \n"));
125 break;
126 case vr_NoAccess:
127 D(bug("[afs validate]: Could not create access point. \n"));
128 break;
129 case vr_ReadError:
130 D(bug("[afs validate]: Could not read disk. \n"));
131 break;
132 case vr_UnknownDiskType:
133 D(bug("[afs validate]: Unhandled disk type. \n"));
134 break;
135 case vr_InvalidChksum:
136 D(bug("[afs validate]: Invalid block checksum. \n"));
137 break;
138 case vr_StructureDamaged:
139 D(bug("[afs validate]: Structure damaged. \n"));
140 break;
141 case vr_OutOfMemory:
142 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
143 break;
144 case vr_BlockUsedTwice:
145 D(bug("[afs validate]: Physical block used more than once. \n"));
146 break;
147 case vr_Aborted:
148 D(bug("[afs validate]: Aborted by user. \n"));
149 break;
150 case vr_BlockOutsideDisk:
151 D(bug("[afs validate]: Block outside volume boundaries. \n"));
152 break;
156 struct BlockCache *bc = getBlock(afs, vol, vol->rootblock);
157 ULONG* mem = bc->buffer;
159 if (res != vr_OK)
161 mem[BLK_BITMAP_VALID_FLAG(vol)] = 0;
162 vol->state = ID_VALIDATING;
164 else
165 vol->state = ID_VALIDATED;
167 if (verify_checksum(&ds, mem) != 0)
169 D(bug("[afs validate]: block checksum does not match.\n"));
170 bc->flags |= BCF_WRITE;
174 * it's not neccessary here, but res is holding validation result
175 * one may wish to open some requester at this point in the future
177 return res;
181 * validate range
183 LONG check_block_range(DiskStructure *ds, ULONG num)
185 if ((num < ds->vol->bstartblock) || (num >= ds->vol->countblocks))
187 D(bug("[afs validate]: Block located outside volume range. Condition %lu <= %lu < %lu not met.\n",
188 ds->vol->bstartblock,
189 num,
190 ds->vol->countblocks));
191 showError(ds->afs, ERR_BLOCK_OUTSIDE_RANGE, num);
192 return 0;
194 return 1;
198 * begin
200 ValidationResult start_superblock(DiskStructure *ds)
202 ValidationResult res = vr_OK;
204 res = collect_directory_blocks(ds, ds->vol->rootblock);
205 D(bug("[afs validate]: validation complete. Result: %ld\n", res));
208 * record bitmap back to disk, set bitmap valid flag, and update checksum of the root sector
209 * please note: it is hell important to have this checksum valid ;) so you better keep an eye
210 * on all your changes
212 if (res == vr_OK)
214 record_bitmap(ds);
217 return res;
221 * verify_checksum
223 ULONG verify_checksum(DiskStructure *ds, ULONG *block)
225 ULONG sum = 0;
226 LONG i;
228 D(bug("[afs validate]: verifying block checksum... "));
230 for (i=0; i<ds->vol->SizeBlock; i++)
232 sum += OS_BE2LONG(block[i]);
235 if (sum != 0)
237 D(bug("checksum invalid!\n"));
238 sum -= OS_BE2LONG(block[BLK_CHECKSUM]);
239 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block[BLK_CHECKSUM], sum));
240 block[BLK_CHECKSUM] = OS_LONG2BE(-sum);
241 return -sum;
244 D(bug("checksum valid!\n"));
245 return 0;
249 * verify_bm_checksum
251 ULONG verify_bm_checksum(DiskStructure *ds, ULONG *block)
253 ULONG sum = 0;
254 ULONG i;
256 D(bug("[afs validate]: verifying bitmap checksum... "));
258 for (i=0; i<ds->vol->SizeBlock; i++)
260 sum += OS_BE2LONG(block[i]);
263 if (sum != 0)
265 D(bug("checksum invalid!\n"));
266 sum -= OS_BE2LONG(block[0]);
267 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block[0], sum));
268 block[0] = OS_LONG2BE(-sum);
269 return -sum;
272 D(bug("checksum valid!\n"));
273 return 0;
277 * check single block - this procedure should do as much as possible
279 ValidationResult check_block(DiskStructure* ds, struct BlockCache* block)
281 ULONG* mem;
282 ULONG id;
285 * first check cache validity
287 if (0 == block)
289 D(bug("[afs validate] Error: no block passed.\n"));
290 return vr_ReadError;
293 if (0 == block->blocknum)
295 D(bug("[afs validate] Block could not be read.\n"));
296 return vr_ReadError;
299 mem = block->buffer;
302 * check types first: we expect block of T_SHORT (2) or T_LIST (16) type
304 id = OS_BE2LONG(mem[BLK_PRIMARY_TYPE]);
305 if ((id != T_SHORT) &&
306 (id != T_LIST))
308 D(bug("[afs validate]: block is not of known-type (%08lx). asking whether to correct\n", id));
310 if (block->blocknum == ds->vol->rootblock)
312 if ((0 == (ds->flags & ValFlg_DisableReq_MaybeNotAFS)) && (0 == showError(ds->afs, ERR_POSSIBLY_NOT_AFS)))
313 return vr_Aborted;
314 ds->flags |= ValFlg_DisableReq_MaybeNotAFS;
315 D(bug("[afs validate]: will continue per user decision. faulty block will be removed from structure.\n"));
317 return vr_StructureDamaged;
321 * set bitmap block (mark as used)
323 if (bm_mark_block(ds, block->blocknum))
325 D(bug("[afs validate]: block %lu used twice! aborting!\n", block->blocknum));
326 return vr_BlockUsedTwice;
329 return vr_OK;
332 ValidationResult collect_bitmap(DiskStructure* ds, struct BlockCache* block)
334 ULONG strt=BLK_BITMAP_POINTERS_START(ds->vol),
335 stop=BLK_BITMAP_POINTERS_END(ds->vol),
336 ext =BLK_BITMAP_EXTENSION(ds->vol),
337 i, blk;
339 ULONG* mem = block->buffer;
343 for (i=strt; i<=stop; ++i)
345 blk = OS_BE2LONG(mem[i]);
346 if (blk == 0)
347 continue;
350 * verify whether bitmap block resides on disk
351 * if the range validation fails, simply do not add the block to the list
353 * unfortunately bitmap block reallocation has not been implemented
354 * since this is the most unlikely happening issue
356 if (0 == check_block_range(ds, blk))
357 return vr_BlockOutsideDisk;
360 * mark block as used.
361 * the bitmap blocks *could be reallocated* (and in fact should be)
362 * if the bm_mark_block returns something else than st_OK (that is,
363 * in case, when block is out of range, or simply used twice).
365 * i'm sorry, but this has not been introduced yet.
367 if (bm_mark_block(ds, blk) != st_OK)
369 showError(ds->afs, ERR_BLOCK_USED_TWICE, blk);
370 return vr_BlockUsedTwice;
374 * add bitmap block to bitmap block set
376 bm_add_bitmap_block(ds, blk);
380 * collect bitmap extension blocks
382 if ((blk = OS_BE2LONG(mem[ext])) != 0)
384 D(bug("[afs validate] Following to next bitmap extension block at %08lx\n", blk));
385 if (0 == check_block_range(ds, blk))
386 return vr_BlockOutsideDisk;
388 if (bm_mark_block(ds, blk) != st_OK)
389 return vr_BlockUsedTwice;
391 block = getBlock(ds->afs, ds->vol, blk);
392 mem = block->buffer;
393 strt = 0;
394 stop = ds->vol->SizeBlock-2;
395 ext = ds->vol->SizeBlock-1;
398 while (blk != 0);
400 return vr_OK;
403 ValidationResult collect_file_extensions(DiskStructure* ds, struct BlockCache* block)
405 ULONG i, blk, clr=0;
406 ULONG* mem = block->buffer;
408 ds->file_blocks = 0;
412 for (i=BLK_TABLE_END(ds->vol); i>=BLK_TABLE_START; --i)
414 if (clr != 0)
416 mem[i] = 0;
417 block->flags |= BCF_WRITE;
418 continue;
421 blk = OS_BE2LONG(mem[i]);
424 * if this was the last data block, purge rest
426 if (blk == 0)
428 clr = 1;
429 continue;
434 * verify if block still belongs to this disk, purge if it doesn't
436 if (0 == check_block_range(ds, blk))
438 D(bug("[afs validate] file data block outside range. truncating\n"));
439 block->flags |= BCF_WRITE;
440 clr = 1;
441 mem[i] = 0;
442 continue;
446 * mark block as used.
448 if (bm_mark_block(ds, blk) != st_OK)
450 D(bug("[afs validate] file data block used twice. truncating\n"));
451 block->flags |= BCF_WRITE;
452 clr = 1;
453 mem[i] = 0;
454 continue;
457 ++ds->file_blocks;
461 * if block is marked as 'write', make sure it holds correct sum.
463 if (block->flags & BCF_WRITE)
464 verify_checksum(ds, block->buffer);
466 * collect bitmap extension blocks
468 if ((blk = OS_BE2LONG(mem[BLK_EXTENSION(ds->vol)])) != 0)
470 D(bug("[afs validate] Following to next file extension block at %08lx\n", blk));
471 if (0 == check_block_range(ds, blk))
473 D(bug("[afs validate] Extension block outside range. truncating file\n"));
474 mem[BLK_EXTENSION(ds->vol)] = 0;
475 block->flags |= BCF_WRITE;
476 blk = 0;
478 else if (bm_mark_block(ds, blk) != st_OK)
480 D(bug("[afs validate] Bitmap block already marked as used. truncating file\n"));
481 mem[BLK_EXTENSION(ds->vol)] = 0;
482 block->flags |= BCF_WRITE;
483 blk = 0;
485 else
487 block = getBlock(ds->afs, ds->vol, blk);
488 mem = block->buffer;
493 * update sum; if changed, mark block for writing
495 if (0 != verify_checksum(ds, block->buffer))
496 block->flags |= BCF_WRITE;
498 while (blk != 0);
500 return vr_OK;
504 * collect directories
506 ValidationResult collect_directory_blocks(DiskStructure *ds, ULONG blk)
508 struct BlockCache *bc;
509 LONG primary_type;
510 LONG entry_type;
511 ULONG id;
513 D(bug("[afs validate]: analyzing block %lu\n", blk));
516 * check range. Note that the whole process here is not 'reversible':
517 * if you mark bitmap blocks as used, and then just return in the middle,
518 * it won't get freed automagically, so mark it only when you know it should be there.
520 if (0 == check_block_range(ds, blk))
521 return vr_BlockOutsideDisk;
524 * we don't set block usage flags: we will re-read the block anyways.
526 bc = getBlock(ds->afs, ds->vol, blk);
529 * initial block analysis
531 id = check_block(ds, bc);
532 if (id != vr_OK)
533 return id;
536 * collect types
538 entry_type = OS_BE2LONG(bc->buffer[BLK_SECONDARY_TYPE(ds->vol)]);
539 primary_type = OS_BE2LONG(bc->buffer[BLK_PRIMARY_TYPE]);
542 * for root block: collect all bitmap blocks now
546 * i won't hide it: there's plenty of things to check here, but
547 * since it's just a validation task
548 * i have decided to remove the redundant code.
552 * ok, if anyone wants to remove the bitmap allocations and re-allocate them by himself
553 * here's the good place to start doing it. myself, i think the best way is to rely on
554 * operating system in this matter: unless someone damaged the disk structure, it's
555 * perfectly safe to assume that number of allocated bitmap blocks is just enough for us
556 * to store the bitmap. Again, it's not a salvaging, repairing NOR reorganizing task.
560 * oh, btw; we also want to collect the bitmap blocks here so we know where to save new bitmap.
562 if (entry_type == 1)
564 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
566 id = collect_bitmap(ds, bc);
567 if (id != vr_OK)
568 return id;
571 * initially -- mark bitmap status as valid
573 bc = getBlock(ds->afs, ds->vol, blk);
574 bc->buffer[BLK_BITMAP_VALID_FLAG(ds->vol)] = ~0;
575 verify_checksum(ds, bc->buffer);
576 bc->flags |= BCF_WRITE;
580 * this is the collection procedure
581 * we check for directories/files here only. if an entry is not a directory,
582 * check if it is a file and accumulate data blocks for it.
583 * here we are interested only in FOUR of SEVEN types: ROOT, DIRECTORY, FILE and FILE EXTENSION
585 if ((entry_type == 1) || (entry_type == 2)) // 1 = root, which is handled already, 3 = symlink, which we don't need, 4 = hardlink, which we don't want
587 LONG i;
590 * clear directory cache block.
591 * fancy thing about directory cache: it is the best way to run into inconsistencies between file trees.
592 * two trees, one kept for compatibility (which is not kept btw as dostype is different), and the other
593 * for 'faster directory listing', but not always in sync
595 if (bc->buffer[BLK_EXTENSION(ds->vol)] != 0)
597 D(bug("[afs validate]: clearing dircache pointer\n"));
598 bc->buffer[BLK_EXTENSION(ds->vol)] = 0;
599 verify_checksum(ds, bc->buffer);
600 bc->flags |= BCF_WRITE;
603 for (i=BLK_TABLE_START; i<=BLK_TABLE_END(ds->vol); i++)
605 id = OS_BE2LONG(bc->buffer[i]);
606 if (id != 0)
609 * proceed with block collection. unless user decides to stop validation
610 * move on and bug them about whatever is really bad
612 id = collect_directory_blocks(ds, id);
613 if (id == vr_Aborted)
614 return id;
617 * restore current block
618 * this will work well if the modified block gets marked as BCF_WRITE
620 bc = getBlock(ds->afs, ds->vol, blk);
622 if (vr_OK != id)
624 /* it's a good idea to flag this requester, so it shows only once */
625 if ((0 == (ds->flags & ValFlg_DisableReq_DataLossImminent)) && (0 == showError(ds->afs, ERR_DATA_LOSS_POSSIBLE)))
626 return vr_Aborted;
628 ds->flags |= ValFlg_DisableReq_DataLossImminent;
629 bc->buffer[i] = 0;
630 verify_checksum(ds, bc->buffer);
631 bc->flags |= BCF_WRITE;
638 * collect file data blocks.
639 * some files are too large to fit in a single list of data blocks
640 * this covers scanning more blocks
642 * crap, i don't know if i should use entry_type=0 here, too :|
644 if (entry_type == -3)
646 collect_file_extensions(ds, bc);
647 bc = getBlock(ds->afs, ds->vol, blk);
648 id = OS_BE2LONG(bc->buffer[BLK_BYTE_SIZE(ds->vol)]);
649 if (id > (ds->file_blocks * ds->vol->SizeBlock << 2))
651 bc->buffer[BLK_BYTE_SIZE(ds->vol)] = OS_LONG2BE(ds->file_blocks * ds->vol->SizeBlock << 2);
652 verify_checksum(ds, bc->buffer);
653 bc->flags |= BCF_WRITE;
658 * finally, move on to next file in this hash chain. IF next file suffers anything, remove it and following files from hash chain.
660 id = OS_BE2LONG(bc->buffer[BLK_HASHCHAIN(ds->vol)]);
661 if (id != 0)
663 D(bug("[afs validate]: collecting other items in this chain\n"));
666 * collect other elements
668 id = collect_directory_blocks(ds, id);
671 * if aborted, simply quit
673 if (id == vr_Aborted)
675 return id;
679 * otherwise alter structure
681 if (id != 0)
683 D(bug("[afs validate]: removing faulty chain\n"));
684 bc = getBlock(ds->afs, ds->vol, blk);
685 bc->buffer[BLK_HASHCHAIN(ds->vol)] = 0;
686 verify_checksum(ds, bc->buffer);
687 bc->flags |= BCF_WRITE;
691 return vr_OK;
695 * record bitmap: stores bitmap in RAM (if you redefine a little) and saves it to disk.
697 void record_bitmap(DiskStructure *ds)
699 struct BlockCache *bc;
700 ULONG *mem;
701 ULONG i;
703 for (i=0; i<ds->bm_lastblk; i++)
705 bc = getBlock(ds->afs, ds->vol, ds->bm_blocks[i]);
706 mem = bc->buffer;
708 CopyMemQuick(&((char*)ds->bitmap)[i*((ds->vol->SizeBlock-1)<<2)], &mem[1], (ds->vol->SizeBlock-1)<<2);
709 verify_bm_checksum(ds, mem);
710 bc->flags |= BCF_WRITE;
715 * allocates enough store for the complete bitmap
717 LONG bm_allocate_bitmap(DiskStructure *ds)
719 ULONG i;
720 ULONG bmap_blk_size;
723 * bitmap block size: blk_size - 4
725 bmap_blk_size = ((ds->vol->SizeBlock-1)<<2);
728 * total bitmap size is calculated as follows:
729 * - total number of bits (rounded up to nearest multiple of 8) converted to bytes
730 * - the above rounded up to nearest size of a single block - 4 bytes
732 i = (((ds->vol->countblocks + 7) >> 3) + (bmap_blk_size - 1)) / bmap_blk_size;
734 D(bug("[afs validate]: allocating bitmaps (%ld bytes)\n", i * bmap_blk_size));
735 ds->bitmap = AllocVec(i * bmap_blk_size, MEMF_ANY);
737 D(bug("[afs validate]: allocating storage for bitmap blocks (%ld bytes)\n", i * sizeof(ULONG)));
738 ds->bm_blocks = (ULONG*)AllocVec(i * sizeof(ULONG), MEMF_ANY | MEMF_CLEAR);
740 D(bug("[afs validate]: allocating storage for bitmap extension blocks (%ld bytes) - way more than we really need\n", i * sizeof(ULONG)));
741 ds->bme_blocks = (ULONG*)AllocVec(i * sizeof(ULONG), MEMF_ANY | MEMF_CLEAR);
743 if ((ds->bitmap == 0) || (ds->bm_blocks == 0) || (ds->bme_blocks == 0))
745 D(bug("[afs validate]: Unable to allocate memory for bitmap!\n"));
746 return -1;
749 i *= bmap_blk_size;
751 while (i--)
753 ((UBYTE*)ds->bitmap)[i] = ~0;
756 ds->bm_lastblk = 0;
757 ds->bme_lastblk = 0;
759 return 0;
762 void bm_free_bitmap(DiskStructure* ds)
764 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
765 if (ds->bitmap != 0)
766 FreeVec(ds->bitmap);
767 if (ds->bm_blocks != 0)
768 FreeVec(ds->bm_blocks);
769 if (ds->bme_blocks != 0)
770 FreeVec(ds->bme_blocks);
773 BitmapResult bm_mark_block(DiskStructure *ds, ULONG block)
775 if ((block < ds->vol->bstartblock) || (block >= ds->vol->countblocks))
777 D(bug("block %ld is out of disk area range\n", block));
778 return st_OutOfRange;
781 block -= ds->vol->bootblocks;
783 #if AROS_BIG_ENDIAN
784 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << (block & 31))) == 0)
785 #else
786 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << ((block & 31)^24))) == 0)
787 #endif
789 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block+ds->vol->bstartblock)));
790 return st_AlreadyInUse;
793 #if AROS_BIG_ENDIAN
794 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << (block & 31));
795 #else
796 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << ((block & 31)^24));
797 #endif
799 return st_OK;
802 void bm_add_bitmap_block(DiskStructure *ds, ULONG blk)
804 ds->bm_blocks[ds->bm_lastblk] = blk;
805 ds->bm_lastblk++;
808 void bm_add_bitmap_extension_block(DiskStructure *ds, ULONG blk)
810 ds->bme_blocks[ds->bme_lastblk] = blk;
811 ds->bme_lastblk++;
815 #endif
816 /* vim: set noet:fdm=marker:fmr={,} :*/