SunNFSRemoteDirImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Demers, November 8, 1987 1:37:53 pm PST
DIRECTORY
Basics USING [DoubleAnd, DoubleNot, DoubleOr],
BasicTime USING [GMT],
FS USING [Error],
RemoteFile USING [ServerHandle],
Rope USING [Cat, Equal, FromRefText, IsEmpty, ROPE, ToRefText],
SunMountClient USING [Mnt, Umnt],
SunNFS USING [AttrStat, Cookie, DirOpRes, EachDirEntryProc, FHandle, FileName, SAttr, Stat, TimeVal],
SunNFSClient USING [Getattr, Lookup, Mkdir, Readdir],
SunNFSRemoteFile USING [CompareSunTimes, CreateCaseFile, DirEntries, DirEntriesObject, EachDirEntryProc, NameReaderObject, NameWriterObject, ObtainRPCHandleAndConversation, ReleaseRPCHandleAndConversation, ReadDirComponent, RemoteDirObject, ReportNFSError, ReportRPCError, ResetNameReader, ServerData],
SunRPC USING [Error, Handle, SetRemote],
SunRPCAuth USING [Conversation]
;
SunNFSRemoteDirImpl: CEDAR MONITOR
IMPORTS Basics, Rope, FS, SunMountClient, SunNFSClient, SunNFSRemoteFile, SunRPC
EXPORTS SunNFSRemoteFile
~ {
OPEN SunNFSRemoteFile;
Copied Types
FHandle: TYPE ~ SunNFS.FHandle;
GMT: TYPE ~ BasicTime.GMT;
NameReader: TYPE ~ REF NameReaderObject;
NameWriter: TYPE ~ REF NameWriterObject;
RemoteDirHandle: TYPE ~ REF RemoteDirObject;
ROPE: TYPE ~ Rope.ROPE;
ServerHandle: TYPE ~ RemoteFile.ServerHandle;
Parameters
initialRemoteDirTTL: CARDINAL ← 600;
initialCreateModeTTL: CARDINAL ← 600;
defaultCreateMode: CARD ← ownerRWBits;
fsModeFileName: ROPE ← ".fsmode";
dirSearchBlocksize: CARD ← 1024; -- should be 8K???
initialDirContentTTL: CARDINAL ← 60;
dirEntriesPerObject: CARDINAL ← 30;
File mode stuff
regularModeBits: CARD ~ 0100000B;
directoryModeBits: CARD ~ 040700B;
accessBits: CARD ~ 07777B; -- includes suid, sgid
ownerRWBits: CARD ~ 0600B;
ownerSearchBit: CARD ~ 0100B;
groupRWBits: CARD ~ 060B;
groupSearchBit: CARD ~ 010B;
otherRWBits: CARD ~ 06B;
otherSearchBit: CARD ~ 01B;
FixModeForRegularFile: PUBLIC PROC [mode: CARD] RETURNS [CARD] ~ {
mode ← LOOPHOLE[Basics.DoubleOr[LOOPHOLE[mode], LOOPHOLE[regularModeBits]]];
RETURN [mode];
};
FixModeForDirectory: PUBLIC PROC [mode: CARD] RETURNS [CARD] ~ {
mode ← LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[mode], LOOPHOLE[accessBits]]];
mode ← LOOPHOLE[Basics.DoubleOr[LOOPHOLE[mode], LOOPHOLE[directoryModeBits]]];
IF LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[mode], LOOPHOLE[groupRWBits]], CARD] # 0
THEN mode ← LOOPHOLE[Basics.DoubleOr[LOOPHOLE[mode], LOOPHOLE[groupSearchBit]]];
IF LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[mode], LOOPHOLE[otherRWBits]], CARD] # 0
THEN mode ← LOOPHOLE[Basics.DoubleOr[LOOPHOLE[mode], LOOPHOLE[otherSearchBit]]];
RETURN [mode];
};
GetModeAccessBits: PUBLIC PROC [mode: CARD] RETURNS [CARD] ~ {
RETURN [LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[mode], LOOPHOLE[accessBits]]] ];
};
UpdateModeAccessBits: PUBLIC PROC [mode: CARD, newAccessBits: CARD] RETURNS [CARD] ~ {
mode ← LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[mode], LOOPHOLE[Basics.DoubleNot[LOOPHOLE[accessBits]]]]];
newAccessBits ← LOOPHOLE[Basics.DoubleAnd[LOOPHOLE[newAccessBits], LOOPHOLE[accessBits]]];
RETURN[ LOOPHOLE[Basics.DoubleOr[LOOPHOLE[mode], LOOPHOLE[newAccessBits]]] ];
};
Cache of remote directory handles
FindRemoteDirChild: ENTRY PROC [dH: RemoteDirHandle, childName: ROPE]
RETURNS [dHChild: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
FOR dHChild ← dH.child, dHChild.sibling WHILE dHChild # NIL DO
IF Rope.Equal[dHChild.nameComponent, childName, TRUE] THEN {
dHChild.useCount ← dHChild.useCount.SUCC;
RETURN;
};
ENDLOOP;
};
InsertRemoteDirChild: PUBLIC ENTRY PROC [dH: RemoteDirHandle, childName: ROPE, fHandle: SunNFS.FHandle] RETURNS [dHChild: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
dHChild ← NEW[RemoteDirObject ← [parent~dH, sibling~dH.child, nameComponent~childName, fHandle~fHandle, createMode~0, createModeTTL~0, contentMTime~[0, 0], contentTTL~0, useCount~1, ttl~initialRemoteDirTTL]];
dH.child ← dHChild;
};
PinRemoteDirPath: PUBLIC ENTRY PROC [dH: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
WHILE dH # NIL DO
dH.useCount ← dH.useCount.SUCC;
dH ← dH.parent;
ENDLOOP;
};
UnPinRemoteDir: PUBLIC ENTRY PROC [dH: RemoteDirHandle]
RETURNS [dHParent: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
dH.useCount ← dH.useCount.PRED;
dH.ttl ← initialRemoteDirTTL;
RETURN[dH.parent];
};
UnPinRemoteDirPath: PUBLIC ENTRY PROC [dH: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
WHILE dH # NIL DO
dH.useCount ← dH.useCount.PRED;
dH.ttl ← initialRemoteDirTTL;
dH ← dH.parent;
ENDLOOP;
};
GetRemoteDirRoot: PUBLIC ENTRY PROC [sH: ServerHandle]
RETURNS [dH: RemoteDirHandle] ~ {
ENABLE UNWIND => NULL;
d: ServerData ← NARROW[sH.data];
dH ← d.remoteDirs;
IF dH = NIL THEN ERROR;
dH.useCount ← dH.useCount.SUCC;
};
VerifySubdirectory: PROC [sH: ServerHandle, dH: RemoteDirHandle, name: ROPE] RETURNS [fH: SunNFS.FHandle] ~ {
Return fHandle for subdirectory if it exists; return NIL if subdirectory doesn't exist, else raise appropriate FS.Error.
reply: SunNFS.DirOpRes;
reply ← LookupThruSymLinks[sH, dH, name];
SELECT reply.status FROM
ok => {
IF reply.attributes.type # dir THEN ReportNFSError[notdir, sH, name];
fH ← reply.file;
};
noent => {
fH ← NIL;
};
ENDCASE => {
ReportNFSError[reply.status, sH, name];
};
};
CreateSubdirectory: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, name: ROPE, desiredMode: CARD] RETURNS [fH: SunNFS.FHandle] ~ {
Create subdirectory in the directory specified by <sH, dH>.
rpcH: SunRPC.Handle;
c: SunRPCAuth.Conversation;
reply: SunNFS.DirOpRes;
[rpcH, c] ← ObtainRPCHandleAndConversation[sH];
{ ENABLE UNWIND => ReleaseRPCHandleAndConversation[sH, rpcH, c];
reply ← SunNFSClient.Mkdir[rpcH, c, [dH.fHandle, Rope.ToRefText[name]],
SunNFS.SAttr[
mode~FixModeForDirectory[desiredMode],
uid~CARD.LAST, -- => default
gid~CARD.LAST, -- => default
size~CARD.LAST, -- => default
atime~[CARD.LAST, CARD.LAST], -- => default
mtime~[CARD.LAST, CARD.LAST] -- => default
]
! SunRPC.Error => ReportRPCError[code, sH, name]];
SELECT reply.status FROM
ok => {
fH ← reply.file;
};
ENDCASE => {
ReportNFSError[reply.status, sH, name];
};
};
ReleaseRPCHandleAndConversation[sH, rpcH, c];
};
RefreshRemoteDirContent: PROC [sH: ServerHandle, dH: RemoteDirHandle, staleOK: BOOL] RETURNS [content: DirEntries] ~ {
Read directory content from server if it has changed since oldMTime.
h: SunRPC.Handle;
c: SunRPCAuth.Conversation;
tail: DirEntries ← NIL;
oldMTime, mtime: SunNFS.TimeVal;
EachEntry: SunNFS.EachDirEntryProc -- [fileid: CARD, filename: FileName] RETURNS [continue: BOOL ← TRUE] -- ~ {
nameRope: ROPE ~ Rope.FromRefText[filename];
IF (tail = NIL) OR (tail.count >= tail.maxCount) THEN {
temp: DirEntries ~ NEW[DirEntriesObject[dirEntriesPerObject]];
IF tail = NIL THEN content ← temp ELSE tail.next ← temp;
tail ← temp;
};
tail.entries[tail.count] ← nameRope;
tail.count ← tail.count + 1;
};
CheckOutContent: ENTRY PROC RETURNS [locked: BOOL] ~ {
ENABLE UNWIND => NULL;
WHILE dH.contentLocked DO WAIT dH.contentAvailable ENDLOOP;
content ← dH.content;
oldMTime ← dH.contentMTime;
locked ← dH.contentLocked ← ((NOT staleOK) OR (dH.contentTTL = 0));
};
CheckInContent: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
dH.content ← content;
dH.contentMTime ← mtime;
dH.contentTTL ← initialDirContentTTL;
dH.contentLocked ← FALSE;
BROADCAST dH.contentAvailable;
};
UnlockContent: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
dH.contentLocked ← FALSE;
BROADCAST dH.contentAvailable;
};
IF NOT CheckOutContent[].locked THEN RETURN;
{
ENABLE UNWIND => {
UnlockContent[];
IF h # NIL THEN ReleaseRPCHandleAndConversation[sH, h, c];
};
{
attrStat: SunNFS.AttrStat;
[h, c] ← ObtainRPCHandleAndConversation[sH];
attrStat ← SunNFSClient.Getattr[h, c, dH.fHandle
! SunRPC.Error => ReportRPCError[code, sH]];
IF attrStat.status # ok THEN ReportNFSError[attrStat.status, sH];
mtime ← attrStat.attributes.mtime;
};
IF CompareSunTimes[mtime, oldMTime] = greater THEN {
status: SunNFS.Stat;
eof: BOOLFALSE;
cookie: SunNFS.Cookie ← NIL;
WHILE NOT eof DO
[status, eof, cookie] ← SunNFSClient.Readdir[h, c, dH.fHandle, cookie, dirSearchBlocksize, EachEntry
! SunRPC.Error => ReportRPCError[code, sH]];
IF status # ok THEN ReportNFSError[status, sH];
ENDLOOP;
};
};
CheckInContent[];
ReleaseRPCHandleAndConversation[sH, h, c];
};
GetCreateMode: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, forDirectory: BOOL]
RETURNS [createMode: CARD] ~ {
rawMode: CARD ← RefreshCreateMode[sH, dH];
RETURN [IF forDirectory THEN FixModeForDirectory[rawMode] ELSE FixModeForRegularFile[rawMode]];
};
RefreshCreateMode: PROC [sH: ServerHandle, dH: RemoteDirHandle]
RETURNS [createMode: CARD] ~ {
The returned create mode has no type bits set ...
CheckOutCreateMode: ENTRY PROC RETURNS [ok: BOOL] ~ --INLINE-- {
ENABLE UNWIND => NULL;
createMode ← dH.createMode;
ok ← (dH.createModeTTL > 0);
};
CheckInCreateMode: ENTRY PROC ~ --INLINE-- {
ENABLE UNWIND => NULL;
dH.createMode ← createMode;
dH.createModeTTL ← initialCreateModeTTL;
};
IF dH = NIL THEN RETURN [defaultCreateMode];
IF CheckOutCreateMode[].ok THEN RETURN;
{ reply: SunNFS.DirOpRes ← LookupThruSymLinks[sH, dH, fsModeFileName];
SELECT reply.status FROM
ok => createMode ← GetModeAccessBits[reply.attributes.mode];
noent => createMode ← RefreshCreateMode[sH, dH.parent];
ENDCASE => ReportNFSError[reply.status, sH];
};
CheckInCreateMode[];
};
EnumerateDirectory: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, eachDirEntry: EachDirEntryProc, staleOK: BOOL] ~ {
content: DirEntries;
content ← RefreshRemoteDirContent[sH, dH, staleOK];
FOR p: DirEntries ← content, p.next WHILE p # NIL DO
FOR i: CARDINAL IN [0 .. p.count) DO
IF NOT eachDirEntry[p.entries[i]].continue THEN RETURN;
ENDLOOP;
ENDLOOP;
};
FollowDirPath: PUBLIC PROC [sH: ServerHandle, nR: NameReader, case: BOOL, create: BOOL] RETURNS [dH: RemoteDirHandle ← NIL] ~ {
ENABLE UNWIND => {
IF dH # NIL THEN UnPinRemoteDirPath[dH];
};
dH ← GetRemoteDirRoot[sH];
DO
dHParent: RemoteDirHandle ← dH;
created: BOOL;
component: ROPE ← ReadDirComponent[nR, FALSE];
IF Rope.IsEmpty[component] THEN EXIT;
[dH, created] ← GetRemoteDirChild[sH~sH, dH~dH, childName~component, create~create];
IF created THEN {
componentWithCase: ROPE;
ResetNameReader[nR, -1];
componentWithCase ← ReadDirComponent[nR, TRUE];
CreateCaseFile[sH, dHParent, componentWithCase ! FS.Error => CONTINUE ];
};
ENDLOOP;
};
GetRemoteDirChild: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, childName: ROPE, create: BOOL]
RETURNS [dHChild: RemoteDirHandle, created: BOOLFALSE] ~ {
Given a (pinned) RemoteDirHandle, return a (pinned) RemoteDirHandle for the named child, if that child exists and is a directory. Otherwise raise FS.Error[...].
fH: SunNFS.FHandle;
IF (dHChild ← FindRemoteDirChild[dH, childName]) # NIL THEN RETURN;
fH ← VerifySubdirectory[sH, dH, childName];
IF fH = NIL THEN {
desiredMode: CARD;
IF NOT create THEN ReportNFSError[noent, sH, childName];
desiredMode ← GetCreateMode[sH, dH, TRUE];
fH ← CreateSubdirectory[sH, dH, childName, desiredMode];
created ← TRUE;
};
dHChild ← InsertRemoteDirChild[dH, childName, fH];
};
SweepRemoteDirCache: PUBLIC ENTRY PROC [root: RemoteDirHandle, seconds: CARD] ~ {
Note this can't delete the root!
ENABLE UNWIND => NULL;
SweepInner: PROC [dH: RemoteDirHandle] ~ {
prev: RemoteDirHandle ← NIL;
FOR p: RemoteDirHandle ← dH.child, p.sibling WHILE p # NIL DO
SweepInner[p];
IF (p.ttl = 0) AND (p.child = NIL) AND (p.useCount = 0)
THEN {
IF prev = NIL THEN dH.child ← p.sibling ELSE prev.sibling ← p.sibling;
}
ELSE {
prev ← p;
};
ENDLOOP;
IF dH.ttl > seconds THEN dH.ttl ← dH.ttl - seconds ELSE dH.ttl ← 0;
IF dH.contentTTL > seconds
THEN {
dH.contentTTL ← dH.contentTTL - seconds;
}
ELSE {
dH.contentTTL ← 0;
IF NOT dH.contentLocked THEN {
dH.content ← NIL;
dH.contentMTime ← [0, 0];
};
};
IF dH.createModeTTL > seconds
THEN dH.createModeTTL ← dH.createModeTTL - seconds
ELSE dH.createModeTTL ← 0;
};
IF root # NIL THEN SweepInner[root];
};
Lookup through symbolic links
LookupThruSymLinks: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, name: ROPE] RETURNS [dirOpRes: SunNFS.DirOpRes] ~ {
rpcH, rpcHForMount: SunRPC.Handle;
c: SunRPCAuth.Conversation;
data: ServerData ← NARROW[sH.data];
{
ENABLE {
UNWIND => {
IF rpcHForMount # NIL
THEN rpcH ← SunRPC.SetRemote[rpcHForMount, data.address, data.port];
IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[sH, rpcH, c];
};
SunRPC.Error => {
ReportRPCError[code, sH];
};
};
fullPathName: ROPE;
fullPathNameRefText: REF TEXT;
attrStat: SunNFS.AttrStat;
fH: REF TEXT;
[rpcH, c] ← ObtainRPCHandleAndConversation[sH];
dirOpRes ← SunNFSClient.Lookup[rpcH, c, [dH.fHandle, Rope.ToRefText[name]] ];
SELECT dirOpRes.status FROM
ok => NULL;
ENDCASE => GOTO Out;
SELECT dirOpRes.attributes.type FROM
lnk => NULL;
ENDCASE => GOTO Out;
At this point we've found a symbolic link. We let the mount server follow it ...
fullPathName ← name;
FOR finger: RemoteDirHandle ← dH, finger.parent WHILE finger # NIL DO
fullPathName ← Rope.Cat[finger.nameComponent, "/", fullPathName];
ENDLOOP;
fullPathNameRefText ← Rope.ToRefText[fullPathName];
rpcHForMount ← SunRPC.SetRemote[rpcH, data.address, data.mountPort];
rpcH ← NIL;
fH ← SunMountClient.Mnt[rpcHForMount, c, fullPathNameRefText].directory;
IF fH = NIL THEN { dirOpRes.status ← noent; GOTO Out };
SunMountClient.Umnt[rpcHForMount, c, fullPathNameRefText
! SunRPC.Error => CONTINUE ];
rpcH ← SunRPC.SetRemote[rpcHForMount, data.address, data.port];
rpcHForMount ← NIL;
attrStat ← SunNFSClient.Getattr[rpcH, c, fH];
dirOpRes ← [attrStat.status, fH, attrStat.attributes];
EXITS
Out => NULL;
};
IF rpcHForMount # NIL
THEN rpcH ← SunRPC.SetRemote[rpcHForMount, data.address, data.port];
IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[sH, rpcH, c];
};
}...