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šœ&œ˜