DIRECTORY
Basics,
BasicTime,
PBasics,
Process USING [Detach, MsecToTicks, Pause, SecondsToTicks, Ticks],
RefText,
Rope,
SunMount,
SunNFS,
YggDID USING [DID, EqualDIDs, StabilizeDID, ValidateDID, VolatilizeDID],
YggDIDPrivate USING [DIDForNFSRoot, DIDRep],
YggDIDMap USING [CreateExplicitDID],
YggFixedNames,
YggEnvironment,
YggFile,
YggNaming,
YggNav,
YggNFS,
YggRep,
YggTransaction;

YggNFSImpl: CEDAR MONITOR
IMPORTS Basics, BasicTime, PBasics, Process, RefText, Rope, YggDID, YggDIDPrivate, YggDIDMap, YggFile, YggFixedNames, YggNaming, YggNav, YggRep, YggTransaction
EXPORTS YggDID, YggNFS
~ BEGIN

ErrorOnMistake: BOOL _ TRUE;  -- TRUE for unit debugging

ROPE: TYPE = Rope.ROPE;

DID: PUBLIC TYPE ~ REF DIDRep;
DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep;

AllNulls: PACKED ARRAY [0..Basics.charsPerWord) OF CHAR _ ALL[0C];

BadProps: PUBLIC ERROR [stat: SunNFS.Stat] = CODE;

dawnOfHistory: SunNFS.TimeVal _ SunTimeFromGMT[sunEpoch];

EmptyCookie: SunNFS.Cookie;

nullFAttr: SunNFS.FAttr _ [non, 0, 1, 0, 0, 0, 1024, 0, 0, 0, 0, dawnOfHistory, dawnOfHistory, dawnOfHistory];

sunEpoch: BasicTime.GMT _ BasicTime.Pack[
[year~1970, month~January, day~1, hour~0, minute~0, second~0, zone~0, dst~no]
];

CookieJarItem: TYPE = RECORD [
timeIssued: BasicTime.GMT,
cookie: SunNFS.Cookie,
nameToStart: REF TEXT
];
CookieJar: LIST OF CookieJarItem _ NIL;
LastCookie: SunNFS.Cookie;

SizeOfTimeCache: INT = 077B;  -- all low bits on
TimeCache: ARRAY [0..SizeOfTimeCache] OF LIST OF TimeCacheItem _ ALL[NIL];
TimeCacheItem: TYPE = RECORD[
did: DID,
atime: SunNFS.TimeVal,
mtime: SunNFS.TimeVal,
itemCreateTime: BasicTime.GMT
];

Getattr: PUBLIC PROC [file: SunNFS.FHandle] RETURNS [reply: SunNFS.AttrStat] ~ {
gotProps: BOOL _  TRUE;
replyStat: SunNFS.Stat;
reply _ InnerGetattr[file: file, fileDID: YggEnvironment.nullDID, trans: YggEnvironment.nullTransID ! BadProps => {
gotProps _ FALSE;
replyStat _ stat;
CONTINUE;
};
 ];
IF ~gotProps THEN {
reply _ [replyStat, nullFAttr];
};
};

Setattr: PUBLIC PROC [file: SunNFS.FHandle, attributes: SunNFS.SAttr] RETURNS [reply: SunNFS.AttrStat] ~ {
gotProps: BOOL _  TRUE;
replyStat: SunNFS.Stat;
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
currentAttrs: SunNFS.AttrStat;
IF attributes.mode = CARD.LAST AND attributes.uid = CARD.LAST AND attributes.gid = CARD.LAST AND attributes.size = CARD.LAST THEN { -- only changing times
currentAttrs _ InnerGetattr[file: file, fileDID: YggEnvironment.nullDID, trans: YggEnvironment.nullTransID ! BadProps => {
gotProps _ FALSE;
replyStat _ stat;
CONTINUE;
};
 ];

IF gotProps THEN {
IF attributes.atime = [CARD.LAST, CARD.LAST] AND attributes.mtime = [CARD.LAST, CARD.LAST] THEN RETURN [currentAttrs];  -- changing nothing
IF attributes.atime # [CARD.LAST, CARD.LAST] THEN currentAttrs.attributes.atime _ attributes.atime;
IF attributes.mtime # [CARD.LAST, CARD.LAST] THEN currentAttrs.attributes.mtime _ attributes.mtime;
StickInTimeCache[file, currentAttrs.attributes.atime, currentAttrs.attributes.mtime];
reply _ currentAttrs;
}
 ELSE {
reply _ [replyStat, nullFAttr];
};
}
 ELSE {
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
currentAttrs _ InnerGetattr[file: file, fileDID: YggEnvironment.nullDID, trans: YggEnvironment.nullTransID ! BadProps => {
gotProps _ FALSE;
replyStat _ stat;
CONTINUE;
};
 ];
IF gotProps THEN {
foundC: BOOL _ FALSE;
atimeC: SunNFS.TimeVal;
mtimeC: SunNFS.TimeVal;
reply.status _ ok;
[foundC, atimeC, mtimeC] _ RemoveTimeFromCache[file];
IF foundC THEN {
IF attributes.atime # [CARD.LAST, CARD.LAST] AND atimeC # [CARD.LAST, CARD.LAST] THEN attributes.atime _ atimeC;
IF attributes.mtime # [CARD.LAST, CARD.LAST] AND mtimeC # [CARD.LAST, CARD.LAST] THEN attributes.mtime _ mtimeC;
};
reply.attributes _ ChangeAttrsUnderTrans [trans: trans, fileDID: DIDFromFHandle[file], currentAttrs: currentAttrs.attributes, changeAttrs: attributes];
outcome _ YggTransaction.Finish[trans, commit];
IF outcome # commit THEN {RETURN [[status: acces, attributes: nullFAttr]]};
}
 ELSE {
reply _ [replyStat, nullFAttr];
outcome _ YggTransaction.Finish[trans, abort];
};
};
};

Lookup: PUBLIC PROC [which: SunNFS.DirOpArgs] RETURNS [reply: SunNFS.DirOpRes] ~ {
dirDID: YggDID.DID;
lookUpName: ROPE;
nameFound: BOOL;
moreThanOneMatch: BOOL;
didFound: YggDID.DID;
dirDID _ DIDFromFHandle[which.dir];
IF ~YggDID.ValidateDID[dirDID] THEN RETURN[[status: noent, file: NIL, attributes: nullFAttr]];
IF ~YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: dirDID] THEN RETURN[[status: notdir, file: NIL, attributes: nullFAttr]];
TRUSTED {lookUpName _ LOOPHOLE[which.name];};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch, didFound: didFound] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: dirDID, namePattern: lookUpName, version: "h"];
IF ~nameFound OR moreThanOneMatch THEN RETURN[[status: noent, file: NIL, attributes: nullFAttr] ];
RETURN[[status: ok,
file: FHandleFromDID[didFound],
attributes: InnerGetattr[file: NIL, fileDID: didFound, trans: YggEnvironment.nullTransID ! BadProps => CONTINUE ].attributes]];
RETURN[[status: acces, file: NIL, attributes: nullFAttr]];
};

Readlink: PUBLIC PROC [file: SunNFS.FHandle] RETURNS [status: SunNFS.Stat, data: SunNFS.Path] ~ {
fileDID: YggDID.DID;
isDirectory: BOOL _ FALSE;
fileDID _ DIDFromFHandle[file];
IF ~YggDID.ValidateDID[fileDID] THEN RETURN[noent, NIL];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: fileDID];
IF isDirectory THEN RETURN[isdir, NIL]
 ELSE {
contentsType: YggRep.DocType;
contentsType _ YggNav.GetTypeOfContents[trans: YggEnvironment.nullTransID, did: fileDID];
IF contentsType = YggRep.symbolicLink THEN {
trouble: BOOL _ FALSE;
bytesMoved: CARD _ 0;
bytesStored: CARD _ 0;
bytesStored _ YggNav.GetSize[trans: YggEnvironment.nullTransID, did: fileDID];
data _ NEW[TEXT[bytesStored]];
TRUSTED {bytesMoved _ YggNav.GetUninterpretedContents[trans: YggEnvironment.nullTransID, did: fileDID, firstByte: 0, byteCount: bytesStored, to: PointerFromRefText[data] ! YggNav.Error => {trouble _ TRUE; CONTINUE}]; };
IF trouble THEN RETURN[acces, NIL];
data.length _ bytesMoved;
RETURN[ok, data];
}
 ELSE RETURN[acces, NIL];
};
};

