<> <> <> <> <> <> <> <<>> DIRECTORY Disk USING[ Channel, invalid, Label, ok, PageCount, PageNumber, Request, Status ], DiskFace USING[ AbsID, DontCare, RelID, Tries, wordsPerPage ], File USING[ Error, FP, GetVolumeID, PageCount, PageNumber, RC, Volume, VolumeID ], FileBackdoor USING [], FileInternal, VolumeFormat USING[ AbsID, Attributes, lastLogicalRun, LogicalPage, LogicalPageCount, LogicalRun, LogicalRunObject, RelID, RunPageCount ], VM USING[AddressForPageNumber, Allocate, Free, Interval, PageCount, PageNumber, PageNumberForAddress, PagesForWords, SwapIn], VMBacking USING[RunTableIndex, RunTableObject, RunTablePageNumber]; FilePagesImpl: CEDAR MONITOR LOCKS FileInternal.FileImplMonitorLock IMPORTS File, FileInternal, VM EXPORTS DiskFace, File, FileBackdoor, FileInternal SHARES File = { <> <<>> --DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes; --DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID; --DiskFace.--RelID: PUBLIC TYPE = VolumeFormat.RelID; Handle: TYPE = REF Object; --File.--Object: PUBLIC TYPE = FileInternal.Object; RunTable: TYPE = FileInternal.RunTable; RunTableObject: TYPE = VMBacking.RunTableObject; RunTableIndex: TYPE = VMBacking.RunTableIndex; PhysicalRun: TYPE = FileInternal.PhysicalRun; lastRun: VMBacking.RunTablePageNumber = LAST[INT]; -- end marker in runTable -- MaxTransferRun: PUBLIC Disk.PageCount _ 200; <> scratchWriter: PUBLIC LONG POINTER; -- scratch buffer for writing (all 0) scratchReader: PUBLIC LONG POINTER; -- scratch buffer for reading badData: Disk.Status = [unchanged[dataCRCError]]; -- indicates data is bad but label is ok labelTries: DiskFace.Tries = 5; notReallyFree: PUBLIC INT _ 0; <> <<******** Subroutines for access to file pages ******** -->> HeaderLabel: PUBLIC PROC[fp: File.FP] RETURNS[Disk.Label] = { <> RETURN[ [ fileID: [rel[RelID[fp]]], filePage: 0, attributes: Attributes[header], dontCare: LOOPHOLE[LONG[0]] ] ] }; DataLabel: PUBLIC PROC[fp: File.FP] RETURNS[Disk.Label] = { <> RETURN[ [ fileID: [rel[RelID[fp]]], filePage: 0, attributes: Attributes[data], dontCare: LOOPHOLE[LONG[0]] ] ] }; FreeLabel: PUBLIC PROC[volume: File.Volume] RETURNS[Disk.Label] = { <> RETURN[ [ fileID: [abs[AbsID[File.GetVolumeID[volume]]]], filePage: 0, attributes: Attributes[freePage], dontCare: LOOPHOLE[LONG[0]] ] ] }; WriteLabels: PUBLIC PROC[channel: Disk.Channel, diskPage: Disk.PageNumber, count: Disk.PageCount, data: LONG POINTER, label: POINTER TO Disk.Label] RETURNS[ status: Disk.Status, countDone: Disk.PageCount] = TRUSTED { <> req: Disk.Request _ [ diskPage: diskPage, data: IF data = NIL THEN scratchWriter ELSE data, incrementDataPtr: data # NIL, command: [header: verify, label: write, data: write], count: count ]; [status, countDone] _ FileInternal.DoPinnedIO[channel, label, @req]; }; VerifyLabels: PUBLIC PROC[channel: Disk.Channel, diskPage: Disk.PageNumber, count: Disk.PageCount, label: POINTER TO Disk.Label] RETURNS[ status: Disk.Status, countDone: Disk.PageCount] = TRUSTED { <> req: Disk.Request _ [ diskPage: diskPage, data: FileInternal.scratchReader, incrementDataPtr: FALSE, command: [header: verify, label: verify, data: read], count: count, tries: labelTries ]; [status, countDone] _ FileInternal.DoPinnedIO[channel, label, @req]; IF status = badData THEN { status _ Disk.ok; countDone _ countDone+1; label.filePage _ label.filePage+1 }; }; GetScratchPage: PUBLIC PROC RETURNS [data: LONG POINTER] = TRUSTED { <> temp: LONG POINTER TO ARRAY [0..DiskFace.wordsPerPage) OF WORD; interval: VM.Interval _ VM.Allocate[VM.PagesForWords[DiskFace.wordsPerPage]]; VM.SwapIn[interval]; data _ VM.AddressForPageNumber[interval.page]; temp _ LOOPHOLE[data]; temp^ _ ALL[0]; }; FreeScratchPage: PUBLIC PROC[data: LONG POINTER] = TRUSTED { <> VM.Free[ [ page: VM.PageNumberForAddress[data], count: VM.PagesForWords[DiskFace.wordsPerPage] ] ]; }; <<>> AddRun: PUBLIC PROC[file: Handle, run: POINTER TO PhysicalRun, logicalPage: VolumeFormat.LogicalPage, okPages: VolumeFormat.RunPageCount] = TRUSTED { <> <> logical: LONG POINTER TO VolumeFormat.LogicalRunObject _ file.logicalRunTable; physical: RunTable = file.runTable; oldNRuns: CARDINAL _ physical.nRuns; physical.nDataPages _ file.size + okPages; IF oldNRuns > 0 AND logical[oldNRuns-1].first + logical[oldNRuns-1].size = logicalPage AND logical[oldNRuns-1].size <= LAST[VolumeFormat.RunPageCount] - okPages AND run.channel = physical[oldNRuns-1].channel THEN logical[oldNRuns-1].size _ logical[oldNRuns-1].size + okPages ELSE { <> IF physical.nRuns+1 = logical.maxRuns THEN { <> ExtendPhysicalRunTable[file]; logical _ file.logicalRunTable; -- recompute since it is changed by ExtendFileHeader in ExtendPhysicalRunTable oldNRuns _ physical.nRuns; }; physical[oldNRuns] _ run^; logical[oldNRuns].first _ logicalPage; logical[oldNRuns].size _ okPages; physical.nRuns _ oldNRuns + 1; IF physical.nRuns = physical.length THEN { <> file.runTable _ NEW[RunTableObject[physical.length*2]]; <> file.runTable.nDataPages _ physical.nDataPages; file.runTable.nRuns _ physical.nRuns; FOR i: CARDINAL IN [0..physical.length) DO file.runTable[i] _ physical[i] ENDLOOP; }; file.runTable[oldNRuns + 1].filePage _ lastRun; logical[oldNRuns + 1].first _ VolumeFormat.lastLogicalRun; }; }; ExtendPhysicalRunTable: PROC [file: Handle] = TRUSTED { <> <> newRunPages: VolumeFormat.LogicalPageCount = FileInternal.RunsToHeaderPages[file.logicalRunTable.maxRuns] + 1; diskPage1: Disk.PageNumber; restOfHeaderSize: Disk.PageCount; oldChannel: Disk.Channel; [diskPage: diskPage1, size: restOfHeaderSize, channel: oldChannel] _ FileInternal.FindRun[ start: [-file.logicalRunTable.headerPages+1], nPages: file.diskRunPages + file.diskPropertyPages - 1, runTable: file.runTable]; IF restOfHeaderSize # file.diskRunPages + file.diskPropertyPages - 1 THEN <> ERROR File.Error[fragmented]; FileInternal.ExtendFileHeader[file: file, newRunPages: newRunPages, newPropertyPages: file.diskPropertyPages]; -- recomputes logical.maxRuns }; RemoveFromRunTable: PUBLIC PROC[file: Handle, remove: INT] = TRUSTED { <> logical: LONG POINTER TO VolumeFormat.LogicalRunObject = file.logicalRunTable; physical: RunTable = file.runTable; physical.nDataPages _ file.size; WHILE remove > 0 DO IF physical.nRuns = 0 THEN ERROR File.Error[inconsistent]; { runSize: VolumeFormat.RunPageCount = logical[physical.nRuns-1].size; amount: VolumeFormat.RunPageCount = MIN[remove, runSize]; logical[physical.nRuns-1].size _ runSize - amount; IF runSize - amount = 0 THEN { <> physical.nRuns _ physical.nRuns - 1; physical[physical.nRuns].filePage _ lastRun; logical[physical.nRuns].first _ VolumeFormat.lastLogicalRun; }; remove _ remove - amount; }; ENDLOOP; }; SpliceOutDataPage: PUBLIC PROC[file: Handle, filePage: File.PageCount] RETURNS [oldPage: Disk.PageNumber _ [0], oldChannel: Disk.Channel _ NIL] = TRUSTED { <> runNumber: CARDINAL; nearTo: VolumeFormat.LogicalPage; newRun: FileInternal.PhysicalRun; newLogicalRun: VolumeFormat.LogicalRun ; labelsThisTime: Disk.PageCount _ 0; IF filePage >= file.runTable.nDataPages THEN ERROR File.Error[unknownPage]; [diskPage: oldPage, channel: oldChannel] _ FindRun[start: [filePage], nPages: 1, runTable: file.runTable]; IF file.runTable.nRuns+3 >= file.logicalRunTable.maxRuns THEN { -- may split too soon ExtendPhysicalRunTable[file]; -- make sure there is enough room FileInternal.WriteRunTable[file]; -- write it out }; [runNumber] _ SplitPhysicalRunTable[file: file, page: [filePage+file.runPages+file.propertyPages]]; IF filePage+1 < file.runTable.nDataPages THEN [] _ SplitPhysicalRunTable[file: file, page: [filePage+file.runPages+file.propertyPages+1]]; IF file.logicalRunTable.runs[runNumber].size # 1 THEN ERROR; nearTo _ file.logicalRunTable.runs[runNumber].first; WHILE labelsThisTime # 1 DO <> status: Disk.Status; labelsOK: Disk.PageCount; freeLabel: Disk.Label _ FileInternal.FreeLabel[file.volume]; newLogicalRun _ FileInternal.Alloc[volume: file.volume, first: nearTo, size: 1, min: 1 ]; freeLabel.filePage _ newLogicalRun.first; [newRun.channel, newRun.diskPage] _ FileInternal.TranslateLogicalRun[newLogicalRun, file.volume]; nearTo _ [newLogicalRun.first + 1]; -- hint for next call of Alloc, if needed TRUSTED{ [status, labelsOK] _ FileInternal.VerifyLabels[newRun.channel, newRun.diskPage, newLogicalRun.size, @freeLabel] }; IF status # Disk.ok THEN notReallyFree _ notReallyFree+1; -- statistics IF labelsOK = 1 THEN { <> dataLabel: Disk.Label _ FileInternal.DataLabel[file.fp]; dataLabel.filePage _ filePage; TRUSTED{[status, labelsThisTime] _ FileInternal.WriteLabels[newRun.channel, newRun.diskPage, 1, file.headerVM+DiskFace.wordsPerPage, @dataLabel]}; }; ENDLOOP; -- end of WHILE labelsThisTime # 1 DO file.logicalRunTable.runs[runNumber].first _ newLogicalRun.first; FileInternal.WriteRunTable[file]; [] _ FileInternal.TranslateLogicalRunTable[file: file]; <> }; SplitPhysicalRunTable: PUBLIC PROC[file: Handle, page: VolumeFormat.LogicalPage] RETURNS [runNumber: CARDINAL] = TRUSTED { <> logicalRunTable: LONG POINTER TO VolumeFormat.LogicalRunObject = file.logicalRunTable; filePage: VolumeFormat.LogicalPage _ [0]; FOR runNumber IN [0..logicalRunTable.maxRuns) DO IF logicalRunTable.runs[runNumber].first = VolumeFormat.lastLogicalRun THEN ERROR File.Error[inconsistent]; IF page = filePage THEN EXIT; -- split already in place IF page >= filePage AND page < filePage+logicalRunTable.runs[runNumber].size THEN { scratchLogicalRun: VolumeFormat.LogicalRun _ logicalRunTable.runs[runNumber]; <> FOR j: CARDINAL IN [runNumber+1..logicalRunTable.maxRuns) DO -- asumes enough room in the run table sLogicalRun: VolumeFormat.LogicalRun = logicalRunTable.runs[j]; logicalRunTable.runs[j] _ scratchLogicalRun; IF scratchLogicalRun.first = VolumeFormat.lastLogicalRun THEN EXIT; scratchLogicalRun _ sLogicalRun; ENDLOOP; logicalRunTable.runs[runNumber].size _ page - filePage; runNumber _ runNumber+1; logicalRunTable.runs[runNumber].first _ [logicalRunTable.runs[runNumber].first + page - filePage]; logicalRunTable.runs[runNumber].size _ logicalRunTable.runs[runNumber].size - page + filePage; RETURN; }; filePage _ [filePage+logicalRunTable.runs[runNumber].size]; ENDLOOP; }; FreeRun: PUBLIC PROC[logicalRun: VolumeFormat.LogicalRun, volume: File.Volume, verifyLabel: POINTER TO Disk.Label _ NIL] = { <> <> label: Disk.Label _ FreeLabel[volume]; WHILE logicalRun.size > 0 DO channel: Disk.Channel; diskPage: Disk.PageNumber; status: Disk.Status; verifyStatus: Disk.Status _ Disk.ok; thisTime: Disk.PageCount _ logicalRun.size; countDone: Disk.PageCount; Consume: PROC = { logicalRun.first _ [logicalRun.first + countDone]; logicalRun.size _ logicalRun.size - countDone; IF verifyLabel # NIL THEN TRUSTED { verifyLabel.filePage _ verifyLabel.filePage + countDone; }; countDone _ 0; }; [channel, diskPage] _ FileInternal.TranslateLogicalRun[logicalRun, volume]; IF verifyLabel # NIL THEN TRUSTED { <> temp: Disk.Label _ verifyLabel^; [verifyStatus, thisTime] _ VerifyLabels[channel, diskPage, thisTime, @temp]; }; label.filePage _ logicalRun.first; FileInternal.Free[volume, [first: logicalRun.first, size: thisTime]]; TRUSTED{[status, countDone] _ WriteLabels[channel, diskPage, thisTime, NIL, @label]}; SELECT status FROM Disk.ok => NULL; Disk.invalid => ERROR File.Error[wentOffline, diskPage+countDone]; ENDCASE => countDone _ countDone + 1; -- Page is in our file, but not writeable Consume[]; IF verifyLabel # NIL AND status = Disk.ok AND verifyStatus # Disk.ok THEN TRUSTED { <> free: Disk.Label _ FreeLabel[volume]; free.filePage _ logicalRun.first; [channel, diskPage] _ FileInternal.TranslateLogicalRun[logicalRun, volume]; [verifyStatus, countDone] _ VerifyLabels[channel, diskPage, logicalRun.size, @free]; IF verifyStatus # Disk.ok AND countDone = 0 THEN countDone _ 1 -- label isn't in our file, but isn't free, so ignore it ELSE FileInternal.Free[volume, [first: logicalRun.first, size: countDone]]; Consume[]; } ENDLOOP; }; FindRun: PUBLIC PROC[start: File.PageNumber, nPages: File.PageCount, runTable: RunTable] RETURNS [diskPage: Disk.PageNumber, size: Disk.PageCount, channel: Disk.Channel] = { <> probe: RunTableIndex _ runTable.nRuns / 2; -- NB: round down -- increment: CARDINAL _ probe; IF runTable.nRuns = 0 THEN ERROR File.Error[inconsistent]; IF start + nPages > runTable.nDataPages THEN ERROR File.Error[unknownPage, start]; IF start < runTable[0].filePage THEN ERROR File.Error[unknownPage, start]; DO increment _ (increment+1)/2; SELECT TRUE FROM runTable[probe].filePage > start => probe _ IF probe < increment THEN 0 ELSE probe-increment; runTable[probe+1].filePage <= start => probe _ MIN[probe + increment, runTable.nRuns-1]; ENDCASE => RETURN[ diskPage: [runTable[probe].diskPage + (start-runTable[probe].filePage)], size: IF start + nPages <= runTable[probe+1].filePage THEN nPages ELSE runTable[probe+1].filePage - start, channel: runTable[probe].channel ] ENDLOOP; }; maxTransferRun: Disk.PageCount _ 200; <> Transfer: PUBLIC PROC[file: Handle, data: LONG POINTER, filePage: File.PageNumber, nPages: File.PageCount, action: FileInternal.ActionType, where: FileInternal.WhereLocation ] = { <> label: Disk.Label _ IF where = header THEN FileInternal.HeaderLabel[file.fp] ELSE FileInternal.DataLabel[file.fp]; label.filePage _ filePage; WHILE nPages > 0 DO status: Disk.Status; countDone: Disk.PageCount; channel: Disk.Channel; req: Disk.Request; firstDiskPage: Disk.PageNumber; req.data _ data; req.incrementDataPtr _ TRUE; req.command _ IF action = read THEN [header: verify, label: verify, data: read] ELSE [header: verify, label: verify, data: write]; TRUSTED{ [diskPage: req.diskPage, size: req.count, channel: channel] _ FileInternal.FindRun[ -- NB: first call of FindRun checks entire transfer is within file start: IF where = header THEN [-file.logicalRunTable.headerPages+filePage] ELSE filePage, nPages: nPages, runTable: file.runTable] }; firstDiskPage _ req.diskPage; IF req.count > maxTransferRun THEN req.count _ maxTransferRun; TRUSTED{[status, countDone] _ FileInternal.DoPinnedIO[channel, @label, @req]}; FileInternal.CheckStatus[status, firstDiskPage+countDone]; -- use firstDiskPage instead of req.diskPage since DiskImpl modifies req.diskPage TRUSTED{data _ data + countDone * DiskFace.wordsPerPage}; nPages _ nPages - countDone; filePage _ [filePage + countDone]; ENDLOOP; }; WriteRunTable: PUBLIC PROC[file: Handle] = TRUSTED { <> <> IF file.diskRunPages = file.runPages AND file.diskPropertyPages = file.propertyPages THEN { <> FileInternal.Transfer[file: file, data: file.headerVM, filePage: [0], nPages: file.runPages, action: write, where: header]; } ELSE { <> labelsThisTime: Disk.PageCount _ 0; size: VolumeFormat.LogicalPageCount = file.runPages + file.propertyPages - 1 ; nearTo: VolumeFormat.LogicalPage _ file.logicalRunTable.runs[0].first; <> IF file.logicalRunTable.runs[0].size # 1 THEN { [] _ FileInternal.SplitPhysicalRunTable[file: file, page: [1]]; }; IF file.logicalRunTable.runs[1].size # file.diskRunPages+file.diskPropertyPages-1 THEN { [] _ FileInternal.SplitPhysicalRunTable[file: file, page: [file.diskRunPages+file.diskPropertyPages]]; }; WHILE labelsThisTime # size DO <> logicalRun: VolumeFormat.LogicalRun; status: Disk.Status; labelsOK: Disk.PageCount; run: FileInternal.PhysicalRun; freeLabel: Disk.Label _ FileInternal.FreeLabel[file.volume]; logicalRun _ FileInternal.Alloc[volume: file.volume, first: nearTo, size: size, min: size]; freeLabel.filePage _ logicalRun.first; [run.channel, run.diskPage] _ FileInternal.TranslateLogicalRun[logicalRun, file.volume]; nearTo _ [logicalRun.first + size]; -- hint for next call of Alloc, if needed TRUSTED { [status, labelsOK] _ FileInternal.VerifyLabels[run.channel, run.diskPage, logicalRun.size, @freeLabel] }; IF status # Disk.ok THEN notReallyFree _ notReallyFree+1; -- statistics IF labelsOK = size THEN { <> headerLabel: Disk.Label _ FileInternal.HeaderLabel[file.fp]; headerLabel.filePage _ 1; TRUSTED { [status, labelsThisTime] _ FileInternal.WriteLabels[run.channel, run.diskPage, size, file.headerVM+DiskFace.wordsPerPage, @headerLabel]; }; IF labelsThisTime = size THEN { destroyLogicalRun: VolumeFormat.LogicalRun _ file.logicalRunTable.runs[1]; file.logicalRunTable.runs[1] _ logicalRun; file.diskRunPages _ file.runPages ; file.diskPropertyPages _ file.propertyPages ; file.logicalRunTable.headerPages _ file.diskRunPages + file.diskPropertyPages ; [] _ FileInternal.TranslateLogicalRunTable[file]; <> <> FileInternal.Transfer[file: file, data: file.headerVM, filePage: [0], nPages: 1, action: write, where: header]; <> headerLabel.filePage _ 1; FileInternal.FreeRun[logicalRun: destroyLogicalRun, volume: file.volume, verifyLabel: @headerLabel]; } ELSE { <> IF labelsThisTime > 0 THEN { headerLabel.filePage _ 1; FileInternal.FreeRun[ [first: [logicalRun.first], size: labelsOK], file.volume, @headerLabel]; }; IF size - labelsThisTime - 1 > 0 THEN { <> FileInternal.Free[volume: file.volume, logicalRun: [first: [logicalRun.first + labelsOK + 1], size: size - labelsThisTime - 1]]; }; }; } ELSE { <> IF labelsOK > 0 THEN FileInternal.Free[volume: file.volume, logicalRun: [first: [logicalRun.first], size: labelsOK]]; IF size - labelsOK - 1 > 0 THEN FileInternal.Free[volume: file.volume, logicalRun: [first: [logicalRun.first + labelsOK + 1], size: size - labelsOK - 1]]; }; ENDLOOP; }; }; scratchWriter _ GetScratchPage[]; scratchReader _ GetScratchPage[]; }. <<>> <<>> <<>> <> <> <<>> <> <> <> <<>>