FSUtil.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Schroeder, December 13, 1983 9:58 am
Bob Hagmann June 6, 1986 1:42:47 pm PDT
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.ROPENIL;
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.ROPENIL;
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: INTLAST[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"];
END;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLTRUE;
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: BOOLTRUE] = 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: BOOLFALSE];
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: BOOLTRUE;
isRootFile: BOOLFALSE;
insertError: BOOLFALSE;
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];
};
};
};
};
};
attr = data => {
};
attr = freePage => {
};
ENDCASE => {
};
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: BOOLFALSE] = {
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.
Bob Hagmann June 19, 1985 8:45:23 am PDT
added FSDeleteOrphanFiles