Read: PUBLIC PROC [file: SunNFS.FHandle, offset, count: CARD, block: REF TEXT] RETURNS [reply: SunNFS.AttrStat] ~ {
bytesMoved: CARD;
fileDID: YggDID.DID;
IF block.maxLength < count THEN RETURN[[status: acces, attributes: nullFAttr]];
block.length _ count;
fileDID _ DIDFromFHandle[file];
IF ~YggDID.ValidateDID[fileDID] THEN RETURN[[status: noent, attributes: nullFAttr]];
TRUSTED {bytesMoved _ YggNav.GetUninterpretedContents [trans: YggEnvironment.nullTransID, did: fileDID, firstByte: offset, byteCount: count, to: PointerFromRefText[block]];};
block.length _ bytesMoved;
reply _ Setattr[file, [mode: CARD.LAST, uid: CARD.LAST, gid: CARD.LAST, size: CARD.LAST, atime: SunTimeFromGMT[BasicTime.Now[]], mtime: [CARD.LAST, CARD.LAST]]];
};
Write: PUBLIC PROC [file: SunNFS.FHandle, offset, count: CARD, block: REF TEXT] RETURNS [reply: SunNFS.AttrStat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
bytesMoved: CARD;
fileDID: YggDID.DID;
currentAttrs: SunNFS.AttrStat;
attrs: SunNFS.FAttr;
gotProps: BOOL _  TRUE;
replyStat: SunNFS.Stat;
IF block.maxLength < count THEN RETURN[[status: acces, attributes: nullFAttr]];
block.length _ count;
fileDID _ DIDFromFHandle[file];
IF ~YggDID.ValidateDID[fileDID] THEN RETURN[[status: noent, attributes: nullFAttr]];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
TRUSTED {bytesMoved _ YggNav.SetUninterpretedContents [trans: trans, did: fileDID, setDocType: FALSE, docType: YggRep.noValue, firstByte: offset, byteCount: count, from: PointerFromRefText[block]];};
currentAttrs _ InnerGetattr[file: NIL, fileDID: fileDID, trans: trans ! BadProps => {
gotProps _ FALSE;
replyStat _ stat;
CONTINUE;
};
 ];
IF gotProps THEN {
time: SunNFS.TimeVal;
time _ SunTimeFromGMT[BasicTime.Now[]];
[] _ RemoveTimeFromCache[file];
attrs _ ChangeAttrsUnderTrans [trans: trans, fileDID: DIDFromFHandle[file], currentAttrs: currentAttrs.attributes, changeAttrs: [mode: CARD.LAST, uid: CARD.LAST, gid: CARD.LAST, size: CARD.LAST, atime: time, mtime: time]];
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[[status: ok, attributes: attrs]] ELSE RETURN [[status: acces, attributes: nullFAttr]];
}
 ELSE {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [[replyStat, nullFAttr]];
};
};

Create: PUBLIC PROC [where: SunNFS.DirOpArgs, attributes: SunNFS.SAttr] RETURNS [reply: SunNFS.DirOpRes] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
dirDID: YggDID.DID;
newDID: YggDID.DID;
isDirectory: BOOL _ FALSE;
notADirectory: BOOL _ TRUE;
newName: ROPE;
nameFound: BOOL;
moreThanOneMatch: BOOL;
attrs: SunNFS.FAttr _ nullFAttr;
dirDID _ DIDFromFHandle[where.dir];
IF ~YggDID.ValidateDID[dirDID] THEN RETURN[[status: noent, file: NIL, attributes: nullFAttr]];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: dirDID];
IF ~isDirectory THEN RETURN[[status: notdir, file: NIL, attributes: nullFAttr]];
newName _ Rope.FromRefText[where.name];
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: dirDID, namePattern: newName, version: "h"];
IF nameFound OR moreThanOneMatch THEN RETURN[[status: exist, file: NIL, attributes: nullFAttr]];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
newDID _ YggNav.CreateObject[trans: trans, containerDID: dirDID, makeRoot: FALSE];
YggNav.SetContents[trans: trans, did: newDID, contents: [docType: YggRep.uninterpretedBytes, bits: NIL]];
[notADirectory, nameFound] _ YggNaming.UpdateItem[trans: trans, directoryDid: dirDID, name: newName, version: "h", did: newDID, updateType: insert];
IF notADirectory OR nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN[[status: IF nameFound THEN exist ELSE notdir, file: NIL, attributes: nullFAttr]];
};
attrs _ ChangeAttrsUnderTrans[trans: trans, fileDID: newDID, currentAttrs: nullFAttr, changeAttrs: attributes];
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[[status: ok, file: FHandleFromDID[newDID], attributes: attrs]] ELSE RETURN [[status: acces, file: NIL, attributes: nullFAttr]];
};

Remove: PUBLIC PROC [which: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
status _ RemoveUnderTrans[trans: trans, which: which].status;
IF status # ok THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN;
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[ok] ELSE RETURN [acces];
};

Rename: PUBLIC PROC [from, to: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
rmDID: YggDID.DID;
fromDirDID: YggDID.DID;
toDirDID: YggDID.DID;
rmFileName: ROPE;
moreThanOneMatch: BOOL _ FALSE;
nameFound: BOOL _ FALSE;
isDirectory: BOOL _ FALSE;
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
fromDirDID _ DIDFromFHandle[from.dir];
toDirDID _ DIDFromFHandle[to.dir];
TRUSTED {rmFileName _ LOOPHOLE[from.name];};
IF ~YggDID.ValidateDID[fromDirDID] THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[noent];
};
isDirectory _ YggNaming.HasDirectory[trans: trans, directoryDid: fromDirDID];
IF ~isDirectory THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[notdir];
};
isDirectory _ YggNaming.HasDirectory[trans: trans, directoryDid: toDirDID];
IF ~isDirectory THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[notdir];
};
[status, rmDID] _ RemoveUnderTrans[trans: trans, which: from, destroyIfLastDir: FALSE];
IF status # ok THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN;
};
status _ LinkUnderTrans[trans: trans, toDID: rmDID, as: to];
IF status # ok THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN;
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[ok] ELSE RETURN [acces];
};

Link: PUBLIC PROC [to: SunNFS.FHandle, as: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
status _ LinkUnderTrans[trans: trans, toDID: DIDFromFHandle[to], as: as];
IF status # ok THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN;
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[ok] ELSE RETURN [acces];
};

Symlink: PUBLIC PROC [from: SunNFS.DirOpArgs, to: SunNFS.Path, attributes: SunNFS.SAttr] RETURNS [status: SunNFS.Stat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
attrs: SunNFS.FAttr _ nullFAttr;
dirDID: YggDID.DID;
linkDID: YggDID.DID;
isDirectory: BOOL _ FALSE;
lookUpName: ROPE;
notADirectory: BOOL _ TRUE;
nameFound: BOOL;
bytesMoved: CARD;
moreThanOneMatch: BOOL;
dirDID _ DIDFromFHandle[from.dir];
IF ~YggDID.ValidateDID[dirDID] THEN RETURN[noent];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: dirDID];
IF ~isDirectory THEN RETURN[notdir];
TRUSTED {lookUpName _ LOOPHOLE[from.name];};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: dirDID, namePattern: lookUpName, version: "h"];
IF moreThanOneMatch THEN RETURN[status: noent];
IF nameFound THEN RETURN[status: exist];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
linkDID _ YggNav.CreateObject[trans: trans, containerDID: dirDID, makeRoot: FALSE];
TRUSTED {bytesMoved _ YggNav.SetUninterpretedContents[trans: trans, did: linkDID, setDocType: TRUE, docType: YggRep.symbolicLink, firstByte: 0, byteCount: to.length, from: PointerFromRefText[to]]; };
[notADirectory, nameFound] _ YggNaming.UpdateItem[trans: trans, directoryDid: dirDID, name: lookUpName, version: "h", did: linkDID, updateType: insert];
IF notADirectory OR nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN[IF nameFound THEN exist ELSE notdir];
};
attrs _ ChangeAttrsUnderTrans[trans: trans, fileDID: linkDID, currentAttrs: nullFAttr, changeAttrs: attributes];
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[ok] ELSE RETURN [acces];
};

