afs-handler: Found another location where AmigaOS docs did not match reality.
[AROS.git] / rom / filesys / afs / validator.c
blobe1a4b86add446411156363b3c31bf49bb8a913c0
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
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 #include "extstrings.h"
38 #endif
40 /*******************************************
41 Name : checkValid
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)
51 #ifdef __AROS__
52 struct BlockCache *blockbuffer;
54 if (vol == NULL)
55 return 0;
57 blockbuffer = getBlock(afs, vol, vol->rootblock);
58 UBYTE n[MAX_NAME_LENGTH];
59 STRPTR name;
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))
70 return 1;
72 return 0;
75 return (vol->state == ID_VALIDATED) ? 1 : 0;
76 #else
77 return 1;
78 #endif
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
86 Input : volume
87 Output: none
88 Author: Tomasz Wiszkowski
89 ********************************************/
90 LONG launchValidator(struct AFSBase *afsbase, struct Volume *volume)
92 #ifdef __AROS__
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);
101 #else
102 return 0;
103 #endif
109 /*****************************************
110 * this set is good only for AROS NATIVE
111 ****************************************/
112 #ifdef __AROS__
114 LONG validate(struct AFSBase *afs, struct Volume *vol)
116 DiskStructure ds;
117 ValidationResult res = vr_OK;
120 * fill in diskstructure. we will need it for the sake of validation.
122 ds.vol = vol;
123 ds.afs = afs;
124 ds.flags = 0;
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);
135 bm_free_bitmap(&ds);
137 switch (res)
139 case vr_OK:
140 D(bug("[afs validate]: Validation complete. \n"));
141 break;
142 case vr_NoAccess:
143 D(bug("[afs validate]: Could not create access point. \n"));
144 break;
145 case vr_ReadError:
146 D(bug("[afs validate]: Could not read disk. \n"));
147 break;
148 case vr_UnknownDiskType:
149 D(bug("[afs validate]: Unhandled disk type. \n"));
150 break;
151 case vr_InvalidChksum:
152 D(bug("[afs validate]: Invalid block checksum. \n"));
153 break;
154 case vr_StructureDamaged:
155 D(bug("[afs validate]: Structure damaged. \n"));
156 break;
157 case vr_OutOfMemory:
158 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
159 break;
160 case vr_BlockUsedTwice:
161 D(bug("[afs validate]: Physical block used more than once. \n"));
162 break;
163 case vr_Aborted:
164 D(bug("[afs validate]: Aborted by user. \n"));
165 break;
166 case vr_BlockOutsideDisk:
167 D(bug("[afs validate]: Block outside volume boundaries. \n"));
168 break;
172 struct BlockCache *bc = getBlock(afs, vol, vol->rootblock);
173 ULONG* mem = bc->buffer;
175 if (res != vr_OK)
177 mem[BLK_BITMAP_VALID_FLAG(vol)] = 0;
178 vol->state = ID_VALIDATING;
180 else
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
193 return res;
197 * validate range
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,
205 num,
206 ds->vol->countblocks));
207 showError(ds->afs, ERR_BLOCK_OUTSIDE_RANGE, num);
208 return 0;
210 return 1;
214 * begin
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
228 if (res == vr_OK)
230 record_bitmap(ds);
233 return res;
237 * verify_checksum
239 ULONG verify_checksum(DiskStructure *ds, ULONG *block)
241 ULONG sum = 0;
242 LONG i;
244 D(bug("[afs validate]: verifying block checksum... "));
246 for (i=0; i<ds->vol->SizeBlock; i++)
248 sum += OS_BE2LONG(block[i]);
251 if (sum != 0)
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);
257 return -sum;
260 D(bug("checksum valid!\n"));
261 return 0;
265 * verify_bm_checksum
267 ULONG verify_bm_checksum(DiskStructure *ds, ULONG *block)
269 ULONG sum = 0;
270 ULONG i;
272 D(bug("[afs validate]: verifying bitmap checksum... "));
274 for (i=0; i<ds->vol->SizeBlock; i++)
276 sum += OS_BE2LONG(block[i]);
279 if (sum != 0)
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);
285 return -sum;
288 D(bug("checksum valid!\n"));
289 return 0;
293 * check single block - this procedure should do as much as possible
295 ValidationResult check_block(DiskStructure* ds, struct BlockCache* block)
297 ULONG* mem;
298 ULONG id;
301 * first check cache validity
303 if (0 == block)
305 D(bug("[afs validate] Error: no block passed.\n"));
306 return vr_ReadError;
309 if (0 == block->blocknum)
311 D(bug("[afs validate] Block could not be read.\n"));
312 return vr_ReadError;
315 mem = block->buffer;
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) &&
322 (id != T_LIST))
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)))
329 return vr_Aborted;
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;
345 return vr_OK;
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),
353 i, blk;
355 ULONG* mem = block->buffer;
359 for (i=strt; i<=stop; ++i)
361 blk = OS_BE2LONG(mem[i]);
362 if (blk == 0)
363 continue;
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);
408 mem = block->buffer;
409 strt = 0;
410 stop = ds->vol->SizeBlock-2;
411 ext = ds->vol->SizeBlock-1;
414 while (blk != 0);
416 return vr_OK;
419 ValidationResult collect_file_extensions(DiskStructure* ds, struct BlockCache* block)
421 ULONG i, blk, clr=0;
422 ULONG* mem = block->buffer;
424 ds->file_blocks = 0;
428 for (i=BLK_TABLE_END(ds->vol); i>=BLK_TABLE_START; --i)
430 if (clr != 0)
432 mem[i] = 0;
433 block->flags |= BCF_WRITE;
434 continue;
437 blk = OS_BE2LONG(mem[i]);
440 * if this was the last data block, purge rest
442 if (blk == 0)
444 clr = 1;
445 continue;
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;
456 clr = 1;
457 mem[i] = 0;
458 continue;
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;
468 clr = 1;
469 mem[i] = 0;
470 continue;
473 ++ds->file_blocks;
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;
492 blk = 0;
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;
499 blk = 0;
501 else
503 block = getBlock(ds->afs, ds->vol, blk);
504 mem = block->buffer;
509 * update sum; if changed, mark block for writing
511 if (0 != verify_checksum(ds, block->buffer))
512 block->flags |= BCF_WRITE;
514 while (blk != 0);
516 return vr_OK;
520 * collect directories
522 ValidationResult collect_directory_blocks(DiskStructure *ds, ULONG blk)
524 struct BlockCache *bc;
525 LONG entry_type;
526 LONG primary_type;
527 ULONG id;
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);
548 if (id != vr_OK)
549 return id;
552 * collect types
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) {
558 case T_LIST:
559 break;
560 case T_SHORT:
561 break;
562 case T_DATA:
563 break;
564 default:
565 /* TODO: handle unknown primary type */
566 break;
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.
590 if (entry_type == 1)
592 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
594 id = collect_bitmap(ds, bc);
595 if (id != vr_OK)
596 return id;
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
615 LONG i;
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]);
634 if (id != 0)
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)
642 return id;
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);
650 if (vr_OK != id)
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)))
654 return vr_Aborted;
656 ds->flags |= ValFlg_DisableReq_DataLossImminent;
657 bc->buffer[i] = 0;
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)]);
689 if (id != 0)
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)
703 return id;
707 * otherwise alter structure
709 if (id != 0)
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;
719 return vr_OK;
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;
728 ULONG *mem;
729 ULONG i;
731 for (i=0; i<ds->bm_lastblk; i++)
733 bc = getBlock(ds->afs, ds->vol, ds->bm_blocks[i]);
734 mem = bc->buffer;
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)
747 ULONG i;
748 ULONG bmap_blk_size;
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"));
774 return -1;
777 i *= bmap_blk_size;
779 while (i--)
781 ((UBYTE*)ds->bitmap)[i] = ~0;
784 ds->bm_lastblk = 0;
785 ds->bme_lastblk = 0;
787 return 0;
790 void bm_free_bitmap(DiskStructure* ds)
792 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
793 if (ds->bitmap != 0)
794 FreeVec(ds->bitmap);
795 if (ds->bm_blocks != 0)
796 FreeVec(ds->bm_blocks);
797 if (ds->bme_blocks != 0)
798 FreeVec(ds->bme_blocks);
801 BitmapResult bm_mark_block(DiskStructure *ds, ULONG block)
803 if ((block < ds->vol->bstartblock) || (block >= ds->vol->countblocks))
805 D(bug("block %ld is out of disk area range\n", block));
806 return st_OutOfRange;
809 block -= ds->vol->bootblocks;
811 #if AROS_BIG_ENDIAN
812 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << (block & 31))) == 0)
813 #else
814 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << ((block & 31)^24))) == 0)
815 #endif
817 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block+ds->vol->bstartblock)));
818 return st_AlreadyInUse;
821 #if AROS_BIG_ENDIAN
822 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << (block & 31));
823 #else
824 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << ((block & 31)^24));
825 #endif
827 return st_OK;
830 void bm_add_bitmap_block(DiskStructure *ds, ULONG blk)
832 ds->bm_blocks[ds->bm_lastblk] = blk;
833 ds->bm_lastblk++;
836 void bm_add_bitmap_extension_block(DiskStructure *ds, ULONG blk)
838 ds->bme_blocks[ds->bme_lastblk] = blk;
839 ds->bme_lastblk++;
843 #endif
844 /* vim: set noet:fdm=marker:fmr={,} :*/