<> <> <> <> <> <> <> <<>> DIRECTORY BootFile USING [ Location ], Disk USING [ Channel, DoIO, DriveAttributes, invalid, Label, labelCheck, ok, PageNumber, PageCount, Request, SameDrive, Status ], DiskFace USING [ DontCare, RelID, wordsPerPage ], File USING [ Create, Error, FileID, FP, nullFP, Handle, NextVolume, Open, PageCount, PageNumber, PagesForWords, RC, Read, VolumeFile, VolumeID, Write ], FileBackdoor USING [ SetRoot, VolumeFlusher], FileInternal, PhysicalVolume USING [ Physical, PhysicalRC, SubVolumeDetails ], Process USING [ Detach, MsecToTicks, Pause, SecondsToTicks, Ticks ], ProcessorFace USING [ GetClockPulses, microsecondsPerHundredPulses ], Rope USING [ Equal, Fetch, FromRefText, Length, ROPE ], VolumeFormat USING [ AbsID, Attributes, LogicalPage, LogicalPageCount, LogicalRoot, LogicalRun, LRCurrentVersion, LRSeal, LVBootFile, nullDiskFileID, RunPageCount, VAMObject, VAMChunk, vamChunkPos, volumeLabelLength ], VM USING [ AddressForPageNumber, Allocate, Interval, MakeUnchanged, PageCount, PageNumber, PageNumberForAddress, PagesForWords, State, SwapIn, Unpin], VMBacking USING [ Run ]; LogicalVolumeImpl: CEDAR MONITOR LOCKS volume USING volume: Volume IMPORTS Disk, File, Process, ProcessorFace, FileBackdoor, FileInternal, Rope, VM EXPORTS DiskFace--RelID,AbsID,Attributes--, File, FileBackdoor, FileInternal, PhysicalVolume--SetPhysicalRoot-- SHARES File = { ROPE: TYPE = Rope.ROPE; <<******** Data Types and minor subroutines ******** -->> --DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes; --DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID; --File.--FileID: PUBLIC TYPE = FileInternal.FileID; Volume: TYPE = REF VolumeObject; --File.--VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject; PhysicalRun: TYPE = VMBacking.Run; DoPinnedIO: UNSAFE PROC [channel: Disk.Channel, label: LONG POINTER TO Disk.Label, req: POINTER TO Disk.Request] RETURNS [ status: Disk.Status, countDone: Disk.PageCount] = TRUSTED { interval: VM.Interval = [ page: VM.PageNumberForAddress[req.data], count: VM.PagesForWords[ (IF req.incrementDataPtr THEN req.count ELSE 1)*DiskFace.wordsPerPage] ]; VM.SwapIn[interval: interval, kill: req.command.data=read, pin: TRUE]; [status, countDone] _ Disk.DoIO[channel, label, req ! UNWIND => VM.Unpin[interval] ]; VM.Unpin[interval]; }; notCheckpointing: CONDITION; --FileInternal.--LockVolume: PUBLIC ENTRY PROC [volume: Volume] = { <> WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; volume.vamFile _ NIL; -- cf. GrabVAMFile volume.checkpointing _ TRUE; }; <<******** Access to volume root page and volume's list of sub-volumes ******** -->> volRoot: VolumeFormat.LogicalPage = [0]; -- logical page number of volume root page -- --File.--GetVolumeID: PUBLIC PROC [volume: Volume] RETURNS [id: File.VolumeID] = { RETURN[volume.id] }; LogRootLabel: PROC [id: File.VolumeID] RETURNS [Disk.Label] = { RETURN[ [ fileID: [abs[AbsID[id]]], filePage: 0, attributes: Attributes[logicalRoot], dontCare: LOOPHOLE[LONG[0]] ] ] }; RootTransfer: INTERNAL PROC [volume: Volume, direction: {read, write, init} ] RETURNS [why: File.RC] = TRUSTED { status: Disk.Status; countDone: Disk.PageCount; label: Disk.Label _ LogRootLabel[volume.id]; req: Disk.Request _ [ diskPage: volume.subVolumes.first.address, data: volume.root, incrementDataPtr: TRUE, command: SELECT direction FROM read => [header: verify, label: verify, data: read], write => [header: verify, label: verify, data: write], init => [header: verify, label: write, data: write], ENDCASE => ERROR, count: 1 ]; [status, countDone] _ DoPinnedIO[volume.subVolumes.first.channel, @label, @req]; RETURN[ FileInternal.TranslateStatus[status] ] }; --FileInternal.--ReadRootPage: PUBLIC ENTRY PROC [volume: Volume] = TRUSTED { ENABLE UNWIND => NULL; IF volume.root = NIL THEN volume.root _ VM.AddressForPageNumber[VM.Allocate[VM.PagesForWords[DiskFace.wordsPerPage]].page]; volume.rootStatus _ RootTransfer[volume, read]; IF volume.rootStatus = ok AND (volume.root.seal # VolumeFormat.LRSeal OR volume.root.version # VolumeFormat.LRCurrentVersion) THEN volume.rootStatus _ inconsistent; IF volume.rootStatus = ok THEN TRUSTED{ volume.name _ GetName[@volume.root.label, volume.root.labelLength] }; IF volume.rootStatus = ok AND volume.root.type # cedar THEN volume.rootStatus _ nonCedarVolume; IF volume.rootStatus = ok THEN { ENABLE File.Error => { volume.vamStatus _ SELECT why FROM unknownFile, unknownPage => inconsistent, ENDCASE => why; CONTINUE }; volume.lastFileID _ volume.root.lastFileID; ReadVAM[volume]; volume.vamStatus _ ok; } ELSE volume.vamStatus _ inconsistent; volume.checkpointing _ FALSE; BROADCAST notCheckpointing; ConsiderFlushing[volume]; }; --FileInternal.--InitRootPage: PUBLIC ENTRY PROC [volume: Volume, name: ROPE] RETURNS [File.RC] = TRUSTED { ENABLE UNWIND => NULL; IF volume.root = NIL THEN volume.root _ VM.AddressForPageNumber[VM.Allocate[ VM.PagesForWords[DiskFace.wordsPerPage]].page]; volume.root^ _ [ vID: volume.id, labelLength: MIN[name.Length[], VolumeFormat.volumeLabelLength], volumeSize: volume.size <> ]; FOR i: CARDINAL IN [0..volume.root.labelLength) DO volume.root.label[i] _ name.Fetch[i]; ENDLOOP; volume.rootStatus _ RootTransfer[volume, init]; IF volume.rootStatus = ok THEN [] _ FileInternal.InitLogicalMarkers[volume]; volume.name _ name; volume.lastFileID _ volume.root.lastFileID; volume.vamStatus _ inconsistent; -- until someone calls EraseVolume volume.checkpointing _ FALSE; BROADCAST notCheckpointing; ConsiderFlushing[volume]; RETURN[volume.rootStatus] }; GetName: PROC [ chars: LONG POINTER TO PACKED ARRAY [0..VolumeFormat.volumeLabelLength) OF CHAR, length: INT] RETURNS [ROPE] = TRUSTED { 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]] }; --File.--GetVolumeName: PUBLIC ENTRY PROC [volume: Volume] RETURNS [ROPE] = { ENABLE UNWIND => NULL; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[volume.name] }; --File.--FindVolumeFromID: PUBLIC PROC [id: File.VolumeID] RETURNS [volume: Volume] = { volume _ NIL; DO volume _ File.NextVolume[volume]; IF volume = NIL THEN EXIT; IF GetVolumeID[volume] = id THEN EXIT; ENDLOOP; }; --File.--FindVolumeFromName: PUBLIC PROC [name: ROPE] RETURNS [volume: Volume] = { volume _ NIL; DO volume _ File.NextVolume[volume]; IF volume = NIL THEN EXIT; IF Rope.Equal[GetVolumeName[volume ! File.Error => CONTINUE], name, FALSE] THEN EXIT; ENDLOOP; }; --File.--LogicalInfo: PUBLIC ENTRY PROC [volume: Volume] RETURNS [ id: File.VolumeID, size: INT, rootStatus: File.RC, name: ROPE, vamStatus: File.RC, free: INT, freeboard: INT] = { <<{name, vamStatus, free, freeboard} only valid if rootStatus is ok or nonCedarVolume>> <<{free, freeboard} only valid if vamStatus is ok>> ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; RETURN[ id: volume.id, size: volume.size, rootStatus: volume.rootStatus, name: volume.name, vamStatus: volume.vamStatus, free: volume.free, freeboard: volume.freeboard] }; --FileInternal.--TranslateLogicalRun: PUBLIC --EXTERNAL-- PROC [logicalRun: VolumeFormat.LogicalRun, volume: Volume] RETURNS [channel: Disk.Channel, diskPage: Disk.PageNumber] = { <> FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF sv.first.start <= logicalRun.first AND sv.first.start + sv.first.size > logicalRun.first THEN RETURN [channel: sv.first.channel, diskPage: [sv.first.address + (logicalRun.first - sv.first.start)] ]; REPEAT FINISHED => RETURN WITH ERROR File.Error[inconsistent] ENDLOOP; }; --PhysicalVolume.--GetPhysical: PUBLIC PROC [volume: Volume, page: VolumeFormat.LogicalPage _ [0]] RETURNS [PhysicalVolume.Physical] = { 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] }; --FileInternal.--RecordRootFile: PUBLIC ENTRY PROC [volume: Volume, root: File.VolumeFile, fp: File.FP, page: File.PageNumber, id: DiskFace.RelID, link: DiskFace.DontCare, channel: Disk.Channel] = TRUSTED { ENABLE UNWIND => NULL; why: File.RC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; IF NOT Disk.SameDrive[channel, volume.subVolumes.first.channel] THEN RETURN WITH ERROR File.Error[mixedDevices]; <> volume.root.rootFile[root] _ [fp: fp, page: page]; IF root IN [FIRST[VolumeFormat.LVBootFile]..LAST[VolumeFormat.LVBootFile]] THEN volume.root.bootingInfo[root] _ [fID: [rel[id]], firstPage: page, firstLink: link]; why _ RootTransfer[volume, write]; IF why = ok THEN why _ FileInternal.WriteLogicalMarkers[volume]; IF why # ok THEN RETURN WITH ERROR File.Error[why, page]; }; --FileBackdoor.--GetRoot: PUBLIC ENTRY PROC [volume: Volume, root: File.VolumeFile] RETURNS [fp: File.FP, page: File.PageNumber _ [0]] = TRUSTED { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; [fp: fp, page: page] _ volume.root.rootFile[root]; }; --FileInternal.--GetLogicalLocation: PUBLIC ENTRY PROC [volume: Volume, root: VolumeFormat.LVBootFile] RETURNS [location: BootFile.Location] = TRUSTED { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; location.diskFileID _ volume.root.bootingInfo[root]; [type: location.deviceType, ordinal: location.deviceOrdinal] _ Disk.DriveAttributes[volume.subVolumes.first.channel]; }; --PhysicalVolume.--SetPhysicalRoot: PUBLIC ENTRY PROC [volume: Volume, root: File.VolumeFile] = TRUSTED { ENABLE UNWIND => NULL; physRC: PhysicalVolume.PhysicalRC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; physRC _ FileInternal.SetPhysicalLocation[volume.subVolumes.first.physical, root, volume.root.bootingInfo[root]]; IF physRC # ok THEN RETURN WITH ERROR File.Error[physRC, volume.root.bootingInfo[root].firstPage]; }; --FileBackdoor.--MarkDebugger: PUBLIC ENTRY PROC [volume: Volume, isDebugger: BOOL] = TRUSTED { ENABLE UNWIND => NULL; why: File.RC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; volume.root.coCedar _ isDebugger; why _ RootTransfer[volume, write]; IF why = ok THEN why _ FileInternal.WriteLogicalMarkers[volume]; IF why # ok THEN RETURN WITH ERROR File.Error[why]; }; --FileBackdoor.--IsDebugger: PUBLIC ENTRY PROC [volume: Volume] RETURNS [BOOL] = TRUSTED { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus = nonCedarVolume THEN RETURN[volume.root.type # pilot]; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[volume.root.coCedar] }; EraseRoot: INTERNAL PROC [volume: Volume] = TRUSTED { volume.root.bootingInfo _ ALL[VolumeFormat.nullDiskFileID]; volume.root.rootFile _ ALL[]; }; idGroupSize: INT = 100; -- number if id's to allocate at a time. --FileInternal.--NewID: PUBLIC ENTRY PROC [volume: Volume] RETURNS [FileID] = TRUSTED { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; DO lastRecorded: FileID = volume.root.lastFileID; why: File.RC; IF volume.lastFileID < lastRecorded THEN EXIT; volume.root.lastFileID _ FileID[lastRecorded + idGroupSize]; why _ RootTransfer[volume, write]; IF why # ok THEN RETURN WITH ERROR File.Error[why]; ENDLOOP -- This is a loop only to recover from garbage ID's --; RETURN[ volume.lastFileID _ [volume.lastFileID + 1] ] }; <<******** Volume Allocation Map ********>> VAM: TYPE = FileInternal.VAM; PageBits: TYPE = MACHINE DEPENDENT RECORD[ls(0): CARDINAL, ms(1): CARDINAL]; VAMChunks: PROC [pages: VolumeFormat.LogicalPageCount] RETURNS [CARDINAL] = TRUSTED { OPEN p: LOOPHOLE[pages+LAST[CARDINAL]-1, PageBits]; RETURN[p.ms] }; VAMWords: PROC [pages: VolumeFormat.LogicalPageCount] RETURNS [INT] = { <> RETURN[VolumeFormat.vamChunkPos + VAMChunks[pages]*4096]; }; VAMFilePages: PROC [pages: VolumeFormat.LogicalPageCount] RETURNS [File.PageCount] = INLINE { RETURN[File.PagesForWords[VAMWords[pages]]]; }; VAMVMPages: PROC [pages: VolumeFormat.LogicalPageCount] RETURNS [VM.PageCount] = INLINE { RETURN[VM.PagesForWords[VAMWords[pages]]]; }; IsUsed: PUBLIC ENTRY PROC [volume: Volume, page: VolumeFormat.LogicalPage] RETURNS [inUse: BOOL] = { RETURN[Used[volume.vam, page]]; }; Used: PROC [vam: VAM, page: VolumeFormat.LogicalPage] RETURNS [BOOL] = TRUSTED INLINE { <> OPEN p: LOOPHOLE[page, PageBits]; RETURN[LOOPHOLE[@vam.used + p.ms*SIZE[VolumeFormat.VAMChunk], LONG POINTER TO VolumeFormat.VAMChunk][p.ls]]; }; SetPageUsed: PUBLIC ENTRY PROC [volume: Volume, page: VolumeFormat.LogicalPage, inUse: BOOL] RETURNS [wasInUse: BOOL] = { wasInUse _ Used[volume.vam, page]; SetUsed[volume: volume, page: page, inUse: inUse]; volume.vamChanged _ TRUE; }; SetUsed: INTERNAL PROC [volume: Volume, page: VolumeFormat.LogicalPage, inUse: BOOL] = TRUSTED INLINE { <> OPEN p: LOOPHOLE[page, PageBits]; chunk: LONG POINTER TO VolumeFormat.VAMChunk = LOOPHOLE[@volume.vam.used + p.ms*SIZE[VolumeFormat.VAMChunk]]; IF chunk[p.ls] # inUse THEN { chunk[p.ls] _ inUse; IF inUse THEN volume.free _ volume.free-1 ELSE volume.free _ volume.free+1; }; }; AllocVAM: INTERNAL PROC [volume: Volume] = TRUSTED { IF volume.vam = NIL THEN { words: INT = VAMWords[volume.size]; volume.vam _ VM.AddressForPageNumber[VM.Allocate[VM.PagesForWords[words]].page]; volume.vam.size _ volume.size; }; volume.vam.rover _ [0]; }; ReadVAM: INTERNAL PROC [volume: Volume] = TRUSTED { this: VolumeFormat.LogicalPage _ [0]; IF volume.rootStatus # ok THEN ERROR; -- checked by our caller volume.vamFile _ File.Open[volume, volume.root.rootFile[VAM].fp --File.Error is caught by our caller--]; AllocVAM[volume]; File.Read[volume.vamFile, [0], VAMFilePages[volume.size], volume.vam]; SetUsed[volume, volRoot, TRUE]; volume.free _ 0; DO IF this = volume.vam.size THEN EXIT; IF NOT Used[volume.vam, this] THEN volume.free _ volume.free+1; this _ [this+1]; ENDLOOP; BROADCAST vamFileRelease; -- significant if we called GrabVAMFile for a checkpoint }; --FileBackdoor.--GetVolumePages: PUBLIC ENTRY PROC [volume: Volume] RETURNS [size, free, freeboard: INT] = { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[size: volume.size, free: volume.free, freeboard: volume.freeboard] }; --FileBackdoor.--SetFlusher: PUBLIC ENTRY PROC [volume: Volume, flusher: FileBackdoor.VolumeFlusher, data: REF ANY] = { ENABLE UNWIND => NULL; volume.flusherData _ data; volume.flusher _ flusher; IF volume.freeboard = 0 THEN volume.freeboard _ MIN[volume.size/5, 1000]; ConsiderFlushing[volume]; }; ConsiderFlushing: INTERNAL PROC [volume: Volume] = { IF volume.rootStatus = ok AND volume.free < volume.freeboard - volume.freeboard / 10 -- be sure it's worth while AND volume.flusher # NIL AND NOT volume.flushing THEN TRUSTED{ volume.flushing _ TRUE; Process.Detach[ FORK FlusherProcess[volume] ]; } }; FlusherProcess: PROC [volume: Volume] = { DO flusher: FileBackdoor.VolumeFlusher; data: REF ANY; lack: VolumeFormat.LogicalPageCount; continue: BOOL; urgent: BOOL; Examine: ENTRY PROC [volume: Volume] = { flusher _ volume.flusher; data _ volume.flusherData; lack _ MAX[volume.freeboard - volume.free, 0]; continue _ volume.free < volume.freeboard AND volume.flusher # NIL; urgent _ volume.free < volume.freeboard / 2; IF NOT continue THEN volume.flushing _ FALSE; }; start: LONG CARDINAL _ ProcessorFace.GetClockPulses[]; elapsed: LONG CARDINAL; progress: BOOL; Examine[volume]; IF NOT continue THEN EXIT; progress _ flusher[volume, lack, data]; elapsed _ ProcessorFace.GetClockPulses[] - start; IF progress THEN { IF NOT urgent THEN Process.Pause[Process.MsecToTicks[ MIN[(elapsed+99)/100 * ProcessorFace.microsecondsPerHundredPulses, 500]]]; } ELSE Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; }; --FileBackdoor.--GetFlusher: PUBLIC ENTRY PROC [volume: Volume] RETURNS [FileBackdoor.VolumeFlusher, REF ANY] = { ENABLE UNWIND => NULL; RETURN[volume.flusher, volume.flusherData] }; --FileInternal.--Flush: PUBLIC PROC [volume: Volume, lack: VolumeFormat.LogicalPageCount] RETURNS [BOOL] = { flusher: FileBackdoor.VolumeFlusher; data: REF ANY; [flusher, data] _ GetFlusher[volume]; IF flusher # NIL AND flusher[volume, lack, data] THEN RETURN[TRUE]; RETURN[FALSE] }; --FileBackdoor.--SetFreeboard: PUBLIC ENTRY PROC [volume: Volume, freeboard: INT] = { ENABLE UNWIND => NULL; volume.freeboard _ freeboard; ConsiderFlushing[volume]; }; --FileInternal.--FindLargestFreeBlockInBiggestSubVolume: PUBLIC ENTRY PROC [volume: Volume] RETURNS [first: VolumeFormat.LogicalPage _ [0], count: VolumeFormat.LogicalPageCount _ 0, subVolume: PhysicalVolume.SubVolumeDetails _ NIL] = TRUSTED{ <> vam: VAM; pos: INT ; logicalPos: VolumeFormat.LogicalPage; currentFirst: INT ; vamLast: INT ; vam _ volume.vam; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF subVolume = NIL OR sv.first.size > subVolume.size THEN subVolume _ sv.first; ENDLOOP; vamLast _ subVolume.start + subVolume.size ; currentFirst _ vamLast + 1 ; FOR pos IN [ subVolume.start..vamLast ) DO logicalPos _ [pos]; IF Used[vam, logicalPos] OR pos = vamLast - 1 THEN { IF pos-currentFirst >= count THEN { first _ [currentFirst] ; count _ pos-currentFirst+1; } ELSE currentFirst _ vamLast + 1; } ELSE IF currentFirst = vamLast + 1 THEN currentFirst _ pos ; ENDLOOP; }; --FileInternal.--Alloc: PUBLIC ENTRY PROC [volume: Volume, first: VolumeFormat.LogicalPage, size, min: VolumeFormat.LogicalPageCount] RETURNS [given: VolumeFormat.LogicalRun] = TRUSTED { ENABLE UNWIND => NULL; <> reqSize: VolumeFormat.RunPageCount = MIN[size,LAST[VolumeFormat.RunPageCount]]; pos: VolumeFormat.LogicalPage; vam: VAM; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; IF volume.free < size THEN RETURN WITH ERROR File.Error[volumeFull]; IF size = 0 THEN RETURN[[first: first, size: 0]]; -- main algorithm gives at least one page vam _ volume.vam; pos _ IF first = 0 THEN vam.rover ELSE first; min _ MAX[ MIN[min, reqSize], 1 ]; -- otherwise the algorithm doesn't work! <> THROUGH [0 .. (vam.size+min-1)/min] DO IF pos >= vam.size THEN pos _ [0]; IF NOT Used[vam, pos] THEN { <> start, end: VolumeFormat.LogicalPage _ pos; given.size _ 1; WHILE start > 0 AND given.size < reqSize DO IF Used[vam, [start-1]] THEN EXIT; start _ [start-1]; given.size _ given.size + 1; ENDLOOP; WHILE end < vam.size-1 AND given.size < reqSize DO IF Used[vam, [end+1]] THEN EXIT; end _ [end+1]; given.size _ given.size + 1; ENDLOOP; IF given.size >= min THEN { given.first _ start; EXIT }; }; pos _ [pos+min]; REPEAT FINISHED => RETURN WITH ERROR File.Error[volumeFull] ENDLOOP; volume.vamChanged _ TRUE; FOR i: INT IN [0..given.size) DO SetUsed[volume, [given.first+i], TRUE] ENDLOOP; IF first = 0 THEN vam.rover _ given.first; ConsiderFlushing[volume]; }; --FileInternal.--Free: PUBLIC ENTRY PROC [volume: Volume, logicalRun: VolumeFormat.LogicalRun] = { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; volume.vamChanged _ TRUE; FOR i: VolumeFormat.RunPageCount IN [0..logicalRun.size) DO SetUsed[volume, [logicalRun.first + i], FALSE]; ENDLOOP; }; <<******** Creation and writing of the VAM file ********>> <<>> <> vamFileRelease: CONDITION; GrabVAMFile: ENTRY PROC [volume: Volume] RETURNS [file: File.Handle] = TRUSTED { ENABLE UNWIND => NULL; DO WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; IF (file _ volume.vamFile) # NIL THEN { volume.vamFile _ NIL; EXIT }; WAIT vamFileRelease; -- because we must be in the process of erasing the volume ENDLOOP; volume.vamChanged _ FALSE; -- because our caller is about to write it to disk }; ReleaseVAMFile: ENTRY PROC [volume: Volume, file: File.Handle, changed: BOOL] = TRUSTED { ENABLE UNWIND => NULL; volume.vamChanged _ volume.vamChanged OR changed--indicates if commit failed--; IF (volume.vamFile _ file) = NIL THEN volume.vamStatus _ inconsistent; BROADCAST vamFileRelease; }; --FileInternal.--Commit: PUBLIC PROC [volume: Volume] = TRUSTED { <> <> vamFile: File.Handle _ GrabVAMFile[volume]; -- raises File.Error if vamFile isn't ok. InnerCommit[volume, vamFile ! File.Error => { ReleaseVAMFile[volume, vamFile, TRUE]; vamFile _ NIL }; -- release our lock UNWIND => IF vamFile # NIL THEN ReleaseVAMFile[volume, vamFile, TRUE] ]; ReleaseVAMFile[volume, vamFile, FALSE]; }; InnerCommit: PROC [volume: Volume, vamFile: File.Handle] = TRUSTED { filePageCount: File.PageCount = VAMFilePages[volume.size]; vmPagesPerFilePage: INT = VM.PagesForWords[DiskFace.wordsPerPage]; FOR filePage: File.PageNumber _ [0], [filePage+1] UNTIL filePage = filePageCount DO fileBaseAddr: LONG POINTER = LOOPHOLE[volume.vam + filePage * DiskFace.wordsPerPage]; baseVMPage: VM.PageNumber = VM.PageNumberForAddress[fileBaseAddr]; FOR i: INT IN [0..vmPagesPerFilePage) DO IF VM.State[baseVMPage+i].dataState = changed THEN { VM.MakeUnchanged[[page: baseVMPage, count: vmPagesPerFilePage]]; File.Write[vamFile, filePage, 1, fileBaseAddr]; EXIT }; ENDLOOP; ENDLOOP; }; --FileBackdoor.--EraseVolume: PUBLIC PROC [volume: Volume] = TRUSTED { EntryErase[volume]; <> { ENABLE UNWIND => ReleaseVAMFile[volume, NIL, TRUE];-- vamState _ inconsistent vamFile: File.Handle _ NIL; NoteBad: PROC [bad: VolumeFormat.LogicalPage] = CHECKED { Silly: ENTRY PROC [volume: Volume] = { SetUsed[volume, bad, TRUE] }; Silly[volume] }; LongFree: PROC [first: VolumeFormat.LogicalPage, size: INT] = TRUSTED { <> WHILE size > 0 DO amount: CARDINAL = MIN[size, LAST[VolumeFormat.RunPageCount]]; FileInternal.FreeRun[[first: first, size: amount], volume]; first _ [first+amount]; size _ size - amount; ENDLOOP; }; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO <> IF volRoot >= sv.first.start AND volRoot < sv.first.start + sv.first.size THEN { <> IF volRoot > 0 THEN LongFree[first: [sv.first.start+1], size: volRoot-sv.first.start-1]; LongFree[first: [volRoot+1], size: sv.first.size-(volRoot-sv.first.start)-1]; } ELSE LongFree[first: [sv.first.start+1], size: sv.first.size-1]; <> FileInternal.GetBadPages[sv.first, NoteBad]; ENDLOOP; vamFile _ File.Create[volume, VAMFilePages[volume.size]]; InnerCommit[volume, vamFile]; FileBackdoor.SetRoot[VAM, vamFile]; ReleaseVAMFile[volume, vamFile, FALSE]; }; }; EntryErase: ENTRY PROC [volume: Volume] = TRUSTED { ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; WHILE volume.vamStatus = ok AND volume.vamFile = NIL DO WAIT vamFileRelease ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; volume.vamStatus _ inconsistent; volume.vamFile _ NIL; EraseRoot[volume]; AllocVAM[volume]; volume.free _ volume.vam.size; -- to prevent underflow in SetUsed FOR i: INT IN [0..volume.vam.size) DO SetUsed[volume, [i], TRUE] ENDLOOP; volume.free _ 0; volume.vamStatus _ ok; -- but we still own the (not-yet-created) volume.vamFile }; CommitVAMFiles: PROC = { commitPause: Process.Ticks _ Process.SecondsToTicks[1]; DO FOR this: Volume _ File.NextVolume[NIL,FALSE], File.NextVolume[this,FALSE] UNTIL this = NIL DO IF this.vamChanged--in monitor!-- THEN Commit[this ! File.Error => CONTINUE]; ENDLOOP; Process.Pause[commitPause]; ENDLOOP; }; TRUSTED { Process.Detach[FORK CommitVAMFiles[]]; }; }. <> <> <> <> <<>>