Mkdir: PUBLIC PROC [where: SunNFS.DirOpArgs, attributes: SunNFS.SAttr] RETURNS [reply: SunNFS.DirOpRes] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
whereDirDID: YggDID.DID;
newDirDID: YggDID.DID;
newDirName: ROPE;
isDirectory: BOOL;
notADirectory: BOOL _ TRUE;
nameFound: BOOL;
moreThanOneMatch: BOOL;
whereDirDID _ DIDFromFHandle[where.dir];
TRUSTED {newDirName _ LOOPHOLE[where.name];};
IF ~YggDID.ValidateDID[whereDirDID] THEN RETURN[[status: noent, file: NIL, attributes: nullFAttr]];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: whereDirDID];
IF ~isDirectory THEN RETURN[[status: notdir, file: NIL, attributes: nullFAttr]];
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: whereDirDID, namePattern: newDirName, version: "h"];
IF moreThanOneMatch THEN RETURN[[status: noent, file: NIL, attributes: nullFAttr]];
IF nameFound THEN RETURN[[status: exist, file: NIL, attributes: nullFAttr]];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
newDirDID _ YggNav.CreateObject[trans: trans, containerDID: whereDirDID, makeRoot: FALSE];
YggNav.SetContents[trans: trans, did: newDirDID, contents: [docType: YggRep.uninterpretedBytes, bits: NIL]];
[notADirectory, nameFound] _ YggNaming.UpdateItem[trans: trans, directoryDid: whereDirDID, name: newDirName, version: "h", did: newDirDID, updateType: insert];
IF notADirectory OR nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [[status: IF nameFound THEN exist ELSE notdir, file: NIL, attributes: nullFAttr]];
};
IF YggNaming.MkDir[trans: trans, did: newDirDID] THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [[status: exist, file: NIL, attributes: nullFAttr]];
};
[notADirectory, nameFound] _ YggNaming.UpdateItem[trans: trans, directoryDid: newDirDID, name: "..", version: "h", did: whereDirDID, updateType: insert];
IF notADirectory OR nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [[status: IF nameFound THEN exist ELSE notdir, file: NIL, attributes: nullFAttr]];
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[[status: ok, file: FHandleFromDID[newDirDID], attributes: nullFAttr]] ELSE RETURN [[status: acces, file: NIL, attributes: nullFAttr]];
};

Rmdir: PUBLIC PROC [which: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
whichDirDID: YggDID.DID;
rmDirDID: YggDID.DID;
rmDirName: ROPE;
isDirectory: BOOL;
nameFound: BOOL;
moreThanOneMatch: BOOL;
nameMatched: ROPE;
versionMatched: ROPE;
whichDirDID _ DIDFromFHandle[which.dir];
TRUSTED {rmDirName _ LOOPHOLE[which.name];};
IF ~YggDID.ValidateDID[whichDirDID] THEN RETURN[noent];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: whichDirDID];
IF ~isDirectory THEN RETURN[notdir];
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch, didFound: rmDirDID, nameMatched: nameMatched, versionMatched: versionMatched] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: whichDirDID, namePattern: rmDirName, version: "h"];
IF moreThanOneMatch THEN RETURN[status: noent];
IF ~nameFound THEN RETURN[status: noent];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: trans, directoryDid: rmDirDID, namePattern: "*", version: "*"];
IF moreThanOneMatch THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN[status: notempty];  --  more than ".." in directory
};
IF ~nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN[status: acces];  --  no ".." in directory
};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: trans, directoryDid: rmDirDID, namePattern: "..", version: "*"];
IF ~nameFound THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN[status: notempty];  --  where's the ".."?  something else is there!
};
IF ~YggNaming.DeleteItem[trans: trans, directoryDid: whichDirDID, name: nameMatched, version: versionMatched] THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [noent];
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[ok] ELSE RETURN [acces];
};

Readdir: PUBLIC PROC [dir: SunNFS.FHandle, cookie: SunNFS.Cookie _ NIL, count: CARD, eachDirEntry: YggNFS.EachDirEntryProc] RETURNS [status: SunNFS.Stat _ ok, eof: BOOL _ FALSE, newCookie: SunNFS.Cookie] ~ {
enum: YggNaming.EnumProc ~ {
accept: BOOL;
continue: BOOL;
[accept, continue] _ eachDirEntry[fileid: did.didLow, filename: name];
IF accept THEN lastAcceptedName _ name;
RETURN[~continue];
};
notADirectory: BOOL _ FALSE;
zeroCookie: BOOL _ FALSE;
cookieOK: BOOL _ TRUE;
refTextNnameToStart: REF TEXT _ NIL;
nameToStart: ROPE _ NIL;
lastAcceptedName: REF TEXT _ NIL;
dirDID: YggDID.DID;
IF cookie = NIL THEN zeroCookie _ TRUE
 ELSE {
FOR i: CARDINAL IN [0..SunNFS.cookieSize) DO
IF cookie[i] = VAL[0] THEN EXIT;
REPEAT FINISHED => zeroCookie _ TRUE;
ENDLOOP;
};
IF ~zeroCookie THEN [cookieOK, refTextNnameToStart] _ LookupCookie[cookie];
nameToStart _ RefText.TrustTextAsRope[refTextNnameToStart];
IF ~cookieOK THEN RETURN[status: stale, eof: TRUE, newCookie: EmptyCookie];
dirDID _ DIDFromFHandle[dir];
IF ~YggDID.ValidateDID[dirDID] THEN RETURN[noent, TRUE, EmptyCookie];
[notADirectory, eof] _ YggNaming.EnumerateEntries[trans: YggEnvironment.nullTransID, directoryDid: dirDID, namePattern: "*", version: "h", nameToStart: nameToStart, nameToStartVersion: "h", proc: enum];
IF notADirectory THEN RETURN[notdir, TRUE, EmptyCookie];
IF ~eof THEN {
newCookie _ CookieForName[lastAcceptedName];
}
 ELSE newCookie _ EmptyCookie;
};


