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 ’BackupLogImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Carl Hauser, March 12, 1986 4:16:42 pm PST Check the first record to see that it really is a RecordIDRecord. Now position as requested. BackupLog.Format BackupLog.Write Check the first record to see that it really is a RecordIDRecord. Read the first record to find the starting RecordID in this backup log file BackupLog.Read Κƒ˜codešœ™Kšœ Οmœ7™BK™*—˜šΟk ˜ K˜K˜ K˜ K˜Kšžœ˜Kšžœ˜K˜ K˜ K˜K˜K˜——K˜šΠbl œžœž˜šž˜Kšžœ˜Kšžœ˜K˜ —šž˜K˜ Kšœž˜K˜—šž˜K˜ —K˜K˜Kšœ žœ˜,Kšœžœ˜(Kšœ žœ˜.Kšœ žœ˜*K˜5Kšœ žœ ˜0Kšœ žœ˜.Kšœ žœ˜'Kšœ žœ˜%Kšœ žœ˜$K˜0Kšœžœ˜Kšœ žœ˜(Kšœžœ"˜8K˜Kšœžœžœ˜%Kšœžœžœ˜'—K˜Kšœ žœžœ˜K˜šœžœ˜&Kšœ žœ˜Kšœžœ˜#Kšœžœ˜Kšœžœ˜Kšœž˜K˜K˜—š œžœžœž œžœ˜1K˜Kšœ˜Kšœž˜K˜—K˜š Οnœžœžœžœžœ˜eKšœ˜Kšœ žœ£žœ˜ΈK˜šœA™AKš žœLžœžœžœžœ˜–Kšžœ#žœžœ˜I—˜K˜(K˜—™KšœR˜R—K˜—K˜š Πbn œžœžœžœ žœžœ˜UKšžœžœ˜Kš žœ žœžœžœžœ#˜PK˜K˜—Kšœ™š œžœžœžœ žœžœ"žœ˜†Kšœ;˜;Kšœ žœ ˜Kšœ žœ4žœ ˜PKšœ žœ˜˜¦KšœFžœ žœ˜ZK˜K˜Kšžœ/žœ˜K˜—K˜K˜Kšœ žœžœ˜K˜K˜š  œž œžœžœžœ˜cKšœ˜Kšœžœ’žœ˜ΉK˜šœA™AKš žœPžœžœžœžœ˜˜Kšžœ#žœžœ˜IK˜—™KK˜*Kšœ>žœ˜D—K˜K˜—K˜Kšœ™š œžœ žœ˜!Kšœ žœ˜Kšœb˜bšœ'˜'Kšœ)˜)—Kšœ˜šœ žœžœž˜Kšœ ˜ Kšœ)˜)KšžœΟc˜0—K˜—K˜š œž œžœ˜$Kšžœžœžœ˜.šžœ žœžœ˜K˜Kšœ˜Kšœ žœ˜K˜—K˜—Kšžœ˜K˜Kšžœž˜ K˜—…—(M