<> <> <> DIRECTORY Commander USING [CommandProc, Handle, Register], CommandTool USING [CurrentWorkingDirectory, NextArgument, Parse], Convert USING [Error, CardFromRope, IntFromRope], Disk USING [Channel, ok, DoIO, DriveAttributes, Label, NextChannel, PageCount, PageNumber, Request, Status], File USING [Error, FindVolumeFromID, FindVolumeFromName, FP, GetVolumeName, Handle, Info, LogicalInfo, NextVolume, Open, PageCount, PageNumber, Read, SystemVolume, Volume, VolumeID, Write, wordsPerPage], FileBackdoor USING [PhysicalPageBad, SpliceOutDataPage], FileInternal USING [Handle, Alloc, Commit, FindRun, Free, GetBadPages, TranslateLogicalRun], FS USING [ComponentPositions, Close, Delete, Error, nullOpenFile, ExpandName, Open, OpenFile], FSBackdoor USING [Flush, GetFileHandle, Version], FSFileOps USING [GetNameBodyAndVersion], IagoOps USING [FileError], IO, PhysicalVolume USING [PhysicalRC, GetSubVolumes, NextPhysical, Physical, PhysicalInfo, SubVolumes, SubVolumeDetails, SubVolumeDetailsObject], Rope, VM USING [wordsPerPage, AddressForPageNumber, Allocate, Free, Interval, nullInterval, PagesForWords, SwapIn, Unpin], VMBacking USING [Run, RunTableIndex], VolumeFormat USING [Attributes, LogicalPage, LogicalRun], FixBadPage; FixBadPageImpl: CEDAR MONITOR IMPORTS Commander, CommandTool, Convert, Disk, File, FileBackdoor, FileInternal, FS, FSBackdoor, FSFileOps, IagoOps, IO, PhysicalVolume, Rope, VM EXPORTS FixBadPage = BEGIN OPEN IO, FixBadPage; STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; CmdHandle: TYPE = Commander.Handle; RecoveryOption: TYPE = {askUser, doNothing, recoverData, cautiouslyRecoverData, deleteFile}; RecoveryStatus: TYPE = {unknown, couldnt, mostly, ok, deleted}; Weirdness: SIGNAL = CODE; FixBadPageRef: TYPE = REF FixBadPageRecord; FixBadPageRecord: TYPE = RECORD[ fileName: ROPE, -- may ne NIL(??) fullName: ROPE, cached: BOOL _ FALSE, -- an accelerator, gotten from fileName openFile: FS.OpenFile, fHandle: File.Handle, filePage: INT _ -1, pageStatus: PageStatus _ unknown, channel: Disk.Channel, p: PhysicalVolume.Physical, diskPage: INT _ -1, vol: File.Volume, logicalPage: VolumeFormat.LogicalPage _ [-1], rs: RecoveryStatus _ unknown, option: RecoveryOption _ askUser ]; fileForBadPage: ROPE = "\nThe %gfile containing (physical page) %g is: \"%g\"\n"; headerPage: ROPE = "\n*** Physical page %g is Header page %g of %gfile: \"%g\"\n"; maxReadTries: INT _ 5; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> <> <<>> RecoverFileWithBadPage: PUBLIC ENTRY PROC[in, out: STREAM, fileName: ROPE] RETURNS[filePage: INT] = { ENABLE UNWIND => NULL; IF out # NIL THEN out.PutRope["\nRecoverFileWithBadPage not yet implemented\n"]; RETURN[-1] }; DeleteFileWithBadPage: PUBLIC ENTRY PROC[in, out: STREAM, fileName: ROPE] = { ENABLE UNWIND => NULL; IF out # NIL THEN out.PutRope["\nDeleteFileWithBadPage not yet implemented\n"]; }; RecoverFromBadPage: PUBLIC ENTRY PROC[in, out: STREAM, diskPage: INT, drive: INT _ 0] RETURNS[fileName: ROPE] = { ENABLE UNWIND => NULL; IF out # NIL THEN out.PutRope["\nRecoverFromBadPage not yet implemented\n"]; RETURN[NIL]; }; GetRidOfBadPage: PUBLIC ENTRY PROC[in, out: STREAM, diskPage: INT, drive: INT _ 0] RETURNS[fileName: ROPE, pageStatus: PageStatus] = { ENABLE UNWIND => NULL; IF out # NIL THEN out.PutRope["\nGetRidOfBadPage not yet implemented\n"]; RETURN[NIL, error]; }; <<* * * * * * * * command * * * * * * * *>> FixBP: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithPageNum[cmd, askUser]; }; RecoverBP: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithPageNum[cmd, recoverData]; }; CautiouslyRecoverBP: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithPageNum[cmd, cautiouslyRecoverData]; }; DeleteBP: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithPageNum[cmd, deleteFile]; }; DescP: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithPageNum[cmd, doNothing]; }; FixF: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithFileName[cmd, askUser]; }; CautiouslyRecoverF: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithFileName[cmd, cautiouslyRecoverData]; }; RecoverF: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithFileName[cmd, recoverData]; }; DeleteF: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; WithFileName[cmd, deleteFile]; }; ListBadPages: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; p: PhysicalVolume.Physical _ CheckForPhysicalVolumes[cmd]; anyBad: BOOL _ FALSE; sv: PhysicalVolume.SubVolumes; subV: PhysicalVolume.SubVolumeDetails; vol: File.Volume; badList: LIST OF VolumeFormat.LogicalPage; IF p = NIL THEN RETURN; sv _ PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO KeepList: PROC[logicalPage: VolumeFormat.LogicalPage] = { badList _ CONS[logicalPage, badList]; }; badList _ NIL; subV _ NEW[PhysicalVolume.SubVolumeDetailsObject _ sv[i]]; FileInternal.GetBadPages[subVolume: subV, work: KeepList]; IF badList = NIL THEN LOOP; anyBad _ TRUE; vol _ File.FindVolumeFromID[subV.id]; cmd.out.PutF["\n\nBad Pages for %g {subvolume %g}:", rope[File.GetVolumeName[vol]], card[i]]; FOR vls: LIST OF VolumeFormat.LogicalPage _ badList, vls.rest UNTIL vls=NIL DO bad: Disk.PageNumber _ [vls.first + subV.address + subV.start]; cmd.out.PutF["\n Logical page: %g (%bB) ", int[vls.first], int[vls.first]]; cmd.out.PutF["{Physical page: %g (%bB)}", int[bad], int[bad]]; ENDLOOP; ENDLOOP; IF ~anyBad THEN cmd.out.PutRope["No bad pages listed\n"] ELSE cmd.out.PutChar['\n]; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> WithPageNum: PROC[cmd: CmdHandle, recoveryOption: RecoveryOption] = { out: STREAM = cmd.out; drive: INT; fbp: FixBadPageRef _ NEW[FixBadPageRecord]; [] _ CommandTool.Parse[cmd]; fbp.diskPage _ PageNumFromCmd[cmd]; fbp.option _ recoveryOption; IF fbp.diskPage = -1 THEN RETURN; [drive, fbp.channel] _ CheckForMultipleDrives[cmd]; IF drive = -1 THEN RETURN; IF (fbp.p _ PhysicalVolumeForChannel[fbp.channel, cmd.out]) = NIL THEN RETURN; PhysicalToLogicalPage[fbp]; -- fills in vol, logicalPage IF fbp.vol = NIL THEN RETURN; FileForLogicalPage[fbp, out]; -- fills in fileName, fHandle, pageStatus, filePage CommonCode[fbp, cmd]; }; WithFileName: PROC[cmd: CmdHandle, recoveryOption: RecoveryOption] = { <> out: STREAM = cmd.out; fbp: FixBadPageRef _ NEW[FixBadPageRecord]; [] _ CommandTool.Parse[cmd]; fbp.fileName _ FileNameFromCmd[cmd]; fbp.option _ recoveryOption; fbp.openFile _ FS.Open[fbp.fileName ! FS.Error => { out.PutF[" ... %g\n", IO.rope[error.explanation]]; CONTINUE }; ]; IF fbp.openFile = FS.nullOpenFile THEN RETURN; fbp.fHandle _ FSBackdoor.GetFileHandle[fbp.openFile]; LookForBadPageInFile[fbp, out]; -- fills in diskPage, channel, pageStatus IF fbp.diskPage= -1 THEN { out.PutF["\n Couldn't find a bad page in %g - quitting\n", IO.rope[fbp.fileName] ]; FS.Close[fbp.openFile]; RETURN }; IF fbp.filePage = -1 THEN { out.PutF["DiskPage %g was reported bad but couldn't find it as a file page in file %g - quitting\n", IO.int[fbp.diskPage], IO.rope[fbp.fileName] ]; FS.Close[fbp.openFile]; RETURN }; IF (fbp.p _ PhysicalVolumeForChannel[fbp.channel, cmd.out]) = NIL THEN RETURN; PhysicalToLogicalPage[fbp]; -- fills in vol, logicalPage CommonCode[fbp, cmd]; }; CommonCode: PROC[fbp: FixBadPageRef, cmd: CmdHandle] = { full: BOOL; pStatus: PhysicalVolume.PhysicalRC; cached: ROPE = "(cached) "; IF fbp.fileName # NIL THEN { cp: FS.ComponentPositions; dir: ROPE = IF (fbp.vol = File.SystemVolume[]) THEN "///" ELSE IO.PutFR["//%g/", IO.rope[File.GetVolumeName[fbp.vol]] ]; [fbp.fileName, cp, ] _ FS.ExpandName[fbp.fileName, dir]; fbp.cached _ cp.server.length > 0; }; SELECT fbp.pageStatus FROM headerPage => { cmd.out.PutF[headerPage, IO.int[fbp.diskPage], IO.int[fbp.filePage], IO.rope[IF fbp.cached THEN cached ELSE ""], IO.rope[fbp.fileName]]; IF fbp.option # doNothing THEN cmd.out.PutRope["Can't recover from a bad header page; see a wizard\n\n"]; RETURN }; dataPage => { cmd.out.PutF[fileForBadPage, IO.rope[IF fbp.cached THEN cached ELSE ""], IO.int[fbp.diskPage], IO.rope[fbp.fileName] ]; IF fbp.option = askUser THEN fbp.option _ GetOption[cmd]; SELECT fbp.option FROM doNothing => RETURN; recoverData => RecoverFile[fbp, cmd.out, FALSE]; cautiouslyRecoverData => { RecoverFile[fbp, cmd.out, TRUE]; IF fbp.rs = couldnt THEN { cmd.out.PutF[ "Couldn't read the data in %g tries - quitting\n", IO.int[maxReadTries] ]; RETURN }; }; deleteFile => DeleteThisFile[fbp, cmd.out]; ENDCASE => ERROR; }; freePage => fbp.rs _ ok; ENDCASE => NULL; [full, pStatus] _ FileBackdoor.PhysicalPageBad[fbp.p, [fbp.diskPage]]; IF pStatus = ok THEN AllocatePageInVAM[fbp.vol, fbp.logicalPage]; -- do this even if full IF full THEN cmd.out.PutRope["\n*** Bad Page Table is full\n"]; SELECT fbp.rs FROM couldnt => cmd.out.PutRope["\n*** Couldn't fix the file\n"]; mostly => cmd.out.PutF["\n Replaced file page %g with probably mostly correct bits\n", IO.int[fbp.filePage] ]; ok => IF fbp.pageStatus # freePage THEN cmd.out.PutF["\n Was able to recover file page %g\n", IO.int[fbp.filePage] ]; deleted => cmd.out.PutF["\n %g has been deleted\n", IO.rope[fbp.fileName]]; unknown => cmd.out.PutRope["\n RecoveryStatus is unknown - bug??\n"]; ENDCASE => NULL; }; PageNumFromCmd: PROC[cmd: CmdHandle] RETURNS[pageNum: INT] = { arg: ROPE = CommandTool.NextArgument[cmd]; pageNum _ -1; pageNum _ Convert.IntFromRope[arg ! Convert.Error => CONTINUE ]; IF pageNum = -1 THEN cmd.out.PutF["\nCouldn't parse %g as a page number - quitting\n", IO.rope[arg] ] }; FileNameFromCmd: PROC[cmd: CmdHandle] RETURNS[fileName: ROPE] = { name: ROPE = CommandTool.NextArgument[cmd]; fileName _ FS.ExpandName[name, CommandTool.CurrentWorkingDirectory[]].fullFName; }; GetOption: PROC[cmd: CmdHandle] RETURNS[RecoveryOption] = { cmd.out.PutRope["Your options are: do nothing, try to recover the data, cautiously try to recover the data, or simply delete the file\n"]; cmd.out.PutRope["Confirm by typing y(CR) or Y(CR) or CR; deny by typing any other key (except DEL) followed by CR\n"]; IF GetConfirmation[cmd, "Try to recover the data: "] THEN RETURN[recoverData]; IF GetConfirmation[cmd, "Cautiously try to recover the data: "] THEN RETURN[cautiouslyRecoverData]; IF GetConfirmation[cmd, "Delete the file: "] THEN RETURN[deleteFile]; cmd.out.PutRope["Nothing will be done\n\n"]; RETURN[doNothing]; }; GetConfirmation: PROC[cmd: CmdHandle, msg: ROPE] RETURNS[BOOL] = { char: CHAR; line: ROPE; cmd.err.PutRope[msg]; line _ cmd.in.GetLineRope[]; IF line.Length[] = 0 THEN RETURN[TRUE]; -- only CR typed RETURN[(char _ line.Fetch[0]) = 'y OR (char = 'Y) OR (char = '\n)]; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> <<>> CheckForMultipleDrives: PROC[cmd: CmdHandle] RETURNS[drive: INT, d: Disk.Channel] = { first: Disk.Channel = Disk.NextChannel[NIL]; diskRope: ROPE; IF first = NIL THEN { cmd.out.PutRope["No disk found - quitting\n"]; RETURN[-1, NIL]; }; IF Disk.NextChannel[first] # NIL THEN { -- multiple drive IF (diskRope _ CommandTool.NextArgument[cmd]) = NIL THEN { cmd.out.PutRope["With multiple drive you must specify a drive number - quitting\n"]; RETURN[-1, NIL]; }; drive _ Convert.IntFromRope[diskRope ! Convert.Error => { drive _ -1; CONTINUE} ]; IF drive = -1 THEN { cmd.out.PutF["\nCouldn't parse %g as a drive number - quitting\n", IO.rope[diskRope] ]; RETURN[-1, NIL]; }; FOR d _ first, Disk.NextChannel[d] UNTIL d = NIL DO IF Disk.DriveAttributes[d].ordinal = drive THEN RETURN[drive, d]; ENDLOOP; cmd.out.PutF["\n Drive %g doesn't exist - quitting\n", IO.int[drive] ]; RETURN[-1, NIL]; }; RETURN[1, first]; }; CheckForLogicalVolumes: PROC[cmd: CmdHandle] RETURNS[File.Volume] = { first: File.Volume = File.NextVolume[NIL]; lvName: ROPE; IF File.NextVolume[first] # NIL THEN { IF (lvName _ CommandTool.NextArgument[cmd]) = NIL THEN { cmd.out.PutRope["You must specify a logical volume - quitting\n"]; RETURN[NIL]; }; FOR vol: File.Volume _ first, File.NextVolume[vol] UNTIL vol = NIL DO IF Rope.Equal[File.LogicalInfo[vol].name, lvName, FALSE] THEN RETURN [vol]; ENDLOOP; cmd.out.PutF["\n Logical Volume %g doesn't exist - quitting\n", IO.rope[ lvName ] ]; RETURN[NIL]; }; RETURN[first]; }; CheckForPhysicalVolumes: PROC[cmd: CmdHandle] RETURNS[PhysicalVolume.Physical] = { first: PhysicalVolume.Physical = PhysicalVolume.NextPhysical[NIL]; pName: ROPE; IF PhysicalVolume.NextPhysical[first] # NIL THEN { [] _ CommandTool.Parse[cmd]; IF (pName _ CommandTool.NextArgument[cmd]) = NIL THEN { cmd.out.PutRope["You must specify a physical volume - quitting\n"]; RETURN[NIL]; }; FOR p: PhysicalVolume.Physical _ first, PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO IF Rope.Equal[PhysicalVolume.PhysicalInfo[p].name, pName, FALSE] THEN RETURN [p]; ENDLOOP; cmd.out.PutF["\n Physical volume %g doesn't exist - quitting\n", IO.rope[ pName ] ]; RETURN[NIL]; }; RETURN[first]; }; PhysicalVolumeForChannel: PROC[channel: Disk.Channel, out: STREAM] RETURNS[p: PhysicalVolume.Physical] = { p _ PhysicalVolume.NextPhysical[NIL]; DO IF PhysicalVolume.PhysicalInfo[p].channel = channel THEN RETURN; p _ PhysicalVolume.NextPhysical[p]; IF p = NIL THEN EXIT; ENDLOOP; out.PutF["\n No physcial volume found for drive %g - quitting\n", IO.int[Disk.DriveAttributes[channel].ordinal] ]; }; PhysicalToLogicalPage: PROC[fbp: FixBadPageRef] = { sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[fbp.p]; FOR i: CARDINAL IN [0..sv.count) DO addr: INT = sv[i].address; IF fbp.diskPage >= addr AND fbp.diskPage < addr+sv[i].size THEN { fbp.logicalPage _ [fbp.diskPage-addr]; fbp.vol _ File.FindVolumeFromID[sv[i].id]; RETURN }; ENDLOOP; fbp.logicalPage _ [-1]; fbp.vol _ NIL; }; LogicalToPhysicalPage: PROC[logicalPage: VolumeFormat.LogicalPage, volName: ROPE] RETURNS[page: Disk.PageNumber] = { vol: File.Volume = File.FindVolumeFromName[volName]; IF vol = NIL THEN RETURN[[-1]]; [ , page] _ FileInternal.TranslateLogicalRun[[logicalPage, 1], vol]; }; FileForLogicalPage: PROC[fbp: FixBadPageRef, out: STREAM] = { initLabel: Disk.Label; data: LONG POINTER; label: Disk.Label _ initLabel; request: Disk.Request; status: Disk.Status; attr: VolumeFormat.Attributes; val: WORD; run: VolumeFormat.LogicalRun; pageSpace: VM.Interval _ VM.nullInterval; { ENABLE UNWIND => FreeSpace[pageSpace]; [pageSpace, data] _ GetSpace[1]; run _ [[fbp.logicalPage], 1]; [fbp.channel, fbp.diskPage] _ FileInternal.TranslateLogicalRun[run, fbp.vol ! File.Error => { out.PutF["\nFile.Error (%g) during TranslateLogicalRun of logical page %g\n", IO.rope[IagoOps.FileError[why]], IO.int[fbp.logicalPage]]; GOTO doQuit; }; ]; BEGIN ENABLE File.Error => { out.PutF["\n File.Error (%g) reading page %g (physical page %g)\n", rope[IagoOps.FileError[why]], int[fbp.logicalPage], int[fbp.diskPage]]; GOTO keepGoing; }; request _ [diskPage: [fbp.diskPage], data: data, incrementDataPtr: FALSE, command: [verify, read, read], count: 1]; TRUSTED { [status, ]_ Disk.DoIO[channel: fbp.channel, label: @label, request: @request] }; attr _ LOOPHOLE[label.attributes]; IF attr = freePage THEN { fbp.pageStatus _ freePage; out.PutF["\n Page %g (physical page %g) is free, with status %g", int[fbp.logicalPage], int[fbp.diskPage], rope[StatusToRope[status].r]]; FreeSpace[pageSpace]; RETURN; }; IF attr = data OR attr = header THEN { fp: File.FP _ LOOPHOLE[label.fileID.relID]; version: FSBackdoor.Version; IF attr = data THEN fbp.pageStatus _ dataPage ELSE fbp.pageStatus _ headerPage; fbp.fHandle _ File.Open[fbp.vol, fp]; [fbp.fileName, version]_ FSFileOps.GetNameBodyAndVersion[f: fbp.fHandle ! FS.Error => { out.PutF["\n Page %g (physical page %g),\n\t***FS error: \"%g\"\n", int[fbp.logicalPage], int[fbp.diskPage], rope[error.explanation] ]; out.PutF["\tFile.FP is: [id: %g (%bB), da: %g (%bB)]\n", IO.card[LOOPHOLE[fp.id]], IO.card[LOOPHOLE[fp.id]], IO.card[LOOPHOLE[fp.da]], IO.card[LOOPHOLE[fp.da]]]; out.PutF["\tIs %g page %g of some file", IF attr = data THEN rope["data"] ELSE rope["header"], IO.int[label.filePage] ]; CONTINUE}]; fbp.filePage _ label.filePage; IF fbp.fileName # NIL THEN fbp.fileName _ IO.PutFR["%g!%g", IO.rope[fbp.fileName], IO.card[version] ]; FreeSpace[pageSpace]; RETURN; }; val _ LOOPHOLE[label.attributes]; out.PutF["\nOther type of page: logical: %g (physical %g), attributes: %g (%06b)\n", IO.int[fbp.logicalPage], int[fbp.diskPage], IO.rope[AttributesToRope[attr]], IO.card[val]]; EXITS keepGoing => NULL; END; -- ENABLE File.Error EXITS doQuit => NULL; }; FreeSpace[pageSpace]; fbp.pageStatus _ error; }; RecoverFile: PROC[fbp: FixBadPageRef, out: STREAM, cautious: BOOL] = { pageSpace: VM.Interval _ VM.nullInterval; data: LONG POINTER; initLabel: Disk.Label; label: Disk.Label _ initLabel; request: Disk.Request; status: Disk.Status; fbp.rs _ unknown; { ENABLE UNWIND => FreeSpace[pageSpace]; count: INT _ 0; otherData: LONG POINTER; oldPage: INT; oldChannel: Disk.Channel; [pageSpace, data] _ GetSpace[1]; otherData _ LOOPHOLE[data + VM.wordsPerPage]; <> request _ [diskPage: [fbp.diskPage], data: data, incrementDataPtr: FALSE, command: [verify, read, read], count: 1]; DO TRUSTED { [status, ]_ Disk.DoIO[channel: fbp.channel, label: @label, request: @request] }; IF status = Disk.ok THEN { fbp.rs _ ok; EXIT }; IF (count _ count + 1) >= maxReadTries THEN { IF cautious THEN fbp.rs _ couldnt; EXIT; }; ENDLOOP; <> IF fbp.rs = couldnt THEN RETURN; [oldPage, oldChannel] _ FileBackdoor.SpliceOutDataPage[fbp.fHandle, fbp.filePage]; IF oldPage # fbp.diskPage OR oldChannel # fbp.channel THEN { SIGNAL Weirdness; fbp.rs _ couldnt; RETURN; }; File.Write[fbp.fHandle, [fbp.filePage], 1, data ! File.Error => {fbp.rs _ couldnt} ]; }; }; DeleteThisFile: PROC[fbp: FixBadPageRef, out: STREAM] = { BEGIN ENABLE File.Error => { out.PutRope[IagoOps.FileError[why]]; fbp.rs _ couldnt; CONTINUE }; IF fbp.openFile # FS.nullOpenFile THEN FS.Close[fbp.openFile]; IF fbp.cached THEN FSBackdoor.Flush[fbp.fileName, File.GetVolumeName[fbp.vol]] ELSE FS.Delete[fbp.fileName]; fbp.rs _ deleted; END; fbp.fHandle _ NIL; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> AllocatePageInVAM: PROC[vol: File.Volume, logicalPage: VolumeFormat.LogicalPage] = { given: VolumeFormat.LogicalRun _ FileInternal.Alloc[volume: vol, first: logicalPage, size: 1, min: 1]; IF given.first = logicalPage THEN FileInternal.Commit[vol] ELSE FileInternal.Free[vol, given]; }; GetSpace: PROC[num: INT] RETURNS[space: VM.Interval, data: LONG POINTER] = TRUSTED { space _ VM.Allocate[VM.PagesForWords[num*File.wordsPerPage]]; data _ VM.AddressForPageNumber[space.page]; VM.SwapIn[interval: space, kill: TRUE, pin: TRUE]; }; FreeSpace: PROC[space: VM.Interval] = TRUSTED { IF space = VM.nullInterval THEN RETURN; VM.Unpin[space]; VM.Free[space]; space _ VM.nullInterval; }; StatusToRope: PROC[status: Disk.Status] RETURNS[r: ROPE] = { WITH s: status SELECT FROM changed => r _ "drive's ChangeCount changed; no IO performed"; unchanged => SELECT s.status FROM inProgress => r _ "inProgress"; goodCompletion => r _ "good Completion"; notReady => r _ "notReady"; recalibrateError => r _ "recalibrateError"; seekTimeout => r _ "seekTimeout"; headerCRCError => r _ "headerCRCError"; labelCRCError => r _ "labelCRCError"; dataCRCError => r _ "dataCRCError"; headerNotFound => r _ "headerNotFound"; labelVerifyError => r _ "labelVerifyError"; dataVerifyError => r _ "dataVerifyError"; overrunError => r _ "overrunError"; writeFault => r _ "writeFault"; memoryError => r _ "memoryError"; memoryFault => r _ "memoryFault"; clientError => r _ "clientError"; operationReset => r _ "operationReset"; otherError => r _ "otherError"; ENDCASE; ENDCASE; }; AttributesToRope: PROC[attributes: VolumeFormat.Attributes] RETURNS[ROPE] = { RETURN[SELECT attributes FROM physicalRoot => "PhysicalRoot", badPageList => "BadPageList", badPage => "BadPage", subVolumeMarker => "SubVolumeMarker", logicalRoot => "LogicalRoot", freePage => "FreePage", header => "HeaderPage", data => "DataPage", lastCedar => "LastCedarAttribute", ENDCASE => "UnknownType"]; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> <<>> LookForBadPageInFile: PROC[fbp: FixBadPageRef, out: STREAM] = { ENABLE FS.Error => { out.PutRope[" ... "]; IF error.code = $invalidPropertyPage THEN out.PutRope["not an FS file"] ELSE out.PutRope[error.explanation]; CONTINUE }; size: File.PageCount _ File.Info[fbp.fHandle].size; didStop: BOOL _ FALSE; pageSpace: VM.Interval _ VM.nullInterval; numPages: INT = 25; { ENABLE UNWIND => FreeSpace[pageSpace]; data: LONG POINTER; [pageSpace, data]_ GetSpace[numPages]; out.PutF["\nReading pages from %g file (%g pages)\n", IO.rope[fbp.fileName], IO.int[size]]; TRUSTED { i: File.PageCount _ 0; WHILE i < size DO IF i # 0 THEN IF i MOD 100 = 0 THEN IF i MOD 1000 = 0 THEN out.PutF["(%g)", IO.int[i]] ELSE out.PutChar['~]; File.Read[fbp.fHandle, [i], numPages, data ! File.Error => {IF why # unknownPage THEN { fbp.diskPage _ diskPage; didStop _ TRUE }; EXIT} ]; i _ i + numPages; ENDLOOP; IF didStop THEN { ff: FileInternal.Handle; FOR j: File.PageCount IN [i..i+numPages) DO File.Read[fbp.fHandle, [j], 1, data ! File.Error => { fbp.filePage _ j; EXIT}]; ENDLOOP; IF fbp.filePage # -1 THEN { ff _ LOOPHOLE[fbp.fHandle]; [fbp.diskPage, , fbp.channel] _ FileInternal.FindRun[[fbp.filePage], 1, ff.runTable]; }; out.PutChar['\n]; }; }; fbp.pageStatus _ dataPage; -- the only thing that will get to here I think FreeSpace[pageSpace]; }; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> ShowRunTable: Commander.CommandProc = { openFile: FS.OpenFile; fileName: ROPE = FileNameFromCmd[cmd]; IF fileName = NIL THEN RETURN; openFile _ FS.Open[fileName ! FS.Error => { cmd.out.PutF[" ... %g\n", IO.rope[error.explanation] ]; CONTINUE }; ]; IF openFile = FS.nullOpenFile THEN RETURN; ShowRuns[cmd.out, FSBackdoor.GetFileHandle[openFile]]; FS.Close[openFile]; }; RunTableFromFileID: Commander.CommandProc = { fHandle: File.Handle; fp: File.FP; id, da: INT _ -1; volume: File.Volume; arg: ROPE; [] _ CommandTool.Parse[cmd]; IF (arg _ CommandTool.NextArgument[cmd]) = NIL THEN RETURN; id _ Convert.CardFromRope[arg ! Convert.Error => { id _ -1; CONTINUE} ]; IF id = -1 THEN RETURN; IF (arg _ CommandTool.NextArgument[cmd]) = NIL THEN RETURN; da _ Convert.CardFromRope[arg ! Convert.Error => { da _ -1; CONTINUE} ]; IF da = -1 THEN RETURN; fp.id _ LOOPHOLE[id]; fp.da _ LOOPHOLE[da]; volume _ CheckForLogicalVolumes[cmd]; IF volume = NIL THEN RETURN; fHandle _ File.Open[volume, fp ! File.Error => { IF why = unknownFile THEN cmd.out.PutRope["Unknown file - perhaps deleted\n"] ELSE cmd.out.PutF[" %g\n", IO.rope[IagoOps.FileError[why]] ]; CONTINUE } ]; IF fHandle = NIL THEN RETURN; ShowRuns[cmd.out, fHandle]; }; ShowRuns: PROC[out: STREAM, fHandle: File.Handle] = { fiHandle: FileInternal.Handle; nRuns: INT; TRUSTED { fiHandle _ LOOPHOLE[fHandle] }; nRuns _ fiHandle.runTable.nRuns; FOR i: VMBacking.RunTableIndex IN [0.. fiHandle.runTable.nRuns) DO thisRun: VMBacking.Run = fiHandle.runTable[i]; pagesInRun: INT; IF thisRun.channel = NIL THEN EXIT; -- precaution SELECT TRUE FROM nRuns = 1 => pagesInRun _ fiHandle.runTable.nDataPages - thisRun.filePage; i = nRuns-1 => pagesInRun _ fiHandle.runTable.nDataPages - thisRun.filePage; ENDCASE => pagesInRun _ fiHandle.runTable[i+1].filePage - thisRun.filePage; out.PutF[" [filePage: %g, diskPage: %g, drive: %g, pages: %g]", IO.int[thisRun.filePage], IO.int[thisRun.diskPage], IO.int[Disk.DriveAttributes[thisRun.channel].ordinal], IO.int[pagesInRun]]; ENDLOOP; out.PutChar['\n]; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> <<-- think of more descriptive names!!>> <<>> Commander.Register["FixBadPage", FixBP, "General purpose FixBadPage"]; Commander.Register["CautiouslyRecoverBadPage", CautiouslyRecoverBP, "Recover a file with the given bad page if the data can be read without error"]; Commander.Register["RecoverBadPage", RecoverBP, "Recover a file with the given bad page"]; Commander.Register["DeleteBadPage", DeleteBP, "Delete/flush a file with the given bad page"]; Commander.Register["DescribePage", DescP, "Find which file has the given bad page"]; Commander.Register["FixFile", FixF, "Recover a file that has a bad page"]; Commander.Register["RecoverFile", RecoverF, "Recover a file that has a bad page"]; Commander.Register["CautiouslyRecoverFile", CautiouslyRecoverF, "Recover a file that has a bad page if the data can be read without error"]; Commander.Register["DeleteFile", DeleteF, "Delete/flush a file that has a bad page"]; Commander.Register["ListBadPages", ListBadPages, "List bad pages for some physical volume"]; Commander.Register["ShowRunTable", ShowRunTable, "Show the run table for named file"]; Commander.Register["RunTableFromFileID", RunTableFromFileID, "Show the run table for a given fileID [ id, da]"]; END.