ExtraIagoOpsImpl.mesa
Last Edited by: Willie-Sue, February 6, 1985 8:41:16 am PST
DIRECTORY
Ascii,
Basics USING [BytePair, LongDivMod],
Convert USING [Error, IntFromRope],
Disk USING [Channel, Label, PageCount, PageNumber, Request, Status, DoIO, DriveAttributes],
DiskFace USING [DeviceHandle, DiskAddress, GetDeviceAttributes],
File USING [Error, FP, Handle, nullFP, PageCount, PageNumber, Volume, VolumeID, VolumeFile, wordsPerPage, Delete, FindVolumeFromID, GetVolumeName, GetRoot, Info, Open, Read],
FileExtra USING [PhysicalPageBad],
FileInternal USING [Handle, Alloc, Commit, FindRun, Free, GetBadPages, TranslateLogicalRun],
FS USING [Error, GetInfo, OpenFile, nullOpenFile, Close, Open],
FSBackdoor USING [Version, GetFileHandle],
FSFileOps USING [GetNameBodyAndVersion],
FormatDisk USING [altoRegionJargon, altoRegionsSize, hardUCodeSize],
IagoOps USING [Confirm, FileError, GetArg, GetDrive, GetFile, GetLogical, GetNumber, GetPhysical, PutID],
IO,
PhysicalVolume USING [Physical, PhysicalRC, SubVolumes, SubVolumeDetails, SubVolumeDetailsObject, GetSubVolumes, NextPhysical, PhysicalInfo],
Rope,
SystemVersion USING [machineType],
VM USING [Interval, nullInterval, PagesForWords, AddressForPageNumber, Allocate, Free, SwapIn, Unpin],
VolumeFormat USING [Attributes, RelID, BadPageList, LogicalPage, LogicalRun, PhysicalRoot],
ExtraIagoOps;
ExtraIagoOpsImpl:
CEDAR PROGRAM
IMPORTS
Basics, Convert, Disk, DiskFace, File, FileExtra, FileInternal, FormatDisk, FS, FSBackdoor, FSFileOps, IagoOps, IO, PhysicalVolume, Rope, SystemVersion, VM
EXPORTS ExtraIagoOps =
BEGIN OPEN IO;
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
HowToShowData: TYPE = {none, all, text, stop};
pagePrompt: ROPE = "\nPage 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] =
BEGIN
d← IagoOps.GetDrive[in, out];
drive← IF d # NIL THEN Disk.DriveAttributes[d].ordinal ELSE -1;
END;
GetPageNumber:
PROC[in, out:
STREAM, max:
INT]
RETURNS[page:
INT] =
BEGIN
page← 0;
page← IagoOps.GetNumber[
in: in, out: out, default: page, max: max, prompt: pagePrompt, help: pageHelp];
END;
GetNumberOfPages:
PROC[in, out:
STREAM]
RETURNS[count:
INT] =
BEGIN
count← 1;
count← IagoOps.GetNumber[
in: in, out: out, default: count, max: 7, prompt: numPagesPrompt, help: numPagesHelp];
END;
GetHowToShow:
PROC[in, out:
STREAM]
RETURNS[howToShowData: HowToShowData] =
BEGIN
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;
END;
Loophole:
PUBLIC
PROC[in, out:
STREAM] = {
Help: PROC = { PutRope[out, "type a number, ending in B or H"] };
ok: BOOL ← TRUE;
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", int[size]]
ELSE out.PutRope["\nNot a number"];
};
GetAddress:
PUBLIC PROC[d: DiskFace.DeviceHandle, page:
INT]
RETURNS[addr: DiskFace.DiskAddress] =
BEGIN
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 ] ]
END;
PrintPageAndDiskAddr:
PROC[page: Disk.PageNumber, d: DiskFace.DeviceHandle, out: STREAM] =
BEGIN
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]];
END;
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:
BOOL←
TRUE] =
BEGIN
r: ROPE;
wasOK: BOOL;
[r, wasOK]← StatusToRope[status];
IF dontReportOK AND wasOK THEN RETURN;
out.PutF["\n\n **********Status reported as: %g **********", rope[r]];
END;
OKStatusForWrite:
PROC[status: Disk.Status, in, out:
STREAM]
RETURNS[ok:
BOOL] =
BEGIN
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];
END;
ChannelToDeviceHandle:
PROC[c: Disk.Channel]
RETURNS[d: DiskFace.DeviceHandle] =
BEGIN
ordinal: CARDINAL← Disk.DriveAttributes[c].ordinal;
d← LOOPHOLE[ordinal, DiskFace.DeviceHandle];
END;
VolumeFileToRope:
PROC[which: File.VolumeFile]
RETURNS[
ROPE] =
BEGIN
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"];
END;
AttributesToRope:
PROC[attributes: VolumeFormat.Attributes]
RETURNS[
ROPE] =
BEGIN
RETURN[
SELECT attributes
FROM
physicalRoot => "PhysicalRoot",
badPageList => "BadPageList",
badPage => "BadPage",
subVolumeMarker => "SubVolumeMarker",
logicalRoot => "LogicalRoot",
freePage => "FreePage",
header => "HeaderPage",
data => "DataPage",
lastCedar => "LastCedarAttribute",
ENDCASE => "UnknownType"];
END;
-- * * * * * * * * commands implementation * * * * * * * *
AddPageToBadPageTable:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
page: INT;
p: PhysicalVolume.Physical← IagoOps.GetPhysical[in, out];
name: ROPE← PhysicalVolume.PhysicalInfo[p].name;
page← GetPageNumber[in, out, LAST[INT]];
out.PutF["\nAdding physical page %g to bad page table for %g", int[page], rope[name]];
[]← FileExtra.PhysicalPageBad[p, [page]];
END;
DescribeAllocated:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
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]]
];
BEGIN
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
BEGIN
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]]
];
END;
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]]
];
END;
ENDLOOP;
END;
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:
STREAM] =
TRUSTED {
badPagesInfo: LIST OF BadPagesRec← NIL;
count: INT← 0;
pageSpace: VM.Interval← VM.nullInterval;
realPage: Disk.PageNumber;
Dbp: BadPagesListProc =
TRUSTED { badPagesInfo← CONS[ [ls, subV, vol, drive], badPagesInfo] };
DoBadPages[in, out, Dbp, FALSE];
IF badPagesInfo = NIL THEN RETURN;
BEGIN
ENABLE
UNWIND => FreeSpace[pageSpace];
initLabel: Disk.Label;
data: LONG POINTER;
[pageSpace, data]← GetSpace[1];
FOR bp:
LIST
OF BadPagesRec← badPagesInfo, bp.rest
UNTIL bp =
NIL
DO
label: Disk.Label ← initLabel;
request: Disk.Request;
status: Disk.Status;
attr: VolumeFormat.Attributes;
val: WORD;
thisVol: File.Volume;
run: VolumeFormat.LogicalRun;
channel: Disk.Channel;
FOR ls:
LIST
OF VolumeFormat.LogicalPage← bp.first.ls, ls.rest
UNTIL ls=
NIL
DO
run← [[ls.first], 1];
[channel, realPage]← FileInternal.TranslateLogicalRun[run, bp.first.vol];
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 (count← count + 1)
MOD 5 = 0
THEN {
out.PutRope["\nContinue?"];
IF ~IagoOps.Confirm[in, out] THEN { FreeSpace[pageSpace]; RETURN};
};
IF thisVol # bp.first.vol
THEN {
thisVol ← bp.first.vol;
out.PutF["\n\nBad Pages for %g\n", rope[File.GetVolumeName[thisVol]]];
};
IF attr = freePage
THEN
{ out.PutF["\n Page %g (physical page %g) is free, with status %g",
int[ls.first], int[realPage], rope[StatusToRope[status].r]];
LOOP
};
IF attr = data
OR attr = header
THEN {
fp: File.FP← LOOPHOLE[label.fileID.relID];
handle: File.Handle← File.Open[bp.first.vol, fp];
nameBody: Rope.Text;
version: FSBackdoor.Version;
[nameBody, version]← FSFileOps.GetNameBodyAndVersion[f: handle];
out.PutF["\n Page %g (physical page %g), with status %g\n",
int[ls.first], 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]];
LOOP;
};
val← LOOPHOLE[label.attributes];
out.PutF["\nOther type of page: filePage: %g, attributes: %g (%06b)\n",
int[label.filePage], rope[AttributesToRope[attr]], card[val]];
ENDLOOP;
ENDLOOP;
FreeSpace[pageSpace];
END;
};
EnsureBadPagesInVAM:
PUBLIC
PROC[in, out:
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:
STREAM] = {
Print: BadPagesListProc =
TRUSTED {
FOR vls:
LIST
OF VolumeFormat.LogicalPage← ls, vls.rest
UNTIL vls=
NIL
DO
PrintPageInfo[out, subV, vls.first, drive]; ENDLOOP;
};
DoBadPages[in, out, Print, TRUE];
};
MarkPageAllocated:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
vol: File.Volume← IagoOps.GetLogical[in, out];
page: INT← GetPageNumber[in, out, LAST[INT]];
logicalPage: VolumeFormat.LogicalPage← [page];
AllocatePageInVAM[out, vol, logicalPage];
END;
ReadDiskPages:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
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];
END;
ReadFilePages:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
name: ROPE← IagoOps.GetFile[in: in, out: out];
openFile: FS.OpenFile;
handle: File.Handle;
fiHandle: FileInternal.Handle;
fullName: ROPE← NIL;
diskPage: Disk.PageNumber;
channel: Disk.Channel;
pagesInFile, startingPage, count: INT;
howToShowData: HowToShowData← none;
openFile ←
FS.Open[name: name, remoteCheck:
FALSE !
FS.Error =>
{ out.PutRope[" ... "]; out.PutRope[error.explanation]; CONTINUE } ];
IF openFile = NIL THEN RETURN;
pagesInFile ← FS.GetInfo[openFile].pages;
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];
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];
END;
ReadLogicalPages:
PUBLIC
PROC[in, out:
STREAM] =
BEGIN
page, count: INT;
d: Disk.Channel;
diskPage: Disk.PageNumber;
howToShowData: HowToShowData;
run: VolumeFormat.LogicalRun;
volume: File.Volume← IagoOps.GetLogical[in, out];
IF volume = 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;
run← [[page], 1];
[d, diskPage]← FileInternal.TranslateLogicalRun[run, volume];
out.PutF["\nReading %g pages starting at (logical) %g on volume: %g ...",
int[count], int[page], rope[File.GetVolumeName[volume]]];
ReadAndReport[in, out, diskPage, count, d, howToShowData];
END;
ReadNamedFile:
PUBLIC PROC[in, out:
STREAM] =
BEGIN
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];
END;
ShowDiskAddress:
PUBLIC
PROC[in, out:
STREAM] =
BEGIN
d: Disk.Channel← GetDrive[in: in, out: out].d;
page: INT← GetPageNumber[in, out, LAST[INT]];
out.PutChar['\n];
PrintPageAndDiskAddr[[page], ChannelToDeviceHandle[d], out];
END;
UpdateVAM: PUBLIC PROC[in, out: STREAM] = {};
WriteDiskPage:
PUBLIC
PROC[in, out:
STREAM] =
TRUSTED
BEGIN
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];
BEGIN 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
BEGIN
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];
END
ELSE
BEGIN
ReportStatus[out, status];
out.PutRope["\n Write NOT done\n"];
END;
FreeSpace[pageSpace];
END;
END;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DeleteSystemFile:
PUBLIC PROC[in, out:
STREAM, which: File.VolumeFile] =
BEGIN
volume: File.Volume← IagoOps.GetLogical[in, out];
fp: File.FP;
file: File.Handle;
name: ROPE← VolumeFileToRope[which];
fp← File.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]]
};
};
END;
ReadAndReport:
PROC[
in, out: STREAM, first, count: INT, d: Disk.Channel, howToShowData: HowToShowData] =
TRUSTED
BEGIN
pageSpace: VM.Interval← VM.nullInterval;
BEGIN
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", 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",
int[label.filePage], rope[AttributesToRope[attr]], card[val]];
IF howToShowData # none
THEN
BEGIN
ptr: LONG POINTER TO CARDINAL← LOOPHOLE[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
BEGIN
FOR i:
INT
IN [0..numLines)
DO
out.PutF["%03b/ ", int[i*valsPerLine]];
FOR j:
INT
IN [0..valsPerLine)
DO
val← (ptr+ j)^; out.PutF[" %06b", 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;
END
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;
END;
ENDLOOP;
FreeSpace[pageSpace];
END;
END;
CheckChar:
PROC[ch:
CHAR]
RETURNS[
CHAR] =
BEGIN
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]
END;
ReadSystemFile:
PUBLIC PROC[in, out:
STREAM, which: File.VolumeFile] =
BEGIN
volume: File.Volume← IagoOps.GetLogical[in, out];
fp: File.FP;
page: File.PageNumber;
handle: File.Handle;
name: ROPE← VolumeFileToRope[which];
[fp, page]← File.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];
END;
CheckPages:
PROC[handle: File.Handle, name:
ROPE, out:
STREAM] =
BEGIN
ENABLE
FS.Error =>
BEGIN
out.PutRope[" ... "];
IF error.code = $invalidPropertyPage
THEN out.PutRope["not an FS file"]
ELSE out.PutRope[error.explanation];
CONTINUE
END;
size: File.PageCount← File.Info[handle].size;
didStop: BOOL← FALSE;
diskPage: Disk.PageNumber;
channel: Disk.Channel;
pageSpace: VM.Interval ← VM.nullInterval;
numPages: INT = 25;
BEGIN ENABLE UNWIND => FreeSpace[pageSpace];
data: LONG POINTER;
[pageSpace, data]← GetSpace[numPages];
out.PutF["\nReading pages from %g file (%g pages)\n", rope[name], int[size]];
TRUSTED
BEGIN
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)", int[i]] ELSE out.PutChar['~];
File.Read[handle, [i], numPages, data ! File.Error => {
IF didStop ← (why # unknownPage)
THEN
IF why # hardware
THEN {
out.PutChar['\n]; out.PutRope[IagoOps.FileError[why]]; out.PutChar['\n] };
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 (%g) reading page %g\n",
rope[IagoOps.FileError[why]], int[j] ];
badPage← j; EXIT}];
ENDLOOP;
ff← LOOPHOLE[handle];
IF badPage = -1
THEN {
size: INT;
[diskPage, size, ] ← FileInternal.FindRun[[badPage], numPages, ff.runTable];
out.PutRope["\n Error did not re-occur"];
IF size = numPages
THEN {
out.PutF["\nError page is in (file) pages [%g..%g), ",
int[i], int[i+numPages-1] ];
out.PutF["(disk) pages [%g..%g)\n",
int[diskPage], int[diskPage+numPages-1] ]
}
ELSE {
numLeft: INT ← numPages;
out.PutRope["\nError page is in: "];
DO
out.PutF[" (file) pages [%g..%g), ", int[i], int[i+size-1] ];
out.PutF["(disk) pages [%g..%g); ",
int[diskPage], int[diskPage+size-1] ];
IF (numLeft ← numLeft - size) <= 0
THEN
{ out.PutChar['\n]; EXIT };
i ← i + size;
[diskPage, size, ] ← FileInternal.FindRun[[badPage], numLeft, ff.runTable];
ENDLOOP;
};
}
ELSE {
[diskPage, , channel]← FileInternal.FindRun[[badPage], 1, ff.runTable];
out.PutChar['\n];
PrintPageAndDiskAddr[diskPage, ChannelToDeviceHandle[channel], out];
ReadAndReport[NIL, out, diskPage, 1, channel, none];
};
};
END;
FreeSpace[pageSpace];
END;
END;
AllocatePageInVAM:
PROC[out: STREAM, vol: File.Volume, logicalPage: VolumeFormat.LogicalPage] =
BEGIN
given: VolumeFormat.LogicalRun←
FileInternal.Alloc[volume: vol, first: logicalPage, size: 1, min: 1];
IF given.first = logicalPage
THEN
BEGIN
FileInternal.Commit[vol];
out.PutF["\n Logical page %g has been added to the VAM for volume: %g",
int[logicalPage], rope[File.GetVolumeName[vol]]]
END
ELSE
BEGIN
FileInternal.Free[vol, given];
out.PutF["\n Logical page %g was already in the VAM", int[logicalPage]];
END;
END;
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.ROPE ← NIL,
reserved: ReservedList ← NIL,
rest: REF PhysicalObject ← NIL ];
DoBadPages:
PROC[in, out:
STREAM, proc: BadPagesListProc, doOut:
BOOL] =
BEGIN
p: PhysicalVolume.Physical← IagoOps.GetPhysical[in, out];
anyBad: BOOL← FALSE;
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"];
END;
PrintPageInfo:
PROC[out:
STREAM, subV: PhysicalVolume.SubVolumeDetails,
logicalPage: VolumeFormat.LogicalPage, drive: DiskFace.DeviceHandle] =
BEGIN
bad: Disk.PageNumber← [logicalPage + subV.address + subV.start];
out.PutF["\n Logical page: %g (%bB)", int[logicalPage], int[logicalPage]];
PrintPageAndDiskAddr[bad, drive, out];
END;
GetSpace:
PROC[num:
INT]
RETURNS[space:
VM.Interval, data:
LONG
POINTER] =
TRUSTED
BEGIN
space← VM.Allocate[VM.PagesForWords[num*File.wordsPerPage]];
data← VM.AddressForPageNumber[space.page];
VM.SwapIn[interval: space, kill: TRUE, pin: TRUE];
END;
FreeSpace:
PROC[space:
VM.Interval] =
TRUSTED
BEGIN
IF space = VM.nullInterval THEN RETURN;
VM.Unpin[space];
VM.Free[space];
space← VM.nullInterval;
END;
END.