Statfs: PUBLIC PROC [file: SunNFS.FHandle] RETURNS [reply: SunNFS.FSAttrStat] ~ {
blockSize: CARD;
secondaryBlocks: CARD;
secondaryBlocksFree: CARD;
tertiaryBlocks: CARD;
tertiaryBlocksFree: CARD;
[blockSize, secondaryBlocks, secondaryBlocksFree, tertiaryBlocksFree, tertiaryBlocksFree] _ YggFile.ServerInfo[];
RETURN[[status: ok, attributes: [tsize: 4096, bsize: blockSize, blocks: secondaryBlocks + tertiaryBlocks, bfree: secondaryBlocksFree + tertiaryBlocksFree, bavail: secondaryBlocks + tertiaryBlocks]]];
};
InnerGetattr: PROC [file: SunNFS.FHandle, fileDID: YggDID.DID, trans: YggEnvironment.TransID] RETURNS [attrs: SunNFS.AttrStat] ~ {
now: SunNFS.TimeVal;
isDirectory: BOOL _ FALSE;
properties: LIST OF YggRep.Attribute;
valueSet: LIST OF YggRep.TypedPrimitiveElement;
now _ SunTimeFromGMT[BasicTime.Now[]];
IF file # NIL THEN fileDID _ DIDFromFHandle[file];
IF ~YggDID.ValidateDID[fileDID] THEN ERROR BadProps[noent];
attrs.status _ ok;
attrs.attributes _ nullFAttr;
isDirectory _ YggNaming.HasDirectory[trans: trans, directoryDid: fileDID];
IF isDirectory THEN attrs.attributes.type _ dir
 ELSE {
contentsType: YggRep.DocType;
contentsType _ YggNav.GetTypeOfContents[trans: trans, did: fileDID];
IF contentsType = YggRep.symbolicLink THEN attrs.attributes.type _ lnk
 ELSE attrs.attributes.type _ reg;
};
properties _ YggNav.GetProperty[trans: trans, did: fileDID, propertyName: YggFixedNames.NFSPropertyName].property;
IF properties = NIL OR properties.rest # NIL THEN ERROR BadProps[acces];
IF properties.first.value = NIL THEN ERROR BadProps[acces];
IF properties.first.value.rest # NIL THEN ERROR BadProps[acces];
valueSet _ properties.first.value.first.valueSet;
IF valueSet.first.docType # YggRep.int THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, REF INT] THEN ERROR BadProps[acces];
attrs.attributes.mode _ NARROW[valueSet.first.bits, REF INT]^;
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.int THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, REF INT] THEN ERROR BadProps[acces];
attrs.attributes.uid _ NARROW[valueSet.first.bits, REF INT]^;
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.int THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, REF INT] THEN ERROR BadProps[acces];
attrs.attributes.gid _ NARROW[valueSet.first.bits, REF INT]^;
valueSet _ valueSet.rest;
attrs.attributes.size _ YggNav.GetSize[trans: trans, did: fileDID];
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.int THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, REF INT] THEN ERROR BadProps[acces];
attrs.attributes.blocksize _ NARROW[valueSet.first.bits, REF INT]^;
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.int THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, REF INT] THEN ERROR BadProps[acces];
attrs.attributes.blocks _ NARROW[valueSet.first.bits, REF INT]^;
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.date THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, YggRep.AccurateGMT] THEN ERROR BadProps[acces];
attrs.attributes.atime _ SunTimeFromAccurateGMT[NARROW[valueSet.first.bits, YggRep.AccurateGMT]];
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.date THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, YggRep.AccurateGMT] THEN ERROR BadProps[acces];
attrs.attributes.mtime _ SunTimeFromAccurateGMT[NARROW[valueSet.first.bits, YggRep.AccurateGMT]];
valueSet _ valueSet.rest;
IF valueSet = NIL THEN ERROR BadProps[acces];
IF valueSet.first.docType # YggRep.date THEN ERROR BadProps[acces];
IF ~ISTYPE[valueSet.first.bits, YggRep.AccurateGMT] THEN ERROR BadProps[acces];
attrs.attributes.ctime _ SunTimeFromAccurateGMT[NARROW[valueSet.first.bits, YggRep.AccurateGMT]];

IF valueSet.rest # NIL THEN ERROR BadProps[acces];
};

ChangeAttrsUnderTrans: PROC [trans: YggEnvironment.TransID, fileDID: YggDID.DID, currentAttrs: SunNFS.FAttr, changeAttrs: SunNFS.SAttr] RETURNS [newAttributes: SunNFS.FAttr] ~ {
newAttributes _ currentAttrs;
IF changeAttrs.mode # CARD.LAST THEN newAttributes.mode _ changeAttrs.mode;
IF changeAttrs.uid # CARD.LAST THEN newAttributes.uid _ changeAttrs.uid;
IF changeAttrs.gid # CARD.LAST THEN newAttributes.gid _ changeAttrs.gid;
IF changeAttrs.size # CARD.LAST THEN {
YggNav.SetSize[trans: trans, did: fileDID, size: changeAttrs.size];
newAttributes.size _ changeAttrs.size;
};
IF changeAttrs.atime # [CARD.LAST, CARD.LAST] THEN newAttributes.atime _ changeAttrs.atime;
IF changeAttrs.mtime # [CARD.LAST, CARD.LAST] THEN newAttributes.mtime _ changeAttrs.mtime;
InnerSetattr[file: NIL, fileDID: fileDID, trans: trans, attrs: newAttributes];
};
InnerSetattr: PROC [file: SunNFS.FHandle, fileDID: YggDID.DID, trans: YggEnvironment.TransID, attrs: SunNFS.FAttr] ~ {
valueSet: LIST OF YggRep.TypedPrimitiveElement _ NIL;
IF file # NIL THEN fileDID _ DIDFromFHandle[file];
IF ~YggDID.ValidateDID[fileDID] THEN ERROR BadProps[noent];
valueSet _ LIST[
[YggRep.int, NEW[INT _ attrs.mode]],  -- mode
[YggRep.int, NEW[INT _ attrs.uid]],  -- uid
[YggRep.int, NEW[INT _ attrs.gid]],  -- gid
[YggRep.int, NEW[INT _ attrs.blocksize]],  -- blocksize
[YggRep.int, NEW[INT _ attrs.blocks]],  -- blocks
[YggRep.date, AccurateGMTFromSunTime[attrs.atime]],  -- atime
[YggRep.date, AccurateGMTFromSunTime[attrs.mtime]],  -- mtime
[YggRep.date, AccurateGMTFromSunTime[attrs.ctime]]];  -- ctime
YggNav.SetProperty[trans: trans, did: fileDID, propertyName: YggFixedNames.NFSPropertyName, property: [attributeName: YggFixedNames.NFSPropertyName, ordered: FALSE, value: LIST[[NIL, valueSet]]], appendProperty: FALSE];
};


StickInTimeCache: ENTRY PROC [file: SunNFS.FHandle, atime: SunNFS.TimeVal, mtime: SunNFS.TimeVal] ~ {
bucket: INT;
fileDid: DID;
fileDid _ DIDFromFHandle[file];
bucket _ PBasics.BITAND[SizeOfTimeCache, fileDid.didLow];
FOR tcl: LIST OF TimeCacheItem _ TimeCache[bucket], tcl.rest UNTIL tcl = NIL DO
IF tcl.first.did.didLow = fileDid.didLow AND tcl.first.did.didHigh = fileDid.didHigh THEN {
tcl.first.atime _ atime;
tcl.first.mtime _ mtime;
EXIT;
};
REPEAT FINISHED => {
TimeCache[bucket] _ CONS [[fileDid, atime, mtime, BasicTime.Now[]], TimeCache[bucket]];
};
ENDLOOP;
};

RemoveTimeFromCache: ENTRY PROC [file: SunNFS.FHandle] RETURNS [ found: BOOL _ FALSE, atime: SunNFS.TimeVal, mtime: SunNFS.TimeVal] ~ {
bucket: INT;
fileDid: DID;
prev: LIST OF TimeCacheItem _ NIL;
fileDid _ DIDFromFHandle[file];
bucket _ PBasics.BITAND[SizeOfTimeCache, fileDid.didLow];
FOR tcl: LIST OF TimeCacheItem _ TimeCache[bucket], tcl.rest UNTIL tcl = NIL DO
IF tcl.first.did.didLow = fileDid.didLow AND tcl.first.did.didHigh = fileDid.didHigh THEN {
IF prev = NIL THEN TimeCache[bucket] _ tcl.rest
 ELSE prev.rest _ tcl.rest;
RETURN[TRUE, tcl.first.atime, tcl.first.mtime];
};
prev _ tcl;
ENDLOOP;
RETURN[FALSE, [CARD.LAST, CARD.LAST], [CARD.LAST, CARD.LAST]];
};


