2 Copyright © 1995-2013, 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
;
57 blockbuffer
= getBlock(afs
, vol
, vol
->rootblock
);
58 UBYTE n
[MAX_NAME_LENGTH
];
60 name
=(STRPTR
)((char *)blockbuffer
->buffer
+(BLK_DISKNAME_START(vol
)*4));
61 StrCpyFromBstr(name
, n
);
62 while (vol
->state
== ID_WRITE_PROTECTED
63 && showError(afs
, ERR_WRITEPROTECT
, n
));
65 if (vol
->state
== ID_VALIDATING
)
67 if (showError(afs
, ERR_DISKNOTVALID
))
69 if (vr_OK
== launchValidator(afs
, vol
))
75 return (vol
->state
== ID_VALIDATED
) ? 1 : 0;
81 /*******************************************
82 Name : launchValidator
83 Descr : launch validation process for specified medium
84 since we need at least a stub function, it has to be
85 declared outside __AROS__ scope
88 Author: Tomasz Wiszkowski
89 ********************************************/
90 LONG
launchValidator(struct AFSBase
*afsbase
, struct Volume
*volume
)
93 D(bug("[afs]: flushing cache...\n"));
94 flushCache(afsbase
, volume
);
97 * initially this was meant to be a synchronous validation
98 * but due to obvious problems with IO commands idea was eventually given up
100 return validate(afsbase
, volume
);
109 /*****************************************
110 * this set is good only for AROS NATIVE
111 ****************************************/
114 LONG
validate(struct AFSBase
*afs
, struct Volume
*vol
)
117 ValidationResult res
= vr_OK
;
120 * fill in diskstructure. we will need it for the sake of validation.
127 * we could use some inhibiting here
129 if (0 == bm_allocate_bitmap(&ds
))
131 inhibit(afs
, vol
, 1);
132 res
= start_superblock(&ds
);
133 inhibit(afs
, vol
, 0);
140 D(bug("[afs validate]: Validation complete. \n"));
143 D(bug("[afs validate]: Could not create access point. \n"));
146 D(bug("[afs validate]: Could not read disk. \n"));
148 case vr_UnknownDiskType
:
149 D(bug("[afs validate]: Unhandled disk type. \n"));
151 case vr_InvalidChksum
:
152 D(bug("[afs validate]: Invalid block checksum. \n"));
154 case vr_StructureDamaged
:
155 D(bug("[afs validate]: Structure damaged. \n"));
158 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
160 case vr_BlockUsedTwice
:
161 D(bug("[afs validate]: Physical block used more than once. \n"));
164 D(bug("[afs validate]: Aborted by user. \n"));
166 case vr_BlockOutsideDisk
:
167 D(bug("[afs validate]: Block outside volume boundaries. \n"));
172 struct BlockCache
*bc
= getBlock(afs
, vol
, vol
->rootblock
);
173 ULONG
* mem
= bc
->buffer
;
177 mem
[BLK_BITMAP_VALID_FLAG(vol
)] = 0;
178 vol
->state
= ID_VALIDATING
;
181 vol
->state
= ID_VALIDATED
;
183 if (verify_checksum(&ds
, mem
) != 0)
185 D(bug("[afs validate]: block checksum does not match.\n"));
186 bc
->flags
|= BCF_WRITE
;
190 * it's not neccessary here, but res is holding validation result
191 * one may wish to open some requester at this point in the future
199 LONG
check_block_range(DiskStructure
*ds
, ULONG num
)
201 if ((num
< ds
->vol
->bstartblock
) || (num
>= ds
->vol
->countblocks
))
203 D(bug("[afs validate]: Block located outside volume range. Condition %lu <= %lu < %lu not met.\n",
204 ds
->vol
->bstartblock
,
206 ds
->vol
->countblocks
));
207 showError(ds
->afs
, ERR_BLOCK_OUTSIDE_RANGE
, num
);
216 ValidationResult
start_superblock(DiskStructure
*ds
)
218 ValidationResult res
= vr_OK
;
220 res
= collect_directory_blocks(ds
, ds
->vol
->rootblock
);
221 D(bug("[afs validate]: validation complete. Result: %ld\n", res
));
224 * record bitmap back to disk, set bitmap valid flag, and update checksum of the root sector
225 * please note: it is hell important to have this checksum valid ;) so you better keep an eye
226 * on all your changes
239 ULONG
verify_checksum(DiskStructure
*ds
, ULONG
*block
)
244 D(bug("[afs validate]: verifying block checksum... "));
246 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
248 sum
+= OS_BE2LONG(block
[i
]);
253 D(bug("checksum invalid!\n"));
254 sum
-= OS_BE2LONG(block
[BLK_CHECKSUM
]);
255 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block
[BLK_CHECKSUM
], sum
));
256 block
[BLK_CHECKSUM
] = OS_LONG2BE(-sum
);
260 D(bug("checksum valid!\n"));
267 ULONG
verify_bm_checksum(DiskStructure
*ds
, ULONG
*block
)
272 D(bug("[afs validate]: verifying bitmap checksum... "));
274 for (i
=0; i
<ds
->vol
->SizeBlock
; i
++)
276 sum
+= OS_BE2LONG(block
[i
]);
281 D(bug("checksum invalid!\n"));
282 sum
-= OS_BE2LONG(block
[0]);
283 D(bug("[afs validate]: current sum: %08lx, valid sum: %08lx. correcting\n", block
[0], sum
));
284 block
[0] = OS_LONG2BE(-sum
);
288 D(bug("checksum valid!\n"));
293 * check single block - this procedure should do as much as possible
295 ValidationResult
check_block(DiskStructure
* ds
, struct BlockCache
* block
)
301 * first check cache validity
305 D(bug("[afs validate] Error: no block passed.\n"));
309 if (0 == block
->blocknum
)
311 D(bug("[afs validate] Block could not be read.\n"));
318 * check types first: we expect block of T_SHORT (2) or T_LIST (16) type
320 id
= OS_BE2LONG(mem
[BLK_PRIMARY_TYPE
]);
321 if ((id
!= T_SHORT
) &&
324 D(bug("[afs validate]: block is not of known-type (%08lx). asking whether to correct\n", id
));
326 if (block
->blocknum
== ds
->vol
->rootblock
)
328 if ((0 == (ds
->flags
& ValFlg_DisableReq_MaybeNotAFS
)) && (0 == showError(ds
->afs
, ERR_POSSIBLY_NOT_AFS
)))
330 ds
->flags
|= ValFlg_DisableReq_MaybeNotAFS
;
331 D(bug("[afs validate]: will continue per user decision. faulty block will be removed from structure.\n"));
333 return vr_StructureDamaged
;
337 * set bitmap block (mark as used)
339 if (bm_mark_block(ds
, block
->blocknum
))
341 D(bug("[afs validate]: block %lu used twice! aborting!\n", block
->blocknum
));
342 return vr_BlockUsedTwice
;
348 ValidationResult
collect_bitmap(DiskStructure
* ds
, struct BlockCache
* block
)
350 ULONG strt
=BLK_BITMAP_POINTERS_START(ds
->vol
),
351 stop
=BLK_BITMAP_POINTERS_END(ds
->vol
),
352 ext
=BLK_BITMAP_EXTENSION(ds
->vol
),
355 ULONG
* mem
= block
->buffer
;
359 for (i
=strt
; i
<=stop
; ++i
)
361 blk
= OS_BE2LONG(mem
[i
]);
366 * verify whether bitmap block resides on disk
367 * if the range validation fails, simply do not add the block to the list
369 * unfortunately bitmap block reallocation has not been implemented
370 * since this is the most unlikely happening issue
372 if (0 == check_block_range(ds
, blk
))
373 return vr_BlockOutsideDisk
;
376 * mark block as used.
377 * the bitmap blocks *could be reallocated* (and in fact should be)
378 * if the bm_mark_block returns something else than st_OK (that is,
379 * in case, when block is out of range, or simply used twice).
381 * i'm sorry, but this has not been introduced yet.
383 if (bm_mark_block(ds
, blk
) != st_OK
)
385 showError(ds
->afs
, ERR_BLOCK_USED_TWICE
, blk
);
386 return vr_BlockUsedTwice
;
390 * add bitmap block to bitmap block set
392 bm_add_bitmap_block(ds
, blk
);
396 * collect bitmap extension blocks
398 if ((blk
= OS_BE2LONG(mem
[ext
])) != 0)
400 D(bug("[afs validate] Following to next bitmap extension block at %08lx\n", blk
));
401 if (0 == check_block_range(ds
, blk
))
402 return vr_BlockOutsideDisk
;
404 if (bm_mark_block(ds
, blk
) != st_OK
)
405 return vr_BlockUsedTwice
;
407 block
= getBlock(ds
->afs
, ds
->vol
, blk
);
410 stop
= ds
->vol
->SizeBlock
-2;
411 ext
= ds
->vol
->SizeBlock
-1;
419 ValidationResult
collect_file_extensions(DiskStructure
* ds
, struct BlockCache
* block
)
422 ULONG
* mem
= block
->buffer
;
428 for (i
=BLK_TABLE_END(ds
->vol
); i
>=BLK_TABLE_START
; --i
)
433 block
->flags
|= BCF_WRITE
;
437 blk
= OS_BE2LONG(mem
[i
]);
440 * if this was the last data block, purge rest
450 * verify if block still belongs to this disk, purge if it doesn't
452 if (0 == check_block_range(ds
, blk
))
454 D(bug("[afs validate] file data block outside range. truncating\n"));
455 block
->flags
|= BCF_WRITE
;
462 * mark block as used.
464 if (bm_mark_block(ds
, blk
) != st_OK
)
466 D(bug("[afs validate] file data block used twice. truncating\n"));
467 block
->flags
|= BCF_WRITE
;
477 * if block is marked as 'write', make sure it holds correct sum.
479 if (block
->flags
& BCF_WRITE
)
480 verify_checksum(ds
, block
->buffer
);
482 * collect bitmap extension blocks
484 if ((blk
= OS_BE2LONG(mem
[BLK_EXTENSION(ds
->vol
)])) != 0)
486 D(bug("[afs validate] Following to next file extension block at %08lx\n", blk
));
487 if (0 == check_block_range(ds
, blk
))
489 D(bug("[afs validate] Extension block outside range. truncating file\n"));
490 mem
[BLK_EXTENSION(ds
->vol
)] = 0;
491 block
->flags
|= BCF_WRITE
;
494 else if (bm_mark_block(ds
, blk
) != st_OK
)
496 D(bug("[afs validate] Bitmap block already marked as used. truncating file\n"));
497 mem
[BLK_EXTENSION(ds
->vol
)] = 0;
498 block
->flags
|= BCF_WRITE
;
503 block
= getBlock(ds
->afs
, ds
->vol
, blk
);
509 * update sum; if changed, mark block for writing
511 if (0 != verify_checksum(ds
, block
->buffer
))
512 block
->flags
|= BCF_WRITE
;
520 * collect directories
522 ValidationResult
collect_directory_blocks(DiskStructure
*ds
, ULONG blk
)
524 struct BlockCache
*bc
;
529 D(bug("[afs validate]: analyzing block %lu\n", blk
));
532 * check range. Note that the whole process here is not 'reversible':
533 * if you mark bitmap blocks as used, and then just return in the middle,
534 * it won't get freed automagically, so mark it only when you know it should be there.
536 if (0 == check_block_range(ds
, blk
))
537 return vr_BlockOutsideDisk
;
540 * we don't set block usage flags: we will re-read the block anyways.
542 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
545 * initial block analysis
547 id
= check_block(ds
, bc
);
554 entry_type
= OS_BE2LONG(bc
->buffer
[BLK_SECONDARY_TYPE(ds
->vol
)]);
555 primary_type
= OS_BE2LONG(bc
->buffer
[BLK_PRIMARY_TYPE
]);
557 switch (primary_type
) {
565 /* TODO: handle unknown primary type */
570 * for root block: collect all bitmap blocks now
574 * i won't hide it: there's plenty of things to check here, but
575 * since it's just a validation task
576 * i have decided to remove the redundant code.
580 * ok, if anyone wants to remove the bitmap allocations and re-allocate them by himself
581 * here's the good place to start doing it. myself, i think the best way is to rely on
582 * operating system in this matter: unless someone damaged the disk structure, it's
583 * perfectly safe to assume that number of allocated bitmap blocks is just enough for us
584 * to store the bitmap. Again, it's not a salvaging, repairing NOR reorganizing task.
588 * oh, btw; we also want to collect the bitmap blocks here so we know where to save new bitmap.
592 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
594 id
= collect_bitmap(ds
, bc
);
599 * initially -- mark bitmap status as valid
601 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
602 bc
->buffer
[BLK_BITMAP_VALID_FLAG(ds
->vol
)] = ~0;
603 verify_checksum(ds
, bc
->buffer
);
604 bc
->flags
|= BCF_WRITE
;
608 * this is the collection procedure
609 * we check for directories/files here only. if an entry is not a directory,
610 * check if it is a file and accumulate data blocks for it.
611 * here we are interested only in FOUR of SEVEN types: ROOT, DIRECTORY, FILE and FILE EXTENSION
613 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
618 * clear directory cache block.
619 * fancy thing about directory cache: it is the best way to run into inconsistencies between file trees.
620 * two trees, one kept for compatibility (which is not kept btw as dostype is different), and the other
621 * for 'faster directory listing', but not always in sync
623 if (bc
->buffer
[BLK_EXTENSION(ds
->vol
)] != 0)
625 D(bug("[afs validate]: clearing dircache pointer\n"));
626 bc
->buffer
[BLK_EXTENSION(ds
->vol
)] = 0;
627 verify_checksum(ds
, bc
->buffer
);
628 bc
->flags
|= BCF_WRITE
;
631 for (i
=BLK_TABLE_START
; i
<=BLK_TABLE_END(ds
->vol
); i
++)
633 id
= OS_BE2LONG(bc
->buffer
[i
]);
637 * proceed with block collection. unless user decides to stop validation
638 * move on and bug them about whatever is really bad
640 id
= collect_directory_blocks(ds
, id
);
641 if (id
== vr_Aborted
)
645 * restore current block
646 * this will work well if the modified block gets marked as BCF_WRITE
648 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
652 /* it's a good idea to flag this requester, so it shows only once */
653 if ((0 == (ds
->flags
& ValFlg_DisableReq_DataLossImminent
)) && (0 == showError(ds
->afs
, ERR_DATA_LOSS_POSSIBLE
)))
656 ds
->flags
|= ValFlg_DisableReq_DataLossImminent
;
658 verify_checksum(ds
, bc
->buffer
);
659 bc
->flags
|= BCF_WRITE
;
666 * collect file data blocks.
667 * some files are too large to fit in a single list of data blocks
668 * this covers scanning more blocks
670 * crap, i don't know if i should use entry_type=0 here, too :|
672 if (entry_type
== -3)
674 collect_file_extensions(ds
, bc
);
675 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
676 id
= OS_BE2LONG(bc
->buffer
[BLK_BYTE_SIZE(ds
->vol
)]);
677 if (id
> (ds
->file_blocks
* ds
->vol
->SizeBlock
<< 2))
679 bc
->buffer
[BLK_BYTE_SIZE(ds
->vol
)] = OS_LONG2BE(ds
->file_blocks
* ds
->vol
->SizeBlock
<< 2);
680 verify_checksum(ds
, bc
->buffer
);
681 bc
->flags
|= BCF_WRITE
;
686 * finally, move on to next file in this hash chain. IF next file suffers anything, remove it and following files from hash chain.
688 id
= OS_BE2LONG(bc
->buffer
[BLK_HASHCHAIN(ds
->vol
)]);
691 D(bug("[afs validate]: collecting other items in this chain\n"));
694 * collect other elements
696 id
= collect_directory_blocks(ds
, id
);
699 * if aborted, simply quit
701 if (id
== vr_Aborted
)
707 * otherwise alter structure
711 D(bug("[afs validate]: removing faulty chain\n"));
712 bc
= getBlock(ds
->afs
, ds
->vol
, blk
);
713 bc
->buffer
[BLK_HASHCHAIN(ds
->vol
)] = 0;
714 verify_checksum(ds
, bc
->buffer
);
715 bc
->flags
|= BCF_WRITE
;
723 * record bitmap: stores bitmap in RAM (if you redefine a little) and saves it to disk.
725 void record_bitmap(DiskStructure
*ds
)
727 struct BlockCache
*bc
;
731 for (i
=0; i
<ds
->bm_lastblk
; i
++)
733 bc
= getBlock(ds
->afs
, ds
->vol
, ds
->bm_blocks
[i
]);
736 CopyMemQuick(&((char*)ds
->bitmap
)[i
*((ds
->vol
->SizeBlock
-1)<<2)], &mem
[1], (ds
->vol
->SizeBlock
-1)<<2);
737 verify_bm_checksum(ds
, mem
);
738 bc
->flags
|= BCF_WRITE
;
743 * allocates enough store for the complete bitmap
745 LONG
bm_allocate_bitmap(DiskStructure
*ds
)
751 * bitmap block size: blk_size - 4
753 bmap_blk_size
= ((ds
->vol
->SizeBlock
-1)<<2);
756 * total bitmap size is calculated as follows:
757 * - total number of bits (rounded up to nearest multiple of 8) converted to bytes
758 * - the above rounded up to nearest size of a single block - 4 bytes
760 i
= (((ds
->vol
->countblocks
+ 7) >> 3) + (bmap_blk_size
- 1)) / bmap_blk_size
;
762 D(bug("[afs validate]: allocating bitmaps (%ld bytes)\n", i
* bmap_blk_size
));
763 ds
->bitmap
= AllocVec(i
* bmap_blk_size
, MEMF_ANY
);
765 D(bug("[afs validate]: allocating storage for bitmap blocks (%ld bytes)\n", i
* sizeof(ULONG
)));
766 ds
->bm_blocks
= (ULONG
*)AllocVec(i
* sizeof(ULONG
), MEMF_ANY
| MEMF_CLEAR
);
768 D(bug("[afs validate]: allocating storage for bitmap extension blocks (%ld bytes) - way more than we really need\n", i
* sizeof(ULONG
)));
769 ds
->bme_blocks
= (ULONG
*)AllocVec(i
* sizeof(ULONG
), MEMF_ANY
| MEMF_CLEAR
);
771 if ((ds
->bitmap
== 0) || (ds
->bm_blocks
== 0) || (ds
->bme_blocks
== 0))
773 D(bug("[afs validate]: Unable to allocate memory for bitmap!\n"));
781 ((UBYTE
*)ds
->bitmap
)[i
] = ~0;
790 void bm_free_bitmap(DiskStructure
* ds
)
792 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
794 FreeVec(ds
->bm_blocks
);
795 FreeVec(ds
->bme_blocks
);
798 BitmapResult
bm_mark_block(DiskStructure
*ds
, ULONG block
)
800 if ((block
< ds
->vol
->bstartblock
) || (block
>= ds
->vol
->countblocks
))
802 D(bug("block %ld is out of disk area range\n", block
));
803 return st_OutOfRange
;
806 block
-= ds
->vol
->bootblocks
;
809 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << (block
& 31))) == 0)
811 if ((((ULONG
*)ds
->bitmap
)[block
>> 5] & (1 << ((block
& 31)^24))) == 0)
814 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block
+ds
->vol
->bstartblock
)));
815 return st_AlreadyInUse
;
819 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << (block
& 31));
821 ((ULONG
*)ds
->bitmap
)[block
>> 5] &= ~(1 << ((block
& 31)^24));
827 void bm_add_bitmap_block(DiskStructure
*ds
, ULONG blk
)
829 ds
->bm_blocks
[ds
->bm_lastblk
] = blk
;
833 void bm_add_bitmap_extension_block(DiskStructure
*ds
, ULONG blk
)
835 ds
->bme_blocks
[ds
->bme_lastblk
] = blk
;
841 /* vim: set noet:fdm=marker:fmr={,} :*/