-- AccessControlLightImpl.mesa
-- Last edited by
-- Kolling on July 14, 1983 11:15 am


DIRECTORY

AccessControl
USING[AccessFailed, LockFailed, OperationFailed, StaticallyInvalid, Unknown],
AccessControlCache
USING[VerifyClient],
AccessControlFile
USING[LockConflict, LockItem, LockFileOrPageRun],
AccessControlMain
USING[ChangeSpaceViaOwnerName, CommonChangeOwnerAndHeader,
CommonChangeOwner, GetOwnerRecordForReadAndUnlock,
PhaseOneSpaceOwnerChanges, ReadAllOwnerProperties],
AccessControlMainAux
USING[ReadOwnerAccessListFromRecord, ReadOwnerAccListFromRecord],
AccessControlPrivate
USING[FileDataRec, InternalAccessControlLogicError, OwnerAccListType,
PntrAccList, SpecialOwnerForAlpineAdmin, StringRep],
AccessControlLight,
AccessControlTransMap
USING[EnableAlpineWheel, Handle, IsAlpineWheel],
AccessControlUtility
USING[Compare, CompareCaseMatters, MakeRNameFromStringRep],
AlpineEnvironment
USING[AccessList, AccessRights, Conversation, LockMode, nullRootFile, OpenFileID,
OwnerName, OwnerProperty, OwnerPropertySet, OwnerPropertyValuePair, PageCount,
Principal, Property, PropertyValuePair, RName, VolumeGroupID, VolumeID],
AlpineFile
USING[GetUniversalFile, LockFailed, PropertySet, ReadProperties, Unknown],
AlpineIdentity
USING[myLocalConversation],
AlpineInternal
USING[TransHandle],
AlpineVolume
USING[GetEnclosingGroup],
ClientMap
USING[GetName];


AccessControlLightImpl: CEDAR PROGRAM
IMPORTS AC: AccessControl, ACC: AccessControlCache, ACF: AccessControlFile, ACM:
AccessControlMain, ACMA: AccessControlMainAux, ACP: AccessControlPrivate, ACU:
AccessControlUtility, AF: AlpineFile, AID: AlpineIdentity, AV: AlpineVolume, ClientMap,
TM: AccessControlTransMap
EXPORTS AccessControl, AccessControlLight =


BEGIN OPEN AE: AlpineEnvironment, AI: AlpineInternal;


-- vanilla owner procedures:



AddOwner: PUBLIC PROCEDURE[conversation: AE.Conversation, transHandle:
AI.TransHandle, volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName,
overCommitQuotaIfNeeded: BOOLEAN, ownerProperties: LIST OF AE.OwnerPropertyValuePair]
RETURNS[spaceLeftOnVolumeGroup: AE.PageCount] =
TRUSTED BEGIN -- non system-fatal errors: AC.AccessFailed[alpineWheel], AC.LockFailed[timeout], AC.OperationFailed[duplicateOwner, ownerRecordFull, ownerDatabaseFull, totalQuotaExceeded], AC.StaticallyInvalid(badLengthName, badOwnerPropList, reservedOwnerName), AC.Unknown[transID, volumeGroupID].
quota: AE.PageCount;
myLockItem: ACF.LockItem;
IF NOT TM.IsAlpineWheel[transHandle, conversation]
THEN ERROR AC.AccessFailed[alpineWheel];
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid;
[, quota, ownerProperties] ←
ApplyMinsDefaultsAndFilterRequestedWriteProperties[ownerProperties, ownerName, TRUE];
ownerProperties ← CONS[[spaceInUse[0]], ownerProperties];
DO
BEGIN
RETURN
[ACM.CommonChangeOwnerAndHeader[volGroupID, add,
transHandle, ownerName, overCommitQuotaIfNeeded, ownerProperties, quota
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH
l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END]];
EXITS lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;



RemoveOwner: PUBLIC PROCEDURE[conversation: AE.Conversation, transHandle:
AI.TransHandle, volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName] =
TRUSTED BEGIN -- non system-fatal errors: AC.AccessFailed[alpineWheel], AC.LockFailed[timeout], AC.OperationFailed[ownerRecordInUse, spaceInUseByThisOwner], AC.StaticallyInvalid(badLengthName, reservedOwnerName), AC.Unknown[owner, transID, volumeGroupID].
myLockItem: ACF.LockItem;
IF NOT TM.IsAlpineWheel[transHandle, conversation]
THEN ERROR AC.AccessFailed[alpineWheel];
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid;
DO
BEGIN
[] ← ACM.CommonChangeOwnerAndHeader[volGroupID, remove, transHandle, ownerName,
TRUE, NIL, 0
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END];
RETURN;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;