PruneTimeCache: PROC ~ {
ticksToWait: Process.Ticks;
ticksToWait _ Process.MsecToTicks[1713];
DO
now: BasicTime.GMT _ BasicTime.Now[];
FOR hval: INT IN [0..SizeOfTimeCache) DO
pruneInternal: ENTRY PROC ~ {
prev: LIST OF TimeCacheItem _ NIL;
FOR tcl: LIST OF TimeCacheItem _ TimeCache[hval], tcl.rest UNTIL tcl = NIL DO
IF BasicTime.Period[from: tcl.first.itemCreateTime, to: now] > 30 THEN {
trans: YggEnvironment.TransID;
gotProps: BOOL _  TRUE;
replyStat: SunNFS.Stat;
currentAttrs: SunNFS.AttrStat;
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
currentAttrs _ InnerGetattr[file: NIL, fileDID: tcl.first.did, trans: YggEnvironment.nullTransID ! BadProps => {
gotProps _ FALSE;
replyStat _ stat;
CONTINUE;
};
 ];
IF gotProps THEN {
[] _ ChangeAttrsUnderTrans [trans: trans, fileDID: tcl.first.did, currentAttrs: currentAttrs.attributes, changeAttrs: [CARD.LAST, CARD.LAST, CARD.LAST, CARD.LAST, tcl.first.atime, tcl.first.mtime]];
[] _ YggTransaction.Finish[trans, commit];
}
 ELSE {
[] _ YggTransaction.Finish[trans, abort];
};
IF prev = NIL THEN TimeCache[hval] _ tcl.rest
 ELSE prev.rest _ tcl.rest;
LOOP;
};
prev _ tcl;
ENDLOOP;
};
pruneInternal[];
Process.Pause[5];
ENDLOOP;
Process.Pause[ticksToWait];
ENDLOOP;
};

RemoveUnderTrans: PROC [trans: YggEnvironment.TransID, which: SunNFS.DirOpArgs, destroyIfLastDir: BOOL _ TRUE] RETURNS [status: SunNFS.Stat, rmDID: YggDID.DID] ~ {
whichDirDID: YggDID.DID;
rmFileName: ROPE;
isDirectory: BOOL;
nameFound: BOOL;
moreThanOneMatch: BOOL;
nameMatched: ROPE;
versionMatched: ROPE;
success: BOOL _ TRUE;
parentsDids: LIST OF YggDID.DID;
whichDirDID _ DIDFromFHandle[which.dir];
TRUSTED {rmFileName _ LOOPHOLE[which.name];};
IF ~YggDID.ValidateDID[whichDirDID] THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[noent, YggEnvironment.nullDID];
};
isDirectory _ YggNaming.HasDirectory[trans: trans, directoryDid: whichDirDID];
IF ~isDirectory THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[notdir, YggEnvironment.nullDID];
};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch, didFound: rmDID, nameMatched: nameMatched, versionMatched: versionMatched] _ YggNaming.Lookup[trans: trans, directoryDid: whichDirDID, namePattern: rmFileName, version: "h"];
IF moreThanOneMatch OR ~nameFound THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[status: noent, rmDID: YggEnvironment.nullDID];
};
IF ~YggNaming.DeleteItem[trans: trans, directoryDid: whichDirDID, name: nameMatched, version: versionMatched] THEN {
IF ErrorOnMistake THEN ERROR;
RETURN [noent, YggEnvironment.nullDID];
};
[dids: parentsDids, success: success] _ YggNav.GetParents[trans: trans, did: rmDID, dontWait: FALSE];
FOR didList: LIST OF YggDID.DID _ parentsDids, didList.rest UNTIL didList = NIL DO
IF YggDID.EqualDIDs[didList.first, whichDirDID] THEN EXIT;
REPEAT FINISHED => {
IF ErrorOnMistake THEN ERROR;
RETURN [noent, YggEnvironment.nullDID];
};
ENDLOOP;
success _ YggNav.RemoveFromContainer[trans: trans, did: rmDID, containerDID: whichDirDID];
IF ~success THEN {
IF ErrorOnMistake THEN ERROR;
RETURN [noent, YggEnvironment.nullDID];
};
IF parentsDids.rest = NIL AND destroyIfLastDir THEN {
YggNav.RemoveObject[trans: trans, did: rmDID];
[] _ RemoveTimeFromCache[file: FHandleFromDID[rmDID]];
};
RETURN [ok, rmDID];
};

LinkUnderTrans: PROC [trans: YggEnvironment.TransID, toDID: YggDID.DID, as: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
dirDID: YggDID.DID;
isDirectory: BOOL _ FALSE;
notADirectory: BOOL _ FALSE;
lookUpName: ROPE;
nameFound: BOOL;
moreThanOneMatch: BOOL;
dirDID _ DIDFromFHandle[as.dir];
IF ~YggDID.ValidateDID[dirDID] THEN RETURN[noent];
isDirectory _ YggNaming.HasDirectory[trans: YggEnvironment.nullTransID, directoryDid: dirDID];
IF ~isDirectory THEN RETURN[notdir];
TRUSTED {lookUpName _ LOOPHOLE[as.name];};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch] _ YggNaming.Lookup[trans: YggEnvironment.nullTransID, directoryDid: dirDID, namePattern: lookUpName, version: "h"];
IF moreThanOneMatch THEN RETURN[status: noent];
IF nameFound THEN RETURN[status: exist];
[notADirectory, nameFound] _ YggNaming.UpdateItem[trans: trans, directoryDid: dirDID, name: lookUpName, version: "h", did: toDID, updateType: insert];
YggNav.AddToContainer[trans: trans, did: toDID, containerDID: dirDID];
IF notADirectory OR nameFound THEN {
RETURN [IF nameFound THEN exist ELSE notdir];
};
RETURN[ok];
};

PointerFromRefText: UNSAFE PROC [block: REF READONLY TEXT] RETURNS [LONG POINTER] ~ TRUSTED INLINE {
RETURN[ LOOPHOLE[block, LONG POINTER] + UNITS[TEXT[0]] ] };

DIDFromFHandle: PROC [file: SunNFS.FHandle] RETURNS [did: YggDID.DID] ~ TRUSTED {
did _ YggDID.VolatilizeDID[PointerFromRefText[file]];
};

FHandleFromDID: PROC [did: YggDID.DID] RETURNS [file: SunNFS.FHandle] ~ TRUSTED {
file _ NEW[TEXT[SunNFS.fhSize]];
YggDID.StabilizeDID[did, PointerFromRefText[file]];
};

GMTFromSunTime: PROC [sunTime: SunNFS.TimeVal] RETURNS [gmt: BasicTime.GMT] ~ {
RETURN [BasicTime.Update[sunEpoch, INT[sunTime.seconds]]];
};

AccurateGMTFromSunTime: PROC [sunTime: SunNFS.TimeVal] RETURNS [agmt: YggRep.AccurateGMT] ~ {
agmt _ NEW[YggRep.AccurateGMTRep];
agmt.gmt _ BasicTime.Update[sunEpoch, INT[sunTime.seconds]];
agmt.usecs _ sunTime.useconds;
};

SunTimeFromGMT: PROC [gmt: BasicTime.GMT] RETURNS [sunTime: SunNFS.TimeVal] ~ {
RETURN [ [seconds~BasicTime.Period[from~sunEpoch, to~gmt], useconds~0] ];
};

SunTimeFromAccurateGMT: PROC [agmt: YggRep.AccurateGMT] RETURNS [sunTime: SunNFS.TimeVal] ~ {
RETURN[ [seconds~BasicTime.Period[from~sunEpoch, to~agmt.gmt], useconds~agmt.usecs] ];
};

CompareSunTimes: PROC [t1, t2: SunNFS.TimeVal] RETURNS [Basics.Comparison] ~ {
RETURN [SELECT t1.seconds FROM
< t2.seconds => less,
> t2.seconds => greater,
ENDCASE => Basics.CompareCard[t1.useconds, t2.useconds]];
};
LookupCookie: ENTRY PROC [cookie: SunNFS.Cookie] RETURNS [cookieOK: BOOL _ FALSE, nameToStart: REF TEXT _ NIL] ~ {
FOR loc: LIST OF CookieJarItem _ CookieJar, loc.rest UNTIL loc = NIL DO
IF loc.first.cookie = cookie THEN RETURN[cookieOK: TRUE, nameToStart: loc.first.nameToStart];
FOR i: CARDINAL IN [0..SunNFS.cookieSize) DO
IF loc.first.cookie[i] # cookie[i] THEN EXIT;
REPEAT FINISHED => RETURN[cookieOK: TRUE, nameToStart: loc.first.nameToStart];
ENDLOOP;
ENDLOOP;
};

