-- 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.