WriteOwnerProperties: PUBLIC PROCEDURE[conversation: AE.Conversation,
transHandle: AI.TransHandle, volGroupID: AE.VolumeGroupID, ownerName:
AE.OwnerName, overCommitQuotaIfNeeded: BOOLEAN, ownerProperties: LIST OF
AE.OwnerPropertyValuePair] =
TRUSTED BEGIN -- non system-fatal errors: AC.AccessFailed[alpineWheel, ownerEntry], AC.LockFailed[timeout], AC.OperationFailed[ownerRecordFull, ownerRecordInUse, regServersUnavailable, totalQuotaExceeded], Ac.StaticallyInvalid(badLengthName, badOwnerPropList, reservedOwnerName), AC.Unknown[owner, transID, volumeGroupID].
needHeader: BOOLEAN;
quota: AE.PageCount;
myLockItem: ACF.LockItem;
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid;
[needHeader, quota, ownerProperties] ←
ApplyMinsDefaultsAndFilterRequestedWriteProperties[ownerProperties, ownerName,
FALSE];
IF needHeader
THEN BEGIN
IF (NOT TM.IsAlpineWheel[transHandle, conversation])
THEN ERROR AC.AccessFailed[alpineWheel];
DO
BEGIN
[] ← ACM.CommonChangeOwnerAndHeader[volGroupID, writeProps, transHandle,
ownerName, overCommitQuotaIfNeeded, ownerProperties, quota
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END];
RETURN;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END

ELSE BEGIN
-- an asserted AW can do anything. The owner and clients in the modify list can set the create list. The owner and clients in the create list can set the rootFile.
IF (NOT TM.IsAlpineWheel[transHandle, conversation])
THEN
FOR ownerProperty: LIST OF AE.OwnerPropertyValuePair ← ownerProperties,
ownerProperty.rest
UNTIL ownerProperty = NIL
DO
SELECT ownerProperty.first.property FROM
createAccessList => IF (NOT CheckClientInOwnerAccessLists[conversation,
transHandle, volGroupID, ownerName, [create: FALSE, modify:
TRUE]][modify]) THEN ERROR AC.AccessFailed[ownerEntry];
rootFile => IF (NOT CheckClientInOwnerAccessLists[conversation,
transHandle, volGroupID, ownerName, [create: TRUE, modify:
FALSE]][create]) THEN ERROR AC.AccessFailed[ownerEntry];
modifyAccessList => ERROR AC.AccessFailed[ownerEntry];
ENDCASE => ERROR ACP.InternalAccessControlLogicError;
ENDLOOP;
DO
BEGIN
ACM.CommonChangeOwner[volGroupID, transHandle, ownerName,
ownerProperties
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END];
RETURN;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;
END;



ReadOwnerProperties: PUBLIC PROCEDURE[conversation: AE.Conversation,
transHandle: AI.TransHandle, volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName,
desiredOwnerProperties: AE.OwnerPropertySet] RETURNS [ownerProperties: LIST OF
AE.OwnerPropertyValuePair] =
TRUSTED BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
myLockItem: ACF.LockItem;
DO
BEGIN
RETURN
[ACM.ReadAllOwnerProperties[volGroupID, transHandle, ownerName,
desiredOwnerProperties
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END]];
EXITS lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;




-- Disk space allocation and deallocation:



ChangeSpaceViaOwner: PUBLIC PROCEDURE[transHandle: AI.TransHandle, volGroupID:
AE.VolumeGroupID, ownerName: AE.OwnerName, nPages: AE.PageCount] RETURNS [okay:
BOOLEAN] =
TRUSTED BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
myLockItem: ACF.LockItem;
IF ((nPages = 0) OR (ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]))
THEN RETURN[TRUE];
DO
BEGIN
RETURN
[ACM.ChangeSpaceViaOwnerName[volGroupID, transHandle, ownerName, nPages
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END]];
EXITS lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;


-- Just like ChangeSpaceViaOwner, but does an Alpine Wheels check. For debugging.

