YggNavImpl.mesa
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Bob Hagmann May 3, 1988 9:51:35 am PDT
Top level navigational interface for Yggdrasil.
DIRECTORY
Basics USING [ByteBlt],
Random USING [Create, NextInt, RandomStream],
RefText USING [AppendChar, AppendRope, New],
Rope USING [Equal, Find, FromRefText, Length, ROPE],
YggDID USING [DID, ValidateDID],
YggDummyRPC USING [unencrypted],
YggEnvironment USING [Outcome, TransID],
YggIndex USING [],
YggNav USING [ErrorDesc, LinkList],
YggRep USING [AccurateGMT, AccurateGMTRep, Attribute, Bits, BitsRep, BytesFromBits, BytesToBits, date, DocType, float, int, lastReservedDocType, rope, shortRope, SetSizeOfBits, TypedPrimitiveElement, uninterpretedBytes, VDoc, VolatizeFromDID],
YggTransaction USING [Check, Create, Finish];
YggNavImpl: CEDAR MONITOR
IMPORTS Basics, Random, RefText, Rope, YggDID, YggRep, YggTransaction
EXPORTS YggDID, YggNav
~ BEGIN
Types, variables, and constants
ROPE: TYPE = Rope.ROPE;
Error: PUBLIC ERROR [error: YggNav.ErrorDesc] = CODE;
RootPath: PUBLIC ROPE ← "/tregonsee";
RootPathToRoots: PUBLIC ROPE ← "/tregonsee";
randomCharStream: Random.RandomStream;
DID: PUBLIC TYPE ~ REF DIDRep;
DIDRep: PUBLIC TYPE ~ ROPE;
For Phase 0, the DIDRep is just a string that names a directory (without the trailing /).
Transactions
Note: none of the root operations operate under transaction control
StartTransaction: PUBLIC PROC RETURNS [trans: YggEnvironment.TransID] ~ {
Start up a new transaction.
trans ← YggTransaction.Create[conversation: YggDummyRPC.unencrypted, createLocalWorker: TRUE];
};
EndTransaction: PUBLIC PROC [trans: YggEnvironment.TransID, commit: BOOL] RETURNS [ok: BOOLTRUE] ~ {
Commit or abort a transaction.
outcome: YggEnvironment.Outcome;
[outcome: outcome] ← YggTransaction.Finish[conversation: YggDummyRPC.unencrypted, transID: trans, requestedOutcome: IF commit THEN commit ELSE abort, continue: FALSE];
IF outcome = commit THEN RETURN [TRUE] ELSE RETURN [FALSE];
};
CheckTransaction: PUBLIC PROC [trans: YggEnvironment.TransID] RETURNS [ok: BOOL ← TRUE] ~ {
Check on a transaction. If it has been aborted, ok is FALSE.
outcome: YggEnvironment.Outcome;
[outcome: outcome] ← YggTransaction.Check[transID: trans];
IF outcome = unknown THEN RETURN [TRUE] ELSE RETURN [FALSE];
};
Root objects
Note: none of the root operations operate under transaction (or any other!) control
GetRoots: PUBLIC PROC RETURNS [roots: LIST OF DID] ~ {
Get all the root objects for the service.
enumProc: PROC [localName, symbolicName: ROPE] RETURNS [continue: BOOLTRUE] = {
did: DID;
did ← NEW[DIDRep];
did^ ← symbolicName;
roots ← CONS[did, roots];
};
EnumerateSymbolicLinksInDirectory [directory: RootPathToRoots, proc: enumProc];
};
AddRoot: PUBLIC PROC [did: DID] RETURNS [ok: BOOL] ~ {
Add a root object. If the DID does not exist, FALSE will be returned.
tries: INT ← 0;
nameSize: INT ← 4;
unixdid: REF TEXT ~ UnixStringFromRope[did^];
name1: CARD ~ LOOPHOLE[unixdid, CARD]+UNITS[TEXT[0]];
IF ~YggDID.ValidateDID[did] THEN RETURN [FALSE];
DO
sl: INT;
symname: REF TEXT ← RefText.New[nameSize];
unixSymname: REF TEXTNIL;
name2: CARD;
FOR charNo: INT IN [0..nameSize) DO
[] ← RefText.AppendChar[to: symname, from: Random.NextInt[] + 'a];
ENDLOOP;
unixSymname ← UnixStringFromRope[""];
name2 ← LOOPHOLE[unixSymname, CARD]+UNITS[TEXT[0]];
sl ← Symlink[name1, name2];
IF sl = 0 THEN EXIT;
tries ← tries + 1;
IF tries MOD 250 = 0 THEN nameSize ← MIN[50, nameSize + 1];
ENDLOOP;
};
RemoveRoot: PUBLIC PROC [did: DID] RETURNS [ok: BOOL] ~ {
Remove a root object. If the DID is not a root object, FALSE will be returned.
enumProc: PROC [localName, symbolicName: ROPE] RETURNS [continue: BOOLTRUE] = {
IF Rope.Equal[did^, symbolicName] THEN {
nameToDelete ← localName;
continue ← FALSE;
};
};
nameToDelete: ROPENIL;
EnumerateSymbolicLinksInDirectory [directory: RootPathToRoots, proc: enumProc];
IF nameToDelete # NIL THEN {
unixNameToDelete: REF TEXT ~ UnixStringFromRope[nameToDelete];
[] ← Unlink[LOOPHOLE[unixNameToDelete, CARD]+UNITS[TEXT[0]]];
};
};
Object contents
GetTypeOfContents: PUBLIC PROC [trans: YggEnvironment.TransID, did: YggDID.DID] RETURNS [YggRep.DocType] ~ {
Get the contents of the object with the specified did.
Errors: $invalidDID, $invalidTrans
document: YggRep.VDoc;
document ← YggRep.VolatizeFromDID[trans, did];
RETURN[document.contents.docType];
};
GetUninterpretedContents: PUBLIC UNSAFE PROC [trans: YggEnvironment.TransID, did: DID, firstByte: CARD, byteCount: CARD, to: LONG POINTER] RETURNS [bytesMoved: CARD] ~ {
Get the contents of the object with the specified did. The contents must not be a well known interpreted type (e. g., int, date, float, ...)
Errors: $invalidDID, $invalidTrans, $invalidReservedType
document: YggRep.VDoc;
document ← YggRep.VolatizeFromDID[trans, did];
IF document.contents.docType # YggRep.uninterpretedBytes AND document.contents.docType <= YggRep.lastReservedDocType THEN ERROR Error[[$invalidReservedType, "Attempt to access a TypedPrimitiveElement with reserved type as uninterpreted"]];
YggRep.BytesFromBits[bits: document.contents.bits, startByte: firstByte, block: [base: LOOPHOLE[to], startIndex: 0, count: byteCount]];
};
GetContents: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] RETURNS [contents: YggRep.TypedPrimitiveElement] ~ {
Get the contents of the object with the specified did.
Errors: $invalidDID, $invalidTrans
document: YggRep.VDoc;
document ← YggRep.VolatizeFromDID[trans, did];
RETURN[document.contents];
};
SetUninterpretedContents: PUBLIC UNSAFE PROC [trans: YggEnvironment.TransID, did: YggDID.DID, firstByte: CARD, byteCount: CARD, from: LONG POINTER] RETURNS [bytesMoved: CARD] ~ {
Set the contents of the object with the specified did. The object is grown if needed.
Errors: $invalidDID, $invalidTrans
document: YggRep.VDoc;
newRef: BOOL;
newBits: YggRep.Bits;
document ← YggRep.VolatizeFromDID[trans, did, readWrite, [write, wait]];
IF document.contents.docType # YggRep.uninterpretedBytes AND document.contents.docType <= YggRep.lastReservedDocType THEN ERROR Error[[$invalidReservedType, "Attempt to set a TypedPrimitiveElement with reserved type as uninterpreted"]];
[newRef: newRef, newBits: newBits] ← YggRep.BytesToBits[bits: document.contents.bits, startByte: firstByte, block: [base: LOOPHOLE[from], startIndex: 0, count: byteCount]];
IF newRef THEN document.contents.bits ← newBits;
RETURN[byteCount];
};
SetContents: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, contents: YggRep.TypedPrimitiveElement] ~ {
Set the contents of the object with the specified did. The object is grown if needed.
Errors: $invalidDID, $invalidTrans
document: YggRep.VDoc;
document ← YggRep.VolatizeFromDID[trans, did, readWrite, [write, wait]];
document.contents ← contents;
};
SetSize: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, size: CARD] ~ {
Set the contents of the object with the specified did. The object is grown if needed.
Errors: $invalidDID, $invalidTrans,
document: YggRep.VDoc;
newRef: BOOL;
newBits: YggRep.Bits;
document ← YggRep.VolatizeFromDID[trans, did, readWrite, [write, wait]];
IF document.contents.docType # YggRep.uninterpretedBytes AND document.contents.docType <= YggRep.lastReservedDocType THEN ERROR Error[[$invalidReservedType, "Attempt to set a TypedPrimitiveElement with reserved type as uninterpreted"]];
[newRef: newRef, newBits: newBits] ← YggRep.SetSizeOfBits[bits: document.contents.bits, size: size];
IF newRef THEN document.contents.bits ← newBits;
};
Object property manipulation
GetProperty: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, propertyName: ROPE] RETURNS [propertyExists: BOOL, property: YggRep.Attribute] ~ {
Get the named property of the object with the specified did. If the property does not exist, [FALSE, [NIL, NIL]] is returned.
Errors: $invalidDID, $invalidTrans
};
GetAllProperties: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] RETURNS [properties: LIST OF YggRep.Attribute] ~ {
Get all the properties of the object with the specified did. If no properties exist, NIL is returned.
Errors: $invalidDID, $invalidTrans
};
ListAllProperties: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] RETURNS [propertyNames: LIST OF ROPE] ~ {
Get all the property names of the object with the specified did. If no properties exist, NIL is returned.
Errors: $invalidDID, $invalidTrans
};
SetProperty: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, propertyName: ROPE, property: YggRep.Attribute] ~ {
Set the contents of the object with the specified did. The object is grown if needed.
Errors: $invalidDID, $invalidTrans
};
Object property construction
Note: all of these procedures copy data.
ConstructIntTPE: PUBLIC PROC [int: INT32] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for a 32 bit integer.
Errors: $invalidDID, $invalidTrans
ri: REF INT32;
ri ← NEW[INT32 ← int];
property ← [YggRep.int, ri];
};
ConstructFloatTPE: PUBLIC PROC [float: REAL32] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for a 32 bit real.
rReal: REF REAL32;
rReal ← NEW[REAL32 ← float];
property ← [YggRep.float, rReal];
};
ConstructDateTPE: PUBLIC PROC [date: YggRep.AccurateGMTRep] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for a date.
rAccurateGMT: YggRep.AccurateGMT;
rAccurateGMT ← NEW[YggRep.AccurateGMTRep ← date];
property ← [YggRep.date, rAccurateGMT];
};
ConstructShortRopeTPE: PUBLIC PROC [rope: ROPE] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for a ROPE that is short (less than a few disk pages preferred) and contains no nulls.
Errors: $shortRopeWithNulls
IF Rope.Find[rope, "\000"] = -1 THEN {
property ← [YggRep.shortRope, rope];
}
ELSE ERROR Error[[$shortRopeWithNulls, "Rope has nulls"]];
};
ConstructRopeTPE: PUBLIC PROC [rope: ROPE] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for a ROPE that is long (more than a disk page preferred) and may contain no nulls.
property ← [YggRep.rope, rope];
};
ConstructUninterpretedBytesTPE: PUBLIC PROC [docType: YggRep.DocType, bytes: LONG POINTER, size: CARD] RETURNS [property: YggRep.TypedPrimitiveElement] ~ {
Construct a YggRep.TypedPrimitiveElement for an array of bytes.
Errors: $$invalidReservedType
nBytes: CARD;
rBits: REF YggRep.BitsRep;
IF docType # YggRep.uninterpretedBytes AND docType <= YggRep.lastReservedDocType THEN ERROR Error[[$invalidReservedType, "Attempt to create a TypedPrimitiveElement with reserved type"]];
rBits ← NEW[YggRep.BitsRep[size]];
rBits.validBytes ← size;
TRUSTED {nBytes ← Basics.ByteBlt[
from: [blockPointer: bytes, startIndex: 0, stopIndexPlusOne: size],
to: [blockPointer: LOOPHOLE[rBits, POINTER] + SIZE[YggRep.BitsRep[0]], startIndex: 0, stopIndexPlusOne: size]];
};
IF nBytes # size THEN ERROR;
property ← [docType, rBits];
};
Links
GetTypedOutlinks: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, linkType: ROPE] RETURNS [LIST OF DIDNIL] ~ {
Get the did's of all the outlinks for this object with "link type" propertyName.
Errors: $invalidDID, $invalidTrans
};
GetAllOutlinks: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] RETURNS [LIST OF YggNav.LinkList ← NIL] ~ {
Get the did's of all the outlinks for this object.
Errors: $invalidDID, $invalidTrans
};
GetTypedInlinks: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, linkType: ROPE] RETURNS [LIST OF DIDNIL] ~ {
Get the did's of all the inlinks for this object with "link type" propertyName.
Errors: $invalidDID, $invalidTrans
};
GetAllInlinks: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] RETURNS [LIST OF YggNav.LinkList ← NIL] ~ {
Get the did's of all the inlinks for this object.
Errors: $invalidDID, $invalidTrans
};
SnapLink: PUBLIC PROC [trans: YggEnvironment.TransID, fromDID: DID, toDID: DID, linkType: ROPE] RETURNS [linkDID: DIDNIL] ~ {
Construct a link between the two dids.
Errors: $invalidDID, $invalidTrans
};
Containers
GetDefaultContainer: PUBLIC PROC [trans: YggEnvironment.TransID] RETURNS [DIDNIL] ~ {
Get the default container for this transaction.
Errors: $invalidDID, $invalidTrans
};
SetDefaultContainer: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] ~ {
Set the default container for this transaction.
Errors: $invalidDID, $invalidTrans
};
AddToContainer: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, containerDID: DID] ~ {
Add the object to the container.
Errors: $invalidDID, $invalidTrans
};
RemoveFromContainer: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID, containerDID: DID] RETURNS [ok: BOOLTRUE] ~ {
Remove the object from the container.
Errors: $invalidDID, $invalidTrans
};
GetObjectsInContainer: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID] RETURNS [LIST OF DIDNIL] ~ {
Get the did's of all the objects in this container. containerDID of NIL means use the default container.
Errors: $invalidDID, $invalidTrans
};
AddContainerToContainer: PUBLIC PROC [trans: YggEnvironment.TransID, subcontainerDID: DID, containerDID: DID] ~ {
Add the subcontainer to the container.
Errors: $invalidDID, $invalidTrans
};
RemoveContainerFromContainer: PUBLIC PROC [trans: YggEnvironment.TransID, subcontainerDID: DID, containerDID: DID] RETURNS [ok: BOOLTRUE] ~ {
Remove the subcontainer from the container.
Errors: $invalidDID, $invalidTrans
};
GetContainersInContainer: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID] RETURNS [LIST OF DIDNIL] ~ {
Get the did's of all the containers in this container. containerDID of NIL means use the default container.
Errors: $invalidDID, $invalidTrans
};
Object creation and destruction
CreateObject: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID] RETURNS [did: DIDNIL] ~ {
Make up a new object in the specified container. The default container for the transaction is used if containerDID is NIL.
Errors: $invalidDID, $invalidTrans
};
RemoveObject: PUBLIC PROC [trans: YggEnvironment.TransID, did: DID] ~ {
Remove a object. Any links pointing to this object are also removed. This is a recursive process (if the links are really objects).
Errors: $invalidDID, $invalidTrans
};
Indices
CreateIndex: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID, pattern: ROPE] RETURNS [did: DIDNIL] ~ {
Index all properties in the container whose property name matches the pattern.
Errors: $invalidDID, $invalidTrans
};
ListIndexPatterns: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID] RETURNS [patterns: LIST OF ROPENIL] ~ {
Remove a pattern from a container. The pattern must exactly match an existing pattern.
Errors: $invalidDID, $invalidTrans
};
RemoveIndex: PUBLIC PROC [trans: YggEnvironment.TransID, containerDID: DID, pattern: ROPE] RETURNS [ok: BOOLTRUE] ~ {
Remove a pattern from a container. The pattern must exactly match an existing pattern.
Errors: $invalidDID, $invalidTrans
};
Utilities
From dir.h:
typedef struct 𡤍irdesc {
 int dd�
 long dd←loc;
 long dd←size;
 long dd�se;
 long dd𡤎ntno;
 long dd𡤋size;
 char *dd𡤋uf;
} DIR;