CookieForName: ENTRY PROC [lastAcceptedName: REF TEXT] RETURNS [cookie: SunNFS.Cookie _ EmptyCookie] ~ {
cookie _ NextCookie[];
CookieJar _ CONS[[BasicTime.Now[], cookie, lastAcceptedName], CookieJar];
};

NextCookie: INTERNAL PROC RETURNS [cookie: SunNFS.Cookie _ EmptyCookie] ~ {
FOR i: CARDINAL IN [0..SunNFS.cookieSize) DO
IF LastCookie[i].ORD = CHAR.LAST.ORD THEN {LastCookie[i] _ VAL[0]; LOOP};
LastCookie[i] _ VAL[LastCookie[i].ORD.SUCC];
EXIT;
REPEAT FINISHED => ERROR;
ENDLOOP;
cookie _ LastCookie;
RETURN;
};

CookieMonster: PROC ~ {
ticksToWait: Process.Ticks;
ticksToWait _ Process.SecondsToTicks[5];
DO
innerTrim: ENTRY PROC = {
now: BasicTime.GMT;
prevCookie: LIST OF CookieJarItem _ NIL;
now _ BasicTime.Now[];
FOR loc: LIST OF CookieJarItem _ CookieJar, loc.rest UNTIL loc = NIL DO
IF BasicTime.Period[from: loc.first.timeIssued, to: now] > 6 THEN {
IF prevCookie = NIL THEN CookieJar _ loc.rest
 ELSE prevCookie.rest _ loc.rest;
LOOP;
};
prevCookie _ loc;
ENDLOOP;
};
Process.Pause[ticksToWait];
innerTrim[];
ENDLOOP;
};
MakeFileSystem: PUBLIC ENTRY PROC RETURNS [alreadyExists: BOOL _ FALSE] ~ {
ENABLE UNWIND => NULL;
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
IF YggDID.ValidateDID[YggDIDPrivate.DIDForNFSRoot] THEN RETURN [TRUE];
trans _ YggTransaction.CreateTrans[YggEnvironment.nullTransID];
IF ~YggDIDMap.CreateExplicitDID[trans, YggDIDPrivate.DIDForNFSRoot] THEN RETURN [TRUE];
YggNav.SetContents[trans: trans, did: YggDIDPrivate.DIDForNFSRoot, contents: [docType: YggRep.uninterpretedBytes, bits: YggRep.SetSizeOfBits[NIL, 0].newBits]];
IF YggNaming.MkDir[trans: trans, did: YggDIDPrivate.DIDForNFSRoot] THEN {
outcome _ YggTransaction.Finish[trans, abort];
RETURN [TRUE];
};
outcome _ YggTransaction.Finish[trans, commit];
IF outcome = commit THEN RETURN[FALSE] ELSE RETURN [TRUE];
};
Mnt: PUBLIC PROC [directory: SunMount.Path] RETURNS [reply: SunMount.FHStatus] ~ {
badStat: CARD ;
badStat _ ORD[SunNFS.Stat.notdir];
SELECT RefText.Length[directory] FROM
= 0 => {};
= 1 => {
IF RefText.Fetch[directory, 0] # '/ THEN RETURN[[status: ORD[SunNFS.Stat.notdir], directory: NIL]]
};
>= 2 => {
RETURN[[status: ORD[SunNFS.Stat.notdir], directory: NIL]]
};
ENDCASE => ERROR;
RETURN[[status: 0, directory: FHandleFromDID[YggDIDPrivate.DIDForNFSRoot]]];
};

Dump: PUBLIC PROC [eachMount: SunMount.EachMountProc] ~ {
};

Umnt: PUBLIC PROC [directory: SunMount.Path] ~ {
};

Umntall: PUBLIC PROC [] ~ {
};

Export: PUBLIC PROC [eachExport: SunMount.EachExportProc, eachGroup: SunMount.EachGroupProc] ~ {
};

TRUSTED {Process.Detach[FORK CookieMonster[] ];};
TRUSTED {Process.Detach[FORK PruneTimeCache[] ];};

EmptyCookie _ RefText.New[SunNFS.cookieSize];
LastCookie _ RefText.New[SunNFS.cookieSize];
FOR i: CARDINAL IN [0..SunNFS.cookieSize) DO
EmptyCookie[i] _ VAL[0];
LastCookie[i] _ VAL[0];
ENDLOOP;
LastCookie[0] _ VAL[100];  -- should this be stable and count up forever?

END.
�����YggNFSImpl.mesa
Copyright Ó 1988, 1989 by Xerox Corporation.  All rights reserved.
Bob Hagmann March 21, 1989 10:13:11 am PST


Top level NFS interface for Yggdrasil.

To do:
1) Make sure the errors are right
2) Make sure that file delete has the correct semantics (remove actually can destroy data; is that OK in hypertext land?)
3) Check the arguments (file names OK, permissions)

