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 {exists _ FALSE; CONTINUE}]; IF exists THEN WITH r: header SELECT type FROM create => BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead ERROR; FileInstance.SetMaxDeltaVersion[fileInstance, 1]; FileInstance.Unregister[fileInstance]; END; FOR type: DefinedFileRecord IN DefinedFileRecord DO AlpineLog.RegisterRecoveryProc[type, FileRecoveryProc]; ENDLOOP; END. ŠFileLogImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by: Taft on April 10, 1983 3:16 pm MBrown on January 30, 1984 1:48:20 pm PST Hauser, March 7, 1985 2:57:05 pm PST Carl Hauser, March 18, 1986 10:07:37 am PST FileLog. Internal procedures Attempt to read the longest possible FileLogRecord, and make sure it is at least as long as the shortest possible FileLogRecord. Recover the update only if the file still exists, under the assumption that if it does not exist then it must have been deleted by some later committed transaction. GetSize is an efficient way to check for the existence of a file because FilePageMgr caches the file's existence and size. owner is not actually needed for recovery, so omit this: owner: OwnerName; Fetch: SAFE PROCEDURE RETURNS [c: CHAR] = TRUSTED {c _ r.owner.text[index]; index _ index+1}; index: CARDINAL _ 0; owner _ Rope.FromProc[len: r.owner.length, p: Fetch]; Nothing to be done: the change is also represented in a writeLeaderPage type log record where it will be taken care of. Nothing to be done: the change is already represented in the file. Unregister to balance the above Register, so the fileInstance use count will be zero when recovery finished. Pretend that an update has occurred (even if it hasn't) so that the fileInstance will stay around. Module initialization Hauser, March 7, 1985 2:56:28 pm PST Added copyright. ΚE– "cedar" style˜šœ™Icodešœ Οmœ1™<—šœ™Jšœ™Jšœ)™)K™$K™+—unitšΟk ˜ K˜Kšœžœ˜K˜ K˜K˜K˜ K˜ Kšœ˜K˜ Kšœžœ˜!—šœ ž˜Kšžœ@˜GKšžœ˜Kšžœ˜ Kšžœžœ ˜—J™™šΟn œžœž œb˜}Kšž˜K˜1Kšœžœ˜$Kšœžœžœž œO˜ƒKšœ,žœ žœMžœ˜“K˜eKšœžœ+žœžœ˜ΌKšžœ˜—šŸ œžœž œ&˜AKšž˜Kšœ+žœ žœ˜UK˜eKšœžœ˜Kšžœ˜—šŸ œžœž œ;˜WKšž˜Kšœ,žœ žœ+˜iK˜eKšœ€žœ˜ŸKšžœ˜—š Ÿ œžœž œ,žœžœ7žœ˜°Kšž˜K˜aKšœ/žœ žœUžœ˜K˜eKšœ—žœ0˜ΛKšžœ˜—š Ÿ œžœž œ,žœžœ+žœ'˜―Kšž˜K˜"K˜!KšœTžœ˜vKšžœžœžœΟc˜AKšžœ6žœžœ  ˜Ršžœ,žœN˜~Kšž œ F˜S—KšœEžœž œš˜‰Kšžœžœžœ *˜QKšžœ˜!Kšžœ˜—šŸœžœž œTžœ˜šKšž˜K˜rKšœ4žœ žœBžœ˜K˜eKšœœžœ5˜ΥKšžœ˜—šŸœžœž œ<žœ3˜šKšž˜K˜'K˜!KšœTžœ#˜{Kšžœžœžœ ˜AKšžœ6žœžœ  ˜RKšœ>˜>KšœEžœ{˜ΔKšžœžœžœ *˜HKšžœ˜K˜—šŸžœž œ$Ÿœ˜`Kšž˜Kšœ.žœ žœ9˜yK˜eKšœ‚žœ˜£Kšžœ˜K˜—šŸžœž œ$Ÿœ˜^Kšž˜Kšœ5žœ žœ1˜xK˜eKšœ‰žœ$˜±Kšžœ˜K˜—šŸ œžœž œ&˜CKšž˜K˜3Kšœ)žœ žœžœ˜oK˜eKšœ}žœ˜™Kšžœ˜——J˜J˜šœ™šŸœ "œ˜MKšž˜K˜"K˜Kšœ žœ˜Kšœžœžœ˜Kš žœžœžœžœžœ˜,Jšœ€™€Kšœcžœ˜yKš žœ žœžœžœ ˜MK˜eJšœŸ™Ÿ˜CKšœ$žœžœ˜6—š žœžœžœ žœž˜.˜ Kšž˜šœ8™8Jšœ™šœžœžœž™1Jšœ+™+—Jšœžœ™—š žœ žœžœžœ ˜MJšœ5™5—K˜XKšžœ˜—˜ Kšž˜Kš žœ žœžœžœ ˜MK˜