FileUtil.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Utilities for the File Package
Last Edited by:
Bob Hagmann, June 20, 1985 8:58:33 am PDT
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: BOOLFALSE;
PrintVAMStats: Commander.CommandProc = {
NowPage: FileInternal.EnumeratePagesProc = TRUSTED {
PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label] RETURNS[exit: BOOLFALSE];
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: BOOLFALSE;
ComputeVAMProc: Commander.CommandProc = {
NowPage: FileInternal.EnumeratePagesProc = TRUSTED {
PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label] RETURNS[exit: BOOLFALSE];
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: BOOLFALSE;
FPRec: TYPE = RECORD [fp: File.FP];
OpenFileRec: TYPE = RECORD [
key: REF FPRec,
file: FileInternal.Handle ← NIL
];
getKey: RedBlackTree.GetKey = {
PROC [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key];
myData: REF OpenFileRec = NARROW[data];
RETURN[myData.key];
};
compareKeys: RedBlackTree.Compare = {
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;
};
DeleteOrphanPagesProc: Commander.CommandProc = {
openFileTable: RedBlackTree.Table ← NIL;
{
ENABLE UNWIND => {
IF openFileTable # NIL THEN RedBlackTree.DestroyTable[openFileTable];
};
NowPage: FileInternal.EnumeratePagesProc = TRUSTED {
PROC[status: Disk.Status, da: VolumeFormat.LogicalPage, label: POINTER TO Disk.Label, diskPage: INT] RETURNS[exit: BOOLFALSE];
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;
};
Start code
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.