2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id: validator.c 25132 2007-01-03 01:43:09Z neil $
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"
25 #include <dos/dostags.h>
26 #include <utility/tagitem.h>
27 #include <exec/ports.h>
28 #include <exec/types.h>
29 #include "afsblocks.h"
32 #include <exec/ports.h>
35 #include <aros/debug.h>
39 /*******************************************
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
)
51 if (vol
->state
== ID_VALIDATED
)
54 if (showError(afs
, ERR_DISKNOTVALID
))
56 if (vr_OK
== launchValidator(afs
, vol
))
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
72 Author: Tomasz Wiszkowski
73 ********************************************/
74 LONG
launchValidator(struct AFSBase
*afsbase
, struct Volume
*volume
)
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
);
93 /*****************************************
94 * this set is good only for AROS NATIVE
95 ****************************************/
98 LONG
validate(struct AFSBase
*afs
, struct Volume
*vol
)
101 ValidationResult res
= vr_OK
;
104 * fill in diskstructure. we will need it for the sake of validation.
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);
124 D(bug("[afs validate]: Validation complete. \n"));
127 D(bug("[afs validate]: Could not create access point. \n"));
130 D(bug("[afs validate]: Could not read disk. \n"));
132 case vr_UnknownDiskType
:
133 D(bug("[afs validate]: Unhandled disk type. \n"));
135 case vr_InvalidChksum
:
136 D(bug("[afs validate]: Invalid block checksum. \n"));
138 case vr_StructureDamaged
:
139 D(bug("[afs validate]: Structure damaged. \n"));
142 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
144 case vr_BlockUsedTwice
:
145 D(bug("[afs validate]: Physical block used more than once. \n"));
148 D(bug("[afs validate]: Aborted by user. \n"));
150 case vr_BlockOutsideDisk
:
151 D(bug("[afs validate]: Block outside volume boundaries. \n"));
156 struct BlockCache
*bc
= getBlock(afs
, vol
, vol
->rootblock
);
157 ULONG
* mem
= bc
->buffer
;
161 mem
[BLK_BITMAP_VALID_FLAG(vol
)] = 0;
162 vol
->state
= ID_VALIDATING
;
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
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
,
190 ds
->vol
->countblocks
));
191 showError(ds
->afs
, ERR_BLOCK_OUTSIDE_RANGE
, num
);
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
223 ULONG
verify_checksum(DiskStructure
*ds
, ULONG
*block
)
228 D(bug("[afs validate]: verifying block checksum... "));
230 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
232 sum
+= OS_BE2LONG(block
[i
]);
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
);
244 D(bug("checksum valid!\n"));
251 ULONG
verify_bm_checksum(DiskStructure
*ds
, ULONG
*block
)
256 D(bug("[afs validate]: verifying bitmap checksum... "));
258 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
260 sum
+= OS_BE2LONG(block
[i
]);
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
);
272 D(bug("checksum valid!\n"));
277 * check single block - this procedure should do as much as possible
279 ValidationResult
check_block(DiskStructure
* ds
, struct BlockCache
* block
)
285 * first check cache validity
289 D(bug("[afs validate] Error: no block passed.\n"));
293 if (0 == block
->blocknum
)
295 D(bug("[afs validate] Block could not be read.\n"));
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
) &&
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
)))
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
;
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
),
339 ULONG
* mem
= block
->buffer
;
343 for (i
=strt
; i
<=stop
; ++i
)
345 blk
= OS_BE2LONG(mem
[i
]);
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
);
394 stop
= ds
->vol
->SizeBlock
-2;
395 ext
= ds
->vol
->SizeBlock
-1;
403 ValidationResult
collect_file_extensions(DiskStructure
* ds
, struct BlockCache
* block
)
406 ULONG
* mem
= block
->buffer
;
412 for (i
=BLK_TABLE_END(ds
->vol
); i
>=BLK_TABLE_START
; --i
)
417 block
->flags
|= BCF_WRITE
;
421 blk
= OS_BE2LONG(mem
[i
]);
424 * if this was the last data block, purge rest
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
;
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
;
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
;
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
;
487 block
= getBlock(ds
->afs
, ds
->vol
, blk
);
493 * update sum; if changed, mark block for writing
495 if (0 != verify_checksum(ds
, block
->buffer
))
496 block
->flags
|= BCF_WRITE
;
504 * collect directories
506 ValidationResult
collect_directory_blocks(DiskStructure
*ds
, ULONG blk
)
508 struct BlockCache
*bc
;
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
);
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.
564 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
566 id
= collect_bitmap(ds
, bc
);
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
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
]);
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
)
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
);
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
)))
628 ds
->flags
|= ValFlg_DisableReq_DataLossImminent
;
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
)]);
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
)
679 * otherwise alter structure
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
;
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
;
703 for (i
=0; i
<ds
->bm_lastblk
; i
++)
705 bc
= getBlock(ds
->afs
, ds
->vol
, ds
->bm_blocks
[i
]);
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
)
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"));
753 ((UBYTE
*)ds
->bitmap
)[i
] = ~0;
762 void bm_free_bitmap(DiskStructure
* ds
)
764 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
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
;
784 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << (block
& 31))) == 0)
786 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << ((block
& 31)^24))) == 0)
789 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block
+ds
->vol
->bstartblock
)));
790 return st_AlreadyInUse
;
794 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << (block
& 31));
796 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << ((block
& 31)^24));
802 void bm_add_bitmap_block(DiskStructure
*ds
, ULONG blk
)
804 ds
->bm_blocks
[ds
->bm_lastblk
] = blk
;
808 void bm_add_bitmap_extension_block(DiskStructure
*ds
, ULONG blk
)
810 ds
->bme_blocks
[ds
->bme_lastblk
] = blk
;
816 /* vim: set noet:fdm=marker:fmr={,} :*/