Exported NFS procedures
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;
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: 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]];
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: 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 ~ {
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: 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]]];
};
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: 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;
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:
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 ~ {
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: 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];
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:
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];
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]];
};