ChangeSpaceForOwner: PUBLIC PROCEDURE [conversation: AE.Conversation, transHandle:
AI.TransHandle, volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName, nPages:
AE.PageCount] =
BEGIN -- non system-fatal errors: AC.AccessFailed[alpineWheel], AC.LockFailed[timeout], AC.OperationFailed[quotaExceeded], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
IF NOT TM.IsAlpineWheel[transHandle, conversation]
THEN ERROR AC.AccessFailed[alpineWheel];
IF
NOT ChangeSpaceViaOwner[transHandle, volGroupID, ownerName, nPages]
THEN ERROR AC.OperationFailed[quotaExceeded];
END;


InconsistencyBetweenFilePropAndOwnerDataBase: ERROR = CODE;

ChangeSpaceViaOpenFileID: PUBLIC PROCEDURE[conversation: AE.Conversation,
transHandle: AI.TransHandle, openFileID: AE.OpenFileID, nPages: AE.PageCount] RETURNS
[okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[openFileID, transID, volumeGroupID].
ownerName: AE.OwnerName ← ReadOwnerName[conversation, openFileID];
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin] THEN RETURN[TRUE];
RETURN[ChangeSpaceViaOwner[transHandle, GetVolGroupIDFromOpenFileID[conversation,
openFileID], ownerName, nPages
! AC.StaticallyInvalid => GOTO horrible;
AC.Unknown => IF why = owner THEN GOTO horrible]];
EXITS horrible => ERROR InconsistencyBetweenFilePropAndOwnerDataBase;
END;




-- Checking permissions:



PermissionToCreateFilesForThisOwner: PUBLIC PROCEDURE[conversation:
AE.Conversation, transHandle: AI.TransHandle, volGroupID: AE.VolumeGroupID,
ownerName: AE.OwnerName] RETURNS [okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.StaticallyInvalid(badLengthName, reservedOwnerName), AC.Unknown[owner, transID, volumeGroupID].
IF conversation = AID.myLocalConversation THEN RETURN[TRUE];
IF
TM.IsAlpineWheel[transHandle, conversation]
THEN BEGIN
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid
ELSE RETURN[TRUE];
END;
RETURN[CheckClientInOwnerAccessLists[conversation, transHandle, volGroupID, ownerName,
[create: TRUE, modify: FALSE]][create]];
END;



-- the file access list can be modified by the owner or anyone in the current owner create list. Owner is not checked for explicitly below because the owner create list minimum has owner.

PermissionToModifyFileAccessList: PUBLIC PROCEDURE[conversation: AE.Conversation,
transHandle: AI.TransHandle, openFileID: AE.OpenFileID] RETURNS [okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.StaticallyInvalid(badLengthName, reservedOwnerName), AC.Unknown[openFileID, transID, volumeID, volumeGroupID].
volGroupID: AE.VolumeGroupID;
ownerName: AE.OwnerName ← ReadOwnerName[conversation, openFileID];
IF TM.IsAlpineWheel[transHandle, conversation]
THEN BEGIN
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid
ELSE RETURN[TRUE];
END;
volGroupID ← GetVolGroupIDFromOpenFileID[conversation, openFileID];
RETURN[CheckClientInOwnerAccessLists[conversation, transHandle, volGroupID, ownerName,
[create: TRUE, modify: FALSE]][create]];
END;



CheckClientInOwnerAccessLists
: PROCEDURE[conversation: AE.Conversation,
transHandle: TM.Handle, volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName,
reqLists: ARRAY ACP.OwnerAccListType OF BOOLEAN] RETURNS [inList: ARRAY
ACP.OwnerAccListType OF BOOLEAN] = TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
fileDataRec: ACP.FileDataRec;
myLockItem: ACF.LockItem;
DO
BEGIN
ACM.GetOwnerRecordForReadAndUnlock[volGroupID, transHandle, ownerName,
@fileDataRec
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH
l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END];
EXIT;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
FOR
listType: ACP.OwnerAccListType IN ACP.OwnerAccListType
DO
inList[listType] ← (IF reqLists[listType]
THEN IsClientInOwnerAccList[conversation,
ACMA.ReadOwnerAccListFromRecord[@fileDataRec, listType], ownerName]
ELSE FALSE);
ENDLOOP;
END;



-- the only access checking procedure that uses only the file properties database.

PermissionToAccessFile: PUBLIC PROCEDURE[conversation: AE.Conversation, transHandle:
AI.TransHandle, openFileID: AE.OpenFileID, requestedAccess: AE.AccessRights] RETURNS [okay:
BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.StaticallyInvalid(reservedOwnerName), AC.Unknown[openFileID, transID].
IF conversation = AID.myLocalConversation THEN RETURN[TRUE];
IF
TM.IsAlpineWheel[transHandle, conversation]
THEN BEGIN
IF ACU.Compare[ReadOwnerName[conversation, openFileID],
ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid
ELSE RETURN[TRUE];
END;
SELECT requestedAccess FROM
readWrite => RETURN[IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readWrite]]];
readOnly => okay ← IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readOnly]] OR
IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readWrite]];
ENDCASE
=> ERROR;
END
;




