AccessControlMainImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Carl Hauser, May 23, 1985 11:49:25 am PDT
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, UnlockFile, WritePages],
AlpineIdentity
USING[myLocalConversation],
AlpineInternal
USING[TransHandle],
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];
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];
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]];
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];
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];
MakeSparseOwnerFile:
INTERNAL
SAFE
PROCEDURE[maxNumberOfOwnersAllowed:
NAT]
RETURNS[newMaxNumberOfOwnersAllowed:
NAT] =
CHECKED BEGIN
non system-fatal errors: none.
RETURN[(maxNumberOfOwnersAllowed*14)/10];
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;
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;
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];
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];
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];
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];
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];
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];
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];
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;
PhaseTwoOrAbortSpaceAndOwnerChanges:
PUBLIC
ENTRY
SAFE
PROCEDURE[transHandle:
TM.Handle, outcome: AE.CommitOrAbort, continueTrans: TM.Handle ← NIL] = CHECKED BEGIN
non system-fatal errors: none.
PhaseTwoOrAbort[transHandle, outcome, continueTrans];
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]];
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;
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];
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;
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;
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];
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;
misc.
FindLastDataRecNum:
INTERNAL
SAFE
PROCEDURE[volGroupKey: VolGroupKey]
RETURNS [lastDataRecNum:
ACP.RecNumber] =
CHECKED
INLINE BEGIN
non system-fatal errors: none.
RETURN[volGroupKey.lastDataRecNum];
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;
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;
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;
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;
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, continueTrans:
TM.Handle
← NIL]
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 = phaseTwo) OR (phase = abort) THEN RemoveOrReassignLightOpenFileID[nextVolGroupKey, transHandle, continueTrans];
ENDLOOP; -- end of volGroup records.
done ← TRUE;
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, continueTrans:
TM.Handle
← NIL] =
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, continueTrans].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;
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;
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;
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];
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;
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]];
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;
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];
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;
RemoveOrReassignLightOpenFileID:
INTERNAL
SAFE
PROCEDURE[volGroupKey: VolGroupKey, transHandle:
TM.Handle, continueTrans:
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 continueTrans # NIL THEN openFileIDsKey.transHandle ← continueTrans
ELSE {
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];
UnlockOwnerDB:
PUBLIC
ENTRY
SAFE
PROCEDURE [conversation: AlpineEnvironment.Conversation, transHandle:
TM.Handle, volGroupID: AlpineEnvironment.VolumeGroupID] =
TRUSTED
BEGIN
non system-fatal errors:
StaticallyInvalid
AC.Unknown[transID, VolumeGroupID].
ownerOpenFileID: AE.OpenFileID ← FindVolGroupCheckRegGetLightOpenFileID[@volGroupID, prevReg, transHandle, TRUE].ownerOpenFileID;
IF ownerOpenFileID # AE.nullOpenFileID THEN AF.UnlockFile[AID.myLocalConversation, ownerOpenFileID];
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];
regVolGroups.next ← regVolGroups;
regVolGroups.prev ← regVolGroups;
END.
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