YggNFSImpl.mesa
Copyright Ó 1988, 1989 by Xerox Corporation. All rights reserved.
Bob Hagmann March 20, 1989 5:17:14 pm 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)
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
Types, variables, and constants
ErrorOnMistake: BOOLTRUE; -- 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 CHARALL[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
];
Exported NFS procedures
Getattr: PUBLIC PROC [file: SunNFS.FHandle] RETURNS [reply: SunNFS.AttrStat] ~ {
gotProps: BOOLTRUE;
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: BOOLTRUE;
replyStat: SunNFS.Stat;
trans: YggEnvironment.TransID;
outcome: YggTransaction.Outcome;
currentAttrs: SunNFS.AttrStat;
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.
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLTRUE;
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: BOOLFALSE;
notADirectory: BOOLTRUE;
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]];
TRUSTED {newName ← RefText.TrustTextAsRope[where.name];};
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];
};
[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];
};
[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: BOOLFALSE;
lookUpName: ROPE;
notADirectory: BOOLTRUE;
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: BOOLTRUE;
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: BOOLFALSE, newCookie: SunNFS.Cookie] ~ {
enum: YggNaming.EnumProc ~ {
EnumProc: TYPE = PROC [name: ROPE, version: Version, did: YggDID.DID] RETURNS [stop: BOOL];
accept: BOOL;
continue: BOOL;
[accept, continue] ← eachDirEntry[fileid: did.didLow, filename: name];
IF accept THEN lastAcceptedName ← name;
RETURN[~continue];
};
notADirectory: BOOLFALSE;
zeroCookie: BOOLFALSE;
cookieOK: BOOLTRUE;
refTextNnameToStart: REF TEXTNIL;
nameToStart: ROPENIL;
lastAcceptedName: REF TEXTNIL;
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]]];
};
Utilities
InnerGetattr: PROC [file: SunNFS.FHandle, fileDID: YggDID.DID, trans: YggEnvironment.TransID] RETURNS [attrs: SunNFS.AttrStat] ~ {
Gets the attributes for the file specified either by FHandle or DID (FHandle used if not NIL). Raises BadProps if anything goes wrong.
now: SunNFS.TimeVal;
isDirectory: BOOLFALSE;
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;
mode
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;
uid
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;
gid
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;
size
attrs.attributes.size ← YggNav.GetSize[trans: trans, did: fileDID];
blocksize
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;
blocks
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;
atime
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;
mtime
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;
ctime
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] ~ {
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.
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: BOOLFALSE, 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 ~ {
bucket: INT;
prev: LIST OF TimeCacheItem ← NIL;
FOR tcl: LIST OF TimeCacheItem ← TimeCache[bucket], tcl.rest UNTIL tcl = NIL DO
IF BasicTime.Period[from: tcl.first.itemCreateTime, to: now] > 30 THEN {
trans: YggEnvironment.TransID;
gotProps: BOOLTRUE;
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];
IF outcome # commit THEN weShouldDoSomethingHere;
}
ELSE {
There is a problem, but there is no way to report it.
[] ← YggTransaction.Finish[trans, abort];
};
IF prev = NIL THEN TimeCache[bucket] ← 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: BOOLTRUE] RETURNS [status: SunNFS.Stat, rmDID: YggDID.DID] ~ {
whichDirDID: YggDID.DID;
rmFileName: ROPE;
isDirectory: BOOL;
nameFound: BOOL;
moreThanOneMatch: BOOL;
nameMatched: ROPE;
versionMatched: ROPE;
success: BOOLTRUE;
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];
RETURN [ok, rmDID];
};
LinkUnderTrans: PROC [trans: YggEnvironment.TransID, toDID: YggDID.DID, as: SunNFS.DirOpArgs] RETURNS [status: SunNFS.Stat] ~ {
dirDID: YggDID.DID;
isDirectory: BOOLFALSE;
notADirectory: BOOLFALSE;
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]];
};
Cookie stuff
LookupCookie: ENTRY PROC [cookie: SunNFS.Cookie] RETURNS [cookieOK: BOOLFALSE, nameToStart: REF TEXTNIL] ~ {
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 ~ {
Toss all cookies older than 6 seconds
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;
};
Exported Administrative procedures
MakeFileSystem: PUBLIC ENTRY PROC RETURNS [alreadyExists: BOOLFALSE] ~ {
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];
};
Exported Mount procedures
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] ~ {
};
Initialization
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.