2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
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>
37 #include "extstrings.h"
40 /*******************************************
42 Descr : verify whether disk is validated and writable.
43 since we need at least a stub function, it has to be
44 declared outside __AROS__ scope
45 Input : afsbase, volume
46 Output: 0 for unvalidated disc, 1 otherwise
47 Author: Tomasz Wiszkowski
48 ********************************************/
49 LONG
checkValid(struct AFSBase
*afs
, struct Volume
*vol
)
52 struct BlockCache
*blockbuffer
;
53 blockbuffer
= getBlock(afs
, vol
, vol
->rootblock
);
54 UBYTE n
[MAX_NAME_LENGTH
];
56 name
=(STRPTR
)((char *)blockbuffer
->buffer
+(BLK_DISKNAME_START(vol
)*4));
57 StrCpyFromBstr(name
, n
);
58 while (vol
->state
== ID_WRITE_PROTECTED
59 && showError(afs
, ERR_WRITEPROTECT
, n
));
61 if (vol
->state
== ID_VALIDATING
)
63 if (showError(afs
, ERR_DISKNOTVALID
))
65 if (vr_OK
== launchValidator(afs
, vol
))
71 return vol
->state
== ID_VALIDATED
? 1 : 0;
77 /*******************************************
78 Name : launchValidator
79 Descr : launch validation process for specified medium
80 since we need at least a stub function, it has to be
81 declared outside __AROS__ scope
84 Author: Tomasz Wiszkowski
85 ********************************************/
86 LONG
launchValidator(struct AFSBase
*afsbase
, struct Volume
*volume
)
89 D(bug("[afs]: flushing cache...\n"));
90 flushCache(afsbase
, volume
);
93 * initially this was meant to be a synchronous validation
94 * but due to obvious problems with IO commands idea was eventually given up
96 return validate(afsbase
, volume
);
105 /*****************************************
106 * this set is good only for AROS NATIVE
107 ****************************************/
110 LONG
validate(struct AFSBase
*afs
, struct Volume
*vol
)
113 ValidationResult res
= vr_OK
;
116 * fill in diskstructure. we will need it for the sake of validation.
123 * we could use some inhibiting here
125 if (0 == bm_allocate_bitmap(&ds
))
127 inhibit(afs
, vol
, 1);
128 res
= start_superblock(&ds
);
129 inhibit(afs
, vol
, 0);
136 D(bug("[afs validate]: Validation complete. \n"));
139 D(bug("[afs validate]: Could not create access point. \n"));
142 D(bug("[afs validate]: Could not read disk. \n"));
144 case vr_UnknownDiskType
:
145 D(bug("[afs validate]: Unhandled disk type. \n"));
147 case vr_InvalidChksum
:
148 D(bug("[afs validate]: Invalid block checksum. \n"));
150 case vr_StructureDamaged
:
151 D(bug("[afs validate]: Structure damaged. \n"));
154 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
156 case vr_BlockUsedTwice
:
157 D(bug("[afs validate]: Physical block used more than once. \n"));
160 D(bug("[afs validate]: Aborted by user. \n"));
162 case vr_BlockOutsideDisk
:
163 D(bug("[afs validate]: Block outside volume boundaries. \n"));
168 struct BlockCache
*bc
= getBlock(afs
, vol
, vol
->rootblock
);
169 ULONG
* mem
= bc
->buffer
;
173 mem
[BLK_BITMAP_VALID_FLAG(vol
)] = 0;
174 vol
->state
= ID_VALIDATING
;
177 vol
->state
= ID_VALIDATED
;
179 if (verify_checksum(&ds
, mem
) != 0)
181 D(bug("[afs validate]: block checksum does not match.\n"));
182 bc
->flags
|= BCF_WRITE
;
186 * it's not neccessary here, but res is holding validation result
187 * one may wish to open some requester at this point in the future
195 LONG
check_block_range(DiskStructure
*ds
, ULONG num
)
197 if ((num
< ds
->vol
->bstartblock
) || (num
>= ds
->vol
->countblocks
))
199 D(bug("[afs validate]: Block located outside volume range. Condition %lu <= %lu < %lu not met.\n",
200 ds
->vol
->bstartblock
,
202 ds
->vol
->countblocks
));
203 showError(ds
->afs
, ERR_BLOCK_OUTSIDE_RANGE
, num
);
212 ValidationResult
start_superblock(DiskStructure
*ds
)
214 ValidationResult res
= vr_OK
;
216 res
= collect_directory_blocks(ds
, ds
->vol
->rootblock
);
217 D(bug("[afs validate]: validation complete. Result: %ld\n", res
));
220 * record bitmap back to disk, set bitmap valid flag, and update checksum of the root sector
221 * please note: it is hell important to have this checksum valid ;) so you better keep an eye
222 * on all your changes
235 ULONG
verify_checksum(DiskStructure
*ds
, ULONG
*block
)
240 D(bug("[afs validate]: verifying block checksum... "));
242 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
244 sum
+= OS_BE2LONG(block
[i
]);
249 D(bug("checksum invalid!\n"));
250 sum
-= OS_BE2LONG(block
[BLK_CHECKSUM
]);
251 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block
[BLK_CHECKSUM
], sum
));
252 block
[BLK_CHECKSUM
] = OS_LONG2BE(-sum
);
256 D(bug("checksum valid!\n"));
263 ULONG
verify_bm_checksum(DiskStructure
*ds
, ULONG
*block
)
268 D(bug("[afs validate]: verifying bitmap checksum... "));
270 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
272 sum
+= OS_BE2LONG(block
[i
]);
277 D(bug("checksum invalid!\n"));
278 sum
-= OS_BE2LONG(block
[0]);
279 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block
[0], sum
));
280 block
[0] = OS_LONG2BE(-sum
);
284 D(bug("checksum valid!\n"));
289 * check single block - this procedure should do as much as possible
291 ValidationResult
check_block(DiskStructure
* ds
, struct BlockCache
* block
)
297 * first check cache validity
301 D(bug("[afs validate] Error: no block passed.\n"));
305 if (0 == block
->blocknum
)
307 D(bug("[afs validate] Block could not be read.\n"));
314 * check types first: we expect block of T_SHORT (2) or T_LIST (16) type
316 id
= OS_BE2LONG(mem
[BLK_PRIMARY_TYPE
]);
317 if ((id
!= T_SHORT
) &&
320 D(bug("[afs validate]: block is not of known-type (%08lx). asking whether to correct\n", id
));
322 if (block
->blocknum
== ds
->vol
->rootblock
)
324 if ((0 == (ds
->flags
& ValFlg_DisableReq_MaybeNotAFS
)) && (0 == showError(ds
->afs
, ERR_POSSIBLY_NOT_AFS
)))
326 ds
->flags
|= ValFlg_DisableReq_MaybeNotAFS
;
327 D(bug("[afs validate]: will continue per user decision. faulty block will be removed from structure.\n"));
329 return vr_StructureDamaged
;
333 * set bitmap block (mark as used)
335 if (bm_mark_block(ds
, block
->blocknum
))
337 D(bug("[afs validate]: block %lu used twice! aborting!\n", block
->blocknum
));
338 return vr_BlockUsedTwice
;
344 ValidationResult
collect_bitmap(DiskStructure
* ds
, struct BlockCache
* block
)
346 ULONG strt
=BLK_BITMAP_POINTERS_START(ds
->vol
),
347 stop
=BLK_BITMAP_POINTERS_END(ds
->vol
),
348 ext
=BLK_BITMAP_EXTENSION(ds
->vol
),
351 ULONG
* mem
= block
->buffer
;
355 for (i
=strt
; i
<=stop
; ++i
)
357 blk
= OS_BE2LONG(mem
[i
]);
362 * verify whether bitmap block resides on disk
363 * if the range validation fails, simply do not add the block to the list
365 * unfortunately bitmap block reallocation has not been implemented
366 * since this is the most unlikely happening issue
368 if (0 == check_block_range(ds
, blk
))
369 return vr_BlockOutsideDisk
;
372 * mark block as used.
373 * the bitmap blocks *could be reallocated* (and in fact should be)
374 * if the bm_mark_block returns something else than st_OK (that is,
375 * in case, when block is out of range, or simply used twice).
377 * i'm sorry, but this has not been introduced yet.
379 if (bm_mark_block(ds
, blk
) != st_OK
)
381 showError(ds
->afs
, ERR_BLOCK_USED_TWICE
, blk
);
382 return vr_BlockUsedTwice
;
386 * add bitmap block to bitmap block set
388 bm_add_bitmap_block(ds
, blk
);
392 * collect bitmap extension blocks
394 if ((blk
= OS_BE2LONG(mem
[ext
])) != 0)
396 D(bug("[afs validate] Following to next bitmap extension block at %08lx\n", blk
));
397 if (0 == check_block_range(ds
, blk
))
398 return vr_BlockOutsideDisk
;
400 if (bm_mark_block(ds
, blk
) != st_OK
)
401 return vr_BlockUsedTwice
;
403 block
= getBlock(ds
->afs
, ds
->vol
, blk
);
406 stop
= ds
->vol
->SizeBlock
-2;
407 ext
= ds
->vol
->SizeBlock
-1;
415 ValidationResult
collect_file_extensions(DiskStructure
* ds
, struct BlockCache
* block
)
418 ULONG
* mem
= block
->buffer
;
424 for (i
=BLK_TABLE_END(ds
->vol
); i
>=BLK_TABLE_START
; --i
)
429 block
->flags
|= BCF_WRITE
;
433 blk
= OS_BE2LONG(mem
[i
]);
436 * if this was the last data block, purge rest
446 * verify if block still belongs to this disk, purge if it doesn't
448 if (0 == check_block_range(ds
, blk
))
450 D(bug("[afs validate] file data block outside range. truncating\n"));
451 block
->flags
|= BCF_WRITE
;
458 * mark block as used.
460 if (bm_mark_block(ds
, blk
) != st_OK
)
462 D(bug("[afs validate] file data block used twice. truncating\n"));
463 block
->flags
|= BCF_WRITE
;
473 * if block is marked as 'write', make sure it holds correct sum.
475 if (block
->flags
& BCF_WRITE
)
476 verify_checksum(ds
, block
->buffer
);
478 * collect bitmap extension blocks
480 if ((blk
= OS_BE2LONG(mem
[BLK_EXTENSION(ds
->vol
)])) != 0)
482 D(bug("[afs validate] Following to next file extension block at %08lx\n", blk
));
483 if (0 == check_block_range(ds
, blk
))
485 D(bug("[afs validate] Extension block outside range. truncating file\n"));
486 mem
[BLK_EXTENSION(ds
->vol
)] = 0;
487 block
->flags
|= BCF_WRITE
;
490 else if (bm_mark_block(ds
, blk
) != st_OK
)
492 D(bug("[afs validate] Bitmap block already marked as used. truncating file\n"));
493 mem
[BLK_EXTENSION(ds
->vol
)] = 0;
494 block
->flags
|= BCF_WRITE
;
499 block
= getBlock(ds
->afs
, ds
->vol
, blk
);
505 * update sum; if changed, mark block for writing
507 if (0 != verify_checksum(ds
, block
->buffer
))
508 block
->flags
|= BCF_WRITE
;
516 * collect directories
518 ValidationResult
collect_directory_blocks(DiskStructure
*ds
, ULONG blk
)
520 struct BlockCache
*bc
;
525 D(bug("[afs validate]: analyzing block %lu\n", blk
));
528 * check range. Note that the whole process here is not 'reversible':
529 * if you mark bitmap blocks as used, and then just return in the middle,
530 * it won't get freed automagically, so mark it only when you know it should be there.
532 if (0 == check_block_range(ds
, blk
))
533 return vr_BlockOutsideDisk
;
536 * we don't set block usage flags: we will re-read the block anyways.
538 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
541 * initial block analysis
543 id
= check_block(ds
, bc
);
550 entry_type
= OS_BE2LONG(bc
->buffer
[BLK_SECONDARY_TYPE(ds
->vol
)]);
551 primary_type
= OS_BE2LONG(bc
->buffer
[BLK_PRIMARY_TYPE
]);
553 switch (primary_type
) {
561 /* TODO: handle unknown primary type */
566 * for root block: collect all bitmap blocks now
570 * i won't hide it: there's plenty of things to check here, but
571 * since it's just a validation task
572 * i have decided to remove the redundant code.
576 * ok, if anyone wants to remove the bitmap allocations and re-allocate them by himself
577 * here's the good place to start doing it. myself, i think the best way is to rely on
578 * operating system in this matter: unless someone damaged the disk structure, it's
579 * perfectly safe to assume that number of allocated bitmap blocks is just enough for us
580 * to store the bitmap. Again, it's not a salvaging, repairing NOR reorganizing task.
584 * oh, btw; we also want to collect the bitmap blocks here so we know where to save new bitmap.
588 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
590 id
= collect_bitmap(ds
, bc
);
595 * initially -- mark bitmap status as valid
597 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
598 bc
->buffer
[BLK_BITMAP_VALID_FLAG(ds
->vol
)] = ~0;
599 verify_checksum(ds
, bc
->buffer
);
600 bc
->flags
|= BCF_WRITE
;
604 * this is the collection procedure
605 * we check for directories/files here only. if an entry is not a directory,
606 * check if it is a file and accumulate data blocks for it.
607 * here we are interested only in FOUR of SEVEN types: ROOT, DIRECTORY, FILE and FILE EXTENSION
609 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
614 * clear directory cache block.
615 * fancy thing about directory cache: it is the best way to run into inconsistencies between file trees.
616 * two trees, one kept for compatibility (which is not kept btw as dostype is different), and the other
617 * for 'faster directory listing', but not always in sync
619 if (bc
->buffer
[BLK_EXTENSION(ds
->vol
)] != 0)
621 D(bug("[afs validate]: clearing dircache pointer\n"));
622 bc
->buffer
[BLK_EXTENSION(ds
->vol
)] = 0;
623 verify_checksum(ds
, bc
->buffer
);
624 bc
->flags
|= BCF_WRITE
;
627 for (i
=BLK_TABLE_START
; i
<=BLK_TABLE_END(ds
->vol
); i
++)
629 id
= OS_BE2LONG(bc
->buffer
[i
]);
633 * proceed with block collection. unless user decides to stop validation
634 * move on and bug them about whatever is really bad
636 id
= collect_directory_blocks(ds
, id
);
637 if (id
== vr_Aborted
)
641 * restore current block
642 * this will work well if the modified block gets marked as BCF_WRITE
644 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
648 /* it's a good idea to flag this requester, so it shows only once */
649 if ((0 == (ds
->flags
& ValFlg_DisableReq_DataLossImminent
)) && (0 == showError(ds
->afs
, ERR_DATA_LOSS_POSSIBLE
)))
652 ds
->flags
|= ValFlg_DisableReq_DataLossImminent
;
654 verify_checksum(ds
, bc
->buffer
);
655 bc
->flags
|= BCF_WRITE
;
662 * collect file data blocks.
663 * some files are too large to fit in a single list of data blocks
664 * this covers scanning more blocks
666 * crap, i don't know if i should use entry_type=0 here, too :|
668 if (entry_type
== -3)
670 collect_file_extensions(ds
, bc
);
671 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
672 id
= OS_BE2LONG(bc
->buffer
[BLK_BYTE_SIZE(ds
->vol
)]);
673 if (id
> (ds
->file_blocks
* ds
->vol
->SizeBlock
<< 2))
675 bc
->buffer
[BLK_BYTE_SIZE(ds
->vol
)] = OS_LONG2BE(ds
->file_blocks
* ds
->vol
->SizeBlock
<< 2);
676 verify_checksum(ds
, bc
->buffer
);
677 bc
->flags
|= BCF_WRITE
;
682 * finally, move on to next file in this hash chain. IF next file suffers anything, remove it and following files from hash chain.
684 id
= OS_BE2LONG(bc
->buffer
[BLK_HASHCHAIN(ds
->vol
)]);
687 D(bug("[afs validate]: collecting other items in this chain\n"));
690 * collect other elements
692 id
= collect_directory_blocks(ds
, id
);
695 * if aborted, simply quit
697 if (id
== vr_Aborted
)
703 * otherwise alter structure
707 D(bug("[afs validate]: removing faulty chain\n"));
708 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
709 bc
->buffer
[BLK_HASHCHAIN(ds
->vol
)] = 0;
710 verify_checksum(ds
, bc
->buffer
);
711 bc
->flags
|= BCF_WRITE
;
719 * record bitmap: stores bitmap in RAM (if you redefine a little) and saves it to disk.
721 void record_bitmap(DiskStructure
*ds
)
723 struct BlockCache
*bc
;
727 for (i
=0; i
<ds
->bm_lastblk
; i
++)
729 bc
= getBlock(ds
->afs
, ds
->vol
, ds
->bm_blocks
[i
]);
732 CopyMemQuick(&((char*)ds
->bitmap
)[i
*((ds
->vol
->SizeBlock
-1)<<2)], &mem
[1], (ds
->vol
->SizeBlock
-1)<<2);
733 verify_bm_checksum(ds
, mem
);
734 bc
->flags
|= BCF_WRITE
;
739 * allocates enough store for the complete bitmap
741 LONG
bm_allocate_bitmap(DiskStructure
*ds
)
747 * bitmap block size: blk_size - 4
749 bmap_blk_size
= ((ds
->vol
->SizeBlock
-1)<<2);
752 * total bitmap size is calculated as follows:
753 * - total number of bits (rounded up to nearest multiple of 8) converted to bytes
754 * - the above rounded up to nearest size of a single block - 4 bytes
756 i
= (((ds
->vol
->countblocks
+ 7) >> 3) + (bmap_blk_size
- 1)) / bmap_blk_size
;
758 D(bug("[afs validate]: allocating bitmaps (%ld bytes)\n", i
* bmap_blk_size
));
759 ds
->bitmap
= AllocVec(i
* bmap_blk_size
, MEMF_ANY
);
761 D(bug("[afs validate]: allocating storage for bitmap blocks (%ld bytes)\n", i
* sizeof(ULONG
)));
762 ds
->bm_blocks
= (ULONG
*)AllocVec(i
* sizeof(ULONG
), MEMF_ANY
| MEMF_CLEAR
);
764 D(bug("[afs validate]: allocating storage for bitmap extension blocks (%ld bytes) - way more than we really need\n", i
* sizeof(ULONG
)));
765 ds
->bme_blocks
= (ULONG
*)AllocVec(i
* sizeof(ULONG
), MEMF_ANY
| MEMF_CLEAR
);
767 if ((ds
->bitmap
== 0) || (ds
->bm_blocks
== 0) || (ds
->bme_blocks
== 0))
769 D(bug("[afs validate]: Unable to allocate memory for bitmap!\n"));
777 ((UBYTE
*)ds
->bitmap
)[i
] = ~0;
786 void bm_free_bitmap(DiskStructure
* ds
)
788 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
791 if (ds
->bm_blocks
!= 0)
792 FreeVec(ds
->bm_blocks
);
793 if (ds
->bme_blocks
!= 0)
794 FreeVec(ds
->bme_blocks
);
797 BitmapResult
bm_mark_block(DiskStructure
*ds
, ULONG block
)
799 if ((block
< ds
->vol
->bstartblock
) || (block
>= ds
->vol
->countblocks
))
801 D(bug("block %ld is out of disk area range\n", block
));
802 return st_OutOfRange
;
805 block
-= ds
->vol
->bootblocks
;
808 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << (block
& 31))) == 0)
810 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << ((block
& 31)^24))) == 0)
813 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block
+ds
->vol
->bstartblock
)));
814 return st_AlreadyInUse
;
818 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << (block
& 31));
820 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << ((block
& 31)^24));
826 void bm_add_bitmap_block(DiskStructure
*ds
, ULONG blk
)
828 ds
->bm_blocks
[ds
->bm_lastblk
] = blk
;
832 void bm_add_bitmap_extension_block(DiskStructure
*ds
, ULONG blk
)
834 ds
->bme_blocks
[ds
->bme_lastblk
] = blk
;
840 /* vim: set noet:fdm=marker:fmr={,} :*/