DIRECTORY
Basics USING[ BITNOT, LowHalf ],
BasicTime USING[ FromPupTime, GetClockPulses, GMT, TimeNotKnown ],
Booting USING[ Boot, Switches, switches ],
DebuggerSwap USING [EnableSwatWatcher],
Disk USING [Channel, DriveAttributes, Label, NextChannel, PageCount, PageNumber],
File USING [EraseVolume, Error, FindVolumeFromName, FindVM, FP, GetRoot, GetVolumeID, GetVolumeName, GetVolumePages, Handle, Info, IsDebugger, LogicalInfo, MarkDebugger, NextVolume, nullFP, Open, RC, SetRoot, SetSize, SystemVolume, Volume, VolumeID, VolumeFile, wordsPerPage],
FileExtra USING [CreateLogicalVolume, CreatePhysicalVolume, PhysicalPageBad],
FormatDisk USING [BadPage, IdentifyInitialMicrocode, InstallInitialMicrocode, MicrocodeInstallFailure, Operation, Sweep],
FS USING[ Close, ComponentPositions, Copy, Create, Delete, EnumerateForInfo, EnumerateForNames, Error, ExpandName, GetInfo, InfoProc, NameProc, Open, OpenFile, Read, Rename, SetKeep, SetDefaultWDir ],
FSBackdoor USING[ CloseVolume, EnumerateCacheForNames, Flush, FNameFromHandle, GetFileHandle, NameProc, ScavengeDirectoryAndCache ],
GermSwap USING[ bootedFrom, Switch ],
IagoOps --USING everything--,
IO USING[ PutChar, PutF, PutRope, STREAM ],
PhysicalVolume USING[ GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SetPhysicalRoot, SubVolumes ],
ProcessorFace USING[ processorID ],
PupDefs USING[ GetMyName ],
Rope USING [ Cat, Equal, Fetch, Find, Index, Length, ROPE, Run, Substr ],
SimpleTerminal USING [TurnOff, TurnOn],
SystemVersion USING[ machineType, uCodeCedar, uCodeDate, uCodeFloatingPoint, uCodeVersion ],
UserCredentials USING [ChangeState, Login, LoginOptions],
VM USING[ AddressForPageNumber, Allocate, Free, Interval, PagesForWords, Pin],
VMSideDoor USING [rmPages];
Attach:
PROC[in, out:
IO.
STREAM] =
BEGIN
from: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "From ",
check: TRUE];
to: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "To "];
out.PutRope[" ... "];
FS.Copy[from: from, to: to, remoteCheck: FALSE, attach: TRUE];
out.PutRope["done"];
END;
BootLogical:
PROC[in, out:
IO.
STREAM] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out];
reject: Rope.ROPE = Booting.Boot[[logical[v]], IagoOps.GetSwitches[in, out]];
IF reject # NIL THEN { out.PutRope[" ... couldn't: "]; out.PutRope[reject] };
END;
Copy:
PROC[in, out:
IO.
STREAM] =
BEGIN
from: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "From ",
check: TRUE];
to: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "To "];
out.PutRope[" ... "];
FS.Copy[from: from, to: to];
out.PutRope["done"];
END;
CreateLogical:
PROC[in, out:
IO.
STREAM] =
BEGIN
name: Rope.
ROPE ←
IF File.FindVolumeFromName[IagoOps.clientVolName] =
NIL
THEN IagoOps.clientVolName
ELSE NIL;
size: INT;
p: PhysicalVolume.Physical;
free: INT;
HelpCreation:
PROC =
BEGIN
out.PutRope["? type a string which is not the same as the name of any existing volume"];
END;
DO
name ← IagoOps.GetArg[
in: in,
out: out,
prompt: Rope.Cat["\nNew volume name: "],
default: name,
help: HelpCreation];
IF File.FindVolumeFromName[name] # NIL
THEN out.PutRope[" ... already exists"]
ELSE EXIT
ENDLOOP;
p ← IagoOps.GetPhysical[in, out];
IagoOps.ReservePages[in, out, p];
free ← PhysicalVolume.PhysicalInfo[p].free;
size ← IagoOps.GetSize[in: in, out: out, default: free, max: free];
out.PutRope["\nAre you sure? "];
IF IagoOps.Confirm[in, out]
THEN
BEGIN
new: File.Volume ← NIL;
out.PutRope[" ... "];
new ← FileExtra.CreateLogicalVolume[
id: IagoOps.NewID[], where: LIST[p], size: size, name: name];
out.PutRope["done"];
IagoOps.clientVolName ← name;
out.PutRope["\nDo you want to use this as the system volume for a disk world-swap debugger (\"CoCedar\")? "];
IF IagoOps.Confirm[in, out] THEN File.MarkDebugger[new, TRUE];
END;
END;
CloseFSVolumes:
PROC =
BEGIN
FOR v: File.Volume ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL
DO FSBackdoor.CloseVolume[v] ENDLOOP;
END;
CreatePhysical:
PROC[in, out:
IO.
STREAM]
RETURNS[done:
BOOL ←
FALSE] =
BEGIN
HelpCreation:
PROC =
BEGIN
out.PutRope["? type a string which is not the same as the name of any existing volume"];
END;
d: Disk.Channel = IagoOps.GetDrive[in, out];
alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND IagoOps.CheckAltoRegions[in, out];
name: Rope.
ROPE = IagoOps.GetArg[
in: in,
out: out,
prompt: Rope.Cat["\nNew volume name: "],
default: PupDefs.GetMyName[],
help: HelpCreation];
IF IagoOps.ConfirmDestruction[in, out, Rope.Cat[
"the entire disk drive",
IF alto THEN " (excluding Alto regions)" ELSE NIL ] ]
THEN
BEGIN
new: PhysicalVolume.Physical;
CloseFSVolumes[];
new ← FileExtra.CreatePhysicalVolume[where: d, name: name, id: IagoOps.NewID[]];
IagoOps.ReservePages[in, out, new];
done ← TRUE;
InstallCredentials[in, out];
DebuggerSwap.EnableSwatWatcher[TRUE];
END;
END;
CreateUserWorld:
PROC[in, out:
IO.
STREAM] =
BEGIN
Install:
PROC[where: File.Volume, which: File.VolumeFile[microcode..bootFile]] =
BEGIN
wDir: Rope.ROPE = Rope.Cat["[]<", File.GetVolumeName[where], ">"];
remote: Rope.ROPE = IagoOps.RemoteRootFileName[which];
local: Rope.ROPE = IagoOps.LocalRootFileName[which];
fsFile: FS.OpenFile;
file: File.Handle;
out.PutF["\nInstalling %g as %g%g ... ",
[rope[remote]], [rope[wDir]], [rope[local]] ];
FS.Copy[from: remote, to: local, wDir: wDir];
fsFile ← FS.Open[name: local, lock: write, wDir: wDir];
file ← FSBackdoor.GetFileHandle[fsFile];
File.SetRoot[which, file];
FS.Close[fsFile];
out.PutRope["done"];
END;
format, coCedar, protect: BOOL;
altoText: Rope.ROPE ← NIL;
myName: Rope.ROPE = PupDefs.GetMyName[];
d: Disk.Channel = Disk.NextChannel[NIL];
newP: PhysicalVolume.Physical;
newC, newD: File.Volume;
out.PutChar['\n];
IagoOps.ReserveAltoRegions[in, out];
IF IagoOps.CheckAltoRegions[in, out]
THEN altoText ← " (excluding the Alto disk regions)";
out.PutRope["\n\nHas your disk already been formatted? (Confirm if the disk has previously been used for Cedar or Pilot, AND you have not reduced the amount of disk reserved for Alto volumes) "];
format ← NOT IagoOps.Confirm[in, out];
out.PutRope["\n\nDo you want to password-protect this disk so that only you can access it? "];
protect ← IagoOps.Confirm[in, out];
out.PutRope["\n\nDo you want to have a disk world-swap debugger (CoCedar) on this disk? "];
coCedar ← IagoOps.Confirm[in, out];
IF NOT Booting.switches[d]
THEN
BEGIN
out.PutRope["\n\nDo you want me to use software from the PreCedar directory instead of the Cedar directory? "];
IF IagoOps.Confirm[in, out] THEN Booting.switches[d] ← TRUE;
END;
out.PutRope["\n\nI intend to perform the following operations:"];
IF format
THEN
BEGIN
out.PutRope["\n - format the entire disk"];
out.PutRope[altoText];
out.PutChar[';];
END;
out.PutF["\n - create a new physical volume named \"%g\";", [rope[myName]] ];
IF coCedar THEN out.PutRope["\n - create a 35000 page debugger logical volume named \"Debugger\" with a 16000 page virtual memory;"];
out.PutF["\n - create a maximum-sized logical volume named \"Cedar\" with a 16000 page virtual memory;\n - install microcode, germ and boot files(s) from the \"%g\" directory;\n - make the \"Cedar\" volume be your physical boot volume;\n - boot the volume(s) to initialize the software.\n", [rope[IF Booting.switches[d] THEN "PreCedar" ELSE "Cedar"]] ];
IF NOT IagoOps.ConfirmDestruction[in, out, Rope.Cat["the entire disk on drive RD0", altoText]]
THEN RETURN;
CloseFSVolumes[];
out.PutChar['\n];
IF format THEN TRUSTED{ DoFormatOrScan[in: in, out: out, d: d, format: TRUE, passes: 1] };
out.PutRope["\nCreating physical volume ... "];
newP ← FileExtra.CreatePhysicalVolume[where: d, name: myName, id: IagoOps.NewID[]];
IagoOps.ReservePages[in, out, newP];
[] ← UserCredentials.ChangeState[IF protect THEN name ELSE nameHint];
out.PutRope["done"];
IF coCedar
THEN
BEGIN
out.PutRope["\nCreating logical volume \"Debugger\" ... "];
newD ← FileExtra.CreateLogicalVolume[id: IagoOps.NewID[], where:
LIST[newP],
size: 35000,
name: "Debugger"];
File.MarkDebugger[newD, TRUE];
out.PutRope["done. Erasing it ... "];
File.EraseVolume[newD];
out.PutRope["done"];
CreateVMFile[out, newD, 16000];
Install[where: newD, which: bootFile];
END;
out.PutRope["\nCreating logical volume \"Cedar\" ... "];
newC ← FileExtra.CreateLogicalVolume[
id: IagoOps.NewID[],
where: LIST[newP],
size: PhysicalVolume.PhysicalInfo[newP].free,
name: "Cedar"];
out.PutRope["done. Erasing it ... "];
File.EraseVolume[newC];
out.PutRope["done"];
CreateVMFile[out, newC, 16000];
Install[where: newC, which: microcode];
Install[where: newC, which: germ];
Install[where: newC, which: bootFile];
PhysicalVolume.SetPhysicalRoot[newC, microcode];
PhysicalVolume.SetPhysicalRoot[newC, germ];
PhysicalVolume.SetPhysicalRoot[newC, bootFile];
BEGIN
init: Rope.ROPE = IagoOps.InitialMicrocodeFileName[];
out.PutRope["\nInstalling "]; out.PutRope[init];
IF DoInstallation[out, d, init]
THEN [] ←
Booting.Boot[[logical[IF coCedar THEN newD ELSE newC]], [d: Booting.switches[d]] ];
END;
END;
CreateVM:
PROC[in, out:
IO.
STREAM] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out, "On "];
size: INT = IagoOps.GetSize[in: in, out: out, default: 16000, max: File.GetVolumePages[v].size];
oldFP: File.FP = File.GetRoot[v, VM].fp;
oldFile: File.Handle ← NIL;
IF oldFP # File.nullFP
THEN oldFile ← File.Open[v, oldFP ! File.Error =>
IF why = unknownFile THEN CONTINUE];
IF oldFile # NIL
THEN
BEGIN
out.PutF["\nThat volume already has a VM backing file of size %g. Do you want me to set its size to %g? ", [integer[File.Info[oldFile].size]], [integer[size]] ];
IF IagoOps.Confirm[in, out]
THEN { out.PutRope[" ... "]; File.SetSize[oldFile, size]; out.PutRope["done"] };
END
ELSE CreateVMFile[out, v, size]; -- Create the file ourselves
END;
CreateVMFile:
PROC[out:
IO.
STREAM, v: File.Volume, size:
INT] =
BEGIN
newFile: FS.OpenFile;
vmName: Rope.
ROPE =
Rope.Cat["[]<", File.GetVolumeName[v], ">", IagoOps.LocalRootFileName[VM]];
out.PutF["\nCreating %g ... ", [rope[vmName]] ];
newFile ← FS.Create[name: vmName, pages: size];
File.SetRoot[VM, FSBackdoor.GetFileHandle[newFile]];
FS.Close[newFile];
out.PutRope["done"];
END;
Delete:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
DoName:
FS.NameProc =
BEGIN
count ← count+1;
out.PutF["\nDeleting %g ... ", [rope[fullFName]]];
BEGIN
ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE };
FS.Delete[fullFName];
out.PutRope["done"];
END;
RETURN[TRUE];
END;
pattern: Rope.
ROPE ← IagoOps.GetFile[
in: in,
out: out,
pattern: TRUE];
IF Rope.Find[pattern, "!"] < 0 THEN pattern ← pattern.Cat["!L"];
out.PutRope[" ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
DescribeDrives:
PROC[in, out:
IO.
STREAM] =
BEGIN
FOR c: Disk.Channel ← Disk.NextChannel[NIL], Disk.NextChannel[c] UNTIL c = NIL
DO ordinal:
INT;
nPages: INT;
[ordinal: ordinal, nPages: nPages] ← Disk.DriveAttributes[c];
out.PutF["\nRD%g, size: %g pages",
[integer[ordinal]],
[integer[nPages]],
];
ENDLOOP;
END;
DescribeLV:
PROC[in, out:
IO.
STREAM] =
BEGIN
FOR v: File.Volume ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL
DO
BEGIN
ENABLE File.Error => out.PutRope["\nUnexpected File.Error"]--and let it propagate--;
id: File.VolumeID;
rootStatus, vamStatus: File.RC;
name: Rope.ROPE;
size, free, freeboard: INT;
[id: id, size: size, rootStatus: rootStatus, name: name, vamStatus: vamStatus, free: free, freeboard: freeboard] ← File.LogicalInfo[v];
out.PutRope["\n\nID: "]; IagoOps.PutID[out, File.GetVolumeID[v]];
IF v = File.SystemVolume[] THEN out.PutRope[" (the system volume)"];
SELECT rootStatus
FROM
ok, nonCedarVolume => NULL;
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", [rope[name]] ];
IF File.IsDebugger[v] THEN out.PutRope["\nThis is a debugger volume"];
out.PutF["\nSize: %g", [integer[size]] ];
IF rootStatus = nonCedarVolume THEN { out.PutRope["\nNot a Cedar volume"]; LOOP };
SELECT vamStatus
FROM
ok => out.PutF[", free pages: %g", [integer[free]] ];
wentOffline => out.PutRope["\nVolume went offline during VAM operation"];
inconsistent => out.PutRope["\nDon't understand the VAM"];
software => out.PutRope["\nCan't find the VAM (label-check)"];
hardware => out.PutRope["\nHard disk error reading the VAM"];
unknownFile => out.PutRope["\nCan't find the VAM (unknown-file)"];
unknownPage => out.PutRope["\nCan't find the VAM (unknown-page)"];
ENDCASE => ERROR File.Error[vamStatus];
FOR vf: File.VolumeFile IN File.VolumeFile
DO PutVolumeFile[out, v, vf] ENDLOOP;
END;
ENDLOOP;
END;
PutVolumeFile:
PROC[out:
IO.
STREAM, v: File.Volume, vf: File.VolumeFile] =
BEGIN
fp: File.FP ← File.GetRoot[v, vf].fp;
file: File.Handle;
name: Rope.ROPE;
IF fp = File.nullFP THEN RETURN;
out.PutF["\n%g fp: [id:%b,da:%b]",
[rope[
SELECT vf
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"]],
[cardinal[LOOPHOLE[fp.id]]],
[cardinal[LOOPHOLE[fp.da]]]
];
file ← File.Open[v, fp ! File.Error =>
{ out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; file ← NIL; CONTINUE } ];
IF file # NIL
THEN
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;
out.PutF[", size: %g", [integer[File.Info[file].size]] ];
name ← FSBackdoor.FNameFromHandle[file];
out.PutRope[", name: "]; out.PutRope[name];
END;
END;
DescribeMachine:
PROC[in, out:
IO.
STREAM] =
BEGIN
id: RECORD[a,b,c: CARDINAL] = LOOPHOLE[ProcessorFace.processorID];
out.PutF["\nThis processor is a %g, id = [%x,%x,%x], available real memory: %g pages",
[rope[
SELECT SystemVersion.machineType
FROM
dolphin => "Dolphin",
dorado => "Dorado",
dandelion => "Dandelion",
dicentra => "Dicentra",
ENDCASE => "{unknown processor type!}"]],
[integer[id.a]],
[integer[id.b]],
[integer[id.c]],
[integer[VMSideDoor.rmPages]] ];
out.PutF["\nThe microcode is version %g of %g.",
[integer[SystemVersion.uCodeVersion]],
[time[BasicTime.FromPupTime[SystemVersion.uCodeDate]]] ];
IF NOT SystemVersion.uCodeCedar
THEN out.PutRope[" No Cedar microcode."];
IF NOT SystemVersion.uCodeFloatingPoint
THEN out.PutRope[" No floating-point microcode."];
END;
DescribePV:
PROC[in, out:
IO.
STREAM] =
BEGIN
FOR p: PhysicalVolume.Physical ← PhysicalVolume.NextPhysical[
NIL],
PhysicalVolume.NextPhysical[p] UNTIL p = NIL
DO IagoOps.ReservePages[in, out, p] ENDLOOP;
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;
[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];
TRUSTED
BEGIN
microcodeInstalled: BOOL;
time: BasicTime.GMT;
name: Rope.ROPE;
[microcodeInstalled, time, name] ← FormatDisk.IdentifyInitialMicrocode[channel];
IF microcodeInstalled
THEN
BEGIN
out.PutF["\nContains initial microcode %g of %g",
[rope[name]], [time[time]] ];
END;
END;
out.PutF["\nName: %g\nSize: %g, free pages: %g\nLogical sub-volumes:",
[rope[name]],
[integer[size]],
[integer[free]]
];
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]]
];
ENDLOOP;
END;
ENDLOOP;
END;
EraseLogical:
PROC[in, out:
IO.
STREAM] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out];
IF IagoOps.ConfirmDestruction[in, out, "the entire logical volume"]
THEN
BEGIN
out.PutRope["\nErasing ... "];
File.EraseVolume[v];
out.PutRope["done"];
FSBackdoor.CloseVolume[v];
END;
END;
FormatOrScan:
UNSAFE PROC[in, out:
IO.
STREAM, format:
BOOL] =
UNCHECKED
BEGIN
d: Disk.Channel = IagoOps.GetDrive[in, out];
alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND IagoOps.CheckAltoRegions[in, out];
passes:
INT = IagoOps.GetNumber[in, out, 10, 999,
"\nHow many check passes should I make? ",
"? Type how often I should make transfers over the entire disk trying to find bad spots"];
IF NOT format
OR IagoOps.ConfirmDestruction[in, out, Rope.Cat[
"the entire disk drive",
IF alto THEN " (excluding Alto regions)" ELSE NIL ] ]
THEN DoFormatOrScan[in, out, d, format, passes];
END;
DoFormatOrScan:
UNSAFE
PROC[in, out:
IO.
STREAM, d: Disk.Channel, format:
BOOL, passes:
INT] =
UNCHECKED
BEGIN
initLabel: Disk.Label;
pageSpace: VM.Interval = VM.Allocate[VM.PagesForWords[File.wordsPerPage]];
data: LONG POINTER = VM.AddressForPageNumber[pageSpace.page];
soft, hard: INT ← 0;
BadList:
TYPE =
LIST
OF
RECORD[
page: Disk.PageNumber, correctable: BOOLEAN, reported: BOOL ← FALSE];
badPages: BadList ← NIL;
SweepDisk:
UNSAFE
PROC[out:
IO.
STREAM, d: Disk.Channel, operation: FormatDisk.Operation] =
UNCHECKED
BEGIN
origin: Disk.PageNumber ← [0];
label: Disk.Label ← initLabel;
correctableOnes: BOOL ← FALSE;
out.PutRope[
SELECT operation
FROM
format => "\nFormatting ... ",
write => "\nWriting ... ",
read => "\nReading ... ",
verify => "\nVerifying ... "
ENDCASE => ERROR];
DO firstPage: Disk.PageNumber; pageCount: Disk.PageCount;
[firstPage, pageCount] ← IagoOps.NextRun[d, origin];
IF pageCount < 0 THEN EXIT;
FormatDisk.Sweep[d, operation, firstPage, pageCount, @label, data];
origin ← [firstPage+pageCount];
ENDLOOP;
DO someBad:
BOOL ←
FALSE;
FOR b: BadList ← badPages, b.rest UNTIL b = NIL
DO
IF (b.first.correctable = correctableOnes)
AND
NOT b.first.reported
THEN
BEGIN
out.PutF[
IF someBad
THEN ", %b"
ELSE
IF correctableOnes
THEN "\nSoft errors: %b"
ELSE "\nHard errors: %b",
[integer[b.first.page]] ];
someBad ← TRUE;
b.first.reported ← TRUE;
END;
ENDLOOP;
IF NOT correctableOnes AND NOT someBad THEN out.PutRope["no hard errors"];
IF correctableOnes THEN EXIT ELSE correctableOnes ← TRUE;
ENDLOOP;
END;
IF format THEN CloseFSVolumes[];
BEGIN
ENABLE
BEGIN
UNWIND => VM.Free[pageSpace];
FormatDisk.BadPage
--[page: Disk.PageNumber, correctable: BOOLEAN]-- =>
BEGIN
FOR b: BadList ← badPages, b.rest UNTIL b = NIL
DO
IF b.first.page = page
THEN
BEGIN
IF b.first.correctable
THEN
BEGIN
b.first.reported ← FALSE;
IF NOT correctable
THEN { b.first.correctable ← FALSE; soft ← soft-1; hard ← hard+1 };
END;
RESUME;
END;
ENDLOOP;
badPages ← CONS[first: [page: page, correctable: correctable], rest: badPages];
IF correctable THEN soft ← soft+1 ELSE hard ← hard+1;
IF hard > 100
THEN
BEGIN
out.PutRope["\nThere have been more than 100 hard disk errors. Do you want me to continue? "];
IF NOT IagoOps.Confirm[in, out] THEN CONTINUE;
hard ← 0;
END;
RESUME
END;
END;
VM.Pin[pageSpace];
IF format
THEN
BEGIN
init: BOOL ← TRUE;
Randomize:
UNSAFE
PROC =
UNCHECKED
BEGIN
RandomizeOne[@initLabel, SIZE[Disk.Label]];
RandomizeOne[data, VM.PagesForWords[File.wordsPerPage]];
init ← NOT init;
END;
RandomizeOne:
UNSAFE
PROC[where:
LONG
POINTER, nwords:
CARDINAL] =
UNCHECKED
BEGIN
array: LONG POINTER TO ARRAY OF CARDINAL = LOOPHOLE[where];
FOR i: CARDINAL IN [0..nwords)
DO array[i] ←
IF init
THEN Basics.LowHalf[BasicTime.GetClockPulses[]]
ELSE Basics.BITNOT[array[i]];
ENDLOOP;
END;
Randomize[];
SweepDisk[out, d, format];
SweepDisk[out, d, verify];
THROUGH [1..passes]
DO
-- First, set label and data to random values
Randomize[];
SweepDisk[out, d, write];
SweepDisk[out, d, verify];
ENDLOOP;
END
ELSE
THROUGH [1..passes]
DO SweepDisk[out, d, read] ENDLOOP;
Record any bad pages
BEGIN
p: PhysicalVolume.Physical;
FOR p ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p]
UNTIL p = NIL
DO IF PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT ENDLOOP;
IF p = NIL
THEN
BEGIN
out.PutRope["\nCreating a physical volume to hold the bad page table ..."];
p ← FileExtra.CreatePhysicalVolume[
where: d, name: "JustForTheBadPageTable", id: IagoOps.NewID[]];
out.PutRope["done"];
END;
IF badPages # NIL
THEN
BEGIN
rc: PhysicalVolume.PhysicalRC ← ok;
out.PutRope["\nRecording bad pages in physical bad page table ... "];
FOR b: BadList ← badPages, b.rest UNTIL b = NIL OR rc # ok
DO IF NOT b.first.correctable THEN rc ← FileExtra.PhysicalPageBad[p, b.first.page];
ENDLOOP;
out.PutRope[IF rc = ok THEN "done" ELSE IagoOps.FileError[rc]];
END;
END;
END;
VM.Free[pageSpace];
END;
FlushCache:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
DoName: FSBackdoor.NameProc =
BEGIN
count ← count+1;
out.PutF["\nFlushing %g ... ", [rope[fullGName]]];
BEGIN
ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE };
FSBackdoor.Flush[fullGName: fullGName, volName: volName];
out.PutRope["done"];
END;
RETURN[TRUE];
END;
v: File.Volume = IagoOps.GetLogical[in, out, "On "];
volName: Rope.ROPE = File.GetVolumeName[v];
pattern: Rope.ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
out.PutRope[" ... "];
FSBackdoor.EnumerateCacheForNames[DoName, volName, pattern];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
InstallCredentials:
PROC[in, out:
IO.
STREAM] =
BEGIN
out.PutRope["\nDo you want to password-protect this disk so that only you can access it? "];
[] ← UserCredentials.ChangeState[IF IagoOps.Confirm[in, out] THEN name ELSE nameHint];
END;
InstallInitial:
PROC[in, out:
IO.
STREAM] =
BEGIN
d: Disk.Channel = IagoOps.GetDrive[in, out];
name: Rope.
ROPE = IagoOps.GetFile[
in: in, out: out,
extension: ".eb",
default: IagoOps.InitialMicrocodeFileName[],
check: TRUE
];
out.PutRope[" ... installing"];
[] ← DoInstallation[out, d, name];
END;
DoInstallation:
PROC[out:
IO.
STREAM, d: Disk.Channel, name: Rope.
ROPE]
RETURNS[done:
BOOL ←
TRUE] =
BEGIN
dest: Rope.ROPE = "InitialMicrocode.eb";
wDir: Rope.ROPE ← NIL;
fsFile: FS.OpenFile;
pages: INT ← 0;
v: File.Volume;
FOR v ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL
DO
IF File.LogicalInfo[v].vamStatus = ok
AND File.GetVolumePages[v].free >= 100
THEN EXIT;
REPEAT
FINISHED =>
BEGIN
out.PutRope[" ... I can't find a suitable logical volume for buffering the file. Create one then try again"];
RETURN
END
ENDLOOP;
wDir ← Rope.Cat["[]<", File.GetVolumeName[v], ">"];
out.PutF[" as %g%g ... ", [rope[wDir]], [rope[dest]] ];
FS.Copy[from: name, to: dest, wDir: wDir];
fsFile ← FS.Open[name: dest, lock: read, wDir: wDir];
pages ← FS.GetInfo[fsFile].pages;
TRUSTED
BEGIN
pos: INT ← 0;
Reader:
UNSAFE
PROC[ ptr:
LONG
POINTER]
RETURNS [ok:
BOOL ←
TRUE] =
BEGIN
IF pages <= 0 THEN RETURN[FALSE];
FS.Read[file: fsFile, from: pos, nPages: 1, to: ptr];
pages ← pages-1; pos ← pos+1;
END;
FormatDisk.InstallInitialMicrocode[d, Reader ! FormatDisk.MicrocodeInstallFailure =>
BEGIN
out.PutRope[
SELECT why
FROM
emptyFile => "empty microcode file",
firstPageBad => "first page of microcode area of disk is bad",
flakeyPageFound => "bad page in microcode area of disk",
microcodeTooBig => "microcode too big for fixed area of disk",
other => "other error during installation",
ENDCASE => "unknown error during installation"];
done ← FALSE;
CONTINUE
END ];
END;
FS.Close[fsFile];
IF done THEN out.PutRope["done"];
END;
InstallLogicalFile:
PROC[in, out:
IO.
STREAM, which: File.VolumeFile[checkpoint..bootFile]] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out, "For "];
localVolume: Rope.ROPE = Rope.Cat["[]<", File.GetVolumeName[v], ">"];
name: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
extension: IagoOps.ext[which],
default: IagoOps.RemoteRootFileName[which],
wDir: localVolume,
check: TRUE
];
fsFile: FS.OpenFile;
file: File.Handle;
PhysicalToo:
PROC
RETURNS[
BOOL] =
BEGIN
IF which # bootFile
THEN { out.PutRope["\nInstalling on the physical volume"]; RETURN[TRUE] };
IF File.IsDebugger[v] THEN RETURN[FALSE];
out.PutRope["\nDo you want to use this file when you boot the physical volume? "];
RETURN[IagoOps.Confirm[in, out]]
END;
"name" is a canonicalized FS name.
out.PutRope[" ... "];
IF name.Length[] # 0
AND name.Fetch[0] = '[
AND Rope.Run[s1: name, s2: localVolume, case: FALSE] # localVolume.Length[]
AND ( v # File.SystemVolume[] OR Rope.Run[s1: name, s2: "[]<>"] # 4 )
THEN
BEGIN
localName: Rope.ROPE ← IagoOps.LocalRootFileName[which];
Help:
PROC =
{ out.PutRope["? Please type the name of an FS local file such as \"a.b\""] };
out.PutRope[
IF name.Length[] >=1
AND name.Fetch[1] = ']
THEN "that file is on another volume ..."
ELSE "that file is on a server ..."];
See what local name the user would like
DO
localName ← IagoOps.GetArg[
in: in,
out: out,
prompt: "\nCopy to local file name (please confirm or alter): ",
default: localName,
help: Help];
IF localName.Length[] = 0 OR localName.Fetch[0] = '[ THEN Help[] ELSE EXIT;
ENDLOOP;
out.PutRope[" ... copying ... "];
FS.Copy[from: name, to: localName, wDir: localVolume];
fsFile ← FS.Open[name: localName, lock: write, wDir: localVolume];
END
ELSE fsFile ← FS.Open[name: name, lock: write, wDir: localVolume];
file ← FSBackdoor.GetFileHandle[fsFile];
IF File.Info[file].volume # v
THEN out.PutRope["I'm confused: the file is on the wrong volume"]
ELSE { out.PutRope["installing ... "]; File.SetRoot[which, file]; out.PutRope["done"] };
FS.Close[fsFile];
IF PhysicalToo[]
THEN { PhysicalVolume.SetPhysicalRoot[v, which]; out.PutRope[" ... done"] };
END;
ListCache:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
DoName: FSBackdoor.NameProc =
BEGIN
count ← count+1;
out.PutF["\n %g", [rope[fullGName]]];
RETURN[TRUE];
END;
v: File.Volume = IagoOps.GetLogical[in, out, "On "];
pattern: Rope.ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
out.PutRope[" ... "];
FSBackdoor.EnumerateCacheForNames[DoName, File.GetVolumeName[v], pattern];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
ListFileInfo:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
DoName:
FS.InfoProc =
BEGIN
count ← count+1;
out.PutF["\n %g, keep: %g, bytes: %g, created: %g", [rope[fullFName]], [cardinal[keep]], [integer[bytes]], [time[created]] ];
IF attachedTo # NIL THEN out.PutF[", attached to %g", [rope[attachedTo]] ];
RETURN[TRUE];
END;
pattern: Rope.ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
out.PutRope[" ... "];
FS.EnumerateForInfo[pattern, DoName];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
ListNames:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
prevFile: Rope.ROPE ← NIL;
DoName:
FS.NameProc =
BEGIN
newFile, newPrefix: Rope.ROPE;
newPos: FS.ComponentPositions;
count ← count+1;
[fullFName: newFile, cp: newPos] ← FS.ExpandName[fullFName];
newPrefix ← fullFName.Substr[len: newPos.ver.start];
IF prevFile # NIL AND newPos.ver.length # 0 AND prevFile.Equal[newPrefix, FALSE]
THEN out.PutF[", %g",
[rope[fullFName.Substr[start: newPos.ver.start, len: newPos.ver.length]]] ]
ELSE
BEGIN
prevFile ← newPrefix;
out.PutF["\n%g", [rope[fullFName]]];
END;
RETURN[TRUE];
END;
pattern: Rope.ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
out.PutRope[" ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
Login:
PROC[options: UserCredentials.LoginOptions] =
BEGIN
TurnOnProc:
PROC
RETURNS [in, out:
IO.
STREAM] = {
[in: in, out: out] ← SimpleTerminal.TurnOn[];
out.PutRope["\nPlease login ...\n"];
};
TurnOffProc:
PROC [in, out:
IO.
STREAM] = {
SimpleTerminal.TurnOff[]
};
UserCredentials.Login[startInteraction: TurnOnProc, endInteraction: TurnOffProc,
options: options];
END;
Rename:
PROC[in, out:
IO.
STREAM] =
BEGIN
from: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "From ",
check: TRUE];
to: Rope.
ROPE = IagoOps.GetFile[
in: in,
out: out,
prompt: "To "];
out.PutRope[" ... "];
FS.Rename[from: from, to: to];
out.PutRope["done"];
END;
Rollback:
PROC[in, out:
IO.
STREAM] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out];
reject: Rope.ROPE;
mySwitches: Booting.Switches ← ALL[FALSE];
mySwitches[r] ← TRUE;
reject ← Booting.Boot[[logical[v]], mySwitches];
IF reject # NIL THEN { out.PutRope[" ... couldn't: "]; out.PutRope[reject] };
END;
Scavenge:
PROC[in, out:
IO.
STREAM] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out];
out.PutRope[" ... "];
[] ← File.FindVM[];
FSBackdoor.ScavengeDirectoryAndCache[File.GetVolumeName[v]];
out.PutRope["done"];
END;
SetKeep:
PROC[in, out:
IO.
STREAM] =
BEGIN
count: INT ← 0;
DoName:
FS.NameProc =
BEGIN
count ← count+1;
fullFName ← fullFName.Substr[start: 0, len: fullFName.Index[0, "!"]];
out.PutF["\nSetting %g ... ", [rope[fullFName]]];
BEGIN
ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE };
FS.SetKeep[fullFName, k];
out.PutRope["done"];
END;
RETURN[TRUE];
END;
pattern: Rope.
ROPE ← IagoOps.GetFile[
in: in,
out: out,
pattern: TRUE,
prompt: "For "];
k:
INT = IagoOps.GetNumber[
in,
out,
1,
LAST[CARDINAL]-1,
"\nNumber of versions to keep: ",
"? Type how many versions of each file should be kept (excess versions will be deleted)"];
IF Rope.Find[pattern, "!"] < 0 THEN pattern ← pattern.Cat["!H"];
out.PutRope[" ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN out.PutRope["not found"]
ELSE out.PutF["\n%g files", [integer[count]] ];
END;
SetPhysicalFile:
PROC[in, out:
IO.
STREAM, which: File.VolumeFile[checkpoint..bootFile]] =
BEGIN
v: File.Volume = IagoOps.GetLogical[in, out, "From "];
out.PutRope[" ... "];
PhysicalVolume.SetPhysicalRoot[v, which];
out.PutRope["done"];
END;
SetWDir:
PROC[in, out:
IO.
STREAM] =
BEGIN
Help:
PROC =
{ out.PutRope["? Type the prefix of a file name, such as \"[]<volume>\""] };
name: Rope.
ROPE = IagoOps.GetArg[
in: in,
out: out,
prompt: "\nWorking directory: ",
default:
IF IagoOps.clientVolName.Length[] # 0
THEN Rope.Cat["[]<", IagoOps.clientVolName, ">"]
ELSE NIL,
help: Help];
FS.SetDefaultWDir[name];
END;