Call CloseDevice() before DeleteIORequest(), and don't call
[AROS.git] / rom / filesys / afs / validator.c
blob384287c5d91ac0345432371e0a181fd5c512d522
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;
53 blockbuffer = getBlock(afs, vol, vol->rootblock);
54 UBYTE n[MAX_NAME_LENGTH];
55 STRPTR name;
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))
66 return 1;
68 return 0;
71 return vol->state == ID_VALIDATED ? 1 : 0;
72 #else
73 return 1;
74 #endif
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
82 Input : volume
83 Output: none
84 Author: Tomasz Wiszkowski
85 ********************************************/
86 LONG launchValidator(struct AFSBase *afsbase, struct Volume *volume)
88 #ifdef __AROS__
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);
97 #else
98 return 0;
99 #endif
105 /*****************************************
106 * this set is good only for AROS NATIVE
107 ****************************************/
108 #ifdef __AROS__
110 LONG validate(struct AFSBase *afs, struct Volume *vol)
112 DiskStructure ds;
113 ValidationResult res = vr_OK;
116 * fill in diskstructure. we will need it for the sake of validation.
118 ds.vol = vol;
119 ds.afs = afs;
120 ds.flags = 0;
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);
131 bm_free_bitmap(&ds);
133 switch (res)
135 case vr_OK:
136 D(bug("[afs validate]: Validation complete. \n"));
137 break;
138 case vr_NoAccess:
139 D(bug("[afs validate]: Could not create access point. \n"));
140 break;
141 case vr_ReadError:
142 D(bug("[afs validate]: Could not read disk. \n"));
143 break;
144 case vr_UnknownDiskType:
145 D(bug("[afs validate]: Unhandled disk type. \n"));
146 break;
147 case vr_InvalidChksum:
148 D(bug("[afs validate]: Invalid block checksum. \n"));
149 break;
150 case vr_StructureDamaged:
151 D(bug("[afs validate]: Structure damaged. \n"));
152 break;
153 case vr_OutOfMemory:
154 D(bug("[afs validate]: Could not allocate memory to complete operation. \n"));
155 break;
156 case vr_BlockUsedTwice:
157 D(bug("[afs validate]: Physical block used more than once. \n"));
158 break;
159 case vr_Aborted:
160 D(bug("[afs validate]: Aborted by user. \n"));
161 break;
162 case vr_BlockOutsideDisk:
163 D(bug("[afs validate]: Block outside volume boundaries. \n"));
164 break;
168 struct BlockCache *bc = getBlock(afs, vol, vol->rootblock);
169 ULONG* mem = bc->buffer;
171 if (res != vr_OK)
173 mem[BLK_BITMAP_VALID_FLAG(vol)] = 0;
174 vol->state = ID_VALIDATING;
176 else
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
189 return res;
193 * validate range
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,
201 num,
202 ds->vol->countblocks));
203 showError(ds->afs, ERR_BLOCK_OUTSIDE_RANGE, num);
204 return 0;
206 return 1;
210 * begin
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
224 if (res == vr_OK)
226 record_bitmap(ds);
229 return res;
233 * verify_checksum
235 ULONG verify_checksum(DiskStructure *ds, ULONG *block)
237 ULONG sum = 0;
238 LONG i;
240 D(bug("[afs validate]: verifying block checksum... "));
242 for (i=0; i<ds->vol->SizeBlock; i++)
244 sum += OS_BE2LONG(block[i]);
247 if (sum != 0)
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);
253 return -sum;
256 D(bug("checksum valid!\n"));
257 return 0;
261 * verify_bm_checksum
263 ULONG verify_bm_checksum(DiskStructure *ds, ULONG *block)
265 ULONG sum = 0;
266 ULONG i;
268 D(bug("[afs validate]: verifying bitmap checksum... "));
270 for (i=0; i<ds->vol->SizeBlock; i++)
272 sum += OS_BE2LONG(block[i]);
275 if (sum != 0)
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);
281 return -sum;
284 D(bug("checksum valid!\n"));
285 return 0;
289 * check single block - this procedure should do as much as possible
291 ValidationResult check_block(DiskStructure* ds, struct BlockCache* block)
293 ULONG* mem;
294 ULONG id;
297 * first check cache validity
299 if (0 == block)
301 D(bug("[afs validate] Error: no block passed.\n"));
302 return vr_ReadError;
305 if (0 == block->blocknum)
307 D(bug("[afs validate] Block could not be read.\n"));
308 return vr_ReadError;
311 mem = block->buffer;
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) &&
318 (id != T_LIST))
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)))
325 return vr_Aborted;
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;
341 return vr_OK;
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),
349 i, blk;
351 ULONG* mem = block->buffer;
355 for (i=strt; i<=stop; ++i)
357 blk = OS_BE2LONG(mem[i]);
358 if (blk == 0)
359 continue;
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);
404 mem = block->buffer;
405 strt = 0;
406 stop = ds->vol->SizeBlock-2;
407 ext = ds->vol->SizeBlock-1;
410 while (blk != 0);
412 return vr_OK;
415 ValidationResult collect_file_extensions(DiskStructure* ds, struct BlockCache* block)
417 ULONG i, blk, clr=0;
418 ULONG* mem = block->buffer;
420 ds->file_blocks = 0;
424 for (i=BLK_TABLE_END(ds->vol); i>=BLK_TABLE_START; --i)
426 if (clr != 0)
428 mem[i] = 0;
429 block->flags |= BCF_WRITE;
430 continue;
433 blk = OS_BE2LONG(mem[i]);
436 * if this was the last data block, purge rest
438 if (blk == 0)
440 clr = 1;
441 continue;
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;
452 clr = 1;
453 mem[i] = 0;
454 continue;
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;
464 clr = 1;
465 mem[i] = 0;
466 continue;
469 ++ds->file_blocks;
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;
488 blk = 0;
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;
495 blk = 0;
497 else
499 block = getBlock(ds->afs, ds->vol, blk);
500 mem = block->buffer;
505 * update sum; if changed, mark block for writing
507 if (0 != verify_checksum(ds, block->buffer))
508 block->flags |= BCF_WRITE;
510 while (blk != 0);
512 return vr_OK;
516 * collect directories
518 ValidationResult collect_directory_blocks(DiskStructure *ds, ULONG blk)
520 struct BlockCache *bc;
521 LONG entry_type;
522 LONG primary_type;
523 ULONG id;
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);
544 if (id != vr_OK)
545 return id;
548 * collect types
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) {
554 case T_LIST:
555 break;
556 case T_SHORT:
557 break;
558 case T_DATA:
559 break;
560 default:
561 /* TODO: handle unknown primary type */
562 break;
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.
586 if (entry_type == 1)
588 D(bug("[afs validate]: Checking and collecting bitmap blocks\n"));
590 id = collect_bitmap(ds, bc);
591 if (id != vr_OK)
592 return id;
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
611 LONG i;
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]);
630 if (id != 0)
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)
638 return id;
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);
646 if (vr_OK != id)
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)))
650 return vr_Aborted;
652 ds->flags |= ValFlg_DisableReq_DataLossImminent;
653 bc->buffer[i] = 0;
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)]);
685 if (id != 0)
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)
699 return id;
703 * otherwise alter structure
705 if (id != 0)
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;
715 return vr_OK;
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;
724 ULONG *mem;
725 ULONG i;
727 for (i=0; i<ds->bm_lastblk; i++)
729 bc = getBlock(ds->afs, ds->vol, ds->bm_blocks[i]);
730 mem = bc->buffer;
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)
743 ULONG i;
744 ULONG bmap_blk_size;
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"));
770 return -1;
773 i *= bmap_blk_size;
775 while (i--)
777 ((UBYTE*)ds->bitmap)[i] = ~0;
780 ds->bm_lastblk = 0;
781 ds->bme_lastblk = 0;
783 return 0;
786 void bm_free_bitmap(DiskStructure* ds)
788 D(bug("[afs validate]: freeing previously allocated bitmaps\n"));
789 if (ds->bitmap != 0)
790 FreeVec(ds->bitmap);
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;
807 #if AROS_BIG_ENDIAN
808 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << (block & 31))) == 0)
809 #else
810 if ((((ULONG*)ds->bitmap)[block >> 5] & (1 << ((block & 31)^24))) == 0)
811 #endif
813 D(bug("Duplicate block allocation for block %ld! Overlapping files?\n", (block+ds->vol->bstartblock)));
814 return st_AlreadyInUse;
817 #if AROS_BIG_ENDIAN
818 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << (block & 31));
819 #else
820 ((ULONG*)ds->bitmap)[block >> 5] &= ~(1 << ((block & 31)^24));
821 #endif
823 return st_OK;
826 void bm_add_bitmap_block(DiskStructure *ds, ULONG blk)
828 ds->bm_blocks[ds->bm_lastblk] = blk;
829 ds->bm_lastblk++;
832 void bm_add_bitmap_extension_block(DiskStructure *ds, ULONG blk)
834 ds->bme_blocks[ds->bme_lastblk] = blk;
835 ds->bme_lastblk++;
839 #endif
840 /* vim: set noet:fdm=marker:fmr={,} :*/