-- 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: BOOLEAN _ FALSE; 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: BOOLEAN _ FALSE; 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.