-- procedure that supplies defaults for the file access lists:


GetDefaultForFileAccessList: PUBLIC PROCEDURE[transHandle: AI.TransHandle,
volGroupID: AE.VolumeGroupID, ownerName: AE.OwnerName, accessListType:
AE.AccessRights] RETURNS[accessList: AlpineEnvironment.AccessList] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC..StaticallyInvalid(badLengthName), AC.Unknown[owner, transID, volumeGroupID].
accessList ← NIL;
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin] THEN RETURN;
SELECT accessListType FROM
readWrite => TRUSTED BEGIN
fileDataRec: ACP.FileDataRec;
myLockItem: ACF.LockItem;
DO
BEGIN
ACM.GetOwnerRecordForReadAndUnlock[volGroupID, transHandle,
ownerName, @fileDataRec
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile,
l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID,
l.pageRun];
ENDCASE; GOTO lockConflict; END];
EXIT;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
accessList ← ACMA.ReadOwnerAccessListFromRecord[@fileDataRec, create,
ownerName];
END;
ENDCASE => accessList ← CONS["world", accessList];
END;




-- procedures to manipulate the user supplied owner properties:



-- checks list of requested properties for writing, for duplicates and illegals (spaceInUse). Also notes if the caller must get the owner database header to set any of these properties (quotaPresent). Under supplyDefaults, the defaults supplied are for the two access lists, the root file (just to reserve space) and the quota.

ApplyMinsDefaultsAndFilterRequestedWriteProperties: PUBLIC
PROCEDURE[ownerProperties: LIST OF AE.OwnerPropertyValuePair, ownerName: AE.OwnerName,
supplyDefaults: BOOLEAN] RETURNS[quotaPresent: BOOLEAN, quota: AE.PageCount,
newOwnerProperties: LIST OF AE.OwnerPropertyValuePair] = TRUSTED
BEGIN -- non system-fatal errors: AC.StaticallyInvalid(badOwnerPropList).
ownerPropertySet: AE.OwnerPropertySet ← ALL[FALSE];
quotaPresent ← FALSE;
quota ← 0;
newOwnerProperties ← NIL;
FOR ownerProperty: LIST OF AE.OwnerPropertyValuePair ← ownerProperties, ownerProperty.rest
UNTIL ownerProperty = NIL
DO
IF ownerPropertySet[ownerProperty.first.property]
THEN ERROR AC.StaticallyInvalid;
ownerPropertySet[ownerProperty.first.property] ← TRUE;
WITH prop: ownerProperty.first
SELECT FROM
createAccessList => newOwnerProperties ←
CONS[[createAccessList[ApplyMinToOwnerAccessList[prop.createAccessList,
create, ownerName]]], newOwnerProperties];
modifyAccessList => newOwnerProperties ←
CONS[[modifyAccessList[ApplyMinToOwnerAccessList[prop.modifyAccessList,
modify, ownerName]]], newOwnerProperties];
rootFile => newOwnerProperties ← CONS[[rootFile[prop.rootFile]], newOwnerProperties];
quota => BEGIN quotaPresent ← TRUE;
newOwnerProperties ← CONS[[quota[prop.quota]], newOwnerProperties];
END;
ENDCASE => ERROR AC.StaticallyInvalid;
ENDLOOP;
IF supplyDefaults THEN
BEGIN
IF NOT ownerPropertySet[createAccessList]
THEN newOwnerProperties ←
CONS[[createAccessList[GetDefaultForOwnerAccessList[create, ownerName]]],
newOwnerProperties];
IF NOT ownerPropertySet[modifyAccessList]
THEN newOwnerProperties ←
CONS[[modifyAccessList[GetDefaultForOwnerAccessList[modify, ownerName]]],
newOwnerProperties];
IF NOT ownerPropertySet[quota]
THEN BEGIN newOwnerProperties ← CONS[[quota[0]], newOwnerProperties];
quotaPresent ← TRUE;
END;
IF NOT ownerPropertySet[rootFile]
THEN newOwnerProperties ← CONS[[rootFile[AE.nullRootFile]], newOwnerProperties];
END;
END;



