<> <> <> <<>> DIRECTORY Disk USING[ DoIO, Channel, DriveAttributes, InspectDiskShape, Label, labelCheck, NextChannel, ok, PageCount, PageNumber, Request, Status, Valid ], DiskFace USING[ wordsPerPage ], File USING[ VolumeID ], FileInternal USING[ ReadRootPage, TranslateStatus, VolumeObject ], VolumeFormat, PhysicalVolume USING[ Credentials, PhysicalRC, SubVolumeDetails, SubVolumeDetailsObject ], VM USING[ AddressToPageNumber, Allocate, Free, PageNumberToAddress, Pin, Unpin, wordsPerPage ], Rope USING[ ROPE, FromRefText ]; PhysicalVolumeImpl: CEDAR MONITOR IMPORTS Disk, FileInternal, Rope, VM EXPORTS DiskFace--AbsID--, File, PhysicalVolume = BEGIN <> <<******** List of known physical volumes ********>> PageData: TYPE = LONG POINTER; pagePages: INT = (DiskFace.wordsPerPage+VM.wordsPerPage-1)/VM.wordsPerPage; AccessPage: UNSAFE PROC[channel: Disk.Channel, page: Disk.PageNumber, label: LONG POINTER TO Disk.Label, data: PageData, direction: { read, write }, verifyLabel: BOOL --else read label--] RETURNS[ status: Disk.Status ] = TRUSTED BEGIN ENABLE UNWIND => VM.Unpin[[page: VM.AddressToPageNumber[data], count: pagePages]]; countDone: Disk.PageCount; req: Disk.Request _ [ diskPage: page, data: data, command: [header: verify, label: IF verifyLabel THEN verify ELSE read, data: IF direction=read THEN read ELSE write], count: 1 ]; VM.Pin[[page: VM.AddressToPageNumber[data], count: pagePages]]; [status, countDone] _ Disk.DoIO[channel, label, @req]; VM.Unpin[[page: VM.AddressToPageNumber[data], count: pagePages]]; END; FreePage: UNSAFE PROC[data: PageData] = TRUSTED { VM.Free[[page: VM.AddressToPageNumber[data], count: pagePages]] }; ReadPage: PROC[channel: Disk.Channel, page: Disk.PageNumber, label: LONG POINTER TO Disk.Label, verifyLabel: BOOL] RETURNS[ status: Disk.Status, data: PageData ] = TRUSTED BEGIN data _ VM.PageNumberToAddress[VM.Allocate[pagePages].page]; status _ AccessPage[channel, page, label, data, read, verifyLabel]; IF status # Disk.ok THEN { FreePage[data]; data _ NIL }; END; PhysRoot: TYPE = LONG POINTER TO VolumeFormat.PhysicalRoot; --DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID; --DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes; PhysRootLabel: PROC[id: File.VolumeID] RETURNS[Disk.Label] = TRUSTED BEGIN RETURN[ [ fileID: [abs[AbsID[id]]], filePage: 0, attributes: Attributes[physicalRoot], dontCare: LOOPHOLE[LONG[0]] ] ] END; CredentialsLabel: PROC[id: File.VolumeID] RETURNS[Disk.Label] = TRUSTED BEGIN <> cannedID: MACHINE DEPENDENT RECORD[a,b,c,d,e: CARDINAL] = [a: 076543B, b: 021076B, c: 054321B, d: 076543B, e: 021076B]; -- ask Roy! RETURN[ [ fileID: [abs[AbsID[LOOPHOLE[cannedID]]]], filePage: 0, attributes: Attributes[LOOPHOLE[0]], dontCare: LOOPHOLE[LONG[0]] ] ] END; ReadPhysicalRoot: PROC[channel: Disk.Channel] RETURNS[ status: Disk.Status, root: PhysRoot ] = TRUSTED BEGIN label: Disk.Label; data: PageData; [status, data] _ ReadPage[channel, VolumeFormat.rootPageNumber, @label, FALSE]; root _ LOOPHOLE[data]; IF status = Disk.ok AND label # PhysRootLabel[root.pvID] THEN { status _ Disk.labelCheck; FreePage[data]; root _ NIL }; END; --PhysicalVolume.--PhysicalObject: PUBLIC TYPE = RECORD[ channel: Disk.Channel, rootStatus: PhysicalVolume.PhysicalRC _ ok, root: PhysRoot _ NIL, name: Rope.ROPE _ NIL, rest: REF PhysicalObject _ NIL ]; Physical: TYPE = REF PhysicalObject; physicalList: REF PhysicalObject _ NIL; -- known physical volumes -- NextPhysical: PUBLIC PROC[prev: Physical, wait: BOOL] RETURNS[next: Physical] = TRUSTED BEGIN DO lastCh: Disk.Channel; [next, lastCh] _ EntryNextPhysical[prev]; IF next # NIL OR NOT wait THEN EXIT; [] _ Disk.NextChannel[lastCh, TRUE]; ENDLOOP; END; lastChannel: Disk.Channel _ NIL; EntryNextPhysical: ENTRY PROC[prev: Physical] RETURNS[next: Physical, lastCh: Disk.Channel] = { [next, lastCh] _ InnerNextPhysical[prev] }; InnerNextPhysical: INTERNAL PROC[prev: Physical] RETURNS[next: Physical, lastCh: Disk.Channel] = TRUSTED BEGIN next _ IF prev = NIL THEN physicalList ELSE prev.rest; -- check whether found volume has changed state -- WHILE next # NIL AND NOT Disk.Valid[next.channel] DO AssignPhysical[prev, next.rest]; next _ next.rest; ENDLOOP; IF next = NIL THEN BEGIN -- Check for a new channel -- new: Disk.Channel = Disk.NextChannel[lastChannel]; IF new # NIL THEN { AssignPhysical[prev, NewPhysical[new]]; lastChannel _ new }; next _ IF prev = NIL THEN physicalList ELSE prev.rest; END; lastCh _ lastChannel; END; AssignPhysical: INTERNAL PROC[tail: Physical, new: Physical] = { IF tail = NIL THEN physicalList _ new ELSE tail.rest _ new }; NewPhysical: INTERNAL PROC[channel: Disk.Channel] RETURNS[ new: Physical ] = TRUSTED BEGIN status: Disk.Status; new _ NEW[PhysicalObject _ [channel: channel]]; IF NOT Disk.InspectDiskShape[channel, quickReadOnly] THEN ERROR; [status, new.root] _ ReadPhysicalRoot[channel]; new.rootStatus _ FileInternal.TranslateStatus[status]; IF new.rootStatus # ok THEN RETURN; IF new.root.seal # VolumeFormat.PRSeal OR new.root.version # VolumeFormat.PRCurrentVersion THEN { new.rootStatus _ inconsistent; RETURN }; TRUSTED{ new.name _ GetName[@new.root.label, new.root.labelLength] }; <> FOR s: INT IN [0..new.root.subVolumeCount) DO svd: LONG POINTER TO VolumeFormat.SubVolumeDesc = @new.root.subVolumes[s]; l: REF LogicalObject _ NIL; FOR l _ logicalList, l.rest UNTIL l = NIL DO IF l.id = svd.lvID THEN EXIT REPEAT FINISHED => BEGIN l _ NEW[LogicalObject _ [id: svd.lvID, size: svd.lvSize, rest: NIL]]; IF lastLogical = NIL THEN logicalList _ l ELSE lastLogical.rest _ l; lastLogical _ l; END; ENDLOOP; -- Now, "l" is appropriate LogicalObject -- BEGIN this: PhysicalVolume.SubVolumeDetails = NEW[PhysicalVolume.SubVolumeDetailsObject _ [ id: svd.lvID, start: svd.lvPage, size: svd.nPages, physical: new, channel: channel, address: svd.pvPage] ]; prev: LIST OF PhysicalVolume.SubVolumeDetails _ NIL; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ l.subVols, sv.rest UNTIL sv = NIL DO IF NOT Disk.Valid[sv.first.channel] THEN BEGIN -- remove from chain -- IF prev = NIL THEN l.subVols _ sv.rest ELSE prev.rest _ sv.rest; LOOP END; IF sv.first.start > this.start THEN BEGIN -- insert before "sv" -- IF this.start + this.size > sv.first.start THEN { new.rootStatus _ inconsistent; RETURN }; IF prev = NIL THEN l.subVols _ CONS[first: this, rest: sv] ELSE prev.rest _ CONS[first: this, rest: sv]; EXIT END; IF sv.first.start + sv.first.size > this.start THEN { new.rootStatus _ inconsistent; RETURN }; prev _ sv; REPEAT FINISHED => IF prev = NIL THEN l.subVols _ CONS[first: this, rest: NIL] ELSE prev.rest _ CONS[first: this, rest: NIL] ENDLOOP; END; ENDLOOP; END; GetName: PROC[ chars: LONG POINTER TO PACKED ARRAY [0..VolumeFormat.volumeLabelLength) OF CHAR, length: INT] RETURNS[Rope.ROPE] = TRUSTED BEGIN text: REF TEXT = NEW[TEXT[length]]; FOR i: INT IN [0..length) DO text[i] _ chars[i] ENDLOOP; text.length _ length; RETURN[Rope.FromRefText[text]] END; PhysicalInfo: PUBLIC PROC[physical: Physical] RETURNS[ channel: Disk.Channel, rootStatus: PhysicalVolume.PhysicalRC, id: File.VolumeID, -- unique id of this physical volume -- name: Rope.ROPE, size: Disk.PageCount, free: Disk.PageCount, timeValid: BOOL, time: VolumeFormat.TimeParameters] = TRUSTED BEGIN size _ Disk.DriveAttributes[physical.channel].nPages; channel _ physical.channel; rootStatus _ physical.rootStatus; IF rootStatus = ok THEN BEGIN lastSV: VolumeFormat.SubVolumeDesc = physical.root.subVolumes[physical.root.subVolumeCount-1]; id _ physical.root.pvID; name _ physical.name; free _ size - (lastSV.pvPage + lastSV.nPages); timeValid _ physical.root.timeParametersValid; time _ physical.root.timeParameters; END; END; WriteTimeParameters: PUBLIC PROC[physical: Physical, timeValid: BOOL, time: VolumeFormat.TimeParameters] RETURNS[ PhysicalVolume.PhysicalRC ] = TRUSTED BEGIN label: Disk.Label _ PhysRootLabel[physical.root.pvID]; status: Disk.Status; physical.root.timeParametersValid _ timeValid; IF timeValid THEN physical.root.timeParameters _ time; status _ AccessPage[physical.channel, VolumeFormat.rootPageNumber, @label, physical.root, write, TRUE]; RETURN[ FileInternal.TranslateStatus[status] ] END; ReadCredentials: PUBLIC UNSAFE PROC[physical: Physical, credentials: PhysicalVolume.Credentials] RETURNS[ PhysicalVolume.PhysicalRC ] = UNCHECKED BEGIN label: Disk.Label _ CredentialsLabel[physical.root.pvID]; status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber, @label, credentials, read, TRUE]; RETURN[ FileInternal.TranslateStatus[status] ] END; WriteCredentials: PUBLIC PROC[physical: Physical, credentials: PhysicalVolume.Credentials] RETURNS[ PhysicalVolume.PhysicalRC ] = TRUSTED BEGIN label: Disk.Label _ CredentialsLabel[physical.root.pvID]; status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber, @label, credentials, write, TRUE]; RETURN[ FileInternal.TranslateStatus[status] ] END; <<******** Accumulation of sub-volumes into logical volumes ********>> LogicalObject: TYPE = RECORD[ id: File.VolumeID, size: VolumeFormat.LogicalPageCount, subVols: LIST OF PhysicalVolume.SubVolumeDetails _ NIL, -- ordered list of fragments -- rest: REF LogicalObject ]; Logical: TYPE = REF LogicalObject; logicalList: REF LogicalObject _ NIL; -- known logical volumes -- NextLogical: PROC[prev: Logical, wait: BOOL] RETURNS[next: Logical] = BEGIN DO lastP: Physical; [next, lastP] _ EntryNextLogical[prev]; IF next # NIL OR NOT wait THEN EXIT; [] _ NextPhysical[lastP, TRUE]; ENDLOOP; END; lastPhysical: REF PhysicalObject _ NIL; EntryNextLogical: ENTRY PROC[prev: Logical] RETURNS[next: Logical, lastP: Physical] = { [next, lastP] _ InnerNextLogical[prev] }; InnerNextLogical: INTERNAL PROC[prev: Logical] RETURNS[next: Logical, lastP: Physical] = BEGIN -- check for any more physical volumes -- FOR p: Physical _ InnerNextPhysical[lastPhysical].next, InnerNextPhysical[p].next UNTIL p = NIL DO lastPhysical _ p ENDLOOP; next _ prev; DO wanted: INT _ 0; next _ IF next = NIL THEN logicalList ELSE next.rest; IF next = NIL THEN RETURN[NIL, lastPhysical]; -- check entire volume is known -- FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ next.subVols, sv.rest UNTIL sv = NIL DO IF wanted # sv.first.start OR NOT Disk.Valid[sv.first.channel] THEN EXIT; wanted _ wanted + sv.first.size; ENDLOOP; IF wanted = next.size THEN EXIT; ENDLOOP; RETURN[next, lastPhysical] END; Valid: INTERNAL PROC[list: LIST OF PhysicalVolume.SubVolumeDetails] RETURNS[BOOL] = TRUSTED BEGIN FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ list, sv.rest UNTIL sv = NIL DO IF NOT Disk.Valid[sv.first.channel] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE] END; <<******** Top-level: list of File.Volume objects ********>> Volume: TYPE = REF VolumeObject; --File.--VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject; volumeList: Volume _ NIL; -- known volumes -- --File.--NextVolume: PUBLIC PROC[volume: Volume, wait: BOOL _ FALSE] RETURNS[next: Volume] = BEGIN DO lastLog: Logical; [next, lastLog] _ EntryNext[volume]; IF next # NIL OR NOT wait THEN EXIT; [] _ NextLogical[lastLog, TRUE]; ENDLOOP; <> IF next # NIL AND NOT next.initialised THEN { FileInternal.ReadRootPage[next]; NoteRootRead[next] }; END; lastLogical: Logical _ NIL; rootRead: CONDITION; EntryNext: ENTRY PROC[prev: Volume] RETURNS[next: Volume, lastLog: Logical] = BEGIN next _ IF prev = NIL THEN volumeList ELSE prev.rest; -- check whether next volume has gone offline -- WHILE next # NIL AND NOT Valid[next.subVolumes] DO Assign[prev, next.rest]; next _ next.rest; ENDLOOP; IF next = NIL THEN BEGIN -- Check for a new volume -- new: Logical = InnerNextLogical[lastLogical].next; IF new # NIL THEN { Assign[prev, NewVolume[new]]; lastLogical _ new }; next _ IF prev = NIL THEN volumeList ELSE prev.rest; END ELSE UNTIL next.initialised DO WAIT rootRead ENDLOOP; lastLog _ lastLogical; END; Assign: INTERNAL PROC[tail: Volume, new: Volume] = { IF tail = NIL THEN volumeList _ new ELSE tail.rest _ new }; NoteRootRead: ENTRY PROC[volume: Volume] = { volume.initialised _ TRUE; BROADCAST rootRead }; NewVolume: INTERNAL PROC[logical: Logical] RETURNS[volume: Volume] = BEGIN volume _ NEW[VolumeObject _ [id: logical.id, subVolumes: logical.subVols, size: logical.size]]; END; --PhysicalVolume.--GetPhysical: PUBLIC PROC[volume: Volume, page: VolumeFormat.LogicalPage _ [0]] RETURNS[Physical] = BEGIN FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF sv.first.start <= page AND sv.first.start + sv.first.size > page THEN RETURN[sv.first.physical]; ENDLOOP; RETURN[NIL] END; END.