AccessControlMainImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last edited by
Kolling on May 27, 1983 4:00 pm
Last Edited by: Kupfer, August 6, 1984 2:48:56 pm PDT
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;
this module should contain no checking for privileges, since we do not want to wait for Grapevine inside the monitor.
for Light operations:
For owner routines that need to get the header page.
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
non system-fatal errors: ACF.LockConflict, AC.OperationFailed[duplicateOwner, ownerDatabaseFull, ownerRecordFull, ownerRecordInUse, spaceInUseByThisOwner, totalQuotaExceeded], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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 goes to ACV because inuse is sum and also it will remove the volatile entry for this owner.
remove => {
RemoveTheOwner[volGroupKey, ownerName, transHandle, @fileDataRec];
fileDataRec.state ← deleted;
fileHeaderRec.numberOfOwners ← fileHeaderRec.numberOfOwners - 1;
};
writeProps goes to ACV because it will change the volatile entry's quota.
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;
For WriteProperties, when it doesn't need to get the header page. I.e., createAccessList, modifyAccessList or rootFile.
CommonChangeOwner: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, ownerName: AE.OwnerName, ownerProperties: LIST OF AE.OwnerPropertyValuePair] = TRUSTED BEGIN
non system-fatal errors: ACF.LockConflict, AC.OperationFailed[ownerRecordFull], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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;
for Heavy operations:
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
non system-fatal errors: AC.StaticallyInvalid(ownerCacheEntriesOutOfRange, maxOwnerEntriesTooSmall, volGroupQuotaLTSpaceForOwnerFile), AC.OperationFailed[insufficientSpace], AC.Unknown[fileID, transID, volumeID, volumeGroupID].
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];
initialize the header record.
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];
write all data records as empty.
EmptyOutAnOwnerFile[ownerOpenFileID, lastDataRecNum];
write the weird owner record.
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];
The following error is logged (SkiPatrolLog) at a lower level.
convertToACNoSpace => ERROR AC.OperationFailed[insufficientSpace];
END;
MakeSparseOwnerFile: INTERNAL SAFE PROCEDURE[maxNumberOfOwnersAllowed: NAT]
RETURNS[newMaxNumberOfOwnersAllowed: NAT] = CHECKED BEGIN
non system-fatal errors: none.
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
non system-fatal errors: ACF.LockConflict, AC.OperationFailed[duplicateVolumeGroup, ownerFileFormatOrVolGroupMismatch], AC.Unknown[fileID, transID, volumeID].
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 {
Log the mismatch, telling what exactly went wrong.
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
non system-fatal errors: ACF.LockConflict, AC.Unknown[transID, volumeGroupID].
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;
called when need to make room for more owner entries, clean up owner database, etc. Will change the size of the file based on maxNumberOfOwnersAllowed. This procedure can error back once it has started modifying the owner file, but I believe that all those cases reflect the disappearance of the transaction or a fatal system error.
ReorgOwnerFile: PUBLIC ENTRY SAFE PROCEDURE[volGroupID: AE.VolumeGroupID, transHandle: TM.Handle, maxNumberOfOwnersAllowed: NAT] = TRUSTED BEGIN
non system-fatal errors: ACF.LockConflict, AC.OperationFailed[insufficientSpace, ownerDatabaseFull], AC.Unknown[transID, volumeGroupID].
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
This error is logged (SkiPatrolLog) at a lower level.
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];
};
some preliminary checking and catch some errors.
[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]];
reinitialize the owner file.
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[];
adjust the specialowner's quota.
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;
called only by clients who have the volume group exclusively locked.
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
non system-fatal errors: ACF.LockConflict, AC.Unknown[fileID, transID, volumeID].
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;
expects the volGroupKey to be in a valid state.
EmptyOutAnOwnerFile: INTERNAL SAFE PROCEDURE[ownerOpenFileID: AE.OpenFileID, lastDataRecNum: ACP.RecNumber] = TRUSTED BEGIN
non system-fatal errors: AC.Unknown[transID].
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;
dump out any pending updates before reorganize and unregister.
FlushOutAVolumeGroup: INTERNAL SAFE PROCEDURE[ownerOpenFileID: AE.OpenFileID, oldVolGroupKey: VolGroupKey, transHandle: TM.Handle] = TRUSTED BEGIN
non system-fatal errors: AC.Unknown[transID].
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;
User supplied prevOwner = NIL means beginning of file. Returned ownerName = NIL means no more owners. Locks out all other transactions from the owner file while this transaction is in progress. If the same transaction tries something funny, like a reorganize between calls to EnumerateAllOwners, it deserves what it gets.
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
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;
For debugging. Locks up the entire owner file while this transaction is in progress. Very similar to EnumerateAllOwners, but tells about the empty and deleted records as well. Refing the contRecNumber is due to compiler limitation on long records.
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badRecordNumber), AC.Unknown[transID, volumeGroupID].
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;
For debugging. Locks up the entire owner database file while this transaction is in progress. Note that ReadOwnerDatabaseHeader and EnumerateAllDataEntriesInOwnerFile called in the same transaction will be consistent provided the transaction doesn't alter the owner database file itself.
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
non system-fatal errors: ACF.LockConflict, AC.Unknown[transID, volumeGroupID].
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;
misc.:
VolGroupRegStatus: TYPE = {prevReg, prevUnreg};
if the file is not opened, the lastDataRecNum returned is not valid.
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
non system-fatal errors: ACF.LockConflict, AC.OperationFailed[duplicateVolumeGroup], AC.Unknown[transID, volumeGroupID].
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
non system-fatal errors: ACF.LockConflict, AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
[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
non system-fatal errors: none.
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;
Processing at end of a transaction:
PhaseOneSpaceOwnerChanges: PUBLIC ENTRY SAFE PROCEDURE[transHandle: TM.Handle] = TRUSTED BEGIN
non system-fatal errors: AFC.LockConflict, AC.Unknown[transID].
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
non system-fatal errors: none.
PhaseTwoOrAbort[transHandle, outcome];
END;
herewith the old "volatile structure" module:
registered volume groups. We can't use LISTs, because there's a problem with LISTs and opaque types.
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
];
for init and reorganize volume groups. also used by register and unregister since it's easier.
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
non system-fatal errors: none.
newVolGroupKey ← NEW[RegVolGroup ← [pntrVolGroupID^, pntrOwnerFileUniversalFile^, lastDataRecNum, totalQuota, quotaLeft, [NIL, ACP.nullRecNumber], opType, transHandle, NIL, NIL, NIL, NIL, NIL]];
END;
replace the old volGroup with the new one.
PresentDummyVolGroup: INTERNAL SAFE PROCEDURE[oldVolGroupKey, newVolGroupKey: VolGroupKey] = CHECKED BEGIN
non system-fatal errors: none.
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;
handle ops
handle (de)allocation.
don't allow allocations over quota, and the owner doesn't get credit for deallocations until the end of the transaction. A request for 0 pages is not expected.
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
non system-fatal errors: none.
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;
ownerKey is guaranteed non-NIL by caller.
RoomForAlloc: INTERNAL SAFE PROCEDURE[ownerKey: OwnerKey, nPages: AE.PageCount] RETURNS[okay: BOOLEAN] = CHECKED INLINE BEGIN
non system-fatal errors: none.
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;
handle owner ops.
ChangeQuota doesn't care if the owner isn't in the volatile structure. Also, it is okay to change the quota even if the current allocation will then be over quota.
ChangeQuotaOwner: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, transHandle: TM.Handle, newQuota, oldQuota: AE.PageCount] = CHECKED BEGIN
non system-fatal errors: AC.OperationFailed[ownerRecordInUse].
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
non system-fatal errors: AC.OperationFailed[ownerRecordInUse, spaceInUseByThisOwner].
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
non system-fatal errors: none.
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;
handle enumeration "cache".
FindEnumRecNum: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, prevOwnerName: AE.OwnerName] RETURNS [valid: BOOLEAN, prevDataRecNum: ACP.RecNumber] = CHECKED BEGIN
non system-fatal errors: none.
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
non system-fatal errors: none.
volGroupKey.enumCache ← [ownerName, dataRecNum];
statsNSetEnums ← statsNSetEnums + 1;
END;
misc.
FindLastDataRecNum: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey] RETURNS [lastDataRecNum: ACP.RecNumber] = CHECKED INLINE BEGIN
non system-fatal errors: none.
RETURN[volGroupKey.lastDataRecNum];
END;
routine called at the beginning of an action. ($5 to the first person who can memorize the name on the first try. --kupfer)
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
non system-fatal errors: AC.OperationFailed[duplicateVolumeGroup], AC.Unknown[VolumeGroupID].
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;
routines called to flush out the volatile structure before reorganize and unregister.
FindFirstRecNeedingUpdateForThisVolGroup: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey] RETURNS [ownerKey: OwnerKey, dataRecNum: ACP.RecNumber] = CHECKED BEGIN
non system-fatal errors: none.
IF (ownerKey ← volGroupKey.ownerKey) # NIL THEN dataRecNum ← ownerKey.dataRecNum;
END;
update the file record, and get the next volatile record.
RemoveRecAndGetNextRecForVolGroup: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerKey: OwnerKey] RETURNS [nextOwnerKey: OwnerKey, nextDataRecNum: ACP.RecNumber] = CHECKED BEGIN
non system-fatal errors: none.
nextOwnerKey ← (volGroupKey.ownerKey ← ownerKey.next);
IF nextOwnerKey # NIL THEN nextDataRecNum ← nextOwnerKey.dataRecNum;
END;
routine called to write out the transaction's information at Phase One.
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
non system-fatal errors: none.
[done, nextVolGroupKey, nextOwnerKey, nextTransKey, nextDataRecNum, nextOwnerOpenFileID] ← SearchForTransRec[transHandle, phaseOne, transKey];
IF ((NOT done) AND (nextOwnerOpenFileID = AE.nullOpenFileID)) THEN
ERROR ACP.InternalAccessControlLogicError;
END;
At Phase One this routine sets phaseOneSeen for the prev record (if any) for this transaction, finds the "next" record for this transaction (it ignores ones with phaseOneSeen set), and reports it to the caller. At PhaseTwo or Abort, it finds all the records for the specified transaction, (iff PhaseTwo updates their volatile owners), then deletes them.
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
non system-fatal errors: none.
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;
At Phase Two this routine makes the volatile owner records reflect this transaction's being committed, deletes the trans records, and handles heavy op cleanup. At Abort, it deletes the trans records and does heavy op abort.
PhaseTwoOrAbort: INTERNAL SAFE PROCEDURE[transHandle: TM.Handle, outcome: AE.CommitOrAbort] = CHECKED BEGIN
non system-fatal errors: none.
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;
utility routines for the volatile structure:
CreateOwnerEntry: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName, dataRecNum: ACP.RecNumber, pntrDataRec: ACP.PntrDataRec] RETURNS[ownerKey: OwnerKey] = CHECKED BEGIN
non system-fatal errors: none.
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
non system-fatal errors: none.
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;
returns NIL if not found.
FindOwnerEntry: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, ownerName: AE.OwnerName] RETURNS[ownerKey: OwnerKey] = CHECKED BEGIN
non system-fatal errors: none.
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
non system-fatal errors: none.
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
non system-fatal errors: none.
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
non system-fatal errors: none.
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
non system-fatal errors: none.
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
non system-fatal errors: none.
openFileIDsKey: OpenFileIDsKey ← NEW[OpenFileIDsEntry ← [transHandle, ownerOpenFileID,
volGroupKey.openFileIDsKey]];
volGroupKey.openFileIDsKey ← openFileIDsKey;
END;
RemoveLightOpenFileID: INTERNAL SAFE PROCEDURE[volGroupKey: VolGroupKey, transHandle: TM.Handle] = CHECKED BEGIN
non system-fatal errors: none.
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
non system-fatal errors: AC.Unknown[VolumeGroupID].
volGroupKey: VolGroupKey ← FindVolGroupCheckRegGetLightOpenFileID[@volumeGroupID, prevReg, NIL, FALSE].volGroupKey;
RETURN[volGroupKey.ownerFileUniversalFile];
END;
statsNActions, statsNEnumFindWin, and statsNEnumFindLose can be too large due to the locking cycling.
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
non system-fatal errors: none.
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".
Edited on July 25, 1984 9:56:09 am PDT, by Kupfer
Add SkiPatrolLog probes and reformat (node structure, fix indentation).
changes to: CommonChangeOwnerAndHeader, CreateAndInitOwnerFile, RegisterVolGroup, ReorgOwnerFile, ChangeQuotaOwner, RemoveTheOwner, FindVolGroupCheckRegGetLightOpenFileID, AccessControlMainImpl (plus lots of routines for reformatting!)
Edited on August 6, 1984 2:46:19 pm PDT, by Kupfer
Remove the possible race condition in SkiPatrolLog probes by assigning the PROC to a temporary variable.
changes to: CommonChangeOwnerAndHeader, RegisterVolGroup, ReorgOwnerFile, ChangeQuotaOwner, RemoveTheOwner, FindVolGroupCheckRegGetLightOpenFileID