<> <> <> <> <> DIRECTORY Commander, CommandTool, Disk, DiskFace, File, FileInternal, IO, RedBlackTree, Rope, VolumeFormat; FileUtil: CEDAR PROGRAM IMPORTS Commander, CommandTool, File, FileInternal, IO, RedBlackTree EXPORTS DiskFace, File = BEGIN VAMStatsStop: BOOL _ FALSE; PrintVAMStats: Commander.CommandProc = { NowPage: FileInternal.EnumeratePagesProc = TRUSTED { <> 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; }; attr = data => { IF FileInternal.IsUsed[volume: volume, page: da] THEN datas _ datas + 1 ELSE freeDatas_ freeDatas + 1; }; attr = freePage => { IF FileInternal.IsUsed[volume: volume, page: da] THEN freeMissings _ freeMissings + 1 ELSE frees _ frees + 1; }; ENDCASE => { IF FileInternal.IsUsed[volume: volume, page: da] THEN others _ others + 1 ELSE freeOthers_ freeOthers + 1; }; exit _ VAMStatsStop; }; argv: CommandTool.ArgumentVector; volume: File.Volume; volumeFree: INT; volumeSize: INT; badStatus: INT _ 0; headers, freeHeaders, datas, freeDatas, freeMissings, frees, others, freeOthers: INT _ 0; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN GOTO Failed; IF argv.argc # 2 THEN GOTO Usage; volume _ File.FindVolumeFromName[argv[1]]; IF volume = NIL THEN GOTO NoVolume; [size: volumeSize, free: volumeFree] _ File.LogicalInfo[volume]; VAMStatsStop _ FALSE; TRUSTED { FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: TRUE, work: NowPage]; }; cmd.out.PutF[" Volume %g is %s pages with %g pages free in the VAM\n", IO.rope[argv[1]], IO.int[volumeSize], IO.int[volumeFree] ]; IF badStatus > 0 THEN cmd.out.PutF[" %g pages had read errors\n", IO.int[badStatus]]; cmd.out.PutF[" Headers %g (%g free in VAM), Data %g (%g free in VAM) \n", IO.int[headers], IO.int[freeHeaders], IO.int[datas], IO.int[freeDatas] ]; cmd.out.PutF[" Others %g (%g free in VAM), Free %g (%g not free in VAM) \n", IO.int[others], IO.int[freeOthers], IO.int[frees], IO.int[freeMissings] ]; EXITS Failed => RETURN[$Failure, msg]; Usage => RETURN[$Failure, " Usage: VAMStats volumename\n"]; NoVolume => RETURN[$Failure, " No such volume\n"]; }; StopVAMStats: Commander.CommandProc = { VAMStatsStop _ TRUE; }; ComputeVAMStop: BOOL _ FALSE; ComputeVAMProc: Commander.CommandProc = { NowPage: FileInternal.EnumeratePagesProc = TRUSTED { <> 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 => { 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]; }; }; exit _ ComputeVAMStop; }; argv: CommandTool.ArgumentVector; volume: File.Volume; volumeFree: INT; volumeSize: INT; badStatus: INT _ 0; headers, freeHeaders, datas, freeDatas, freeMissings, frees, others, freeOthers: INT _ 0; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN GOTO Failed; IF argv.argc # 2 THEN GOTO Usage; volume _ File.FindVolumeFromName[argv[1]]; IF volume = NIL THEN GOTO NoVolume; [size: volumeSize, free: volumeFree] _ File.LogicalInfo[volume]; ComputeVAMStop _ FALSE; TRUSTED { FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: TRUE, work: NowPage]; }; cmd.out.PutF[" Volume %g is %s pages with %g pages free in the VAM\n", IO.rope[argv[1]], IO.int[volumeSize], IO.int[volumeFree] ]; IF badStatus > 0 THEN cmd.out.PutF[" %g pages had read errors\n", IO.int[badStatus]]; cmd.out.PutF[" Headers %g (%g free in VAM), Data %g (%g free in VAM) \n", IO.int[headers], IO.int[freeHeaders], IO.int[datas], IO.int[freeDatas] ]; cmd.out.PutF[" Others %g (%g free in VAM), Free %g (%g not free in VAM) \n", IO.int[others], IO.int[freeOthers], IO.int[frees], IO.int[freeMissings] ]; EXITS Failed => RETURN[$Failure, msg]; Usage => RETURN[$Failure, " Usage: ComputeVAM volumename\n"]; NoVolume => RETURN[$Failure, " No such volume\n"]; }; StopComputeVAMProc: Commander.CommandProc = { ComputeVAMStop _ TRUE; }; Handle: TYPE = REF Object; --File.--Object: PUBLIC TYPE = FileInternal.Object; --DiskFace.--RelID: PUBLIC TYPE = VolumeFormat.RelID; DeleteOrphanPagesStop: BOOL _ FALSE; FPRec: TYPE = RECORD [fp: File.FP]; OpenFileRec: TYPE = RECORD [ key: REF FPRec, file: FileInternal.Handle _ NIL ]; getKey: RedBlackTree.GetKey = { <> myData: REF OpenFileRec = NARROW[data]; RETURN[myData.key]; }; compareKeys: RedBlackTree.Compare = { <> 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; }; DeleteOrphanPagesProc: Commander.CommandProc = { openFileTable: RedBlackTree.Table _ NIL; { ENABLE UNWIND => { IF openFileTable # NIL THEN RedBlackTree.DestroyTable[openFileTable]; }; NowPage: FileInternal.EnumeratePagesProc = TRUSTED { <> processPage: PROC [where: FileInternal.WhereLocation] = TRUSTED -- INLINE -- { file: FileInternal.Handle _ NIL; relid: RelID = label.fileID.relID; diskPageFromFile: Disk.PageNumber; IF lastFile # NIL THEN { IF lastFileFP = relid THEN file _ lastFile; }; IF file = NIL THEN { openFP: File.FP _ relid; dataFound: RedBlackTree.UserData; key: REF FPRec _ NEW[FPRec _ [openFP]]; dataFound _ RedBlackTree.Lookup[self: openFileTable, lookupKey: key]; IF dataFound = NIL THEN { insertData: REF OpenFileRec; 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 { insertData _ NEW [OpenFileRec _ [key, file]]; RedBlackTree.Insert[self: openFileTable, dataToInsert: insertData, insertKey: key]; }; } ELSE { openFileData: REF OpenFileRec = NARROW[dataFound]; file _ openFileData.file; }; lastFileFP _ openFP; lastFile _ file; }; IF file # NIL THEN [diskPage: diskPageFromFile] _ FileInternal.FindRun[ start: IF where = header THEN [-file.logicalRunTable.headerPages+label.filePage] ELSE [label.filePage], nPages: 1, runTable: file.runTable ]; IF (file = NIL) OR (diskPageFromFile # diskPage) THEN { freedPages _ freedPages + 1; FileInternal.FreeRun[logicalRun: [da, 1], volume: volume, verifyLabel: NIL]; }; }; 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 => { processPage[header]; }; attr = data => { processPage[data]; }; attr = freePage => { }; ENDCASE => { }; exit _ DeleteOrphanPagesStop; }; argv: CommandTool.ArgumentVector; volume: File.Volume; volumeFree: INT; volumeSize: INT; badStatus: INT _ 0; lastFileFP: File.FP ; lastFile: FileInternal.Handle _ NIL; freedPages: INT _ 0; pageCounter: INT _ 0 ; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN GOTO Failed; IF argv.argc # 2 THEN GOTO Usage; volume _ File.FindVolumeFromName[argv[1]]; IF volume = NIL THEN GOTO NoVolume; [size: volumeSize, free: volumeFree] _ File.LogicalInfo[volume]; DeleteOrphanPagesStop _ FALSE; openFileTable _ RedBlackTree.Create[getKey: getKey, compare: compareKeys]; cmd.out.PutRope["Starting disk scan. A dot is printed for every 1000 pages\n"]; TRUSTED { FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: TRUE, work: NowPage]; }; cmd.out.PutF["\nFreed %g pages.\n", IO.int[freedPages]]; RedBlackTree.DestroyTable[openFileTable]; EXITS Failed => RETURN[$Failure, msg]; Usage => RETURN[$Failure, " Usage: DeleteOrphanPages volumename\n"]; NoVolume => RETURN[$Failure, " No such volume\n"]; }; }; StopDeleteOrphanPagesProc: Commander.CommandProc = { DeleteOrphanPagesStop _ TRUE; }; <> CHECKED BEGIN Commander.Register[key: "VAMStats", proc: PrintVAMStats, doc: "Compute and show some accuracy statistics on the VAM for the given volume"]; Commander.Register[key: "StopVAMStats", proc: StopVAMStats, doc: "Stop VAMStats early"]; Commander.Register[key: "ComputeVAM", proc: ComputeVAMProc, doc: "Compute VAM from disk labels for the given volume"]; Commander.Register[key: "StopComputeVAM", proc: StopComputeVAMProc, doc: "Stop Compute VAM from disk labels"]; Commander.Register[key: "DeleteOrphanPages", proc: DeleteOrphanPagesProc, doc: "Mark pages as free that are not in any file, or are not in the file they claim to be in"]; Commander.Register[key: "StopDeleteOrphanPages", proc: StopDeleteOrphanPagesProc, doc: "Stop Delete Orphan Pages"]; END; END. <<>>