<> <> <<>> <> <> <> DIRECTORY AccessControl USING[OperationFailed, StaticallyInvalid, Unknown], AccessControlFile USING[ComputeLastDataRecNumberFromFileLength, FirstDataRecNum, GetNextDataRecNumber, LengthToSetOwnerFile, LockConflict, OutOfRecNumRange, PagesPerRec, ReadDataRec, ReadDataRecViaRecNum, ReadHeaderRec, Stopped, UnlockDataRecViaRecNum, WordsPerRec, WriteDataRec, WriteHeaderRec], AccessControlLight USING[ApplyMinsDefaultsAndFilterRequestedWriteProperties], AccessControlMain USING[Caller, OpWeight], AccessControlMainAux USING[InitializeOwnerRecord, OverWriteOwnerSpaceInUseToRecord, ReadOwnerAccessListFromRecord, ReadOwnerQuotaFromRecord, ReadOwnerRootFileFromRecord, ReadOwnerSpaceInUseFromRecord, WriteOwnerPropsToRecord], AccessControlPrivate USING[FileDataRec, FileHeaderRec, InternalAccessControlLogicError, nullRecNumber, OwnerFileFormatVersion, PntrDataRec, PntrHeaderRec, RecNumber, SpecialOwnerForAlpineAdmin], AccessControlTransMap USING[GetTransID, Handle, nullHandle], AccessControlUtility USING[Compare, MakeOwnerStringRepFromRName, MakeRNameFromOwnerStringRep], AlpineEnvironment USING[AccessList, AccessRights, CommitOrAbort, Conversation, FileID, LockMode, nullOpenFileID, nullUniversalFile, nullVolumeGroupID, OpenFileID, OwnerName, OwnerProperty, OwnerPropertySet, OwnerPropertyValuePair, PageCount, PageNumber, TransID, UniversalFile, VolumeGroupID, VolumeID], AlpineFile USING[Create, Delete, GetSize, LockFailed, Open, OperationFailed, ReadPages, SetSize, Unknown, WritePages], AlpineIdentity USING[myLocalConversation], Convert USING[RopeFromInt], Rope USING[Cat, Concat, ROPE], SkiPatrolHooks USING[TransIDFromTransHandle], SkiPatrolLog USING[notice, OpFailureInfo]; AccessControlMainImpl: MONITOR IMPORTS AC: AccessControl, ACF: AccessControlFile, ACMA: AccessControlMainAux, ACP: AccessControlPrivate, ACL: AccessControlLight, ACU: AccessControlUtility, AF: AlpineFile, AID: AlpineIdentity, Convert, Rope, SkiPatrolHooks, SkiPatrolLog, TM: AccessControlTransMap EXPORTS AccessControl, AccessControlMain = BEGIN OPEN ACM: AccessControlMain, AE: AlpineEnvironment; <> <> <> CommonChangeOwnerAndHeader: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, caller: ACM.Caller, transHandle: TM.Handle, ownerName: AE.OwnerName, overCommitQuotasIfNeeded: BOOLEAN, ownerProperties: LIST OF AE.OwnerPropertyValuePair, newQuota: AE.PageCount] RETURNS[spaceLeftOnVolumeGroup: AE.PageCount] = TRUSTED BEGIN <> ENABLE BEGIN AC.Unknown => IF why = openFileID THEN GOTO transAborted; UNWIND => NULL; END; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileHeaderRec: ACP.FileHeaderRec; fileDataRec: ACP.FileDataRec; dataRecNum, lastDataRecNum: ACP.RecNumber; oldQuota: AE.PageCount; increaseInSpace: AE.PageCount; reclaimedRec: BOOLEAN; [volGroupKey, ownerOpenFileID, lastDataRecNum] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, light, TRUE]; ACF.ReadHeaderRec[ownerOpenFileID, write, @fileHeaderRec]; [dataRecNum, reclaimedRec] _ ACF.ReadDataRec[ownerOpenFileID, ownerName, (IF caller = add THEN wantEmpty ELSE wantOwner), write, @fileDataRec, lastDataRecNum ! AC.OperationFailed => IF ((why = ownerDatabaseFull) AND (fileHeaderRec.numberOfOwners < fileHeaderRec.maxNumberOfOwnersAllowed)) THEN ERROR]; oldQuota _ (IF caller = add THEN 0 ELSE ACMA.ReadOwnerQuotaFromRecord[@fileDataRec]); increaseInSpace _ newQuota - oldQuota; IF increaseInSpace >= 0 AND NOT overCommitQuotasIfNeeded AND fileHeaderRec.quotaLeft <= increaseInSpace THEN { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: totalQuotaExceeded, where: "AccessControlMainImpl.CommonChangeOwnerAndHeader", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Can't alter quota for owner ", ownerName] ]]; ERROR AC.OperationFailed[totalQuotaExceeded]; }; SELECT caller FROM add => { IF ((NOT reclaimedRec) AND (fileHeaderRec.numberOfOwnerSlotsInUse = fileHeaderRec.maxNumberOfOwnersAllowed)) THEN { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: ownerDatabaseFull, where: "AccessControlMainImpl.CommonChangeOwnerAndHeader", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Trying to add owner ", ownerName] ]]; ERROR AC.OperationFailed[ownerDatabaseFull]; }; ACMA.InitializeOwnerRecord[@fileDataRec]; ACU.MakeOwnerStringRepFromRName[ownerName, @fileDataRec.ownerName]; ACMA.WriteOwnerPropsToRecord[@fileDataRec, ownerProperties, ownerName]; fileDataRec.state _ valid; fileHeaderRec.numberOfOwners _ fileHeaderRec.numberOfOwners + 1; IF NOT reclaimedRec THEN fileHeaderRec.numberOfOwnerSlotsInUse _ fileHeaderRec.numberOfOwnerSlotsInUse + 1; }; <> remove => { RemoveTheOwner[volGroupKey, ownerName, transHandle, @fileDataRec]; fileDataRec.state _ deleted; fileHeaderRec.numberOfOwners _ fileHeaderRec.numberOfOwners - 1; }; <> writeProps => { ChangeQuotaOwner[volGroupKey, ownerName, transHandle, newQuota, oldQuota]; ACMA.WriteOwnerPropsToRecord[@fileDataRec, ownerProperties, ownerName]; }; ENDCASE; fileHeaderRec.quotaLeft _ fileHeaderRec.quotaLeft - increaseInSpace; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec]; ACF.WriteHeaderRec[ownerOpenFileID, @fileHeaderRec]; RETURN[fileHeaderRec.quotaLeft] EXITS transAborted => ERROR AC.Unknown[transID]; END; <> CommonChangeOwner: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerName: AE.OwnerName, ownerProperties: LIST OF AE.OwnerPropertyValuePair] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; ownerOpenFileID: AE.OpenFileID; dataRecNum: ACP.RecNumber; fileDataRec: ACP.FileDataRec; [, ownerOpenFileID, dataRecNum] _ CheckRegStatGetVolGroupOpenFileAndDemandOwnerDataRec[transHandle, @volGroupID, ownerName, write, @fileDataRec]; ACMA.WriteOwnerPropsToRecord[@fileDataRec, ownerProperties, ownerName]; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec ! AC.Unknown => IF why = openFileID THEN GOTO transAborted]; EXITS transAborted => ERROR AC.Unknown[transID]; END; ReadAllOwnerProperties: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerName: AE.OwnerName, desiredOwnerProperties: AE.OwnerPropertySet] RETURNS [ownerProperties: LIST OF AE.OwnerPropertyValuePair] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileDataRec: ACP.FileDataRec; [volGroupKey, ownerOpenFileID, ] _ CheckRegStatGetVolGroupOpenFileAndDemandOwnerDataRec[transHandle, @volGroupID, ownerName, read, @fileDataRec]; RETURN[ReadAllOwnerPropsFromRec[transHandle, volGroupKey, ownerName, desiredOwnerProperties, @fileDataRec]]; END; GetOwnerRecordForReadAndUnlock: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerName: AE.OwnerName, pntrDataRec: ACP.PntrDataRec] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; dataRecNum, lastDataRecNum: ACP.RecNumber; [volGroupKey, ownerOpenFileID, dataRecNum, lastDataRecNum] _ CheckRegStatGetVolGroupOpenFileAndDemandOwnerDataRec[transHandle, @volGroupID, ownerName, read, pntrDataRec]; ACF.UnlockDataRecViaRecNum[ownerOpenFileID, dataRecNum, lastDataRecNum ! ACF.OutOfRecNumRange => GOTO horrible; AC.Unknown => IF why = openFileID THEN GOTO transAborted]; EXITS horrible => ERROR ACP.InternalAccessControlLogicError; transAborted => ERROR AC.Unknown[transID]; END; ChangeSpaceViaOwnerName: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerName: AE.OwnerName, nPages: AE.PageCount] RETURNS [okay: BOOLEAN] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileDataRec: ACP.FileDataRec; dataRecNum, lastDataRecNum: ACP.RecNumber; [volGroupKey, ownerOpenFileID, dataRecNum, lastDataRecNum] _ CheckRegStatGetVolGroupOpenFileAndDemandOwnerDataRec[transHandle, @volGroupID, ownerName, read, @fileDataRec]; okay _ Allocate[volGroupKey, ownerName, transHandle, nPages, @fileDataRec, dataRecNum]; ACF.UnlockDataRecViaRecNum[ownerOpenFileID, dataRecNum, lastDataRecNum ! ACF.OutOfRecNumRange => GOTO horrible; AC.Unknown => IF why = openFileID THEN GOTO transAborted] EXITS horrible => ERROR ACP.InternalAccessControlLogicError; transAborted => ERROR AC.Unknown[transID]; END; <> CreateAndInitOwnerFile: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerFileVolID: AE.VolumeID, totalQuota, volumeGroupSize: AE.PageCount, overCommitQuotaDuringInitializeIfNeeded: BOOLEAN, maxNumberOfOwnersAllowed: NAT, nOwnerCacheEntries: NAT] RETURNS[ownerFileID: AE.FileID] = TRUSTED BEGIN <> ENABLE BEGIN UNWIND => NULL; AC.Unknown => IF why = openFileID THEN GOTO transAborted; END; ownerOpenFileID: AE.OpenFileID; ownerUniversalFile: AE.UniversalFile; fileSize: AE.PageCount; lastDataRecNum: ACP.RecNumber; fileHeaderRec: ACP.FileHeaderRec; fileDataRec: ACP.FileDataRec; dataRecNum: ACP.RecNumber; quotaLeft: AE.PageCount; IF maxNumberOfOwnersAllowed < 1 THEN ERROR AC.StaticallyInvalid; [, , ] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevUnreg, heavy, FALSE]; fileSize _ ACF.LengthToSetOwnerFile[MakeSparseOwnerFile[maxNumberOfOwnersAllowed]]; IF (((quotaLeft _ totalQuota - fileSize) < 0) AND (NOT overCommitQuotaDuringInitializeIfNeeded)) THEN ERROR AC.StaticallyInvalid; [ownerOpenFileID, ownerUniversalFile] _ AF.Create[AID.myLocalConversation, TM.GetTransID[transHandle], volGroupID, ACP.SpecialOwnerForAlpineAdmin, fileSize, log, sequential ! AF.OperationFailed => IF why = insufficientSpace THEN GOTO convertToACNoSpace]; ownerFileID _ ownerUniversalFile.fileID; lastDataRecNum _ ACF.ComputeLastDataRecNumberFromFileLength[fileSize]; ACF.ReadHeaderRec[ownerOpenFileID, write, @fileHeaderRec]; <> fileHeaderRec _ [version: ACP.OwnerFileFormatVersion, volGroupID: volGroupID, totalQuota: totalQuota, quotaLeft: quotaLeft, volumeGroupSize: volumeGroupSize, numberOfOwners: 1, numberOfOwnerSlotsInUse: 1, maxNumberOfOwnersAllowed: maxNumberOfOwnersAllowed, spare: ALL[0]]; ACF.WriteHeaderRec[ownerOpenFileID, @fileHeaderRec]; <> EmptyOutAnOwnerFile[ownerOpenFileID, lastDataRecNum]; <> dataRecNum _ ACF.ReadDataRec[ownerOpenFileID, ACP.SpecialOwnerForAlpineAdmin, wantEmpty, write, @fileDataRec, lastDataRecNum].dataRecNum; ACU.MakeOwnerStringRepFromRName[ACP.SpecialOwnerForAlpineAdmin, @fileDataRec.ownerName]; ACMA.WriteOwnerPropsToRecord[ @fileDataRec, CONS[ [spaceInUse[fileSize]], ACL.ApplyMinsDefaultsAndFilterRequestedWriteProperties[ CONS[[quota[fileSize]], NIL], ACP.SpecialOwnerForAlpineAdmin, TRUE ].newOwnerProperties ], ACP.SpecialOwnerForAlpineAdmin ]; fileDataRec.state _ valid; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec]; statsNInit _ statsNInit + 1; EXITS transAborted => ERROR AC.Unknown[transID]; <> convertToACNoSpace => ERROR AC.OperationFailed[insufficientSpace]; END; MakeSparseOwnerFile: INTERNAL SAFE PROCEDURE[maxNumberOfOwnersAllowed: NAT] RETURNS[newMaxNumberOfOwnersAllowed: NAT] = CHECKED BEGIN <> RETURN[(maxNumberOfOwnersAllowed*14)/10]; END; RegisterVolGroup: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerFileUniversalFile: AE.UniversalFile, nOwnerCacheEntries: NAT] RETURNS[newOwnerFileID: AE.FileID] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; ownerOpenFileID: AE.OpenFileID; fileHeaderRec: ACP.FileHeaderRec; lastDataRecNum: ACP.RecNumber; oldVolGroupKey: VolGroupKey; [oldVolGroupKey, , ] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevUnreg, heavy, FALSE]; [ownerOpenFileID, , lastDataRecNum, newOwnerFileID] _ OpenFileGetSizeAndHeaderRec[transHandle, ownerFileUniversalFile, @fileHeaderRec]; IF NOT ((fileHeaderRec.version = ACP.OwnerFileFormatVersion) AND (fileHeaderRec.volGroupID = volGroupID)) THEN { <> logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN { message: Rope.ROPE _ ""; IF fileHeaderRec.version # ACP.OwnerFileFormatVersion THEN message _ Rope.Cat[ "Format version number mismatch (expected ", Convert.RopeFromInt[ACP.OwnerFileFormatVersion], ", got ", Convert.RopeFromInt[fileHeaderRec.version], ")" ]; IF fileHeaderRec.volGroupID # volGroupID THEN { IF message # "" THEN message _ message.Concat[" AND "]; message _ message.Concat["Volume Group ID mismatch"]; }; logProc[[ what: ownerFileFormatOrVolGroupMismatch, where: "AccessControlMainImpl.RegisterVolGroup", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: message ]] }; ERROR AC.OperationFailed[ownerFileFormatOrVolGroupMismatch]; }; ownerFileUniversalFile.fileID _ newOwnerFileID; PresentDummyVolGroup[oldVolGroupKey, MakeDummyVolGroup[@volGroupID, @ownerFileUniversalFile, lastDataRecNum, fileHeaderRec.totalQuota, fileHeaderRec.quotaLeft, reg, transHandle]]; statsNRegister _ statsNRegister + 1; END; UnRegisterVolGroup: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; oldVolGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; [oldVolGroupKey, ownerOpenFileID] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, heavy, TRUE]; FlushOutAVolumeGroup[ownerOpenFileID, oldVolGroupKey, transHandle]; -- dump out any pending updates in the volatile structure. PresentDummyVolGroup[oldVolGroupKey, MakeDummyVolGroup[@volGroupID, , , , , unreg, transHandle]]; statsNUnReg _ statsNUnReg + 1; END; <> ReorgOwnerFile: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, maxNumberOfOwnersAllowed: NAT] = TRUSTED BEGIN <> ENABLE BEGIN AC.Unknown => IF why = openFileID THEN GOTO transAborted; AF.Unknown => SELECT what FROM openFileID => GOTO transAborted; transID => GOTO convertToACUnkTrans; ENDCASE => NULL; UNWIND => NULL; END; oldVolGroupKey, newVolGroupKey: VolGroupKey; ownerOpenFileID, tempOpenFileID: AE.OpenFileID; tempPageNumber: AE.PageNumber _ 0; tempDataRecNum: ACP.RecNumber; newSizeOfOwnerFile, oldSizeOfOwnerFile, tempFileSize: AE.PageCount; fileHeaderRec: ACP.FileHeaderRec; fileDataRec: ACP.FileDataRec; dataRecNum, oldLastDataRecNum, newLastDataRecNum, tempLastDataRecNum: ACP.RecNumber; currentEntries: NAT; SaveOldOwnerRecordsInTempFile: SAFE PROCEDURE = TRUSTED { tempOpenFileID _ AF.Create[AID.myLocalConversation, TM.GetTransID[transHandle], volGroupID, ACP.SpecialOwnerForAlpineAdmin, tempFileSize, log, sequential ! AF.OperationFailed => IF why = insufficientSpace THEN GOTO convertToACNoSpace].openFileID; tempLastDataRecNum _ ACF.ComputeLastDataRecNumberFromFileLength[tempFileSize]; tempDataRecNum _ ACF.FirstDataRecNum; DO ACF.ReadDataRecViaRecNum[ownerOpenFileID, tempDataRecNum, read, @fileDataRec, oldLastDataRecNum]; IF fileDataRec.state = valid THEN { AF.WritePages[AID.myLocalConversation, tempOpenFileID, [firstPage: tempPageNumber, count: ACF.PagesPerRec], DESCRIPTOR[@fileDataRec, ACF.WordsPerRec], [write, fail]]; tempPageNumber _ tempPageNumber + ACF.PagesPerRec; }; tempDataRecNum _ ACF.GetNextDataRecNumber[tempDataRecNum, oldLastDataRecNum, stopOnEof, ! ACF.Stopped => EXIT]; ENDLOOP; EXITS <> convertToACNoSpace => ERROR AC.OperationFailed[insufficientSpace]; }; CopyOldOwnerRecordsToNewFile: SAFE PROCEDURE = TRUSTED { FOR tempPageNumber _ 0, tempPageNumber + ACF.PagesPerRec UNTIL tempPageNumber >= tempFileSize DO bitDumpDataRec: ACP.FileDataRec; AF.ReadPages[AID.myLocalConversation, tempOpenFileID, [firstPage: tempPageNumber, count: ACF.PagesPerRec], DESCRIPTOR[@fileDataRec, ACF.WordsPerRec], [write, fail]]; dataRecNum _ ACF.ReadDataRec[ownerOpenFileID, ACU.MakeRNameFromOwnerStringRep[ @fileDataRec.ownerName], wantEmpty, write, @bitDumpDataRec, newLastDataRecNum].dataRecNum; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec]; ENDLOOP; AF.Delete[AID.myLocalConversation, tempOpenFileID]; }; <> [oldVolGroupKey, ownerOpenFileID, oldLastDataRecNum] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, heavy, TRUE]; ACF.ReadHeaderRec[ownerOpenFileID, write, @fileHeaderRec]; currentEntries _ fileHeaderRec.numberOfOwners; IF ((maxNumberOfOwnersAllowed = 0) OR (currentEntries > maxNumberOfOwnersAllowed)) THEN { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: ownerDatabaseFull, where: "AccessControlMainImpl.ReorgOwnerFile", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: "" ]]; ERROR AC.OperationFailed[ownerDatabaseFull]; }; newSizeOfOwnerFile _ ACF.LengthToSetOwnerFile[MakeSparseOwnerFile[(maxNumberOfOwnersAllowed + 1)]]; tempFileSize _ currentEntries*ACF.PagesPerRec; FlushOutAVolumeGroup[ownerOpenFileID, oldVolGroupKey, transHandle]; -- dump out any pending updates in the volatile structure. SaveOldOwnerRecordsInTempFile[]; oldSizeOfOwnerFile _ AF.GetSize[AID.myLocalConversation, ownerOpenFileID, [read, fail]]; <> AF.SetSize[AID.myLocalConversation, ownerOpenFileID, newSizeOfOwnerFile, [write, fail]]; newLastDataRecNum _ ACF.ComputeLastDataRecNumberFromFileLength[newSizeOfOwnerFile]; newVolGroupKey _ MakeDummyVolGroup[@volGroupID, @oldVolGroupKey.ownerFileUniversalFile, newLastDataRecNum, fileHeaderRec.totalQuota, fileHeaderRec.quotaLeft, reorg, transHandle]; EmptyOutAnOwnerFile[ownerOpenFileID, newLastDataRecNum]; CopyOldOwnerRecordsToNewFile[]; <> dataRecNum _ ACF.ReadDataRec[ownerOpenFileID, ACP.SpecialOwnerForAlpineAdmin, wantOwner, write, @fileDataRec, newLastDataRecNum].dataRecNum; ACMA.WriteOwnerPropsToRecord[@fileDataRec, CONS[[spaceInUse[newSizeOfOwnerFile]], CONS[[quota[newSizeOfOwnerFile]], NIL]], ACP.SpecialOwnerForAlpineAdmin]; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec]; fileHeaderRec.quotaLeft _ fileHeaderRec.quotaLeft - (newSizeOfOwnerFile - oldSizeOfOwnerFile); fileHeaderRec.maxNumberOfOwnersAllowed _ maxNumberOfOwnersAllowed; fileHeaderRec.numberOfOwnerSlotsInUse _ currentEntries; ACF.WriteHeaderRec[ownerOpenFileID, @fileHeaderRec]; PresentDummyVolGroup[oldVolGroupKey, newVolGroupKey]; statsNReorg _ statsNReorg + 1; EXITS convertToACUnkTrans => ERROR AC.Unknown[transID]; transAborted => ERROR AC.Unknown[transID]; END; <> OpenFileGetSizeAndHeaderRec: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, ownerFileUniversalFile: AE.UniversalFile, pntrHeaderRec: ACP.PntrHeaderRec] RETURNS [ownerOpenFileID: AE.OpenFileID, fileSize: AE.PageCount, lastDataRecNum: ACP.RecNumber, newOwnerFileID: AE.FileID] = TRUSTED BEGIN <> ENABLE BEGIN AF.Unknown => SELECT what FROM openFileID => GOTO unkOpenFileID; transID => GOTO unkTransID; ENDCASE => NULL; AC.Unknown => IF why = openFileID THEN GOTO unkOpenFileID; END; [ownerOpenFileID, newOwnerFileID] _ AF.Open[AID.myLocalConversation, TM.GetTransID[transHandle], ownerFileUniversalFile, readWrite, [write, fail], log, sequential ! AF.Unknown => SELECT what FROM volumeID => GOTO unkVolID; fileID => GOTO unkFileID; ENDCASE; AF.LockFailed => IF why = conflict THEN GOTO lockConflict]; fileSize _ AF.GetSize[AID.myLocalConversation, ownerOpenFileID, [read, fail]]; lastDataRecNum _ ACF.ComputeLastDataRecNumberFromFileLength[fileSize]; ACF.ReadHeaderRec[ownerOpenFileID, write, pntrHeaderRec]; EXITS lockConflict => ERROR ACF.LockConflict[[write, file[TM.GetTransID[transHandle], ownerFileUniversalFile, sequential]]]; unkFileID => ERROR AC.Unknown[fileID]; unkOpenFileID, unkTransID => ERROR AC.Unknown[transID]; unkVolID => ERROR AC.Unknown[volumeID]; END; <> EmptyOutAnOwnerFile: INTERNAL SAFE PROCEDURE[ownerOpenFileID: AE.OpenFileID, lastDataRecNum: ACP.RecNumber] = TRUSTED BEGIN <> dataRecNum: ACP.RecNumber _ ACF.FirstDataRecNum; fileDataRec: ACP.FileDataRec; ACMA.InitializeOwnerRecord[@fileDataRec]; DO ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec ! AC.Unknown => IF why = openFileID THEN GOTO transAborted]; dataRecNum _ ACF.GetNextDataRecNumber[dataRecNum, lastDataRecNum, stopOnEof, ! ACF.Stopped => EXIT]; ENDLOOP; EXITS transAborted => ERROR AC.Unknown[transID]; END; <> FlushOutAVolumeGroup: INTERNAL SAFE PROCEDURE[ownerOpenFileID: AE.OpenFileID, oldVolGroupKey: VolGroupKey, transHandle: TM.Handle] = TRUSTED BEGIN <> ENABLE AC.Unknown => IF why = openFileID THEN GOTO transAborted; ownerKey: OwnerKey; dataRecNum: ACP.RecNumber; lastDataRecNum: ACP.RecNumber _ FindLastDataRecNum[oldVolGroupKey]; fileDataRec: ACP.FileDataRec; [ownerKey, dataRecNum] _ FindFirstRecNeedingUpdateForThisVolGroup[oldVolGroupKey]; UNTIL ownerKey = NIL DO ACF.ReadDataRecViaRecNum[ownerOpenFileID, dataRecNum, write, @fileDataRec, lastDataRecNum]; ACMA.OverWriteOwnerSpaceInUseToRecord[ @fileDataRec, (ACMA.ReadOwnerQuotaFromRecord[@fileDataRec] - ownerKey.quotaLeft) + ownerKey.transKey.alloc + ownerKey.transKey.dealloc]; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec]; [ownerKey, dataRecNum] _ RemoveRecAndGetNextRecForVolGroup[oldVolGroupKey, ownerKey]; ENDLOOP; EXITS transAborted => ERROR AC.Unknown[transID]; END; <> EnumAllOwners: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, prevOwnerName: AE.OwnerName, desiredOwnerProperties: AE.OwnerPropertySet] RETURNS [ownerName: AE.OwnerName, ownerProperties: LIST OF AE.OwnerPropertyValuePair] = TRUSTED BEGIN <> ENABLE BEGIN UNWIND => NULL; AC.Unknown => IF why = openFileID THEN GOTO transAborted; END; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileDataRec: ACP.FileDataRec; dataRecNum, prevDataRecNum, lastDataRecNum: ACP.RecNumber; [volGroupKey, ownerOpenFileID, lastDataRecNum] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, heavy, TRUE]; IF prevOwnerName = NIL THEN dataRecNum _ ACF.FirstDataRecNum ELSE BEGIN valid: BOOLEAN; [valid, prevDataRecNum] _ FindEnumRecNum[volGroupKey, prevOwnerName]; IF (NOT valid) THEN prevDataRecNum _ ACF.ReadDataRec[ownerOpenFileID, prevOwnerName, wantOwner, read, @fileDataRec, lastDataRecNum].dataRecNum; dataRecNum _ ACF.GetNextDataRecNumber[prevDataRecNum, lastDataRecNum, stopOnEof, ! ACF.Stopped => GOTO pastEof]; END; DO ACF.ReadDataRecViaRecNum[ownerOpenFileID, dataRecNum, read, @fileDataRec, lastDataRecNum]; IF fileDataRec.state = valid THEN EXIT; dataRecNum _ ACF.GetNextDataRecNumber[dataRecNum, lastDataRecNum, stopOnEof, ! ACF.Stopped => GOTO pastEof]; ENDLOOP; ownerName _ ACU.MakeRNameFromOwnerStringRep[@fileDataRec.ownerName]; ownerProperties _ ReadAllOwnerPropsFromRec[transHandle, volGroupKey, ownerName, desiredOwnerProperties, @fileDataRec]; SetEnumRecNum[volGroupKey, ownerName, dataRecNum]; statsNEnumAll _ statsNEnumAll + 1; EXITS pastEof => RETURN[NIL, NIL]; transAborted => ERROR AC.Unknown[transID]; END; <> SickOwnerFile: ERROR = CODE; EnumAllDataEntriesInOwnerFile: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, contRecNum: INT, desiredOwnerProperties: AE.OwnerPropertySet] RETURNS [entryEmpty, entryValid: BOOLEAN, ownerName: AE.OwnerName, ownerProperties: LIST OF AE.OwnerPropertyValuePair, nextContRecNum: INT] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileDataRec: ACP.FileDataRec; dataRecNum: ACP.RecNumber _ IF contRecNum = 0 THEN ACF.FirstDataRecNum ELSE LOOPHOLE[contRecNum, ACP.RecNumber]; lastDataRecNum: ACP.RecNumber; [volGroupKey, ownerOpenFileID, lastDataRecNum] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, heavy, TRUE]; ACF.ReadDataRecViaRecNum[ownerOpenFileID, dataRecNum, read, @fileDataRec, lastDataRecNum ! ACF.OutOfRecNumRange => GOTO badRecNumber; AC.Unknown => IF why = openFileID THEN GOTO transAborted]; SELECT fileDataRec.state FROM valid, deleted => BEGIN ownerName _ ACU.MakeRNameFromOwnerStringRep[ @fileDataRec.ownerName]; ownerProperties _ ReadAllOwnerPropsFromRec[transHandle, volGroupKey, ownerName, desiredOwnerProperties, @fileDataRec]; entryEmpty _ FALSE; entryValid _ (fileDataRec.state = valid); END; empty => entryEmpty _ TRUE; ENDCASE => ERROR SickOwnerFile; nextContRecNum _ LOOPHOLE[ACF.GetNextDataRecNumber[dataRecNum, lastDataRecNum, stopOnEof, ! ACF.Stopped => GOTO done]]; statsNEnumAll _ statsNEnumAll + 1; EXITS badRecNumber => ERROR AC.StaticallyInvalid; done => nextContRecNum _ 0; transAborted => ERROR AC.Unknown[transID]; END; <> ReadOwnerFileHead: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, opWeight: ACM.OpWeight] RETURNS [version: NAT, recordedVolGroupID: AE.VolumeGroupID, totalQuota, quotaLeft, volumeGroupSize: AE.PageCount, numberOfOwners, numberOfOwnerSlotsInUse, maxNumberOfOwnersAllowed: NAT] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerOpenFileID: AE.OpenFileID; fileHeaderRec: ACP.FileHeaderRec; [volGroupKey, ownerOpenFileID, ] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, @volGroupID, prevReg, opWeight, TRUE]; ACF.ReadHeaderRec[ownerOpenFileID, read, @fileHeaderRec ! AC.Unknown => IF why = openFileID THEN GOTO transAborted]; RETURN[version: fileHeaderRec.version, recordedVolGroupID: fileHeaderRec.volGroupID, totalQuota: fileHeaderRec.totalQuota, quotaLeft: fileHeaderRec.quotaLeft, volumeGroupSize: fileHeaderRec.volumeGroupSize, numberOfOwners: fileHeaderRec.numberOfOwners, numberOfOwnerSlotsInUse: fileHeaderRec.numberOfOwnerSlotsInUse, maxNumberOfOwnersAllowed: fileHeaderRec.maxNumberOfOwnersAllowed]; EXITS transAborted => ERROR AC.Unknown[transID]; END; <> VolGroupRegStatus: TYPE = {prevReg, prevUnreg}; <> CheckRegStatGetVolGroupMaybeOpenFile: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, pntrVolGroupID: LONG POINTER TO AE.VolumeGroupID, regStatus: VolGroupRegStatus, opWeight: ACM.OpWeight, openFile: BOOLEAN] RETURNS[volGroupKey: VolGroupKey, ownerOpenFileID: AE.OpenFileID, lastDataRecNum: ACP.RecNumber] = TRUSTED BEGIN <> IF ((openFile) AND (regStatus = prevUnreg)) THEN ERROR ACP.InternalAccessControlLogicError; [volGroupKey, ownerOpenFileID] _ FindVolGroupCheckRegGetLightOpenFileID[pntrVolGroupID, regStatus, transHandle, ((opWeight = light) AND (openFile))]; IF openFile THEN { IF ownerOpenFileID = AE.nullOpenFileID THEN { ownerOpenFileID _ AF.Open[ AID.myLocalConversation, TM.GetTransID[transHandle], volGroupKey.ownerFileUniversalFile, readWrite, [(IF opWeight = light THEN intendWrite ELSE write), fail], log, (IF opWeight = light THEN random ELSE sequential) ! AF.Unknown => IF what = transID THEN GOTO unkTransID; AF.LockFailed => IF why = conflict THEN GOTO lockConflict ].openFileID; IF opWeight = light THEN SetLightOpenFileID[volGroupKey, transHandle, ownerOpenFileID]; }; lastDataRecNum _ FindLastDataRecNum[volGroupKey]; }; EXITS unkTransID => ERROR AC.Unknown[transID]; lockConflict => ERROR ACF.LockConflict[[ (IF opWeight = light THEN intendWrite ELSE write), file[ TM.GetTransID[transHandle], volGroupKey.ownerFileUniversalFile, (IF opWeight = light THEN random ELSE sequential) ] ]]; END; CheckRegStatGetVolGroupOpenFileAndDemandOwnerDataRec: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, pntrVolGroupID: LONG POINTER TO AE.VolumeGroupID, ownerName: AE.OwnerName, recLockMode: AE.LockMode, pntrDataRec: ACP.PntrDataRec] RETURNS[volGroupKey: VolGroupKey, ownerOpenFileID: AE.OpenFileID, dataRecNum, lastDataRecNum: ACP.RecNumber] = TRUSTED BEGIN <> [volGroupKey, ownerOpenFileID, lastDataRecNum] _ CheckRegStatGetVolGroupMaybeOpenFile[transHandle, pntrVolGroupID, prevReg, light, TRUE]; dataRecNum _ ACF.ReadDataRec[ownerOpenFileID, ownerName, wantOwner, recLockMode, pntrDataRec, lastDataRecNum ! AC.Unknown => IF why = openFileID THEN GOTO transAborted].dataRecNum; EXITS transAborted => ERROR AC.Unknown[transID]; END; ReadAllOwnerPropsFromRec: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, volGroupKey: VolGroupKey, ownerName: AE.OwnerName, desiredOwnerProperties: AE.OwnerPropertySet, pntrFileDataRec: ACP.PntrDataRec] RETURNS [ownerProperties: LIST OF AE.OwnerPropertyValuePair] = CHECKED BEGIN <> ownerProperty: AE.OwnerPropertyValuePair _ [quota[0]]; -- initialize so GC won't think it has a REF. ownerProperties _ NIL; FOR ownerPropType: AE.OwnerProperty IN AE.OwnerProperty DO IF desiredOwnerProperties[ownerPropType] THEN ownerProperties _ SELECT ownerPropType FROM rootFile => CONS[[rootFile[ACMA.ReadOwnerRootFileFromRecord[pntrFileDataRec]]], ownerProperties], spaceInUse => CONS[ [spaceInUse[ACMA.ReadOwnerSpaceInUseFromRecord[pntrFileDataRec] + EnumOwnerSpaceChange[volGroupKey, ownerName, transHandle]]], ownerProperties ], quota => CONS[ [quota[ACMA.ReadOwnerQuotaFromRecord[pntrFileDataRec]]], ownerProperties ], modifyAccessList => CONS[ [modifyAccessList[ACMA.ReadOwnerAccessListFromRecord[pntrFileDataRec, modify, ownerName]]], ownerProperties ], createAccessList => CONS[ [createAccessList[ACMA.ReadOwnerAccessListFromRecord[pntrFileDataRec, create, ownerName]]], ownerProperties ], ENDCASE => ERROR ACP.InternalAccessControlLogicError; ENDLOOP; END; <> PhaseOneSpaceOwnerChanges: PUBLIC ENTRY SAFE PROCEDURE[transHandle: TM.Handle] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; volGroupKey: VolGroupKey; ownerKey: OwnerKey; transKey: TransKey; ownerOpenFileID: AE.OpenFileID; dataRecNum: ACP.RecNumber; fileDataRec: ACP.FileDataRec; done: BOOLEAN; [done, volGroupKey, ownerKey, transKey, ownerOpenFileID, dataRecNum] _ RemoveOldRecAndGetNextRecForTrans[transHandle, NIL]; UNTIL done DO ACF.ReadDataRecViaRecNum[ownerOpenFileID, dataRecNum, write, @fileDataRec, FindLastDataRecNum[volGroupKey] ! ACF.OutOfRecNumRange => GOTO horrible; AC.Unknown => IF why = openFileID THEN GOTO transAborted]; ACMA.OverWriteOwnerSpaceInUseToRecord[@fileDataRec, (ACMA.ReadOwnerQuotaFromRecord[@fileDataRec] - ownerKey.quotaLeft) + transKey.alloc + transKey.dealloc]; ACF.WriteDataRec[ownerOpenFileID, dataRecNum, @fileDataRec ! AC.Unknown => IF why = openFileID THEN GOTO transAborted;]; [done, volGroupKey, ownerKey, transKey, ownerOpenFileID, dataRecNum] _ RemoveOldRecAndGetNextRecForTrans[transKey.transHandle, transKey]; REPEAT horrible => ERROR ACP.InternalAccessControlLogicError; transAborted => ERROR AC.Unknown[transID]; ENDLOOP; END; PhaseTwoOrAbortSpaceAndOwnerChanges: PUBLIC ENTRY SAFE PROCEDURE[transHandle: TM.Handle, outcome: AE.CommitOrAbort] = CHECKED BEGIN <> PhaseTwoOrAbort[transHandle, outcome]; END; <> <> regVolGroups: VolGroupKey _ NEW[RegVolGroup _ [AE.nullVolumeGroupID, AE.nullUniversalFile, ACP.nullRecNumber, 0, 0, [NIL, ACP.nullRecNumber], header, TM.nullHandle, NIL, NIL, NIL, NIL]]; VolGroupKey: TYPE = REF RegVolGroup; RegVolGroup: TYPE = RECORD[ volGroupID: AE.VolumeGroupID, ownerFileUniversalFile: AE.UniversalFile, lastDataRecNum: ACP.RecNumber, totalQuota: AE.PageCount, quotaLeft: AE.PageCount, enumCache: EnumerationCache, currentState: HeavyOpType, -- reg, unreg, reorg, normal, header. heavyTrans: TM.Handle, openFileIDsKey: OpenFileIDsKey, initialVolGroup: REF RegVolGroup, -- so we can undo heavies. ownerKey: OwnerKey, next, prev: VolGroupKey ]; EnumerationCache: TYPE = RECORD[ownerName: AE.OwnerName, dataRecNum: ACP.RecNumber]; OwnerKey: TYPE = REF OwnerEntry; OwnerEntry: TYPE = RECORD[ownerName: AE.OwnerName, dataRecNum: ACP.RecNumber, quotaLeft: AE.PageCount, transKey: TransKey, next, prev: OwnerKey]; TransKey: TYPE = REF TransEntry; TransEntry: TYPE = RECORD[ transHandle: TM.Handle, alloc: AE.PageCount, dealloc: AE.PageCount, phaseOneSeen: BOOLEAN, next, prev: TransKey ]; OpenFileIDsKey: TYPE = REF OpenFileIDsEntry; -- for light ops only. OpenFileIDsEntry: TYPE = RECORD[ transHandle: TM.Handle, openFileID: AE.OpenFileID, next: OpenFileIDsKey ]; <> HeavyOpType: TYPE = {reg, unreg, reorg, normal, header}; MakeDummyVolGroup: INTERNAL SAFE PROCEDURE[pntrVolGroupID: LONG POINTER TO AE.VolumeGroupID, pntrOwnerFileUniversalFile: LONG POINTER TO AE.UniversalFile, lastDataRecNum: ACP.RecNumber, totalQuota, quotaLeft: AE.PageCount, opType: HeavyOpType, transHandle: TM.Handle] RETURNS [newVolGroupKey: VolGroupKey] = TRUSTED BEGIN <> newVolGroupKey _ NEW[RegVolGroup _ [pntrVolGroupID^, pntrOwnerFileUniversalFile^, lastDataRecNum, totalQuota, quotaLeft, [NIL, ACP.nullRecNumber], opType, transHandle, NIL, NIL, NIL, NIL, NIL]]; END; <> PresentDummyVolGroup: INTERNAL SAFE PROCEDURE[oldVolGroupKey, newVolGroupKey: VolGroupKey] = CHECKED BEGIN <> IF oldVolGroupKey = NIL THEN BEGIN IF newVolGroupKey.currentState # reg THEN ERROR ACP.InternalAccessControlLogicError; newVolGroupKey.next _ regVolGroups.next; newVolGroupKey.prev _ regVolGroups; regVolGroups.next.prev _ newVolGroupKey; regVolGroups.next _ newVolGroupKey; END ELSE BEGIN newVolGroupKey.initialVolGroup _ (IF oldVolGroupKey.currentState = normal THEN oldVolGroupKey ELSE oldVolGroupKey.initialVolGroup); newVolGroupKey.next _ oldVolGroupKey.next; newVolGroupKey.prev _ oldVolGroupKey.prev; oldVolGroupKey.prev.next _ newVolGroupKey; oldVolGroupKey.next.prev _ newVolGroupKey; END; END; <> <> <<>> <> Allocate: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, transHandle: TM.Handle, nPages: AE.PageCount, pntrDataRec: ACP.PntrDataRec, dataRecNum: ACP.RecNumber] RETURNS [okay: BOOLEAN] = CHECKED BEGIN <> ownerKey: OwnerKey _ FindOwnerEntry[volGroupKey, ownerName]; transKey: TransKey; IF nPages = 0 THEN ERROR ACP.InternalAccessControlLogicError; okay _ ((nPages < 0)) OR ((ownerKey = NIL) AND (ACMA.ReadOwnerQuotaFromRecord[pntrDataRec] - ACMA.ReadOwnerSpaceInUseFromRecord[pntrDataRec] - nPages >= 0)) OR ((ownerKey # NIL) AND (RoomForAlloc[ownerKey, nPages])); IF NOT okay THEN BEGIN statsNAllocReqsLose _ statsNAllocReqsLose + 1; RETURN; END; IF ownerKey = NIL THEN BEGIN ownerKey _ CreateOwnerEntry[volGroupKey, ownerName, dataRecNum, pntrDataRec]; transKey _ CreateTransEntry[ownerKey, transHandle]; END ELSE transKey _ FindOrCreateTransEntry[ownerKey, transHandle]; IF nPages <= 0 THEN BEGIN transKey.dealloc _ transKey.dealloc + nPages; statsNDeallocReqs _ statsNDeallocReqs + 1; END ELSE BEGIN transKey.alloc _ transKey.alloc + nPages; statsNAllocReqsWin _ statsNAllocReqsWin + 1; END; END; <> RoomForAlloc: INTERNAL SAFE PROCEDURE[ownerKey: OwnerKey, nPages: AE.PageCount] RETURNS[okay: BOOLEAN] = CHECKED INLINE BEGIN <> quotaLeft: AE.PageCount _ ownerKey.quotaLeft; FOR transKey: TransKey _ ownerKey.transKey, transKey.next UNTIL transKey = NIL DO quotaLeft _ quotaLeft - transKey.alloc; ENDLOOP; RETURN[(quotaLeft - nPages) >= 0]; END; <> <> ChangeQuotaOwner: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, transHandle: TM.Handle, newQuota, oldQuota: AE.PageCount] = CHECKED BEGIN <> ownerKey: OwnerKey _ FindOwnerEntry[volGroupKey, ownerName]; IF ownerKey # NIL THEN BEGIN IF ((ownerKey.transKey.transHandle # transHandle) OR (ownerKey.transKey.next # NIL)) THEN { logProc: SAFE PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: ownerRecordInUse, where: "AccessControlMainImpl.ChangeQuotaOwner", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Owner = ", ownerName] ]]; ERROR AC.OperationFailed[ownerRecordInUse]; -- thorn. }; ownerKey.quotaLeft _ ownerKey.quotaLeft + (newQuota - oldQuota); END; END; RemoveTheOwner: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, transHandle: TM.Handle, pntrDataRec: ACP.PntrDataRec] = CHECKED BEGIN <> ownerKey: OwnerKey _ FindOwnerEntry[volGroupKey, ownerName]; IF ownerKey # NIL THEN { IF ((ownerKey.transKey.transHandle # transHandle) OR (ownerKey.transKey.next # NIL)) THEN { logProc: SAFE PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: ownerRecordInUse, where: "AccessControlMainImpl.RemoveTheOwner", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Owner = ", ownerName] ]]; ERROR AC.OperationFailed[ownerRecordInUse]; }; IF ACMA.ReadOwnerQuotaFromRecord[pntrDataRec] # (ownerKey.quotaLeft - ownerKey.transKey.alloc - ownerKey.transKey.dealloc) THEN { logProc: SAFE PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: spaceInUseByThisOwner, where: "AccessControlMainImpl.RemoveTheOwner", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Owner = ", ownerName] -- (notice "=", not "is") ]]; ERROR AC.OperationFailed[spaceInUseByThisOwner]; }; RemoveOwnerEntry[volGroupKey, ownerKey]; } ELSE { IF (ACMA.ReadOwnerSpaceInUseFromRecord[pntrDataRec] # 0) THEN { logProc: SAFE PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: spaceInUseByThisOwner, where: "AccessControlMainImpl.RemoveTheOwner", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: Rope.Concat["Owner is ", ownerName] -- (notice "is" vs. "=") ]]; ERROR AC.OperationFailed[spaceInUseByThisOwner]; }; }; statsNRemoveOwner _ statsNRemoveOwner + 1; END; EnumOwnerSpaceChange: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, transHandle: TM.Handle] RETURNS [inUseChange: AE.PageCount] = CHECKED BEGIN <> ownerKey: OwnerKey _ FindOwnerEntry[volGroupKey, ownerName]; IF ownerKey = NIL THEN RETURN[0]; FOR transKey: TransKey _ ownerKey.transKey, transKey.next UNTIL transKey = NIL DO IF transKey.transHandle = transHandle THEN GOTO found; REPEAT found => RETURN[transKey.alloc + transKey.dealloc]; FINISHED => RETURN[0]; ENDLOOP; END; <> FindEnumRecNum: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, prevOwnerName: AE.OwnerName] RETURNS [valid: BOOLEAN, prevDataRecNum: ACP.RecNumber] = CHECKED BEGIN <> valid _ ((volGroupKey.enumCache.ownerName # NIL) AND (ACU.Compare[volGroupKey.enumCache.ownerName, prevOwnerName])); IF valid THEN statsNEnumFindWin _ statsNEnumFindWin + 1 ELSE statsNEnumFindLose _ statsNEnumFindLose + 1; RETURN[valid, volGroupKey.enumCache.dataRecNum]; END; SetEnumRecNum: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, dataRecNum: ACP.RecNumber] = CHECKED BEGIN <> volGroupKey.enumCache _ [ownerName, dataRecNum]; statsNSetEnums _ statsNSetEnums + 1; END; <> FindLastDataRecNum: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey] RETURNS [lastDataRecNum: ACP.RecNumber] = CHECKED INLINE BEGIN <> RETURN[volGroupKey.lastDataRecNum]; END; <> FindVolGroupCheckRegGetLightOpenFileID: INTERNAL SAFE PROCEDURE[pntrVolGroupID: LONG POINTER TO AE.VolumeGroupID, regStatus: VolGroupRegStatus, transHandle: TM.Handle, findLightOpenFileID: BOOLEAN] RETURNS[volGroupKey: VolGroupKey, ownerOpenFileID: AE.OpenFileID] = CHECKED BEGIN <> ownerOpenFileID _ AE.nullOpenFileID; volGroupKey _ regVolGroups.next; DO IF volGroupKey = regVolGroups THEN BEGIN volGroupKey _ NIL; EXIT; END; TRUSTED {IF volGroupKey.volGroupID = pntrVolGroupID^ THEN EXIT}; volGroupKey _ volGroupKey.next; ENDLOOP; IF (regStatus = prevReg) = ((volGroupKey = NIL) OR (volGroupKey.currentState = unreg)) THEN { IF regStatus = prevReg THEN ERROR AC.Unknown[volumeGroupID] ELSE { logProc: SAFE PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: duplicateVolumeGroup, where: "AccessControlMainImpl.FindVolGroupCheckRegGetLightOpenFileID", transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle], message: "" ]]; ERROR AC.OperationFailed[duplicateVolumeGroup]; }; }; IF findLightOpenFileID THEN ownerOpenFileID _ FindLightOpenFileID[volGroupKey, transHandle]; statsNActions _ statsNActions + 1; END; <> FindFirstRecNeedingUpdateForThisVolGroup: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey] RETURNS [ownerKey: OwnerKey, dataRecNum: ACP.RecNumber] = CHECKED BEGIN <> IF (ownerKey _ volGroupKey.ownerKey) # NIL THEN dataRecNum _ ownerKey.dataRecNum; END; <> RemoveRecAndGetNextRecForVolGroup: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerKey: OwnerKey] RETURNS [nextOwnerKey: OwnerKey, nextDataRecNum: ACP.RecNumber] = CHECKED BEGIN <> nextOwnerKey _ (volGroupKey.ownerKey _ ownerKey.next); IF nextOwnerKey # NIL THEN nextDataRecNum _ nextOwnerKey.dataRecNum; END; <> RemoveOldRecAndGetNextRecForTrans: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, transKey: TransKey] RETURNS[done: BOOLEAN, nextVolGroupKey: VolGroupKey, nextOwnerKey: OwnerKey, nextTransKey: TransKey, nextOwnerOpenFileID: AE.OpenFileID, nextDataRecNum: ACP.RecNumber] = TRUSTED BEGIN <> [done, nextVolGroupKey, nextOwnerKey, nextTransKey, nextDataRecNum, nextOwnerOpenFileID] _ SearchForTransRec[transHandle, phaseOne, transKey]; IF ((NOT done) AND (nextOwnerOpenFileID = AE.nullOpenFileID)) THEN ERROR ACP.InternalAccessControlLogicError; END; <> SearchForTransRec: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, phase: {phaseOne, phaseTwo, abort}, prevTransKey: TransKey] RETURNS[done: BOOLEAN, nextVolGroupKey: VolGroupKey, nextOwnerKey: OwnerKey, nextTransKey: TransKey, nextDataRecNum: ACP.RecNumber, nextOwnerOpenFileID: AE.OpenFileID] = CHECKED BEGIN <> IF ((phase = phaseOne) AND (prevTransKey # NIL)) THEN prevTransKey.phaseOneSeen _ TRUE; FOR nextVolGroupKey _ regVolGroups.next, nextVolGroupKey.next UNTIL nextVolGroupKey = regVolGroups DO FOR nextOwnerKey _ nextVolGroupKey.ownerKey, nextOwnerKey.next UNTIL nextOwnerKey = NIL DO FOR nextTransKey _ nextOwnerKey.transKey, nextTransKey.next UNTIL nextTransKey = NIL DO IF (nextTransKey.transHandle = transHandle) AND ((phase # phaseOne) OR (NOT nextTransKey.phaseOneSeen)) THEN SELECT phase FROM phaseOne => RETURN[FALSE, nextVolGroupKey, nextOwnerKey, nextTransKey, nextOwnerKey.dataRecNum, FindLightOpenFileID[nextVolGroupKey, transHandle]]; ENDCASE => { IF phase = phaseTwo THEN nextOwnerKey.quotaLeft _ nextOwnerKey.quotaLeft - nextTransKey.alloc - nextTransKey.dealloc; RemoveTransEntry[nextOwnerKey, nextTransKey]; IF nextOwnerKey.transKey = NIL THEN RemoveOwnerEntry[nextVolGroupKey, nextOwnerKey]; EXIT; }; ENDLOOP; -- end of trans records. ENDLOOP; -- end of owner records. IF phase # phaseOne THEN RemoveLightOpenFileID[nextVolGroupKey, transHandle]; ENDLOOP; -- end of volGroup records. done _ TRUE; END; <> PhaseTwoOrAbort: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, outcome: AE.CommitOrAbort] = CHECKED BEGIN <> volGroupKey: VolGroupKey; IF outcome = abort THEN statsNAborts _ statsNAborts + 1 ELSE statsNPhaseTwos _ statsNPhaseTwos + 1; IF (NOT SearchForTransRec[transHandle, (IF outcome = commit THEN phaseTwo ELSE abort), NIL].done) THEN ERROR ACP.InternalAccessControlLogicError; volGroupKey _ regVolGroups.next; DO IF volGroupKey = regVolGroups THEN EXIT; IF volGroupKey.heavyTrans = transHandle THEN { oldVolGroupKey: VolGroupKey _ volGroupKey.initialVolGroup; SELECT TRUE FROM ((outcome = abort) AND (oldVolGroupKey = NIL)) OR ((outcome = commit) AND (volGroupKey.currentState = unreg)) => BEGIN -- remove this vol group from list. volGroupKey.prev.next _ volGroupKey.next; volGroupKey.next.prev _ volGroupKey.prev; END; (outcome = abort) => -- here the oldVolGroupKey # NIL BEGIN -- put old vol group back. volGroupKey.prev.next _ oldVolGroupKey; volGroupKey.next.prev _ oldVolGroupKey; oldVolGroupKey.prev _ volGroupKey.prev; oldVolGroupKey.next _ volGroupKey.next; END; ENDCASE => -- here ((outcome = commit) AND (volGroupKey.currentState # unreg)) BEGIN -- make new vol group real. volGroupKey.initialVolGroup _ NIL; volGroupKey.currentState _ normal; volGroupKey.heavyTrans _ TM.nullHandle; END; }; volGroupKey _ volGroupKey.next; ENDLOOP; END; <> CreateOwnerEntry: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, dataRecNum: ACP.RecNumber, pntrDataRec: ACP.PntrDataRec] RETURNS[ownerKey: OwnerKey] = CHECKED BEGIN <> ownerKey _ NEW[OwnerEntry _ [ownerName, dataRecNum, ACMA.ReadOwnerQuotaFromRecord[pntrDataRec] - ACMA.ReadOwnerSpaceInUseFromRecord[pntrDataRec], NIL, volGroupKey.ownerKey, NIL]]; IF volGroupKey.ownerKey # NIL THEN volGroupKey.ownerKey.prev _ ownerKey; volGroupKey.ownerKey _ ownerKey; END; RemoveOwnerEntry: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerKey: OwnerKey] = CHECKED BEGIN <> IF ownerKey.prev # NIL THEN ownerKey.prev.next _ ownerKey.next ELSE volGroupKey.ownerKey _ ownerKey.next; IF ownerKey.next # NIL THEN ownerKey.next.prev _ ownerKey.prev; END; <> FindOwnerEntry: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName] RETURNS[ownerKey: OwnerKey] = CHECKED BEGIN <> ownerKey _ volGroupKey.ownerKey; UNTIL ownerKey = NIL DO IF ACU.Compare[ownerKey.ownerName, ownerName] THEN RETURN; ownerKey _ ownerKey.next; ENDLOOP; RETURN[NIL]; END; CreateTransEntry: INTERNAL SAFE PROCEDURE[ownerKey: OwnerKey, transHandle: TM.Handle] RETURNS[transKey: TransKey] = CHECKED BEGIN <> transKey _ NEW[TransEntry _ [transHandle, 0, 0, FALSE, ownerKey.transKey, NIL]]; IF ownerKey.transKey # NIL THEN ownerKey.transKey.prev _ transKey; ownerKey.transKey _ transKey; END; FindOrCreateTransEntry: INTERNAL SAFE PROCEDURE[ownerKey: OwnerKey, transHandle: TM.Handle] RETURNS[transKey: TransKey] = CHECKED BEGIN <> transKey _ ownerKey.transKey; UNTIL transKey = NIL DO IF transKey.transHandle = transHandle THEN RETURN; transKey _ transKey.next; ENDLOOP; RETURN[CreateTransEntry[ownerKey, transHandle]]; END; RemoveTransEntry: INTERNAL SAFE PROCEDURE[ownerKey: OwnerKey, transKey: TransKey] = CHECKED INLINE BEGIN <> IF transKey.prev # NIL THEN transKey.prev.next _ transKey.next ELSE ownerKey.transKey _ transKey.next; IF transKey.next # NIL THEN transKey.next.prev _ transKey.prev; END; FindLightOpenFileID: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, transHandle: TM.Handle] RETURNS[ownerOpenFileID: AE.OpenFileID] = CHECKED BEGIN <> FOR openFileIDsKey: OpenFileIDsKey _ volGroupKey.openFileIDsKey, openFileIDsKey.next UNTIL openFileIDsKey = NIL DO IF openFileIDsKey.transHandle = transHandle THEN RETURN[openFileIDsKey.openFileID]; ENDLOOP; RETURN[AE.nullOpenFileID]; END; SetLightOpenFileID: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, transHandle: TM.Handle, ownerOpenFileID: AE.OpenFileID] = CHECKED BEGIN <> openFileIDsKey: OpenFileIDsKey _ NEW[OpenFileIDsEntry _ [transHandle, ownerOpenFileID, volGroupKey.openFileIDsKey]]; volGroupKey.openFileIDsKey _ openFileIDsKey; END; RemoveLightOpenFileID: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, transHandle: TM.Handle] = CHECKED BEGIN <> prevOpenFileIDsKey: OpenFileIDsKey _ NIL; FOR openFileIDsKey: OpenFileIDsKey _ volGroupKey.openFileIDsKey, openFileIDsKey.next UNTIL openFileIDsKey = NIL DO IF openFileIDsKey.transHandle = transHandle THEN { IF prevOpenFileIDsKey = NIL THEN volGroupKey.openFileIDsKey _ openFileIDsKey.next ELSE prevOpenFileIDsKey.next _ openFileIDsKey.next; RETURN; }; prevOpenFileIDsKey _ openFileIDsKey; ENDLOOP; END; ReadOwnerUniversalFile: PUBLIC ENTRY SAFE PROCEDURE[volumeGroupID: AE.VolumeGroupID] RETURNS[dBID: AE.UniversalFile] = TRUSTED BEGIN <> volGroupKey: VolGroupKey _ FindVolGroupCheckRegGetLightOpenFileID[@volumeGroupID, prevReg, NIL, FALSE].volGroupKey; RETURN[volGroupKey.ownerFileUniversalFile]; END; <> statsNRegister, statsNInit, statsNUnReg, statsNReorg, statsNEnumAll, statsNAllocReqsWin, statsNAllocReqsLose, statsNDeallocReqs, statsNRemoveOwner, statsNEnumFindWin, statsNEnumFindLose, statsNSetEnums, statsNActions, statsNPhaseOnes, statsNPhaseTwos, statsNAborts: INT _ 0; ReportVolatileStats: PUBLIC ENTRY SAFE PROCEDURE RETURNS[nRegs, nInits, nUnRegs, nReorganizes, nEnumAlls, nAllocReqsWin, nAllocReqsLose, nDeallocReqs, nRemoveOwner, nEnumFindWin, nEnumFindLose, nSetEnums, nActions, nPhaseOnes, nPhaseTwos, nAborts: INT] = CHECKED BEGIN <> RETURN[statsNRegister, statsNInit, statsNUnReg, statsNReorg, statsNEnumAll, statsNAllocReqsWin, statsNAllocReqsLose, statsNDeallocReqs, statsNRemoveOwner, statsNEnumFindWin, statsNEnumFindLose, statsNSetEnums, statsNActions, statsNPhaseOnes, statsNPhaseTwos, statsNAborts]; END; regVolGroups.next _ regVolGroups; regVolGroups.prev _ regVolGroups; END. Edit Log Initial: Kolling: January 11, 1983 5:52 pm: upper level module which protects delicate operations within a "monster monitor". <> <> <> <> <> <> <<>>