<> <> <> DIRECTORY AlpineEnvironment, AlpineLog, BackupLog, Basics, FS, IO, LogBasic, LogInline, LogRep, Rope, Worker; BackupLogImpl: CEDAR MONITOR IMPORTS FS, IO, LogInline EXPORTS BackupLog = BEGIN OPEN BackupLog; VolumeID: TYPE = AlpineEnvironment.VolumeID; FileID: TYPE = AlpineEnvironment.FileID; FileStore: TYPE = AlpineEnvironment.FileStore; TransID: TYPE = AlpineEnvironment.TransID; nullTransID: TransID = AlpineEnvironment.nullTransID; PageNumber: TYPE = AlpineEnvironment.PageNumber; PageCount: TYPE = AlpineEnvironment.PageCount; WordNumber: TYPE = LogBasic.WordNumber; WordCount: TYPE = LogBasic.WordCount; RecordID: TYPE = AlpineLog.RecordID; nullRecordID: RecordID = AlpineLog.nullRecordID; Block: TYPE = AlpineLog.Block; RecordType: TYPE = AlpineLog.RecordType; wordsPerPage: CARDINAL = AlpineEnvironment.wordsPerPage; ClientProgrammingError: ERROR = CODE; InternalProgrammingError: ERROR = CODE; backupLog: IO.STREAM; startBackupID: RecordID; logStreamOptions: FS.StreamOptions = [ tiogaRead: FALSE, commitAndReopenTransOnFlush: FALSE, truncatePagesOnClose: FALSE, finishTransOnClose: FALSE, closeFSOpenFileOnClose: TRUE ]; RecordIDRecord: TYPE = MACHINE DEPENDENT RECORD [ pad: [0..1] _ 0, type: RecordType _ logRecordID, recordID: RecordID _ TRASH ]; OpenForNormalOperation: PUBLIC PROC [backupLogName: Rope.ROPE, initialPosition: RecordID] ~ TRUSTED { recordIDRecord: RecordIDRecord; backupLog _ FS.StreamOpen[ fileName: backupLogName, accessOptions: $write, streamOptions: logStreamOptions, streamBufferParms: [vmPagesPerBuffer: 64, nBuffers: 2], remoteCheck: FALSE]; <> IF backupLog.UnsafeGetBlock[block: MakeBlock[@recordIDRecord, (RecordIDRecord.SIZE)*2]] # (RecordIDRecord.SIZE)*2 THEN ERROR InternalProgrammingError; IF recordIDRecord.type # logRecordID THEN ERROR InternalProgrammingError; startBackupID _ recordIDRecord.recordID; <> backupLog.SetIndex[LogInline.WordsFromSubtract[initialPosition, startBackupID]*2]; }; MakeBlock: PROC [base: LONG POINTER, count: CARDINAL] RETURNS [Basics.UnsafeBlock] ~ TRUSTED INLINE { RETURN[ [base: LOOPHOLE[base, LONG POINTER TO Basics.RawBytes], count: count] ]; }; <> Format: PUBLIC PROC [backupLogName: Rope.ROPE, pages: INT, firstRecordID: RecordID] RETURNS[firstUsableRecordID: RecordID] = TRUSTED { recordIDRecord: RecordIDRecord _ [recordID: firstRecordID]; openFile: FS.OpenFile; openFile _ FS.Create[name: backupLogName, pages: pages, setKeep: TRUE, keep: 1]; backupLog _ FS.StreamFromOpenFile[ openFile: openFile, accessRights: $write, streamOptions: logStreamOptions, streamBufferParms: [vmPagesPerBuffer: 64, nBuffers: 2]]; [] _ Write[recordData: [base: @recordIDRecord, length: RecordIDRecord.SIZE], force: TRUE]; Force[]; backupLog.Close[]; RETURN[LogInline.AddLC[firstRecordID, RecordIDRecord.SIZE]]; }; Force: PUBLIC PROC [] = { backupLog.Flush[]; }; <> Write: PUBLIC UNSAFE PROC [continuation: BOOL _ FALSE, recordData: BackupLog.Block, force: BOOL] RETURNS [followingRecord: RecordID] = TRUSTED { FOR b: LONG POINTER TO BackupLog.Block _ @recordData, b.rest WHILE b # NIL DO backupLog.UnsafePutBlock[block: MakeBlock[b.base, b.length*2]]; ENDLOOP; followingRecord _ LogInline.AddLC[startBackupID, backupLog.GetIndex/2]; }; RecordIDOfNextWrite: PUBLIC PROC [] RETURNS [RecordID] = TRUSTED { RETURN [LogInline.AddLC[startBackupID, backupLog.GetIndex/2]]; }; recoveryLog: IO.STREAM; startRecoveryID: RecordID; OpenForRecovery: PUBLIC PROC [backupLogName: Rope.ROPE] RETURNS [firstRecord: RecordID] ~ TRUSTED { recordIDRecord: RecordIDRecord; recoveryLog _ FS.StreamOpen[ fileName: backupLogName, accessOptions: $read, streamOptions: logStreamOptions, streamBufferParms: [vmPagesPerBuffer: 64, nBuffers: 2], remoteCheck: FALSE]; <> IF recoveryLog.UnsafeGetBlock[block: MakeBlock[@recordIDRecord, 2*(RecordIDRecord.SIZE)]] # 2*(RecordIDRecord.SIZE) THEN ERROR InternalProgrammingError; IF recordIDRecord.type # logRecordID THEN ERROR InternalProgrammingError; <> startRecoveryID _ recordIDRecord.recordID; firstRecord _ LogInline.AddLC[startRecoveryID, RecordIDRecord.SIZE]; }; <> Read: PUBLIC ReadProc = TRUSTED { bytesRead: INT; recoveryLog.SetIndex[ LogInline.WordsFromSubtract[thisRecord, startRecoveryID]*2 + 2*wordsToSkip]; bytesRead _ recoveryLog.UnsafeGetBlock[ block: MakeBlock[ to.base, to.length*2]]; wordsRead _ bytesRead / 2; status _ SELECT TRUE FROM wordsRead = to.length => normal, wordsRead < to.length => sourceExhausted, ENDCASE => destinationFull; -- shouldn't occur; }; Close: PUBLIC PROC [] RETURNS [] ~ { IF recoveryLog # NIL THEN recoveryLog.Close[]; IF backupLog # NIL THEN { Force[]; backupLog.Close[]; backupLog _ NIL; }; }; END. CHANGE LOG