<> <> <> 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, 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 _ [ 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[]; c _ SunRPCAuth.Initiate[SunRPCAuth.unixFlavor, SunAuthUnix.FixNameForUnix[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; <> 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]; <<(Drop dH without unpinning it, so fHandle is never flushed from cache)>> 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]; }...