<> <> <> <> <> <> <> DIRECTORY AlpineEnvironment, Basics USING [bytesPerWord], FileInstance, FileLockFormat, FileLog, FilePageMgr, LeaderPage, LeaderPageFormat, AlpineLog, Rope USING [InlineFlatten, Text]; FileLogImpl: PROGRAM IMPORTS FileInstance, FileLog, FilePageMgr, LeaderPage, AlpineLog, Rope EXPORTS FileLog SHARES Rope = BEGIN OPEN FileLog; <<>> <> LogCreate: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, initialSize: PageCount, owner: AlpineEnvironment.OwnerName] = BEGIN textOwner: Rope.Text = Rope.InlineFlatten[owner]; length: CARDINAL = textOwner.length; textBlock: AlpineLog.Block _ [ base: BASE[DESCRIPTOR[textOwner.text]], length: (length+Basics.bytesPerWord-1)/Basics.bytesPerWord]; record: FileLogRecord[create] _ [ volumeID: NULL, fileID: NULL, specifics: create [initialSize: initialSize, owner: [length: length, text: NULL]]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: create, recordData: [base: @record, length: SIZE[FileLogRecord[create]], rest: @textBlock], force: TRUE]; END; LogDelete: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle] = BEGIN record: FileLogRecord[delete] _ [volumeID: NULL, fileID: NULL, specifics: delete []]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: delete, recordData: [base: @record, length: SIZE[FileLogRecord[delete]]]]; END; LogSetSize: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, old, new: PageCount] = BEGIN record: FileLogRecord[setSize] _ [volumeID: NULL, fileID: NULL, specifics: setSize [old: old, new: new]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: setSize, recordData: [base: @record, length: SIZE[FileLogRecord[setSize]]]]; END; LogWritePages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER, pageRun: PageRun, referencePattern: ReferencePattern] RETURNS [recordID: LogRecordID] = BEGIN dataBlock: AlpineLog.Block _ [base: where, length: pageRun.count*AlpineEnvironment.wordsPerPage]; record: FileLogRecord[writePages] _ [volumeID: NULL, fileID: NULL, specifics: writePages [pageRun: pageRun, referencePattern: referencePattern, data: NULL]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [thisRecord: recordID] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: writePages, recordData: [base: @record, length: SIZE[FileLogRecord[writePages]], rest: @dataBlock]]; END; LogReadPages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER, pageRun: PageRun, recordID: LogRecordID] RETURNS [referencePattern: ReferencePattern] = BEGIN header: FileLogRecord[writePages]; status: AlpineLog.ReadProcStatus; [status: status] _ AlpineLog.Read[thisRecord: recordID, to: [base: @header, length: SIZE[FileLogRecord[writePages]]]]; IF status=sourceExhausted THEN ERROR; -- wrong length log record IF FileInstance.GetFileID[fileInstance] # header.fileID THEN ERROR; -- wrong file IF pageRun.firstPageheader.pageRun.firstPage+header.pageRun.count THEN ERROR; -- pageRun is not a subinterval of the one contained in the log record [status: status] _ AlpineLog.Read[thisRecord: recordID, wordsToSkip: SIZE[FileLogRecord[writePages]] + CARDINAL[pageRun.firstPage-header.pageRun.firstPage] * AlpineEnvironment.wordsPerPage, to: [base: where, length: pageRun.count * AlpineEnvironment.wordsPerPage]]; IF status=sourceExhausted THEN ERROR; -- log record not as long as it says it is RETURN [header.referencePattern]; END; LogWriteLeaderPages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, leaderPage: LeaderPageFormat.LeaderPageHandle] RETURNS [recordID: LogRecordID] = BEGIN dataBlock: AlpineLog.Block _ [base: @leaderPage.record, length: AlpineEnvironment.wordsPerPage*leaderPage.nPages]; record: FileLogRecord[writeLeaderPage] _ [volumeID: NULL, fileID: NULL, specifics: writeLeaderPage [pageCount: leaderPage.nPages, data: NULL]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [thisRecord: recordID] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: writeLeaderPage, recordData: [base: @record, length: SIZE[FileLogRecord[writeLeaderPage]], rest: @dataBlock]]; END; LogReadLeaderPages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, recordID: LogRecordID] RETURNS[ leaderPage: LeaderPageFormat.LeaderPageHandle ] = BEGIN header: FileLogRecord[writeLeaderPage]; status: AlpineLog.ReadProcStatus; [status: status] _ AlpineLog.Read[thisRecord: recordID, to: [base: @header, length: SIZE[FileLogRecord[writeLeaderPage]]]]; IF status=sourceExhausted THEN ERROR; -- wrong length log record IF FileInstance.GetFileID[fileInstance] # header.fileID THEN ERROR; -- wrong file leaderPage _ LeaderPage.newLeaderPageObject[header.pageCount]; [status: status] _ AlpineLog.Read[thisRecord: recordID, wordsToSkip: SIZE[FileLogRecord[writeLeaderPage]], to: [base: @leaderPage.record, length: AlpineEnvironment.wordsPerPage*header.pageCount]]; IF status#normal THEN ERROR; -- log record not as long as it says it is END; LogChangeHWM: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, oldHWM, newHWM: PageCount] = BEGIN record: FileLogRecord[changeHWM] _ [volumeID: NULL, fileID: NULL, specifics: changeHWM [oldHWM: oldHWM, newHWM: newHWM]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: changeHWM, recordData: [base: @record, length: SIZE[FileLogRecord[changeHWM]]]]; END; LogWritePagesToBase: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, pageRun: PageRun] = BEGIN record: FileLogRecord[writePagesToBase] _ [volumeID: NULL, fileID: NULL, specifics: writePagesToBase[pageRun: pageRun]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: writePagesToBase, recordData: [base: @record, length: SIZE[FileLogRecord[writePagesToBase]]]]; END; LogFileLock: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle] = BEGIN lockSubID: FileLockFormat.FileLockSubID = [file[]]; record: FileLogRecord[lock] _ [volumeID: NULL, fileID: NULL, specifics: lock [lockSubID: LOOPHOLE[lockSubID]]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ AlpineLog.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: lock, recordData: [base: @record, length: SIZE[FileLogRecord[lock]]]]; END; <> FileRecoveryProc: AlpineLog.RecoveryProc --[record, type, trans, outcome]-- = BEGIN fileInstance: FileInstance.Handle; header: FileLogRecord; wordsRead: CARDINAL; exists: BOOLEAN _ TRUE; IF type NOT IN DefinedFileRecord THEN ERROR; <> [wordsRead: wordsRead] _ AlpineLog.ReadForRecovery[thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]]; IF wordsRead> [] _ FilePageMgr.GetSize[FileInstance.GetFileHandle[fileInstance] ! FilePageMgr.NoSuchFile => {exists _ FALSE; CONTINUE}]; IF exists THEN WITH r: header SELECT type FROM create => BEGIN <> <> <> <<{c _ r.owner.text[index]; index _ index+1};>> <> IF wordsRead> RecoverCreate[fileInstance: fileInstance, initialSize: r.initialSize, outcome: outcome]; END; delete => BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead> END; writePagesToBase => BEGIN IF wordsRead> END; ENDCASE => ERROR; <> FileInstance.SetMaxDeltaVersion[fileInstance, 1]; FileInstance.Unregister[fileInstance]; END; <> FOR type: DefinedFileRecord IN DefinedFileRecord DO AlpineLog.RegisterRecoveryProc[type, FileRecoveryProc]; ENDLOOP; END. <> <> <<>>