<> <> <> <> <> 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; <> 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]; <>> <<{cmd.out.PutF[" %g\n", IO.rope[error.explanation]]; CONTINUE};>> <> <> 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"]; 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: BOOL _ FALSE; FSEstablishInvariants: Commander.CommandProc = CHECKED { <> <> <> <> <> 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 => { <> cmd.out.PutF["Label check on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]]; }; hardware => { <> cmd.out.PutF["Disk hardware error on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]]; }; unknownPage => { <> cmd.out.PutF["Unknown page on file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]]; }; unknownFile => { <> cmd.out.PutF["File disappeared during read for file %g, disk page %g\n", IO.rope[fileName], IO.int[diskPage]]; }; 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 { <> 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 { <> 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; }; <> 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 { <> myData: REF OpenFileRec = NARROW[data]; RETURN[myData.key]; }; compareKeys: RedBlackTree.Compare = CHECKED { <> 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; }; <> 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. <<>> <> <> <<>>