DIRECTORY Arpa USING [Address, nullAddress], ArpaUDP USING [nullPort], Atom USING [GetPName], Basics USING [HFromCard16, LowHalf], BasicTime USING [GMT], Convert USING [RopeFromCard], ConvertExtras USING [ArpaAddressFromRope], FS USING [Error], FSBackdoor USING [ErrorCode, ProduceError], FSRemoteFileBackdoor USING [FSServerProcs, FSServerProcsObject, viewFS], RefText USING [Append, New], RemoteFile USING [Error, GetProcsProc, GetServerProc, Register, ServerHandle, ServerObject, ServerProcs, ServerProcsObject, SweepProc, ValidateProc], Rope USING [Cat, Concat, Equal, FromRefText, IsEmpty, ROPE, ToRefText], SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ], SunAuthUnix USING [FixNameForUnix], SunMount USING [EachExportProc, EachGroupProc, FHStatus, program, programVersion], SunMountClient USING [Export, Mnt, Umntall], SunNFS USING [FHandle, program, programVersion, Stat], SunNFSRemoteFile USING [DirEntries, GetRemoteDirChild, GetRemoteDirRoot, InsertRemoteDirChild, RemoteDirHandle, RemoteDirObject, ServerData, ServerDataObject, SunNFSDelete, SunNFSEnumerateForInfo, SunNFSEnumerateForNames, SunNFSGetInfo, SunNFSRename, SunNFSRetrieve, SunNFSStore, SunNFSUnixClose, SunNFSUnixCreate, SunNFSUnixDelete, SunNFSUnixEnumerate, SunNFSUnixGetInfo, SunNFSUnixLink, SunNFSUnixMkDir, SunNFSUnixOpen, SunNFSUnixRead, SunNFSUnixRename, SunNFSUnixRmDir, SunNFSUnixSetInfo, SunNFSUnixSetUser, SunNFSUnixSymLink, SunNFSUnixWrite, SweepRemoteDirCache], SunPMap USING [ipProtocolUDP, udpPort], SunPMapClient USING [GetPort], SunRPC USING [Create, Destroy, Error, Handle, SetRemote], SunRPCAuth USING [Conversation, Error, Initiate, Terminate, unixFlavor], SunYPAgent USING [Error, First, Handle, Match, Next, ObtainHandle, ReleaseHandle, TextSeq, Tokenize, TokenizeUsingSeparator], UnixRemoteFile USING [UnixServerProcs, UnixServerProcsObject, viewUnix], UserCredentials USING [Get] ; SunNFSRemoteServerImpl: CEDAR MONITOR IMPORTS Atom, Basics, Convert, ConvertExtras, FS, FSBackdoor, FSRemoteFileBackdoor, RefText, RemoteFile, Rope, SafeStorage, SunAuthUnix, SunMountClient, SunNFSRemoteFile, SunPMapClient, SunRPC, SunRPCAuth, SunYPAgent, UnixRemoteFile, UserCredentials EXPORTS SunNFSRemoteFile ~ { OPEN SunNFSRemoteFile; ROPE: TYPE ~ Rope.ROPE; ServerHandle: TYPE ~ RemoteFile.ServerHandle; myFlavor: ATOM _ $NFS; hostsMapName: ROPE _ "hosts.byname"; initialRemoteDirTTL: CARDINAL _ 120; downServerTTL: CARDINAL _ 60; upServerTTL: CARDINAL _ 0; -- irrelevant serverProcs: RemoteFile.ServerProcs _ NEW[RemoteFile.ServerProcsObject _ [ sweep~SunNFSSweepServer, validate~SunNFSValidate, getProcs~SunNFSGetProcs ] ]; fsServerProcs: FSRemoteFileBackdoor.FSServerProcs _ NEW[FSRemoteFileBackdoor.FSServerProcsObject _ [ delete~SunNFSDelete, enumerateForInfo~SunNFSEnumerateForInfo, enumerateForNames~SunNFSEnumerateForNames, getInfo~SunNFSGetInfo, rename~SunNFSRename, retrieve~SunNFSRetrieve, store~SunNFSStore ] ]; unixServerProcs: UnixRemoteFile.UnixServerProcs _ NEW[UnixRemoteFile.UnixServerProcsObject _ [ setUser~SunNFSUnixSetUser, open~SunNFSUnixOpen, close~SunNFSUnixClose, create~SunNFSUnixCreate, delete~SunNFSUnixDelete, mkDir~SunNFSUnixMkDir, rmDir~SunNFSUnixRmDir, link~SunNFSUnixLink, symLink~SunNFSUnixSymLink, rename~SunNFSUnixRename, enumerate~SunNFSUnixEnumerate, getInfo~SunNFSUnixGetInfo, setInfo~SunNFSUnixSetInfo, read~SunNFSUnixRead, write~SunNFSUnixWrite ] ]; SunNFSGetServer: PUBLIC RemoteFile.GetServerProc -- [server: ROPE] RETURNS [h: ServerHandle, downMsg: ROPE] -- ~ { ypH: SunYPAgent.Handle _ NIL; rpcH: SunRPC.Handle _ NIL; c: SunRPCAuth.Conversation _ NIL; data: ServerData; val: REF TEXT; downMsg _ "BUG in SunNFSGetServer"; -- hope not to see this. { ENABLE { SunYPAgent.Error => { downMsg _ NIL; GOTO Out }; SunRPCAuth.Error => { downMsg _ "Sun Auth error"; GOTO Out }; SunRPC.Error => { downMsg _ "Sun RPC error"; GOTO Out }; }; tokens: SunYPAgent.TextSeq; myName, myPassword: ROPE; mountPort, nfsPort: CARD; mntResult: SunMount.FHStatus; data _ NEW[ServerDataObject _ [ ttl~upServerTTL, downMsg~NIL, hostName~server, address~Arpa.nullAddress, port~ArpaUDP.nullPort, mountPort~ArpaUDP.nullPort ] ]; SafeStorage.EnableFinalization[data]; ypH _ SunYPAgent.ObtainHandle[]; val _ SunYPAgent.Match[ypH, hostsMapName, server ! SunYPAgent.Error => CONTINUE]; IF val = NIL THEN val _ YPMatchIgnoringCase[ypH, hostsMapName, server]; tokens _ SunYPAgent.Tokenize[val]; IF tokens.length < 1 THEN { downMsg _ "error in YP hosts map"; GOTO Out }; data.address _ ConvertExtras.ArpaAddressFromRope[Rope.FromRefText[tokens[0]]]; SunYPAgent.ReleaseHandle[ypH]; ypH _ NIL; rpcH _ SunRPC.Create[data.address, Basics.HFromCard16[SunPMap.udpPort]]; [myName, myPassword] _ UserCredentials.Get[]; myName _ SunAuthUnix.FixNameForUnix[myName]; c _ SunRPCAuth.Initiate[SunRPCAuth.unixFlavor, myName, myPassword]; mountPort _ SunPMapClient.GetPort[rpcH, c, SunMount.program, SunMount.programVersion, SunPMap.ipProtocolUDP]; nfsPort _ SunPMapClient.GetPort[rpcH, c, SunNFS.program, SunNFS.programVersion, SunPMap.ipProtocolUDP]; data.port _ Basics.HFromCard16[Basics.LowHalf[nfsPort]]; data.mountPort _ Basics.HFromCard16[Basics.LowHalf[mountPort]]; IF (mountPort = 0) OR (nfsPort = 0) THEN { downMsg _ "service not exported"; GOTO Out }; rpcH _ SunRPC.SetRemote[rpcH, data.address, data.mountPort]; mntResult _ SunMountClient.Mnt[rpcH, c, Rope.ToRefText["/"]]; SunMountClient.Umntall[rpcH, c ! SunRPC.Error => CONTINUE]; IF mntResult.status # 0 THEN { downMsg _ "mount failed (/)"; GOTO Out }; data.remoteDirs _ NEW[RemoteDirObject _ [ nameComponent~"", fHandle~mntResult.directory, createMode~0, createModeTTL~0, contentMTime~[0, 0], contentTTL~0, useCount~1, ttl~initialRemoteDirTTL ] ]; h _ NEW[RemoteFile.ServerObject _ [ flavor~myFlavor, name~server, procs~serverProcs, data~data ] ]; downMsg _ MountExportedDirectories[h, rpcH, c]; IF downMsg # NIL THEN { h _ NIL; GOTO Out }; rpcH _ SunRPC.SetRemote[rpcH, data.address, data.port]; ReleaseRPCHandleAndConversation[h, rpcH, c]; rpcH _ NIL; c _ NIL; data.hForUnix _ SunRPC.Create[data.address, data.port]; data.cForUnix _ SunRPCAuth.Initiate[SunRPCAuth.unixFlavor, myName, myPassword]; downMsg _ NIL; EXITS Out => NULL; }; IF (downMsg # NIL) AND (h # NIL) THEN ERROR; -- DEBUG IF ypH # NIL THEN SunYPAgent.ReleaseHandle[ypH]; ypH _ NIL; IF rpcH # NIL THEN SunRPC.Destroy[rpcH]; rpcH _ NIL; IF c # NIL THEN SunRPCAuth.Terminate[c]; c _ NIL; }; YPMatchIgnoringCase: PROC [ypH: SunYPAgent.Handle, mapName: ROPE, server: ROPE] RETURNS [val: REF TEXT] ~ { key: ROPE; [key, val] _ SunYPAgent.First[ypH, mapName]; DO IF Rope.Equal[key, server, FALSE] THEN RETURN; [key, val] _ SunYPAgent.Next[ypH, mapName, key]; ENDLOOP; }; Export: TYPE ~ REF ExportObject; ExportObject: TYPE ~ RECORD [ next: Export, path: REF TEXT ]; MountExportedDirectories: PROC [sH: ServerHandle, mountH: SunRPC.Handle, mountC: SunRPCAuth.Conversation] RETURNS [failMsg: ROPE _ NIL] ~ { exports, exportsTail: Export _ NIL; EachExport: SunMount.EachExportProc -- [fileSys: Path] RETURNS [continue: BOOL] -- ~ { IF exportsTail = NIL THEN exports _ exportsTail _ NEW[ExportObject] ELSE { exportsTail.next _ NEW[ExportObject]; exportsTail _ exportsTail.next }; exportsTail.path _ RefText.Append[RefText.New[fileSys.length], fileSys]; }; EachGroup: SunMount.EachGroupProc -- [group] RETURNS [continue: BOOL] -- ~ { RETURN [TRUE]; }; { ENABLE SunRPC.Error, SunRPCAuth.Error, FS.Error => { failMsg _ "can't get exports from mount server"; GOTO Out; }; SunMountClient.Export[mountH, mountC, EachExport, EachGroup]; IF exports = NIL THEN { failMsg _ "no exported file system on server"; GOTO Out }; }; DO swapped: BOOL _ FALSE; FOR p: Export _ exports, p.next WHILE p.next # NIL DO IF p.path.length > p.next.path.length THEN { temp: REF TEXT ~ p.path; p.path _ p.next.path; p.next.path _ temp; swapped _ TRUE; }; ENDLOOP; IF NOT swapped THEN EXIT; ENDLOOP; FOR p: Export _ exports, p.next WHILE p # NIL DO ENABLE SunRPC.Error, SunRPCAuth.Error, FS.Error => { failMsg _ Rope.Cat["mount failed (", Rope.FromRefText[p.path], ")"]; GOTO Out; }; pathComponents: SunYPAgent.TextSeq; mntResult: SunMount.FHStatus; dH: RemoteDirHandle; IF (p.path.length < 1) OR (p.path[0] # '/) THEN { failMsg _ Rope.Cat["mount export syntax error: ", Rope.FromRefText[p.path]]; GOTO Out; }; WHILE (p.path.length > 1) AND (p.path[p.path.length-1] = '/) DO p.path.length _ p.path.length - 1; ENDLOOP; IF p.path.length = 1 THEN LOOP; -- root has already been mounted mntResult _ SunMountClient.Mnt[mountH, mountC, p.path]; SunMountClient.Umntall[mountH, mountC ! SunRPC.Error => CONTINUE]; IF mntResult.status # 0 THEN { failMsg _ Rope.Cat["mount failed (", Rope.FromRefText[p.path], ")"]; GOTO Out; }; pathComponents _ SunYPAgent.TokenizeUsingSeparator[p.path, '/]; IF pathComponents.length < 2 THEN ERROR; -- already checked for this above! dH _ GetRemoteDirRoot[sH]; FOR j: CARDINAL IN [1 .. pathComponents.length-1) DO [dHChild~dH] _ GetRemoteDirChild[sH, dH, Rope.FromRefText[pathComponents.refText[j]], FALSE ! FS.Error => { failMsg _ "mount failed (subdir search)"; GOTO Out }]; ENDLOOP; dH _ InsertRemoteDirChild[dH, Rope.FromRefText[pathComponents.refText[pathComponents.length-1]], mntResult.directory]; ENDLOOP; EXITS Out => NULL; }; SunNFSValidate: RemoteFile.ValidateProc -- [h: ServerHandle] RETURNS [obsolete: BOOL, downMsg: ROPE] -- ~ { data: ServerData _ NARROW[h.data]; IF (data.ttl = 0) AND (data.downMsg # NIL) THEN RETURN [TRUE, data.downMsg]; RETURN [FALSE, data.downMsg]; }; SunNFSSweepServer: RemoteFile.SweepProc -- [h: ServerHandle, seconds: CARD] -- ~ { data: ServerData _ NARROW[h.data]; rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; SweepServerInner: ENTRY PROC ~ INLINE { ENABLE UNWIND => NULL; IF data.ttl > seconds THEN { data.ttl _ data.ttl - seconds; } ELSE { data.ttl _ 0; rpcH _ data.h; data.h _ NIL; c _ data.c; data.c _ NIL; }; }; SweepRemoteDirCache[data.remoteDirs, seconds]; SweepServerInner[]; IF rpcH # NIL THEN SunRPC.Destroy[rpcH]; IF c # NIL THEN SunRPCAuth.Terminate[c]; }; SunNFSGetProcs: RemoteFile.GetProcsProc -- [h: ServerHandle, view: ATOM] RETURNS [procs: REF] -- ~ { SELECT view FROM FSRemoteFileBackdoor.viewFS => procs _ fsServerProcs; UnixRemoteFile.viewUnix => procs _ unixServerProcs; ENDCASE => ERROR RemoteFile.Error[$notImplemented, "view not implemented"]; }; ObtainRPCHandleAndConversation: PUBLIC PROC [sH: ServerHandle] RETURNS [h: SunRPC.Handle, c: SunRPCAuth.Conversation] ~ { ENABLE UNWIND => { IF h # NIL THEN SunRPC.Destroy[h]; IF c # NIL THEN SunRPCAuth.Terminate[c]; }; data: ServerData _ NARROW[sH.data]; myName, myPassword: ROPE; [myName, myPassword] _ UserCredentials.Get[]; [h, c] _ ObtainRPCHandleAndConversationInner[sH]; IF h = NIL THEN h _ SunRPC.Create[data.address, data.port]; IF c = NIL THEN c _ SunRPCAuth.Initiate[SunRPCAuth.unixFlavor, SunAuthUnix.FixNameForUnix[myName], myPassword ! SunRPCAuth.Error => ReportAuthError[code, sH, NIL]]; }; ObtainRPCHandleAndConversationInner: ENTRY PROC [sH: ServerHandle] RETURNS [h: SunRPC.Handle, c: SunRPCAuth.Conversation] ~ { ENABLE UNWIND => NULL; data: ServerData _ NARROW[sH.data]; h _ data.h; data.h _ NIL; c _ data.c; data.c _ NIL; }; ReleaseRPCHandleAndConversation: PUBLIC PROC [sH: ServerHandle, h: SunRPC.Handle, c: SunRPCAuth.Conversation] ~ { [h, c] _ ReleaseRPCHandleAndConversationInner[sH, h, c]; IF h # NIL THEN SunRPC.Destroy[h]; IF c # NIL THEN SunRPCAuth.Terminate[c]; }; ReleaseRPCHandleAndConversationInner: ENTRY PROC [sH: ServerHandle, h: SunRPC.Handle, c: SunRPCAuth.Conversation] RETURNS [hOut: SunRPC.Handle, cOut: SunRPCAuth.Conversation] ~ { ENABLE UNWIND => NULL; data: ServerData _ NARROW[sH.data]; IF data.h = NIL THEN data.h _ h ELSE hOut _ h; IF data.c = NIL THEN data.c _ c ELSE cOut _ c; }; SetServerDown: ENTRY PROC [sH: ServerHandle, downMsg: ROPE] ~ { data: ServerData _ NARROW[sH.data]; data.downMsg _ downMsg; data.ttl _ downServerTTL; }; ReportRPCError: PUBLIC PROC [code: ATOM, sH: ServerHandle, name, msg: ROPE] ~ { r: ROPE; fsErrorCode: FSBackdoor.ErrorCode; r _ Rope.Cat["SunRPC.Error[", Atom.GetPName[code], "]"]; IF sH # NIL THEN { SetServerDown[sH, r]; r _ Rope.Cat[r, ", Server: ", sH.name]; }; IF NOT Rope.IsEmpty[name] THEN r _ Rope.Cat[r, ", File: ", name]; IF NOT Rope.IsEmpty[msg] THEN r _ Rope.Cat[r, ", ", msg]; fsErrorCode _ (SELECT code FROM $unreachable => serverInaccessible, $timeout => connectionTimedOut, $badCredentials, $badVerifier => badCredentials, $wrongCredentials, $wrongVerifier, $weakCredentials => accessDenied, $wrongProgram, $wrongProgramVersion, $wrongProc => connectionRejected, ENDCASE => software); FSBackdoor.ProduceError[fsErrorCode, r]; }; ReportAuthError: PUBLIC PROC [code: ATOM, sH: ServerHandle, msg: ROPE] ~ { r: ROPE; fsErrorCode: FSBackdoor.ErrorCode; r _ Rope.Cat["SunRPCAuth.Error[", Atom.GetPName[code], "]"]; IF sH # NIL THEN { SetServerDown[sH, r]; r _ Rope.Cat[r, ", Server: ", sH.name]; }; IF NOT Rope.IsEmpty[msg] THEN r _ Rope.Cat[r, ", ", msg]; fsErrorCode _ (SELECT code FROM $badCredentials, $badVerifier => badCredentials, $wrongUserPassword, $wrongService, $wrongCredentials, $wrongVerifier => accessDenied, $timeout => serverInaccessible, ENDCASE => software); FSBackdoor.ProduceError[fsErrorCode, r]; }; ReportNFSError: PUBLIC PROC [status: SunNFS.Stat, sH: ServerHandle, name, msg: ROPE] ~ { r, statusRope: ROPE; fsErrorCode: FSBackdoor.ErrorCode; SELECT status FROM perm => { statusRope _ "perm"; fsErrorCode _ accessDenied }; noent => { statusRope _ "noent"; fsErrorCode _ unknownFile }; io => { statusRope _ "io"; fsErrorCode _ hardware }; nxio => { statusRope _ "nxio"; fsErrorCode _ unknownVolume }; acces => { statusRope _ "acces"; fsErrorCode _ accessDenied }; exist => { statusRope _ "exist"; fsErrorCode _ accessDenied }; nodev => { statusRope _ "nodev"; fsErrorCode _ unknownVolume }; notdir => { statusRope _ "notdir"; fsErrorCode _ fileTypeMismatch }; isdir => { statusRope _ "isdir"; fsErrorCode _ fileTypeMismatch }; fbig => { statusRope _ "fbig"; fsErrorCode _ quotaExceeded }; nospc => { statusRope _ "nospc"; fsErrorCode _ volumeFull }; rofs => { statusRope _ "rofs"; fsErrorCode _ accessDenied }; nametoolong => { statusRope _ "nametoolong"; fsErrorCode _ illegalName }; dquot => { statusRope _ "dquot"; fsErrorCode _ quotaExceeded }; stale => { statusRope _ "stale"; fsErrorCode _ invalidOpenFile }; ENDCASE => { statusRope _ Convert.RopeFromCard[ORD[status]]; fsErrorCode _ software; }; r _ Rope.Cat["SunNFS Error[", statusRope, "]"]; IF sH # NIL THEN r _ Rope.Cat[r, ", Server: ", sH.name]; IF NOT Rope.IsEmpty[name] THEN r _ Rope.Cat[r, ", File: ", name]; IF NOT Rope.IsEmpty[msg] THEN r _ Rope.Cat[r, ", ", msg]; FSBackdoor.ProduceError[fsErrorCode, r]; }; ReportFSError: PUBLIC PROC [code: FSBackdoor.ErrorCode, sH: ServerHandle, name, msg: ROPE] ~ { r: ROPE _ NIL; IF sH # NIL THEN r _ Rope.Concat["Server: ", sH.name]; IF NOT Rope.IsEmpty[name] THEN { IF r # NIL THEN r _ Rope.Concat[r, ", "]; r _ Rope.Cat[r, "File: ", name]; }; IF NOT Rope.IsEmpty[msg] THEN { IF r # NIL THEN r _ Rope.Concat[r, ", "]; r _ Rope.Concat[r, msg]; }; FSBackdoor.ProduceError[code, r]; }; dfq: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[]; Finalizer: PROC ~ { DO data: ServerData _ NARROW[SafeStorage.FQNext[dfq]]; p, next: RemoteDirHandle; FOR p _ data.remoteDirs, next WHILE p # NIL DO SELECT TRUE FROM (p.child # NIL) => { next _ p.child; p.child _ NIL }; (p.sibling # NIL) => { next _ p.sibling; p.sibling _ NIL }; ENDCASE => { next _ p.parent }; ENDLOOP; ENDLOOP; }; SafeStorage.EstablishFinalization[type~CODE[ServerDataObject], npr~0, fq~dfq]; RemoteFile.Register[myFlavor, SunNFSGetServer]; }... ΤSunNFSRemoteServerImpl.mesa Copyright Σ 1987 by Xerox Corporation. All rights reserved. Demers, November 10, 1987 6:38:56 pm PST Copied Types Parameters Registered with FSRemoteFileBackdoor Create server data object ... Look up host address Contact the server PortMapper, bind to Mount and NFS service ports. Get fHandle for desired file system ... Create server object (it's a little early to do this, but we need it to mount subdirectories, so we create it now and NIL it out if the subdirectory mounts fail) ... Get fHandles for mounted subdirectories, if any (note at this point h looks like a valid, up handle) ... Store RPC handle and credentials into server object ... Okay ... On failure raises SunYPAgent.Error[$noMoreEntries]. Ask mount server for exported mount points ... Sort mount paths by length to ensure the mounts are done in the right order. Bubble sort is good enough, since they ought to be presented by the server in order anyway ... Mount each specified directory, making a pinned directory cache entry for it ... Syntax check ... Get fHandle for mount Insert entry in directory cache ... (Drop dH without unpinning it, so fHandle is never flushed from cache) Error Reporting Finalization Initialization ΚΧ˜codešœ™K™˜>Kšœ>˜>Kšœ?˜?KšœD˜DKšœB˜BKšœ=˜=Kšœ<˜