struct direct {
 u←long d𡤏ileno;  /* file number of entry */
 u←short d←reclen;  /* length of this record */
 u←short d←namlen;  /* length of string in d←name */
 char d←name[MAXNAMLEN + 1]; /* name (up to MAXNAMLEN + 1) */
};
DIR: TYPE ~ PACKED RECORD [
ddFd: INT,
ddLoc: INT,
ddSize: INT,
ddBbase: INT,
ddEntno: INT,
ddBsize: INT,
ddBuf: LONG POINTER TO ARRAY [0..0] OF CHAR
];
direct: TYPE ~ PACKED RECORD [
dFileno: CARD32,
dReclen: CARD16,
dNamlen: CARD16,
dName: LONG POINTER TO ARRAY [0..0] OF CHAR
];
UnixStringFromRope: PROC [rope: ROPE] RETURNS [REF TEXT] ~ {
len: INT ← rope.Length[];
rtn: REF TEXT ← RefText.New[len+1];
[] ← RefText.AppendRope[to: rtn, from: rope];
[] ← RefText.AppendChar[to: rtn, from: '\000];
RETURN[rtn];
};
EnumerateSymbolicLinksInDirectory: PROC [directory: ROPE, proc: PROC [localName, symbolicName: ROPE] RETURNS [continue: BOOL ← TRUE]] ~ {
dirStream: LONG POINTER TO DIR;
cont: BOOL;
unixDirName: REF TEXT ~ UnixStringFromRope[directory];
dirStream ← Opendir[LOOPHOLE[unixDirName, CARD]+UNITS[TEXT[0]]];
IF dirStream # NIL THEN {
symLinkBuff: REF TEXT ← RefText.New[256];
symLinkResult: REF TEXT ← RefText.New[1024];
resetLen: INT;
[] ← RefText.AppendRope[to: symLinkBuff, from: directory];
[] ← RefText.AppendChar[to: symLinkBuff, from: '/];
resetLen ← symLinkBuff.length;
DO
dirp: LONG POINTER TO direct;
cc: INT;
symLinkName: ROPE;
localName: ROPE;
localNameLength: INT;
dirp ← Readdir[dirStream];
IF dirp = NIL THEN EXIT;
symLinkBuff.length ← resetLen;
TRUSTED {
localNameLength ← dirp.dNamlen;
FOR charNo: INT IN [0..localNameLength] DO
[] ← RefText.AppendChar[to: symLinkBuff, from: dirp.dName[charNo]];
ENDLOOP;
};
[] ← RefText.AppendChar[to: symLinkBuff, from: '\000];
cc ← Readlink[path: LOOPHOLE[symLinkBuff, CARD] +UNITS[TEXT[0]], buf: LOOPHOLE[symLinkResult, CARD]+UNITS[TEXT[0]], bufsiz: 1023];
IF cc <= 0 THEN LOOP;
symLinkResult.length ← cc;
symLinkName ← Rope.FromRefText[s: symLinkResult, start: 0, len: cc];
localName ← Rope.FromRefText[s: symLinkBuff, start: 0, len: resetLen + localNameLength];
cont ← proc[localName, symLinkName];
IF ~cont THEN EXIT;
ENDLOOP;
[] ← Closedir[dirStream];
};
};
Unix system calls
Symlink: PROC [name1: CARD, name2: CARD] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"symlink"
};
Opendir: PROC [name: CARD] RETURNS [LONG POINTER TO DIR] ~ TRUSTED MACHINE CODE {
"opendir"
};
Readdir: PROC [LONG POINTER TO DIR] RETURNS [LONG POINTER TO direct] ~ TRUSTED MACHINE CODE {
"readdir"
};
Closedir: PROC [LONG POINTER TO DIR] RETURNS [trash: INT] ~ TRUSTED MACHINE CODE {
"closedir"
};
Readlink: PROC [path: CARD, buf: CARD, bufsiz: INT] RETURNS [cc: INT] ~ TRUSTED MACHINE CODE {
"readlink"
};
Unlink: PROC [path: CARD] RETURNS [cc: INT] ~ TRUSTED MACHINE CODE {
"unlink"
};
Initialization
randomCharStream ← Random.Create[range: 26];
END.