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]; 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. bFSUtil.mesa Copyright c 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 Commands ENABLE FS.Error => {cmd.out.PutF[" %g\n", IO.rope[error.explanation]]; CONTINUE}; FS.Close[[f]]; count _ count + 1; 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. i.e. label-check disk hardware/microcode detected a hard disk error page would be beyond the end of the file file does not exist (possibly deleted since it was opened) the volume's (or file's) permanent data structures look inconsistent PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label, diskPage: INT] RETURNS[exit: BOOL _ FALSE]; FS file discovered not in FS BTree Utilities PROC [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]; PROC [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]; Start code Bob Hagmann June 19, 1985 8:45:23 am PDT added FSDeleteOrphanFiles ΚO– "cedar" style˜codešœ ™ Kšœ Οmœ1™<—™K™%K™'—code1šΟk ˜ J˜Jšœ žœžœ ˜4J˜Jšœ žœ!˜0Jšœ žœ6˜GLšœžœ2˜?L˜L˜ Lšœžœ%žœF˜wLšœ ˜ Lšœ žœ/˜ALšœžœ˜'Lšžœžœ-˜5Lšœ žœX˜hLšœžœ4˜?Lšœ˜L˜Lšœ žœ>˜ML˜L˜Lšžœžœ:žœ˜JLšœ ˜ LšœžœBžœ˜XLšžœ˜Lšœ ˜ —šœž˜Lšžœž˜LšžœVžœRžœž˜ΛLšžœ ˜Lšžœ1˜7Lšœž˜L˜Lšžœžœžœ˜L˜Lšœžœžœ žœ˜#L˜šœ žœžœ˜Lšœžœ˜L˜—L˜LšΟc œžœžœ˜5—head™šΟb œž ˜1š Οnœžœžœžœž˜!Lšž˜šžœžœžœ ž˜4š žœžœ<žœžœž˜Všžœžœž˜šœž˜#Lšœžœžœ˜Lšœ˜šœ žœ ˜Lšœžœ ž˜Lšœ ˜ —Lšœ)žœ˜0Lš žœžœžœžœ+žœ.˜–šžœž˜ Lšœ žœ˜šžœžœ ™Lšœžœžœ™@—Lšžœ ™L™Lšžœ˜—Lšžœ˜—Lšžœ˜—Lšžœ˜—Lšžœ˜—Lšžœ˜—Lšœ!˜!Lšœ žœ˜Lšœžœ˜Lšœ žœžœžœ˜0šœ!˜!Lšœ+žœ˜8—Lšžœžœžœžœ˜Lšžœžœžœ˜!šœžœ4˜=Lšœžœ<žœ ˜MLšœ ˜ —L˜ š žœžœžœ0žœ žœž˜Všžœžœ ˜&Lšœžœ˜4Lšžœ˜Lšœ˜—Lšœ˜L˜Lšžœ˜—Lšœ+žœ˜;šž˜Lšœ žœ˜ Lšœ žœ/˜>—Lšžœ˜L˜—š œž˜1Lšž˜š ‘œžœžœžœž˜ Lšž˜šžœžœžœ ž˜4šžœ;žœžœž˜NLšžœž˜šžœž˜ Lšœ#žœ žœ4žœžœžœ žœžœ žœžœžœžœžœ˜ώLšžœž˜LšžœžœJ˜jLšžœ˜—Kšžœ˜—Kšžœ˜—Lšžœ˜—Lšœ˜Lšžœ˜L˜—š œž˜/Lšž˜š ‘œžœžœžœž˜ Lšž˜šžœžœžœ ž˜4š žœžœ<žœžœž˜Všžœžœž˜šœž˜#Lšœžœžœ˜šœ žœ ˜Lšœžœ ž˜L˜ —Lšžœ ž˜Lšžœžœ žœ˜>Lšžœ˜—Lšžœ˜—Kšžœ˜—Kšžœ˜—Lšžœ˜—Lšœ˜Lšžœ˜—š œž˜1Lšž˜š ‘œžœžœžœž˜ Lšž˜šžœžœžœ ž˜4š žœžœ<žœžœž˜Všžœžœž˜šœ$˜$šžœžœ˜Lšžœž˜ Lš œ žœžœ!žœžœ ˜PLšœžœ žœžœ ˜VLšžœ˜——Lšžœ˜—Kšžœ˜—Kšžœ˜—Lšžœ˜—Lšœ˜Lšžœ˜L˜—š œž˜)Lšž˜Lšœ!˜!Lšœžœžœžœ˜Lšœžœ˜+šœ!˜!Lšœ+žœ˜7—Lšžœžœžœžœ˜)Lšžœžœžœ˜!Lšžœ˜šžœž˜ Lšžœžœ˜0šžœž˜ Lšœ žœ8žœ˜QLšœ4˜4Lšž˜—Lšžœžœ˜Lšž˜—Lšžœ4žœ˜Dš žœ žœžœžœKž˜‡Lšžœžœ$žœžœ˜6Lšžœ3žœ˜>Lšœ˜Lšžœ˜—Lšœ˜Lšžœ žœF˜[šžœ˜L˜——š  œž˜.Lšž˜Lšžœž˜"šžœž˜ Lšœ1˜1Lšžœ˜Lšžœ˜—š žœžœBžœžœž˜bLšœžœžœ ˜YLšžœ˜—Lšžœ˜L˜—š  œž˜-Lšž˜Lšžœž˜"Lšžœ2žœ"˜Zšžœž˜ Lšœ=žœ"žœ"˜…Lšžœ˜ šžœž˜ Lšœžœ΄˜½LšœLžœ!žœ$žœ ˜€Lš œWžœ#žœ!žœ#žœ&žœ!˜Lšžœ˜—Lšžœ˜—Lšžœ˜—L˜Lšœžœžœ˜,L˜š œžœ˜8™Lšœ_™_LšœC™C—L™‚L™ρLšœžœ˜"Lšœžœ žœ˜&Lšœžœžœ˜˜šžœžœžœ˜Lšžœ žœžœ$˜9Lš žœ žœžœžœžœžœ˜OL˜—Lšœ!˜!Lšœ˜Lšœ#žœ˜'Lšœ žœ˜Lšœ˜Lšœ žœžœ˜L˜L˜š   œžœžœžœžœ˜VLšœ˜Lšžœžœžœ˜L˜—š   œžœžœžœžœžœ˜9Lšœ žœ˜Lšœ˜Lšœ žœ˜Lšœ˜LšœN˜NLšœdžœ˜lL˜šžœ žœ žœ˜!Lšžœ žœ žœžœ˜KLšœžœ˜L˜—šžœžœž˜code2šœ ˜ N˜Nšœ˜—šœ ˜ Nšœ˜Nšœ˜—Nšžœ˜ —šžœžœ˜Lšœ žœžœ˜Lšœžœ žœ˜'Lšœ žœ ˜šœ1˜1šžœž˜šœ5˜5Jšœ žœ˜Jšžœ˜ J˜—Jšžœ˜—Lšœ˜—Lšœ˜šžœ žœ˜Jšœ žœ˜šžœžœ˜Jšœ]žœ˜pJšœG˜GJ˜—šžœ žœ˜Jšœ]˜]Jšœ1žœ˜DJ˜—L˜—šœžœ˜Lšœ0˜0šžœ žœ˜Lšœ žœ˜'LšœM˜ML˜—šœžœ˜Jšœ žœ˜šžœžœ˜JšœO˜OJšœG˜GJ˜—šžœ žœ˜Lšœžœ˜1šžœ'žœ ˜6Lšœ(žœžœ˜WLšžœ˜ Lšœ˜—Lšœ˜J˜—L˜—L˜—L˜—Lšœ%˜%L˜—š œžœžœžœ žœžœžœ˜\Lšœ žœ˜Lšœ žœ˜Lšœ%˜%šžœ žœž˜-Lšœžœžœ#˜3šœY˜YLšœ žœ˜šžœž˜šœ ˜ LšŸ™Lšœ7žœžœ˜\L˜—šœ ˜ LšŸ2™2Lšœ?žœžœ˜dL˜—šœ˜LšŸ(™(Lšœ8žœžœ˜]L˜—šœ˜LšŸ:™:LšœIžœžœ˜nL˜—šœ˜LšŸD™DLšœ_žœžœ˜„L˜—Lšžœ˜—Lšžœ˜ Lšœ˜—Lšœ˜Lšœ˜Lšžœ˜—L˜L˜—š œ$žœ˜4Lšžœ;žœžœžœžœžœžœ™Lšœ žœ˜;L˜šžœ žœ žœ˜"Lšžœ žœ žœžœ˜LLšœžœ˜L˜—L˜šžœžœž˜Jšœ3˜3šœ˜Jšœžœ˜Jšœ"˜"Jšœ žœ ˜Jšœ!˜!Jšœžœ žœ˜'šžœžœ˜Jšœ?˜?šžœ žœžœ˜šœ=˜=šžœž˜šœ5˜5Jšœ˜Jšžœ˜ J˜—Jšžœ˜—Jšœ˜—Jšœ˜šžœžœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜Jšœ žœž˜Jšœžœ˜šœ7žœ ˜DJšœ žœ˜Jšžœ˜ Jšœ˜—Jšœ˜šžœ žœ˜J™"Jšœ žœ˜Jšœ-˜-šžœ žœ˜Jšœžœ˜ Jšœžœ˜Jšœžœ˜Jšœ˜Jšœ˜šœIžœ ˜VJšœA˜AJšœ žœ˜Jšžœ˜ Jšœ˜—Jšœ˜šœ=žœ ˜JJšœL˜LJšœ žœ˜Jšžœ˜ Jšœ˜—Jšœ˜šžœžœ˜ Jšœ;˜;Jšœ žœ˜J˜—šžœ žœ˜šžœ˜Jšœžœ˜Jšžœ˜ Jšœ˜—šžœ$žœ˜,Jšœ‡˜‡J˜—šœžœ˜Jšœw˜wJ˜—J˜—Jšœ˜—šžœ žœ žœ˜#Jšœ žœ˜šžœžœ˜Jšžœ žœ:žœ˜jJšœ^žœ˜pJšœG˜GJ˜—šžœ žœ˜Lšœžœ˜;šœ"˜"Lšœ(žœ˜ELšžœ˜ Lšœ˜—Lšœ˜J˜—J˜—J˜—šœžœŸ˜šžœžœž˜2Jšœžœ˜Jšœ4˜4Jšžœžœžœžœ˜5Jšžœ˜—šžœ žœ˜Jšœ žœ˜šžœžœ˜Jšœ‘žœ˜³JšœG˜GJ˜—šžœ žœ˜JšœK˜KJšœ˜J˜—J˜—J˜—J˜—šœžœ˜Jšœ žœ˜šžœžœ˜Jšœ…žœ˜—JšœG˜GJ˜—šžœ žœ˜Jšœ+žœ˜=JšœGžœ˜LJ˜—J˜—J˜—J˜—Jšœ˜—šœ˜Jšœ˜—šœ˜Jšœ˜—šžœ˜ Jšœ˜——Lšœ%˜%L˜—L˜L˜Lšœ žœ˜&šœ!˜!Lšœ+žœ˜8—Lšžœžœžœžœ˜šžœ ž˜Lšœ˜Lš œžœ$žœžœžœžœžœ˜ZLšžœžœ˜—Lšœ*˜*Lšœ/žœ žœ˜ELš žœ žœžœžœžœžœ ˜7Lšœ žœ˜&LšœD˜DLšœ žœžœ˜-Lšœžœ#˜5L˜Idefault˜[Ošœ8žœ žœžœO˜―šžœ žœžœ˜0O˜POšœFžœ˜[O˜ O˜—Ošžœ žœžœ$˜9Lš ž œ žœžœžœžœžœ˜Zšž˜Lšœ žœ˜ Lšœ žœ;˜JLšœ žœ"˜4—L˜—L˜L˜—š œžœ˜Lšœžœžœ˜'Lšžœ ˜L˜L˜—š  œžœ˜-Lšžœ4žœ™TLšœžœžœ˜'Lšœžœ žœ˜Lšœ žœžœ žœ˜7Lšœ žœ ˜Lšœžœžœ ˜Lšœžœžœ ˜šžœžœž˜Lšœžœ˜Lšœžœ ˜ Lšœžœ ˜Lšžœžœ˜—L˜L˜—š‘œžœ žœžœžœ žœ žœžœ˜Sšžœžœžœ ˜šžœž˜ Kš œžœžœžœžœžœ˜GKš œ žœžœžœžœ˜=Kš œžœžœžœžœ˜"šžœ˜ Kšžœ_˜a——Kšžœ˜—Kšœ˜—L˜L˜—™ šžœž˜ LšœZ˜ZLšœ`˜`Lšœ^˜^LšœZ˜ZLšœ]˜]LšœZ˜ZLšœe˜eLšœ‘˜‘Lšœx˜xLšžœ˜—Lšžœ˜L™—™(Kšœ™—K™—…—MFkχ