Types, variables, and constants
Exported NFS procedures
Optimize this procedure by checking for last access time is the change.  If so, remember it in a cache without really doing the update.  If request if for non-last access time, pull and delete last access time from cache (if it's there).  Sweep cache periodically and do "server" transactions to update the objects.

TRUSTED {newName _ RefText.TrustTextAsRope[where.name];};
[nameFound: nameFound, moreThanOneMatch: moreThanOneMatch, didFound: rmDID] _ YggNaming.Lookup[trans: trans, directoryDid: fromDirDID, namePattern: rmFileName, version: "h"];
IF moreThanOneMatch OR ~nameFound THEN {
IF ErrorOnMistake THEN ERROR;
RETURN[status: noent];
};
EnumProc: TYPE = PROC [name: ROPE, version: Version, did: YggDID.DID] RETURNS [stop: BOOL];
Utilities
Gets the attributes for the file specified either by FHandle or DID (FHandle used if not NIL).  Raises BadProps if anything goes wrong.
mode
uid
gid
size
blocksize
blocks
atime
mtime
ctime

Sets the attributes (except for size -- do that by YggNav.SetSize!) for the file specified either by FHandle or DID (FHandle used if not NIL).  Raises BadProps if anything goes wrong.
IF outcome # commit THEN weShouldDoSomethingHere;
There is a problem, but there is no way to report it.
Cookie stuff
Toss all cookies older than 6 seconds
Exported Administrative procedures
Exported Mount procedures
Initialization
Ê'‰��˜�codešœ™KšœB™BKšœ*™*—K™�K™�K™&K™�™K™!K™yK™3—K™�šÏk	˜	Kšœ˜Kšœ
˜
Kšœ˜Kšœœ5˜BK˜Kšœ˜Kšœ	˜	Kšœ˜Kšœœœ7˜HKšœœ˜,Kšœ
œ˜$Kšœ˜Kšœ˜Kšœ˜Kšœ
˜
Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜�KšÑbln
œœ˜Kšœ˜˜ŸKšœ˜šœ˜K˜�—head™Icode0šœœœÏc˜8M˜�Mšœœœ˜K˜�Kšœœœœ˜Kšœœœ˜+K˜�Kšœ
œœœœœ˜BK˜�KšÏnœœœœ˜2K˜�Kšœ9˜9K˜�Kšœ˜K˜�Kšœn˜nK˜�šœœ˜)K˜MK˜K˜�—˜K˜Kšœ˜Kšœ
˜K˜—K˜'Kšœ˜K˜�Kšœ0˜0Kš
œœœœœœœ˜Jšœœœ˜Kšœœ˜	Kšœ˜Kšœ˜K˜K˜—K˜�—™š œœœœ˜PKšœ
œœ˜Kšœ˜šœs˜sKšœœ˜Kšœ˜Kšœ˜	Kšœ˜—Kšœ˜šœœ˜Kšœ˜K˜—K˜—K˜�š œœœ2œ˜jKšœ
œœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K™»šœœœœœœœœœœœœœŸ˜ššœz˜zKšœœ˜Kšœ˜Kšœ˜	Kšœ˜K˜K˜�—šœ
œ˜KšœœœœœœœœœœœœŸ˜‹Kšœœœœœœ2˜cKšœœœœœœ2˜cKšœU˜UKšœ˜K˜—šœœ˜Kšœ˜K˜—K˜—šœœ˜Kšœ?˜?šœz˜zKšœœ˜Kšœ˜Kšœ˜	Kšœ˜—Kšœ˜šœ
œ˜Kšœœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ5˜5šœœ˜Kšœœœœœœœœœœœ˜pKšœœœœœœœœœœœ˜pK˜—Kšœ—˜—Kšœ/˜/Kšœœœ+˜KK˜—šœœ˜Kšœ˜Kšœ.˜.K˜—K˜—K˜—K˜�š œœœœ˜RKšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ#˜#Kšœœœœ˜^KšœRœœœ˜”Kšœœ˜-KšœÂ˜ÂKš
œœœœœ˜bšœ
˜Kšœ˜KšœœEœ˜—Kšœœ˜:K˜—K˜�š œœœœ-˜aKšœœ˜Kšœ
œœ˜Kšœ˜Kšœœœœ˜8Kšœ_˜_Kšœ
œœœ˜&šœœ˜Kšœ˜KšœY˜Yšœ$œ˜,Kšœ	œœ˜Kšœœ˜Kšœ
œ˜KšœN˜NKšœœœ˜KšœÀœœ˜ÛJšœ	œœœ˜#Kšœ˜Kšœ˜K˜—Kšœœœœ˜Kšœ˜—K˜—K˜�š œœœ'œ	œœœ˜sKšœœ˜Kšœœ˜Kšœœœ)˜OKšœ˜Kšœ˜Kšœœœ)˜TKšœ§˜®Kšœ˜Kšœœœœœœœœœ2œœœœ˜¡K˜—K™�š œœœ'œ	œœœ˜tKšœ˜Kšœ ˜ Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ
œœ˜Kšœ˜Kšœœœ)˜OKšœ˜Kšœ˜Kšœœœ)˜TKšœ?˜?KšœXœc˜Çšœ"œ0˜UKšœœ˜Kšœ˜Kšœ˜	Kšœ˜—Kšœ˜šœ
œ˜Kšœ˜Kšœ'˜'Kšœ˜Kšœ‡œœœœœœœœ˜ÞKšœ/˜/Kš
œœœ"œœ*˜vK˜—šœœ˜Kšœ.˜.Kšœ˜ K˜—K˜—K˜�š œœœ5œ˜lKšœ˜Kšœ ˜ Kšœœ˜Kšœœ˜Kšœ
œœ˜Kšœœœ˜Kšœ	œ˜Kšœœ˜Kšœœ˜Kšœ ˜ Kšœ#˜#Kšœœœœ˜^Kšœ^˜^Kšœœœœ˜PKšœ2™9Kšœ'˜'Kšœ«˜«Kšœœœœ˜`Kšœ?˜?KšœKœ˜RJšœi˜iKšœ”˜”šœœœ˜$Kšœ.˜.Kš
œ
œœœœ˜XK˜—Kšœo˜oKšœ/˜/Kšœœœ@œœœ˜ŸKšœ˜—K˜�š œœœœ˜OKšœ˜Kšœ ˜ Kšœ?˜?Kšœ=˜=šœ
œ˜Kšœ.˜.Kšœ˜K˜—Kšœ/˜/Kš
œœœœœ	˜8K˜—K˜�š œœœœ˜RKšœ˜Kšœ ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ?˜?Kšœ&˜&Kšœ"˜"Kšœœ˜,šœ!œ˜)Kšœœœ˜Kšœ˜K˜—KšœM˜Mšœœ˜Kšœœœ˜Kšœ	˜K˜—KšœK˜Kšœœ˜Kšœœœ˜Kšœ	˜K˜—Kšœ®™®šœœœ™(Kšœœœ™Kšœ™K™—KšœPœ˜Wšœ
œ˜Kšœ.˜.Kšœ˜K˜—Kšœ<˜<šœ
œ˜Kšœ.˜.Kšœ˜K˜—Kšœ/˜/Kš
œœœœœ	˜8K˜—K˜�š œœœ,œ˜^Kšœ˜Kšœ ˜ Kšœ?˜?KšœI˜Išœ
œ˜Kšœ.˜.Kšœ˜K˜—Kšœ/˜/Kš
œœœœœ	˜8K˜—K˜�š œœœEœ˜zKšœ˜Kšœ ˜ Kšœ ˜ Kšœœ˜Kšœœ˜Kšœ
œœ˜Kšœœ˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ"˜"Kšœœœ˜2Kšœ^˜^Kšœœœ	˜$Kšœœ˜,Kšœ®˜®Kšœœœ˜/Kšœœœ˜(Kšœ?˜?KšœLœ˜SKšœWœe˜ÇKšœqœ$˜˜šœœœ˜$Kšœ.˜.Kšœœœœ	˜,K˜—Kšœp˜pKšœ/˜/Kš
œœœœœ	˜8K˜—K˜�š œœœ5œ˜kKšœ˜Kšœ ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœœœ˜Kšœ˜Kšœ˜Kšœ(˜(Kšœœ˜-Kšœ"œœœ˜cKšœc˜cKšœœœœ˜PKšœ³˜³Kšœœœœ˜SKšœœœœ˜LKšœ?˜?KšœSœ˜ZJšœl˜lKšœvœ&˜Ÿšœœ˜$Kšœ.˜.Kšœ6œ˜YK˜—šœ1œ˜7Kšœ.˜.Kšœœ˜;Kšœ˜—Kšœnœ(˜™šœœ˜$Kšœ.˜.Kšœ6œ˜YK˜—Kšœ/˜/Kšœœœ,œœœœ˜¦K˜—K˜�š œœœœ˜NKšœ˜Kšœ ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœ
œ˜Kšœœ˜Kšœœ˜Kšœ
œ˜Kšœœ˜Kšœ(˜(Kšœœ˜,Kšœ"œœ˜7Kšœc˜cKšœœœ	˜$Kšœ€˜€Kšœœœ˜/Kšœœœ˜)Kšœ?˜?Kšœ”˜”šœœ˜Kšœ.˜.KšœŸ˜:K˜—šœœ˜Kšœ.˜.KšœŸ˜0K˜—Kšœ•˜•šœœ˜Kšœ.˜.KšœŸ/˜JK˜—šœlœ˜tKšœ.˜.Kšœ	˜K˜—Kšœ/˜/Kš
œœœœœ	˜8K˜—K˜�š œœœ/œ	œ)œ!œœ˜Ïšœ˜Kš
œ
œœœ œœœ™[Kšœœ˜
Kšœ
œ˜KšœF˜FKšœœ˜'Kšœ˜Kšœ˜—Kšœœœ˜Kšœœœ˜Kšœ
œœ˜Kšœœœœ˜$Kšœ
œœ˜Kšœœœœ˜!Kšœœ˜Kšœ
œœ˜&šœœ˜šœœœ˜,Kšœ
œœœ˜ Kšœœœ˜%Kšœ˜—K˜—Kšœ
œ8˜KKšœ;˜;Kšœœœœ˜KKšœ˜Kšœœœœ˜EKšœÊ˜ÊKšœœœ	œ˜8šœœ˜Kšœ,˜,K˜—Kšœœ˜K˜—K˜�K˜�š œœœœ˜QKšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœq˜qKšœÁ˜ÇK˜——™	š œœ(œ!œ˜‚Kšœ@œœ+™‡Kšœ˜Kšœ
œœ˜Kšœœœ˜%Kšœ
œœ˜/Kšœ&˜&Kšœœœ ˜2Kšœœœ˜;Kšœ˜Kšœ˜KšœJ˜JKšœ
œ˜/šœœ˜Kšœ˜KšœD˜DKšœ$œ˜FKšœœ˜"Kšœ˜—Kšœr˜rKšœœœœœœ˜HKšœœœœ˜;Kšœœœœ˜@šœ1˜1K™—Kšœ%œœ˜BKšœœœœœœ˜DKšœœœœ˜>šœ˜K™—Kšœœœœ˜-Kšœ%œœ˜BKšœœœœœœ˜DKšœœœœ˜=šœ˜K™—Kšœœœœ˜-Kšœ%œœ˜BKšœœœœœœ˜DKšœœœœ˜=šœ˜K™—šœC˜CK™	—Kšœœœœ˜-Kšœ%œœ˜BKšœœœœœœ˜DKšœœœœ˜Cšœ˜K™—Kšœœœœ˜-Kšœ%œœ˜BKšœœœœœœ˜DKšœœœœ˜@šœ˜K™—Kšœœœœ˜-Kšœ&œœ˜CKšœœ*œœ˜OKšœ0œ+˜ašœ˜K™—Kšœœœœ˜-Kšœ&œœ˜CKšœœ*œœ˜OKšœ0œ+˜ašœ˜K™—Kšœœœœ˜-Kšœ&œœ˜CKšœœ*œœ˜OKšœ0œ+˜aK˜�Kšœœœœ˜2K˜K˜�—š œœ1œb˜±Kšœ˜Kšœœœœ'˜KKšœœœœ%˜HKšœœœœ%˜Hšœœœœ˜&KšœC˜CKšœ&˜&K˜—Kšœœœœœœ)˜[Kšœœœœœœ)˜[KšœN˜NK˜K™�—š œœ(œ9˜vKšœ%Ÿ’™·Kšœ
œœ œ˜5Kšœœœ ˜2Kšœœœ˜;šœœ˜Kšœ
œœŸ˜-Kšœ
œœŸ˜+Kšœ
œœŸ˜+Kšœ
œœŸ˜7Kšœ
œœŸ	˜1Kšœ5Ÿ˜=Kšœ5Ÿ˜=Kšœ6Ÿ˜>—Kš	œžœ	œœœ˜ÛK˜K˜�K˜�—š œ
œI˜eKšœœ˜Kšœ	œ˜
Kšœ˜Kšœœ"˜9šœœœ-œœ˜Ošœ'œ)œ˜[Kšœ˜Kšœ˜Kšœ˜K˜—šœœ˜Kšœœ?˜WK˜—Kšœ˜—K˜K˜�—š œœœœ
œœ3˜‡Kšœœ˜Kšœ	œ˜
Kšœœœœ˜"Kšœ˜Kšœœ"˜9šœœœ-œœ˜Ošœ'œ)œ˜[Kšœœœ˜/Kšœœ˜Kšœœ$˜/K˜—Kšœ˜Kšœ˜—Kšœœœœœœœœœœ˜>K˜—˜�K˜�—š œœ˜Icode2šœ˜Nšœ(˜(š˜Kšœœ˜%šœœœ˜(šœœœ˜Kšœœœœ˜"šœœœ+œœ˜Mšœ@œ˜HKšœ˜Kšœ
œœ˜Kšœ˜Kšœ˜Kšœ?˜?šœp˜pKšœœ˜Kšœ˜Kšœ˜	Kšœ˜—Kšœ˜šœ
œ˜Kš	œwœœœœ*˜ÆKšœ*˜*Kšœœ™1K˜—šœœ˜K™5Kšœ)˜)K˜—Kšœœœ˜-Kšœœ˜Jšœ˜J˜—Kšœ˜Kšœ˜—K˜—K˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜K˜�—š œœLœœœ%œ˜£Kšœœ˜Kšœœ˜Kšœ
œ˜Kšœœ˜Kšœœ˜Kšœ
œ˜Kšœœ˜Kšœ	œœ˜Kšœ
œœœ˜ Kšœ(˜(Kšœœ˜-šœ"œ˜*Kšœœœ˜Kšœ ˜&K˜—KšœN˜Nšœœ˜Kšœœœ˜Kšœ!˜'K˜—Kšœé˜éšœœœ˜(Kšœœœ˜Kšœ/˜5K˜—šœlœ˜tKšœœœ˜Kšœ!˜'K˜—Kšœ^œ˜eš
œ
œœœœœ˜RKšœ.œœ˜:šœœ˜Kšœœœ˜Kšœ!˜'K˜—Kšœ˜—KšœZ˜Zšœ
œ˜Kšœœœ˜Kšœ!˜'K˜—šœœœœ˜5Kšœ.˜.Kšœ6˜6K˜—Kšœ
˜Kšœ˜K˜�—š œœ/œœ˜Kšœœ˜Kšœ
œœ˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ ˜ Kšœœœ˜2Kšœ^˜^Kšœœœ	˜$Kšœœ˜*Kšœ®˜®Kšœœœ˜/Kšœœœ˜(Kšœ–˜–KšœF˜Fšœœœ˜$Kšœœœœ	˜-K˜—Kšœ˜K˜K˜�—š œœœ	œœœœœœœœ˜dKšœœœœœœ	˜;K˜�—š
 œœœœœ˜QKšœ5˜5K˜K˜�—š
 œœœœœ˜QKšœ ˜ Kšœ3˜3K˜K˜�—š œœœœ˜OKšœœ˜:K˜K˜�—š œœœ˜]Kšœœ˜"Kšœ&œ˜<Kšœ˜K˜K˜�—š œœœœ˜OKšœC˜IK˜K˜�—š œœœ˜]KšœP˜VK˜K˜�—š œœœ˜Nšœœ˜Kšœ˜Kšœ˜Kšœ2˜9—K˜——™š œœœœœœœœœ˜ršœœœ%œœ˜GNšœœœœ&˜]šœœœ˜,Kšœ!œœ˜-Nšœœœœ&˜NNšœ˜—Nšœ˜—K˜K˜�—š
 
œ
œœœœ*˜hKšœ˜KšœI˜IK˜K˜�—š 
œœœ*˜Kšœœœ˜,Kšœœœœœœœœ˜INšœœœœ˜,Nšœ˜Nšœœœ˜Nšœ˜—Kšœ˜Kšœ˜K˜K˜�—š 
œœ˜N™%Nšœ˜Nšœ(˜(š˜šÏb	œœœ˜Nšœœ˜Nšœœœœ˜(N˜šœœœ%œœ˜Gšœ;œ˜CNšœœœ˜-Nšœœ˜!Nšœ˜N˜—Nšœ˜Nšœ˜—N˜—Nšœ˜Nšœ˜Kšœ˜—K˜——™"š œœœœœœœ˜KKšœœœ˜Kšœ˜Kšœ ˜ Kšœ1œœœ˜FKšœ?˜?KšœBœœœ˜WJšœŸ˜ŸšœCœ˜IKšœ.˜.Kšœœ˜Kšœ˜—Kšœ/˜/Kšœœœœœœœ˜:K˜——™š œœœœ˜RK˜Kšœ
œ˜"šœ˜%K˜
˜Kš
œ"œœ
œ!œ˜bK˜—˜	Kšœ
œ!œ˜9K˜—Kšœœ˜—KšœF˜LK˜—K˜�š œœ(˜9K˜—K˜�š œœ˜0K˜—K˜�š œœ˜K˜—K˜�š œœM˜`K˜—K˜�—™Kšœœ˜1Kšœœ˜2K˜�Kšœ-˜-Kšœ,˜,šœœœ˜,Kšœœ˜Kšœœ˜Kšœ˜—KšœœŸ.˜I—K˜�Kšœ˜—�…—����ŽF��¼×��