--the caller is not allowed to specify world. We filter out duplicate settings of owner, as a by-product.

ApplyMinToOwnerAccessList: PROCEDURE [accessList: AE.AccessList, accListType:
ACP.OwnerAccListType, ownerName: AE.OwnerName] RETURNS[newAccessList: AE.AccessList] =
BEGIN -- non system-fatal errors: none.
ownerSeen: BOOLEANFALSE;
newAccessList ← NIL;
FOR list: AE.AccessList ← accessList, list.rest
UNTIL list = NIL
DO
SELECT TRUE FROM
ACU.Compare[list.first, "world"], ACU.CompareCaseMatters[list.first, "*"] => NULL;
ACU.Compare[list.first, "owner"], ACU.Compare[list.first, ownerName] => NULL;
ENDCASE => newAccessList ← CONS[list.first, newAccessList];
ENDLOOP;
newAccessList ← CONS["Owner", newAccessList];
END;



GetDefaultForOwnerAccessList: PROCEDURE [accListType: ACP.OwnerAccListType,
ownerName: AE.OwnerName] RETURNS[accessList: AE.AccessList] =
BEGIN -- non system-fatal errors: none.
IF ACU.Compare[ownerName, ACP.SpecialOwnerForAlpineAdmin]
THEN RETURN[NIL]
ELSE RETURN[LIST["Owner"]];
END;




-- procedures to search access lists:



IsClientInOwnerAccList: PROCEDURE[conversation: AE.Conversation, pntrAccList:
ACP.PntrAccList, ownerName: AE.OwnerName] RETURNS [okay: BOOLEAN]
= TRUSTED
BEGIN -- non system-fatal errors: AC.OperationFailed[regServersUnavailable].
clientName: AE.Principal;
accessList: AE.AccessList ← NIL;
pntrStringRep: LONG POINTER TO ACP.StringRep ← @pntrAccList.principals;
IF pntrAccList.world THEN RETURN[TRUE];
clientName ← ClientMap.GetName[conversation];
IF ((pntrAccList.owner) AND (IsClientInListOfVanillaNames[clientName, LIST[ownerName]]))
THEN RETURN[TRUE];
FOR accListCount: CARDINAL IN [0..pntrAccList.count)
DO name: AE.RName;
size: CARDINAL;
[name, size] ← ACU.MakeRNameFromStringRep[pntrStringRep];
accessList ← CONS[name, accessList];
pntrStringRep ← pntrStringRep + size;
ENDLOOP;
RETURN[IsClientInListOfVanillaNames[clientName, accessList]];
END;



-- this checks a file access list, which may contain "world", "*", or "owner".

