From 89ffb09e428a1a2aa29a553f5232030a382f0ff3 Mon Sep 17 00:00:00 2001 From: twilen Date: Tue, 1 Jul 2014 18:48:17 +0000 Subject: [PATCH] Added pfsdoctor. Includes PFS3 v19 large partition and file size support. git-svn-id: https://svn.aros.org/svn/aros/trunk/AROS@49096 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- rom/filesys/pfs3/pfsdoctor/access.c | 290 +++ rom/filesys/pfs3/pfsdoctor/console.c | 322 +++ rom/filesys/pfs3/pfsdoctor/device.c | 672 +++++++ rom/filesys/pfs3/pfsdoctor/doctor.h | 499 +++++ rom/filesys/pfs3/pfsdoctor/fullscan.c | 695 +++++++ rom/filesys/pfs3/pfsdoctor/mmakefile.src | 25 + rom/filesys/pfs3/pfsdoctor/pfs3.h | 310 +++ rom/filesys/pfs3/pfsdoctor/standardscan.c | 3076 +++++++++++++++++++++++++++++ rom/filesys/pfs3/pfsdoctor/stats.c | 104 + 9 files changed, 5993 insertions(+) create mode 100644 rom/filesys/pfs3/pfsdoctor/access.c create mode 100644 rom/filesys/pfs3/pfsdoctor/console.c create mode 100644 rom/filesys/pfs3/pfsdoctor/device.c create mode 100644 rom/filesys/pfs3/pfsdoctor/doctor.h create mode 100644 rom/filesys/pfs3/pfsdoctor/fullscan.c create mode 100644 rom/filesys/pfs3/pfsdoctor/mmakefile.src create mode 100644 rom/filesys/pfs3/pfsdoctor/pfs3.h create mode 100644 rom/filesys/pfs3/pfsdoctor/standardscan.c create mode 100644 rom/filesys/pfs3/pfsdoctor/stats.c diff --git a/rom/filesys/pfs3/pfsdoctor/access.c b/rom/filesys/pfs3/pfsdoctor/access.c new file mode 100644 index 0000000000..26d1541fd4 --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/access.c @@ -0,0 +1,290 @@ +/* $Id$ */ +/* $Log: access.c $ + * Revision 2.6 1999/09/11 16:45:50 Michiel + * Bug (1024 byte blok supprort) in AccessTest fixed + * + * Revision 2.5 1999/07/28 09:24:56 Michiel + * Unneeded anodeblock searches removed + * + * Revision 2.4 1999/05/07 16:49:00 Michiel + * bugfixes etc + * + * Revision 2.3 1999/05/07 09:31:31 Michiel + * bugfix + * + * Revision 2.2 1999/05/04 04:27:13 Michiel + * debugged upto buildrext + * + * Revision 2.1 1999/04/30 12:17:58 Michiel + * Accepts OK disks, bitmapfix and hardlink fix works + * + * Revision 1.2 1999/04/22 15:26:49 Michiel + * compiled + * */ + +#include +#include +#include "pfs3.h" +#include "doctor.h" +#include + +/************************************** + * Non-checking functions for access to structures on PFS3 disks + **************************************/ + +/* get a buildblock from the buildblock cache + */ +cachedblock_t *GetBuildBlock(uint16 bloktype, uint32 seqnr) +{ + struct buildblock *bbl; + + for (bbl = HeadOf(&volume.buildblocks); bbl->next; bbl = bbl->next) + { + if (bbl->b.data->id == bloktype && + bbl->b.data->indexblock.seqnr == seqnr) + { + return &bbl->b; + } + } + + return NULL; +} + +/* get reserved block + * anything, except deldir, root, boot, dir and rext + */ +error_t GetResBlock(cachedblock_t *blok, uint16 bloktype, uint32 seqnr, bool fix) +{ + cachedblock_t blk, *t; + uint32 blknr, *bp = NULL, index, offset; + error_t error = e_none; + + // controleer build block lijst + t = GetBuildBlock(bloktype, seqnr); + if (t) + { + blok->blocknr = t->blocknr; + blok->mode = t->mode; + *blok->data = *t->data; + return e_none; + } + + blk.data = calloc(1, SIZEOF_RESBLOCK); + index = seqnr/INDEX_PER_BLOCK; + offset = seqnr%INDEX_PER_BLOCK; + switch(bloktype) + { + case SBLKID: + + if (seqnr > MAXSUPER) + { + free (blk.data); + return e_number_error; + } + + bp = &rext.data->superindex[seqnr]; + if (!*bp) + { + if (fix) + { + adderror("superindex block not found"); + error = RepairSuperIndex(bp, seqnr); + if (error) + *bp = 0; + else + { + volume.writeblock((cachedblock_t *)&rext); + KillAnodeBitmap(); + } + } + } + break; + + case BMIBLKID: + + bp = &rbl->idx.large.bitmapindex[seqnr]; + if (!*bp) + { + if (fix) + { + adderror("bitmapindex block not found"); + error = RepairBitmapIndex(bp, seqnr); + if (error) + *bp = 0; + else + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + } + } + break; + + case IBLKID: + + if (rbl->options & MODE_SUPERINDEX) + { + error = GetResBlock(&blk, SBLKID, index, fix); + if (error) + { + free (blk.data); + return error; + } + + bp = &blk.data->indexblock.index[offset]; + } + else + { + bp = &rbl->idx.small.indexblocks[seqnr]; + } + if (!*bp) + { + if (fix) + { + adderror("anodeindex block not found"); + error = RepairAnodeIndex(bp, seqnr); + if (error) + *bp = 0; + else + { + /* the anodebitmap, which is made per aib, + * could be too small + */ + KillAnodeBitmap(); + if (rbl->options & MODE_SUPERINDEX) + volume.writeblock((cachedblock_t *)&blk); + else + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + } + } + } + break; + + case ABLKID: + + error = GetResBlock(&blk, IBLKID, index, fix); + if (error) + { + free (blk.data); + return error; + } + + bp = &blk.data->indexblock.index[offset]; + if (!*bp) + { + if (fix) + { + adderror("anode block not found"); + // RepairAnodeBlock already called from RepairAnodeTree + // Pointless to call it again here. + //if (error = RepairAnodeBlock(bp, seqnr)) + // *bp = 0; + //else + // volume.writeblock((cachedblock_t *)&blk); + } + } + break; + + case BMBLKID: + + error = GetResBlock(&blk, BMIBLKID, index, fix); + if (error) + { + free (blk.data); + return error; + } + + bp = &blk.data->indexblock.index[offset]; + if (!*bp) + { + if (fix) + { + adderror("bitmap block not found"); + error = RepairBitmapBlock(bp, seqnr); + if (error) + *bp = 0; + else + volume.writeblock((cachedblock_t *)&blk); + } + } + break; + } + + blknr = *bp; + + free (blk.data); + if (!blknr) + return e_not_found; + + error = volume.getblock(blok, blknr); + if (error) + return error; + + return e_none; +} + +static c_anodeblock_t tablk = { 0 }; +static ULONG tanodedata[MAXRESBLOCKSIZE / 4]; +static anodeblock_t *tanodeblk = (anodeblock_t*)tanodedata; + +bool GetAnode(canode_t *anode, uint32 anodenr, bool fix) +{ + anodenr_t *split = (anodenr_t *)&anodenr; + + if (!(tablk.data && tanodeblk->seqnr == split->seqnr)) + { + tablk.data = tanodeblk; + if (GetResBlock((cachedblock_t *)&tablk, ABLKID, split->seqnr, fix)) + { + tablk.data = NULL; + return false; + } + } + + if (split->offset > ANODES_PER_BLOCK) + return false; + + anode->nr = anodenr; + anode->clustersize = tablk.data->nodes[split->offset].clustersize; + anode->blocknr = tablk.data->nodes[split->offset].blocknr; + anode->next = tablk.data->nodes[split->offset].next; + return true; +} + +bool SaveAnode(canode_t *anode, uint32 nr) +{ + anodenr_t *split = (anodenr_t *)&nr; + c_anodeblock_t ablk; + uint32 buffer[MAXRESBLOCKSIZE/4]; + + tablk.data = NULL; /* kill anode read cache */ + ablk.data = (anodeblock_t *)buffer; + if (GetResBlock((cachedblock_t *)&ablk, ABLKID, split->seqnr, false)) + return false; + + if (split->offset > ANODES_PER_BLOCK) + return false; + + ablk.data->nodes[split->offset].clustersize = anode->clustersize; + ablk.data->nodes[split->offset].blocknr = anode->blocknr; + ablk.data->nodes[split->offset].next = anode->next; + volume.writeblock((cachedblock_t *)&ablk); + return true; +} + +ULONG GetDEFileSize(struct direntry *direntry, struct extrafields *extra, ULONG *high) +{ + *high = 0; + if (rbl->options & MODE_LARGEFILE) { + *high = extra->fsizex; + return direntry->fsize; + } + return direntry->fsize; +} + +ULONG GetDDFileSize(struct deldirentry *dde, ULONG *high) +{ + *high = 0; + if ((rbl->options & MODE_LARGEFILE) && dde->filename[0] <= DELENTRYFNSIZE) { + *high = dde->fsizex; + return dde->fsize; + } + return dde->fsize; +} diff --git a/rom/filesys/pfs3/pfsdoctor/console.c b/rom/filesys/pfs3/pfsdoctor/console.c new file mode 100644 index 0000000000..7d90a84b5f --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/console.c @@ -0,0 +1,322 @@ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pfs3.h" +#include "doctor.h" + +enum mode mode = repair; /* check, repair, unformat or search */ + +struct Window *CheckRepairWnd; +struct stats stats; + +struct +{ + char name[200]; +} targetdevice; +static BOOL verbose = FALSE; +FILE *logfh = NULL; +static BOOL directscsi = FALSE; +static BOOL inhibited = FALSE; + +static const char *accessmodes[] = { "", "Standard", "Direct SCSI", "TD64", "NSD" }; + +void dummyMsg(char *message) +{ +} + +void guiMsg(const char *format, ...) +{ + va_list parms; + va_start (parms, format); + vprintf (format, parms); + va_end (parms); +} + +void guiUpdateStats(void) +{ +} + +void guiStatus(int level, char *message, long maxval) +{ + printf("%s...\n", message); +} + +void guiProgress(int level, long progress) +{ +} + +int guiAskUser(char *message, char *okstr, char *cancelstr) +{ + char ch = 0; + for (;;) { + printf("%s\n", message); + printf("1=<%s> 0=<%s>\n", okstr, cancelstr); + scanf("%c", &ch); + if (ch == '1') + return 1; + if (ch == '0') + return 0; + } +} + +#define OV_Flags LDF_DEVICES|LDF_VOLUMES +static BOOL OpenVolume(void) +{ + struct DosList *dl; + ULONG cylsectors, b; + BOOL t; + int i; + UBYTE *detectbuf; + + /* get device doslist */ + dl = LockDosList (OV_Flags|LDF_READ); + dl = FindDosEntry(dl, targetdevice.name, OV_Flags); + + if (dl && dl->dol_Type == DLT_VOLUME) + { + struct DosList *dl2; + struct DosInfo *di; + + di = (struct DosInfo *)BADDR(((struct RootNode *)DOSBase->dl_Root)->rn_Info); + for (dl2 = (struct DosList *)BADDR(di->di_DevInfo); + dl2; + dl2 = (struct DosList *)BADDR(dl2->dol_Next)) + { + if (dl2->dol_Type == DLT_DEVICE && dl->dol_Task == dl2->dol_Task) + { + unsigned char *dname; + + dl = dl2; + dname = (char *)BADDR(dl->dol_Name); + strncpy(targetdevice.name, dname+1, *dname); + targetdevice.name[*dname] = 0; + break; + } + } + } + + if (!dl || dl->dol_Type == DLT_VOLUME) + { + UnLockDosList(OV_Flags|LDF_READ); + guiMsg("DEVICE "); guiMsg(targetdevice.name); + guiMsg(" not found\nEXITING ...\n\n"); + return FALSE; + } + + UnLockDosList(OV_Flags|LDF_READ); + + /* inhibit device */ + targetdevice.name[strlen(targetdevice.name)] = ':'; + targetdevice.name[strlen(targetdevice.name)] = 0; + if (!(inhibited = Inhibit(targetdevice.name, DOSTRUE))) + { + guiMsg("Device could not be inhibited.\nEXITING ...\n\n"); + return FALSE; + } + + /* init volume structure */ + memset(&volume, 0, sizeof(volume)); + volume.fssm = (struct FileSysStartupMsg *)BADDR(dl->dol_misc.dol_handler.dol_Startup); + volume.dosenvec = (struct DosEnvec *)BADDR(volume.fssm->fssm_Environ); + strcpy(volume.devicename, targetdevice.name); + cylsectors = volume.dosenvec->de_Surfaces * volume.dosenvec->de_BlocksPerTrack; + volume.firstblock = volume.dosenvec->de_LowCyl * cylsectors; + volume.lastblock = (volume.dosenvec->de_HighCyl + 1) * cylsectors - 1; + b = volume.blocksize = volume.dosenvec->de_SizeBlock << 2; + for (i=-1; b; i++) + b >>= 1; + volume.blockshift = i; + volume.rescluster = 0; + volume.disksize = volume.lastblock - volume.firstblock + 1; + volume.lastreserved = volume.disksize - 256; /* temp value, calculated later */ + + volume.status = guiStatus; + volume.showmsg = guiMsg; + volume.askuser = guiAskUser; + volume.progress = guiProgress; + volume.updatestats = guiUpdateStats; + volume.getblock = vol_GetBlock; + volume.writeblock = vol_WriteBlock; + BCPLtoCString(volume.execdevice, (UBYTE *)BADDR(volume.fssm->fssm_Device)); + volume.execunit = volume.fssm->fssm_Unit; + + if (verbose) { + UBYTE name[FNSIZE]; + BCPLtoCString(name, (UBYTE *)BADDR(volume.fssm->fssm_Device)); + volume.showmsg("Device: %s:%lu\n", name, volume.fssm->fssm_Unit); + volume.showmsg("Firstblock: %lu\n", volume.firstblock); + volume.showmsg("Lastblock : %lu\n", volume.lastblock); + volume.showmsg("Blocksize : %lu\n", volume.blocksize); + } + + /* open device */ + if (!OpenDiskDevice(volume.fssm, &volume.port, &volume.request, &t)) + { + guiMsg("Device could not be opened.\nEXITING ...\n\n"); + return FALSE; + } + + InitCache(64, 32); /* make this configurable ? */ + + detectbuf = AllocVec(volume.blocksize, MEMF_PUBLIC); + if (!detectbuf) { + printf("Could not allocated %ld byte buffer.\n", volume.blocksize); + return FALSE; + } + if (!DetectAccessmode(detectbuf, directscsi)) { + printf("PFSDoctor failed to access this disk\n" + "above the 4G boundary after attempting\n" + "TD64, NSD and Direct SCSI\n"); + FreeVec(detectbuf); + return FALSE; + } + FreeVec(detectbuf); + printf("Autodetected disk access mode: %s\n", accessmodes[volume.accessmode]); + + if (volume.accessmode == ACCESS_DS) + { + volume.getrawblocks = dev_GetBlocksDS; + volume.writerawblocks = dev_WriteBlocksDS; + } + else + { + if (volume.accessmode == ACCESS_TD64) + volume.td64mode = TRUE; + else if (volume.accessmode == ACCESS_NSD) + volume.nsdmode = TRUE; + volume.getrawblocks = dev_GetBlocks; + volume.writerawblocks = dev_WriteBlocks; + } + + if (mode == check) + volume.writerawblocks = dev_WriteBlocksDummy; + + return TRUE; +} + +static void CloseVolume(void) +{ + FreeCache(); + + if (inhibited) + Inhibit(targetdevice.name, FALSE); + + if (volume.request) + { + if (!(CheckIO((struct IORequest *)volume.request))) + AbortIO((struct IORequest *)volume.request); + + WaitIO((struct IORequest *)volume.request); + CloseDevice((struct IORequest *)volume.request); + } + + if (volume.request) + DeleteIORequest(volume.request); + + if (volume.port) + DeleteMsgPort(volume.port); + + volume.request = NULL; + volume.port = NULL; +} + +#define TEMPLATE "DEVICE/A,CHECK/S,REPAIR/S,SEARCH/S,UNFORMAT/S,VERBOSE/S,LOGFILE/K,DIRECTSCSI/S" + +#define ARGS_DEVICE 0 +#define ARGS_CHECK 1 +#define ARGS_REPAIR 2 +#define ARGS_SEARCH 3 +#define ARGS_UNFORMAT 4 +#define ARGS_VERBOSE 5 +#define ARGS_LOGFILE 6 +#define ARGS_DIRECTSCSI 7 +#define ARGS_SIZE 8 + +int main(int argc, char *argv[]) +{ + struct RDArgs *rdarg; + LONG args[ARGS_SIZE] = { 0 }; + int cnt = 0; + uint32 opties; + + if (!(rdarg = ReadArgs (TEMPLATE, args, NULL))) + { + PrintFault (ERROR_REQUIRED_ARG_MISSING, "pfsdoctor"); + return RETURN_FAIL; + } + + strcpy(targetdevice.name, (char*)args[ARGS_DEVICE]); + + if (args[ARGS_VERBOSE]) + verbose = TRUE; + + if (args[ARGS_CHECK]) { + mode = check; + cnt++; + } + if (args[ARGS_REPAIR]) { + mode = repair; + cnt++; + } + if (args[ARGS_SEARCH]) { + mode = search; + cnt++; + } + if (args[ARGS_UNFORMAT]) { + mode = unformat; + cnt++; + } + + if (args[ARGS_DIRECTSCSI]) { + directscsi = TRUE; + } + + if (cnt == 0) { + printf("CHECK, REPAIR, SEARCH or UNFORMAT required.\n"); + return RETURN_FAIL; + } + if (cnt > 1) { + printf("Only one command (CHECK, REPAIR, SEARCH, UNFORMAT) parameter allowed.\n"); + return RETURN_FAIL; + } + + if (args[ARGS_LOGFILE]) { + logfh = fopen((char*)args[ARGS_LOGFILE], "w"); + if (!logfh) { + printf("Could not open log file '%s'\n", (char*)args[ARGS_LOGFILE]); + return RETURN_FAIL; + } + } + + if (mode == repair) + opties = SSF_FIX|SSF_ANALYSE|SSF_GEN_BMMASK; + else if (mode == unformat) + opties = SSF_UNFORMAT|SSF_FIX|SSF_ANALYSE|SSF_GEN_BMMASK; + else + opties = SSF_CHECK|SSF_ANALYSE|SSF_GEN_BMMASK; + + if (verbose) + opties |= SSF_VERBOSE; + + if (OpenVolume()) { + StandardScan(opties); + CloseVolume(); + } + + if (logfh) + fclose(logfh); + + FreeArgs(rdarg); + return 0; +} diff --git a/rom/filesys/pfs3/pfsdoctor/device.c b/rom/filesys/pfs3/pfsdoctor/device.c new file mode 100644 index 0000000000..6a08458d37 --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/device.c @@ -0,0 +1,672 @@ +/* $Id$ + * $Log: device.c $ + * Revision 2.5 1999/09/10 22:14:49 Michiel + * Bugfixes etc (1.4) + * + * Revision 2.4 1999/05/07 16:49:00 Michiel + * bugfixes etc + * + * Revision 2.3 1999/05/04 17:59:09 Michiel + * check mode, logfile, search rootblock implemented + * bugfixes + * + * Revision 2.2 1999/05/04 04:27:13 Michiel + * debugged upto buildrext + * + * Revision 2.1 1999/04/30 12:17:58 Michiel + * Accepts OK disks, bitmapfix and hardlink fix works + * + * Revision 1.2 1999/04/22 15:26:05 Michiel + * compiled + * + * Revision 1.1 1999/04/19 22:16:53 Michiel + * Initial revision + * + * Device Access functies, inclusief cache etc. + */ + +#define __USE_SYSBASE +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pfs3.h" +#include "doctor.h" + +#define BLOCKSHIFT (volume.blockshift) + +/************************************** + * Proto + **************************************/ + +static struct cacheline *c_GetCacheLine(uint32 bloknr); + +/************************************** + * Globals + **************************************/ + +struct cache cache; + +/************************************** + * Cache + * + * Cache module. Implemented as proxy on device. This module has a problem + * at the end of the disk: it does not take partial cachelines into + * account. This is no real problem since it is only used for the reserved + * area which is located at the start of the partition. + **************************************/ + +/* create cache + * cacheline size: 4-8-16 + * cachelines: 8-16-32-64-128 + * size = linesize * nolines * blocksize + */ +error_t InitCache(uint32 linesize, uint32 nolines) +{ + int i; + + /* allocate memory for the cache */ + if (!(cache.cachelines = calloc(nolines, sizeof(struct cacheline)))) + return e_out_of_memory; + + cache.linesize = linesize; + cache.nolines = nolines; + NewList((struct List *)&cache.LRUqueue); + NewList((struct List *)&cache.LRUpool); + for (i=0; idevice<. + */ +error_t c_GetBlock(uint8 *data, uint32 bloknr, uint32 bytes) +{ + uint32 lineblnr, offset, total; + struct cacheline *cl = NULL; + error_t error = e_none; + + __chkabort(); + for (total=0; totaldata + offset*volume.blocksize, volume.blocksize); + } + return error; +} + +/* write block (of size 'bytes') to cache + */ +error_t c_WriteBlock(uint8 *data, uint32 bloknr, uint32 bytes) +{ + uint32 lineblnr, offset, total; + struct cacheline *cl = NULL; + error_t error = e_none; + + __chkabort(); + for (total=0; totaldata + offset*volume.blocksize, data, volume.blocksize); + cl->dirty = true; + } + return error; +} + +/* (private) locale function to search a cacheline + */ +static struct cacheline *c_GetCacheLine(uint32 bloknr) +{ + uint32 i; + struct cacheline *cl = NULL; + error_t error; + + for (i=0; idirty) + error = volume.writerawblocks(cl->data, cache.linesize, cl->blocknr); + } + else + { + cl = HeadOf(&cache.LRUpool); + } + + error = volume.getrawblocks(cl->data, cache.linesize, bloknr); + if (error) + return NULL; + cl->blocknr = bloknr; + cl->dirty = false; + } + + MinRemove(cl); + MinAddHead(&cache.LRUqueue, cl); + return cl; +} + +/* update alle dirty blocks in the cache + */ +void UpdateCache(void) +{ + struct cacheline *cl; + error_t error; + + for (cl = HeadOf(&cache.LRUqueue); cl->next; cl=cl->next) + { + if (cl->dirty) + { + error = volume.writerawblocks(cl->data, cache.linesize, cl->blocknr); + if (error) + adderror("write error"); + } + } +} + +/* destroy cache + */ +void FreeCache(void) +{ + int i; + + if (cache.cachelines) + { + UpdateCache(); + for (i = 0; i < cache.nolines; i++) + FreeBufMem(cache.cachelines[i].data); + + free(cache.cachelines); + cache.cachelines = NULL; + } +} + + +/************************************** + * Device functions + **************************************/ + +/* OpenDiskDevice + * + * Open the diskdevice passed in 'startup' and generate the + * messageport and request structure, returned in 'port' + * and 'request' resp. + * + * in startup + * out port, request, trackdisk + */ +BOOL OpenDiskDevice(struct FileSysStartupMsg *startup, struct MsgPort **port, + struct IOExtTD **request, BOOL *trackdisk) +{ + UBYTE name[FNSIZE]; + + *port = CreateMsgPort(); + if(*port) + { + *request = (struct IOExtTD *)CreateIORequest(*port, sizeof(struct IOExtTD)); + if(*request) + { + BCPLtoCString(name, (UBYTE *)BADDR(startup->fssm_Device)); + *trackdisk = (strcmp(name, "trackdisk.device") == 0) || (strcmp(name, "diskspare.device") == 0); + if(OpenDevice(name, startup->fssm_Unit, (struct IORequest *)*request, + startup->fssm_Flags) == 0) + return TRUE; + } + } + return FALSE; +} + +/* + * Get blocks from device. Blocknrs are disk (not partition!) based + */ +error_t dev_GetBlocks(uint8 *buffer, int32 blocks, uint32 blocknr) +{ + struct IOExtTD *request; + uint32 io_length, io_transfer, io_offset, io_actual = 0, io_startblock = 0; + uint8 *io_buffer; + int retry = 4; + +retry_read: + if(blocknr == -1) // blocknr of uninitialised anode + return 1; + + io_length = blocks << volume.blockshift; + io_offset = blocknr << volume.blockshift; + io_buffer = buffer; + if (volume.td64mode || volume.nsdmode) { + // upper 32 bit of offset + io_actual = blocknr >> (32-volume.blockshift); + io_startblock = blocknr; + } + + while (io_length > 0) + { + io_transfer = min(io_length, volume.dosenvec->de_MaxTransfer); + io_transfer &= ~(volume.blocksize-1); + request = volume.request; + request->iotd_Req.io_Command = CMD_READ; + request->iotd_Req.io_Length = io_transfer; + request->iotd_Req.io_Data = io_buffer; // bufmemtype ?? + request->iotd_Req.io_Offset = io_offset; + if (volume.td64mode) { + request->iotd_Req.io_Actual = io_actual; + request->iotd_Req.io_Command = TD_READ64; + } else if (volume.nsdmode) { + request->iotd_Req.io_Actual = io_actual; + request->iotd_Req.io_Command = NSCMD_TD_READ64; + } + + if (DoIO((struct IORequest*)request)) + { + volume.showmsg("READ ERROR\n"); + if (!retry--) + return e_read_error; + goto retry_read; + } + io_buffer += io_transfer; + io_length -= io_transfer; + if (volume.td64mode || volume.nsdmode) { + io_startblock += (io_transfer >> volume.blockshift); + io_offset = io_startblock << volume.blockshift; + io_actual = io_startblock >> (32-volume.blockshift); + } else { + io_offset += io_transfer; + } + } + + return e_none; +} + + +error_t dev_GetBlocksDS(uint8 *buffer, int32 blocks, uint32 blocknr) +{ + uint8 cmdbuf[10]; + uint32 transfer, maxtransfer; + int retry = 4; + +retry_read: + if(blocknr == -1) // blocknr of uninitialised anode + return 1; + + /* chop in maxtransfer chunks */ + maxtransfer = volume.dosenvec->de_MaxTransfer >> volume.blockshift; + while (blocks > 0) + { + transfer = min(blocks,maxtransfer); + *((UWORD *)&cmdbuf[0]) = 0x2800; + *((ULONG *)&cmdbuf[2]) = blocknr; + *((ULONG *)&cmdbuf[6]) = transfer<<8; + if (!DoSCSICommand(buffer,transfer<de_MaxTransfer >> volume.blockshift; + while (blocks > 0) + { + transfer = min(blocks,maxtransfer); + *((UWORD *)&cmdbuf[0]) = 0x2a00; + *((ULONG *)&cmdbuf[2]) = blocknr; + *((ULONG *)&cmdbuf[6]) = transfer<<8; + if (!DoSCSICommand(buffer,blocks<> (32 - BLOCKSHIFT); + io_startblock = blocknr; + } + + while(io_length > 0) + { + io_transfer = min(io_length, volume.dosenvec->de_MaxTransfer); + io_transfer &= ~(volume.blocksize-1); + request = volume.request; + request->iotd_Req.io_Command = CMD_WRITE; + request->iotd_Req.io_Length = io_transfer; + request->iotd_Req.io_Data = io_buffer; // bufmemtype ?? + request->iotd_Req.io_Offset = io_offset; + if (volume.td64mode) { + request->iotd_Req.io_Actual = io_actual; + request->iotd_Req.io_Command = TD_WRITE64; + } else if (volume.nsdmode) { + request->iotd_Req.io_Actual = io_actual; + request->iotd_Req.io_Command = NSCMD_TD_WRITE64; + } + + if (DoIO((struct IORequest*)request)) + { + volume.showmsg("WRITE ERROR\n"); + if (!retry--) + return e_write_error; + goto retry_write; + } + io_buffer += io_transfer; + io_length -= io_transfer; + if (volume.td64mode || volume.nsdmode) { + io_startblock += (io_transfer >> volume.blockshift); + io_offset = io_startblock << volume.blockshift; + io_actual = io_startblock >> (32-volume.blockshift); + } else { + io_offset += io_transfer; + } + } + + return e_none; +} + +/* + * Allocation of memory for buffers + */ + +void *AllocBufMem(uint32 size) +{ + void *buffer; + + buffer = AllocVec(size, volume.dosenvec->de_BufMemType|MEMF_CLEAR); + + if (((uint32)buffer) & ~volume.dosenvec->de_Mask) + { + volume.showmsg ("Memory mask fails - aborting\n"); + FreeVec(buffer); + return NULL; + } + + return buffer; +} + +void FreeBufMem (void *mem) +{ + FreeVec (mem); +} + +/* + * Do direct scsi command + */ + +static uint8 sense[20]; +static struct SCSICmd scsicmd; + +int DoSCSICommand(UBYTE *data, ULONG datalen, UBYTE *command, + UWORD commandlen, UBYTE direction) +{ + scsicmd.scsi_Data = (UWORD *)data; + scsicmd.scsi_Length = datalen; + scsicmd.scsi_Command = command; + scsicmd.scsi_CmdLength = commandlen; + scsicmd.scsi_Flags = SCSIF_AUTOSENSE | direction; /* SCSIF_READ or SCSIF_WRITE */ + scsicmd.scsi_SenseData = sense; + scsicmd.scsi_SenseLength = 18; + scsicmd.scsi_SenseActual = 0; + + volume.request->iotd_Req.io_Length = sizeof(struct SCSICmd); + volume.request->iotd_Req.io_Data = (APTR)&scsicmd; + volume.request->iotd_Req.io_Command = HD_SCSICMD; + DoIO((struct IORequest *)volume.request); + if (scsicmd.scsi_Status) + return 0; + else + return 1; +} + +/* Convert BCPL/Pascal string to a CString. + * dest: resulting c string + * src: source pascal string (no BPTR!) + */ +UBYTE *BCPLtoCString(STRPTR dest, UBYTE *src) +{ + UBYTE len, *to; + + len = *(src++); + len = min (len, PATHSIZE); + to = dest; + + while (len--) + *(dest++) = *(src++); + *dest = 0x0; + + return to; +} + +/* ACCESS MODE AUTODETECT */ + +#define BLOCKSHIFT (volume.blockshift) +#define BLOCKSIZE (volume.blocksize) + +static void fillbuffer(UBYTE *buffer, UBYTE data) +{ + memset (buffer, data + 1, BLOCKSIZE); +} +/* Check if at least one byte has changed */ +static BOOL testbuffer(UBYTE *buffer, UBYTE data) +{ + ULONG cnt; + + for (cnt = 0; cnt < BLOCKSIZE; cnt++) { + if (buffer[cnt] != data + 1) + return TRUE; + } + return FALSE; +} + +static BOOL testread_ds(UBYTE *buffer) +{ + UBYTE cmdbuf[10]; + UBYTE cnt; + + for (cnt = 0; cnt < 2; cnt++) { + ULONG capacity; + + fillbuffer(buffer, 0xfe); + /* Read Capacity */ + *((UWORD *)&cmdbuf[0]) = 0x2500; + *((ULONG *)&cmdbuf[2]) = 0; + *((ULONG *)&cmdbuf[6]) = 0; + if (!DoSCSICommand(buffer, 8, cmdbuf, 10, SCSIF_READ)) { + return FALSE; + } + capacity = *((ULONG*)buffer); + + if (volume.lastblock > capacity) { + return FALSE; + } + fillbuffer(buffer, cnt); + /* Read(10) */ + *((UWORD *)&cmdbuf[0]) = 0x2800; + *((ULONG *)&cmdbuf[2]) = volume.lastblock; + *((ULONG *)&cmdbuf[6]) = 1 << 8; + if (!DoSCSICommand(buffer, 1 << BLOCKSHIFT, cmdbuf, 10, SCSIF_READ)) { + return FALSE; + } + if (testbuffer(buffer, cnt)) { + return TRUE; + } + } + return FALSE; +} + +static BOOL testread_td(UBYTE *buffer) +{ + struct IOExtTD *io = volume.request; + UBYTE cnt; + + if (volume.accessmode == ACCESS_NSD) { + struct NSDeviceQueryResult nsdqr; + UWORD *cmdcheck; + nsdqr.SizeAvailable = 0; + nsdqr.DevQueryFormat = 0; + io->iotd_Req.io_Command = NSCMD_DEVICEQUERY; + io->iotd_Req.io_Length = sizeof(nsdqr); + io->iotd_Req.io_Data = (APTR)&nsdqr; + if (DoIO((struct IORequest*)io) != 0) + return FALSE; + if (!((io->iotd_Req.io_Actual >= 16) && (io->iotd_Req.io_Actual <= sizeof(nsdqr)) && (nsdqr.SizeAvailable == io->iotd_Req.io_Actual))) + return FALSE; + if(nsdqr.DeviceType != NSDEVTYPE_TRACKDISK) + return FALSE; + for(cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++) { + if(*cmdcheck == NSCMD_TD_READ64) + break; + } + if (*cmdcheck == 0) + return FALSE; + } + if (volume.accessmode == ACCESS_TD64) { + UBYTE err; + io->iotd_Req.io_Command = TD_READ64; + io->iotd_Req.io_Length = 0; + io->iotd_Req.io_Data = 0; + io->iotd_Req.io_Offset = 0; + io->iotd_Req.io_Actual = 0; + err = DoIO((struct IORequest*)io); + if (err != 0 && err != IOERR_BADLENGTH && err != IOERR_BADADDRESS) + return FALSE; + } + for (cnt = 0; cnt < 2; cnt++) { + fillbuffer(buffer, cnt); + io->iotd_Req.io_Command = CMD_READ; + io->iotd_Req.io_Length = BLOCKSIZE; + io->iotd_Req.io_Data = buffer; + io->iotd_Req.io_Offset = volume.lastblock << BLOCKSHIFT; + if (volume.accessmode >= ACCESS_TD64) { + io->iotd_Req.io_Actual = volume.lastblock >> (32 - BLOCKSHIFT); + io->iotd_Req.io_Command = volume.accessmode == ACCESS_NSD ? NSCMD_TD_READ64 : TD_READ64; + } + if (DoIO((struct IORequest*)io) != 0) + return FALSE; + if (testbuffer(buffer, cnt)) + return TRUE; + } + return TRUE; +} + +BOOL DetectAccessmode(UBYTE *buffer, BOOL scsidirectfirst) +{ + BOOL inside4G = volume.lastblock < (0x80000000ul >> (BLOCKSHIFT - 1)); + + if (scsidirectfirst) { + volume.accessmode = ACCESS_DS; + if (testread_ds(buffer)) + return TRUE; + } + + if (volume.lastblock < 262144) { + /* Don't bother with tests if small enough (<128M) */ + volume.accessmode = ACCESS_STD; + return TRUE; + } + + if (inside4G) { + /* inside first 4G? Try standard CMD_READ first. */ + volume.accessmode = ACCESS_STD; + if (testread_td(buffer)) + return TRUE; + volume.accessmode = ACCESS_DS; + /* It failed, we may have 1G limit A590 pre-7.0 ROM or CDTV SCSI, try DS */ + if (testread_ds(buffer)) + return TRUE; + volume.accessmode = ACCESS_STD; + return FALSE; + } + /* outside of first 4G, must use TD64, NSD or DS */ + volume.accessmode = ACCESS_NSD; + if (testread_td(buffer)) + return TRUE; + volume.accessmode = ACCESS_TD64; + if (testread_td(buffer)) + return TRUE; + volume.accessmode = ACCESS_DS; + if (testread_ds(buffer)) + return TRUE; + + volume.accessmode = ACCESS_STD; + return FALSE; +} + diff --git a/rom/filesys/pfs3/pfsdoctor/doctor.h b/rom/filesys/pfs3/pfsdoctor/doctor.h new file mode 100644 index 0000000000..6afa8747ec --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/doctor.h @@ -0,0 +1,499 @@ +/* $Id$ + * $Log: doctor.h $ + * Revision 2.8 1999/09/11 16:45:50 Michiel + * Versie 1.5 with Unformat and Repair nodos + * + * Revision 2.7 1999/09/10 22:15:35 Michiel + * Bugfixes etc (1.4) + * + * Revision 2.6 1999/05/28 05:07:33 Michiel + * Fixed bug occuring on empty directory blocks + * Added rbl.always fix; improved rbl.disksize fix + * Reduced cachesize + * + * Revision 2.5 1999/05/17 10:32:39 Michiel + * long filename support, verbose fixes + * + * Revision 2.4 1999/05/07 16:49:00 Michiel + * bugfixes etc + * + * Revision 2.3 1999/05/04 17:59:09 Michiel + * check mode, logfile, search rootblock implemented + * bugfixes + * + * Revision 2.2 1999/05/04 04:27:13 Michiel + * debugged upto buildrext + * + * Revision 2.1 1999/04/30 12:17:58 Michiel + * Accepts OK disks, bitmapfix and hardlink fix works + * + * Revision 1.1 1999/04/19 22:16:53 Michiel + * Initial revision + * + * + * Doctor headerfile + * date: 1999/03/01 + * author: Michiel + */ + +#define __USE_SYSBASE + + + +#ifndef min +#include +#define min(a,b) MIN(a,b) +#define max(a,b) MAX(a,b) +#endif + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; +typedef long int32; +typedef short int16; +typedef char int8; +typedef enum {false, true} bool; + +// last two bytes used for extended file size +#define DELENTRYFNSIZE 16 + +#define BOOTBLOCK 0 +#define ROOTBLOCK 2 +#define FNSIZE 108 +#define PATHSIZE 256 +#define FILENAMESIZE 32 +#define DNSIZE 32 +#define CMSIZE 80 +#define MAX_ENTRYSIZE (sizeof(struct direntry) + FILENAMESIZE + CMSIZE + 32) +#define LONGS_PER_BMB ((rbl->reserved_blksize/4)-3) +#define INDEX_PER_BLOCK ((rbl->reserved_blksize - 3*4) / sizeof(LONG)) +#define ANODES_PER_BLOCK ((rbl->reserved_blksize - 4*4) / sizeof(anode_t)) +#define FIRSTENTRY(blok) ((struct direntry*)((blok)->data->entries)) + +/************************************** + * TD64 support + **************************************/ + +#ifndef TD_READ64 +#define TD_READ64 24 +#define TD_WRITE64 25 +#define TD_SEEK64 26 +#define TD_FORMAT64 27 +#endif + +/************************************** + * NSD support + **************************************/ + +#ifndef NSCMD_DEVICEQUERY +#define NSCMD_DEVICEQUERY 0x4000 +#define NSCMD_TD_READ64 0xc000 +#define NSCMD_TD_WRITE64 0xc001 +#define NSDEVTYPE_TRACKDISK 5 +struct NSDeviceQueryResult +{ + ULONG DevQueryFormat; + ULONG SizeAvailable; + UWORD DeviceType; + UWORD DeviceSubType; + UWORD *SupportedCommands; +}; +#endif + +#define ACCESS_UNDETECTED 0 +#define ACCESS_STD 1 +#define ACCESS_DS 2 +#define ACCESS_TD64 3 +#define ACCESS_NSD 4 + +/************************************** + * Cache + **************************************/ + +/* blocknr has to be initialised on a never used blocknr + * to prevent it to be considered a loaded cacheline. + */ +#define CL_UNUSED 1 +struct cacheline +{ + struct cacheline *next; + struct cacheline *prev; + uint32 blocknr; /* 1 == unused */ + bool dirty; + uint8 *data; +}; + +struct cache +{ + struct MinList LRUqueue; + struct MinList LRUpool; + uint32 linesize; // linesize in blocks + uint32 nolines; + struct cacheline *cachelines; +}; + +extern struct cache cache; + +/************************************** + * Blocks + **************************************/ + +enum mode {check=0, build, repair, search, unformat, done}; +extern enum mode mode; + +/* mode for cached block can be: + * 'check' -- loaded in cache voor check etc --> indirect (by value) edit + * 'build' -- loaded in buildblock cache. --> direct edit (by reference) + */ +typedef struct { + uint32 blocknr; + enum mode mode; + reservedblock_t *data; +} cachedblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + dirblock_t *data; +} c_dirblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + indexblock_t *data; +} c_indexblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + extensionblock_t *data; +} c_extensionblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + deldirblock_t *data; +} c_deldirblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + anodeblock_t *data; +} c_anodeblock_t; + +typedef struct { + uint32 blocknr; + enum mode mode; + bitmapblock_t *data; +} c_bitmapblock_t; + +/* note: data in b allocated by build code */ +typedef struct buildblock +{ + struct buildblock *next; + struct buildblock *prev; + cachedblock_t b; +} buildblock_t; + +typedef struct canode +{ + ULONG clustersize; // number of blocks in cluster + ULONG blocknr; // the block number + ULONG next; // next anode (anode number), 0 = eof + ULONG nr; // the anode number +} canode_t; + +extern rootblock_t *rbl; +extern c_extensionblock_t rext; + +/************************************** + * StandardScan + **************************************/ + +/* abort flag, filled by trap */ +extern bool aborting; + +/* maxima + */ +#define MAX_PASS 7 + +/* options for StandardScan() (flags) + */ +#define SSF_CHECK 1 /* check consistency? */ +#define SSF_FIX 2 /* fix errors? */ +#define SSF_ANALYSE 4 /* analyse volume, count things etc ? */ +#define SSF_UNFORMAT 8 /* undo fast format */ + +/* option flags + */ +#define SSF_GEN_RESBITMAP 16 +#define SSF_GEN_ANODEBITMAP 32 +#define SSF_GEN_MAINBITMAP 64 +#define SSF_GEN_BMMASK (SSF_GEN_RESBITMAP|SSF_GEN_ANODEBITMAP|SSF_GEN_MAINBITMAP) +#define SSF_VERBOSE 128 + +/* ready flags + */ + +/* errors + */ +typedef enum { + e_none = 0, /* no error */ + e_aborted, + e_dirty, + e_remove, + e_repartition, + e_empty, + e_not_found, + e_out_of_memory, + e_fatal_error, + e_rbl_not_found, + e_max_pass_exceeded, + e_res_bitmap_fail, + e_main_bitmap_fail, + e_anode_bitmap_fail, + e_block_outside_partition, + e_block_outside_reserved, + e_options_error, + e_direntry_error, + e_invalid_softlink, + e_anode_error, + e_reserved_area_error, + e_outside_bitmap_error, + e_double_allocation, + e_number_error, + e_syntax_error, + e_read_error, + e_write_error, + e_alloc_fail +} error_t; + +/************************************** + * Bitmap + **************************************/ + +#define BM_ENABLED 1 +#define BM_REBUILD 2 +#define BM_COMPARE 4 +#define BM_FINISHED 8 + +#define InvalidBitmap(bitmap) (bitmap->valid = false) +#define IsBitmapValid(bitmap) (bitmap && bitmap->valid) + +typedef struct +{ + bool valid; /* fix only possible if valid */ + uint32 errorsfound; + uint32 errorsfixed; + uint32 start; + uint32 stop; + uint32 step; + uint32 lwsize; /* size in longwords */ + + uint32 *map; +} bitmap_t; + +/************************************** + * Volume + **************************************/ + +typedef struct { + /* initialise size */ + uint32 firstblock; /* abs blocknr, first and last block */ + uint32 lastblock; + uint32 disksize; /* disksize in blocks */ + uint32 lastreserved; /* rel blocknr, last reserved block */ + uint32 blocksize; /* physical blocksize in bytes */ + int16 blockshift; + uint32 rescluster; + + /* info + */ + char diskname[34]; + int fnsize; + + /* Access */ + error_t (*getblock)(cachedblock_t *blok, uint32 bloknr); + error_t (*writeblock)(cachedblock_t *blok); + + /* status + * keep message if message==NULL + * status 0 = diskname + */ + void (*status)(int level, char *message, long maxval); + void (*progress)(int level, long progress); + void (*updatestats)(void); + void (*showmsg)(const char *format, ...); + int (*askuser)(char *message, char *okstr, char *cancelstr); + + /* flags */ + bool repartitioned; + int accessmode; + bool td64mode, nsdmode; + int standardscan; /* 0=not done/needed, 1=fixed, -1=not fixable */ + + /* bitmaps */ + bitmap_t *mainbitmap; + bitmap_t *anodebitmap; + bitmap_t *resbitmap; + + /* full scan */ + struct MinList buildblocks; /* elements are of type buildblock */ + + /* private */ + struct FileSysStartupMsg *fssm; + struct DosEnvec *dosenvec; + char devicename[FNSIZE]; + char execdevice[FNSIZE]; + int execunit; + + /* device stuff */ + struct MsgPort *port; + struct IOExtTD *request; + error_t (*getrawblocks)(uint8 *data, int32 numblock, uint32 bloknr); + error_t (*writerawblocks)(uint8 *data, int32 numblock, uint32 bloknr); +} volume_t; + +extern volume_t volume; + +extern struct stats +{ + uint32 blocknr; + uint32 prevblknr; + int pass; + int blockschecked; + int numerrors; + int errorsfixed; + int numfiles; + int numdirs; + int numsoftlink; + int numhardlink; + int numrollover; + int fragmentedfiles; + int anodesused; +} stats; + +/************************************** + * Fullscan + **************************************/ + +typedef struct scanelement +{ + uint32 blocknr; + uint32 datestamp; +} scanelement_t; + +void InitFullScan(void); +void ExitFullScan(void); +error_t AllocBuildBlocks(void); +uint32 fs_AllocResBlock(void); +error_t Repartition(uint32 bloknr); +error_t BuildBootBlock(void); +error_t BuildRootBlock(rootblock_t *rbl); +error_t BuildRext(c_extensionblock_t *rext); +error_t BuildIndexBlock(c_indexblock_t *blk, uint16 bloktype, uint32 seqnr); +error_t BuildBitmapBlock(c_bitmapblock_t *blk, uint32 seqnr); +void SearchBlocks(scanelement_t el[], uint32 seqlow, uint32 seqhigh, uint32 start, uint32 stop, uint16 bloktype); +uint32 SearchBlock(uint16 bloktype, uint32 seqnr, uint32 last, uint32 datestamp, uint32 anodenr, uint32 parent); +uint32 SearchLastReserved(volume_t *vol); +uint32 SearchFileSystem(int32 startblok, int32 endblok); + +/**********************************************************************/ +/* Lists */ +/**********************************************************************/ +#define MinAddHead(list, node) AddHead((struct List *)(list), (struct Node *)(node)) +#define MinAddTail(list, node) AddTail((struct List *)(list), (struct Node *)(node)) +#define MinInsert(list, node, listnode) Insert((struct List *)list, (struct Node *)node, (struct Node *)listnode) +#define MinRemove(node) Remove((struct Node *)node) +#define HeadOf(list) ((void *)((list)->mlh_Head)) +#define IsHead(node) (!((node)->prev->prev)) +#define IsTail(node) (!((node)->next->next)) +#ifndef IsMinListEmpty +#define IsMinListEmpty(x) ( ((x)->mlh_TailPred) == (struct MinNode *)(x) ) +#endif + +/* NewList/Remove as macro: + * NewList(x) \ + * (x)->mlh_Head = (struct MinNode *)&((x)->mlh_Tail); \ + * (x)->mlh_Tail = NULL; \ + * (x)->mlh_TailPred = (struct MinNode *)(x) + * + * Remove(x) \ + * (x)->prev->next = (x)->next; + * (x)->next->prev = (x)->prev; + */ + + +/************************************** + * Proto + **************************************/ + +BOOL OpenDiskDevice(struct FileSysStartupMsg *startup, struct MsgPort **port, struct IOExtTD **request, BOOL *trackdisk); +error_t dev_GetBlocks(uint8 *buffer, int32 blocks, uint32 blocknr); +error_t dev_GetBlocksDS(uint8 *buffer, int32 blocks, uint32 blocknr); +error_t dev_WriteBlocksDS(uint8 *buffer, int32 blocks, uint32 blocknr); +error_t dev_WriteBlocks(uint8 *buffer, int32 blocks, uint32 blocknr); +error_t dev_WriteBlocksDummy(uint8 *buffer, int32 blocks, uint32 blocknr); +void *AllocBufMem(uint32 size); +void FreeBufMem (void *mem); +int DoSCSICommand(UBYTE *data, ULONG datalen, UBYTE *command, UWORD commandlen, UBYTE direction); +UBYTE *BCPLtoCString(STRPTR dest, UBYTE *src); +BOOL DetectAccessmode(UBYTE *buffer, BOOL scsidirectfirst); + +/* reflect updated stats in gui */ +void guiUpdateStats(void); +void guiStatus(int level, char *message, long maxval); +void guiProgress(int level, long progress); +void guiMsg(const char *format, ...); +void dummyMsg(char *message); +int guiAskUser(char *message, char *okstr, char *cancelstr); + +/* functions to call when error found or fixed and new block started */ +void clearstats(void); +void adderror(char *message); +void fixederror(char *message); +void enterblock(uint32 blocknr); +void exitblock(void); + +/* device.c */ +error_t InitCache(uint32 linesize, uint32 nolines); +error_t c_GetBlock(uint8 *data, uint32 bloknr, uint32 bytes); +error_t c_WriteBlock(uint8 *data, uint32 bloknr, uint32 bytes); +void UpdateCache(void); +void FreeCache(void); + +/* access.c */ +BOOL AccessTest(void); +cachedblock_t *GetBuildBlock(uint16 bloktype, uint32 seqnr); +error_t GetResBlock(cachedblock_t *blok, uint16 bloktype, uint32 seqnr, bool fix); +bool GetAnode(canode_t *anode, uint32 anodenr, bool fix); +bool SaveAnode(canode_t *anode, uint32 nr); + +/* standardsscan.c */ +error_t StandardScan(uint32 opties); +bool GetPFS2Revision(char *vstring); +error_t vol_GetBlock(cachedblock_t *blok, ULONG bloknr); +error_t vol_WriteBlock(cachedblock_t *blok); +bool IsRootBlock(rootblock_t *r); +error_t ResBlockUsed(uint32 bloknr); +error_t RepairSuperIndex(uint32 *bloknr, uint32 seqnr); +error_t RepairAnodeIndex(uint32 *bloknr, uint32 seqnr); +error_t RepairAnodeBlock(uint32 *bloknr, uint32 seqnr); +error_t RepairBitmapIndex(uint32 *bloknr, uint32 seqnr); +error_t RepairBitmapBlock(uint32 *bloknr, uint32 seqnr); +error_t RepairIndexBlock(uint16 bloktype, error_t (*repairchild)(uint32 *, uint32), + uint32 *bloknr, uint32 seqnr); +void KillAnodeBitmap(void); +void KillMainBitmap(void); +void KillReservedBitmap(void); + +ULONG GetDEFileSize(struct direntry *direntry, struct extrafields *extra, ULONG *high); +ULONG GetDDFileSize(struct deldirentry *dde, ULONG *high); + +#if defined(__GNUC__) +static __inline void __chkabort(void) { }; +#endif +#if defined(__SASC) +void __regargs __chkabort(void); +#endif + diff --git a/rom/filesys/pfs3/pfsdoctor/fullscan.c b/rom/filesys/pfs3/pfsdoctor/fullscan.c new file mode 100644 index 0000000000..e4727c40dc --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/fullscan.c @@ -0,0 +1,695 @@ +/* $Id$ */ +/* $Log: fullscan.c $ + * Revision 2.5 1999/09/10 22:14:49 Michiel + * Bugfixes etc (1.4) + * + * Revision 2.4 1999/05/07 16:49:00 Michiel + * bugfixes etc + * + * Revision 2.3 1999/05/04 17:59:09 Michiel + * check mode, logfile, search rootblock implemented + * bugfixes + * + * Revision 2.2 1999/05/04 04:27:13 Michiel + * debugged upto buildrext + * + * Revision 2.1 1999/04/30 12:17:58 Michiel + * Accepts OK disks, bitmapfix and hardlink fix works + * + * Revision 1.1 1999/04/22 15:25:10 Michiel + * Initial revision + * */ + +#define __USE_SYSBASE +#include +#include + +#include "pfs3.h" +#include "doctor.h" +#include +#include +#include +#include +#include + +uint32 maxdatestamp = 0; + +void InitFullScan(void) +{ + NewList((struct List *)&volume.buildblocks); +} + +/* free mem etc */ +void ExitFullScan(void) +{ + buildblock_t *bbl, *next; + + for (bbl = HeadOf(&volume.buildblocks); (next=bbl->next); bbl=next) + { + if (bbl->b.data) + FreeBufMem(bbl->b.data); + free (bbl); + } +} + +/* Allocate create blocks. Write them to the cache and free + * memory. + * pre: resbitmap ready and valid + */ +error_t AllocBuildBlocks(void) +{ + buildblock_t *bbl, *next; + cachedblock_t tblk; + error_t error = e_none; + uint32 blocknr, offset, seqnr; + uint16 type; + + for (bbl = HeadOf(&volume.buildblocks); bbl->next; bbl=next) + { + volume.progress(0, 1); + next = bbl->next; + blocknr = fs_AllocResBlock(); + if (blocknr) + { + bbl->b.blocknr = blocknr; + bbl->b.mode = done; + volume.writeblock(&bbl->b); + switch(bbl->b.data->id) + { + case EXTENSIONID: + rbl->extension = blocknr; + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + rext.mode = check; + rext.data = calloc(1, SIZEOF_RESBLOCK); + rext.blocknr = blocknr; + memcpy(rext.data, bbl->b.data, SIZEOF_RESBLOCK); + break; + + case SBLKID: + rext.data->superindex[bbl->b.data->indexblock.seqnr] = blocknr; + volume.writeblock((cachedblock_t *)&rext); + break; + + case BMIBLKID: + rbl->idx.large.bitmapindex[bbl->b.data->indexblock.seqnr] = blocknr; + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + break; + + case IBLKID: + if (!(rbl->options & MODE_SUPERDELDIR)) + { + rbl->idx.small.indexblocks[bbl->b.data->indexblock.seqnr] = blocknr; + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + break; + } + type = SBLKID; + + case BMBLKID: + if (bbl->b.data->id == BMBLKID) + type = BMIBLKID; + + seqnr = bbl->b.data->indexblock.seqnr/INDEX_PER_BLOCK; + offset = bbl->b.data->indexblock.seqnr%INDEX_PER_BLOCK; + tblk.data = calloc(1, SIZEOF_RESBLOCK); + if (tblk.data) + { + GetResBlock(&tblk, type, seqnr, true); + tblk.data->indexblock.index[offset] = blocknr; + volume.writeblock(&tblk); + free(tblk.data); + } + else + { + adderror("memory allocation error"); + return e_out_of_memory; + } + break; + + } + MinRemove(bbl); + FreeBufMem(bbl->b.data); + free(bbl); + } + else + { + adderror("allocation error"); + error = e_alloc_fail; + } + } + + return error; +} + +/* Allocate a block from the generated reserved bitmap. + * This reserved bitmap must be complete and valid + */ +uint32 fs_AllocResBlock(void) +{ + uint32 i, field, blocknr; + int32 j; + + for (i=0; ilwsize; i++) + { + field = volume.resbitmap->map[i]; + if (field) + { + for (j=31; j>=0; j--) + { + if (field & (1 << j)) + { + blocknr = rbl->firstreserved + (i*32+(31-j))*volume.rescluster; + if (blocknr < rbl->lastreserved) + { + volume.resbitmap->map[i] &= ~(1 << j); + return blocknr; + } + } + } + } + } + + /* reserved area full */ + return 0; +} + +/* Redefine volume from rootblock located at 'bloknr' + */ +error_t Repartition(uint32 bloknr) +{ + rootblock_t *rbl; + error_t error = e_none; + + if (!(rbl = (rootblock_t *)AllocBufMem (MAXRESBLOCKSIZE))) + return e_out_of_memory; + + // read rootblock + error = c_GetBlock ((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + if (error) + goto ret_error; + + if (!IsRootBlock(rbl) || !rbl->disksize) + { + error = e_syntax_error; + goto ret_error; + } + + volume.firstblock = bloknr - ROOTBLOCK; + volume.lastblock = volume.firstblock + rbl->disksize - 1; + volume.disksize = rbl->disksize; + volume.lastreserved = rbl->lastreserved; + volume.repartitioned = true; + + ret_error: + + FreeBufMem(rbl); + return error; +} + +/* special case: no alloc needed */ +error_t BuildBootBlock(void) +{ + bootblock_t *bbl; + error_t error; + + if (!(bbl = AllocBufMem(2*volume.blocksize))) + return e_out_of_memory; + + memset (bbl, 0, 2*volume.blocksize); + bbl->disktype = ID_PFS_DISK; + error = c_WriteBlock ((UBYTE *)bbl, 1, BOOTBLOCK + volume.firstblock); + FreeBufMem (bbl); + return error; +} + +/* special case: no alloc needed + * Create rootblock. The datestamp is set to the highest value found by + * SeachBlocks, increased by 0x299. All references are uptodate. + */ +error_t BuildRootBlock(rootblock_t *rbl) +{ + struct DateStamp time; + uint32 i, j; + scanelement_t ind[104]; + uint32 resblocksize = 1024; + + volume.status(0, "Building rootblock", 3); + memset (rbl, 0, volume.blocksize); + DateStamp (&time); + + rbl->disktype = ID_PFS_DISK; + rbl->options = MODE_HARDDISK | MODE_SPLITTED_ANODES | MODE_DIR_EXTENSION | + MODE_SIZEFIELD | MODE_DATESTAMP | MODE_EXTROVING; +#if LARGE_FILE_SIZE + rbl->options |= MODE_LARGEFILE; + rbl->disktype = ID_PFS2_DISK; +#endif + + rbl->disksize = volume.disksize; + if (volume.disksize > MAXSMALLDISK) { + rbl->options |= MODE_SUPERINDEX; + if (volume.disksize > MAXDISKSIZE1K) { + rbl->disktype = ID_PFS2_DISK; + resblocksize = 2048; + if (volume.disksize > MAXDISKSIZE2K) + resblocksize = 4096; + } + } + + rbl->datestamp = ~0; // don't be a break on search functions + rbl->creationday = (UWORD)time.ds_Days; + rbl->creationminute = (UWORD)time.ds_Minute; + rbl->creationtick = (UWORD)time.ds_Tick; + rbl->protection = 0xf0; + rbl->firstreserved = 2; + rbl->lastreserved = SearchLastReserved(&volume); + volume.progress(0,1); + rbl->reserved_free = (rbl->lastreserved - rbl->firstreserved)/volume.rescluster; + + rbl->reserved_blksize = resblocksize; + rbl->blocksfree = volume.disksize - rbl->lastreserved; + rbl->alwaysfree = rbl->blocksfree/20; + + rbl->diskname[0] = strlen("FixedDisk"); + memcpy(rbl->diskname+1, "FixedDisk", strlen("FixedDisk")); + + /* extension is created by build extension */ + + /* create bitmap index */ + memset (ind, 0, 104*sizeof(scanelement_t)); + volume.status(1, "Searching bitmap blocks", rbl->lastreserved - 2); + SearchBlocks(ind, 0, 103, rbl->firstreserved, rbl->lastreserved, BMIBLKID); + volume.progress(0,1); + if (rbl->options & MODE_SUPERINDEX) + j = 104; + else + j = 5; + + for (i=0; iidx.large.bitmapindex[i] = ind[i].blocknr; + + /* create anodeindex */ + if (!(rbl->options & MODE_SUPERINDEX)) + { + memset (ind, 0, 104*sizeof(scanelement_t)); + volume.status(1,"Searching index blocks", rbl->lastreserved - 2); + SearchBlocks(ind, 0, 98, rbl->firstreserved, rbl->lastreserved, IBLKID); + for (i=0; i<99; i++) + rbl->idx.small.indexblocks[i] = ind[i].blocknr; + } + rbl->datestamp = maxdatestamp + 0x200; + volume.progress(0,1); + volume.status(1, " ", 100); + return e_none; +} + +/* Search deldirblocks, indexblocks, anodeblocks, bitmapblocks + * el = preallocated and cleared array of scanelement for storing result + * seqlow = lowest element array + * seqhigh = highest element array + * start = first blocknr + * stop = last blocknr + */ +void SearchBlocks(scanelement_t el[], uint32 seqlow, uint32 seqhigh, + uint32 start, uint32 stop, uint16 bloktype) +{ + cachedblock_t blk; + uint32 i, seqnr, ds = 0; + + blk.data = calloc(1, SIZEOF_RESBLOCK); + volume.status(1, NULL, (stop-start)/volume.rescluster); + for (i = start; i < stop; i+=volume.rescluster) + { + volume.progress(1,1); + if (volume.getblock(&blk, i)) + continue; + + if (aborting) + { + volume.showmsg("Aborting block search\n"); + aborting = 0; + break; + } + + if (blk.data->id == bloktype) + { + seqnr = blk.data->indexblock.seqnr; + ds = blk.data->indexblock.datestamp; + if (seqnr >= seqlow && seqnr <= seqhigh && + ds >= el[seqnr-seqlow].datestamp) + { + el[seqnr-seqlow].blocknr = i; + el[seqnr-seqlow].datestamp = ds; + maxdatestamp = max(ds, maxdatestamp); + } + } + } + + free (blk.data); + volume.status(1," ",100); +} + +/* search for a block on a volume. + * bloktype - kind of block to look for + * seqnr - longword on offset 8 (not for REXT) + * last - blocknr. Max is last + 32 + * datestamp - maximum datestamp on offset 4, 0 = no max + * anodenr, parent - for dirblocks only, 0 = ignore + * returns bloknr + * + * bloktypes: REXT, BM, BMI, S, AI, A, D + */ +uint32 SearchBlock(uint16 bloktype, uint32 seqnr, uint32 last, uint32 datestamp, uint32 anodenr, + uint32 parent) +{ + cachedblock_t blk; + uint32 ds, tmp; + int32 i, bloknr; // signed !! + int found = 0; + + blk.data = calloc(1, SIZEOF_RESBLOCK); + bloknr = ds = 0; + if (last) + { + last += 64; + last = min(rbl->lastreserved, last); + } + else + last = rbl->lastreserved; + last -= (last % volume.rescluster); + + if (!datestamp) + datestamp = rbl->datestamp; + + volume.status(1,NULL,1024); + for (i = last; i != last+volume.rescluster; i -= volume.rescluster) + { + /* rollover to end of reserved area */ + if (i < rbl->firstreserved) + { + if (last >= rbl->lastreserved - 4) + break; + i = rbl->lastreserved & ~(volume.rescluster-1); + } + + /* break if nothing found for 128 blocks, and already found promising + * candidate. Uses roundrobin allocation + */ + if (found && !--found) + break; + + volume.progress(1,1); + if (volume.getblock(&blk, i)) + continue; + + if (aborting) + { + // break search, don't break repair + volume.showmsg("Aborting block search\n"); + aborting = 0; + break; + } + + if (blk.data->id == bloktype) + { + if (blk.data->indexblock.seqnr != seqnr && bloktype != EXTENSIONID) + continue; + + if (anodenr && (blk.data->dirblock.anodenr != anodenr || + blk.data->dirblock.parent != parent)) + continue; + + if (datestamp && blk.data->dirblock.datestamp > datestamp) + continue; + + /* found block */ + tmp = (bloktype == EXTENSIONID) ? + (blk.data->extensionblock.datestamp) : + (blk.data->dirblock.datestamp); + + if (tmp >= ds) + { + if (datestamp) + { + if (tmp > datestamp) + continue; + + /* found a promising candidate ? */ + if (datestamp - tmp < 100) + found = 512; // 1.3: was 128 + } + ds = tmp; + bloknr = i; + } + } + } + + free (blk.data); + volume.status(1," ",100); + return (uint32)bloknr; +} + +/* Get last reserved block */ +uint32 SearchLastReserved(volume_t *vol) +{ + cachedblock_t blk; + uint32 i, last, cdwn; + + blk.data = calloc(1, SIZEOF_RESBLOCK); + cdwn = 4096; + i = last = vol->disksize/256; + i -= i % volume.rescluster; + vol->status(1, "Scanning disk", vol->disksize/16 - last); + while (i < vol->disksize/16) + { + vol->progress(1,1); + if (aborting) + { + volume.showmsg("Aborting filesystem search\n"); + aborting = 0; + break; + } + + if (volume.getblock(&blk, i)) + goto s_ret; + + switch (blk.data->id) + { + case DBLKID: + case ABLKID: + case IBLKID: + case BMBLKID: + case BMIBLKID: + case DELDIRID: + case EXTENSIONID: + case SBLKID: + + cdwn = 1024; + last = i; + break; + + default: + + /* Exit if blocks not reserved */ + if (!cdwn--) + goto s_ret; + } + + i += volume.rescluster; + } + + s_ret: + free (blk.data); + vol->status(1, " ", 100); + return last; +} + +/* Search filesystem on specified execdevice/unit. Start at startblok, + * end at endblok + */ +uint32 SearchFileSystem(int32 startblok, int32 endblok) +{ + rootblock_t *rbl; + uint32 blnr = 0, b; + + if (!(rbl = (rootblock_t *)AllocBufMem (MAXRESBLOCKSIZE))) + return e_out_of_memory; + + volume.status(0, "Searching filesystem", endblok-startblok); + startblok = max(startblok, 0); + startblok &= ~1; /* even bloknr */ + for (b = startblok; blastreserved-rbl->firstreserved)/volume.rescluster); + + if (!(r = rext->data = AllocBufMem(SIZEOF_RESBLOCK))) + return e_out_of_memory; + + if (!(bbl = malloc(sizeof(struct buildblock)))) + { + FreeBufMem(r); + return e_out_of_memory; + } + + memset (r, 0, volume.blocksize); + memset (bbl, 0, sizeof(*bbl)); + DateStamp (&time); + + r->id = EXTENSIONID; + r->datestamp = rbl->datestamp; + r->pfs2version = (17<<16) + 99; + r->root_date[0] = time.ds_Days; + r->root_date[1] = time.ds_Minute; + r->root_date[2] = time.ds_Tick; + + volume.progress(1, 1); + if (rbl->options & MODE_SUPERINDEX) + { + memset (ind, 0, 16*sizeof(scanelement_t)); + SearchBlocks(ind, 0, 15, rbl->firstreserved, rbl->lastreserved, SBLKID); + for (i=0; i<16; i++) + r->superindex[i] = ind[i].blocknr; + } + + /* add to list */ + bbl->b.blocknr = rext->blocknr = ~0; + bbl->b.mode = rext->mode = build; + bbl->b.data = (reservedblock_t *)r; + MinAddHead(&volume.buildblocks, bbl); + + volume.status(1, " ", 100); + return e_none; +} + + +/* zet mode to build + */ +error_t BuildIndexBlock(c_indexblock_t *blk, uint16 bloktype, uint32 seqnr) +{ + struct buildblock *bbl; + indexblock_t *ib; + int i; + uint16 childtype; + scanelement_t *ind; + + volume.status(1, "Building indexblock", INDEX_PER_BLOCK); + if (!(ind = calloc(INDEX_PER_BLOCK, sizeof(scanelement_t)))) + return e_out_of_memory; + + if (!(ib = blk->data = AllocBufMem(SIZEOF_RESBLOCK))) { + free(ind); + return e_out_of_memory; + } + + if (!(bbl = malloc(sizeof(struct buildblock)))) + { + FreeBufMem(ib); + free(ind); + return e_out_of_memory; + } + + memset (ib, 0, volume.blocksize); + memset (bbl, 0, sizeof(*bbl)); + + ib->id = bloktype; + ib->datestamp = rbl->datestamp; + ib->seqnr = seqnr; + + /* fill index */ + switch (bloktype) + { + case IBLKID: childtype = ABLKID; break; + case BMIBLKID: childtype = BMBLKID; break; + case SBLKID: childtype = IBLKID; break; + default: return e_fatal_error; + } + + SearchBlocks (ind, seqnr*INDEX_PER_BLOCK, seqnr*INDEX_PER_BLOCK + INDEX_PER_BLOCK - 1, rbl->firstreserved, rbl->lastreserved, childtype); + for (i=0; iindex[i] = ind[i].blocknr; + + /* add to list */ + bbl->b.blocknr = blk->blocknr = ~0; + bbl->b.mode = blk->mode = build; + bbl->b.data = (reservedblock_t *)ib; + MinAddHead(&volume.buildblocks, bbl); + + free(ind); + volume.status(1, " ", 100); + return e_none; +} + +error_t BuildBitmapBlock(c_bitmapblock_t *blk, uint32 seqnr) +{ + struct buildblock *bbl; + bitmapblock_t *bmb; + uint32 *bitmap; + int i; + + volume.status(1, "Building bitmapblock", 100); + if (!(bmb = blk->data = AllocBufMem(SIZEOF_RESBLOCK))) + return e_out_of_memory; + + if (!(bbl = malloc(sizeof(struct buildblock)))) + { + FreeBufMem(bmb); + return e_out_of_memory; + } + + memset (bmb, 0, volume.blocksize); + memset (bbl, 0, sizeof(*bbl)); + + bmb->id = BMBLKID; + bmb->datestamp = rbl->datestamp; + bmb->seqnr = seqnr; + + /* fill bitmap */ + bitmap = bmb->bitmap; + for (i = 0; ib.blocknr = blk->blocknr = ~0; + bbl->b.mode = blk->mode = build; + bbl->b.data = (reservedblock_t *)bmb; + MinAddHead(&volume.buildblocks, bbl); + + volume.status(1, " ", 100); + return e_none; +} + diff --git a/rom/filesys/pfs3/pfsdoctor/mmakefile.src b/rom/filesys/pfs3/pfsdoctor/mmakefile.src new file mode 100644 index 0000000000..a3b66dff5c --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/mmakefile.src @@ -0,0 +1,25 @@ +# $Id$ + +include $(TOP)/config/make.cfg + +FILES := \ + access \ + device \ + fullscan \ + standardscan \ + stats \ + console + +USER_CFLAGS := \ + -DLARGE_FILE_SIZE=1 \ + -DNO_GUI=1 \ + -DREVISION=19 \ + -DREVDATE="\"$(shell date '+%d.%m.%Y')\"" \ + -DADATE="\"$(shell date '+%d.%m.%Y')\"" + +EXEDIR = $(AROSDIR)/C + +%build_prog mmake=workbench-devs-pfs3-c-pfsdoctor \ + progname=PFSDoctor targetdir=$(EXEDIR) files=$(FILES) + +%common diff --git a/rom/filesys/pfs3/pfsdoctor/pfs3.h b/rom/filesys/pfs3/pfsdoctor/pfs3.h new file mode 100644 index 0000000000..c116216aef --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/pfs3.h @@ -0,0 +1,310 @@ +/* + * This file forms part of PFS3 (Professional File System 3) + * Written by Michiel Pelt + * + * PFS3 header file + */ + +#ifndef _PFS_H +#define _PFS_H 1 + +#ifndef EXEC_PORTS_H +#include "exec/ports.h" +#endif /* EXEC_PORTS_H */ + +#if defined(__GNUC__) || defined(__VBCC__) +/* Force SAS/C compatible alignment rules for all these structures */ +#pragma pack(2) +#endif + +/****************************************************************************/ +/* PFS specific actions (packets) */ +/****************************************************************************/ + +#define ACTION_SLEEP 2200 +#define ACTION_UPDATE_ANODE 2201 +#define ACTION_PFS_INFO 2202 +#define ACTION_PFS_CONFIG 2203 +#define ACTION_REMOVE_DIRENTRY 2204 +#define ACTION_CREATE_ROLLOVER 2205 +#define ACTION_SET_ROLLOVER 2206 +#define ACTION_IS_PFS2 2211 +#define ACTION_ADD_IDLE_SIGNAL 2220 +#define ACTION_SET_DELDIR 2221 +#define ACTION_SET_FNSIZE 2222 + +/****************************************************************************/ +/* PFS3 blocks */ +/****************************************************************************/ + +typedef struct bootblock +{ + LONG disktype; /* PFS\1 */ + UBYTE not_used[508]; +} bootblock_t; + +typedef struct rootblock +{ + LONG disktype; + ULONG options; /* bit 0 is harddisk mode */ + ULONG datestamp; + UWORD creationday; /* days since Jan. 1, 1978 (like ADOS; WORD instead of LONG) */ + UWORD creationminute; /* minutes past midnight */ + UWORD creationtick; /* ticks past minute */ + UWORD protection; /* protection bits (ala ADOS) */ + UBYTE diskname[32]; /* disk label (pascal string) */ + ULONG lastreserved; /* reserved area. blocknumbers */ + ULONG firstreserved; + ULONG reserved_free; /* number of reserved blocks (blksize blocks) free */ + UWORD reserved_blksize; /* size of reserved blocks in bytes */ + UWORD rblkcluster; /* number of blocks in rootblock, including bitmap */ + ULONG blocksfree; /* blocks free */ + ULONG alwaysfree; /* minimum number of blocks free */ + ULONG roving_ptr; /* current LONG bitmapfield nr for allocation */ + ULONG deldir; /* (before 4.3) deldir location */ + ULONG disksize; /* disksize in sectors */ + ULONG extension; /* rootblock extension */ + ULONG not_used; + union + { + struct + { + ULONG bitmapindex[5]; /* 5 bitmap indexblocks */ + ULONG indexblocks[99]; /* 99 indexblocks */ + } small; + struct + { + ULONG bitmapindex[104]; /* 104 bitmap indexblock */ + } large; + } idx; +} rootblock_t; + + +typedef struct rootblockextension +{ + UWORD id; /* id ('EX') */ + UWORD not_used_1; + ULONG ext_options; + ULONG datestamp; + ULONG pfs2version; /* pfs2 format revision */ + UWORD root_date[3]; /* root directory datestamp */ + UWORD volume_date[3]; /* volume datestamp */ + ULONG postponed_op[4]; /* postponed operation (private) */ + ULONG reserved_roving; /* reserved roving pointer */ + UWORD rovingbit; /* bitnr in rootblock->roving_ptr field */ + UWORD curranseqnr; /* anode allocation roving pointer */ + UWORD deldirroving; /* (5.1) deldir roving pointer */ + UWORD deldirsize; /* (5.1) size of deldir in blocks, 0 is disabled */ + UWORD fnsize; /* (5.1) filename size */ + UWORD not_used_2[3]; + ULONG superindex[16]; /* (4.2) MODE_SUPERINDEX only */ + UWORD dd_uid; /* (5.1) deldir user id (17.9) */ + UWORD dd_gid; /* (5.1) deldir group id */ + ULONG dd_protection; /* (5.1) deldir protection */ + UWORD dd_creationday; /* (5.1) deldir datestamp */ + UWORD dd_creationminute; + UWORD dd_creationtick; + UWORD not_used_3; + ULONG deldir[32]; /* (5.1) 32 deldir blocks */ + ULONG not_used_4[188]; +} extensionblock_t; + +/* structure for both normal as reserved bitmap + * normal: normal clustersize + * reserved: directly behind rootblock. As long as necessary. + */ +typedef struct bitmapblock +{ + UWORD id; /* 'BM' (bitmap block) */ + UWORD not_used; + ULONG datestamp; + ULONG seqnr; + ULONG bitmap[253]; /* the bitmap. */ +} bitmapblock_t; + +typedef struct indexblock +{ + UWORD id; /* 'AI', 'BI', or 'SB' */ + UWORD not_used; + ULONG datestamp; + ULONG seqnr; + LONG index[253]; /* the indices */ +} indexblock_t; + +typedef struct +{ + UWORD seqnr; + UWORD offset; +} anodenr_t; + +typedef struct anode +{ + ULONG clustersize; + ULONG blocknr; + ULONG next; +} anode_t; + +typedef struct anodeblock +{ + UWORD id; /* 'AB' */ + UWORD not_used; + ULONG datestamp; + ULONG seqnr; + ULONG not_used_2; + struct anode nodes[84]; +} anodeblock_t; + +typedef struct dirblock +{ + UWORD id; /* 'DB' */ + UWORD not_used; + ULONG datestamp; + UWORD not_used_2[2]; + ULONG anodenr; /* anodenr belonging to this directory (points to FIRST block of dir) */ + ULONG parent; /* parent */ + UBYTE entries[0]; /* entries */ +} dirblock_t; + +struct direntry +{ + UBYTE next; /* sizeof direntry */ + BYTE type; /* dir, file, link etc */ + ULONG anode; /* anode nummer */ + ULONG fsize; /* sizeof file */ + UWORD creationday; /* days since Jan. 1, 1978 (like ADOS; WORD instead of LONG) */ + UWORD creationminute; /* minutes past modnight */ + UWORD creationtick; /* ticks past minute */ + UBYTE protection; /* protection bits (like DOS) */ + UBYTE nlength; /* lenght of filename */ + UBYTE startofname; /* filename, followed by filenote length & filenote */ + UBYTE pad; /* make size even */ +}; + +struct extrafields +{ + ULONG link; /* link anodenr */ + UWORD uid; /* user id */ + UWORD gid; /* group id */ + ULONG prot; /* byte 1-3 of protection */ + ULONG virtualsize; /* virtual rollover filesize */ + ULONG rollpointer; /* rollover fileoffset */ + UWORD fsizex; /* extended bits 32-47 of direntry.fsize */ +}; + +struct deldirentry +{ + ULONG anodenr; /* anodenr */ + ULONG fsize; /* size of file */ + UWORD creationday; /* datestamp */ + UWORD creationminute; + UWORD creationtick; + UBYTE filename[16]; /* filename; filling up to 30 chars */ + // was previously filename[18] + // now last two bytes used for extended file size + UWORD fsizex; /* extended bits 32-47 of fsize */ +}; + +typedef struct deldirblock +{ + UWORD id; /* 'DD' */ + UWORD not_used; + ULONG datestamp; + ULONG seqnr; + UWORD not_used_2[3]; + UWORD uid; /* user id */ + UWORD gid; /* group id */ + ULONG protection; + UWORD creationday; + UWORD creationminute; + UWORD creationtick; + struct deldirentry entries[0]; /* 31 entries */ +} deldirblock_t; + +union reservedblock +{ + UWORD id; + + anodeblock_t anodeblock; + bitmapblock_t btmapblock; + bootblock_t bootblock; + dirblock_t dirblock; + deldirblock_t deldirblock; + indexblock_t indexblock; + rootblock_t rootblock; + extensionblock_t extensionblock; +}; + +typedef union reservedblock reservedblock_t; + +/* limits */ +#define MAXSMALLBITMAPINDEX 4 +#define MAXBITMAPINDEX 103 +#define MAXNUMRESERVED (4096 + 255*1024*8) +#define MAXSUPER 15 +#define MAXSMALLINDEXNR 98 +#define MAXDELDIRSEQNR 31 + +/* maximum disksize in blocks, limited by number of bitmapindexblocks */ +#define MAXSMALLDISK (5*253*253*32) +#define MAXDISKSIZE1K (104*253*253*32) +#define MAXDISKSIZE2K (104*509*509*32) +#define MAXDISKSIZE4K ((ULONG)104*1021*1021*32) + +#define MAXRESBLOCKSIZE 4096 + +/* disk id 'PFS\1' */ +#define ID_PFS_DISK (0x50465301L) /* 'PFS\1' */ +#define ID_PFS2_DISK (0x50465302L) /* 'PFS\2' */ +#define ID_MUPFS_DISK (0x6d755046L) /* 'muPF' */ +#define ID_AFS_DISK (0x41465301L) /* 'AFS\1' */ + +/* block id's */ +#define DBLKID 0x4442 +#define ABLKID 0x4142 +#define IBLKID 0x4942 +#define BMBLKID 0x424D +#define BMIBLKID 0x4D49 +#define DELDIRID 0x4444 +#define EXTENSIONID 0x4558 +#define SBLKID 0x5342 +#define SIZEOF_RESBLOCK (rbl->reserved_blksize) + + +/* predefined anodes */ +#define ANODE_EOF 0 +#define ANODE_BADBLOCKS 4 +#define ANODE_ROOTDIR 5 +#define ANODE_USERFIRST 6 + +/* disk options */ +#define MODE_HARDDISK 1 +#define MODE_SPLITTED_ANODES 2 +#define MODE_DIR_EXTENSION 4 +#define MODE_DELDIR 8 +#define MODE_SIZEFIELD 16 +#define MODE_EXTENSION 32 +#define MODE_DATESTAMP 64 +#define MODE_SUPERINDEX 128 +#define MODE_SUPERDELDIR 256 +#define MODE_EXTROVING 512 +#define MODE_LONGFN 1024 +#define MODE_LARGEFILE 2048 +#define MODE_MASK (4096-1) + +/* seperator used for deldirentries */ +#define DELENTRY_SEP '@' + +/* rollover filetype */ +#define ST_ROLLOVERFILE -16 + +/* get filenote from directory entry */ +#define FILENOTE(de) ((UBYTE*)(&((de)->startofname) + (de)->nlength)) + +/* get next directory entry */ +#define NEXTENTRY(de) ((struct direntry*)((UBYTE*)(de) + (de)->next)) + +#if defined(__GNUC__) || defined(__VBCC__) +#pragma pack() +#endif + +#endif /* _PFS_H */ diff --git a/rom/filesys/pfs3/pfsdoctor/standardscan.c b/rom/filesys/pfs3/pfsdoctor/standardscan.c new file mode 100644 index 0000000000..3847d1357f --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/standardscan.c @@ -0,0 +1,3076 @@ +/* $Id$ + * $Log: standardscan.c $ + * Revision 2.10 1999/09/11 16:45:50 Michiel + * Versie 1.5 with Unformat and Repair nodos + * + * Revision 2.9 1999/09/10 22:14:49 Michiel + * Bugfixes etc (1.4) + * + * Revision 2.8 1999/08/01 10:48:21 Michiel + * less verbose + * + * Revision 2.7 1999/05/28 05:07:33 Michiel + * Fixed bug occuring on empty directory blocks + * Added rbl.always fix; improved rbl.disksize fix + * Reduced cachesize + * + * Revision 2.6 1999/05/17 10:32:39 Michiel + * long filename support, verbose fixes + * + * Revision 2.5 1999/05/17 09:27:11 Michiel + * fixed logfile bug + * made verbose less verbose + * + * Revision 2.4 1999/05/07 16:49:00 Michiel + * bugfixes etc + * + * Revision 2.3 1999/05/04 17:59:09 Michiel + * check mode, logfile, search rootblock implemented + * bugfixes + * + * Revision 2.1 1999/04/30 12:17:58 Michiel + * Accepts OK disks, bitmapfix and hardlink fix works + * + * Revision 1.2 1999/04/22 15:23:50 Michiel + * compiled + * + * Revision 1.1 1999/04/19 22:16:53 Michiel + * Initial revision + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pfs3.h" +#include "doctor.h" + +long __stack = 30*1024; +extern struct Window *CheckRepairWnd; + +/************************************** + * Globals + **************************************/ + +struct +{ + /* state flags */ + uint32 flags; /* internal flags */ + uint32 opties; /* options */ + int pass; + BOOL verbose; + BOOL unformat; + + enum {syntax, resbitmap, mainbitmap, anodebitmap, finished} stage; + + struct MinList *doubles; +} ss; + +volume_t volume; +char bericht[256]; +rootblock_t *rbl= NULL; +c_extensionblock_t rext = { 0 }; +bool redosyntax; +bool aborting = 0; // used by break trap + +/************************************** + * Protos + **************************************/ + +static error_t mainStandardScan(uint32 flags); +static error_t exitStandardScan(error_t error); +static error_t ss_CheckDirTree(void); +static error_t vol_CheckBlockNr(ULONG *blocknr); +static error_t GetRootBlock(void); +static error_t CheckRootBlock(void); +static error_t GetRext(void); +static error_t RepairBootBlock(void); +static error_t RepairDeldir(void); +static bool dd_CheckBlock(uint32 bloknr, int seqnr); +static error_t RepairDirTree(void); +static error_t RepairDir(struct direntry *de, c_dirblock_t *parent); +static error_t RepairDirBlock(uint32 bloknr, uint32 anodenr, uint32 parent); +static error_t RepairDirEntry(struct direntry *de, c_dirblock_t *dirblk); +static error_t RepairFile(struct direntry *de, c_dirblock_t *parent); +static error_t RepairHardLink(struct direntry *de, c_dirblock_t *dirblok); +static error_t RepairSoftLink(struct direntry *de, c_dirblock_t *dirblok); +static error_t RepairRollover(struct direntry *de, c_dirblock_t *dirblok); +static error_t RemoveDirEntry(struct direntry *de, c_dirblock_t *dirblk); +static error_t DeleteAnode(uint32 anodechain, uint32 node); +static error_t GetExtraFields(struct extrafields *extrafields, struct direntry *de); +static error_t SetExtraFields(struct extrafields *extrafields, struct direntry *from, c_dirblock_t *dirblk); +static error_t RepairLinkChain(struct direntry *de, c_dirblock_t *dirblk); +static error_t CheckAnode(canode_t *anode, uint32 nr, bool fix); +static error_t RepairAnodeTree(void); +static error_t RepairBitmapTree(void); +static error_t InitReservedBitmap(void); +static error_t InitAnodeBitmap(void); +static error_t InitMainBitmap(void); +static error_t RepairReservedBitmap(void); +static error_t RepairMainBitmap(void); +static error_t RepairAnodeBitmap(void); +static error_t MainBlockUsed(uint32 bloknr); +static error_t AnodeUsed(uint32 bloknr); +static error_t bg_ItemUsed(bitmap_t *bm, uint32 nr); +static BOOL IsAnodeUsed(uint32 nr); +static uint32 vol_SearchFileSystem(void); +static bitmap_t *bg_InitBitmap(int32 start, int32 stop, int32 step); +static void bg_KillBitmap(bitmap_t **bm); +static BOOL bg_IsItemUsed(bitmap_t *bm, uint32 nr); +int SearchInDir(uint32 diranodenr, uint32 target); +static void AddExtraFields(struct direntry *direntry, struct extrafields *extra); +static bool MakeMountFile(char *fname); +static error_t BuildRootBlock_hub(void); + +/************************************** + * Break function L1 + **************************************/ + +#ifdef __SASC +int brk(void) +{ + aborting = 1; + return 0; +} + +void __regargs __chkabort(void) +{ + /* Check & clear CTRL_C signal */ + if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) + { + brk(); + } +} +#endif + +static BOOL FileSizeCheck(ULONG low, ULONG high, ULONG numblocks) +{ + ULONG size, oldlow; + + oldlow = low; + low += volume.blocksize - 1; + if (low < oldlow) + high++; + size = low >> volume.blockshift; + size |= high << (32 - volume.blockshift); + if (size == numblocks) + return TRUE; + return FALSE; +} + +/************************************** + * StandardScan L1 + **************************************/ + +// public interface +error_t StandardScan(uint32 opties) +{ + uint32 flags; + + aborting = 0; + // onbreak(&brk); + memset(&ss, sizeof(ss), 0); + opties = opties; + + flags = opties; + if (opties & (SSF_CHECK|SSF_FIX)) + flags |= SSF_GEN_BMMASK; + + /* init stats */ + memset(&stats, 0, sizeof(stats)); + return mainStandardScan(flags); +} + +// core standardscan +static error_t mainStandardScan(uint32 flags) +{ + error_t error; + + volume.showmsg("Initializing\n"); + ss.flags = flags; + ss.stage = syntax; + ss.pass = stats.pass = 1; + ss.verbose = flags & SSF_VERBOSE; + ss.unformat = flags & SSF_UNFORMAT; + + InitFullScan(); + + /* unformat .. */ + if (ss.unformat) + { + mode = repair; // !! niet really correct !! fix this + volume.showmsg("Unformatting...\n"); + if (!(rbl = (rootblock_t *)AllocBufMem (MAXRESBLOCKSIZE))) + { + adderror("couldn't allocate memory for rootblock"); + return exitStandardScan(e_out_of_memory); + } + if ((error = BuildRootBlock_hub()) || aborting) + return exitStandardScan(error); + } + + /* read rootblock */ + volume.status(0, "rootblock", 100); + if ((error = GetRootBlock()) || aborting) + return exitStandardScan(error); + + volume.progress(0, 40); + if ((error = RepairBootBlock()) || aborting) + return exitStandardScan(error); + + /* after the rootblock and bootblock the boundaries of the partition are known + */ + + /* read rootblockextension */ + volume.progress(0, 30); + rext.data = calloc(1, SIZEOF_RESBLOCK); + rext.mode = check; + if ((error = GetRext()) || aborting) + return exitStandardScan(error); + + GetPFS2Revision(bericht); + volume.showmsg("Disk formatted with "); + volume.showmsg(bericht); + volume.showmsg("\n"); + + /* stage 1 */ + volume.progress(0, 30); + while (ss.stage != finished && ss.pass < MAX_PASS) + { + stats.pass = ss.pass; + sprintf(bericht, "Starting pass %d\n", ss.pass); + volume.showmsg(bericht); + volume.updatestats(); + if (mode == check && stats.numerrors > 0) + break; + error = ss_CheckDirTree(); + if ((error != e_none) || aborting) + return exitStandardScan(error); + if (mode == check) + break; + ss.pass++; + } + + if (ss.stage != finished) + return e_max_pass_exceeded; + + volume.status(0, "finishing up", 2); + volume.progress(0, 1); + exitStandardScan(e_none); + volume.progress(0, 1); + return e_none; +} + +static error_t exitStandardScan(error_t error) +{ + UpdateCache(); + + if (rbl) + { + FreeBufMem(rbl); + rbl = NULL; + } + + if (rext.mode == check) + free (rext.data); + rext.data = NULL; + + KillReservedBitmap(); + KillAnodeBitmap(); + KillMainBitmap(); + ExitFullScan(); + + if ((error != e_none) || aborting) + volume.showmsg("ABORTED\n"); + else if (ss.unformat) + { + volume.askuser( + "Disk has been unformatted. Unformatting can\n" + "cause problems later. Therefor we recommend\n" + "to format this partition after backing up \n" + "all data\n", "OK", NULL); + } + return error; +} + + +/* main loop: check the partition defined by the rootblock and + * rext (rootblock extension) + */ +static error_t ss_CheckDirTree(void) +{ + error_t error; + int newpassneeded = 0; + + clearstats(); + redosyntax = false; + switch (ss.stage) + { + case syntax: + case resbitmap: + + /* initialize reserved bitmap generation */ + if (ss.flags & SSF_GEN_RESBITMAP) + InitReservedBitmap(); + + if (rext.mode == check) + if ((error = ResBlockUsed(rext.blocknr))) + return error; + + /* anode blok tree */ + /* if anodeindexblok faulty --> create (using DiskScan) */ + if ((error = RepairAnodeTree()) || aborting) + return error; + + /* bitmap */ + /* check syntax bitmap tree + * if block faulty --> create, kill bitmap, ss_makebm = false + * and continue */ + if ((error = RepairBitmapTree()) || aborting) + return error; + + case mainbitmap: + + if (ss.flags & SSF_GEN_MAINBITMAP) + InitMainBitmap(); + + case anodebitmap: + + if (ss.flags & SSF_GEN_ANODEBITMAP) + InitAnodeBitmap(); + + default: + + break; + } + + /* deldir */ + if ((error = RepairDeldir()) || aborting) + return error; + + /* directory tree */ + if ((error = RepairDirTree()) || aborting) + return error; + + /* on hardlink conflict */ + if (redosyntax) + { + ss.stage = syntax; + return e_none; + } + + switch (ss.stage) + { + case syntax: + case resbitmap: + + /* need another pass if blocks were created + * (should not be necessary!!) + */ + if (!IsMinListEmpty(&volume.buildblocks)) + newpassneeded = 1; + + /* allocating created blocks & reparing reserved bitmap can only + * be done after the reserved bitmap has been generated. + * ss.flags is set to false if generation was aborted. + */ + if (IsBitmapValid(volume.resbitmap)) + { + if ((error = AllocBuildBlocks())) + return error; + + if ((error = RepairReservedBitmap()) || aborting) + return error; + } + else + { + if (ss.stage < resbitmap) + { + ss.stage = resbitmap; + break; + } + else + return e_res_bitmap_fail; /* fatal error */ + } + + if (newpassneeded) + break; + + case mainbitmap: + case anodebitmap: + + /* bitmap generatie has been aborted (disabled during scan) + * generate main and anodebitmap + */ + if (!IsBitmapValid(volume.mainbitmap)) + { + if (ss.stage < mainbitmap) + { + ss.stage = mainbitmap; + break; + } + else + return e_main_bitmap_fail; /* fatal error */ + } + + if (!IsBitmapValid(volume.anodebitmap)) + { + if (ss.stage < anodebitmap) + { + ss.stage = anodebitmap; + break; + } + else + return e_anode_bitmap_fail; + } + + if ((error = RepairMainBitmap()) || aborting) + return error; + + if ((error = RepairAnodeBitmap()) || aborting) + return error; + + ss.stage = finished; + break; + + default: + // case doubledel: + + ss.stage = mainbitmap; + } + + /* Need another pass if blocks build (created) during + * scan. Calling allocbuildblocks here is pointless; + * the reserved bitmap is not valid because of invalid blocks, + * possibly during non reserved bitmap phase! + */ + if (!IsMinListEmpty(&volume.buildblocks)) + ss.stage = resbitmap; + + return e_none; +} + + +/************************************** + * Volume functions + **************************************/ + +/* functions for in cache struct + * gets block from reserved area and checks partition + * borders etc. + * status/errors: + * read/write errors + * e_none + * block_outside_reserved + * block_outside_partition + */ + +/* check if a block is in partition range */ +static error_t vol_CheckBlockNr(ULONG *blocknr) +{ + if (*blocknr > volume.lastreserved) { + + adderror("block outside reserved area"); + return e_block_outside_reserved; + } + + *blocknr += volume.firstblock; + if (*blocknr > volume.lastblock || *blocknr < volume.firstblock) { + + adderror("block outside partition"); + return e_block_outside_partition; + } + + return e_none; +} + + +error_t vol_GetBlock(cachedblock_t *blok, ULONG bloknr) +{ + error_t error; + ULONG realbloknr; + + realbloknr = bloknr; + if ((error = vol_CheckBlockNr(&realbloknr))) + return error; + + if ((error = c_GetBlock((uint8 *)blok->data, realbloknr, SIZEOF_RESBLOCK))) + { + return error; + } + else + { + blok->mode = check; + blok->blocknr = bloknr; + return e_none; + } +} + +error_t vol_WriteBlock(cachedblock_t *blok) +{ + int status; + uint32 bloknr; + + if (blok->mode != build) + { + bloknr = blok->blocknr; + if ((status = vol_CheckBlockNr(&bloknr))) + return status; + + return c_WriteBlock((uint8 *)blok->data, bloknr, SIZEOF_RESBLOCK); + } + + return e_none; +} + + +/************************************** + * GetRootBlock + **************************************/ + +// public functions + +static error_t BuildRootBlock_hub(void) +{ + error_t error; + + if (volume.askuser( + "Press ok to rebuild rootblock or cancel to exit\n" + "WARNING: make sure correct accessmode (direct-scsi/td64/nsd)\n" + "is selected", "ok", "cancel")) + { + if (!(error = BuildRootBlock(rbl))) + { + fixederror("new rootblock build"); + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + } + } + else + error = e_aborted; + + return error; +} + +/* read from disk and check + * Returns error (0 = ERROR_NONE = ok). + */ +static error_t GetRootBlock(void) +{ + error_t error = e_none; + uint32 bloknr; + int okuser; + struct FileRequester *freq; + char mfname[FNSIZE], *t; + bool ok = false; + + enterblock(ROOTBLOCK); + volume.showmsg("Checking rootblock\n"); + if (rbl) + FreeBufMem(rbl); + + if (!(rbl = (rootblock_t *)AllocBufMem (MAXRESBLOCKSIZE))) + { + adderror("couldn't allocate memory for rootblock"); + return e_out_of_memory; + } + + // read rootblock + if ((error = c_GetBlock ((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize))) + { + adderror("rootblock could not be loaded"); + return error; + } + + // check rootblock type + if (!IsRootBlock(rbl)) + { + adderror("not an AFS, PFS-II or PFS-III disk"); + okuser = volume.askuser("Rootblock not found.\n" + "Select ok to search for misplaced rootblock.", "ok", "cancel"); + + if (okuser && (bloknr = vol_SearchFileSystem())) + { + volume.showmsg("\nRootblock found. Repartitioning .. \n"); + if ((error = Repartition(bloknr))) + return error; + + volume.askuser( + "Partition is misplaced. Probable cause of this problem\n" + "is the use of the 'Get Drive Definition' option in\n" + "HDToolbox after the disk was formatted\n\n" + "Now a mountfile will be needed to mount the partition.\n" + "Press CONTINUE to select location to store the\n" + "mountlist", "CONTINUE", NULL); + + freq = AllocAslRequestTags(ASL_FileRequest, ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, "Select mountfile", ASLFR_InitialFile, "mountme", + ASLFR_InitialDrawer, "df0:", ASLFR_DoSaveMode, TRUE, TAG_DONE); + + do + { + if (AslRequestTags(freq, ASLFR_Window, CheckRepairWnd, TAG_DONE)) + { + t = stpcpy(mfname, freq->fr_Drawer); + t = stpcpy(t, freq->fr_File); + if ((ok = MakeMountFile(mfname))) + volume.showmsg("Mountlist written\n"); + } + else + { + volume.showmsg("no mountlist written\n"); + } + } while (!ok); + + FreeAslRequest(freq); + volume.askuser("Now starting to check/repair\n" + "relocated partition", "CONTINUE", NULL); + + return GetRootBlock(); + } + else + { + okuser = volume.askuser("Rootblock not found.\n" + "Select ok to build a new one.\n" + "WARNING: Make sure this is a PFS partition.\n" + "Non-PFS disks will be destroyed by this operation\n", + "ok", "cancel"); + + if (okuser) + return BuildRootBlock_hub(); + else + { + aborting = 1; + return e_aborted; + } + } + } + + error = CheckRootBlock(); + + switch (error) + { + case e_none: + break; + + case e_repartition: + if ((error = Repartition(volume.firstblock+ROOTBLOCK))) + return error; + + volume.askuser( + "The partition information stored in the RDB does\n" + "not match the partition information used when the\n" + "disk was formatted. Probable cause of this problem\n" + "is the use of the 'Get Drive Definition' option in\n" + "HDToolbox after the disk was formatted\n\n" + "Now a mountfile will be needed to mount the partition.\n" + "Press CONTINUE to select location to store the\n" + "mountlist", "CONTINUE", NULL); + + freq = AllocAslRequestTags(ASL_FileRequest, ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, "Select mountfile", ASLFR_InitialFile, "mountme", + ASLFR_InitialDrawer, "df0:", ASLFR_DoSaveMode, TRUE, TAG_DONE); + + do + { + if (AslRequestTags(freq, ASLFR_Window, CheckRepairWnd, TAG_DONE)) + { + t = stpcpy(mfname, freq->fr_Drawer); + t = stpcpy(t, freq->fr_File); + if ((ok = MakeMountFile(mfname))) + volume.showmsg("Mountlist written\n"); + } + else + { + volume.showmsg("no mountlist written\n"); + } + } while (!ok); + + FreeAslRequest(freq); + volume.askuser("Now starting to check/repair\n" + "redefined partition", "CONTINUE", NULL); + + return GetRootBlock(); + + case e_options_error: + return error; + + default: + return BuildRootBlock_hub(); + } + + exitblock(); + return e_none; +} + +static bool MakeMountFile(char *fname) +{ + FILE *mf; + + if ((mf = fopen(fname, "w"))) + { + fprintf(mf, "/****************************************/\n"); + fprintf(mf, "/* PFSDoctor generated mountlist */\n"); + fprintf(mf, "/****************************************/\n"); + fprintf(mf, "REPAIRED:\n"); + fprintf(mf, "\tDevice = %s\n", volume.execdevice); + fprintf(mf, "\tUnit = %d\n", volume.execunit); + fprintf(mf, "\tBuffers = 300\n"); + fprintf(mf, "\tBufMemType = %lu\n", volume.dosenvec->de_BufMemType); + fprintf(mf, "\tFlags = %lu\n", volume.fssm->fssm_Flags); + fprintf(mf, "\tDosType = %#lx\n", volume.dosenvec->de_DosType); + fprintf(mf, "\tGlobVec = -1\n"); + fprintf(mf, "\tInterleave = %ld\n", volume.dosenvec->de_Interleave); + fprintf(mf, "\tReserved = 2\n"); + fprintf(mf, "\tLowCyl = %lu\n", volume.firstblock); + fprintf(mf, "\tHighCyl = %lu\n", volume.lastblock); + fprintf(mf, "\tMount = 1\n"); + fprintf(mf, "\tSurfaces = 1\n"); + fprintf(mf, "\tBlocksPerTrack = 1\n"); + fprintf(mf, "\tPriority = 10\n"); + fprintf(mf, "\tStackSize = 600\n"); + fprintf(mf, "\tMaxTransfer = %lu\n", volume.dosenvec->de_MaxTransfer); + fprintf(mf, "\tMask = %#lx\n", volume.dosenvec->de_Mask); + fclose(mf); + return true; + } + return false; +} + +static uint32 vol_SearchFileSystem(void) +{ + return SearchFileSystem( + (volume.firstblock > 1024) ? volume.firstblock - 1024 : 0, + volume.firstblock + 1024); +} + +bool IsRootBlock(rootblock_t *r) +{ + ULONG modemask; + + // check rootblock type + if (r->disktype != ID_PFS_DISK && r->disktype != ID_PFS2_DISK) { + if (ss.verbose) + volume.showmsg("Unexpected rootblock id 0x%08lx\n", r->disktype); + return false; + } + + // check options + // require non-null options to accept rootblock as such, + // otherwise it could be a bootblock + modemask = MODE_HARDDISK + MODE_SPLITTED_ANODES + MODE_DIR_EXTENSION; + if ((r->options & modemask) != modemask) { + if (ss.verbose) + volume.showmsg("Unexpected rootblock options 0x%08lx\n", r->options); + return false; + } + + return true; +} + + +/* check loaded rootblock + */ +static error_t CheckRootBlock(void) +{ + int error = 0; + bool dirty = false; + ULONG modemask, resblocksize; + + modemask = MODE_HARDDISK + MODE_SPLITTED_ANODES + MODE_DIR_EXTENSION + + MODE_DELDIR + MODE_SIZEFIELD + MODE_EXTENSION + MODE_DATESTAMP + + MODE_SUPERINDEX + MODE_SUPERDELDIR + MODE_EXTROVING + MODE_LONGFN + + MODE_LARGEFILE; + + if (rbl->options & ~modemask) + { + adderror("Unknown options enabled. Ask for an upgrade."); + return e_options_error; + } + + // check the fields + if (!rbl->diskname[0]) + { + if (mode == check) + adderror("volume has no name"); + else + { + fixederror("volume had no name"); + rbl->diskname[0] = strlen("doctor"); + strcpy((char *)&rbl->diskname[1], "doctor"); + dirty = true; + } + } + + strncpy (volume.diskname, (char *)&rbl->diskname[1], rbl->diskname[0]); + volume.diskname[rbl->diskname[0]+1] = 0; + volume.showmsg("Checking disk "); + volume.showmsg(volume.diskname); + volume.showmsg("\n"); + volume.showmsg("Reserved blocksize: %lu\n", rbl->reserved_blksize); + + resblocksize = 1024; + if (rbl->disktype == ID_PFS2_DISK) { + if (volume.disksize > MAXDISKSIZE4K) { + adderror("too large (>1.6TB) partition size"); + return e_options_error; + } + if (volume.disksize > MAXDISKSIZE1K) { + resblocksize = 2048; + if (volume.disksize > MAXDISKSIZE2K) + resblocksize = 4096; + } + } else { + if (volume.disksize > MAXDISKSIZE1K) { + adderror("too large (>104GB) partition size"); + return e_options_error; + } + } + + if (rbl->reserved_blksize != resblocksize) + { + if (rbl->reserved_blksize == 1024 || rbl->reserved_blksize == 2048 || rbl->reserved_blksize == 4096) { + adderror("reserved blocksize is valid but does not match partition size. Driver size limit problem?"); + return e_options_error; + } + sprintf(bericht, "wrong reserved blocksize of %d", rbl->reserved_blksize); + fixederror(bericht); + rbl->reserved_blksize = resblocksize; + dirty = true; + } + + volume.rescluster = resblocksize / volume.blocksize; + + // size must match, else repartition + if (rbl->options & MODE_SIZEFIELD) + { + if(rbl->disksize != volume.disksize) + { + int32 difference; + + /* uses rule: if difference less then 10%, assume mistake + * in RDB, otherwise assume mistake in rootblock + */ + adderror("wrong disksize"); + volume.showmsg("Rootblock size: %lu, driver reported size: %lu\n", rbl->disksize, volume.disksize); + difference = abs((int32)rbl->disksize - (int32)volume.disksize); + if (difference < volume.disksize/10) + { + error = e_repartition; + } + else if (mode != check) + { + rbl->disksize = volume.disksize; + dirty = true; + fixederror("set disksize"); + } + } + } + + if (rbl->alwaysfree < volume.disksize/40 || + rbl->alwaysfree > volume.disksize/15) + { + fixederror("allocation block buffer out of range"); + rbl->alwaysfree = volume.disksize/20; + dirty = true; + } + + if (dirty) + return c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + + return error; +} + + +/************************************** + * GetRootBlockExtension + **************************************/ + +/* check rootblock extension. Do 'easy fix'. Returns + * false if failure + */ +static error_t GetRext(void) +{ + error_t error; + uint32 bloknr, oldbloknr; + + bloknr = rbl->extension; + enterblock(bloknr); + volume.showmsg("Checking rext\n"); + + if (bloknr) + { + if ((error = volume.getblock((cachedblock_t *)&rext, bloknr))) + return error; + } + + if (!bloknr || rext.data->id != EXTENSIONID) + { + adderror("extension block has wrong id"); + if (mode == repair) + { + volume.status(1, "Searching rext", 256); + oldbloknr = bloknr; + bloknr = SearchBlock(EXTENSIONID, 0, bloknr, 0, 0, 0); + if (bloknr) + { + sprintf(bericht, "replaced block %lu with backup %lu", oldbloknr, bloknr); + fixederror(bericht); + rbl->extension = bloknr; + return GetRext(); + } + else + { + volume.showmsg("No replacement block found\n"); + free (rext.data); + if ((error = BuildRext(&rext))) + adderror("building new rootblockextension failed"); + else + fixederror("new rootblockextension created"); + return error; + } + } + else + { + return e_fatal_error; + } + } + + // ResBlockUsed(bloknr); in initreservedbitmap + + /* check if enabled */ + if (!(rbl->options & MODE_EXTENSION)) + { + rbl->options |= MODE_EXTENSION; + volume.writeblock((cachedblock_t *)&rext); + if (mode == check) + adderror("MODE_EXTENSION is disabled"); + else + fixederror("MODE_EXTENSION was disabled"); + } + + /* fnsize */ + if (rbl->options & MODE_LONGFN) + { + if (rext.data->fnsize < 30 || rext.data->fnsize > 108) + { + sprintf(bericht, "illegal filename size of %d", rext.data->fnsize); + if (mode == check) + adderror(bericht); + else + { + if (rext.data->fnsize < 30) + { + fixederror("reset filename size to 30"); + rext.data->fnsize = 30; + } + else + { + fixederror("reset filename size to 100"); + rext.data->fnsize = 100; + } + volume.writeblock((cachedblock_t *)&rext); + } + } + volume.fnsize = rext.data->fnsize - 1; + } + else + { + volume.fnsize = 32 - 1; + } + + /* tbd fields */ + if (rext.data->postponed_op[0] || + rext.data->postponed_op[1] || + rext.data->postponed_op[2] || + rext.data->postponed_op[3]) + { + if (mode == check) + adderror("rootblock extension TBD not empty"); + else + { + rext.data->postponed_op[0] = + rext.data->postponed_op[1] = + rext.data->postponed_op[2] = + rext.data->postponed_op[3] = 0; + volume.writeblock((cachedblock_t *)&rext); + fixederror("rootblock extension TBD not empty"); + } + } + + exitblock(); + return e_none; +} + + +/* check loaded rootblock extension + */ +bool GetPFS2Revision(char *vstring) +{ + char buf[8]; + uint16 ver = rext.data->pfs2version >> 16; + uint16 rev = rext.data->pfs2version & 0xffff; + + if (!rext.data) + return false; + + if (ver < 16) + strcpy(buf, "AFS"); + else + if (ver == 16) + if (rev < 22) + strcpy(buf, "AFS"); + else + strcpy(buf, "PFS-II"); + else if (ver >= 18) + strcpy(buf, "PFS-III"); + else + strcpy(buf, "PFS-II"); + + sprintf(vstring, "%s %d.%d", buf, ver, rev); + + return true; +} + + + +/************************************** + * RepairBootBlock + **************************************/ + +/* read and check bootblock (not store) + */ +static error_t RepairBootBlock(void) +{ + error_t error; + cachedblock_t bootbl; + + enterblock(BOOTBLOCK); + + // read bootblock + bootbl.data = calloc(1, MAXRESBLOCKSIZE); + if ((error = volume.getblock ((cachedblock_t *)&bootbl, BOOTBLOCK))) + { + adderror("bootblock could not be loaded"); + free (bootbl.data); + return error; + } + + // check bootblock type + if (bootbl.data->bootblock.disktype != ID_PFS_DISK) + { + if (mode == check) + adderror("bootblock has wrong ID"); + else + { + bootbl.data->bootblock.disktype = ID_PFS_DISK; + fixederror("bootblock had wrong ID"); + if ((error = volume.writeblock((cachedblock_t *)&bootbl))) + { + adderror("bootblock could not be written"); + free (bootbl.data); + return error; + } + } + } + + free(bootbl.data); + exitblock(); + return e_none; +} + + +/************************************** + * RepairDeldir + **************************************/ + +/* Check plus fix deldir + */ +static error_t RepairDeldir(void) +{ + int i; + bool remove; + uint32 ddsize; + error_t error; + bool rextdirty = false, rootdirty = false; + + volume.showmsg("Checking deldir\n"); + + /* check deldir version */ + if (rbl->options & MODE_DELDIR) + { + if (rbl->options & MODE_SUPERDELDIR) + { + ddsize = rext.data->deldirsize; + volume.status(0, "Deldir", ddsize); + + for (i=0; i<32; i++) + { + if (rext.data->deldir[i]) + { + enterblock(rext.data->deldir[i]); + if (ss.verbose) + { + sprintf(bericht, "DELDIR %d\n", i); + volume.showmsg(bericht); + } + + remove = false; + if (i <= ddsize) + { + remove = true; + if (dd_CheckBlock(rext.data->deldir[i], i)) + { + if ((error = ResBlockUsed(rext.data->deldir[i]))) + remove = true; + else + remove = false; + } + } + + if (remove && mode == repair) + { + sprintf(bericht, "deldir block %d removed\n", i); + fixederror(bericht); + rext.data->deldir[i] = 0; + ddsize = min(ddsize, i+1); + rextdirty = true; + } + exitblock(); + } + else + { + ddsize = min(ddsize, i+1); + } + } + + if (ddsize != rext.data->deldirsize) + { + rext.data->deldirsize = ddsize; + rextdirty = true; + } + + if (rbl->deldir) + { + if (mode == check) + adderror("reference to old deldir"); + else + { + rbl->deldir = 0; + rootdirty = true; + fixederror("reference to old deldir removed"); + } + } + } + else /* old mode */ + { + if (!rbl->deldir) + { + if (mode == check) + adderror("deldir enabled, but missing"); + else + { + fixederror("deldir enabled, but missing"); + rbl->options &= ~MODE_DELDIR; + rootdirty = true; + } + } + else + { + remove = false; + if (dd_CheckBlock(rbl->deldir, 0)) + { + if ((error = ResBlockUsed(rbl->deldir))) + remove = true; + } + else + remove = true; + + if (remove && mode == repair) + { + fixederror("deldir block removed\n"); + rbl->deldir = 0; + rbl->options &= ~MODE_DELDIR; + rootdirty = true; + } + } + + } + } + else /* deldir disabled */ + { + if (rext.data->deldirsize) + { + if (mode == check) + adderror("deldir size incorrect"); + else + { + fixederror("deldir size incorrect"); + rext.data->deldirsize = 0; + rextdirty = true; + } + } + + if (mode == repair) + { + for (i=0; i<32; i++) + { + if (rext.data->deldir[i]) + { + fixederror("deldir block removed"); + rext.data->deldir[i] = 0; + rextdirty = true; + } + } + } + + if (rbl->deldir) + { + if (mode == check) + adderror("illegal deldir reference"); + else + { + fixederror("deldir block removed"); + rbl->deldir = 0; + rootdirty = true; + } + } + } + + if (rootdirty) + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + if (rextdirty) + volume.writeblock((cachedblock_t *)&rext); + + return e_none; +} + + +static bool dd_CheckBlock(uint32 bloknr, int seqnr) +{ + error_t error; + c_deldirblock_t ddblk; + int i, ddnr; + int32 blocks; + canode_t delnode; + struct deldirentry *dde; + ULONG size, high; + + volume.progress(0, 1); + + /* get block */ + ddblk.data = calloc(1, SIZEOF_RESBLOCK); + if ((error = volume.getblock((cachedblock_t *)&ddblk, bloknr))) + { + adderror("couldn't read deldirblock"); + free (ddblk.data); + return false; + } + + /* check id */ + if (ddblk.data->id != DELDIRID) + { + adderror("deldirblock error"); + free (ddblk.data); + return false; + } + + if (ddblk.data->seqnr != seqnr) + { + adderror("deldirblock error"); + free (ddblk.data); + return false; + } + + // check entries + for (i=0; i<31; i++) + { + blocks = 0; + dde = &ddblk.data->entries[i]; + if (!dde->anodenr) + continue; + + /* syntax fix */ + if (ss.stage == syntax) + { + for (ddnr = dde->anodenr; ddnr; ddnr = delnode.next) + { + if (!GetAnode (&delnode, ddnr, false) || IsAnodeUsed(ddnr)) + { + if (mode == check) + adderror("delfile anode error"); + else + { + fixederror("delfile anode error"); + dde->anodenr = 0; + volume.writeblock((cachedblock_t *)&ddblk); + } + break; + } + + blocks += (uint32)(delnode.clustersize); + } + + size = GetDDFileSize(dde, &high); + if (!FileSizeCheck(size, high, blocks)) + { + sprintf(bericht, "delfile anode %#lx error", dde->anodenr); + if (mode == check) + adderror(bericht); + else + { + fixederror(bericht); + dde->anodenr = 0; + volume.writeblock((cachedblock_t *)&ddblk); + } + } + } + + if (!dde->anodenr) + continue; + + for (ddnr = dde->anodenr; ddnr; ddnr = delnode.next) + { + GetAnode(&delnode, ddnr, false); + /* mark anodes as used */ + if (ss.stage <= anodebitmap) + AnodeUsed(ddnr); + + /* for dde only the anodes need to be checked */ + // if (ss.stage == doubledel && isAnodeDouble(ddnr)) + // dde->anodenr = 0; + } + } + + free(ddblk.data); + return true; +} + + +/************************************** + * Directory tree checker L2 + **************************************/ + +struct dirstack +{ + struct dirstack *parent; + char name[FNSIZE]; + uint32 anodenr; + char *objecttype; +}; + +#define pushdirstack(item) \ + (item)->parent = currentobject; \ + currentobject = (item) + +#define popdirstack() \ + currentobject = currentobject->parent + +struct dirstack *currentobject = NULL; +char objectname[1024] = { 0 }; +char dirtype[] = "DIR "; +char filetype[] = "FILE "; +char hardlinktype[] = "HARDLINK "; +char softlinktype[] = "SOFTLINK "; +char rollovertype[] = "ROLLOVER "; +char unknowntype[] = "OBJECT "; + +static void GetObjectName(void) +{ + struct dirstack *n, *ds; + char *b = objectname; + + objectname[0] = 0; + if (!currentobject) + return; + ds = NULL; + while (ds != currentobject) + { + for (n = currentobject; n->parent != ds; n=n->parent); + if (n->parent && n->parent->parent) + b = stpcpy(b, "/"); + b = stpcpy(b, n->name); + ds = n; + } +} + +static void fixedfileerror(char *errortxt) +{ + if (currentobject) + { + GetObjectName(); + volume.showmsg(currentobject->objecttype); + volume.showmsg(objectname); + volume.showmsg("\n"); + } + if (mode == check) + adderror(errortxt); + else + fixederror(errortxt); +} + +static void addfileerror(char *errortxt) +{ + if (currentobject) + { + GetObjectName(); + volume.showmsg(currentobject->objecttype); + volume.showmsg(objectname); + volume.showmsg("\n"); + } + adderror(errortxt); +} + +/* Check directory tree. Track bitmap if needed. + * Returns fatal error (0 = e_none. continue and errors fixed) + */ +static error_t RepairDirTree(void) +{ + struct dirstack stackbottom = { + NULL, "/", ANODE_ROOTDIR, dirtype }; + error_t error; + + volume.status(0, "root", 256); + volume.showmsg("Starting directory check\n"); + pushdirstack(&stackbottom); + error = RepairDir(NULL, NULL); + popdirstack(); + return error; +} + + +static error_t RepairDir(struct direntry *de, c_dirblock_t *parent) +{ + uint32 mainanodenr, anodenr, parentanodenr; + canode_t dirnode; + error_t error = e_none; + + stats.numdirs++; + if (de) + { + volume.status(0, currentobject->name, 0); + if (ss.verbose) + { + volume.showmsg("DIR "); + volume.showmsg(currentobject->name); + volume.showmsg("\n"); + } + + /* repair linkchain, remove if necessary */ + RepairLinkChain(de, parent); + } + + parentanodenr = parent ? parent->data->anodenr : 0; + mainanodenr = de ? de->anode : ANODE_ROOTDIR; + for (anodenr = mainanodenr; anodenr; anodenr=dirnode.next) + { + /* get anode */ + if (!GetAnode(&dirnode, anodenr, true)) + return e_remove; + + /* check anode */ + if (dirnode.clustersize != 1) + { + fixedfileerror("directory anode has illegal clustersize"); + dirnode.clustersize = 1; + SaveAnode(&dirnode, anodenr); + } + + enterblock(dirnode.blocknr); + error = RepairDirBlock(dirnode.blocknr, mainanodenr, parentanodenr); + exitblock(); + switch (error) + { + case e_empty: + + // BUG: an empty leading dirblock cannot be freed like + // that, the anodes have to moved! + // + // if (!dirnode.next) + // { + ResBlockUsed(dirnode.blocknr); + AnodeUsed(anodenr); + error = e_none; + break; + // } + // + // fixedfileerror("empty directory block"); + + case e_remove: + + if (error != e_empty) + { + if (mode == repair) + fixedfileerror("removing corrupt directory block"); + else + addfileerror("corrupt directory block"); + } + + DeleteAnode(mainanodenr, anodenr); + break; + + case e_none: + + ResBlockUsed(dirnode.blocknr); + AnodeUsed(anodenr); + break; + + default: + + KillReservedBitmap(); + } + + if ((error != e_none) || aborting) + return error; + } + + return e_none; +} + +static error_t RepairDirBlock(uint32 bloknr, uint32 anodenr, uint32 parent) +{ + c_dirblock_t dirblk; + struct direntry *entry; + error_t error; + + /* get block */ + dirblk.data = calloc(1, SIZEOF_RESBLOCK); + if ((error = volume.getblock((cachedblock_t *)&dirblk, bloknr))) + { + free (dirblk.data); + return e_remove; + } + + /* check dirblock */ + if (dirblk.data->id != DBLKID || dirblk.data->anodenr != anodenr || + dirblk.data->parent != parent) + { + free (dirblk.data); + return e_remove; + } + + entry = (struct direntry *)dirblk.data->entries; + + while (entry->next) + { + if ((error = RepairDirEntry(entry, &dirblk))) + { + if (mode == repair) + { + redosyntax = true; + fixedfileerror("removing corrupt directory entry"); + RemoveDirEntry(entry, &dirblk); + } + else + { + addfileerror("corrupt directory entry"); + entry = NEXTENTRY(entry); + } + } + else + entry = NEXTENTRY(entry); + } + + /* has to be done after main check, because an empty block can + * arise + */ + error = e_none; + if (mode == repair) + { + entry = (struct direntry *)dirblk.data->entries; + if (!entry->next) + error = e_empty; + } + + free (dirblk.data); + return error; +} + +static error_t RepairDirEntry(struct direntry *de, c_dirblock_t *dirblk) +{ + struct dirstack dirstack; + error_t error = e_none; + + volume.progress(0, 1); + if (!de || !de->next) + return e_empty; + + if (ss.stage == syntax) + { + if (de->next & 1) + { + addfileerror("odd directory entry length"); + return e_direntry_error; + } + + if (de->nlength + offsetof(struct direntry, nlength) > de->next) + { + addfileerror("invalid filename"); + return e_direntry_error; + } + + if (de->nlength > volume.fnsize) + { + addfileerror("filename too long"); + return e_direntry_error; + } + + if (*FILENOTE(de) + de->nlength + offsetof(struct direntry, nlength) > de->next) + { + addfileerror("invalid filenote"); + return e_direntry_error; + } + } + + pushdirstack(&dirstack); + strncpy(dirstack.name, (char *)&de->startofname, de->nlength); + dirstack.name[de->nlength] = 0; + dirstack.anodenr = de->anode; + dirstack.objecttype = unknowntype; + + switch (de->type) + { + case ST_USERDIR: + + dirstack.objecttype = dirtype; + error = RepairDir(de, dirblk); + break; + + case ST_SOFTLINK: + + dirstack.objecttype = softlinktype; + error = RepairSoftLink(de, dirblk); + break; + + case ST_LINKFILE: + case ST_LINKDIR: + + dirstack.objecttype = hardlinktype; + error = RepairHardLink(de, dirblk); + if (error == e_remove) + redosyntax = true; + break; + + case ST_ROLLOVERFILE: + + dirstack.objecttype = rollovertype; + error = RepairRollover(de, dirblk); + break; + + case ST_FILE: + + dirstack.objecttype = filetype; + error = RepairFile(de, dirblk); + break; + + case ST_PIPEFILE: + case ST_ROOT: + default: + + addfileerror("invalid filetype"); + error = e_direntry_error; + break; + } + + popdirstack(); + return error; +} + +static error_t RepairFile(struct direntry *de, c_dirblock_t *parent) +{ + int32 blokken; + struct extrafields extra; + canode_t filenode; + error_t error; + uint32 anodenr, bl; + ULONG size, high; + + stats.numfiles++; + if ((error = GetExtraFields(&extra, de))) + return error; + + /* check anode chain */ + if (ss.stage == syntax) + { + if (de->type != ST_ROLLOVERFILE && (extra.virtualsize || extra.rollpointer)) + { + extra.virtualsize = 0; + extra.rollpointer = 0; + SetExtraFields(&extra, de, parent); + fixedfileerror("dangling rollover"); + } + + blokken = 0; + for (anodenr = de->anode; anodenr; anodenr = filenode.next) + { + if ((error = CheckAnode(&filenode, anodenr, true))) + return e_remove; + + blokken += filenode.clustersize; + } + + size = GetDEFileSize(de, &extra, &high); + if (!FileSizeCheck(size, high, blokken)) + { + addfileerror("invalid filesize"); + return e_remove; + } + } + + if (extra.link) + RepairLinkChain(de, parent); + + /* bitmap gen */ + for (anodenr = de->anode; anodenr; anodenr = filenode.next) + { + GetAnode(&filenode, anodenr, true); + if ((error = AnodeUsed(anodenr))) + break; + + for (bl = filenode.blocknr; bl < filenode.blocknr + filenode.clustersize; bl++) + if ((error = MainBlockUsed(bl))) + break; + } + + if (error != e_none) + { + /* invalidate bitmaps to force generation in next stage. */ + InvalidBitmap(volume.mainbitmap); + InvalidBitmap(volume.anodebitmap); + return error; + } + + return e_none; +} + +/* Repair hardlink. If a hardlink is removed that has consequences for + * the linkchain and the linked to file. + */ +static error_t RepairHardLink(struct direntry *de, c_dirblock_t *dirblok) +{ + struct extrafields extra; + canode_t linknode; + error_t error; + + stats.numhardlink++; + if ((error = GetExtraFields(&extra, de))) + return e_remove; + + if (!extra.link) + { + addfileerror("invalid hardlink"); + return e_remove; + } + + /* get linknode */ + if (!GetAnode (&linknode, de->anode, true)) + return e_remove; + + /* check linknode linkdir (anode.blocknr) reference (objectdir is + * checked by CheckLinkChain) */ + if (linknode.blocknr != dirblok->data->anodenr) + { + addfileerror("invalid linknode directory reference"); + return e_remove; + } + + /* Check object reference + * Find anodenr in objectdir, with problem of possible + * corruption. Loose directories are not detected. + * It is not checked if the link itself is infact a link, + * just that it exists. + */ + switch (SearchInDir(linknode.clustersize, extra.link)) + { + case 0: + addfileerror("dangling hardlink"); + return e_remove; + + case ST_FILE: + case ST_ROLLOVERFILE: + case ST_SOFTLINK: + case ST_LINKFILE: + if (de->type == ST_LINKDIR) + { + fixedfileerror("invalid hardlink"); + de->type = ST_LINKFILE; + volume.writeblock((cachedblock_t *)dirblok); + } + break; + + default: + if (de->type == ST_LINKFILE) + { + fixedfileerror("invalid hardlink"); + de->type = ST_LINKDIR; + volume.writeblock((cachedblock_t *)dirblok); + } + break; + } + + if ((error = AnodeUsed(de->anode))) + return error; + + return e_none; +} + + +static error_t RepairSoftLink(struct direntry *de, c_dirblock_t *dirblok) +{ + stats.numsoftlink++; + if (de->fsize > 108) + return e_invalid_softlink; + + return RepairFile(de, dirblok); +} + +static error_t RepairRollover(struct direntry *de, c_dirblock_t *dirblok) +{ + struct extrafields extra; + error_t error; + + stats.numrollover++; + if ((error = GetExtraFields(&extra, de))) + return error; + + if (extra.virtualsize > de->fsize || + extra.rollpointer > de->fsize) + { + fixedfileerror("invalid rollover fields"); + extra.virtualsize = 0; + extra.rollpointer = 0; + SetExtraFields(&extra, de, dirblok); + } + + return RepairFile(de, dirblok); +} + + +/* + * Helper functions + */ + +static error_t RemoveDirEntry(struct direntry *de, c_dirblock_t *dirblk) +{ + uint8 *dest, *source; + uint32 movelen; + + dest = (uint8 *)de; + source = dest + de->next; + movelen = ((uint8 *)(dirblk->data) + SIZEOF_RESBLOCK) - dest; + if (movelen > 0 && movelen < SIZEOF_RESBLOCK) + { + memmove(dest, source, movelen); + volume.writeblock((cachedblock_t *)dirblk); + } + else + return e_fatal_error; + + return e_none; +} + + +/* remove node from anodechain + */ +static error_t DeleteAnode(uint32 anodechain, uint32 node) +{ + canode_t prev_node, next_node; + uint32 previous; + + /* node is head */ + if (anodechain == node) + { + if (!GetAnode(&next_node, node, false)) + return e_anode_error; + + if (!next_node.next || !GetAnode(&next_node, next_node.next, false)) + return e_anode_error; + + SaveAnode(&next_node, node); + } + else + { + /* find previous */ + previous = anodechain; + while (true) + { + if (!GetAnode(&prev_node, previous, false)) + return e_anode_error; + + if (prev_node.next == node) + break; // we found it + + previous = prev_node.next; + } + + GetAnode(&next_node, prev_node.next, false); + prev_node.next = next_node.next; + SaveAnode(&prev_node, prev_node.nr); + } + + return e_none; +} + +/* Get the directory entry extension fields + */ +static error_t GetExtraFields(struct extrafields *extrafields, struct direntry *de) +{ + uint16 *extra = (uint16 *)extrafields; + uint16 *fields = (uint16 *)(((uint8 *)de)+de->next); + uint16 flags, i; + + // extract extrafields from directoryentry + flags = *(--fields); + for (i=0; i < sizeof(struct extrafields)/2; i++, flags>>=1) + *(extra++) = (flags&1) ? *(--fields) : 0; + + // check flags field + if (flags) + addfileerror("unknown extrafield flags found (ignored)"); + + // patch protection lower 8 bits + extrafields->prot |= de->protection; + return e_none; +} + +/* Set extrafields. Requirement is that the new directory entry fits. + * To make sure it does, extrafields are removed only, not added. + */ +static error_t SetExtraFields(struct extrafields *extrafields, struct direntry *from, + c_dirblock_t *dirblk) +{ + uint8 entrybuffer[MAX_ENTRYSIZE]; + struct direntry *to; + uint8 *dest, *start, *end; + uint32 movelen; + int diff; + + to = (struct direntry *)entrybuffer; + memcpy(to, from, from->next); + AddExtraFields(to, extrafields); + + /* make room for new direntry */ + diff = to->next - from->next; + dest = (uint8 *)from + to->next; + start = (uint8 *)from + from->next; + end = (uint8 *)dirblk->data + SIZEOF_RESBLOCK; + movelen = (diff > 0) ? (end - dest) : (end - start); + memmove(dest, start, movelen); + + /* add new direntry */ + memcpy((uint8 *)from, to, to->next); + + return volume.writeblock((cachedblock_t *)dirblk); +} + +static void AddExtraFields(struct direntry *direntry, struct extrafields *extra) +{ + UWORD offset, *dirext; + UWORD array[16], i = 0, j = 0; + UWORD flags = 0, orvalue; + UWORD *fields = (UWORD *)extra; + + /* patch protection lower 8 bits */ + extra->prot &= 0xffffff00; + offset = (sizeof(struct direntry) + (direntry->nlength) + *FILENOTE(direntry)) & 0xfffe; + dirext = (UWORD *)((UBYTE *)(direntry) + (UBYTE)offset); + + orvalue = 1; + /* fill packed field array */ + for (i = 0; i < sizeof(struct extrafields) / 2; i++) + { + if (*fields) + { + array[j++] = *fields++; + flags |= orvalue; + } + else + { + fields++; + } + + orvalue <<= 1; + } + + /* add fields to direntry */ + i = j; + while (i) + *dirext++ = array[--i]; + *dirext++ = flags; + + direntry->next = offset + 2 * j + 2; +} + + +/* Repair the hardlinks chain referring to a file + */ +static error_t RepairLinkChain(struct direntry *de, c_dirblock_t *dirblk) +{ + struct extrafields extra; + canode_t linknode; + error_t error; + uint32 parent; + + /* check extra fields */ + if ((error = GetExtraFields(&extra, de))) + return error; + + /* check if object has a linkchain */ + if (!extra.link) + return e_none; + + if (!GetAnode(&linknode, extra.link, false)) + return e_anode_error; + + parent = dirblk->data->anodenr; + for (;;) + { + // check objectdir (clustersize) against actual objectdir + if (linknode.clustersize != parent) + { + fixedfileerror("invalid hardlink"); + linknode.clustersize = parent; + SaveAnode(&linknode, linknode.nr); + } + + // check linkdir left to HardLink::Check + if (!SearchInDir(linknode.blocknr, linknode.nr)) + { + fixedfileerror("dangling hardlink"); + if ((error = DeleteAnode(extra.link, linknode.nr)) + && (linknode.nr == extra.link)) + { + extra.link = 0; + SetExtraFields(&extra, de, dirblk); + } + } + + if (!linknode.next) + return e_none; + + if (!GetAnode (&linknode, linknode.next, false)) + return e_anode_error; + } +} + +/* Get & check an anode + */ +static error_t CheckAnode(canode_t *anode, uint32 nr, bool fix) +{ + /* if an anodeblock does not exist, it is considered empty */ + if (!GetAnode(anode, nr, fix)) + return e_empty; + + if (anode->clustersize == 0 && anode->blocknr == 0 && anode->next == 0) + return e_empty; + + return e_none; +} + +/* Search file by anodenr in a directory + * return filetype or 0 for not found + */ +int SearchInDir(uint32 diranodenr, uint32 target) +{ + canode_t anode; + c_dirblock_t dirblk; + struct direntry *de; + error_t error = e_none; + int ftype; + + /* Get directory and find object */ + dirblk.data = calloc(1, SIZEOF_RESBLOCK); + while (error == e_none && diranodenr) + { + if (!GetAnode(&anode, diranodenr, false)) + break; + + if ((error = volume.getblock((cachedblock_t *)&dirblk, anode.blocknr)) || aborting) + break; + + de = FIRSTENTRY(&dirblk); + while (de->next) + { + if (de->anode == target) + { + ftype = de->type; + free (dirblk.data); + return ftype; + } + + de = NEXTENTRY(de); + } + + diranodenr = anode.next; + } + + free (dirblk.data); + return 0; +} + +/************************************** + * Anode tree checker L2 + **************************************/ + +/* Check anode tree. + * Returns fatal errors (e_none = continue) + */ +static error_t RepairAnodeTree(void) +{ + int i; + error_t error; + uint32 child_blk_nr; + + volume.showmsg("Checking anodetree\n"); + if (rbl->options & MODE_SUPERINDEX) + { + volume.status(0, "Anodetree", 100); + + for (i=0; isuperindex[i])) + { + if ((error = RepairSuperIndex(&rext.data->superindex[i], i)) || aborting) + return error; + + if (rext.data->superindex[i] != child_blk_nr) + volume.writeblock((cachedblock_t *)&rext); + } + } + } + else + { + volume.status(0, "Anodetree", 100); + + for (i=0; iidx.small.indexblocks[i])) + { + if ((error = RepairAnodeIndex(&rbl->idx.small.indexblocks[i], i)) || aborting) + return error; + + if (rbl->idx.small.indexblocks[i] != child_blk_nr) + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + } + } + } + + return e_none; +} + +error_t RepairSuperIndex(uint32 *bloknr, uint32 seqnr) +{ + return RepairIndexBlock(SBLKID, RepairAnodeIndex, bloknr, seqnr); +} + +error_t RepairAnodeIndex(uint32 *bloknr, uint32 seqnr) +{ + volume.progress(0, 0); // reset progress bar + return RepairIndexBlock(IBLKID, RepairAnodeBlock, bloknr, seqnr); +} + +/* missing: check of first, reserved anodes. + * if blocknr is 0 --> search and build + */ +error_t RepairAnodeBlock(uint32 *bloknr, uint32 seqnr) +{ + error_t error = e_none; + c_anodeblock_t ablk; + uint32 oldbloknr; + + // get anodeblock + ablk.data = calloc(1, SIZEOF_RESBLOCK); + if (*bloknr) + { + if ((error = volume.getblock((cachedblock_t *)&ablk, *bloknr))) + { + free (ablk.data); + return error; + } + + enterblock(*bloknr); + } + + // check syntax block + if (ss.stage == syntax) + { + if (ablk.data->id != ABLKID || ablk.data->seqnr != seqnr) + { + if (*bloknr) + { + sprintf(bericht, "anodeblock seqnr %lu blocknr %lu has an incorrect id", seqnr, *bloknr); + adderror(bericht); + } + + if (mode == repair) + { + volume.status(1, "Searching anode block", 256); + oldbloknr = *bloknr; + if ((*bloknr = SearchBlock(ABLKID, seqnr, oldbloknr, 0, 0, 0))) + { + sprintf(bericht, "replaced block %lu with backup %lu", oldbloknr, *bloknr); + fixederror(bericht); + } + else + { + /* remove corrupt block */ + fixederror("no replacement found - removing block"); + free (ablk.data); + exitblock(); + return e_remove; + } + } + } + } + + ResBlockUsed(*bloknr); + free (ablk.data); + exitblock(); + return error; +} + +/************************************** + * Bitmap tree checker L2 + **************************************/ + +/* Check bitmap tree. + * Returns fatal errors (e_none = continue) + */ +static error_t RepairBitmapTree(void) +{ + error_t error = e_none; + uint32 i, num; + uint32 blknr; + + volume.status(0, "Bitmap", 100); + volume.showmsg("Checking bitmap\n"); + + num = ((volume.lastblock - volume.firstblock - rbl->lastreserved - 1) + 31) / 32; + num = (num + INDEX_PER_BLOCK*INDEX_PER_BLOCK - 1)/(INDEX_PER_BLOCK * INDEX_PER_BLOCK); + for (i=0; i < ((rbl->options & MODE_SUPERINDEX) ? (MAXBITMAPINDEX + 1) : (MAXSMALLBITMAPINDEX + 1)); i++) + { + blknr = rbl->idx.large.bitmapindex[i]; + if (i < num) + { + /* force bitmapindexblock to be searched and created */ + if (!blknr) + rbl->idx.large.bitmapindex[i] = rbl->lastreserved; + + if ((error = RepairBitmapIndex(&rbl->idx.large.bitmapindex[i], i)) || aborting) + return error; + + if (rbl->idx.large.bitmapindex[i] != blknr) + c_WriteBlock((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + } + } + + return e_none; +} + + +/* change blocknr if necessary + */ +error_t RepairBitmapIndex(uint32 *bloknr, uint32 seqnr) +{ + volume.progress(0, 0); // reset progress bar + return RepairIndexBlock(BMIBLKID, RepairBitmapBlock, bloknr, seqnr); +} + +error_t RepairBitmapBlock(uint32 *bloknr, uint32 seqnr) +{ + error_t error = e_none; + c_bitmapblock_t bmblk; + uint32 oldbloknr; + + // get anodeblock + bmblk.data = calloc(1, SIZEOF_RESBLOCK); + if (GetBuildBlock(BMBLKID, seqnr)) + return e_none; + + if (*bloknr) + { + if ((error = volume.getblock((cachedblock_t *)&bmblk, *bloknr))) + { + free (bmblk.data); + return error; + } + } + + enterblock(*bloknr); + + // check syntax block + if (ss.stage == syntax) + { + if (bmblk.data->id != BMBLKID || bmblk.data->seqnr != seqnr) + { + if (*bloknr) + { + sprintf(bericht, "bitmapblock seqnr %lu blocknr %lu has an incorrect id", seqnr, *bloknr); + adderror(bericht); + } + + if (mode == repair) + { + volume.status(1, "Searching bitmap block", 256); + oldbloknr = *bloknr; + if ((*bloknr = SearchBlock(BMBLKID, seqnr, oldbloknr, 0, 0, 0))) + { + sprintf(bericht, "replaced block %lu with backup %lu", oldbloknr, *bloknr); + fixederror(bericht); + } + else + { + volume.showmsg("No replacement block found\n"); + free (bmblk.data); + error = BuildBitmapBlock(&bmblk, seqnr); + exitblock(); + if (error) + adderror("building new bitmapblock failed"); + else + fixederror("new bitmapblock created"); + return error; + } + } + } + } + + ResBlockUsed(*bloknr); + free (bmblk.data); + exitblock(); + return error; +} + +/************************************** + * Check index blocks. + * Returns fatal errors (e_none = continue) + */ +error_t RepairIndexBlock(uint16 bloktype, error_t (*repairchild)(uint32 *, uint32), + uint32 *bloknr, uint32 seqnr) +{ + error_t error = e_none; + c_indexblock_t iblk = { 0 }; + cachedblock_t *cblk; + int i; + uint32 child_blk_nr, oldbloknr; + + enterblock(*bloknr); + + if ((cblk = GetBuildBlock(bloktype, seqnr))) + iblk = *(c_indexblock_t *)cblk; + + if (!iblk.data) + { + iblk.data = calloc(1, SIZEOF_RESBLOCK); + + /* read or build indexblock + */ + if ((error = volume.getblock((cachedblock_t *)&iblk, *bloknr))) + { + free (iblk.data); + return error; + } + + /* check id */ + if (ss.stage == syntax) + { + if (iblk.data->id != bloktype || iblk.data->seqnr != seqnr) + { + if (*bloknr) + { + sprintf(bericht, + "indexblock %lu blocknr %lu type %x has incorrect id of %x, %lu", + seqnr, *bloknr, bloktype, iblk.data->id, iblk.data->seqnr); + adderror(bericht); + } + + if (mode == repair) + { + volume.status(1, "Searching index block", 256); + oldbloknr = *bloknr; + if ((*bloknr = SearchBlock(bloktype, seqnr, oldbloknr, 0, 0, 0))) + { + sprintf(bericht, "replaced block %lu with backup %lu", oldbloknr, *bloknr); + fixederror(bericht); + free (iblk.data); + return RepairIndexBlock(bloktype, repairchild, bloknr, seqnr); + } + else + { + volume.showmsg("No replacement block found\n"); + free (iblk.data); + if ((error = BuildIndexBlock(&iblk, bloktype, seqnr))) + { + adderror("building new indexblock failed"); + exitblock(); + return error; + } + fixederror("new indexblock created"); + } + } + else + { + /* check mode failure */ + ResBlockUsed(*bloknr); + free(iblk.data); + return e_none; + } + } + } + } + + for (i=0; iindex[i])) + { + volume.progress(0, 1); + switch (repairchild((uint32 *)&iblk.data->index[i], seqnr*LONGS_PER_BMB + i)) + { + case e_none: + break; + + default: + /* kill block */ + iblk.data->index[i] = 0; + break; + } + + if (aborting) + return error; + + /* save changes */ + if (child_blk_nr != iblk.data->index[i] && iblk.mode == check) + volume.writeblock((cachedblock_t *)&iblk); + } + } + + exitblock(); + if (iblk.mode == check) + { + /* not necessary (allowed) for generated blocks */ + ResBlockUsed(*bloknr); + free (iblk.data); + } + return error; +} + + +/************************************** + * Bitmap Generator L3 + **************************************/ + +// public data + +// enable flags + +// public functions + +static error_t InitReservedBitmap(void) +{ + uint32 reservedsize, bloknr, cluster; + + KillReservedBitmap(); + if (rbl->firstreserved != 2) + { + fixederror("illegal firstreserved"); + rbl->firstreserved = 2; + } + + // reservedsize cannot be smaller then 0 and is not allowed to take more than + // 1/8th of the partition. + volume.lastreserved = rbl->lastreserved; + reservedsize = rbl->lastreserved/volume.rescluster - rbl->firstreserved + 1; + + if (reservedsize <=0 || reservedsize > volume.disksize/8) + { + sprintf(bericht, "illegal reserved area size of %lu blocks", reservedsize); + adderror(bericht); + + if (!(bloknr = SearchLastReserved(&volume))) + return e_fatal_error; + + fixederror(NULL); + rbl->lastreserved = volume.lastreserved = bloknr; + } + + if ((rext.data->reserved_roving + 31)/32 > reservedsize) + { + rext.data->reserved_roving = 0; + volume.writeblock((cachedblock_t *)&rext); + } + + cluster = 1 + ((reservedsize > 125*32) ? (reservedsize - 125*32 + 256*32 - 1)/(256*32) : 0); + // cluster is rounded up to closest reserved blocksize + cluster = (((cluster + rbl->reserved_blksize / 1024 - 1) & ~(rbl->reserved_blksize / 1024 - 1)) * 1024)>>volume.blockshift; + if (rbl->rblkcluster != cluster) + { + rbl->rblkcluster = cluster; + if (mode == check) + fixederror("wrong rootblock cluster size"); + } + + volume.resbitmap = bg_InitBitmap(rbl->firstreserved, rbl->lastreserved, volume.rescluster); + if (!volume.resbitmap) + return e_fatal_error; + + /* mark rootblock cluster as used */ + for (bloknr = 2; bloknr < 2+cluster; bloknr += volume.rescluster) + ResBlockUsed(bloknr); + + return e_none; +} + +/* assumes valid anodetree syntax + */ +static error_t InitAnodeBitmap(void) +{ + c_indexblock_t iblk; + int i, s; + error_t error; + + KillAnodeBitmap(); + iblk.data = calloc(1, SIZEOF_RESBLOCK); + + /* determine number of anodebitmap blocks + * If any anode or indexblocks are generated, the anodebitmap has to be + * destroyed and regenerated. + */ + if (rbl->options & MODE_SUPERINDEX) + { + for (s=MAXSUPER; !rext.data->superindex[s] && s; s--); + + if ((error = GetResBlock((cachedblock_t *)&iblk, SBLKID, s, false))) + { + free (iblk.data); + return error; + } + + for (i=INDEX_PER_BLOCK - 1; !iblk.data->index[i]; i--); + } + else + { + for (s=0, i=MAXSMALLINDEXNR; !rbl->idx.small.indexblocks[i] && i; i--); + } + + s = s*INDEX_PER_BLOCK*INDEX_PER_BLOCK*ANODES_PER_BLOCK; + s += (++i * INDEX_PER_BLOCK * ANODES_PER_BLOCK) - 1; + volume.anodebitmap = bg_InitBitmap(0, s, 1); + for (i=0; ilastreserved+1, + volume.lastblock-volume.firstblock, 1); + + if (!volume.mainbitmap) + return e_fatal_error; + else + return e_none; +} + +void KillAnodeBitmap(void) +{ + bg_KillBitmap(&volume.anodebitmap); +} + +void KillMainBitmap(void) +{ + bg_KillBitmap(&volume.mainbitmap); +} + +void KillReservedBitmap(void) +{ + bg_KillBitmap(&volume.resbitmap); +} + +/* Compare generated reserved bitmap with the actual bitmap + * and fix differences. + * Pre: + * - syntax disk is correct + * - rootblock read + * - reserved bitmap generated AND valid + */ +static error_t RepairReservedBitmap(void) +{ + rootblock_t *lrb; + bitmapblock_t *bitmap; + uint32 i, j, mask, blknr, blocksfree = 0; + bool dirty = false; + error_t error; + uint32 nuba=0, ubna=0; + uint8 *t; + + volume.status(0, "validating reserved", volume.resbitmap->lwsize/0xff); + + /* first save current rootblock */ + if ((error = c_WriteBlock ((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize))) + return error; + + /* now get reserved bitmap with rootblock in memory */ + if (!(lrb = (rootblock_t *)AllocBufMem(volume.blocksize * rbl->rblkcluster))) + return e_out_of_memory; + + t = (uint8 *)lrb; + for (i=0; irblkcluster; i++) + { + if ((error = c_GetBlock (t, ROOTBLOCK + volume.firstblock + i, volume.blocksize))) + { + adderror("reserved bitmap could not be loaded"); + FreeBufMem(lrb); + return error; + } + t += volume.blocksize; + } + + bitmap = (bitmapblock_t *)(lrb + 1); + if (bitmap->id != BMBLKID) + { + /* this is also done at BUILD ROOTBLOCK */ + if (mode == check) + adderror("reserved bitmap id is wrong"); + else + fixederror("reserved bitmap id was wrong"); + + memset(bitmap, 0, rbl->rblkcluster*volume.blocksize - sizeof(*lrb)); + bitmap->id = BMBLKID; + } + + /* now we can check */ + for (i=0; ilwsize; i++) + { + if (!(i&0xff)) + { + if (aborting) + break; + volume.progress(0, 1); + } + + for (j=0; j<32; j++) + { + mask = 0x80000000 >> j; + blknr = (i*32 + j)*volume.resbitmap->step + volume.resbitmap->start; + + if (blknr <= volume.resbitmap->stop) + { + if ((volume.resbitmap->map[i] & mask) != + (bitmap->bitmap[i] & mask)) + { + if (volume.resbitmap->map[i] & mask) + { + nuba++; + sprintf(bericht, "reserved block %lu not used but allocated", blknr); + if (ss.verbose) + fixederror(bericht); + lrb->reserved_free++; + } + else + { + ubna++; + sprintf(bericht, "reserved block %lu used but not allocated", blknr); + if (ss.verbose) + fixederror(bericht); + lrb->reserved_free--; + } + + bitmap->bitmap[i] ^= mask; + dirty = true; + } + + if (bitmap->bitmap[i] & mask) + blocksfree++; + } + } + } + + if (nuba > 0) + { + sprintf(bericht, "%lu reserved blocks not used but allocated", nuba); + if (mode == check) + fixederror(bericht); + } + + if (ubna > 0) + { + sprintf(bericht, "%lu reserved blocks used but not allocated", ubna); + if (mode == check) + fixederror(bericht); + } + + // check blocks free + if (lrb->reserved_free != blocksfree && !aborting) + { + if (mode == check) + fixederror("wrong number of reserved blocks free"); + lrb->reserved_free = blocksfree; + dirty = true; + } + + error = e_none; + if (dirty && !aborting) + { + t = (uint8 *)lrb; + for (i=0; irblkcluster; i++) + { + error = c_WriteBlock (t, ROOTBLOCK + volume.firstblock + i, volume.blocksize); + t += volume.blocksize; + } + memcpy(rbl, lrb, SIZEOF_RESBLOCK); + } + + FreeBufMem(lrb); + return error; +} + +/* Compare generated bitmap with actual bitmap and fix found + * differences. Pre conditions: + * - syntax disk correct + * - main bitmap generated and VALID + * - rootblock and rext loaded + */ +static error_t RepairMainBitmap(void) +{ + uint32 k=0, i, j, bmseqnr; + uint32 mask, blknr, blocksfree = 0; + c_bitmapblock_t bmb; + bitmap_t *gbm; + error_t error = e_none; + bool dirty; + bool build = 0; + uint32 nuba=0, ubna=0; + + if (ss.verbose) + volume.showmsg("checking main bitmap\n"); + + gbm = volume.mainbitmap; + bmb.data = calloc(1, SIZEOF_RESBLOCK); + volume.status(0, "validating main", gbm->lwsize/INDEX_PER_BLOCK); + + for (bmseqnr = 0; bmseqnr <= gbm->lwsize/INDEX_PER_BLOCK; bmseqnr++) + { + volume.progress(0, 1); + + if ((error = GetResBlock((cachedblock_t *)&bmb, BMBLKID, bmseqnr, true)) || aborting) + { + free (bmb.data); + if ((error = BuildBitmapBlock(&bmb, bmseqnr))) + return error; + build = 1; + } + + // check block + for (i=0; i> j; + blknr = k*32 + j + gbm->start; + + if (blknr <= gbm->stop) + { + if ((bmb.data->bitmap[i] & mask) != + (gbm->map[k] & mask)) + { + if (gbm->map[k] & mask) + { + nuba++; + // sprintf(bericht, "block %d not used but allocated", blknr); + // if (ss.verbose) + // fixederror(bericht); + rbl->blocksfree++; + } + else + { + ubna++; + // sprintf(bericht, "block %d used but not allocated", blknr); + // if (ss.verbose) + // fixederror(bericht); + rbl->blocksfree--; + } + + bmb.data->bitmap[i] ^= mask; + dirty = true; + if ((error = volume.writeblock ((cachedblock_t *)&bmb))) + { + free (bmb.data); + return error; + } + } + + if (bmb.data->bitmap[i] & mask) + blocksfree++; + } + } + } + } + + if (nuba > 0) + { + sprintf(bericht, "%lu data blocks not used but allocated", nuba); + fixederror(bericht); + } + + if (ubna > 0) + { + sprintf(bericht, "%lu data blocks used but not allocated", ubna); + fixederror(bericht); + } + + // check blocks free + if (rbl->blocksfree != blocksfree) + { + fixederror("wrong number of blocks free"); + rbl->blocksfree = blocksfree; + dirty = true; + } + + if (dirty) + error = c_WriteBlock ((uint8 *)rbl, ROOTBLOCK + volume.firstblock, volume.blocksize); + + if (!build) + free (bmb.data); + return error; +} + +/* Compare anodes with generated anode bitmap. Fix found + * differences. Pre conditions: + * - syntax disk correct + * - anode bitmap generated and VALID + * - rootblock and rext loaded + */ +static error_t RepairAnodeBitmap(void) +{ + uint32 i, anodenr; + int anodeused; + canode_t anode; + bitmap_t *gbm = volume.anodebitmap; + error_t error; + uint32 nuba=0, ubna=0; + + if (ss.verbose) + volume.showmsg("checking anode bitmap\n"); + volume.status(0, "validating anodes", (gbm->stop - gbm->start)/32); + + for (i=gbm->start; i<=gbm->stop; i++) + { + if (!(i%32)) + { + if (aborting) + break; + volume.progress(0,1); + } + + anodeused = !(gbm->map[i/32] & (0x80000000UL >> i%32)); + anodenr = (i/ANODES_PER_BLOCK << 16) + i%ANODES_PER_BLOCK; + + error = CheckAnode(&anode, anodenr, false); + + if ((error == e_empty) == anodeused) + { + if (anodeused) + { + if (ss.verbose) + { + sprintf(bericht, "anode %#lx used but not allocated", anodenr); + fixederror(bericht); + } + + ubna++; + if (mode == repair) + { + anode.clustersize = 0; + anode.blocknr = 0xffffffff; + anode.next = 0; + SaveAnode(&anode, anodenr); + } + } + else + { + if (ss.verbose) + { + sprintf(bericht, "anode %#lx not used but allocated", anodenr); + fixederror(bericht); + } + + nuba++; + if (mode == repair) + { + anode.clustersize = + anode.blocknr = + anode.next = 0; + SaveAnode(&anode, anodenr); + } + } + } + } + + if (nuba > 0) + { + sprintf(bericht, "%lu anodes not used but allocated", nuba); + fixederror(bericht); + } + + if (ubna > 0) + { + sprintf(bericht, "%lu anodes used but not allocated", ubna); + fixederror(bericht); + } + + return e_none; +} + +error_t ResBlockUsed(uint32 bloknr) +{ + error_t error = e_none; + + if (ss.stage <= resbitmap) + { + error = bg_ItemUsed(volume.resbitmap, bloknr); + if (error == e_double_allocation) + { + sprintf(bericht, "reserved block %lu is double allocated", bloknr); + adderror(bericht); + } + } + return error; +} + +static error_t MainBlockUsed(uint32 bloknr) +{ + error_t error = e_none; + + if (ss.stage <= mainbitmap) + { + error = bg_ItemUsed(volume.mainbitmap, bloknr); + if (error == e_double_allocation) + { + sprintf(bericht, "block %lu is double allocated", bloknr); + addfileerror(bericht); + } + } + return error; +} + +static error_t AnodeUsed(uint32 nr) +{ + error_t error = e_none; + + if (ss.stage <= anodebitmap) + { + nr = (nr >> 16) * ANODES_PER_BLOCK + (uint16)nr; + error = bg_ItemUsed(volume.anodebitmap, nr); + if (error == e_double_allocation) + { + sprintf(bericht, "anode %#lx is double allocated", nr); + addfileerror(bericht); + } + } + return error; +} + +static BOOL IsAnodeUsed(uint32 nr) +{ + if (!nr) return FALSE; + nr = (nr >> 16) * ANODES_PER_BLOCK + (uint16)nr; + return bg_IsItemUsed(volume.anodebitmap, nr); +} + +/* private functions + */ + +/* initialise a bitmap for generation. The range is start to stop with + * stepsize step + */ +static bitmap_t *bg_InitBitmap(int32 start, int32 stop, int32 step) +{ + bitmap_t *mybitmap; + + if (!(mybitmap = calloc(1, sizeof(bitmap_t)))) + return NULL; + + mybitmap->start = start; + mybitmap->stop = stop; + mybitmap->step = step; + mybitmap->lwsize = (((stop-start+step)/step)+31)/32; + + if (!(mybitmap->map = malloc(4 * mybitmap->lwsize))) + { + free(mybitmap); + volume.showmsg("DOCTOR ERROR: out of memory\n"); + return NULL; + } + + memset(mybitmap->map, 0xff, mybitmap->lwsize*4); + mybitmap->valid = true; + return(mybitmap); +} + +static void bg_KillBitmap(bitmap_t **bm) +{ + if (*bm) + { + if ((*bm)->map) + free((*bm)->map); + free(*bm); + } + + *bm = NULL; +} + +static error_t bg_ItemUsed(bitmap_t *bm, uint32 nr) +{ + uint32 index, bit; + + if (!bm) + return e_none; + + if (nr < bm->start || nr > bm->stop) + return e_outside_bitmap_error; + + nr -= bm->start; + nr /= bm->step; + index = nr/32; + bit = nr%32; + + // check & set bit + if (bm->map[index] & (0x80000000UL >> bit)) + { + bm->map[index] ^= (0x80000000UL >> bit); + return e_none; + } + else + { + return e_double_allocation; + } +} + +static BOOL bg_IsItemUsed(bitmap_t *bm, uint32 nr) +{ + uint32 index, bit; + + nr -= bm->start; + nr /= bm->step; + index = nr/32; + bit = nr%32; + + if (bm->map[index] & (0x80000000UL >> bit)) + return FALSE; + else + return TRUE; +} + diff --git a/rom/filesys/pfs3/pfsdoctor/stats.c b/rom/filesys/pfs3/pfsdoctor/stats.c new file mode 100644 index 0000000000..1f396e5d16 --- /dev/null +++ b/rom/filesys/pfs3/pfsdoctor/stats.c @@ -0,0 +1,104 @@ +/* $Id$ */ +/* $Log: stats.c $ + * Revision 1.4 1999/05/28 05:07:33 Michiel + * Fixed bug occuring on empty directory blocks + * Added rbl.always fix; improved rbl.disksize fix + * Reduced cachesize + * + * Revision 1.3 1999/05/04 17:59:09 Michiel + * check mode, logfile, search rootblock implemented + * bugfixes + * + * Revision 1.2 1999/05/04 04:27:13 Michiel + * debugged upto buildrext + * + * Revision 1.1 1999/04/22 15:24:28 Michiel + * Initial revision + * */ + +#include +#include +#include "pfs3.h" +#include "doctor.h" + +static char msgbuffer[512]; +static BOOL error = FALSE; +static BOOL fixed = FALSE; + +void clearstats(void) +{ + stats.numfiles = + stats.numdirs = + stats.numsoftlink = + stats.numhardlink = + stats.numrollover = + stats.fragmentedfiles = + stats.anodesused = 0; + + volume.updatestats(); +} + +void adderror(char *message) +{ + if (!error) + { + if (stats.blocknr) + error = TRUE; + stats.numerrors++; + } + + if (message) + { + sprintf(msgbuffer, "ERROR: %s\n", message); + volume.showmsg(msgbuffer); + } + volume.updatestats(); +} + +void fixederror(char *message) +{ + if (mode == check) + { + adderror(message); + return; + } + + adderror(NULL); + + if (!fixed) + { + if (stats.blocknr) + fixed = TRUE; + stats.errorsfixed++; + } + + if (message) + { + sprintf(msgbuffer, "FIXED: %s\n", message); + volume.showmsg(msgbuffer); + } + else + volume.showmsg("FIXED\n"); + + volume.updatestats(); +} + +void enterblock(uint32 blknr) +{ + if (blknr != stats.blocknr) + { + if (stats.blocknr) + stats.prevblknr = stats.blocknr; + stats.blocknr = blknr; + error = fixed = FALSE; + stats.blockschecked++; + volume.updatestats(); /* optimise !! */ + } +} + +void exitblock(void) +{ + stats.blocknr = stats.prevblknr; + stats.prevblknr = 0; + return; +} -- 2.11.4.GIT