ExtraIagoOpsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Willie-Sue, October 30, 1985 4:34:31 pm PST
Russ Atkinson (RRA) March 11, 1985 8:39:34 pm PST
DIRECTORY
Ascii,
Basics USING [BytePair, LongDivMod],
Convert USING [Error, IntFromRope],
Disk USING [Channel, DoIO, DriveAttributes, Label, PageCount, PageNumber, Request, Status],
DiskFace USING [DeviceHandle, DiskAddress, GetDeviceAttributes],
ExtraIagoOps,
File USING [Delete, Error, FindVolumeFromID, FP, GetVolumeName, Handle, Info, nullFP, nullVolumeID, Open, PageCount, PageNumber, Read, Volume, VolumeFile, VolumeID, wordsPerPage],
FileBackdoor USING [GetRoot, PhysicalPageBad],
FileInternal USING [Handle, Alloc, Commit, FindRun, Free, GetBadPages, TranslateLogicalRun],
FS USING [Close, Error, FileInfo, nullOpenFile, Open, OpenFile],
FSBackdoor USING [GetFileHandle, Version],
FSFileOps USING [GetNameBodyAndVersion],
FormatDisk USING [altoRegionJargon, altoRegionsSize, hardUCodeSize],
IagoOps USING [Confirm, FileError, GetArg, GetDrive, GetFile, GetLogical, GetNumber, GetPhysical, PutID],
IO,
PhysicalVolume USING [GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SubVolumeDetails, SubVolumeDetailsObject, SubVolumes],
Rope,
SystemVersion USING [machineType],
VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, PagesForBytes, PagesForWords, SwapIn, Unpin],
VolumeFormat USING [Attributes, BadPageList, LogicalPage, LogicalRun, PhysicalRoot, RelID];
ExtraIagoOpsImpl: CEDAR PROGRAM
IMPORTS Basics, Convert, Disk, DiskFace, File, FileBackdoor, FileInternal, FormatDisk, FS, FSBackdoor, FSFileOps, IagoOps, IO, PhysicalVolume, Rope, SystemVersion, VM
EXPORTS ExtraIagoOps = { OPEN IO;
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
HowToShowData: TYPE = {none, all, text, stop};
pagePrompt: ROPE = "\nPage number: ";
logicalPagePrompt: ROPE = "\nLogicalPage number: ";
pageHelp: ROPE = "? type the page number (in decimal)";
numPagesPrompt: ROPE = "\nNumber of pages: ";
numPagesHelp: ROPE = "? type the number of pages (in decimal)";
howToPrompt: ROPE = "\nHow to show the data: ";
howToHelp: ROPE = "? type one of {n (none), a (all), t (text), s (stop)}";
* * * * * * * * * * * * * * * * * Utility Procedures * * * * * * * * * * * * * * *
GetDrive: PROC[in, out: STREAM] RETURNS[d: Disk.Channel, drive: INT] = {
d ← IagoOps.GetDrive[in, out];
drive ← IF d # NIL THEN Disk.DriveAttributes[d].ordinal ELSE -1;
};
GetPageNumber: PROC[in, out: STREAM, max: INT] RETURNS[page: INT] = {
page ← 0;
page ← IagoOps.GetNumber[
in: in, out: out, default: page, max: max, prompt: pagePrompt, help: pageHelp];
};
GetLogicalPageNumber: PROC[in, out: STREAM, max: INT]
RETURNS[logicalPage: VolumeFormat.LogicalPage] = {
page: INT ← 0;
page ← IagoOps.GetNumber[
in: in, out: out, default: page, max: max, prompt: logicalPagePrompt, help: pageHelp];
RETURN[[page]];
};
GetNumberOfPages: PROC[in, out: STREAM] RETURNS[count: INT] = {
count ← 1;
count ← IagoOps.GetNumber[
in: in, out: out, default: count, max: 7, prompt: numPagesPrompt, help: numPagesHelp];
};
GetHowToShow: PROC[in, out: STREAM] RETURNS[howToShowData: HowToShowData] = {
howTo: ROPE ← "n (none)";
HowToHelp: PROC = {out.PutRope[howToHelp]};
DO
howTo ← IagoOps.GetArg[
in: in, out: out, default: howTo, prompt: howToPrompt, help: HowToHelp];
SELECT howTo.Fetch[0] FROM
'n, 'N => RETURN[none];
'a, 'A => RETURN[all];
't, 'T => RETURN[text];
's, 'S => RETURN[stop];
ENDCASE => out.PutF["%g is not a legal value; try again", rope[howTo]];
ENDLOOP;
};
Loophole: PUBLIC PROC[in, out: IO.STREAM] = {
Help: PROC = { IO.PutRope[out, "type a number, ending in B or H"] };
ok: BOOLTRUE;
sizeRope: ROPE ← IagoOps.GetArg[ in: in, out: out, prompt: "(xxxB) ", default: "0", help: Help];
size: INT;
size ← Convert.IntFromRope[sizeRope ! Convert.Error => { ok ← FALSE; CONTINUE } ];
IF ok THEN out.PutF[" in decimal is %g", IO.int[size]] ELSE out.PutRope["\nNot a number"];
};
GetAddress: PUBLIC PROC[d: DiskFace.DeviceHandle, page: INT] RETURNS[addr: DiskFace.DiskAddress] = {
ENABLE UNWIND => NULL;
thisCylinder: CARDINAL;
temp: CARDINAL;
cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL;
[cylinders, movingHeads, fixedHeads, sectorsPerTrack]← DiskFace.GetDeviceAttributes[d];
IF cylinders = 0 THEN RETURN[[LAST[CARDINAL], 0, 0]];
[quotient: thisCylinder, remainder: temp] ← Basics.LongDivMod[
num: page, den: sectorsPerTrack * movingHeads];
IF (LOOPHOLE[d, CARDINAL] = 0) AND (SystemVersion.machineType = dorado)
THEN
kludge for funny RD0 formatting on dorado
RETURN[[cylinder: thisCylinder MOD 815,
sector: temp MOD sectorsPerTrack, head: thisCylinder/815 ] ]
ELSE
RETURN[ [cylinder: thisCylinder,
sector: temp MOD sectorsPerTrack, head: temp/sectorsPerTrack ] ]
};
PrintPageAndDiskAddr:
PROC[page: Disk.PageNumber, d: DiskFace.DeviceHandle, out: STREAM] = {
addr: DiskFace.DiskAddress ← GetAddress[d, page];
IF addr.cylinder = LAST[CARDINAL] THEN
{ out.PutF[" Drive %g doesn't exist", int[LOOPHOLE[d, INTEGER]]]; RETURN};
out.PutF[" Page: %g (%bB),", int[page], card[page]];
out.PutF[" [cyl: %g (%bB), head: %g, sector: %g]",
card[addr.cylinder], card[addr.cylinder], card[addr.head], card[addr.sector]];
};
StatusToRope: PROC[status: Disk.Status] RETURNS[r: ROPE, wasOK: BOOL] = {
wasOK ← FALSE;
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"; wasOK ← TRUE};
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;
};
ReportStatus: PROC[out: STREAM, status: Disk.Status, dontReportOK: BOOLTRUE] = {
r: ROPE;
wasOK: BOOL;
[r, wasOK]← StatusToRope[status];
IF dontReportOK AND wasOK THEN RETURN;
out.PutF["\n\n **********Status reported as: %g **********", rope[r]];
};
OKStatusForWrite: PROC[status: Disk.Status, in, out: STREAM] RETURNS[ok: BOOL] = {
WITH s: status SELECT FROM
changed => RETURN[FALSE];
unchanged => SELECT s.status FROM
labelCRCError => NULL;
goodCompletion, dataCRCError, dataVerifyError => RETURN[TRUE];
inProgress, notReady, recalibrateError, seekTimeout, headerCRCError,
headerNotFound, labelVerifyError, overrunError, writeFault, memoryError,
memoryFault, clientError, operationReset, otherError => RETURN[FALSE];
ENDCASE;
ENDCASE;
out.PutRope["\n LabelCRCError reported; attempt the write anyway?"];
ok ← IagoOps.Confirm[in, out];
};
ChannelToDeviceHandle: PROC[c: Disk.Channel] RETURNS[d: DiskFace.DeviceHandle] = {
ordinal: CARDINAL ← Disk.DriveAttributes[c].ordinal;
d ← LOOPHOLE[ordinal, DiskFace.DeviceHandle];
};
VolumeFileToRope: PROC[which: File.VolumeFile] RETURNS[ROPE] = {
RETURN[SELECT which FROM
checkpoint => "Checkpoint",
microcode => "Microcode",
germ => "Germ",
bootFile => "BootFile",
debugger => "Debugger",
debuggee => "Debuggee",
VM => "VM",
VAM => "VAM",
client => "FS-root",
alpine => "Alpine-root",
ENDCASE => "Other-root"];
};
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"];
};
* * * * * * * * commands implementation * * * * * * * *
AddPageToBadPageTable: PUBLIC PROC[in, out: STREAM] = {
p: PhysicalVolume.Physical ← IagoOps.GetPhysical[in, out];
page: INT = GetPageNumber[in, out, LAST[INT]];
name: ROPE = PhysicalVolume.PhysicalInfo[p].name;
out.PutF["\nAdding physical page %g to bad page table for %g", int[page], rope[name]];
[]← FileBackdoor.PhysicalPageBad[p, [page]];
};
DescribeAllocated: PUBLIC PROC[in, out: STREAM] = {
FOR p: PhysicalVolume.Physical ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
channel: Disk.Channel;
rootStatus: PhysicalVolume.PhysicalRC;
id: File.VolumeID;
name: Rope.ROPE;
size: Disk.PageCount;
free: Disk.PageCount;
inUse: INT ← 0;
reserved: INT;
[channel: channel, rootStatus: rootStatus, id: id, name: name, size: size, free: free] ← PhysicalVolume.PhysicalInfo[p];
out.PutF["\n\nID: "]; IagoOps.PutID[out, id];
out.PutF["\nOn drive RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ];
SELECT rootStatus FROM
ok => NULL--drop through to after ENDCASE--;
wentOffline => {
out.PutRope["\nVolume went offline"];
LOOP };
inconsistent => {
out.PutRope["\nRoot page is inconsistent (wrong version?)"];
LOOP };
software => {
out.PutRope["\nCan't find the volume root page (label-check)"];
LOOP };
hardware => {
out.PutRope["\nHard disk error reading the volume root page"];
LOOP };
ENDCASE => ERROR File.Error[rootStatus];
out.PutF["\nName: %g\nSize: %g\n Logical sub-volumes:",
[rope[name]],
[integer[size]]
];
{
sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p];
FOR i: CARDINAL IN [0..sv.count) DO
out.PutF["\n At physical page %g, logical id: ", [integer[sv[i].address]] ];
IagoOps.PutID[out, sv[i].id];
out.PutF[", pages [%g..%g)",
[integer[sv[i].start]],
[integer[sv[i].start+sv[i].size]]
];
inUse ← inUse + sv[i].size;
ENDLOOP;
reserved ← size - inUse - free;
IF reserved # 0 THEN {
num: INT ← reserved - FormatDisk.hardUCodeSize;
IF num >= FormatDisk.altoRegionsSize THEN
out.PutF["\n%g pages (%g %g) reserved for alto allocations",
[integer[num]],
[integer[num/FormatDisk.altoRegionsSize]],
[rope[FormatDisk.altoRegionJargon]]
];
};
IF free # 0 THEN
out.PutF["\n%g pages (%g %g) for potential alto allocations",
[integer[free]],
[integer[(free+1)/FormatDisk.altoRegionsSize]],
[rope[FormatDisk.altoRegionJargon]]
];
};
ENDLOOP;
};
BadPagesRec: TYPE = RECORD[
ls: LIST OF VolumeFormat.LogicalPage,
subV: PhysicalVolume.SubVolumeDetails,
vol: File.Volume,
drive: DiskFace.DeviceHandle
];
BadPagesListProc: TYPE = PROC[ls: LIST OF VolumeFormat.LogicalPage, subV: PhysicalVolume.SubVolumeDetails, vol: File.Volume, drive: DiskFace.DeviceHandle];
DescribeBadPages: PUBLIC PROC[in, out: IO.STREAM] = TRUSTED {
badPagesInfo: LIST OF BadPagesRec ← NIL;
count: INT ← 0;
Dbp: BadPagesListProc =
TRUSTED { badPagesInfo ← CONS[ [ls, subV, vol, drive], badPagesInfo] };
DoBadPages[in, out, Dbp, FALSE];
IF badPagesInfo = NIL THEN RETURN;
FOR bp: LIST OF BadPagesRec ← badPagesInfo, bp.rest UNTIL bp = NIL DO
out.PutF["\n\nBad Pages for %g\n", rope[File.GetVolumeName[bp.first.vol]]];
FOR ls: LIST OF VolumeFormat.LogicalPage ← bp.first.ls, ls.rest UNTIL ls=NIL DO
DescribeOnePage[ls.first, bp.first.vol, in, out];
IF (count ← count + 1) MOD 10 = 0 THEN {
out.PutRope["\nContinue?"];
IF ~IagoOps.Confirm[in, out] THEN RETURN;
};
ENDLOOP;
ENDLOOP;
};
DescribeVolumePage: PUBLIC PROC[in, out: IO.STREAM] = {
vol: File.Volume = IagoOps.GetLogical[in, out];
logicalPage: VolumeFormat.LogicalPage = GetLogicalPageNumber[in, out, LAST[INT]];
DescribeOnePage[logicalPage, vol, in, out];
};
DescribeDiskPage: PUBLIC PROC[in, out: IO.STREAM] = {
p: PhysicalVolume.Physical ← IagoOps.GetPhysical[in, out];
page: INT = GetPageNumber[in, out, LAST[INT]];
name: ROPE = PhysicalVolume.PhysicalInfo[p].name;
logicalPage: VolumeFormat.LogicalPage;
vol: File.Volume;
[logicalPage, vol] ← PhysicalToLogicalPage[p, [page]];
IF vol = NIL THEN {
out.PutF["\nPage %g not valid for %g", IO.int[page], IO.rope[name]];
RETURN;
};
DescribeOnePage[logicalPage, vol, in, out];
};
DescribeOnePage: PROC[
logicalPage: VolumeFormat.LogicalPage, vol: File.Volume, in, out: STREAM] = TRUSTED {
pageSpace: VM.Interval ← VM.nullInterval;
{ ENABLE UNWIND => FreeSpace[pageSpace];
initLabel: Disk.Label;
data: LONG POINTER;
label: Disk.Label ← initLabel;
request: Disk.Request;
status: Disk.Status;
attr: VolumeFormat.Attributes;
realPage: Disk.PageNumber;
val: WORD;
run: VolumeFormat.LogicalRun;
channel: Disk.Channel;
[pageSpace, data]← GetSpace[1];
run ← [[logicalPage], 1];
[channel, realPage]← FileInternal.TranslateLogicalRun[run, vol ! File.Error => {
out.PutF["\nFile.Error (%g) during TranslateLogicalRun of logical page %g\n",
IO.rope[IagoOps.FileError[why]], IO.int[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[logicalPage], int[realPage]];
GOTO keepGoing;
};
request ← [diskPage: [realPage], data: data, incrementDataPtr: FALSE,
command: [verify, read, read], count: 1];
[status, ]← Disk.DoIO[channel: channel, label: @label, request: @request];
attr ← LOOPHOLE[label.attributes];
IF attr = freePage THEN {
out.PutF["\n Page %g (physical page %g) is free, with status %g",
int[logicalPage], int[realPage], rope[StatusToRope[status].r]];
RETURN
};
IF attr = data OR attr = header THEN {
fp: File.FPLOOPHOLE[label.fileID.relID];
handle: File.Handle ← File.Open[vol, fp];
nameBody: Rope.Text;
version: FSBackdoor.Version;
[nameBody, version]← FSFileOps.GetNameBodyAndVersion[f: handle ! FS.Error => {
out.PutF["\n Page %g (physical page %g),\n\t***FS error: \"%g\"\n",
int[logicalPage], int[realPage], 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}];
IF nameBody # NIL THEN {
out.PutF["\n Page %g (physical page %g), with status %g\n",
int[logicalPage], int[realPage], rope[StatusToRope[status].r]];
out.PutF[" *** is %g page %g of file %g, version %g",
IF attr = data THEN rope["data"] ELSE rope["header"],
int[label.filePage],
rope[nameBody],
int[version]];
};
RETURN;
};
val ← LOOPHOLE[label.attributes];
out.PutF["\nOther type of page: logical: %g (physical %g), attributes: %g (%06b)\n",
IO.int[logicalPage], int[realPage], IO.rope[AttributesToRope[attr]], IO.card[val]];
EXITS
keepGoing => NULL;
END;
FreeSpace[pageSpace];
EXITS
doQuit => FreeSpace[pageSpace];
};
};
EnsureBadPagesInVAM: PUBLIC PROC[in, out: IO.STREAM] = {
Ensure: BadPagesListProc = TRUSTED {
FOR vls: LIST OF VolumeFormat.LogicalPage ← ls, vls.rest UNTIL vls=NIL DO
AllocatePageInVAM[out, vol, vls.first];
ENDLOOP;
};
DoBadPages[in, out, Ensure, TRUE];
};
ListBadPages: PUBLIC PROC[in, out: IO.STREAM] = {
Print: BadPagesListProc = TRUSTED {
FOR vls: LIST OF VolumeFormat.LogicalPage ← ls, vls.rest UNTIL vls=NIL DO
PrintBadPageInfo[out, subV, vls.first, drive];
ENDLOOP;
};
DoBadPages[in, out, Print, TRUE];
};
MarkPageAllocated: PUBLIC PROC[in, out: STREAM] = {
vol: File.Volume = IagoOps.GetLogical[in, out];
logicalPage: VolumeFormat.LogicalPage = GetLogicalPageNumber[in, out, LAST[INT]];
AllocatePageInVAM[out, vol, logicalPage];
};
ReadDiskPages: PUBLIC PROC[in, out: STREAM] = {
page, drive, count: INT;
d: Disk.Channel;
howToShowData: HowToShowData;
[d, drive] ← GetDrive[in, out];
IF d = NIL THEN RETURN;
page ← GetPageNumber[in, out, LAST[INT]];
count ← GetNumberOfPages[in, out];
IF count = 0 THEN RETURN;
howToShowData ← GetHowToShow[in, out];
IF howToShowData = stop THEN RETURN;
out.PutF["\nReading %g pages starting at %g on RD%g ...", int[count], int[page], int[drive]];
ReadAndReport[in, out, page, count, d, howToShowData];
};
ReadFilePages: PUBLIC PROC[in, out: STREAM] = {
name: ROPE ← IagoOps.GetFile[in: in, out: out];
openFile: FS.OpenFile;
handle: File.Handle;
fiHandle: FileInternal.Handle;
fullName: ROPENIL;
diskPage: Disk.PageNumber;
channel: Disk.Channel;
bytes, pagesInFile, startingPage, count: INT;
howToShowData: HowToShowData ← none;
[fullName, , , bytes, ]← FS.FileInfo[name: name
! FS.Error =>
SELECT error.code FROM
$unknownServer, $unknownVolume, $unknownFile, $illegalName, $patternNotAllowed => { out.PutRope[" ... "]; out.PutRope[error.explanation]; CONTINUE };
ENDCASE => NULL ];
IF fullName.Length[] = 0 THEN RETURN;
pagesInFile ← VM.PagesForBytes[bytes];
startingPage ← GetPageNumber[in, out, pagesInFile];
count ← GetNumberOfPages[in, out];
IF count = 0 THEN RETURN;
IF (count + startingPage) > pagesInFile THEN count ← pagesInFile - startingPage - 1;
howToShowData ← GetHowToShow[in, out];
IF howToShowData = stop THEN RETURN;
handle ← FSBackdoor.GetFileHandle[openFile ← FS.Open[fullName]];
TRUSTED {fiHandle ← LOOPHOLE[handle]};
FOR i: INT IN [0 .. count) DO
[diskPage, , channel]← FileInternal.FindRun[[startingPage+i], 1, fiHandle.runTable];
ReadAndReport[in, out, diskPage, count, channel, howToShowData];
ENDLOOP;
IF openFile # FS.nullOpenFile THEN FS.Close[openFile];
};
ReadLogicalPages: PUBLIC PROC[in, out: IO.STREAM] = {
count: INT;
d: Disk.Channel;
diskPage: Disk.PageNumber;
howToShowData: HowToShowData;
run: VolumeFormat.LogicalRun;
volume: File.Volume = IagoOps.GetLogical[in, out];
logicalPage: VolumeFormat.LogicalPage;
IF volume = NIL THEN RETURN;
logicalPage ← GetLogicalPageNumber[in, out, LAST[INT]];
count ← GetNumberOfPages[in, out];
IF count = 0 THEN RETURN;
howToShowData ← GetHowToShow[in, out];
IF howToShowData = stop THEN RETURN;
run ← [logicalPage, 1];
[d, diskPage]← FileInternal.TranslateLogicalRun[run, volume];
out.PutF["\nReading %g pages starting at (logical) %g on volume: %g ...", int[count], int[logicalPage], rope[File.GetVolumeName[volume]]];
ReadAndReport[in, out, diskPage, count, d, howToShowData];
};
ReadNamedFile: PUBLIC PROC[in, out: IO.STREAM] = {
name: ROPE = IagoOps.GetFile[in: in, out: out];
openFile: FS.OpenFile;
handle: File.Handle = FSBackdoor.GetFileHandle[openFile ← FS.Open[name]];
IF handle # NIL THEN CheckPages[handle, name, out];
IF openFile # FS.nullOpenFile THEN FS.Close[openFile];
};
ShowDiskAddress: PUBLIC PROC[in, out: IO.STREAM] = {
d: Disk.Channel = GetDrive[in: in, out: out].d;
page: INT = GetPageNumber[in, out, LAST[INT]];
out.PutChar['\n];
PrintPageAndDiskAddr[[page], ChannelToDeviceHandle[d], out];
};
UpdateVAM: PUBLIC PROC[in, out: STREAM] = {};
WriteDiskPage: PUBLIC PROC[in, out: STREAM] = TRUSTED {
diskPage, drive: INT;
d: Disk.Channel;
useOldData: BOOL;
pageSpace: VM.Interval;
[d, drive] ← GetDrive[in, out];
IF d = NIL THEN RETURN;
diskPage ← GetPageNumber[in, out, LAST[INT]];
out.PutRope["\nDo you want to try to rewrite the old data? "];
useOldData ← IagoOps.Confirm[in, out];
{
ENABLE UNWIND => FreeSpace[pageSpace];
data: LONG POINTER;
initLabel: Disk.Label;
label: Disk.Label ← initLabel;
request: Disk.Request;
status: Disk.Status;
[pageSpace, data]← GetSpace[1];
request ← [diskPage: [diskPage], data: data, incrementDataPtr: FALSE,
command: [verify, read, read], count: 1];
[status, ]← Disk.DoIO[channel: d, label: @label, request: @request];
IF OKStatusForWrite[status, in, out]
THEN {
IF ~useOldData THEN
FOR i: INT IN [0 .. File.wordsPerPage) DO (data+i)^← 0; ENDLOOP;
request ← [diskPage: [diskPage], data: data, incrementDataPtr: FALSE,
command: [verify, verify, write], count: 1];
[status, ]← Disk.DoIO[channel: d, label: @label, request: @request];
ReportStatus[out, status, FALSE];
}
ELSE {
ReportStatus[out, status];
out.PutRope["\n Write NOT done\n"];
};
FreeSpace[pageSpace];
};
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DeleteSystemFile: PUBLIC PROC[in, out: IO.STREAM, which: File.VolumeFile] = {
volume: File.Volume ← IagoOps.GetLogical[in, out];
fp: File.FP;
file: File.Handle;
name: ROPE ← VolumeFileToRope[which];
fp ← FileBackdoor.GetRoot[volume, which].fp;
IF fp = File.nullFP THEN {
out.PutF["\n%g file doesn't exist", rope[name]];
RETURN};
file ← File.Open[volume, fp
! File.Error => {
out.PutRope[" ... "];
out.PutRope[IagoOps.FileError[why]];
file ← NIL;
CONTINUE } ];
IF file # NIL THEN {
out.PutF["Confirm deletion of %g file: ", rope[name]];
IF IagoOps.Confirm[in, out] THEN {
out.PutF["\nDeleting %g file", rope[name]];
File.Delete[file];
out.PutF["\n%g file has been deleted", rope[name]]
};
};
};
ReadAndReport: PROC[in, out: STREAM, first, count: INT, d: Disk.Channel, howToShowData: HowToShowData] = TRUSTED {
pageSpace: VM.Interval ← VM.nullInterval;
{
ENABLE UNWIND => FreeSpace[pageSpace];
origin: Disk.PageNumber;
initLabel: Disk.Label;
data: LONG POINTER;
[pageSpace, data]← GetSpace[1];
FOR i: INT IN [0 .. count) DO
label: Disk.Label ← initLabel;
val: WORD;
request: Disk.Request;
status: Disk.Status;
countDone: INT;
attr: VolumeFormat.Attributes;
origin ← [first + i];
request ← [diskPage: origin, data: data, incrementDataPtr: FALSE,
command: [verify, read, read], count: 1];
[status, countDone]← Disk.DoIO[channel: d, label: @label, request: @request];
ReportStatus[out, status];
out.PutF["\n\n *******Label for disk page %g is:\nFileID: ", int[first+i]];
FOR i: INT IN [0..5) DO
val ← LOOPHOLE[@label + i, LONG POINTER TO CARDINAL]^;
out.PutF[" %06b", IO.card[val]];
ENDLOOP;
attr ← LOOPHOLE[label.attributes];
IF attr = data OR attr = header THEN {
relID: VolumeFormat.RelID ← LOOPHOLE[label.fileID.relID];
da: VolumeFormat.LogicalPage ← LOOPHOLE[relID.da];
out.PutF["\n Logical Address of Header: %g", int[da]];
};
val ← LOOPHOLE[label.attributes];
out.PutF["\nfilePage: %g, attributes: %g (%06b)\n",
IO.int[label.filePage], IO.rope[AttributesToRope[attr]], IO.card[val]];
IF howToShowData # none THEN {
ptr: LONG POINTER TO CARDINALLOOPHOLE[data];
valsPerLine: INT = 8;  -- so its easy to change
numLines: INT ← 256/valsPerLine;
out.PutF["\n *****Data for that page is:\n"];
IF howToShowData = all
THEN {
FOR i: INT IN [0..numLines) DO
out.PutF["%03b/ ", IO.int[i*valsPerLine]];
FOR j: INT IN [0..valsPerLine) DO
val ← (ptr+ j)^; out.PutF[" %06b", IO.card[val]];
ENDLOOP;
out.PutRope[" "];  -- some white space
FOR j: INT IN [0..valsPerLine) DO
bytes: Basics.BytePair;
val ← (ptr+ j)^;
bytes ← LOOPHOLE[val, Basics.BytePair];
out.PutChar[CheckChar[LOOPHOLE[bytes.high, CHAR]]];
out.PutChar[CheckChar[LOOPHOLE[bytes.low, CHAR]]];
ENDLOOP;
out.PutChar['\n];
ptr ← ptr + valsPerLine;
ENDLOOP;
}
ELSE
FOR i: INT IN [0 .. 256) DO
bytes: Basics.BytePair;
val ← (ptr+ i)^;
bytes ← LOOPHOLE[val, Basics.BytePair];
out.PutChar[CheckChar[LOOPHOLE[bytes.high, CHAR]]];
out.PutChar[CheckChar[LOOPHOLE[bytes.low, CHAR]]];
ENDLOOP;
};
ENDLOOP;
FreeSpace[pageSpace];
};
};
CheckChar: PROC[ch: CHAR] RETURNS[CHAR] = {
OPEN Ascii;
xx: CARDINAL [0 .. 377B];
IF ch = NUL THEN RETURN[' ];
IF ch IN [ControlA .. ControlZ] THEN RETURN['*];
IF (xx ← LOOPHOLE[ch]) IN [200B .. 377B] THEN RETURN['!];
RETURN[ch]
};
ReadSystemFile: PUBLIC PROC[in, out: STREAM, which: File.VolumeFile] = {
volume: File.Volume ← IagoOps.GetLogical[in, out];
fp: File.FP;
page: File.PageNumber;
handle: File.Handle;
name: ROPE ← VolumeFileToRope[which];
[fp, page]← FileBackdoor.GetRoot[volume, which];
IF fp = File.nullFP THEN RETURN;
handle ← File.Open[volume, fp ! File.Error =>
{ out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; handle ← NIL; CONTINUE } ];
IF handle # NIL THEN CheckPages[handle, name, out];
};
CheckPages: PROC[handle: File.Handle, name: ROPE, 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[handle].size;
didStop: BOOLFALSE;
diskPage: Disk.PageNumber;
channel: Disk.Channel;
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[name], 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[handle, [i], numPages, data ! File.Error =>
{IF why # unknownPage THEN didStop ← TRUE; EXIT}];
i ← i + numPages;
ENDLOOP;
IF didStop THEN {
ff: FileInternal.Handle;
badPage: INT ← -1;
FOR j: File.PageCount IN [i..i+numPages) DO
File.Read[handle, [j], 1, data
! File.Error => {
out.PutF["\nFile error reading page %g\n", IO.int[j]];
out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]];
badPage ← j;
EXIT}];
ENDLOOP;
ff ← LOOPHOLE[handle];
[diskPage, , channel]← FileInternal.FindRun[[badPage], 1, ff.runTable];
out.PutChar['\n];
PrintPageAndDiskAddr[diskPage, ChannelToDeviceHandle[channel], out];
ReadAndReport[NIL, out, diskPage, 1, channel, none];
};
};
FreeSpace[pageSpace];
};
};
AllocatePageInVAM: PROC[
 out: STREAM, 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];
out.PutF["\n Logical page %g has been added to the VAM for volume: %g",
IO.int[logicalPage], IO.rope[File.GetVolumeName[vol]]]
}
ELSE {
FileInternal.Free[vol, given];
out.PutF["\n Logical page %g was already in the VAM", IO.int[logicalPage]];
};
};
PhysicalToLogicalPage: PROC[p: PhysicalVolume.Physical, page: Disk.PageNumber]
RETURNS[logicalPage: VolumeFormat.LogicalPage, vol: File.Volume] = {
sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p];
FOR i: CARDINAL IN [0..sv.count) DO
start: INT = sv[i].start;
IF page >= start AND page < start+sv[i].size THEN {
logicalPage ← [page-start];
vol ← File.FindVolumeFromID[sv[i].id];
RETURN
};
ENDLOOP;
RETURN[[0], File.FindVolumeFromID[File.nullVolumeID]];
};
ReservedList: TYPE = LIST OF RECORD[start: Disk.PageNumber, size: Disk.PageCount];
P2: TYPE = REF PhysicalObject;
PhysicalObject: TYPE = RECORD[
channel: Disk.Channel,
rootStatus: PhysicalVolume.PhysicalRC ← ok,
root: LONG POINTER TO VolumeFormat.PhysicalRoot ← NIL,
bad: LONG POINTER TO VolumeFormat.BadPageList ← NIL,
name: Rope.ROPENIL,
reserved: ReservedList ← NIL,
rest: REF PhysicalObject ← NIL ];
DoBadPages: PROC[in, out: IO.STREAM, proc: BadPagesListProc, doOut: BOOL] = {
p: PhysicalVolume.Physical ← IagoOps.GetPhysical[in, out];
anyBad: BOOLFALSE;
sv: PhysicalVolume.SubVolumes;
drive: DiskFace.DeviceHandle;
subV: PhysicalVolume.SubVolumeDetails;
vol: File.Volume;
badList: LIST OF VolumeFormat.LogicalPage;
IF p = NIL THEN RETURN;
TRUSTED {
p2: P2 ← LOOPHOLE[p];
p2.bad ← NIL;
force reading of BadPageTable from disk
};
drive ← ChannelToDeviceHandle[PhysicalVolume.PhysicalInfo[p].channel];
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];
IF doOut THEN
out.PutF["\n\nBad Pages for %g {subvolume %g}:", rope[File.GetVolumeName[vol]], card[i]];
IF proc # NIL THEN proc[badList, subV, vol, drive];
ENDLOOP;
IF ~anyBad THEN IF doOut THEN out.PutRope["\nNo bad pages listed"];
};
PrintBadPageInfo: PROC[out: STREAM, subV: PhysicalVolume.SubVolumeDetails, logicalPage: VolumeFormat.LogicalPage, drive: DiskFace.DeviceHandle] = {
bad: Disk.PageNumber ← [logicalPage + subV.address + subV.start];
out.PutF["\n Logical page: %g (%bB)", int[logicalPage], int[logicalPage]];
PrintPageAndDiskAddr[bad, drive, out];
};
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;
};
}.