IagoCommands2Impl.mesa - Iago commands implementation
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Andrew Birrell December 7, 1983 4:07 pm
Levin, September 22, 1983 1:32 pm
Willie-Sue, May 10, 1985 4:34:28 pm PDT
Bob Hagmann October 14, 1985 1:01:12 pm PDT
Russ Atkinson (RRA) July 22, 1985 7:16:05 pm PDT
Carl Hauser, November 18, 1985 5:42:30 pm PST
DIRECTORY
BasicTime USING [ GMT, nullGMT ],
Booting USING [ Boot, Switches ],
DefaultRemoteNames USING [Get],
Disk USING [ok],
File USING [Delete, Error, FindVM, FP, GetVolumeName, Handle, Info, LogicalInfo, nullFP, Open, RC, SystemVolume, Volume, VolumeFile],
FileBackdoor USING [GetRoot, IsDebugger, SetRoot],
FileInternal USING [EnumeratePages, EnumeratePagesProc, FreeRun, GetBadPages, IsUsed, SetPageUsed, VolumeObject],
FS USING [BytesForPages, Close, ComponentPositions, Copy, EnumerateForInfo, EnumerateForNames, Error, ExpandName, FileInfo, InfoProc, Lock, NameProc, Open, OpenFile, Rename, SetByteCountAndCreatedTime, SetKeep, SetDefaultWDir ],
FSBackdoor USING [CreateFileProcs, CreateProcsOpenFile, EnumerateCacheForNames, Flush, GetFileHandle, NameProc, ScavengeDirectoryAndCache ],
FSRemoteFile USING [ Retrieve ],
GermSwap USING [ Switch ],
IagoCommands,
IagoOps --USING everything--,
IO USING [ Close, Error, PutChar, PutF, PutF1, PutRope, RIS, RopeFromROS, ROS, SetIndex, STREAM, UnsafeGetBlock ],
Loader USING [ Error, IRItem, Instantiate, Start],
PhysicalVolume USING [ SetPhysicalRoot, SubVolumeDetails ],
PrincOps USING [ ControlModule, NullControl],
Rope USING [ Cat, Equal, Fetch, Find, Index, Length, ROPE, Run, Substr ],
SimpleTerminal USING [TurnOff, TurnOn],
UserCredentials USING [ChangeState, Login, LoginOptions],
VM USING [PagesForBytes],
VolumeFormat USING [Attributes, LogicalPage];
IagoCommands2Impl: CEDAR PROGRAM
IMPORTS Booting, DefaultRemoteNames, File, FileBackdoor, FileInternal, FS, FSBackdoor, FSRemoteFile, IagoOps, IO, Loader, PhysicalVolume, Rope, SimpleTerminal, UserCredentials, VM
EXPORTS IagoCommands, File
SHARES File = {
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
myOpenFileRec: TYPE = RECORD [
strm: STREAM,
created: BasicTime.GMT,
bytes: INT,
pages: INT
];
myOpenFile: TYPE = REF myOpenFileRec;
Volume: TYPE = REF VolumeObject;
-- File -- VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject;
******** The command subroutines (alphabetic order) ********
FlushCache: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
DoName: FSBackdoor.NameProc = {
count ← count+1;
IO.PutF1[out, "\nFlushing %g ... ", [rope[fullGName]]];
{
ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE };
FSBackdoor.Flush[fullGName: fullGName, volName: volName];
IO.PutRope[out, "done"];
};
RETURN[TRUE];
};
v: File.Volume = IagoOps.GetLogical[in, out, "On "];
volName: ROPE = File.GetVolumeName[v];
pattern: ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
IO.PutRope[out, " ... "];
FSBackdoor.EnumerateCacheForNames[DoName, volName, pattern];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
InstallCredentials: PUBLIC PROC [in, out: STREAM] = {
IO.PutRope[out,
"\nDo you want to password-protect this disk? "];
[] ← UserCredentials.ChangeState[IF IagoOps.Confirm[in, out] THEN name ELSE nameHint];
};
InstallLogicalFile: PUBLIC PROC [in, out: STREAM, which: File.VolumeFile[checkpoint..bootFile]] = {
v: File.Volume = IagoOps.GetLogical[in, out, "For "];
localVolume: ROPE = Rope.Cat["[]<", File.GetVolumeName[v], ">"];
createTime: BasicTime.GMT;
name: ROPE = IagoOps.GetFile[
in: in, out: out, extension: IagoOps.ext[which],
default: IagoOps.RemoteRootFileName[which],
wDir: localVolume, check: TRUE
];
localName: ROPE ← name;
fsFile: FS.OpenFile;
file: File.Handle;
PhysicalToo: PROC RETURNS[BOOL] = {
IF which # bootFile THEN {
IO.PutRope[out, "\nInstalling on the physical volume"];
RETURN[TRUE];
};
IF FileBackdoor.IsDebugger[v] THEN RETURN[FALSE];
IO.PutRope[out, "\nDo you want to use this file when you boot the physical volume? "];
RETURN[IagoOps.Confirm[in, out]]
};
"name" is a canonicalized FS name.
IO.PutRope[out, " ... "];
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 {
Help: PROC = {
IO.PutRope[out, "? Please type the name of an FS local file such as \"a.b\""];
};
localName ← IagoOps.LocalRootFileName[which];
IO.PutRope[out, 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;
IO.PutRope[out, " ... copying ... "];
[] ← FS.Copy[from: name, to: localName, wDir: localVolume];
};
createTime ← FS.FileInfo[name: localName, wDir: localVolume].created;
fsFile ← FS.Open[name: localName, lock: write, wDir: localVolume];
file ← FSBackdoor.GetFileHandle[fsFile];
IF File.Info[file].volume # v
THEN IO.PutRope[out, "I'm confused: the file is on the wrong volume"]
ELSE {
IO.PutRope[out, "installing ... "];
FileBackdoor.SetRoot[which, file];
FS.SetByteCountAndCreatedTime[file: fsFile, created: createTime];
IO.PutRope[out, "done"];
};
FS.Close[fsFile];
IF PhysicalToo[] THEN {
PhysicalVolume.SetPhysicalRoot[v, which];
IO.PutRope[out, " ... done"] };
};
ListCache: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
DoName: FSBackdoor.NameProc = {
count ← count+1;
IO.PutF1[out, "\n %g", [rope[fullGName]]];
RETURN[TRUE];
};
v: File.Volume = IagoOps.GetLogical[in, out, "On "];
pattern: ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
IO.PutRope[out, " ... "];
FSBackdoor.EnumerateCacheForNames[DoName, File.GetVolumeName[v], pattern];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
ListFileInfo: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
DoName: FS.InfoProc = {
count ← count+1;
IO.PutF[out, "\n %g, keep: %g, bytes: %g, created: %g", [rope[fullFName]], [cardinal[keep]], [integer[bytes]], [time[created]] ];
IF attachedTo # NIL THEN IO.PutF1[out, ", attached to %g", [rope[attachedTo]] ];
RETURN[TRUE];
};
pattern: ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
IO.PutRope[out, " ... "];
FS.EnumerateForInfo[pattern, DoName];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
ListNames: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
prevFile: ROPENIL;
DoName: FS.NameProc = {
newFile, newPrefix: 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 IO.PutF1[out, ", %g",
[rope[fullFName.Substr[start: newPos.ver.start, len: newPos.ver.length]]] ]
ELSE {
prevFile ← newPrefix;
IO.PutF1[out, "\n%g", [rope[fullFName]]];
};
RETURN[TRUE];
};
pattern: ROPE ← IagoOps.GetFile[in: in, out: out, pattern: TRUE];
IO.PutRope[out, " ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
Login: PUBLIC PROC [options: UserCredentials.LoginOptions] = {
TurnOnProc: PROC RETURNS [in, out: STREAM] = {
[in: in, out: out] ← SimpleTerminal.TurnOn[];
IO.PutRope[out, "\nPlease login ...\n"];
};
TurnOffProc: PROC [in, out: STREAM] = {
SimpleTerminal.TurnOff[]
};
UserCredentials.Login[startInteraction: TurnOnProc, endInteraction: TurnOffProc,
options: options];
};
RecomputeVAM: PUBLIC PROC[in, out: STREAM] = {
NowPage: FileInternal.EnumeratePagesProc = TRUSTED {
PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label] RETURNS[exit: BOOLFALSE];
attr: VolumeFormat.Attributes = LOOPHOLE[label.attributes];
SELECT TRUE FROM
status # Disk.ok => { badStatus ← badStatus + 1; };
attr = header => {
IF FileInternal.IsUsed[volume: volume, page: da]
THEN headers ← headers + 1
ELSE {
freeHeaders ← freeHeaders + 1;
[] ← FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE];
};
};
attr = data => {
IF FileInternal.IsUsed[volume: volume, page: da]
THEN datas ← datas + 1
ELSE {
freeDatas← freeDatas + 1;
[] ← FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE];
};
};
attr = freePage => {
diskaddr: INT = LOOPHOLE[da];
IF label.filePage # diskaddr THEN {
badFreeLabels ← badFreeLabels+1;
IF lastBadFreeLabel+1 # diskaddr THEN {
badFreeLabelRuns ← badFreeLabelRuns+1;
IF listBadFreeLabels THEN out.PutRope["--"];
};
lastBadFreeLabel ← diskaddr;
IF listBadFreeLabels THEN out.PutF[ "(%g)", [integer[diskaddr]] ];
IF fixBadFreeLabels THEN FileInternal.FreeRun[[da,1], volume, label]
ELSE [] ← FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE];
}
ELSE IF FileInternal.IsUsed[volume: volume, page: da]
THEN {
freeMissings ← freeMissings + 1;
[] ← FileInternal.SetPageUsed[volume: volume, page: da, inUse: FALSE];
}
ELSE frees ← frees + 1;
};
ENDCASE => {
IF FileInternal.IsUsed[volume: volume, page: da]
THEN others ← others + 1
ELSE {
freeOthers← freeOthers + 1;
[] ← FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE];
};
};
deltaPages ← deltaPages + 1;
IF deltaPages >= 1000 THEN {
thousandsCount ← thousandsCount + 1;
IF thousandsCount >= 10
THEN {
IO.PutRope[out, "!"];
thousandsCount ← 0;
}
ELSE IO.PutRope[out, "."];
deltaPages ← 0;
};
exit ← FALSE;
};
countBadPages: CARDINAL ← 0;
badPageWorkProc: PROC [page: VolumeFormat.LogicalPage] = TRUSTED {
IF NOT FileInternal.SetPageUsed[volume: volume, page: page, inUse: TRUE].wasInUse THEN countBadPages ← countBadPages+1;
};
volume: File.Volume = IagoOps.GetLogical[in, out];
volumeFree: INT;
volumeSize: INT;
badStatus: INT ← 0;
thousandsCount: INT ← 0;
deltaPages: INT ← 0;
headers, freeHeaders, datas, freeDatas, freeMissings, frees, others, freeOthers: INT ← 0;
badFreeLabels, lastBadFreeLabel, badFreeLabelRuns: INT ← 0;
fixBadFreeLabels, listBadFreeLabels: BOOLFALSE;
[size: volumeSize, free: volumeFree] ← File.LogicalInfo[volume ! File.Error => {
IO.PutRope[out, "LogicalInfo for volume error — "];
IO.PutRope[out, IagoOps.FileError[why]];
};
];
IO.PutRope[out, "\nRepair bad labels on free pages? "];
fixBadFreeLabels ← IagoOps.Confirm[in,out];
IO.PutRope[out, "\nList pages with bad labels as they are encountered? "];
listBadFreeLabels ← IagoOps.Confirm[in,out];
IO.PutRope[out, "\n Scan started "];
TRUSTED {
FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: FALSE, work: NowPage];
};
Bad pages may have been marked FREE in the VAM. Make sure they are marked InUse.
TRUSTED {
volumeAlias: Volume;
volumeAlias ← volume;
FOR sv: LIST OF PhysicalVolume.SubVolumeDetails ← volumeAlias.subVolumes, sv.rest
UNTIL sv = NIL DO
FileInternal.GetBadPages [subVolume: sv.first, work: badPageWorkProc] ;
ENDLOOP;
};
out.PutF["\n Volume has %s pages with %g pages free in the VAM\n",
[integer[volumeSize]], [integer[volumeFree-countBadPages]] ];
IF badStatus > 0 THEN out.PutF[" %g pages had read errors\n", [integer[badStatus]]];
out.PutF["Statisitics on mismatches from scan:\n"];
out.PutF[" Header pages %g (%g free in VAM), Data pages %g (%g free in VAM) \n",
[integer[headers]], [integer[freeHeaders]], [integer[datas]], [integer[freeDatas]] ];
out.PutF[" Other pages %g (%g free in VAM), Free pages %g (%g not free in VAM) \n",
[integer[others]], [integer[freeOthers]], [integer[frees]], [integer[freeMissings]] ];
out.PutF[" Free pages with bad labels %g (in %g runs) which were %gfixed\n",
[integer[badFreeLabels]], [integer[badFreeLabelRuns]], [rope[IF fixBadFreeLabels THEN "" ELSE "not "]] ];
};
Rename: PUBLIC PROC [in, out: STREAM] = {
from: ROPE = IagoOps.GetFile[in: in, out: out, prompt: "From ", check: TRUE];
to: ROPE = IagoOps.GetFile[in: in, out: out, prompt: "To "];
IO.PutRope[out, " ... "];
FS.Rename[from: from, to: to];
IO.PutRope[out, "done"];
};
Rollback: PUBLIC PROC [in, out: STREAM] = {
v: File.Volume = IagoOps.GetLogical[in, out];
reject: ROPE;
mySwitches: Booting.Switches ← ALL[FALSE];
mySwitches[r] ← TRUE;
reject ← Booting.Boot[[logical[v]], mySwitches];
IF reject # NIL THEN { IO.PutRope[out, " ... couldn't: "]; IO.PutRope[out, reject] };
};
RunDiagnosticBCD: PUBLIC PROC [in, out: STREAM] = {
loadedOK: BOOLTRUE;
fName: ROPE;
cp: FS.ComponentPositions;
fsFile: FS.OpenFile;
strm: STREAMNIL;
tryNoCache: BOOLFALSE;
remoteOnly: BOOLFALSE;
cm: PrincOps.ControlModule;
unboundImports: LIST OF Loader.IRItem;
default: ROPE ← Rope.Cat[DefaultRemoteNames.Get[].current, "Iago>ExtraIago.bcd"];
IO.PutRope[out, "\nAvoid using the local disk for caching the bcd file? "];
remoteOnly ← IagoOps.Confirm[in, out];
fName← IagoOps.GetFile[in: in, out: out, prompt: "FileName ", default: default, check: ~remoteOnly];
IF ~remoteOnly THEN fsFile← FS.Open[fName
! FS.Error => {
IF error.code = $noCache OR error.code = $badBTree THEN {
tryNoCache ← TRUE;
CONTINUE;
};
IO.PutRope[out, error.explanation];
GOTO noFile;
};
];
IF tryNoCache OR remoteOnly THEN {
fullFName: ROPE;
bytesOnServer: INT;
pagesOnServer: INT ← 0;
createdTime: BasicTime.GMT;
myfsFile: myOpenFile;
ProduceStream: PROC[fullGName: ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [STREAM] = {
pagesOnServer← VM.PagesForBytes[bytes];
bytesOnServer ← bytes;
createdTime ← created;
RETURN[strm← IO.ROS[]];
};
[fullFName, cp, ]← FS.ExpandName[fName];
FSRemoteFile.Retrieve[fullFName.Substr[cp.server.start, cp.server.length], fullFName.Substr[cp.dir.start-1], BasicTime.nullGMT, ProduceStream
! FS.Error =>
{ out.PutChar['\n]; out.PutRope[error.explanation]; GO TO noFile}];
IF strm = NIL THEN {out.PutRope["\n Can't retreive file"]; GO TO noFile};
strm← IO.RIS[IO.RopeFromROS[strm], strm];
myfsFile ← NEW [myOpenFileRec ← [strm, createdTime, bytesOnServer, pagesOnServer]];
fsFile ← FSBackdoor.CreateProcsOpenFile[clientFile: myfsFile, fileProcs: FSBackdoor.CreateFileProcs[GetInfo: streamInfo, Read: streamReader] ];
};
out.PutRope[" ... "];
TRUSTED {
[cm, unboundImports]← Loader.Instantiate[fsFile
! Loader.Error => {
IO.PutRope[out, message];
loadedOK ← FALSE;
cm← PrincOps.NullControl;
CONTINUE;
};
];
};
IF unboundImports#NIL THEN {
IO.PutRope[out, "\nThere are unbound imports; confirm to start the code anyway? "];
IF ~IagoOps.Confirm[in, out] THEN RETURN;
};
TRUSTED {
IF cm # PrincOps.NullControl THEN Loader.Start[cm ! ABORTED => {
loadedOK ← FALSE;
IO.PutRope[out, "Loader.Start was ABORTED"];
CONTINUE;
};
];
};
IF loadedOK THEN IO.PutRope[out, "Loaded and Started\n"];
FS.Close[fsFile ! FS.Error => CONTINUE];
IF strm # NIL THEN strm.Close[ ! IO.Error => CONTINUE];
EXITS
noFile => NULL;
};
Scavenge: PUBLIC PROC [in, out: STREAM] = {
v: File.Volume = IagoOps.GetLogical[in, out];
volumeFreePages: INT;
rootStatus: File.RC;
fsRootFP: File.FP;
fsRootSize: INT ← 900;
[rootStatus: rootStatus, free: volumeFreePages] ← File.LogicalInfo[v];
SELECT rootStatus FROM
ok => NULL;
nonCedarVolume => {
IO.PutRope[out, "\nVolume is not a Cedar Volume"];
RETURN;
};
wentOffline => {
IO.PutRope[out, "\nVolume went offline"];
RETURN;
};
inconsistent => {
IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"];
RETURN;
};
software => {
IO.PutRope[out, "\nCan't find the volume root page (label-check)"];
RETURN;
};
hardware => {
IO.PutRope[out, "\nHard disk error reading the volume root page"];
RETURN;
};
ENDCASE => ERROR File.Error[rootStatus];
fsRootFP ← FileBackdoor.GetRoot[v, client].fp; -- client is the client file system: FS
IF fsRootFP # File.nullFP THEN {
file: File.Handle ← File.Open[v, fsRootFP ! File.Error => GO TO nope];
fsRootSize ← File.Info[file ! File.Error => CONTINUE].size;
EXITS nope => {};
};
check to see if there is (probably) enough room for a new FS BTree
IF (fsRootSize + 200) > volumeFreePages THEN {
not enough room - see if checkpoint exists, and ask if we can delete it
checkpointFP: File.FP;
checkpointFile: File.Handle ← NIL;
checkpointFP ← FileBackdoor.GetRoot[v, checkpoint].fp;
checkpointFile ← File.Open[v, checkpointFP ! File.Error => CONTINUE];
IF checkpointFile = NIL THEN {
IO.PutRope[out, "\nThere appears not to be enough room to scavenge and there is not a checkpoint to delete. Continue? "];
IF NOT IagoOps.Confirm[in, out] THEN RETURN;
}
ELSE {
IO.PutRope[out, "\nThere appears not to be enough room to scavenge. OK to delete checkpoint? "];
IF IagoOps.Confirm[in, out] THEN {
status: File.RC ← ok;
File.Delete[checkpointFile ! File.Error => { status ← why; CONTINUE;}];
IO.PutRope[out, IF status = ok
THEN "\nCheckpoint file has been deleted"
ELSE "\nError in delete. Checkpoint file has NOT been deleted."];
};
};
checkpointFile ← NIL;
};
IO.PutRope[out, " Scavenging ... "];
[] ← File.FindVM[];
FSBackdoor.ScavengeDirectoryAndCache[File.GetVolumeName[v]];
IO.PutRope[out, "done"];
};
SetKeep: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
DoName: FS.NameProc = {
count ← count+1;
fullFName ← fullFName.Substr[start: 0, len: fullFName.Index[0, "!"]];
IO.PutF1[out, "\nSetting %g ... ", [rope[fullFName]]];
{
ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE };
FS.SetKeep[fullFName, k];
IO.PutRope[out, "done"];
};
RETURN[TRUE];
};
pattern: 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"];
IO.PutRope[out, " ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
SetPhysicalFile: PUBLIC PROC [in, out: STREAM, which: File.VolumeFile[checkpoint..bootFile]] = {
v: File.Volume = IagoOps.GetLogical[in, out, "From "];
IO.PutRope[out, " ... "];
PhysicalVolume.SetPhysicalRoot[v, which];
IO.PutRope[out, "done"];
};
SetWDir: PUBLIC PROC [in, out: STREAM] = {
Help: PROC = {
IO.PutRope[out, "? Type the prefix of a file name, such as \"[]<volume>\""];
};
name: 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];
};
Utilities
streamInfo: PROC [clientFile: REF] RETURNS [keep: CARDINAL, pages, bytes: INT, created: BasicTime.GMT, lock: FS.Lock] = {
myfsFile: myOpenFile = NARROW[clientFile, myOpenFile];
keep ← 1;
pages ← myfsFile.pages;
bytes ← myfsFile.bytes;
created ← myfsFile.created;
lock ← read;
};
streamReader: UNSAFE PROC [clientFile: REF, from, nPages: INT, to: LONG POINTER] = TRUSTED {
strm: STREAM = NARROW[clientFile, myOpenFile].strm;
strm.SetIndex[FS.BytesForPages[from]];
[] ← strm.UnsafeGetBlock[[base: LOOPHOLE[to], startIndex: 0, count: FS.BytesForPages[nPages]]];
};
}.
Bob Hagmann April 30, 1985 12:07:30 pm PDT
add "Fake FS" inside of RunDiagnosticBCD; new command RecomputeVAM
changes to: RunDiagnosticBCD, RecomputeVAM
Bob Hagmann June 13, 1985 8:15:25 am PDT
changed Scavenge to try to delete checkpoint if there is not enough room for a new FS BTree
Bob Hagmann October 14, 1985 12:59:57 pm PDT
changes to: RunDiagnosticBCD, ProduceStream (local of RunDiagnosticBCD)