-- AlpineOwner.mesa
-- Procedures for owner database maintenance.
-- Last edited by
--   Kolling on March 8, 1983 4:21 pm
--   MBrown on March 3, 1983 2:45 pm

-- Some aspects of owner database maintenance are decentralized, in that normal Alpine
-- users may perform them.  These include inspecting individual owner records and
-- updating certain fields of owner records (these updates are restricted by access control
-- checks that are a function of the owner).

-- Other aspects of owner database maintenance are reserved for Alpine administrators.
-- These include creating and deleting owner records, updating disk space quotas,
-- and enumerating the entire set of owners.
-- On a particular server, these operations may only be invoked by individuals who belong
-- to that server's AlpineWheels group.  Since these operations are potentially destructive,
-- they must be explicitly "enabled" by a call to AlpineTransaction.AssertAlpineWheel.

DIRECTORY
  AlpineEnvironment;

AlpineOwner: DEFINITIONS =
  BEGIN

  AccessList: TYPE = AlpineEnvironment.AccessList;  -- necessary, or lupine bombs out.
  Conversation: TYPE = AlpineEnvironment.Conversation;
  OwnerName: TYPE = AlpineEnvironment.OwnerName;
  PageCount: TYPE = AlpineEnvironment.PageCount;
  TransID: TYPE = AlpineEnvironment.TransID;
  VolumeGroupID: TYPE = AlpineEnvironment.VolumeGroupID;


  -- When a procedure raises AccessFailed [alpineWheel] below, this always means that
  -- the (conversation, transID) pair passed to the procedure needed to be asserted
  -- (by AlpineTransaction.AssertAlpineWheel[enable: TRUE]), but was not.


  -- Procedures for both Alpine users and Alpine administrators.

  OwnerProperty: TYPE = AlpineEnvironment.OwnerProperty;
  OwnerPropertyValuePair: TYPE = AlpineEnvironment.OwnerPropertyValuePair;

  OwnerPropertySet: TYPE = AlpineEnvironment.OwnerPropertySet;
  allOwnerProperties: OwnerPropertySet = AlpineEnvironment.allOwnerProperties;

  ReadProperties: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    owner: OwnerName, desiredProperties: OwnerPropertySet ← allOwnerProperties]
    RETURNS [properties: LIST OF OwnerPropertyValuePair];
    -- ! LockFailed {timeout},
    -- ! StaticallyInvalid,
    -- ! Unknown {owner, transID, volumeGroupID}.
    -- Reads the properties specified by desiredProperties, ordered as in the declaration
    -- of OwnerProperty.

  WriteProperties: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    owner: OwnerName, properties: LIST OF OwnerPropertyValuePair,
    enforceTotalQuota: BOOL ← FALSE];
    -- ! AccessFailed {alpineWheel, ownerEntry},
    -- ! LockFailed {timeout},
    -- ! OperationFailed {ownerRecordFull, ownerRecordInUse,
    --    regServersUnavailable, totalQuotaExceeded},
    -- ! StaticallyInvalid,
    -- ! Unknown {owner, transID, volumeGroupID}.
    -- Writes the supplied properties, leaving the others unchanged.  Obtains a write
    -- lock on the owner record.  (For now, WriteProperties fails by raising
    -- OperationFailed [ownerRecordInUse] if a write of the quota is requested and 
    -- another transaction is concurrently changing the space in use by the given
    -- owner.  Retrying may succeed.)

    -- The owner, and members of the owner's Modify access list, can update the
    -- Create access list property of an owner record.  The owner, and members of
    -- the owner's Create access list can update the rootFile property of an owner record.
    -- If an update is restricted to the these properties, but the access control checks
    -- fail, then WriteProperties raises AccessFailed [ownerEntry].  The spaceInUse property
    -- is read only.

    -- If enforceTotalQuota = TRUE and properties includes quota, then WriteProperties
    -- raises OperationFailed [totalQuotaExceeded] if the total of all quotas in the owner
    -- database, including the specified update, exceeds the total number of disk pages
    -- on the volume group.

    -- On all updates, certain high-level invariants are enforced: owner access lists always
    -- have world = FALSE and owner = TRUE.
    -- An update to an access control list may raise OperationFailed [ownerRecordFull].
    -- The system guarantees to reserve enough space for access control list storage so that
    -- each list can contain two maximum-length RNames, but does not control the
    -- division of space between lists.  In order to add an element to one list it may be
    -- necessary to remove an element from another.


  -- Procedures for Alpine administrators only.  These procedures require that
  -- (conversation, transID) be asserted as an Alpine Wheel.

  Create: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    owner: OwnerName, properties: LIST OF OwnerPropertyValuePair,
    enforceTotalQuota: BOOL ← FALSE]
    RETURNS [spaceLeftOnVolumeGroup: PageCount];
    -- ! AccessFailed {alpineWheel},
    -- ! LockFailed {timeout},
    -- ! OperationFailed {duplicateOwner, ownerDatabaseFull,
    --    ownerRecordFull, totalQuotaExceeded},
    -- ! StaticallyInvalid,
    -- ! Unknown {transID, volumeGroupID}.
    -- Creates a new owner in the specified VolumeGroup.  The properties of the new
    -- owner are derived by (in effect) setting all properties to their default values, then
    -- calling WriteProperties with the specified list of properties.

  Destroy: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    owner: OwnerName];
    -- ! AccessFailed {alpineWheel},
    -- ! LockFailed {timeout},
    -- ! OperationFailed {ownerRecordInUse, spaceInUseByThisOwner},
    -- ! StaticallyInvalid,
    -- ! Unknown {owner, transID, volumeGroupID}.
    -- Destroys an existing owner in the specified VolumeGroup.
    -- Raises OperationFailed [spaceInUseByThisOwner] if any files exist whose disk space
    -- is charged against owner.

  ReadNext: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    previousOwner: OwnerName,
    desiredProperties: OwnerPropertySet ← allOwnerProperties]
    RETURNS [owner: OwnerName, properties: LIST OF OwnerPropertyValuePair];
    -- ! AccessFailed {alpineWheel},
    -- ! LockFailed {timeout},
    -- ! StaticallyInvalid,
    -- ! Unknown {owner, transID, volumeGroupID}.
    -- Stateless enumerator for the owner database.  previousOwner = NIL starts an
    -- enumeration, and owner = NIL is returned at the end of an enumeration.
    -- This call locks the entire owner database in write mode to ensure consistency,
    -- which is the reason for restricting its use to Alpine administrators.

  ReadDBProperties: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID]
    RETURNS [nOwners, nEntriesUsed, nEntries: NAT,
    totalQuota, totalSpaceInUse, volumeGroupSize: PageCount];
    -- ! AccessFailed {alpineWheel},
    -- ! LockFailed {timeout},
    -- ! Unknown {transID, volumeGroupID}.
    -- Returns aggregate information about the owner database.
    -- <Locking?>
    -- nOwners is the number of owners in the database, while nEntriesUsed is the
    -- number of database entries in use (deleted entries may occupy space).
    -- When nEntriesUsed = nEntries, any Create call will raise OperationFailed
    -- [ownerDatabaseFull]; a call to ReorganizeDB is required (see below)
    
  ReorganizeDB: PROC [
    conversation: Conversation, transID: TransID, volumeGroupID: VolumeGroupID,
    nEntries: NAT];
    -- ! AccessFailed {alpineWheel},
    -- ! LockFailed {timeout},
    -- ! OperationFailed {insufficientSpace, ownerDatabaseFull},
    -- ! Unknown {transID, volumeGroupID}.
    -- Performs a complete reorganization of the owner database, locking it in write mode
    -- and thereby making it unavailable during the reorganization.
    -- Reorganization is called for when the owner database becomes full (Create raises
    -- OperationFailed [ownerDatabaseFull]), or when the performance of owner database
    -- operations degrades due to hash collisions or deletions.
    -- The new owner database file allows up to nEntries owners; ReorganizeDB raises
    -- OperationFailed [ownerDatabaseFull] if the existing database contains more than
    -- this many owners.  ReorganizeDB raises OperationFailed [insufficientSpace]
    -- if there is not enough scratch space on the volume group to perform
    -- the reorganization.


  -- Errors

  AccessFailed: ERROR [missingAccess: AlpineEnvironment.NeededAccess];

  LockFailed: ERROR [why: AlpineEnvironment.LockFailure];

  OperationFailed: ERROR [why: AlpineEnvironment.OperationFailure];

  StaticallyInvalid: ERROR;

  Unknown: ERROR [what: AlpineEnvironment.UnknownType];

END.