ExtraIagoOpsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Willie-Sue, May 29, 1986 4:09:19 pm PDT
Russ Atkinson (RRA) March 11, 1985 8:39:34 pm PST
DIRECTORY
Ascii,
Basics USING [LongDivMod, DivMod],
Convert USING [Error, IntFromRope],
Disk USING [Channel, DoIO, DriveAttributes, GetDeviceFromChannel, Label, PageCount, PageNumber, Request, Status],
DiskFace USING [DeviceHandle, DiskAddress, GetDeviceAttributes, GetTrueDeviceAttributes],
ExtraIagoOps,
ExtraIagoUtils,
File USING [Delete, Error, FindVolumeFromID, FP, GetVolumeName, Handle, nullFP, Open, PageCount, PageNumber, Volume, VolumeFile, VolumeID, wordsPerPage],
FileBackdoor USING [GetRoot, CreatePhysicalVolume, PhysicalPageBad],
FileInternal USING [Handle, Alloc, Commit, FindRun, Free, GetBadPages, TranslateLogicalRun],
FS USING [Close, Error, FileInfo, nullOpenFile, Open, OpenFile],
FSBackdoor USING [GetFileHandle],
FormatDisk USING [altoRegionJargon, altoRegionsSize, hardUCodeSize, BadPage, Sweep],
IagoOps USING [Confirm, FileError, GetArg, GetDrive, GetFile, GetLogical, GetNumber, GetPhysical, NewID, NextRun, 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 [BadPageList, LogicalPage, LogicalRun, PhysicalRoot];
ExtraIagoOpsImpl: CEDAR PROGRAM
IMPORTS Basics, Convert, Disk, DiskFace, ExtraIagoUtils, File, FileBackdoor, FileInternal, FormatDisk, FS, FSBackdoor, IagoOps, IO, PhysicalVolume, Rope, SystemVersion, VM
EXPORTS ExtraIagoOps =
BEGIN OPEN IO, ExtraIagoUtils;
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
HowToShowData: TYPE = ExtraIagoUtils.HowToShowData;
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)}";
cylinderPrompt: ROPE = "\nCylinder number: ";
startingCylinderPrompt: ROPE = "\nStarting Cylinder number: ";
cylinderHelp: ROPE = "? type the cylinder number (in decimal)";
headPrompt: ROPE = "\nHead number: ";
startingHeadPrompt: ROPE = "\nStarting Head number: ";
headHelp: ROPE = "? type the head number (in decimal)";
sectorPrompt: ROPE = "\nSector number: ";
sectorHelp: ROPE = "? type the sector number (in decimal)";
* * * * * * * * * * * * * * * * * Utility Procedures * * * * * * * * * * * * * * *
GetCylinder: PROC[in, out: STREAM, max: INT] RETURNS[cyl: INT] = {
cyl ← 0;
cyl ← IagoOps.GetNumber[
in: in, out: out, default: cyl, max: max, prompt: cylinderPrompt, help: cylinderHelp];
};
GetStartingCylinder: PROC[in, out: STREAM, max: INT] RETURNS[cyl: INT] = {
cyl ← 0;
cyl ← IagoOps.GetNumber[
in: in, out: out, default: cyl, max: max, prompt: startingCylinderPrompt, help: cylinderHelp];
};
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;
};
GetHeadNumber: PROC[in, out: STREAM, max: INT] RETURNS[head: INT] = {
head ← 0;
head ← IagoOps.GetNumber[
in: in, out: out, default: head, max: max, prompt: headPrompt, help: headHelp];
};
GetStartingHead: PROC[in, out: STREAM, max: INT] RETURNS[head: INT] = {
head ← 0;
head ← IagoOps.GetNumber[
in: in, out: out, default: head, max: max, prompt: startingHeadPrompt, help: headHelp];
};
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, max: INT ← 20] RETURNS[count: INT] = {
count ← 1;
count ← IagoOps.GetNumber[
in: in, out: out, default: count, max: max, prompt: numPagesPrompt, help: numPagesHelp];
};
GetSector: PROC[in, out: STREAM, max: INT] RETURNS[sector: INT] = {
sector ← 0;
sector ← IagoOps.GetNumber[
in: in, out: out, default: sector, max: max, prompt: sectorPrompt, help: sectorHelp];
};
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;
};
IConvert: 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 ] ]
};
* * * * * * * * 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];
};
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];
};
MakeDiskAddress: PUBLIC PROC[in, out: STREAM] = {
drive, cyl, head, sector: INT;
page: INT ← 0;
cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL;
d: Disk.Channel;
device: DiskFace.DeviceHandle;
[d, drive] ← GetDrive[in, out];
IF d = NIL THEN RETURN;
device ← Disk.GetDeviceFromChannel[d];
[cylinders, movingHeads, fixedHeads, sectorsPerTrack] ←
 DiskFace.GetTrueDeviceAttributes[device];
cyl ← GetCylinder[in, out, cylinders-1];
head ← GetHeadNumber[in, out, movingHeads-1];
sector ← GetSector[in, out, sectorsPerTrack-1];
IF cylinders # 0 THEN {
IF drive = 0 AND (SystemVersion.machineType = dorado) THEN-- funny drive 0 addressing
page ← head*cylinders*sectorsPerTrack + cyl*sectorsPerTrack + sector
ELSE
page ← head*sectorsPerTrack + cyl*movingHeads*sectorsPerTrack + sector;
};
out.PutF["\nPage number %g", IO.int[page]];
};
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];
};
MiniDiskDiagnostic: PUBLIC PROC[in, out: IO.STREAM] = {
firstPage, drive, count: INT;
cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL;
d: Disk.Channel;
[d, drive] ← GetDrive[in, out];
IF d = NIL THEN RETURN;
[cylinders, movingHeads, fixedHeads, sectorsPerTrack] ←
 DiskFace.GetTrueDeviceAttributes[Disk.GetDeviceFromChannel[d]];
IF drive = 0 AND (SystemVersion.machineType = dorado) THEN {
ch: CHAR;
isP, isH: BOOLFALSE;
DO
ph: ROPE = "\nType p for specific pages, h for specific head: ";
Hph: PROC = { out.PutRope[ph] };
resp: ROPE ← IagoOps.GetArg[in, out, ph, "p", Hph];
ch ← resp.Fetch[0];
IF (isP ← (ch = 'p OR ch = 'P)) OR (isH ← (ch = 'h OR ch = 'H)) THEN EXIT;
ENDLOOP;
IF isP THEN {
firstPage ← GetPageNumber[in, out, LAST[INT]];
count ← GetNumberOfPages[in, out, LAST[INT]];
}
ELSE {
head: INT ← GetHeadNumber[in, out, movingHeads-1];
startingCyl: INT ← GetStartingCylinder[in, out, cylinders-1];
count ← GetNumberOfPages[in, out, (cylinders-startingCyl)*sectorsPerTrack];
firstPage ← head*cylinders*sectorsPerTrack + startingCyl*sectorsPerTrack;
};
}
ELSE {  -- not drive 0 or not Dorado
ch: CHAR;
isP, isC: BOOLFALSE;
DO
pc: ROPE = "\nType p for specific pages, c for specific cylinder: ";
Hpc: PROC = { out.PutRope[pc] };
resp: ROPE ← IagoOps.GetArg[in, out, pc, "p", Hpc];
ch ← resp.Fetch[0];
IF (isP ← (ch = 'p OR ch = 'P)) OR (isC ← (ch = 'c OR ch = 'C)) THEN EXIT;
ENDLOOP;
IF isP THEN {
firstPage ← GetPageNumber[in, out, LAST[INT]];
count ← GetNumberOfPages[in, out, LAST[INT]];
}
ELSE {
cyl: INT ← GetCylinder[in, out, cylinders-1];
startingHead: INT ← GetStartingHead[in, out, movingHeads-1];
count ← GetNumberOfPages[in, out, LAST[INT]];
firstPage ← startingHead*sectorsPerTrack + cyl*sectorsPerTrack;
};
};
now we can do the real work, having starting page number and count of pages
TRUSTED {ScanThesePages[in, out, d, firstPage, count]};
};
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];
};
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]]
};
};
};
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];
};
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]];
};
};
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];
};
ScanThesePages: UNSAFE PROC [in, out: STREAM, d: Disk.Channel, firstPage, count: INT] = UNCHECKED {
pageSpace: VM.Interval;
data: LONG POINTER;
soft, hard: INT ← 0;
leftToScan: Disk.PageCount ← count;
BadList: TYPE = LIST OF RECORD[
page: Disk.PageNumber, correctable: BOOL, dontReport: BOOLFALSE];
badPages: BadList ← NIL;
errorsThisPass: BOOL;
ScanPages: UNSAFE PROC[out: STREAM, d: Disk.Channel] = UNCHECKED {
origin: Disk.PageNumber ← [firstPage];
initLabel: Disk.Label;
label: Disk.Label ← initLabel;
tildasPrinted: INT ← 0;
pagesPerTilda: INT = 1000;
errorsThisPass ← FALSE;
DO
thisFirstPage: Disk.PageNumber;
pageCount: Disk.PageCount;
IF leftToScan <= 0 THEN EXIT;
[thisFirstPage, pageCount] ← IagoOps.NextRun[d, origin];
IF pageCount <= 0 THEN EXIT;
IF pageCount > leftToScan THEN pageCount ← leftToScan;
WHILE pageCount > pagesPerTilda DO
FormatDisk.Sweep[d, read, thisFirstPage, pagesPerTilda, @label, data];
IF (tildasPrinted ← tildasPrinted + 1) MOD 10 = 0 THEN
out.PutChar['!]
ELSE out.PutChar['~];
pageCount ← pageCount - pagesPerTilda;
thisFirstPage ← [thisFirstPage + pagesPerTilda];
leftToScan ← leftToScan - pagesPerTilda;
ENDLOOP;
here, know that pageCount <= pagesPerTilda
FormatDisk.Sweep[d, read, thisFirstPage, pageCount, @label, data];
IF (tildasPrinted ← tildasPrinted + 1) MOD 10 = 0 THEN
out.PutChar['!]
ELSE out.PutChar['~];
origin ← [thisFirstPage+pageCount];
leftToScan ← leftToScan - pageCount;
ENDLOOP;
IF NOT errorsThisPass THEN out.PutRope[" .. no errors"];
};
{
ENABLE {
UNWIND => VM.Free[pageSpace];
FormatDisk.BadPage => {
[page: Disk.PageNumber, correctable: BOOL]
b: BadList;
FOR b ← badPages, b.rest UNTIL b = NIL DO
IF b.first.page = page THEN {
IF b.first.correctable
THEN {
IF NOT correctable
THEN { b.first.correctable ← FALSE; soft ← soft-1; hard ← hard+1 };
};
EXIT
};
REPEAT FINISHED => {
badPages ← b ←
CONS[first: [page: page, correctable: correctable], rest: badPages];
IF correctable THEN soft ← soft+1 ELSE hard ← hard+1;
}
ENDLOOP;
IF hard > 100 THEN {
out.PutRope["\nThere are over 100 hard disk errors. Continue? "];
IF NOT IagoOps.Confirm[in, out] THEN CONTINUE;
hard ← 0;
};
IF NOT b.first.dontReport THEN {
logicalDiskCyl: CARDINAL;
physicalDiskCyl, physicalDiskTrack, physicalDiskSector: CARDINAL;
cylinder, track, sector, temp: CARDINAL;
device: DiskFace.DeviceHandle ← Disk.GetDeviceFromChannel[d];
IO.PutRope[out, IF errorsThisPass THEN ", " ELSE "\nDisk error(s): "];
errorsThisPass ← TRUE;
[cylinders: physicalDiskCyl, movingHeads: physicalDiskTrack, sectorsPerTrack: physicalDiskSector] ← DiskFace.GetTrueDeviceAttributes[device];
[cylinders: logicalDiskCyl] ← DiskFace.GetDeviceAttributes[device];
IF logicalDiskCyl # physicalDiskCyl
THEN {
Abnormal cyl/track/sect numbering on a Dorado for Alto compatability
[quotient: temp, remainder: sector] ← Basics.LongDivMod[
num: page, den: physicalDiskSector ];
[quotient: track, remainder: cylinder] ← Basics.DivMod[
num: temp, den: physicalDiskCyl];
}
ELSE {
Normal cyl/track/sect numbering
[quotient: cylinder, remainder: temp] ← Basics.LongDivMod[
num: page, den: physicalDiskSector * physicalDiskTrack];
[quotient: track, remainder: sector] ← Basics.DivMod[
num: temp, den: physicalDiskSector];
};
IO.PutF[out, "%bB(%g)",
[integer[page]], [rope[IF correctable THEN "soft" ELSE "hard"]] ];
b.first.dontReport ← NOT correctable; -- suppress messages on subsequent passes
};
RESUME
};
};
[pageSpace, data] ← GetSpace[1];
out.PutChar['\n];
ScanPages[out, d];
Record any bad pages
{
p: PhysicalVolume.Physical ← NIL;
rc: PhysicalVolume.PhysicalRC ← ok;
IF badPages # NIL THEN {
out.PutRope["Do you want the bad pages recorded in the Bad Page Table?"];
IF IagoOps.Confirm[in, out] THEN {
FOR p ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] DO
IF p = NIL OR PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT;
ENDLOOP;
IF p = NIL THEN {
IO.PutRope[out, "\nCreating a physical volume to hold the bad page table ..."];
p ← FileBackdoor.CreatePhysicalVolume[
where: d, name: "JustForTheBadPageTable", id: IagoOps.NewID[]];
IO.PutRope[out, "done"];
};
IO.PutRope[out, "\nRecording bad pages in physical bad page table ... "];
FOR b: BadList ← badPages, b.rest UNTIL b = NIL OR rc # ok DO
full: BOOLFALSE;
IF NOT b.first.correctable THEN {
[full, rc] ← FileBackdoor.PhysicalPageBad[p, b.first.page];
IF full THEN {
IO.PutRope[out, "\nOops, bad page table full. Aborting."];
GO TO tableFull;
};
};
ENDLOOP;
IF rc = ok THEN IO.PutRope[out, " done" ] ELSE ERROR File.Error[rc];
EXITS tableFull => {};
};
};
};
FreeSpace[pageSpace];
};
};
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;
};
END.