-- ScavengeImpl.mesa (last edited by: Gobbel on: 20-Mar-81 11:25:27)
-- This is a cut at the interim Scavenger
DIRECTORY
DiskChannel USING [CompletionStatus, GetAttributes],
Environment USING [bitsPerWord, wordsPerPage],
File USING [
Capability, Create, delete, Delete, DeleteImmutable, Error, GetAttributes, ID, lastPageNumber,
MakeImmutable, MakePermanent, nullCapability, nullID, PageCount, PageNumber,
read, SetSize, Type, Unknown, write],
FileInternal USING [Descriptor, FilePtr, PageGroup],
FMPrograms USING [],
Inline USING [LongCOPY, LowHalf],
KernelFile USING [GetNextFile, GetRootFile],
LabelTransfer USING [ReadLabel, ReadLabelAndData, WriteLabelAndData, WriteLabels, VerifyLabels],
LogicalVolume USING [
BeginScavenging, EndScavenging, Free, FreeVolumePages, Handle, nullVolumePage, PageNumber,
PutRootFile, ReadOnlyVolume, rootPageNumber, Vam, VolumeAccess, VolumeAccessProc,
VolumeAccessStatus],
MarkerPage USING [Find],
PhysicalVolume USING [Error, GetContainingPhysicalVolume, MarkPageBad],
PhysicalVolumeFormat USING [Handle, maxBadPages, PageNumber, rootPageNumber],
PilotDisk USING [GetLabelFilePage, Label, nullLabel],
PilotFileTypes USING [
PilotVFileType, tFreePage, tScavengerLog, tScavengerLogOtherVolume, tUnassigned,
tVolumeAllocationMap, tVolumeFileMap],
PilotSwitches USING [switches],
Runtime USING [CallDebugger],
Scavenger USING [Error, ErrorType, FileEntry, Header, Problem],
SimpleSpace USING [Create, Handle, LongPointer, Map, Page, Unmap],
Space USING [defaultWindow, Handle, PageNumber, WindowOrigin],
SubVolume USING [Find, Handle],
System USING [GetGreenwichMeanTime],
SystemInternal USING [Unimplemented],
Utilities USING [LongPointerFromPage, PageFromLongPointer],
VolAllocMap USING [AccessVAM, AllocPageGroup, Close],
VolFileMap USING [Close, GetPageGroup, InitMap, InsertPageGroup],
Volume USING [
Close, GetNext, GetStatus, GetType, ID, InsufficientSpace, NotOpen, nullID, PageCount, systemID,
Type, TypeSet, Unknown],
VolumeInternal USING [PageNumber];
ScavengeImpl: MONITOR
IMPORTS
DiskChannel, File, Inline, KernelFile, LabelTransfer, LogicalVolume,
MarkerPage, PhysicalVolume, PilotDisk, PilotSwitches, Runtime, Scavenger, SimpleSpace,
System, SystemInternal, SubVolume, Utilities, VolAllocMap, VolFileMap, Volume
EXPORTS LogicalVolume, FMPrograms, Scavenger
SHARES File =
BEGIN
-- NOTE !!!!
-- The following constant definitions of dataError and labelError should be deleted once the real
-- definitions are added to CompletionStatus in DiskChannel.mesa in the next build. (21-Jan-81)
-- Deleted (27-Feb-81)
-- NOTE !!!!
-- The following constant definitions of diskHardwareError and diskNotReady should be deleted once the
-- real definitions are added to ErrorType in Scavenger.mesa in the next build. (21-Jan-81)
-- Deleted (27-Feb-81)
bitsPerPage: CARDINAL = Environment.bitsPerWord*Environment.wordsPerPage;
BadPageList: TYPE = RECORD [
p: POINTER TO PageNumber,
a: ARRAY [0..PhysicalVolumeFormat.maxBadPages] OF PageNumber];
OrphanHandle: PUBLIC TYPE = LogicalVolume.PageNumber;
PageNumber: TYPE = LogicalVolume.PageNumber;
FileProblem: TYPE = RECORD [
fileID: File.ID,
problem: Scavenger.Problem];
problemSpacePages: CARDINAL = 1;
problemArraySize: CARDINAL = (problemSpacePages * Environment.wordsPerPage)/SIZE[FileProblem];
ProblemArray: TYPE = ARRAY [0..problemArraySize) OF FileProblem;
PageInterval: TYPE = RECORD [
startingPage, nextStartingPage: File.PageNumber];
IndexOfContexts: TYPE = [0..1);
Contexts: ARRAY IndexOfContexts OF ScavengeRecord;
freeContext: CONDITION;
ImpossibleScavengerError: PRIVATE ERROR [
{cantFindSubvolume, noRoomForVam, pageAlreadyBusy, labelChanged}] = CODE;
-- The following implement the Scavenger interface. . .
Error: PUBLIC ERROR [Scavenger.ErrorType] = CODE;
DeleteLog: PUBLIC PROC [volume: Volume.ID] =
BEGIN
logFile: File.Capability ← GetLog[volume];
IF logFile=File.nullCapability THEN RETURN;
DeleteLogFile[logFile];
logFile ← File.nullCapability;
LogicalVolume.PutRootFile[@volume, PilotFileTypes.tScavengerLog, @logFile];
END;
DeleteOrphanPage: PUBLIC PROC [volume: Volume.ID, id: OrphanHandle] =
BEGIN
MakeFreePageProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
IF FixFreePage[id, volume] THEN [] ← VolAllocMap.AccessVAM[volume, id, FALSE, TRUE]
ELSE ReallyMarkPageBad[id, svH, volume];
END;
found: BOOLEAN;
svH: SubVolume.Handle;
SELECT Volume.GetStatus[volume] FROM
open => NULL;
unknown => ERROR Volume.Unknown[volume];
ENDCASE => ERROR Volume.NotOpen[volume];
[found, svH] ← SubVolume.Find[volume, id];
IF ~found THEN ERROR Scavenger.Error[orphanNotFound];
SELECT LogicalVolume.VolumeAccess[@volume, MakeFreePageProc, TRUE] FROM
ok => NULL;
volumeReadOnly => ERROR LogicalVolume.ReadOnlyVolume;
ENDCASE => ERROR;
END;
GetLog: PUBLIC PROC [volume: Volume.ID] RETURNS [logFile: File.Capability] =
{RETURN[KernelFile.GetRootFile[PilotFileTypes.tScavengerLog, volume]]};
ReadBadPage: PUBLIC PROC [file: File.ID, page: File.PageNumber, destination: Space.PageNumber] =
BEGIN
fileD: FileInternal.Descriptor ← [file, Volume.nullID, local[, , , ]];
fileP: FileInternal.FilePtr = @fileD;
found: BOOLEAN;
group: FileInternal.PageGroup;
volumePage: LogicalVolume.PageNumber;
IF ~SpecialGetFileDescriptor[fileP] THEN ERROR File.Unknown[[file, File.read]];
[found, group] ← FindContainingPageGroup[fileP, page];
IF ~found THEN ERROR File.Unknown[[file, File.read]];
IF (group.filePage = group.nextFilePage) --page beyond end of file
OR (group.volumePage = LogicalVolume.nullVolumePage) --page doesn't exist-- THEN
ERROR Scavenger.Error[noSuchPage];
volumePage ← group.volumePage + (page - group.filePage);
[] ← VerifyPageLabel[fileD, page, volumePage];
IF ReadPage[fileD, page, volumePage, destination, FALSE].status = labelError THEN
DO Runtime.CallDebugger["Unrecoverable disk error: labelError"L]; ENDLOOP;
END;
ReadOrphanPage: PUBLIC PROC [volume: Volume.ID, id: OrphanHandle, destination: Space.PageNumber]
RETURNS [file: File.ID, type: File.Type, pageNumber: File.PageNumber,readErrors: BOOLEAN] =
BEGIN
fileD: FileInternal.Descriptor ← [File.nullID, volume, local[, , , ]];
label: PilotDisk.Label;
status: DiskChannel.CompletionStatus;
SELECT Volume.GetStatus[volume] FROM
open => NULL;
unknown => ERROR Volume.Unknown[volume];
ENDCASE => ERROR Volume.NotOpen[volume];
IF ~SubVolume.Find[volume, id].success THEN ERROR Scavenger.Error[orphanNotFound];
[label, status] ← ReadPage[fileD, 0, id, destination, FALSE];
SELECT status FROM
labelError => -- expect this status since orphan pages are pages with label checksum errors
RETURN[File.nullID, PilotFileTypes.tUnassigned, 0, TRUE];
goodCompletion, dataError => -- label should be valid in this case
RETURN[label.fileID, label.type, PilotDisk.GetLabelFilePage[@label], status # goodCompletion];
ENDCASE => -- should never get this case -- ERROR;
END;
RewritePage: PUBLIC PROC [file: File.ID, page: File.PageNumber, source: Space.PageNumber] =
BEGIN
fileD: FileInternal.Descriptor ← [file, Volume.nullID, local[, , , ]];
fileP: FileInternal.FilePtr = @fileD;
found: BOOLEAN;
group: FileInternal.PageGroup;
missingPage: BOOLEAN ← FALSE;
IF ~SpecialGetFileDescriptor[fileP] THEN ERROR File.Unknown[[file, File.read + File.write]];
WITH fileD SELECT FROM
local => IF immutable THEN ERROR File.Error[immutable];
ENDCASE => ERROR; -- can't handle remote files.
[found, group] ← FindContainingPageGroup[fileP, page];
IF ~found THEN ERROR File.Unknown[[file, File.read + File.write]];
IF (group.filePage = group.nextFilePage) --page beyond end of file-- THEN
ERROR Scavenger.Error[noSuchPage];
IF group.volumePage = LogicalVolume.nullVolumePage THEN missingPage ← TRUE
ELSE missingPage ← RewriteBadPage[fileD, page, group, source];
IF missingPage THEN ReplaceMissingPage[fileP, page, source];
END;
Scavenge: PUBLIC PROC [volume, logDestination: Volume.ID, repair: BOOLEAN]
RETURNS [logFile: File.Capability] =
BEGIN
context: ScavengeContext;
theError: Scavenger.ErrorType;
BEGIN
IF ~repair THEN ERROR SystemInternal.Unimplemented;
context ← OpenScavengeContext[volume];
LogicalVolume.BeginScavenging[pVID: @volume, context: context !
Scavenger.Error => {theError ← error; GOTO AnError}];
DeleteLog[volume];
IF ~CreateLogFile[logDestination, context] THEN {theError ← cannotWriteLog; GOTO AnError};
context.logHeader.repaired ← TRUE;
OpenLogFile[context];
[] ← PutWords[context, @context.logHeader, SIZE[Scavenger.Header]];
EnumerateFiles[context];
PutHeader[context];
CloseLogFile[context];
logFile ← [context.logFile.fID, File.delete + File.read];
LogicalVolume.PutRootFile[@volume, PilotFileTypes.tScavengerLog, @logFile];
IF volume=logDestination THEN
{File.MakePermanent[logFile]; File.MakeImmutable[logFile]};
CloseScavengeContext[context];
-- The following two statements are a hack to get around the following problem:
-- When we created the log file above and deleted the old log file, we caused
-- FileImpl to fill its tmpsFile cache. The closes are necessary to clear those
-- caches. It is known to be safe to do because VolumeImpl 1) permits a closed
-- volume to be closed again without error, and 2) is only called after FileImpl
-- has cleared its caches (i.e., error checking is done after FileImpl has
-- done its thing).
Volume.Close[volume ! Volume.Unknown => CONTINUE];
LogicalVolume.EndScavenging[@volume];
EXITS
AnError =>
{LogicalVolume.EndScavenging[@volume];
CloseScavengeContext[context];
ERROR Scavenger.Error[theError]};
END;
END;
-- The following implement (part of) the LogicalVolume interface. . .
-- ScavengeContext/ScavengeRecord are here so that multiple scavenges can be going on at once
-- someday. This will occur when VolumeImpl has been modified and IndexOfContexts is changed
-- appropriately.
ScavengeContext: TYPE = POINTER TO ScavengeRecord;
ScavengeRecord: PUBLIC TYPE = RECORD [
occupied: BOOLEAN,
logHeader: Scavenger.Header,
volume: Volume.ID,
buffer: Space.Handle, -- Initialize only
bufferPointer: LONG POINTER, -- Initialize only
problemCount: CARDINAL,
problemSpace: Space.Handle, -- Initialize only
problem: LONG POINTER TO ProblemArray, -- Initialize only
problemIncomplete: BOOLEAN,
logFile: File.Capability,
nextWord: CARDINAL, -- words
fileLengthPages: File.PageCount];
EraseVolume: PUBLIC PROC [
vol: LogicalVolume.Handle, space: SimpleSpace.Handle] =
BEGIN
context: ScavengeContext ← OpenScavengeContext[vol.vID];
ScavengeVolumeInternal[vol, space, TRUE, context];
CloseScavengeContext[context];
END;
ScavengeVolume: PUBLIC PROC [
vol: LogicalVolume.Handle, space: SimpleSpace.Handle, context: ScavengeContext] =
BEGIN
ScavengeVolumeInternal[vol, space, FALSE, context];
END;
-- Internal procs
AdvanceBadPagePointer: PROC [l: POINTER TO BadPageList] = INLINE
{l.p ← l.p + SIZE[PageNumber]};
CloseLogFile: PROC [context: ScavengeContext] =
{SimpleSpace.Unmap[context.buffer]};
CloseScavengeContext: ENTRY PROC [context: ScavengeContext] =
BEGIN
IF context.problemCount # 0 THEN SimpleSpace.Unmap[context.problemSpace];
context.occupied ← FALSE;
NOTIFY freeContext;
END;
CountProblems: PROC [fileID: File.ID, context: ScavengeContext] RETURNS [count: CARDINAL] =
BEGIN
hole: PageInterval ← [0, 0];
count ← 0;
-- Count the unreadable and orphan Problems
FOR i: CARDINAL IN [0..context.problemCount) DO
IF context.problem[i].fileID = fileID THEN count ← count + 1;
ENDLOOP;
-- Count the missing page group Problems (i.e., holes in files)
-- (Only real files have holes; File.nullID is the ID given for orphan pages.)
IF fileID # File.nullID THEN
DO
hole ← FindNextHole[fileID, context, hole];
IF hole.nextStartingPage = 0 THEN EXIT;
count ← count + 1;
ENDLOOP;
END;
CreateLogFile: PROC [logDestination: Volume.ID, context: ScavengeContext]
RETURNS [success: BOOLEAN] =
BEGIN
context.logFile ← File.Create[
logDestination, 1,
IF logDestination=context.volume THEN
PilotFileTypes.tScavengerLog ELSE PilotFileTypes.tScavengerLogOtherVolume
! ANY => GO TO Failed]; -- well, any does suggest we can't create the log.....
context.fileLengthPages ← 1;
RETURN[TRUE]
EXITS Failed => RETURN[FALSE]
END;
CurrentBadPage: PROC [l: POINTER TO BadPageList] RETURNS [PageNumber] =
INLINE {RETURN[l.p↑]};
DeleteLogFile: PROC [logFile: File.Capability] =
BEGIN
immutable: BOOLEAN;
volume: Volume.ID ← Volume.nullID;
volumeTypeSet: Volume.TypeSet ← ALL[FALSE];
[immutable: immutable] ← File.GetAttributes[logFile ! File.Unknown => GOTO return];
IF ~immutable THEN File.Delete[logFile]
ELSE
BEGIN
IF IsUtilityPilot[] THEN volumeTypeSet ← [normal: TRUE, debugger: TRUE, debuggerDebugger: TRUE]
ELSE
FOR type: Volume.Type IN [normal..Volume.GetType[Volume.systemID]] DO
volumeTypeSet[type] ← TRUE; ENDLOOP;
WHILE (volume ← Volume.GetNext[volume, volumeTypeSet]) # Volume.nullID DO
File.DeleteImmutable[logFile, volume
! Volume.Unknown, Volume.NotOpen, File.Unknown, File.Error => LOOP];
ENDLOOP;
END;
EXITS
return => NULL
END;
EnumerateFiles: PROC [context: ScavengeContext] =
BEGIN
file: File.Capability ← File.nullCapability; -- all orphan pages are associated with File.nullID
context.logHeader.incomplete ← TRUE;
DO
fileEntry: Scavenger.FileEntry;
fileEntry.file ← file.fID;
IF ((fileEntry.numberOfProblems ← CountProblems[file.fID, context]) # 0) OR
(file.fID # File.nullID) THEN
BEGIN
IF ~PutWords[context, @fileEntry, SIZE[Scavenger.FileEntry]] THEN RETURN;
IF fileEntry.numberOfProblems # 0 THEN
IF ~ReportProblems[file.fID, context] THEN RETURN;
context.logHeader.numberOfFiles ← context.logHeader.numberOfFiles + 1;
END;
file ← KernelFile.GetNextFile[context.volume, file];
IF file.fID = File.nullID THEN EXIT;
ENDLOOP;
context.logHeader.incomplete ← context.problemIncomplete;
END;
FindContainingPageGroup: PROC [fileP: FileInternal.FilePtr, page: File.PageNumber]
RETURNS [found: BOOLEAN, group: FileInternal.PageGroup] =
BEGIN
GetPageGroupProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
[found, group] ← VolFileMap.GetPageGroup[volume, fileP, page];
IF ~found THEN -- check for hole at beginning of file
{[success: found] ← VolFileMap.GetPageGroup[volume, fileP, File.lastPageNumber];
IF found THEN group ← [0, LogicalVolume.nullVolumePage, group.nextFilePage]};
END;
getAll: Volume.TypeSet = [normal: TRUE, debugger: TRUE, debuggerDebugger: TRUE];
volumeID: Volume.ID; -- this variable necessary because VolumeAccess requires short pointer
found ← FALSE;
IF (fileP.volumeID # Volume.nullID) AND (Volume.GetStatus[fileP.volumeID] = open) THEN
{volumeID ← fileP.volumeID;
SELECT LogicalVolume.VolumeAccess[@volumeID, GetPageGroupProc] FROM
ok => NULL;
ENDCASE => ERROR;
RETURN};
WHILE ~found AND (fileP.volumeID ← Volume.GetNext[fileP.volumeID, getAll]) # Volume.nullID DO
IF Volume.GetStatus[fileP.volumeID] # open THEN LOOP;
volumeID ← fileP.volumeID;
SELECT LogicalVolume.VolumeAccess[@volumeID, GetPageGroupProc] FROM
ok => NULL;
ENDCASE => ERROR;
ENDLOOP;
END;
FindNextHole: PROC [fileID: File.ID, context: ScavengeContext, hole: PageInterval]
RETURNS [nextHole: PageInterval] =
BEGIN
FindNextHoleProc: -- PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
found: BOOLEAN;
pageGroup: FileInternal.PageGroup;
fileD: FileInternal.Descriptor ← [fileID, context.volume, local[, , , ]];
filePage: File.PageNumber ← hole.nextStartingPage;
updateMarkers ← FALSE;
DO
[found, pageGroup] ← VolFileMap.GetPageGroup[volume, @fileD, filePage];
IF ~found THEN -- this is a hole at the very beginning of the file
{nextHole ← [0, pageGroup.nextFilePage]; RETURN; };
IF pageGroup.filePage = pageGroup.nextFilePage THEN -- normal end of file
{nextHole ← [0, 0]; RETURN; };
IF pageGroup.volumePage = LogicalVolume.nullVolumePage THEN
-- this is a hole in the middle of the file
{nextHole ← [pageGroup.filePage, pageGroup.nextFilePage]; RETURN; };
filePage ← pageGroup.nextFilePage;
ENDLOOP;
END;
SELECT LogicalVolume.VolumeAccess[@context.volume, FindNextHoleProc] FROM
ok => NULL;
ENDCASE => ERROR;
END;
FixFreePage: PROC [page: PageNumber, vol: LogicalVolume.Handle] RETURNS [success: BOOLEAN] =
BEGIN
countValid: File.PageCount;
freeFileD: FileInternal.Descriptor = MakeFreeDescriptor[vol];
retryCount: CARDINAL ← 8;
status: DiskChannel.CompletionStatus;
WHILE retryCount > 0 DO
SELECT LabelTransfer.WriteLabels[freeFileD, [page, page, page + 1], FALSE] FROM
goodCompletion =>
{[countValid, status] ← LabelTransfer.VerifyLabels[
file: freeFileD, pageGroup: [page, page, page + 1],
expectErrorAfterFirstPage: FALSE, handleErrors: FALSE];
IF countValid = 1 AND status = goodCompletion THEN RETURN[TRUE]};
ENDCASE => RETURN[FALSE]; -- write error implies we've already done write retries, so give up
retryCount ← retryCount - 1; -- bad read after "good" write implies we should retry the write
ENDLOOP;
RETURN[FALSE];
END;
GetBadList: PROC [
svH: SubVolume.Handle, space: SimpleSpace.Handle, l: POINTER TO BadPageList] =
BEGIN
RootWindow: PROC RETURNS [Space.WindowOrigin] = INLINE
BEGIN
RETURN[[
[LOOPHOLE[MarkerPage.Find[
[drive[DiskChannel.GetAttributes[svH.channel]]]].physicalID↑],
File.read],
PhysicalVolumeFormat.rootPageNumber]];
END;
lvPage: PageNumber;
p: POINTER TO PageNumber;
pv: PhysicalVolumeFormat.Handle
= Utilities.LongPointerFromPage[SimpleSpace.Page[space]];
l.p ← @l.a[0];
SimpleSpace.Map[space, RootWindow[], FALSE];
FOR i: CARDINAL IN [0..Inline.LowHalf[pv.badPageCount]) DO
-- ASSUMEs no more than LAST[CARDINAL] bad pages
IF pv.badPageList[i] NOT IN [svH.pvPage..svH.pvPage + svH.nPages) THEN LOOP;
lvPage ← (pv.badPageList[i] - svH.pvPage) + svH.lvPage;
p ← l.p; l.p ← l.p + SIZE[PageNumber];
-- Pages are supposed to be ordered; we'll make sure
WHILE p # @l.a[0] AND (p - SIZE[PageNumber])↑ > lvPage DO
p↑ ← (p - SIZE[PageNumber])↑; p ← p - SIZE[PageNumber]; ENDLOOP;
p↑ ← lvPage;
ENDLOOP;
SimpleSpace.Unmap[space];
l.p↑ ← LAST[LONG CARDINAL];
l.p ← @l.a[0];
END;
IsUtilityPilot: PROC RETURNS [BOOLEAN] = INLINE
{RETURN[PilotSwitches.switches.u = down]};
MakeFreeDescriptor: PROC [vol: LogicalVolume.Handle]
RETURNS [FileInternal.Descriptor] = INLINE
BEGIN
RETURN[
[LogicalVolume.Free[vol], vol.vID, local[
FALSE, FALSE, vol.volumeSize, PilotFileTypes.tFreePage]]];
END;
OpenLogFile: PROC [context: ScavengeContext] =
BEGIN
SimpleSpace.Map[context.buffer, [context.logFile, 0], FALSE];
context.nextWord ← 0
END;
OpenScavengeContext: ENTRY PROC [volume: Volume.ID]
RETURNS [context: ScavengeContext] =
BEGIN
DO
-- Until a free context is found
FOR i: IndexOfContexts IN IndexOfContexts DO
IF ~Contexts[i].occupied THEN
BEGIN
context ← @Contexts[i];
context.occupied ← TRUE;
context.logHeader ← [
volume: volume, date: System.GetGreenwichMeanTime[],
incomplete: TRUE, repaired: FALSE, numberOfFiles: 0];
context.volume ← volume;
context.problemCount ← 0;
context.problemIncomplete ← FALSE;
RETURN;
END;
ENDLOOP;
WAIT freeContext;
ENDLOOP
END;
OrphanPage: PROC [page: PageNumber, vol: LogicalVolume.Handle, context: ScavengeContext] =
BEGIN
IF context.problemCount = problemArraySize THEN context.problemIncomplete ← TRUE
ELSE
{IF context.problemCount = 0 THEN SimpleSpace.Map[
context.problemSpace, Space.defaultWindow, TRUE];
{OPEN context.problem[context.problemCount];
fileID ← File.nullID;
problem ← [orphan[id: page]];
context.problemCount ← context.problemCount + 1}};
VamPieceMarkBusy[vol, page, 1];
END;
PutHeader: PROC [context: ScavengeContext] =
BEGIN
SimpleSpace.Unmap[context.buffer];
--(Re)--OpenLogFile[context];
LOOPHOLE[context.bufferPointer,
LONG POINTER TO Scavenger.Header]↑ ← context.logHeader;
END;
PutWords: PROC [context: ScavengeContext, words: LONG POINTER, count: CARDINAL]
RETURNS [success: BOOLEAN] =
-- stream interface to ScavengeLog
BEGIN
THROUGH [0..count) DO
IF context.nextWord=Environment.wordsPerPage THEN
BEGIN
File.SetSize[context.logFile, context.fileLengthPages+1
! Volume.InsufficientSpace => GOTO noRoom];
SimpleSpace.Unmap[context.buffer];
SimpleSpace.Map[context.buffer, [context.logFile, context.fileLengthPages], FALSE];
context.nextWord ← 0;
context.fileLengthPages ← context.fileLengthPages + 1;
END;
(context.bufferPointer+context.nextWord)↑ ← words↑;
context.nextWord ← context.nextWord+1;
words ← words+1;
ENDLOOP;
RETURN[TRUE]
EXITS
noRoom => RETURN[FALSE]
END;
ReadPage: PROC [fileD: FileInternal.Descriptor, page: File.PageNumber, volumePage: PageNumber,
destination: Space.PageNumber, handleErrors: BOOLEAN]
RETURNS [label: PilotDisk.Label, status: DiskChannel.CompletionStatus] =
BEGIN
ReadPageProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
[label, status] ← LabelTransfer.ReadLabelAndData[fileD, page, volumePage,
Utilities.PageFromLongPointer[context.bufferPointer], handleErrors];
END;
context: ScavengeContext;
volStatus: LogicalVolume.VolumeAccessStatus;
context ← OpenScavengeContext[fileD.volumeID];
SimpleSpace.Map[context.buffer, Space.defaultWindow, TRUE];
volStatus ← LogicalVolume.VolumeAccess[@fileD.volumeID, ReadPageProc, FALSE];
Inline.LongCOPY[from: context.bufferPointer, nwords: Environment.wordsPerPage,
to: Utilities.LongPointerFromPage[destination]];
SimpleSpace.Unmap[context.buffer];
CloseScavengeContext[context];
SELECT volStatus FROM
ok => NULL;
ENDCASE => ERROR;
SELECT status FROM
notReady => ERROR Scavenger.Error[diskNotReady];
hardwareError, noSuchPage, seekFailed => ERROR Scavenger.Error[diskHardwareError];
ENDCASE;
END;
ReallyMarkPageBad: PROC [page: PageNumber, svH: SubVolume.Handle, vol: LogicalVolume.Handle] =
BEGIN
PhysicalVolume.MarkPageBad[
pvID: PhysicalVolume.GetContainingPhysicalVolume[svH.lvID],
badPage: LOOPHOLE[page - svH.lvPage + svH.pvPage] !
PhysicalVolume.Error => IF error = badSpotTableFull THEN CONTINUE];
VamMarkBadPage[vol, page];
END;
ReplaceMissingPage: PROC [fileP: FileInternal.FilePtr, page: File.PageNumber,
source: Space.PageNumber] =
BEGIN
FillHoleProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
IF LogicalVolume.FreeVolumePages[volume] <= 1 THEN insufficientSpace ← TRUE
ELSE
{group ← [page, LogicalVolume.nullVolumePage, page + 1]; -- probably should set volumePage
-- to last good page of the file before the hole, but punt that for now.
VolAllocMap.AllocPageGroup[volume, fileP, @group, FALSE];
VolFileMap.InsertPageGroup[volume, fileP, @group]};
END;
group: FileInternal.PageGroup;
insufficientSpace: BOOLEAN ← FALSE;
volumeID: Volume.ID ← fileP.volumeID; -- this variable necessary because VolumeAccess requires a
-- short pointer
SELECT LogicalVolume.VolumeAccess[@volumeID, FillHoleProc, TRUE] FROM
ok => NULL;
volumeReadOnly => ERROR LogicalVolume.ReadOnlyVolume;
ENDCASE => ERROR;
IF insufficientSpace THEN ERROR Volume.InsufficientSpace;
[] ← WritePage[fileP↑, page, group.volumePage, source, TRUE]; -- let Pilot handle any disk errors,
-- since this is an ordinary, healthy page being written.
END;
ReportProblems: PROC [fileID: File.ID, context: ScavengeContext]
RETURNS [sufficientSpace: BOOLEAN] =
BEGIN
hole: PageInterval ← [0, 0];
sufficientSpace ← FALSE;
-- Report the unreadable and orphan Problems
FOR i: CARDINAL IN [0..context.problemCount) DO
IF context.problem[i].fileID = fileID THEN
IF ~PutWords[context, @context.problem[i].problem, SIZE[Scavenger.Problem]] THEN RETURN;
ENDLOOP;
-- Report the missing page group Problems (i.e., holes in files)
-- (Only real files have holes; File.nullID is the ID given for orphan pages.)
IF fileID # File.nullID THEN
DO
problem: Scavenger.Problem;
hole ← FindNextHole[fileID, context, hole];
IF hole.nextStartingPage = 0 THEN EXIT;
problem ← [missing[first: hole.startingPage, count: hole.nextStartingPage - hole.startingPage]];
IF ~PutWords[context, @problem, SIZE[Scavenger.Problem]] THEN RETURN;
ENDLOOP;
sufficientSpace ← TRUE;
END;
RewriteBadPage: PROC [fileD: FileInternal.Descriptor, page: File.PageNumber,
group: FileInternal.PageGroup, source: Space.PageNumber] RETURNS [missingPage: BOOLEAN] =
BEGIN
DeleteBadPageProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
ReallyMarkPageBad[volumePage, svH, volume];
-- Should alter the VFM at this point so that the bad page becomes a hole in the file instead, and
-- then fill the hole with a new page, pretending that the page was missing all along.
-- Can't do it very easily because of peculiarities with VolFileMap.DeletePageGroup (only works
-- correctly for page group at end of file). For the moment, just punt the issue. The next time
-- the scavenger is run, it will report the page as missing rather than unreadable because we have
-- added it to the bad page list, so next time RewritePage should work as well.
DO
Runtime.CallDebugger[
"Unreadable page marked permanently bad; suggest running scavenger again."L];
ENDLOOP;
END;
found: BOOLEAN;
retryCount: CARDINAL ← 8;
svH: SubVolume.Handle;
volumePage: LogicalVolume.PageNumber ← group.volumePage + (page - group.filePage);
missingPage ← FALSE;
[] ← VerifyPageLabel[fileD, page, volumePage];
DO
SELECT WritePage[fileD, page, volumePage, source, FALSE] FROM
goodCompletion =>
IF VerifyPageLabel[fileD, page, volumePage] = goodCompletion THEN EXIT
ELSE -- bad read after "good" write implies we should retry the write
IF ((retryCount ← retryCount - 1) <= 0) THEN {missingPage ← TRUE; EXIT};
ENDCASE => {missingPage ← TRUE; EXIT}; -- write error implies we've already retried, give up
ENDLOOP;
IF missingPage THEN -- rewrite of bad page failed, so get rid of it here and let the missing page
-- code in RewritePage substitute a new page for it. (Doesn't work this way yet (see above).)
{[found, svH] ← SubVolume.Find[fileD.volumeID, volumePage];
IF ~found THEN ERROR Scavenger.Error[noSuchPage];
SELECT LogicalVolume.VolumeAccess[@fileD.volumeID, DeleteBadPageProc, TRUE] FROM
ok => NULL;
volumeReadOnly => ERROR LogicalVolume.ReadOnlyVolume;
ENDCASE => ERROR};
END;
ScavengeVolumeInternal: PROC [
vol: LogicalVolume.Handle, space: SimpleSpace.Handle, erase: BOOLEAN,
context: ScavengeContext] =
BEGIN
IF vol.type = nonPilot THEN RETURN; -- Scavenging non-Pilot Volumes is Easy
vol.changing ← TRUE;
IF erase THEN VamFind[vol, space]; -- FInd n unblemished pages
Vam[vol, space, erase, context];
Vfm[vol, space, erase];
vol.changing ← FALSE;
END;
SpecialGetFileDescriptor: PROC [fileP: FileInternal.FilePtr] RETURNS [found: BOOLEAN] =
-- This version of GetFileDescriptor is Special because:
-- 1. it can find a file which is missing page zero;
-- 2. it knows that a file missing page zero is assumed to be immutable and permanent; and
-- 3. it doesn't fail on a file which has a dataError in page zero.
BEGIN
diskLabelCheck: STRING = "Disk label check"L;
group: FileInternal.PageGroup;
label: PilotDisk.Label;
pageZeroMissing: BOOLEAN;
status: DiskChannel.CompletionStatus;
unrecoverableLabelError: STRING = "Unrecoverable disk error: labelError"L;
fileP.volumeID ← Volume.nullID;
[found, group] ← FindContainingPageGroup[fileP, 0];
IF ~found THEN RETURN;
IF group.volumePage = LogicalVolume.nullVolumePage THEN
{fileP.body ← local[immutable: TRUE, temporary: FALSE, size: , type: ];
pageZeroMissing ← TRUE}
--A file with page zero missing is assumed to be immutable and permanent.
ELSE
{[label, status] ← LabelTransfer.ReadLabel[fileP↑, 0, group.volumePage, FALSE];
SELECT status FROM
goodCompletion, dataError =>
{IF (label.fileID # fileP.fileID) OR (PilotDisk.GetLabelFilePage[@label] # 0) THEN
DO Runtime.CallDebugger[diskLabelCheck]; ENDLOOP;
fileP.body ← local[immutable: label.immutable, temporary: label.temporary, size: ,
type: label.type]};
labelError => DO Runtime.CallDebugger[unrecoverableLabelError]; ENDLOOP;
notReady => ERROR Scavenger.Error[diskNotReady];
ENDCASE => ERROR Scavenger.Error[diskHardwareError]};
[found, group] ← FindContainingPageGroup[fileP, File.lastPageNumber];
IF ~found THEN RETURN;
WITH fileP↑ SELECT FROM
local => size ← group.filePage;
ENDCASE => ERROR;
IF pageZeroMissing THEN -- get the type from the last page group of the file
{[found, group] ← FindContainingPageGroup[fileP, group.filePage - 1];
IF ~found THEN RETURN;
[label, status] ← LabelTransfer.ReadLabel[fileP↑, group.filePage, group.volumePage, FALSE];
SELECT status FROM
goodCompletion, dataError =>
{IF (label.fileID # fileP.fileID) OR (PilotDisk.GetLabelFilePage[@label] # group.filePage) THEN
DO Runtime.CallDebugger[diskLabelCheck]; ENDLOOP;
WITH fileP↑ SELECT FROM
local => type ← label.type;
ENDCASE => ERROR};
labelError => DO Runtime.CallDebugger[unrecoverableLabelError]; ENDLOOP;
notReady => ERROR Scavenger.Error[diskNotReady];
ENDCASE => ERROR Scavenger.Error[diskHardwareError]};
END;
UnreadablePage: PROC [label: POINTER TO PilotDisk.Label, page: PageNumber,
vol: LogicalVolume.Handle, context: ScavengeContext] =
BEGIN
filePage: PageNumber = PilotDisk.GetLabelFilePage[label];
SELECT label.type FROM
PilotFileTypes.tFreePage,
PilotFileTypes.tVolumeFileMap =>
{IF ~FixFreePage[page, vol] THEN VamMarkBadPage[vol, page]};
ENDCASE =>
{FOR i: CARDINAL IN [0..context.problemCount) DO
IF context.problem[i].fileID = label.fileID THEN WITH context.problem[i].problem SELECT FROM
unreadable => SELECT TRUE FROM
(first # 0) AND (filePage = (first - 1)) =>
{first ← filePage; count ← count + 1; EXIT}; -- merge with higher group
filePage = (first + count) => {count ← count + 1; EXIT}; -- merge with lower group
ENDCASE;
ENDCASE;
REPEAT
FINISHED =>
IF context.problemCount = problemArraySize THEN
context.problemIncomplete ← TRUE
ELSE
{IF context.problemCount = 0 THEN SimpleSpace.Map[
context.problemSpace, Space.defaultWindow, TRUE];
{OPEN context.problem[context.problemCount];
fileID ← label.fileID;
problem ← [unreadable[first: filePage, count: 1]];
context.problemCount ← context.problemCount + 1}};
ENDLOOP;
VamPieceMarkBusy[vol, page, 1]};
END;
Vam: PROC [vol: LogicalVolume.Handle, space: SimpleSpace.Handle, erase: BOOLEAN,
context: ScavengeContext] =
BEGIN
found: BOOLEAN;
list: BadPageList;
next: PageNumber;
page: PageNumber ← 1;
svEnd: Volume.PageCount;
svH: SubVolume.Handle;
vamEnd: Volume.PageCount ← vol.vamStart + VamSize[vol];
VolAllocMap.Close[TRUE]; -- Flush any context VamImpl has
VamInit[vol];
WHILE page < vol.volumeSize DO
[found, svH] ← SubVolume.Find[vol.vID, page];
IF ~found THEN ERROR ImpossibleScavengerError[cantFindSubvolume];
GetBadList[svH, space, @list];
svEnd ← svH.lvPage + svH.nPages;
WHILE page < svEnd DO
IF page = CurrentBadPage[@list] THEN
BEGIN
VamMarkBadPage[vol, page];
AdvanceBadPagePointer[@list];
page ← page + 1;
LOOP;
END;
IF page IN [vol.vamStart..vamEnd) THEN BEGIN page ← vamEnd; LOOP; END;
next ← MIN[svEnd, CurrentBadPage[@list]];
IF page < vol.vamStart AND next > vol.vamStart THEN next ← vol.vamStart;
IF erase THEN VamPieceErase[vol, page, next]
ELSE VamPieceRebuild[vol, page, next, context];
page ← next;
ENDLOOP;
ENDLOOP;
END;
-- Find VamSize pages of good contiguous disk (for now we don't cross subvolume boundaries)
VamFind: PROC [vol: LogicalVolume.Handle, space: SimpleSpace.Handle] =
BEGIN
found: BOOLEAN;
list: BadPageList;
page: PageNumber ← 1;
next: PageNumber;
svEnd: Volume.PageCount;
svH: SubVolume.Handle;
vamSize: Volume.PageCount ← VamSize[vol];
WHILE page < vol.volumeSize DO
[found, svH] ← SubVolume.Find[vol.vID, page];
IF ~found THEN ERROR ImpossibleScavengerError[cantFindSubvolume];
GetBadList[svH, space, @list];
svEnd ← svH.lvPage + svH.nPages;
WHILE page < svEnd DO
IF page = CurrentBadPage[@list] THEN
{AdvanceBadPagePointer[@list]; page ← page + 1; LOOP};
next ← MIN[svEnd, CurrentBadPage[@list]];
IF (next - page) > vamSize THEN {vol.vamStart ← page; RETURN};
page ← next;
ENDLOOP;
ENDLOOP;
ERROR ImpossibleScavengerError[noRoomForVam];
END;
VamInit: PROC [vol: LogicalVolume.Handle] =
BEGIN
vamEnd: PageNumber ← vol.vamStart + VamSize[vol];
page: PageNumber;
[] ← LabelTransfer.WriteLabels[
FileInternal.Descriptor[
LogicalVolume.Vam[vol], vol.vID, local[
FALSE, FALSE, vol.volumeSize, PilotFileTypes.tVolumeAllocationMap]],
FileInternal.PageGroup[vol.vamStart, vol.vamStart, vamEnd]];
vol.freePageCount ← vol.volumeSize;
-- Decremented every time a page is marked busy
vol.lowerBound ← vol.volumeSize;
-- we will set this when free pages are found.
[] ← VolAllocMap.AccessVAM[vol, LogicalVolume.rootPageNumber, TRUE, FALSE];
FOR page ← vol.vamStart, page + 1 WHILE page < vamEnd DO
[] ← VolAllocMap.AccessVAM[vol, page, TRUE, FALSE];
ENDLOOP;
END;
VamMarkBadPage: PROC [vol: LogicalVolume.Handle, page: PageNumber] =
{[] ← VolAllocMap.AccessVAM[vol, page, TRUE, FALSE]};
VamPageGroup: PROC [label: POINTER TO PilotDisk.Label, page, next: PageNumber,
vol: LogicalVolume.Handle] RETURNS [pageCount: Volume.PageCount] =
BEGIN
filePage: PageNumber = PilotDisk.GetLabelFilePage[label];
freePageGroup: BOOLEAN ← FALSE;
IF filePage = 0 AND label.zeroSize THEN pageCount ← 1
ELSE [countValid: pageCount] ← LabelTransfer.VerifyLabels[
file: [label.fileID, vol.vID,
local[label.immutable, label.temporary, filePage + (next - page), label.type]],
pageGroup: [filePage, page, filePage + (next - page)],
expectErrorAfterFirstPage: TRUE,
handleErrors: FALSE];
IF pageCount = 0 THEN ImpossibleScavengerError[labelChanged];
IF (label.type = PilotFileTypes.tFreePage AND (filePage # page OR
label.fileID # LogicalVolume.Free[vol])) OR label.type = PilotFileTypes.tVolumeFileMap THEN
{freePageGroup ← TRUE;
[] ← LabelTransfer.WriteLabels[MakeFreeDescriptor[vol], [page, page, page + pageCount]]};
IF (label.type = PilotFileTypes.tFreePage) OR freePageGroup THEN
{IF vol.lowerBound > page THEN vol.lowerBound ← page}
ELSE
VamPieceMarkBusy[vol, page, pageCount];
END;
VamPieceErase: PROC [vol: LogicalVolume.Handle, this, next: PageNumber] =
BEGIN
[] ← LabelTransfer.WriteLabels[MakeFreeDescriptor[vol], [this, this, next]];
-- Pages are marked free in vam; no action needed
IF vol.lowerBound > this THEN vol.lowerBound ← this;
END;
VamPieceMarkBusy: PROC [vol: LogicalVolume.Handle, page: PageNumber,
pageCount: Volume.PageCount] =
BEGIN
WHILE pageCount > 0 --no, a subrange won't work-- DO
IF VolAllocMap.AccessVAM[vol, page, TRUE, FALSE] THEN
ERROR ImpossibleScavengerError[pageAlreadyBusy];
-- can't be already set
page ← page + 1;
pageCount ← pageCount - 1;
ENDLOOP;
END;
VamPieceRebuild: PROC [vol: LogicalVolume.Handle, page, next: PageNumber,
context: ScavengeContext] =
BEGIN
label: PilotDisk.Label;
pageCount: Volume.PageCount;
status: DiskChannel.CompletionStatus;
WHILE page < next DO
[label, status] ← LabelTransfer.ReadLabel[[, vol.vID, local[,,,]], 0, page, FALSE];
SELECT status FROM
goodCompletion =>
{pageCount ← VamPageGroup[@label, page, next, vol]; page ← page + pageCount};
dataError => {UnreadablePage[@label, page, vol, context]; page ← page + 1};
labelError => {OrphanPage[page, vol, context]; page ← page + 1};
notReady => ERROR Scavenger.Error[diskNotReady];
ENDCASE => ERROR Scavenger.Error[diskHardwareError];
ENDLOOP;
END;
VamSize: PROC [vol: LogicalVolume.Handle] RETURNS [Volume.PageCount] =
{RETURN[(vol.volumeSize + bitsPerPage)/bitsPerPage]};
VerifyPageLabel: PROC [fileD: FileInternal.Descriptor, page: File.PageNumber,
volumePage: LogicalVolume.PageNumber] RETURNS [status: DiskChannel.CompletionStatus] =
BEGIN
[status: status] ← LabelTransfer.VerifyLabels[fileD, [page, volumePage, page + 1], FALSE, FALSE];
SELECT status FROM
goodCompletion, dataError => NULL; -- the label matched
labelDoesNotMatch => DO Runtime.CallDebugger["Disk label check"L]; ENDLOOP;
labelError => DO Runtime.CallDebugger["Unrecoverable disk error: labelError"L]; ENDLOOP;
notReady => ERROR Scavenger.Error[diskNotReady];
ENDCASE => ERROR Scavenger.Error[diskHardwareError];
END;
Vfm: PROC [vol: LogicalVolume.Handle, space: SimpleSpace.Handle, erase: BOOLEAN] =
BEGIN
found: BOOLEAN;
list: BadPageList;
next: PageNumber;
page: PageNumber ← 1;
svEnd: Volume.PageCount;
svH: SubVolume.Handle;
VolFileMap.Close[TRUE];
VolFileMap.InitMap[vol];
IF ~erase THEN
WHILE page < vol.volumeSize DO
[found, svH] ← SubVolume.Find[vol.vID, page];
IF ~found THEN ERROR ImpossibleScavengerError[cantFindSubvolume];
GetBadList[svH, space, @list];
svEnd ← svH.lvPage + svH.nPages;
WHILE page < svEnd DO
IF page = CurrentBadPage[@list] THEN
{AdvanceBadPagePointer[@list]; page ← page + 1; LOOP};
next ← MIN[svEnd, CurrentBadPage[@list]];
VfmPieceRebuild[vol, page, next];
page ← next;
ENDLOOP;
ENDLOOP;
END;
VfmPageGroup: PROC [label: POINTER TO PilotDisk.Label, page, next: PageNumber,
vol: LogicalVolume.Handle, status: DiskChannel.CompletionStatus]
RETURNS [pageCount: Volume.PageCount] =
BEGIN
fileD: FileInternal.Descriptor ← [, vol.vID, local[, , , ]];
filePage: File.PageNumber;
group: FileInternal.PageGroup;
IF label.type IN PilotFileTypes.PilotVFileType THEN RETURN[pageCount: 1]; -- "volume" files are not
-- listed in the VFM.
filePage ← PilotDisk.GetLabelFilePage[label];
fileD.fileID ← label.fileID;
SELECT TRUE FROM
filePage = 0 AND label.zeroSize => {group ← [0, page, 0]; pageCount ← 1};
status = dataError => {group ← [filePage, page, filePage + 1]; pageCount ← 1};
-- handle a dataError page as a one-page group.
ENDCASE =>
{[countValid: pageCount] ← LabelTransfer.VerifyLabels[
file: [label.fileID, vol.vID,
local[label.immutable, label.temporary, filePage + (next - page), label.type]],
pageGroup: [filePage, page, filePage + (next - page)],
expectErrorAfterFirstPage: TRUE,
handleErrors: FALSE];
IF pageCount = 0 THEN ImpossibleScavengerError[labelChanged];
group ← [filePage, page, filePage + pageCount]};
VolFileMap.InsertPageGroup[vol, @fileD, @group];
END;
VfmPieceRebuild: PROC [vol: LogicalVolume.Handle, page, next: PageNumber] =
BEGIN
fileD: FileInternal.Descriptor ← [, vol.vID, local[, , , ]];
label: PilotDisk.Label;
pageCount: Volume.PageCount;
status: DiskChannel.CompletionStatus;
WHILE page < next DO
IF ~VolAllocMap.AccessVAM[vol, page, FALSE, FALSE] THEN {page ← page + 1; LOOP};
[label, status] ← LabelTransfer.ReadLabel[fileD, 0, page, FALSE];
SELECT status FROM
goodCompletion, dataError =>
{pageCount ← VfmPageGroup[@label, page, next, vol, status]; page ← page + pageCount};
-- a page with a dataError has a readable label; it must be inserted in the VFM so that it can be
-- found by RewritePage via file ID and file page number; it should have been logged already by
-- VamPieceRebuild.
labelError => {page ← page + 1}; -- orphan page; should have been logged already by
-- VamPieceRebuild.
notReady => ERROR Scavenger.Error[diskNotReady];
ENDCASE => ERROR Scavenger.Error[diskHardwareError];
ENDLOOP;
END;
WritePage: PROC [fileD: FileInternal.Descriptor, page: File.PageNumber, volumePage: PageNumber,
source: Space.PageNumber, handleErrors: BOOLEAN]
RETURNS [status: DiskChannel.CompletionStatus] =
BEGIN
WritePageProc: --PROC [volume: LogicalVolume.Handle] RETURNS [updateMarkers: BOOLEAN] --
LogicalVolume.VolumeAccessProc =
BEGIN
updateMarkers ← FALSE;
status ← LabelTransfer.WriteLabelAndData[
file: fileD,
filePage: page,
volumePage: volumePage,
memoryPage: Utilities.PageFromLongPointer[context.bufferPointer],
bootChainLink: LOOPHOLE[PilotDisk.nullLabel.bootChainLink],
handleErrors: handleErrors];
END;
context: ScavengeContext;
volStatus: LogicalVolume.VolumeAccessStatus;
context ← OpenScavengeContext[fileD.volumeID];
SimpleSpace.Map[context.buffer, Space.defaultWindow, TRUE];
Inline.LongCOPY[from: Utilities.LongPointerFromPage[source], nwords: Environment.wordsPerPage,
to: context.bufferPointer];
volStatus ← LogicalVolume.VolumeAccess[@fileD.volumeID, WritePageProc, TRUE];
SimpleSpace.Unmap[context.buffer];
CloseScavengeContext[context];
SELECT volStatus FROM
ok => NULL;
volumeReadOnly => ERROR LogicalVolume.ReadOnlyVolume;
ENDCASE => ERROR;
SELECT status FROM
notReady => ERROR Scavenger.Error[diskNotReady];
hardwareError, noSuchPage, seekFailed => ERROR Scavenger.Error[diskHardwareError];
ENDCASE;
END;
-- Initialization
Initialize: PROC =
BEGIN
FOR i: IndexOfContexts IN IndexOfContexts DO
context: ScavengeContext;
context ← @Contexts[i];
context.occupied ← FALSE;
context.buffer ← SimpleSpace.Create[1, hyperspace];
context.bufferPointer ← SimpleSpace.LongPointer[context.buffer];
context.problemSpace ← SimpleSpace.Create[problemSpacePages, hyperspace];
context.problem ← LOOPHOLE[SimpleSpace.LongPointer[context.problemSpace]];
ENDLOOP;
END;
Initialize[];
END.....
LOG
Time: October 2, 1979 11:51 AM By: Forrest Action: Created file from Vol*MapImpl.mesa
Time: October 16, 1979 1:35 AM By: Forrest Action: Called Vol*map.close after scavange
Time: November 6, 1979 11:34 AM By: Forrest Action: Fixed VFM scavenge (as examining bogus pages)
Time: November 16, 1979 4:13 PM By: Forrest Action: Added check for non-pilot volumes
Time: November 19, 1979 11:50 AM By: Forrest Action: Added missing ~ to erase in VFM
Time: January 10, 1980 6:29 PM By: Forrest Action: Take advantage of new LabelTransfer interface
Time: March 7, 1980 9:16 PM By: Forrest Action: Change calls to Vol*mapClose
Time: May 29, 1980 10:22 AM By: Luniewski Action: PhysicalVolume => PhysicalVolumeFormat. Exports PhysicalVolume.Error due to compiler bug vis a vis PhysicalVolumeImpl.
Time: June 23, 1980 2:11 PM By: Luniewski Action: Interim implementation of the Scavenger interface. This just enumerates files after a scavenge completes.
Time: July 16, 1980 5:57 PM By: McJones Action: Rewrite FOR loop in EnumerateFiles as plain DO loop to avoid Mesa AR 4956; FilePageLabel=>PilotDisk
Time: August 4, 1980 4:33 PM By: Luniewski Action: Delete old log files whenever possible.
Time: September 17, 1980 2:39 PM By: Luniewski Action: Use SimpleSpace.LongPointer instead of Space.LongPointer.
Time: October 9, 1980 4:09 PM By: Forrest Action: Convert log file access to a stream-type interface.
Time: October 11, 1980 10:10 PM By: Forrest Action: Add ExpectErrorAfterFirstPage parameter to VerifyLabels.
Time: January 12, 1981 1:31 PM By: Luniewski Action: New LabelTransfer interface
Time: February 1, 1981 5:12 PM By: Fay Action: Implement Problem entries and associated
procedures for missing, orphan, and unreadable pages.
Time: February 27, 1981 10:00 AM By: Yokota Action: Temporary LOOPHOLEs are removed
Time: 20-Mar-81 11:21:55 By: Gobbel Action: Fixed bug in Scavenger.RewritePage