-- VolumeImpl.mesa (last edited by: Levin on: August 26, 1982 9:14 am)
DIRECTORY
Boot USING [Location, LVBootFiles, nullDiskFileID, VolumeType],
Device USING [Type],
DiskChannel USING [DiskPageCount, Drive, GetAttributes, GetDriveAttributes],
Environment USING [wordsPerPage],
File USING [
Capability, ID, nullCapability, nullID, PageCount, PageNumber, Permissions,
Type],
FileCache USING [
FlushFile, FlushFilesOnVolume, GetFilePtrs, PinnedAction, SetFile,
SetPageGroup],
FileInternal USING [Descriptor, maxPermissions, PageGroup],
FMPrograms USING [],
Inline USING [LowHalf],
KernelFile USING [],
LabelTransfer USING [ReadLabel, WriteLabels],
LogicalVolume USING [
CloseVolumeAndFlushFiles, currentVersion, EraseVolume, Handle, nullBoot, OpenStatus,
OpenVolumeAndDeleteTemps, PageNumber, ReadOnlyVolume, rootPageNumber,
ScavengeContext, ScavengeVolume, seal, SetFree, SetVam, SetVfm, VolumeAccess,
VolumeAccessProc, VolumeAccessStatus],
MarkerPage USING [CreateMarkerPage, Find, UpdateLogicalMarkerPages],
PhysicalVolume USING [Error, ErrorType, ID, PageNumber],
PhysicalVolumeFormat USING [
descriptorSize, Handle, maxSubVols, PageCount, PageNumber,
rootPageNumber, SubVolumeDesc],
PilotFileTypes USING [
PilotRootFileType, tFreePage, tLogicalVolumeRootPage, tVolumeAllocationMap,
tVolumeFileMap],
PilotDisk USING [GetLabelFilePage, GetLabelType, Label],
PilotSwitches USING [switches--k,u,z--],
Process USING [InitializeCondition, Ticks],
Scavenger USING [Error, ErrorType, Scavenge],
SimpleSpace USING [Create, ForceOut, Handle, Map, Page, Unmap],
SpecialVolume USING [],
SubVolume USING [Find, Handle, OnLine],
System USING [GetUniversalID, NetworkAddress, UniversalID],
Utilities USING [LongPointerFromPage],
VolAllocMap USING [Close],
VolFileMap USING [Close],
Volume USING [
ID, NeedsScavenging, NotOpen, nullID, onlyEnumerateCurrentType, Open,
PageCount, Status, systemID, Type, TypeSet, Unknown],
VolumeExtras USING [],
VolumeImplInterface USING [AccessPhysicalVolumeRootPage],
VolumeInternal USING [PageNumber];
VolumeImpl: MONITOR [ -- to protect activeVolume and LVT
bootFile: LONG POINTER TO disk Boot.Location,
pLVBootFiles: POINTER TO Boot.LVBootFiles,
debuggerDeviceType: POINTER TO Device.Type,
debuggerDeviceOrdinal: POINTER TO CARDINAL]
IMPORTS
DiskChannel, FileCache, PilotDisk, Inline, LabelTransfer, LogicalVolume,
MarkerPage, PhysicalVolume, PilotSwitches, Process, Scavenger, SimpleSpace,
SubVolume, System, Utilities, VolAllocMap, VolFileMap, Volume,
VolumeImplInterface
EXPORTS
FMPrograms, KernelFile, LogicalVolume, SpecialVolume, Volume,
VolumeExtras, VolumeImplInterface
SHARES File =
BEGIN
LvHandle: TYPE = LogicalVolume.Handle;
PvHandle: TYPE = PhysicalVolumeFormat.Handle;
scavengingComplete: CONDITION;
debugClass: Boot.VolumeType;
debugSearchState: {tooSoonToLook, looking, done} ← tooSoonToLook;
lvRootPage: LogicalVolume.PageNumber = LogicalVolume.rootPageNumber;
pvRootPage: PhysicalVolumeFormat.PageNumber =
PhysicalVolumeFormat.rootPageNumber;
-- The following is used as a buffer for the scavenger. It must be large enough to hold
-- the largest possible physical volume root page+bad page list
extraBuffer: SimpleSpace.Handle = SimpleSpace.Create[
PhysicalVolumeFormat.descriptorSize/Environment.wordsPerPage, hyperspace, 1];
-- The "active volume" whose LogicalVolume.Descriptor (root page) is buffered in memory
logicalRootPageBuffer: SimpleSpace.Handle = SimpleSpace.Create[1, hyperspace];
activeVolume: LvHandle = Utilities.LongPointerFromPage[
SimpleSpace.Page[logicalRootPageBuffer]];
activeVID: Volume.ID ← Volume.nullID;
-- active volume (nullID => no active volume)
-- Logical Volume Table (lists logical volumes which are on-line)
LVTHandle: TYPE = --LONG--POINTER TO Volume.ID;
VolumeStatus: TYPE = RECORD [
lvID: Volume.ID,
beingScavenged, onLine, open, readOnly: BOOLEAN,
-- readOnly is a user settable property of the volume. A volume is read-only if readOnly is TRUE OR if the volume is of a "higher" type than the system volume.
type: Volume.Type,
pieceCount: CARDINAL [0..777B)];
nullVolumeStatus: VolumeStatus =
[Volume.nullID, FALSE, FALSE, FALSE, FALSE, nonPilot, 0];
LVT: ARRAY LVTIndex OF VolumeStatus ← ALL[nullVolumeStatus];
LVTIndex: TYPE = [0..maxLVs);
maxLVs: CARDINAL = 12; -- Maximum logical volumes allowed on-line at once
VolImplError: ERROR [{
impossibleSelectError, impossibleSubvolumeNotFound, notFoundInLVT,
lvTableFull, illegalReturnCode}] = CODE;
-- Volume
systemID: PUBLIC Volume.ID ← Volume.nullID;
InsufficientSpace: PUBLIC ERROR = CODE;
NeedsScavenging: PUBLIC ERROR = CODE;
NotOpen: PUBLIC ERROR [volume: Volume.ID] = CODE;
Unknown: PUBLIC ERROR [volume: Volume.ID] = CODE;
Close: PUBLIC PROCEDURE [volume: Volume.ID] =
{ LogicalVolume.CloseVolumeAndFlushFiles[@volume] };
GetAttributes: PUBLIC SAFE PROCEDURE [volume: Volume.ID]
RETURNS [
volumeSize, freePageCount: Volume.PageCount, rootFile: File.Capability] = TRUSTED
BEGIN
GetAttributes1: PROCEDURE [vol: LvHandle] =
BEGIN
volumeSize ← vol.volumeSize;
freePageCount ← vol.freePageCount;
rootFile ← vol.clientRootFile;
END;
GetLogicalRootPage[@volume, GetAttributes1];
END;
GetLabelString: PUBLIC PROCEDURE [volume: Volume.ID, s: STRING] =
BEGIN
badVersion: BOOLEAN;
GetLabelString1: PROCEDURE [vol: LvHandle] =
BEGIN
i: CARDINAL;
s.length ← 0;
IF badVersion ← (vol.version # LogicalVolume.currentVersion) THEN RETURN;
s.length ← MIN[s.maxlength, vol.labelLength, LENGTH[vol.label]];
FOR i IN [0..s.length) DO s[i] ← vol.label[i]; ENDLOOP;
END;
IF s # NIL THEN
BEGIN
GetLogicalRootPage[@volume, GetLabelString1];
IF badVersion THEN ERROR Volume.NeedsScavenging;
END;
END;
GetNext: PUBLIC ENTRY SAFE PROCEDURE [
volume: Volume.ID, includeWhichVolumes: Volume.TypeSet ← []]
RETURNS [Volume.ID] = TRUSTED
BEGIN
volumeFound: BOOLEAN ← volume = Volume.nullID;
lv: LVTIndex;
systemVolumeType: Volume.Type = LVGetStatus[Volume.systemID].type;
IF IsUtilityPilot[] THEN
includeWhichVolumes ← [normal: TRUE, debugger: TRUE, debuggerDebugger: TRUE]
ELSE IF includeWhichVolumes = Volume.onlyEnumerateCurrentType THEN
includeWhichVolumes[systemVolumeType] ← TRUE; -- all others are already FALSE
FOR lv IN LVTIndex DO
IF volumeFound AND LVT[lv].lvID # Volume.nullID
AND LVT[lv].onLine AND includeWhichVolumes[LVT[lv].type] THEN
RETURN[LVT[lv].lvID];
IF LVT[lv].lvID = volume THEN volumeFound ← TRUE;
ENDLOOP;
IF volumeFound THEN RETURN[Volume.nullID]
ELSE RETURN WITH ERROR Volume.Unknown[volume];
END;
GetStatus: PUBLIC ENTRY SAFE PROCEDURE [vID: Volume.ID]
RETURNS [status: Volume.Status] = TRUSTED
BEGIN
onLine, open: BOOLEAN;
IF vID = Volume.nullID OR ~LogicalVolumeFind[@vID] THEN RETURN[unknown];
[onLine: onLine, open: open] ← LVGetStatus[vID];
IF ~onLine THEN status ← partiallyOnLine
ELSE IF open THEN status ← open
ELSE
BEGIN -- only activate when absolutely necessary!
Activate[vID];
IF activeVolume.changing THEN status ← closedAndInconsistent
ELSE status ← closedAndConsistent;
END;
END;
GetType: PUBLIC SAFE PROCEDURE [lvID: Volume.ID] RETURNS [type: Volume.Type] = TRUSTED
BEGIN
GetTypeInner: PROCEDURE [vol: LvHandle] = {type ← vol.type};
GetLogicalRootPage[@lvID, GetTypeInner];
END;
IsOnServer: PUBLIC SAFE PROCEDURE [
volume: Volume.ID, netAddress: System.NetworkAddress] = TRUSTED
{--not implemented--};
Open: PUBLIC SAFE PROCEDURE [volume: Volume.ID] = TRUSTED
BEGIN
DeleteTemps: ENTRY PROCEDURE RETURNS[doDelete: BOOLEAN] = INLINE
BEGIN
IF volume = Volume.nullID OR ~LogicalVolumeFind[@volume] THEN
RETURN WITH ERROR Volume.Unknown[volume];
IF LVGetStatus[volume].type <= debugClass -- type of system volume -- THEN
RETURN[TRUE]
ELSE RETURN[FALSE];
END;
IF ~DeleteTemps[] THEN
SELECT OpenLogicalVolume[@volume] FROM
ok, wasOpen => NULL;
Unknown => ERROR Volume.Unknown[volume];
VolumeNeedsScavenging => ERROR Volume.NeedsScavenging;
ENDCASE => ERROR
ELSE LogicalVolume.OpenVolumeAndDeleteTemps[@volume];
END;
SetRootFile: PUBLIC PROCEDURE [volume: Volume.ID, file: File.Capability] =
BEGIN
SetRootFile1: LogicalVolume.VolumeAccessProc =
BEGIN
volume.clientRootFile ← file;
updateMarkers ← TRUE;
END;
SignalVolumeAccess[@volume, SetRootFile1];
END;
--
-- VolumeExtras
OpenVolume: PUBLIC PROCEDURE [volume: Volume.ID, readOnly: BOOLEAN] =
BEGIN
DeleteTemps: ENTRY PROCEDURE RETURNS[doDelete: BOOLEAN] = INLINE
BEGIN
IF volume = Volume.nullID OR ~LogicalVolumeFind[@volume] THEN
RETURN WITH ERROR Volume.Unknown[volume];
IF readOnly THEN RETURN[FALSE];
IF LVGetStatus[volume].type <= debugClass -- type of system volume -- THEN
RETURN[TRUE]
ELSE RETURN[FALSE];
END;
IF ~DeleteTemps[] THEN
SELECT OpenLogicalVolumeInternal[@volume, readOnly] FROM
ok, wasOpen => NULL;
Unknown => ERROR Volume.Unknown[volume];
VolumeNeedsScavenging => ERROR Volume.NeedsScavenging;
ENDCASE => ERROR
ELSE LogicalVolume.OpenVolumeAndDeleteTemps[@volume];
END;
--
-- SpecialVolume
GetLogicalVolumeBootFiles: PUBLIC PROCEDURE [
lvID: Volume.ID, pBootFiles: LONG POINTER TO Boot.LVBootFiles] =
BEGIN
GetLVBootFiles1: PROCEDURE [vol: LvHandle] =
{ pBootFiles↑ ← vol.bootingInfo };
IF pBootFiles # NIL THEN GetLogicalRootPage[@lvID, GetLVBootFiles1];
END;
SetLogicalVolumeBootFiles: PUBLIC PROCEDURE [
lvID: Volume.ID, pBootFiles: LONG POINTER TO Boot.LVBootFiles] =
BEGIN
SetLVBootFiles1: LogicalVolume.VolumeAccessProc =
BEGIN
volume.bootingInfo ← pBootFiles↑;
updateMarkers ← TRUE;
END;
IF pBootFiles # NIL THEN SignalVolumeAccess[@lvID, SetLVBootFiles1];
END;
--
-- KernelFile
GetRootFile: PUBLIC PROCEDURE [type: File.Type, volume: Volume.ID]
RETURNS [file: File.Capability] =
BEGIN
GetRootFile1: PROCEDURE [vol: LvHandle] = {
file ← [vol.rootFileID[type], FileInternal.maxPermissions]};
IF type NOT IN PilotFileTypes.PilotRootFileType THEN ERROR;
GetLogicalRootPage[@volume, GetRootFile1];
IF file.fID = File.nullCapability.fID THEN file ← File.nullCapability;
END;
PutRootFile: PUBLIC PROCEDURE [
pVID: POINTER TO READONLY Volume.ID, type: File.Type,
file: POINTER TO READONLY File.Capability] =
BEGIN
PutProc: LogicalVolume.VolumeAccessProc =
{ volume.rootFileID[type] ← file.fID; updateMarkers ← TRUE };
IF type IN PilotFileTypes.PilotRootFileType THEN
SignalVolumeAccess[pVID, PutProc];
END;
--
-- LogicalVolume
ReadOnlyVolume: PUBLIC ERROR = CODE;
BeginScavenging: PUBLIC ENTRY PROCEDURE [pVID: POINTER TO READONLY Volume.ID,
context: LogicalVolume.ScavengeContext] =
BEGIN
theError: Scavenger.ErrorType;
BEGIN
IF pVID↑ = Volume.nullID OR ~LogicalVolumeFind[pVID] THEN
RETURN WITH ERROR Unknown[pVID↑];
IF LVGetStatus[pVID↑].open THEN RETURN WITH ERROR Scavenger.Error[volumeOpen];
IF IsReadOnly[pVID↑] THEN RETURN WITH ERROR LogicalVolume.ReadOnlyVolume;
Activate[pVID↑];
activeVolume.changing ← TRUE;
SimpleSpace.ForceOut[logicalRootPageBuffer];
LVSetScavenged[pVID↑, TRUE];
LogicalVolume.ScavengeVolume[activeVolume, extraBuffer, context !
Scavenger.Error => {theError ← error; GOTO ScavengerError}];
VolFileMap.Close[TRUE];
VolAllocMap.Close[TRUE];
activeVolume.changing ← FALSE;
Deactivate[];
EXITS
ScavengerError => RETURN WITH ERROR Scavenger.Error[theError];
END;
END;
CloseLogicalVolume: PUBLIC ENTRY PROCEDURE [
pVID: POINTER TO READONLY Volume.ID] =
BEGIN
IF pVID↑ = Volume.nullID OR ~LogicalVolumeFind[pVID] THEN
RETURN WITH ERROR Unknown[pVID↑];
Activate[pVID↑];
FileCache.FlushFilesOnVolume[pVID↑, keep];
-- Let all I/O on the volume quiesce
VolFileMap.Close[TRUE];
VolAllocMap.Close[TRUE];
FileCache.FlushFilesOnVolume[pVID↑, keep];
-- Wait for any I/O that slipped in through the cracks to finish
LVSetOpen[pVID↑, FALSE];
Deactivate[];
END;
EndScavenging: PUBLIC ENTRY PROCEDURE [pVID: POINTER TO READONLY Volume.ID] =
{ LVSetScavenged[pVID↑, FALSE]; BROADCAST scavengingComplete };
GetContainingPhysicalVolume: PUBLIC PROCEDURE [lvID: Volume.ID]
RETURNS [pvID: PhysicalVolume.ID] =
BEGIN
RETURN[
MarkerPage.Find[
[drive[
DiskChannel.GetAttributes[
SubVolume.Find[lvID, lvRootPage].subVolume.channel]]]].physicalID↑]
END;
LogicalVolumeCreate: PUBLIC ENTRY PROCEDURE [
pvID: PhysicalVolume.ID, size: Volume.PageCount, name: STRING,
type: Volume.Type, minPVPageNumber: PhysicalVolume.PageNumber]
RETURNS [Volume.ID] =
BEGIN
i: CARDINAL;
lvID: Volume.ID ← [System.GetUniversalID[]];
minVolumeSize: PhysicalVolumeFormat.PageCount = 50;
bareVolumeSize: PhysicalVolumeFormat.PageCount = 1 + 1 + 6;
thisSv: CARDINAL;
thisSvStart: PhysicalVolumeFormat.PageNumber;
error: PhysicalVolume.ErrorType;
isError: BOOLEAN ← FALSE;
proc1: PROCEDURE [p: PvHandle] =
BEGIN
goodPages: PhysicalVolumeFormat.PageCount ← 0;
pg: PhysicalVolumeFormat.PageCount;
WordsToPages: PROCEDURE [words: PhysicalVolumeFormat.PageNumber]
RETURNS [PhysicalVolumeFormat.PageNumber] = INLINE
{ RETURN [(words+Environment.wordsPerPage-1)/Environment.wordsPerPage]};
IsBadPage: PROCEDURE [pg: PhysicalVolumeFormat.PageNumber]
RETURNS [BOOLEAN] =
BEGIN
i: CARDINAL;
-- ASSUME that there are <= MAX[CARDINAL] bad pages.
FOR i IN [0..Inline.LowHalf[p.badPageCount]) DO
IF p.badPageList[i] = pg THEN RETURN[TRUE]; ENDLOOP;
RETURN[FALSE];
END;
thisSv ← p.subVolumeCount;
IF (isError ← thisSv >= PhysicalVolumeFormat.maxSubVols) THEN
{ error ← tooManySubvolumes; RETURN; };
IF thisSv # 0 THEN
thisSvStart ←
p.subVolumes[thisSv - 1].pvPage + p.subVolumes[thisSv - 1].nPages + 1
ELSE thisSvStart ← pvRootPage +
WordsToPages[PhysicalVolumeFormat.descriptorSize];
thisSvStart ← MAX[thisSvStart, minPVPageNumber];
WHILE IsBadPage[thisSvStart] DO
thisSvStart ← thisSvStart + 1;
IF (size ← size - 1) = 0 THEN EXIT;
ENDLOOP;
WHILE IsBadPage[thisSvStart + size] DO
IF (size ← size - 1) = 0 THEN EXIT; ENDLOOP;
FOR pg ← thisSvStart, pg + 1 WHILE pg < thisSvStart + size DO
IF ~IsBadPage[pg] THEN -- should really look for contig VAM
IF (goodPages ← goodPages + 1) >= bareVolumeSize THEN RETURN;
ENDLOOP;
error ← subvolumeHasTooManyBadPages;
isError ← TRUE;
END;
proc2: PROCEDURE [p: PvHandle] =
BEGIN
p.subVolumes[thisSv] ← [lvID, size, 0, thisSvStart, size];
p.subVolumeCount ← thisSv + 1;
MarkerPage.CreateMarkerPage[p, activeVolume, thisSv];
END;
IF name = NIL OR name.length = 0 THEN
RETURN WITH ERROR PhysicalVolume.Error[nameRequired];
IF size < minVolumeSize THEN
RETURN WITH ERROR PhysicalVolume.Error[pageCountTooSmallForVolume];
IF ~SubVolume.Find[LOOPHOLE[pvID], pvRootPage].success THEN
RETURN WITH ERROR PhysicalVolume.Error[physicalVolumeUnknown];
VolumeImplInterface.AccessPhysicalVolumeRootPage[pvID, proc1];
-- Can not raise any errors due to check in previous statement
IF isError THEN RETURN WITH ERROR PhysicalVolume.Error[error];
IF thisSvStart + size >= DriveSize[pvID] THEN
RETURN WITH ERROR PhysicalVolume.Error[insufficientSpace];
RegisterLogicalSubvolume[[lvID, size, 0, thisSvStart, size], pvID];
[] ← LabelTransfer.WriteLabels[
[LOOPHOLE[lvID], lvID, local[
FALSE, FALSE, 1, PilotFileTypes.tLogicalVolumeRootPage]],
[lvRootPage, lvRootPage, lvRootPage + 1]];
Activate[lvID];
activeVolume↑ ← [vID: lvID, type: type, volumeSize: size];
LogicalVolume.SetFree[activeVolume, [System.GetUniversalID[]]];
LogicalVolume.SetVam[activeVolume, [System.GetUniversalID[]]];
LogicalVolume.SetVfm[activeVolume, [System.GetUniversalID[]]];
activeVolume.labelLength ← MIN[name.length, LENGTH[activeVolume.label]];
FOR i IN [0..activeVolume.labelLength) DO
activeVolume.label[i] ← name[i]; ENDLOOP;
SimpleSpace.ForceOut[logicalRootPageBuffer];
EnterLV[lvID];
LogicalVolumeCheck[lvID];
IF type # nonPilot THEN
LogicalVolume.EraseVolume[activeVolume, extraBuffer];
SimpleSpace.ForceOut[logicalRootPageBuffer];
VolumeImplInterface.AccessPhysicalVolumeRootPage[pvID, proc2, readWrite];
-- Can not raise any errors due to check in previous statement
VolFileMap.Close[TRUE];
VolAllocMap.Close[TRUE];
activeVolume.changing ← FALSE;
Deactivate[];
RETURN[lvID];
END;
LogicalVolumeErase: PUBLIC ENTRY PROCEDURE [lvID: Volume.ID] =
BEGIN
-- May be performed on an Open volume! (in which case, the volume is left Open)
t: PilotFileTypes.PilotRootFileType;
IF lvID = Volume.nullID OR ~LogicalVolumeFind[@lvID] THEN
RETURN WITH ERROR Unknown[lvID];
IF IsReadOnly[lvID] THEN RETURN WITH ERROR LogicalVolume.ReadOnlyVolume;
Activate[lvID];
activeVolume.changing ← TRUE;
FOR t IN
[FIRST[PilotFileTypes.PilotRootFileType]..
LAST[PilotFileTypes.PilotRootFileType]] DO
SELECT t FROM
PilotFileTypes.tVolumeAllocationMap, PilotFileTypes.tVolumeFileMap,
PilotFileTypes.tFreePage => LOOP;
ENDCASE => activeVolume.rootFileID[t] ← File.nullID;
ENDLOOP;
activeVolume.clientRootFile ← File.nullCapability;
activeVolume.bootingInfo ← LogicalVolume.nullBoot;
SimpleSpace.ForceOut[logicalRootPageBuffer];
LogicalVolume.EraseVolume[activeVolume, extraBuffer];
VolFileMap.Close[TRUE];
VolAllocMap.Close[TRUE];
activeVolume.changing ← FALSE;
Deactivate[];
END;
OpenLogicalVolume: PUBLIC PROCEDURE [pVID: POINTER TO READONLY Volume.ID]
RETURNS [LogicalVolume.OpenStatus] =
{ RETURN[OpenLogicalVolumeInternal[pVID, FALSE]]; };
VolumeAccess: PUBLIC ENTRY PROCEDURE [
pVID: POINTER TO READONLY Volume.ID, proc: LogicalVolume.VolumeAccessProc,
modify: BOOLEAN]
RETURNS [LogicalVolume.VolumeAccessStatus] =
BEGIN
open, beingScavenged, updateMarkers: BOOLEAN;
IF pVID↑ = Volume.nullID OR ~LogicalVolumeFind[pVID] THEN
RETURN[volumeUnknown];
IF modify AND IsReadOnly[pVID↑] THEN RETURN[volumeReadOnly];
Activate[pVID↑];
-- Only allow access if the volume is open or being scavenged.
-- The being scavenged option should only be exercised by the Scavenger.
[open: open, beingScavenged: beingScavenged] ← LVGetStatus[pVID↑];
IF ~(open OR beingScavenged) THEN
{ Deactivate[]; RETURN[volumeNotOpen]; };
IF modify THEN
BEGIN
activeVolume.changing ← TRUE;
SimpleSpace.ForceOut[logicalRootPageBuffer];
END;
updateMarkers ← proc[activeVolume];
IF ~modify THEN RETURN[ok];
IF updateMarkers THEN MarkerPage.UpdateLogicalMarkerPages[activeVolume];
activeVolume.changing ← FALSE;
Deactivate[];
RETURN[ok];
END;
--
-- VolumeImplInterface
CheckLogicalVolume: PUBLIC ENTRY PROCEDURE [vID: Volume.ID] = {
LogicalVolumeCheck[vID]; Deactivate[]};
FindLogicalVolume: PUBLIC ENTRY PROCEDURE [vID: POINTER TO READONLY Volume.ID]
RETURNS [BOOLEAN] = {RETURN[LogicalVolumeFind[vID]]};
GetLVStatus: PUBLIC ENTRY PROCEDURE [vID: Volume.ID]
RETURNS [onLine, open: BOOLEAN] = {[open, onLine] ← LVGetStatus[vID]};
OpenInitialVolumes: PUBLIC PROCEDURE =
BEGIN
volumeID: Volume.ID;
GetNextOnLineVolume: ENTRY PROCEDURE [volume: Volume.ID]
RETURNS [Volume.ID] =
BEGIN
volumeFound: BOOLEAN ← volume = Volume.nullID;
lv: LVTIndex;
FOR lv IN LVTIndex DO
IF volumeFound AND LVT[lv].lvID # Volume.nullID AND LVT[lv].onLine
AND LVT[lv].type = debugClass THEN RETURN[LVT[lv].lvID];
IF LVT[lv].lvID = volume THEN volumeFound ← TRUE;
ENDLOOP;
IF volumeFound THEN RETURN[Volume.nullID]
ELSE RETURN WITH ERROR Volume.Unknown[volume];
END;
IF IsUtilityPilot[] THEN RETURN;
FOR volumeID ← GetNextOnLineVolume[Volume.nullID],
GetNextOnLineVolume[volumeID] UNTIL volumeID = Volume.nullID DO
IF volumeID=systemID OR PilotSwitches.switches.k=up THEN
Volume.Open[volumeID ! Volume.NeedsScavenging =>
{[] ← Scavenger.Scavenge[volumeID, volumeID, TRUE]; RETRY}];
ENDLOOP;
END;
PinnedFileEnter: PUBLIC PROCEDURE [
fd: FileInternal.Descriptor, grp: FileInternal.PageGroup] =
BEGIN
IF ~FileCache.GetFilePtrs[0, fd.fileID].success THEN {
FileCache.SetFile[fd, TRUE]; FileCache.SetPageGroup[fd.fileID, grp, TRUE]};
END;
PinnedFileFlush: PUBLIC PROCEDURE [file: File.ID] = {FileCache.FlushFile[file]};
RegisterLogicalSubvolume: PUBLIC PROCEDURE [
sv: PhysicalVolumeFormat.SubVolumeDesc, pvID: PhysicalVolume.ID] =
BEGIN
IF ~SubVolume.Find[sv.lvID, sv.lvPage].success THEN
SubVolume.OnLine[
sv, SubVolume.Find[[LOOPHOLE[pvID]], pvRootPage].subVolume.channel];
IF sv.lvPage = lvRootPage THEN
-- Set the volume file to cover just the root page;
VFileEnter[
sv.lvID, LOOPHOLE[sv.lvID], lvRootPage, lvRootPage + 1,
PilotFileTypes.tLogicalVolumeRootPage];
END;
RegisterVFiles: PUBLIC PROCEDURE [v: LvHandle] =
BEGIN
RegisterVFile: PROCEDURE [t: File.Type] = INLINE {
VFileEnter[v.vID, v.rootFileID[t], 1, v.volumeSize, t]};
RegisterVFile[PilotFileTypes.tFreePage];
RegisterVFile[PilotFileTypes.tVolumeAllocationMap];
RegisterVFile[PilotFileTypes.tVolumeFileMap];
END;
-- perhaps this should use GetLogicalRootPage???
SignalVolumeAccess: PUBLIC PROCEDURE [
pVID: POINTER TO READONLY Volume.ID, proc: LogicalVolume.VolumeAccessProc] =
BEGIN
SELECT LogicalVolume.VolumeAccess[pVID, proc, TRUE] FROM
ok => RETURN;
volumeUnknown => ERROR Volume.Unknown[pVID↑];
volumeNotOpen => ERROR Volume.NotOpen[pVID↑];
volumeReadOnly => ERROR LogicalVolume.ReadOnlyVolume;
ENDCASE => ERROR VolImplError[illegalReturnCode];
END;
SubvolumeOffline: PUBLIC ENTRY PROCEDURE [lvID: Volume.ID, root: BOOLEAN] =
BEGIN
IF root THEN
BEGIN
Activate[lvID];
UnregisterVFiles[activeVolume];
Deactivate[flush];
END;
LVDecrementPieceCount[lvID];
END;
SubvolumeOnline: PUBLIC ENTRY PROCEDURE [lvID: Volume.ID, root: BOOLEAN] =
BEGIN -- we ignore the root argument. It is provided for consistency with
-- SubvolumeOffline where it is currently needed. Eventually we may
-- use it here.
IF LogicalVolumeFind[@lvID] THEN LVIncrementPieceCount[lvID]
ELSE EnterLV[lvID];
RETURN;
END;
UnregisterVFiles: PUBLIC PROCEDURE [v: LvHandle] =
BEGIN
UnregisterVFile: PROCEDURE [t: File.Type] = INLINE {
PinnedFileFlush[v.rootFileID[t]]};
UnregisterVFile[PilotFileTypes.tFreePage];
UnregisterVFile[PilotFileTypes.tVolumeAllocationMap];
UnregisterVFile[PilotFileTypes.tVolumeFileMap];
END;
VFileEnter: PUBLIC PROCEDURE [
volume: Volume.ID, file: File.ID, fileAndVolPage: File.PageNumber,
nextPage: VolumeInternal.PageNumber, type: File.Type] =
BEGIN
PinnedFileEnter[
[file, volume, local[FALSE, FALSE, nextPage, type]],
[fileAndVolPage, fileAndVolPage, nextPage]];
END;
--
-- Logical volume table management (private)
EnterLV: INTERNAL PROCEDURE [vID: Volume.ID] =
BEGIN
FOR lv: LVTIndex IN LVTIndex DO
IF LVT[lv].lvID = Volume.nullID THEN
BEGIN
-- readOnly must be FALSE in the following so that scavenging a volume
-- at Open time works correctly. This may have to be changed if Open
-- ever takes an access mode argument.
LVT[lv] ←
[lvID: vID, pieceCount: 1, beingScavenged: FALSE, open: FALSE,
onLine: FALSE, readOnly: FALSE, type: nonPilot];
RETURN
END;
ENDLOOP;
ERROR VolImplError[lvTableFull];
END;
LogicalVolumeFind: INTERNAL PROCEDURE [vID: POINTER TO READONLY Volume.ID]
RETURNS [found: BOOLEAN] =
BEGIN
FOR lv: LVTIndex IN LVTIndex DO
IF LVT[lv].lvID = vID↑ THEN RETURN[LVT[lv].onLine]; ENDLOOP;
RETURN[FALSE]
END;
LogicalVolumeOffLine: INTERNAL PROCEDURE [vID: Volume.ID] =
BEGIN
FOR lv: LVTIndex IN LVTIndex DO
IF LVT[lv].lvID = vID THEN
BEGIN -- Move remaining entries to front so enumeration works correctly
FOR lvMove: LVTIndex IN [lv + 1..LAST[LVTIndex]) DO
LVT[lvMove - 1] ← LVT[lvMove]; ENDLOOP;
LVT[LAST[LVTIndex]] ← nullVolumeStatus;
RETURN;
END;
ENDLOOP;
ERROR VolImplError[notFoundInLVT];
END;
LVDecrementPieceCount: INTERNAL PROCEDURE [vID: Volume.ID] =
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
p.pieceCount ← p.pieceCount - 1;
IF p.pieceCount = 0 THEN LogicalVolumeOffLine[vID];
END;
LVGetEntryPointer: INTERNAL PROCEDURE [vID: Volume.ID]
RETURNS [POINTER TO VolumeStatus] =
BEGIN
lv: LVTIndex;
FOR lv IN LVTIndex DO IF LVT[lv].lvID = vID THEN RETURN[@LVT[lv]] ENDLOOP;
ERROR VolImplError[notFoundInLVT];
END;
LVGetStatus: INTERNAL PROCEDURE [vID: Volume.ID]
RETURNS [beingScavenged, open, onLine, readOnly: BOOLEAN, type: Volume.Type] =
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
RETURN[p.beingScavenged, p.open, p.onLine, p.readOnly, p.type]
END;
LVIncrementPieceCount: INTERNAL PROCEDURE [vID: Volume.ID] = INLINE
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
p.pieceCount ← p.pieceCount + 1;
END;
LVSetOnLine: INTERNAL PROCEDURE [vID: Volume.ID, onLine: BOOLEAN] = INLINE
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
p.onLine ← onLine;
END;
LVSetOpen: INTERNAL PROCEDURE [vID: Volume.ID, open: BOOLEAN] = INLINE
BEGIN p: POINTER TO VolumeStatus = LVGetEntryPointer[vID]; p.open ← open; END;
LVSetReadOnly: INTERNAL PROCEDURE [vID: Volume.ID, readOnly: BOOLEAN] = INLINE
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
p.readOnly ← readOnly;
END;
LVSetScavenged: INTERNAL PROCEDURE [vID: Volume.ID, beingScavenged: BOOLEAN] =
INLINE
BEGIN
p: POINTER TO VolumeStatus = LVGetEntryPointer[vID];
p.beingScavenged ← beingScavenged;
END;
LVSetType: INTERNAL PROCEDURE [vID: Volume.ID, type: Volume.Type] = INLINE
BEGIN p: POINTER TO VolumeStatus = LVGetEntryPointer[vID]; p.type ← type; END;
--
-- Various utility procedures
DriveSize: PROCEDURE [pvID: PhysicalVolume.ID]
RETURNS [DiskChannel.DiskPageCount] =
BEGIN
RETURN[
DiskChannel.GetDriveAttributes[
MarkerPage.Find[[physicalID[pvID]]].drive].nPages];
END;
IsReadOnly: INTERNAL PROCEDURE [vID: Volume.ID] RETURNS [BOOLEAN] = INLINE {
RETURN[
(~IsUtilityPilot[]) AND
((LVGetStatus[vID].type > debugClass) OR LVGetStatus[vID].readOnly)]};
IsUtilityPilot: PROCEDURE RETURNS [BOOLEAN] = INLINE {
RETURN[PilotSwitches.switches.u = down]};
-- Similar to VolumeAccess, but only requires the Root Page Subvolume to be around.
-- Intended for use by read-only procedures.
GetLogicalRootPage: ENTRY PROCEDURE [
pVID: POINTER TO READONLY Volume.ID, proc: PROCEDURE [LvHandle]] =
BEGIN
IF pVID↑ = Volume.nullID OR ~SubVolume.Find[pVID↑, lvRootPage].success THEN
RETURN WITH ERROR Unknown[pVID↑];
Activate[pVID↑];
IF activeVolume.seal # LogicalVolume.seal THEN {
Deactivate[flush]; RETURN WITH ERROR Unknown[pVID↑]};
proc[activeVolume];
END;
-- Provides access to volume files of one volume at a time
Activate: INTERNAL PROCEDURE [vID: Volume.ID] =
BEGIN
IF activeVID # vID THEN
BEGIN
Deactivate[flush];
SimpleSpace.Map[
logicalRootPageBuffer,
[[LOOPHOLE[vID], FileInternal.maxPermissions], lvRootPage], FALSE];
activeVID ← vID;
END;
END;
-- Deactivates the active volume. If the volume is Open, clear the changing bit.
Deactivate: INTERNAL PROCEDURE [mode: {forceout, flush} ← forceout] =
BEGIN
IF activeVID = Volume.nullID THEN RETURN;
VolFileMap.Close[mode = flush];
VolAllocMap.Close[mode = flush];
IF mode = flush THEN
BEGIN
SimpleSpace.Unmap[logicalRootPageBuffer];
activeVID ← Volume.nullID;
END
ELSE SimpleSpace.ForceOut[logicalRootPageBuffer];
END;
-- When LogicalVolumeCheck is called, the Subvolume is Online; if the LvRoot subvolume
-- is online, the LvRoot page file cache entries are set. If the Root SV is accessible
-- and of the right type, Vam, free and Vfm files are registered.
-- Called from CreateLogicalVolume, CheckLogicalVolume
LogicalVolumeCheck: INTERNAL PROCEDURE [vID: Volume.ID] =
BEGIN
good: BOOLEAN;
size: Volume.PageCount;
svH: SubVolume.Handle;
[good, svH] ← SubVolume.Find[vID, lvRootPage];
IF good THEN good ← ReadAndCheckLogicalRootLabel[vID];
IF good THEN
BEGIN
Activate[vID];
good ← activeVolume.seal = LogicalVolume.seal
AND activeVolume.version = LogicalVolume.currentVersion
AND vID = activeVolume.vID;
IF good THEN
BEGIN
good ← LogicalVolumeLike[vID];
LVSetType[vID, activeVolume.type];
END;
size ← activeVolume.volumeSize;
-- ~good volumes can be debuggers
IF LogicalVolumeDebuggerCheck[activeVolume] THEN
-- Set the debugger boot pointers back in Pilot Control
BEGIN
IF pLVBootFiles ~= NIL THEN pLVBootFiles↑ ← activeVolume.bootingInfo;
[deviceType: debuggerDeviceType↑, deviceOrdinal: debuggerDeviceOrdinal↑] ←
DiskChannel.GetDriveAttributes[
DiskChannel.GetAttributes[svH.channel].drive];
debugSearchState ← done;
END;
END;
WHILE good AND size # svH.lvPage + svH.nPages DO
[good, svH] ← SubVolume.Find[vID, svH.lvPage + svH.nPages]; ENDLOOP;
IF good THEN
BEGIN
-- The theory is that we don't have to check to see if the volume is already on line
-- since this subvolume is new
RegisterVFiles[activeVolume];
LVSetOnLine[vID, TRUE];
END
-- Deactivate is done in caller
END;
-- This may have to get smarter when we have boot messages
LogicalVolumeDebuggerCheck: PROCEDURE [vol: LvHandle]
RETURNS [isMyDebugger: BOOLEAN] =
BEGIN
IF debugSearchState # looking OR vol.seal # LogicalVolume.seal
OR vol.version <= 1 OR vol.bootingInfo[debugger] = Boot.nullDiskFileID
OR vol.bootingInfo[debuggee] = Boot.nullDiskFileID
THEN RETURN[FALSE];
SELECT debugClass FROM
nonPilot, debuggerDebugger => RETURN[FALSE]; -- these guys have no debugger
normal => RETURN[vol.type = debugger];
debugger => RETURN[vol.type = debuggerDebugger];
ENDCASE => ERROR;
END;
LogicalVolumeLike: INTERNAL PROCEDURE [vID: Volume.ID] RETURNS [good: BOOLEAN] =
BEGIN
SELECT TRUE FROM
IsUtilityPilot[] => good ← activeVolume.type # nonPilot;
-- don't put non-Pilot volumes online
debugSearchState = tooSoonToLook =>
-- PhysicalVolumeOnLine has arranged that the first subvolume we find will be
-- the system volume
BEGIN
systemID ← vID;
debugClass ← activeVolume.type;
debugSearchState ← looking;
good ← TRUE;
END;
ENDCASE => good ← activeVolume.type # nonPilot;
END;
OpenLogicalVolumeInternal: ENTRY PROCEDURE [
pVID: POINTER TO READONLY Volume.ID, readOnly: BOOLEAN]
RETURNS [LogicalVolume.OpenStatus] =
BEGIN
IF pVID↑ = Volume.nullID OR ~LogicalVolumeFind[pVID] THEN RETURN[Unknown];
WHILE LVGetStatus[pVID↑].beingScavenged DO WAIT scavengingComplete; ENDLOOP;
IF LVGetStatus[pVID↑].open THEN RETURN[wasOpen];
Activate[pVID↑];
LVSetOpen[pVID↑, TRUE];
LVSetReadOnly[pVID↑, readOnly];
RegisterVFiles[activeVolume];
IF activeVolume.changing THEN
BEGIN
LVSetOpen[pVID↑, FALSE];
Deactivate[];
RETURN[VolumeNeedsScavenging]
END;
Deactivate[];
RETURN[ok];
END;
ReadAndCheckLogicalRootLabel: PROCEDURE [lvID: Volume.ID] RETURNS [BOOLEAN] =
BEGIN
label: PilotDisk.Label ← LabelTransfer.ReadLabel[
[LOOPHOLE[lvID], lvID, local[
FALSE, FALSE, 1, PilotFileTypes.tLogicalVolumeRootPage]], lvRootPage,
lvRootPage].label;
BEGIN OPEN label;
-- could also check pad2...
RETURN[
fileID = LOOPHOLE[lvID] AND PilotDisk.GetLabelFilePage[@label] = lvRootPage
AND ~immutable AND ~temporary AND ~zeroSize AND pad1 = 0 AND
PilotDisk.GetLabelType[@label] = PilotFileTypes.tLogicalVolumeRootPage];
END;
END;
Process.InitializeCondition[@scavengingComplete, LAST[Process.Ticks]];
END.
(For earlier log entries see Pilot 3.0 archive version.)
January 25, 1980 2:54 PM McJones
Delete InstallBootVolume; add Get/SetPhysicalVolumeBootFiles,
GetContainingPhysicalVolume; add minPVPageNumber to
CreateLogicalVolume
February 3, 1980 3:57 PM McJones
Add device types to module parameters, results
March 7, 1980 11:52 PM Forrest
re-arranged opens to call up into fileImpl and from there to here
(see comments in LogicalVolume). Made Openning smarter about always
setting Open bit. Changed FileVolumeAccess to not pass on parameters
deleted from FileAccess Procs.
March 11, 1980 10:37 AM Forrest
Added display of cDeleteTemps.
June 5, 1980 3:13 PM Luniewski
PhysicalVolume => PhysicalVolumeFormat. Added removable volume
support including a new logical volume table format, a GetHints
operation for PhysicalVolumeImpl to use, the Volume.GetStatus
operation and recognition of the ability to unpin file cache entries.
Modified to move all physical volume implementation stuff into
PhysicalVolumeImpl.
June 19, 1980 2:47 PM Luniewski
Added BeginScavenging and EndScavenging.
July 17, 1980 5:13 PM Forrest
Added second arg to GetNext procedure; added GetLabelType..
August 1, 1980 10:00 AM Luniewski
Made less conservative user of Deactivate (performance improvement).
Mods to permit read-up and read-write down of volumes. Hooks to
permit some operations to explicitly specify read-only/read-write
access.
August 15, 1980 2:54 PM McJones
VolumeSet=>TypeSet
September 3, 1980 4:40 PM Luniewski
Fix EnterLV to permit scavenging at Open time. Clear bootingInfo on
erasing a logical volume.
September 18, 1980 7:22 PM Luniewski
Changes for new logical volume format. Open volumes of higher type
than system volume without deleting temps.
September 22, 1980 12:02 PM Luniewski
Do delete temps on volumes of type <= system volume.
October 9, 1980 11:15 AM Luniewski
Implement VolumeExtras.OpenVolume
October 21, 1980 1:54 PM Jose
Increase maxLVs from 8 to 12
January 8, 1981 10:20 AM Gobbel
'K' keyswitch: only open system logical volume
January 12, 1981 3:55 PM Luniewski
New LabelTransfer interface. Change Open to enforce read-onliness.
January 21, 1981 6:07 PM Fay
Changed LogicalVolumeCreate/Erase to call EraseVolume rather than ScavengeVolume;
changed BeginScavenging to accept and pass a context to ScavengeVolume and to catch
and turn Scavenger.Error into a RETURN WITH ERROR.
August 26, 1982 9:14 am Levin
Make things SAFE.