IsClientInFileAccessList: PROCEDURE[conversation: AE.Conversation, openFileID:
AE.OpenFileID, accessList: AE.AccessList] RETURNS [okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.Unknown[openFileID, transID].
newAccessList: AE.AccessList ← NIL;
ownerSeen: BOOLEANFALSE;
FOR list: AE.AccessList ← accessList, list.rest UNTIL list = NIL
DO
SELECT TRUE FROM
ACU.Compare[list.first, "world"] OR ACU.CompareCaseMatters[list.first, "*"] =>
RETURN[TRUE];
ACU.Compare[list.first, "owner"] => ownerSeen ← TRUE;
ENDCASE => newAccessList ← CONS[list.first, newAccessList];
ENDLOOP;
IF ownerSeen
THEN newAccessList ← CONS[ReadOwnerName[conversation, openFileID], newAccessList];
TRUSTED BEGIN okay ← IsClientInListOfVanillaNames[ClientMap.GetName[conversation],
newAccessList]; END;
END;



-- does not expect "world", "*", or "owner". Just does a vanilla check of match or in names.

IsClientInListOfVanillaNames: PROCEDURE[clientName: AE.Principal, accessList:
AE.AccessList] RETURNS [okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.OperationFailed[regServersUnavailable].
FOR list: AE.AccessList ← accessList, list.rest -- check for direct match.
UNTIL list = NIL
DO IF ACU.Compare[list.first, clientName] THEN RETURN[TRUE]; ENDLOOP;
RETURN[ACC.VerifyClient[clientName, accessList]]; -- check in group.
END;




-- misc.:



GetVolGroupIDFromOpenFileID: PROCEDURE[conversation: AE.Conversation, openFileID:
AE.OpenFileID] RETURNS[volGroupID: AE.VolumeGroupID] = TRUSTED
BEGIN -- non system-fatal errors: AC.Unknown[openFileID, transID].
ENABLE AF.Unknown => SELECT what FROM openFileID => GOTO unkOpenFileID; transID =>
GOTO untTransID; ENDCASE => NULL;
RETURN[AV.GetEnclosingGroup[AID.myLocalConversation, , AF.GetUniversalFile[
conversation, openFileID].volumeID]];
EXITS
unkOpenFileID => ERROR AC.Unknown[openFileID];
untTransID => ERROR AC.Unknown[transID];
END;



ReadOwnerName: PROCEDURE[conversation: AE.Conversation, openFileID: AE.OpenFileID]
RETURNS[ownerName: AE.OwnerName] = TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[openFileID, transID].
WITH pVP: ReadFileProperty[conversation, openFileID, owner].first SELECT FROM
owner => ownerName ← pVP.owner;
ENDCASE => ERROR;
END;



ReadFileAccessList: PROCEDURE[conversation: AE.Conversation, openFileID: AE.OpenFileID,
accessListType: AE.AccessRights] RETURNS[fileAccessList: AE.AccessList] = TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[openFileID, transID].
desiredProperty: AE.Property ← (SELECT accessListType FROM readOnly => readAccess,
ENDCASE => modifyAccess);
WITH pVP: ReadFileProperty[conversation, openFileID, desiredProperty].first SELECT FROM
readAccess => BEGIN
IF desiredProperty # readAccess THEN ERROR;
fileAccessList ← pVP.readAccess;
END;
modifyAccess => BEGIN
IF desiredProperty # modifyAccess THEN ERROR;
fileAccessList ← pVP.modifyAccess;
END;
ENDCASE => ERROR;
END;



ReadFileProperty: PROCEDURE[conversation: AE.Conversation, openFileID: AE.OpenFileID,
desiredProperty: AE.Property] RETURNS[list: LIST OF AE.PropertyValuePair] = TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[openFileID, transID].
propSet: AF.PropertySet ← ALL[FALSE];
propSet[desiredProperty] ← TRUE;
RETURN[AF.ReadProperties[conversation, openFileID, propSet, [read, wait]
! AF.Unknown => SELECT what FROM openFileID => GOTO unkOpenFile; transID => GOTO
unkTransID; ENDCASE => NULL;
AF.LockFailed => GOTO deadlock]];
EXITS
deadlock => ERROR AC.LockFailed[timeout];
unkOpenFile => ERROR AC.Unknown[openFileID];
unkTransID => ERROR AC.Unknown[transID];
END;





AlpineWheelsOwnerName: AE.OwnerName ← NIL;


AssertAlpineWheel: PUBLIC PROCEDURE[conversation: AE.Conversation, transHandle:
AI.TransHandle, assert: BOOLEAN] RETURNS [okay: BOOLEAN] =
BEGIN -- non system-fatal errors: AC.OperationFailed[regServersUnavailable].
IF assert THEN
BEGIN
principal: AE.Principal;
TRUSTED BEGIN principal ← ClientMap.GetName[conversation]; END;
IF NOT ACC.VerifyClient[principal, LIST[AlpineWheelsOwnerName]]
THEN RETURN[FALSE];
END;
TM.EnableAlpineWheel[transHandle, conversation, assert];
RETURN[TRUE];
END;



-- Processing at end of a transaction:



PhaseOneSpaceAndOwnerChanges: PUBLIC PROCEDURE[transHandle:
TM.Handle] = TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[transID].
myLockItem: ACF.LockItem;
DO
BEGIN
ACM.PhaseOneSpaceOwnerChanges[transHandle
! ACF.LockConflict => BEGIN myLockItem.mode ← lockItem.mode;
WITH l: lockItem SELECT FROM
file => myLockItem.whatToLock ← file[l.transID, l.universalFile, l.refPattern];
pageRun => myLockItem.whatToLock ← pageRun[l.openFileID, l.pageRun];
ENDCASE; GOTO lockConflict; END];
RETURN;
EXITS
lockConflict => ACF.LockFileOrPageRun[myLockItem]; -- may error AC.OperationFailed[lockTimeout].
END;
ENDLOOP;
END
;




SetAccessControlLightInitialized: PUBLIC PROCEDURE[alpineWheels: AE.OwnerName] =
BEGIN -- non system-fatal errors: none.
AlpineWheelsOwnerName ← alpineWheels;
END;




END.


Edit Log

Initial: Kolling: January 11, 1983 5:52 pm: upper level module for "light" operations.