AccessControlLightImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Hauser, March 7, 1985 1:55:33 pm PST
Carl Hauser, August 8, 1985 2:37:11 pm PDT
Last edited by
Kolling on July 14, 1983 11:15 am
Last Edited by: Kupfer, August 6, 1984 2:35:26 pm PDT
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, LockOption, 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],
Convert
USING[RopeFromInt],
SkiPatrolHooks
USING[TransIDFromTransHandle],
SkiPatrolLog
USING[notice, OpFailureInfo];
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, Convert, Rope, SkiPatrolHooks, SkiPatrolLog, TM: AccessControlTransMap
EXPORTS AccessControl, AccessControlLight =
BEGIN OPEN AE: AlpineEnvironment, AI: AlpineInternal;
Technology export restrictions require some care in determining what constitutes "The world"
TheWorld: AE.RName ← "USRegistries^.Internet";
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 {
logProc: PROC [SkiPatrolLog.OpFailureInfo];
IF (logProc ← SkiPatrolLog.notice.operationFailed) #
NIL
THEN
logProc[[
what: quotaExceeded,
where: "AccessControlLightImpl.ChangeSpaceForOwner",
transID: SkiPatrolHooks.TransIDFromTransHandle[transHandle],
message: Rope.Cat["Quota exceeded for ", ownerName, "; nPages = ", Convert.RopeFromInt[nPages]]
]];
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, lockOption: AE.LockOption ← [read,wait]] 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, lockOption],
ACP.SpecialOwnerForAlpineAdmin]
THEN ERROR AC.StaticallyInvalid
ELSE RETURN[TRUE];
END;
SELECT requestedAccess
FROM
readWrite =>
RETURN[IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readWrite, lockOption], lockOption]];
readOnly => okay ← IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readOnly, lockOption], lockOption] OR
IsClientInFileAccessList[conversation, openFileID,
ReadFileAccessList[conversation, openFileID, readWrite, lockOption], lockOption];
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;
TRUSTED { clientName ← ClientMap.GetName[conversation] };
IF ((pntrAccList.owner
OR pntrAccList.world )
AND (IsClientInListOfVanillaNames[clientName,
LIST[ownerName]]))
THEN RETURN[TRUE];
IF pntrAccList.world THEN accessList ← CONS[TheWorld, accessList];
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, lockOption: AE.LockOption]
RETURNS [okay:
BOOLEAN] =
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.OperationFailed[regServersUnavailable], AC.Unknown[openFileID, transID].
newAccessList: AE.AccessList ← NIL;
worldSeen: BOOLEAN ← FALSE;
ownerName: AE.Principal = ReadOwnerName[conversation, openFileID, lockOption];
clientName: AE.Principal;
TRUSTED { clientName ← ClientMap.GetName[conversation] };
FOR list:
AE.AccessList ← accessList, list.rest
UNTIL list =
NIL DO
SELECT
TRUE
FROM
ACU.Compare[list.first, "world"]
OR
ACU.CompareCaseMatters[list.first, "*"] => {
IF ACU.Compare[ownerName, clientName] THEN RETURN[TRUE];
worldSeen ← TRUE;
};
ACU.Compare[list.first, "owner"] => IF ACU.Compare[ownerName, clientName] THEN RETURN[TRUE];
ENDCASE => newAccessList ← CONS[list.first, newAccessList];
ENDLOOP;
IF worldSeen THEN newAccessList ← CONS[TheWorld, newAccessList];
okay ← IsClientInListOfVanillaNames[clientName, newAccessList];
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, lockOption: AE.LockOption ← [read,wait]]
RETURNS[ownerName:
AE.OwnerName] =
TRUSTED
BEGIN -- non system-fatal errors: AC.LockFailed[timeout], AC.Unknown[openFileID, transID].
WITH pVP: ReadFileProperty[conversation, openFileID, owner, lockOption].first
SELECT
FROM
owner => ownerName ← pVP.owner;
ENDCASE => ERROR;
END;
ReadFileAccessList:
PROCEDURE[conversation:
AE.Conversation, openFileID:
AE.OpenFileID,
accessListType:
AE.AccessRights, lockOption: AE.LockOption]
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, lockOption].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, lockOption: AE.LockOption]
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, lockOption.ifConflict]
!
AF.Unknown =>
SELECT what
FROM openFileID =>
GOTO unkOpenFile; transID =>
GOTO
unkTransID; ENDCASE => NULL;
AF.LockFailed => SELECT why FROM conflict => GOTO conflict; timeout => GOTO deadlock; ENDCASE => NULL]];
EXITS
We log (SkiPatrolLog) the Lock failure at a lower level.
conflict => ERROR AC.LockFailed[conflict];
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.
Initial: Kolling: January 11, 1983 5:52 pm: upper level module for "light" operations.
Edited on July 24, 1984 2:55:28 pm PDT, by Kupfer
Add SkiPatrolLog probe.
changes to: ChangeSpaceForOwner, ReadFileProperty
Edited on August 6, 1984 2:34:30 pm PDT, by Kupfer
Remove the possible race condition in SkiPatrolLog probes by assigning the PROC to a temporary variable.
changes to: ChangeSpaceForOwner