DIRECTORY
Ascii,
BasicTime USING [GMT, Now, nullGMT, Period, Update],
BTree,
Commander USING [CommandProc, Handle, Register],
CommandTool USING [ArgumentVector, Failed, Parse, ResolveRelativePath],
Convert USING [Error, IntFromRope, RopeFromCard, RopeFromTime],
Disk,
DiskFace,
File USING [Delete, Error, FindVolumeFromName, FP, Info, nullFP, Open, Handle, Read, SystemVolume, Volume, VolumeFile],
FileBackdoor,
FileInternal USING [EnumeratePages, EnumeratePagesProc, FreeRun],
FileStreamPrivate USING [FSDataHandle],
FS USING [Close, Delete, Error, ExpandName, GetName],
FSBackdoor USING [EntryPtr, Enumerate, FNameFromHandle, MakeFName, noVersion, TextFromTextRep, Version],
FSDir USING [DeleteEntry, UpdateCachedEntry, UpdateLocalEntry],
FSLock,
FSLockTableImpl,
FSFileOps USING [GetNameBodyAndVersion, GetProps, GetVolumeDesc, VolumeDesc],
FSFileSpaceImpl,
FSOpenFileImpl,
IO USING [card, char, int, GetChar, PutChar, PutF, PutRope, rope, STREAM],
RedBlackTree,
Rope USING [Cat, Equal, Fetch, Find, InlineFetch, IsEmpty, Length, Replace, ROPE, Text],
VM,
VolumeFormat;
FSUtil:
MONITOR
LOCKS @FSLockTableImpl.LOCK
IMPORTS BasicTime, BTree, Commander, CommandTool, Convert, File, FileBackdoor, FileInternal, FS, FSBackdoor, FSDir, FSLockTableImpl, FSFileOps, FSFileSpaceImpl, FSOpenFileImpl, IO, RedBlackTree, Rope, VM
EXPORTS DiskFace
SHARES FSFileSpaceImpl, FSLockTableImpl, FSOpenFileImpl
= BEGIN
ROPE: TYPE = Rope.ROPE;
FPRec: TYPE = RECORD [fp: File.FP];
OpenFileRec:
TYPE =
RECORD [
key: REF FPRec
];
--DiskFace.--RelID: PUBLIC TYPE = VolumeFormat.RelID;
Commands
ForceClose: Commander.CommandProc =
CHECKED BEGIN
Search:
ENTRY
SAFE
PROC =
TRUSTED
BEGIN
FOR i:
CARDINAL
IN [0 .. FSLockTableImpl.buckets)
DO
FOR p:
REF FSLockTableImpl.PRTEntry ← FSLockTableImpl.prt[i], p.chain
UNTIL p =
NIL
DO
WITH p.ref
SELECT
FROM
f: FSOpenFileImpl.OpenFile =>
BEGIN
openName: Rope.ROPE ← NIL;
initialOK: INT;
openName ←
FS.GetName[[f]
! FS.Error => CONTINUE
].fullFName;
initialOK ← Rope.Find[openName, name, 0, FALSE];
IF Rope.Equal[name, openName, FALSE] OR (initialOK = 0 AND Rope.Length[name] < Rope.Length[openName] AND Rope.Fetch[openName, Rope.Length[name]] = '!)
THEN
BEGIN
fileList ← CONS[f, fileList];
ENABLE FS.Error =>
{cmd.out.PutF[" %g\n", IO.rope[error.explanation]]; CONTINUE};
FS.Close[[f]];
count ← count + 1;
END;
END;
ENDCASE;
ENDLOOP;
ENDLOOP;
END;
argv: CommandTool.ArgumentVector;
name: Rope.ROPE;
count: CARDINAL ← 0;
fileList: LIST OF FSOpenFileImpl.OpenFile ← NIL;
argv ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN GOTO Failed;
IF argv.argc # 2 THEN GOTO Usage;
name ←
FS.ExpandName[CommandTool.ResolveRelativePath[argv[1]]
! FS.Error => { msg ← Rope.Cat[" ", error.explanation, "\n"]; GOTO Failed }
].fullFName;
Search[];
FOR flist:
LIST
OF FSOpenFileImpl.OpenFile ← fileList, flist.rest
UNTIL flist =
NIL
DO
FS.Close[[flist.first] !
FS.Error => {
cmd.out.PutF[" %g\n", IO.rope[error.explanation]];
LOOP;
};
];
count ← count + 1;
ENDLOOP;
cmd.out.PutF[" Closed %g open files.\n", IO.card[count]];
EXITS
Failed => RETURN[$Failure, msg];
Usage => RETURN[$Failure, " Usage: ForceClose FullFName\n"];
END;
PrintActiveFiles: Commander.CommandProc =
CHECKED
BEGIN
Print:
ENTRY
SAFE
PROC =
TRUSTED
BEGIN
FOR i:
CARDINAL
IN [0 .. FSLockTableImpl.buckets)
DO
FOR a: FSLock.ActiveFile ← FSLockTableImpl.lockTbl[i], a.next
UNTIL a =
NIL
DO
IF a.nameBody # NIL
THEN
BEGIN
cmd.out.PutF["%3d. %g %g%g %g\n", IO.card[i], IO.rope[FSBackdoor.MakeFName[a.nameBody, a.version]], IO.card[a.fileLockCount], IO.char[SELECT a.fileLock FROM none => 'N, read => 'R, ENDCASE => 'W], IO.rope[IF a.recordLock THEN "record locked" ELSE NIL]];
IF a.attachedTo # NIL
THEN cmd.out.PutF[" <- %g\n", IO.rope[FSBackdoor.MakeFName[a.attachedTo.nameBody, a.attachedTo.version]]];
END;
ENDLOOP;
ENDLOOP;
END;
Print[];
END;
PrintOpenFiles: Commander.CommandProc =
CHECKED
BEGIN
Print:
ENTRY
SAFE
PROC =
TRUSTED
BEGIN
FOR i:
CARDINAL
IN [0 .. FSLockTableImpl.buckets)
DO
FOR p:
REF FSLockTableImpl.PRTEntry ← FSLockTableImpl.prt[i], p.chain
UNTIL p =
NIL
DO
WITH p.ref
SELECT
FROM
f: FSOpenFileImpl.OpenFile =>
BEGIN
openName: Rope.ROPE ← NIL;
openName ←
FS.GetName[[f]
! FS.Error => CONTINUE
].fullFName;
IF openName # NIL
THEN cmd.out.PutF["%3d. %g\n", IO.card[i], IO.rope[openName]];
END;
ENDCASE;
ENDLOOP;
ENDLOOP;
END;
Print[];
END;
PrintFileStreams: Commander.CommandProc =
CHECKED
BEGIN
Print:
ENTRY
SAFE
PROC =
TRUSTED
BEGIN
FOR i:
CARDINAL
IN [0 .. FSLockTableImpl.buckets)
DO
FOR p:
REF FSLockTableImpl.PRTEntry ← FSLockTableImpl.prt[i], p.chain
UNTIL p =
NIL
DO
WITH p.ref
SELECT
FROM
f: FileStreamPrivate.FSDataHandle =>
IF
NOT f.streamIsClosed
THEN BEGIN
lock: Rope.ROPE = (IF f.fileData.accessRights = $read THEN "read" ELSE "write");
cmd.out.PutF["%3d. %g %g\n", IO.card[i], IO.rope[f.fileData.fileName], IO.rope[lock]];
END;
ENDCASE;
ENDLOOP;
ENDLOOP;
END;
Print[];
END;
LRUFlush: Commander.CommandProc =
CHECKED
BEGIN
argv: CommandTool.ArgumentVector;
n: INT ← LAST[INT];
timeLimit: BasicTime.GMT ← BasicTime.Now[];
argv ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => { msg ← errorMsg; CONTINUE }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc < 2 THEN GOTO Usage;
IF Rope.Fetch[argv[1], 0] = '-
THEN
BEGIN
IF Rope.Fetch[argv[1], 1] = 't AND argv.argc = 3
THEN
BEGIN
hoursOld: INT = 60*60*Convert.IntFromRope[argv[2] ! Convert.Error => GOTO Usage];
timeLimit ← BasicTime.Update[timeLimit, - hoursOld];
END
ELSE GOTO Usage;
END
ELSE n ← Convert.IntFromRope[argv[1] ! Convert.Error => GOTO Usage];
THROUGH [1 .. n]
UNTIL FSFileSpaceImpl.lru.first #
NIL
AND BasicTime.Period[from: FSFileSpaceImpl.lru.first.used, to: timeLimit] < 0
DO
IF NOT FSFileSpaceImpl.MakePagesAvailable[NIL, 0, NIL]
THEN { cmd.out.PutRope[" -- no more files to flush!"]; EXIT };
cmd.out.PutChar['.];
ENDLOOP;
cmd.out.PutRope["\n"];
EXITS Usage => RETURN[$Failure, "Usage: LRUFlush numberToFlush -OR- LRUFlush -t hoursOld"];
PrintLRUChain: Commander.CommandProc =
CHECKED
BEGIN
IF FSFileSpaceImpl.lru.first = NIL
THEN
BEGIN
cmd.out.PutRope[" LRU chain currently empty.\n"];
RETURN;
END;
FOR useR:
REF FSFileSpaceImpl.UseRecord ← FSFileSpaceImpl.lru.first, useR.next
UNTIL useR =
NIL
DO
cmd.out.PutF[" %g %g\n", IO.rope[NameFromFP[useR.fp]], IO.rope[RopeFromTime[useR.used]]];
ENDLOOP;
END;
PrintLRUInfo: Commander.CommandProc =
CHECKED
BEGIN
IF FSFileSpaceImpl.lru.vDesc = NIL
THEN cmd.out.PutF[" Flusher disabled; killed = %g\n", IO.int[FSFileSpaceImpl.lru.killed] ]
ELSE
BEGIN
cmd.out.PutF[" Flusher enabled; killed = %g; sorts = %g;\n", IO.int[FSFileSpaceImpl.lru.killed], IO.int[FSFileSpaceImpl.lru.sorts] ];
IF FSFileSpaceImpl.lru.sorts > 0
THEN
BEGIN
free: INT = FSFileSpaceImpl.lru.cumCount - FSFileSpaceImpl.lru.flushed - FSFileSpaceImpl.lru.error - FSFileSpaceImpl.lru.deleted - FSFileSpaceImpl.lru.superseded - FSFileSpaceImpl.lru.open;
cmd.out.PutF[" last sort length = %g; total length = %g; remaining = %g\n", IO.int[FSFileSpaceImpl.lru.count], IO.int[FSFileSpaceImpl.lru.cumCount], IO.int[free] ];
cmd.out.PutF[" flushed = %g; error = %g; deleted = %g; superseded = %g; open = %g;\n", IO.int[FSFileSpaceImpl.lru.flushed], IO.int[FSFileSpaceImpl.lru.error], IO.int[FSFileSpaceImpl.lru.deleted], IO.int[FSFileSpaceImpl.lru.superseded], IO.int[FSFileSpaceImpl.lru.open] ];
END;
END;
END;
FSEstablishInvariantsStopFlag: BOOL ← FALSE;
FSEstablishInvariants: Commander.CommandProc =
CHECKED {
The invariants we want are:
there is a non-to-one correspondence between the files in the FS BTree and FS files on the disk
FS files on the disk are can be opened, are stable and are readable
To do this, we will enumerate the FS BTree, open ALL files found, and read them. If they are unstable, do not read properly, do not exist, or have any other error, they will be removed from the BTree. We remember the FP for the file if the file looks good.
Next, we scan the disk looking for files. When a file is found, it is either a root file, a FS file, or something else. We ignore root files, delete the FS file it it doesn't match a saved FP, and report about the ``something else'' files.
FPTable: RedBlackTree.Table ← NIL;
buffer: VM.Interval ← VM.nullInterval;
bufferAddress: LONG POINTER;
{
ENABLE
UNWIND =>
TRUSTED {
IF FPTable # NIL THEN RedBlackTree.DestroyTable[FPTable];
IF buffer # VM.nullInterval THEN VM.Free[buffer ! VM.AddressFault => CONTINUE];
};
argv: CommandTool.ArgumentVector;
volume: File.Volume;
volumeDesc: FSFileOps.VolumeDesc ← NIL;
badStatus: INT ← 0;
lastEntry: FSBackdoor.EntryPtr;
autoConfirm: BOOL ← FALSE;
fileCounter: INT ← 0;
pageCounter: INT ← 0 ;
matchProc:
UNSAFE
PROC [entry: FSBackdoor.EntryPtr]
RETURNS [accept, stop:
BOOLEAN] ={
lastEntry ← entry;
RETURN[TRUE, FALSE];
};
acceptProc:
SAFE
PROC
RETURNS [stop:
BOOLEAN] =
TRUSTED {
thisFP: File.FP ← File.nullFP;
file: File.Handle;
fileName: ROPE;
nameBodyRopeText: Rope.Text;
nameBodyRopeText ← FSBackdoor.TextFromTextRep[@lastEntry[lastEntry.nameBody]];
fileName ← Rope.Cat[nameBodyRopeText, "!", Convert.RopeFromCard[from: lastEntry.version, showRadix: FALSE]];
fileCounter ← fileCounter + 1;
IF fileCounter
MOD 100 = 0
THEN {
IF fileCounter MOD 1000 = 0 THEN cmd.out.PutF["(%g) ", IO.int[fileCounter]]
ELSE cmd.out.PutRope[". "];
};
WITH e: lastEntry^
SELECT
FROM
local => {
thisFP ← e.fp;
};
cached => {
thisFP ← e.fp;
};
ENDCASE ;
IF thisFP # File.nullFP
THEN {
errorFree: BOOL ← TRUE;
key: REF FPRec ← NEW[FPRec ← [thisFP]];
insertData: REF OpenFileRec;
file ← File.Open[volume, thisFP ! File.Error => {
SELECT why
FROM
unknownFile, unknownPage, inconsistent, software => {
errorFree ← FALSE;
CONTINUE;
};
ENDCASE;
};
];
IF ~errorFree
THEN {
confirmed: BOOL ← autoConfirm;
IF ~autoConfirm
THEN {
cmd.out.PutF["Could not open %g.\nShould it be removed from the FS BTree (Yes/No return)? ", IO.rope[fileName]];
[confirmed, FSEstablishInvariantsStopFlag] ← Confirm[cmd.in, cmd.out];
};
IF confirmed
THEN {
FSDir.DeleteEntry[vDesc: volumeDesc, nameBody: nameBodyRopeText, version: lastEntry.version];
cmd.out.PutF["Removing %g from the FS BTree.\n", IO.rope[fileName]];
};
}
ELSE {
[errorFree: errorFree] ← readit[file, fileName];
IF errorFree
THEN {
insertData ← NEW [OpenFileRec ← [key]];
RedBlackTree.Insert[self: FPTable, dataToInsert: insertData, insertKey: key];
}
ELSE {
confirmed: BOOL ← autoConfirm;
IF ~autoConfirm
THEN {
cmd.out.PutF["Because of error(s), should I delete the file(Yes/No return)? "];
[confirmed, FSEstablishInvariantsStopFlag] ← Confirm[cmd.in, cmd.out];
};
IF confirmed
THEN {
cmd.out.PutF["Deleting %g\n", IO.rope[fileName]];
FS.Delete[name: fileName, wDir: "[]<>" !
FS.Error => {
cmd.out.PutF["Error deleting %g: 5g\n", IO.rope[fileName], IO.rope[error.explanation]];
CONTINUE;
};
];
};
};
};
};
stop ← FSEstablishInvariantsStopFlag;
};
readit:
PROC [file: File.Handle, fileName:
ROPE]
RETURNS[errorFree:
BOOL ←
TRUE] =
TRUSTED {
totalPages: INT;
nowPage: INT ← 0;
[size: totalPages] ← File.Info[file];
WHILE errorFree
AND (nowPage < totalPages)
DO
count: INT = MIN[buffer.count, totalPages-nowPage];
File.Read[file: file, from: [nowPage], nPages: count, to: bufferAddress ! File.Error => {
errorFree ← FALSE;
SELECT why
FROM
software => {
i.e. label-check
cmd.out.PutF["Label check on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]];
};
hardware => {
disk hardware/microcode detected a hard disk error
cmd.out.PutF["Disk hardware error on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]];
};
unknownPage => {
page would be beyond the end of the file
cmd.out.PutF["Unknown page on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]];
};
unknownFile => {
file does not exist (possibly deleted since it was opened)
cmd.out.PutF["File disappeared during read for file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]];
};
inconsistent => {
the volume's (or file's) permanent data structures look inconsistent
cmd.out.PutF["File's permanent data structures look inconsistent for file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]];
};
ENDCASE;
CONTINUE;
};
];
nowPage ← nowPage + count;
ENDLOOP;
};
NowPage: FileInternal.EnumeratePagesProc =
TRUSTED {
PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label, diskPage: INT] RETURNS[exit: BOOL ← FALSE];
attr: VolumeFormat.Attributes = LOOPHOLE[label.attributes];
pageCounter ← pageCounter + 1;
IF pageCounter
MOD 1000 = 0
THEN {
IF pageCounter MOD 10000 = 0 THEN cmd.out.PutF["(%g) ", IO.int[pageCounter]]
ELSE cmd.out.PutRope[". "];
};
SELECT
TRUE
FROM
status # Disk.ok => { badStatus ← badStatus + 1; };
attr = header => {
file: File.Handle ← NIL;
relid: RelID = label.fileID.relID;
openFP: File.FP ← relid;
dataFound: RedBlackTree.UserData;
key: REF FPRec ← NEW[FPRec ← [openFP]];
IF label.filePage = 0
THEN {
dataFound ← RedBlackTree.Lookup[self: FPTable, lookupKey: key];
IF dataFound =
NIL
THEN {
file ← File.Open[volume: volume, fp: openFP ! File.Error => {
SELECT why
FROM
unknownFile, unknownPage, inconsistent, software => {
openFP ← File.nullFP;
CONTINUE;
};
ENDCASE;
};
];
IF file #
NIL
THEN {
isAFSFile: BOOL ← TRUE;
isRootFile: BOOL ← FALSE;
insertError: BOOL ← FALSE;
discoveredFileName: ROPE;
discoveredFileName ← FSBackdoor.FNameFromHandle[file !
FS.Error => {
isAFSFile ← FALSE;
CONTINUE;
};
];
IF isAFSFile
THEN {
FS file discovered not in FS BTree
errorFree: BOOL;
errorFree ← readit[file, discoveredFileName];
IF errorFree
THEN {
bytes: INT;
keep: CARDINAL;
created: BasicTime.GMT;
nameBody: Rope.Text;
version: FSBackdoor.Version;
[bytes: bytes, keep: keep, created: created] ← FSFileOps.GetProps[file !
FS.Error => {
cmd.out.PutF["\nGot an FS file, but can't get the properties\n"];
isAFSFile ← FALSE;
CONTINUE;
}
];
[nameBody, version] ← FSFileOps.GetNameBodyAndVersion[file !
FS.Error => {
cmd.out.PutF["\nGot an FS file, but can't get the name body and version\n"];
isAFSFile ← FALSE;
CONTINUE;
}
];
IF Rope.IsEmpty[nameBody]
THEN {
cmd.out.PutF["\nGot an FS file, but name body is empty\n"];
isAFSFile ← FALSE;
};
IF isAFSFile
THEN {
ENABLE BTree.Error => {
insertError ← TRUE;
CONTINUE;
};
IF Rope.InlineFetch[nameBody, 0] = '[
THEN {
FSDir.UpdateCachedEntry[vDesc: volumeDesc, nameBody: nameBody, version: version, used: BasicTime.nullGMT, fp: openFP, update: insert ];
}
ELSE {
FSDir.UpdateLocalEntry[vDesc: volumeDesc, nameBody: nameBody, version: version, keep:keep, fp: openFP, update: insert];
};
};
};
IF ~isAFSFile
OR insertError
THEN {
confirmed: BOOL ← autoConfirm;
IF ~autoConfirm
THEN {
IF insertError THEN cmd.out.PutF["Could not insert file %g into FS BTree\n", IO.rope[discoveredFileName]];
cmd.out.PutF["Because of error(s), should I delete the file at %b (octal) (Yes/No return)? ", IO.int[diskPage]];
[confirmed, FSEstablishInvariantsStopFlag] ← Confirm[cmd.in, cmd.out];
};
IF confirmed
THEN {
cmd.out.PutF["Deleting %g\n", IO.rope[discoveredFileName]];
File.Delete[file ! File.Error => {
cmd.out.PutF["Error deleting %g: %g\n", IO.rope[discoveredFileName]];
CONTINUE;
};
];
};
};
}
ELSE {
-- not errorFree
FOR volFile: File.VolumeFile
IN File.VolumeFile
DO
fsRootFP: File.FP;
fsRootFP ← FileBackdoor.GetRoot[volume, volFile].fp;
IF fsRootFP = openFP THEN {isRootFile ← TRUE; EXIT;};
ENDLOOP;
IF ~isRootFile
THEN {
confirmed: BOOL ← autoConfirm;
IF ~autoConfirm
THEN {
cmd.out.PutF["Discovered a file starting at disk page %g that could be opened, but is not a FS or a root file.\n\tShould the file be deleted (Yes/No return)? ", IO.int[diskPage]];
[confirmed, FSEstablishInvariantsStopFlag] ← Confirm[cmd.in, cmd.out];
};
IF confirmed
THEN {
cmd.out.PutF["Deleting file starting at disk page %g\n", IO.int[diskPage]];
File.Delete[file];
};
};
};
}
ELSE {
confirmed: BOOL ← autoConfirm;
IF ~autoConfirm
THEN {
cmd.out.PutF["Discovered a filestarting at disk page %g that could not be opened. Should header page 0 be freed (Yes/No return)? ", IO.int[diskPage]];
[confirmed, FSEstablishInvariantsStopFlag] ← Confirm[cmd.in, cmd.out];
};
IF confirmed
THEN {
cmd.out.PutF["Header page at %g freed.\n", IO.int[diskPage]];
FileInternal.FreeRun[logicalRun: [da, 1], volume: volume, verifyLabel: NIL];
};
};
};
};
};
exit ← FSEstablishInvariantsStopFlag;
};
FSEstablishInvariantsStopFlag ← FALSE;
argv ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN GOTO Failed;
SELECT argv.argc
FROM
2 => {};
3 => IF Rope.Equal[argv[2], "AutoConfirm", FALSE] THEN autoConfirm ← TRUE ELSE GOTO Usage;
ENDCASE => GOTO Usage;
volume ← File.FindVolumeFromName[argv[1]];
volumeDesc ← FSFileOps.GetVolumeDesc[argv[1] ! FS.Error => CONTINUE];
IF volume = NIL OR volumeDesc = NIL THEN GOTO NoVolume;
FSEstablishInvariantsStopFlag ← FALSE;
FPTable ← RedBlackTree.Create[getKey: getKey, compare: compareKeys];
buffer ← VM.Allocate[count: 50, in64K: TRUE];
bufferAddress ← VM.AddressForPageNumber[buffer.page];
cmd.out.PutRope["Starting enumerate of FS BTree. A dot is printed for every 100 files\n"];
FSBackdoor.Enumerate[volName: argv[1], nameBodyPattern: NIL, localOnly: FALSE, allVersions: TRUE, version: FSBackdoor.noVersion, matchProc: matchProc, acceptProc: acceptProc];
IF ~FSEstablishInvariantsStopFlag
THEN
TRUSTED {
cmd.out.PutRope["Starting disk scan. A dot is printed for every 1000 pages\n"];
FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: TRUE, work: NowPage];
cmd.out.PutRope["Scan done.\n"];
};
IF FPTable # NIL THEN RedBlackTree.DestroyTable[FPTable];
TRUSTED {IF buffer # VM.nullInterval THEN VM.Free[buffer ! VM.AddressFault => CONTINUE];};
EXITS
Failed => RETURN[$Failure, msg];
Usage => RETURN[$Failure, " Usage: FSEstablishInvariants volumename\n"];
NoVolume => RETURN[$Failure, " No such volume\n"];
};
};
StopFSEstablishInvariants: Commander.CommandProc =
CHECKED {
FSEstablishInvariantsStopFlag ← TRUE;
};
Utilities
months: ARRAY [1..12] OF Rope.ROPE = ["January ", "February ", "March ", "April ", "May ", "June ", "July ", "August ", "September ", "October ", "November ", "December "];
numericMonths: ARRAY [1..12] OF Rope.ROPE = ["1/", "2/", "3/", "4/", "5/", "6/", "7/", "8/", "9/", "10/", "11/", "12/"];
RopeFromTime:
SAFE
PROC [ time: BasicTime.
GMT ]
RETURNS [rope: Rope.
ROPE] =
CHECKED
BEGIN
rope ← Convert.RopeFromTime[from: time, start: $months, end: $seconds, useAMPM: FALSE];
FOR i: [1..12]
IN [1..12]
DO
IF Rope.Find[rope, months[i]] # -1
THEN rope ← Rope.Replace[base: rope, len: Rope.Length[months[i]] , with: numericMonths[i]];
ENDLOOP;
END;
NameFromFP:
SAFE
PROC [ fp: File.
FP ]
RETURNS [fName: Rope.
ROPE] =
CHECKED
BEGIN
nameBody: Rope.Text;
version: FSBackdoor.Version;
[nameBody, version] ← FSFileOps.GetNameBodyAndVersion
[File.Open[File.SystemVolume[], fp]];
fName ← FSBackdoor.MakeFName[nameBody, version];
END;
getKey: RedBlackTree.GetKey =
CHECKED {
PROC [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key];
myData: REF OpenFileRec = NARROW[data];
RETURN[myData.key];
};
compareKeys: RedBlackTree.Compare =
CHECKED {
PROC [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison];
myData: REF OpenFileRec = NARROW[data];
myKey: REF FPRec = NARROW[k];
KKey: File.FP = NARROW[myData, REF OpenFileRec].key.fp;
DKey: File.FP = myKey.fp;
KInt: INT = LOOPHOLE[KKey.da];
DInt: INT = LOOPHOLE[DKey.da];
SELECT
TRUE
FROM
KInt < DInt => RETURN [less];
KInt > DInt => RETURN [greater];
KInt = DInt => RETURN [equal];
ENDCASE => ERROR;
};
Confirm:
PROC[in, out:
IO.
STREAM]
RETURNS [confirm:
BOOL, rubout:
BOOL ←
FALSE] = {
DO c:
CHAR =
IO.GetChar[in];
SELECT c
FROM
'y, 'Y, Ascii.CR => { IO.PutRope[out, "yes\n"]; RETURN [TRUE, FALSE] };
'n, 'N => { IO.PutRope[out, "no\n"]; RETURN [FALSE, FALSE] };
Ascii.DEL => RETURN [FALSE, TRUE];
ENDCASE =>
IO.PutRope[out, "?\nConfirm by typing \"Y\" or CR, deny by typing \"N\", abort by typing DEL: "];
ENDLOOP;
};
Start code
CHECKED
BEGIN
Commander.Register[key: "OpenFiles", proc: PrintOpenFiles, doc: "Shows all FS.OpenFiles"];
Commander.Register[key: "ActiveFiles", proc: PrintActiveFiles, doc: "Shows all FS.ActiveFiles"];
Commander.Register[key: "FileStreams", proc: PrintFileStreams, doc: "Shows all file streams"];
Commander.Register[key: "LRUChain", proc: PrintLRUChain, doc: "Shows FS cache lru chain"];
Commander.Register[key: "LRUInfo", proc: PrintLRUInfo, doc: "Shows FS cache lru statistics"];
Commander.Register[key: "LRUFlush", proc: LRUFlush, doc: "Flushes GNames from cache lru"];
Commander.Register[key: "ForceClose", proc: ForceClose, doc: "Closes all FS.OpenFiles for an FName"];
Commander.Register[key: "FSEstablishInvariants", proc: FSEstablishInvariants, doc: "FSEstablishInvariants volumeName {AutoConfirm} -- Re-Establish the invariant that all FS files on the disk are stable and readable, and that there is a one-to-one correspondence between the files in the FS BTree and FS files on the disk. AutoConfirm will automatically delete files or free headers for bogus files"];
Commander.Register[key: "StopFSEstablishInvariants", proc: StopFSEstablishInvariants, doc: "Stops FSDeleteOrphanFiles"];
END;
END.