FixBadPageImpl.mesa
Copyright © 198g by Xerox Corporation. All rights reserved.
Willie-Sue, October 6, 1986 6:06:37 pm PDT
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: BOOLFALSE,  -- 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;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
client interface
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: BOOLFALSE;
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] = {
first have to find the bad page - hoping it shows up
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.FPLOOPHOLE[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];
try to read the data fom the old page
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;
data now has (almost) the bits from the page we are going to remove from the file
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: BOOLFALSE;
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.