<> <> <> DIRECTORY Arpa USING [Address], ArpaExtras USING [MyAddress], Basics USING [CompareCard, Comparison], BasicTime USING [earliestGMT, GetClockPulses, GMT, Now, nullGMT, Pack, Period, Update], FS USING [Error], FSBackdoor USING [highestVersion, lowestVersion, noVersion, Version], FSName USING [BangVersionFile], FSRemoteFileBackdoor USING [DeleteProc, DoIOProc, ErrorNotImplemented, GetInfoProc, RenameProc, RetrieveProc, ServerObject, StoreProc], IO USING [GetBlock, PutBlock, PutFR, STREAM], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Concat, Equal, Length, ROPE, ToRefText], SunNFS USING [AttrStat, DirOpArgs, DirOpRes, FHandle, FileName, FType, maxData, SAttr, Stat, TimeVal], SunNFSClient USING [Create, Link, Read, Remove, Rename, Setattr, Write], SunNFSFSRemoteFile USING [CreateNameReader, DecodeVersionFromNFSName, EachDirEntryProc, EncodeVersionInNFSName, EnumerateDirectory, FollowDirPath, GetCreateMode, LookupThruSymLinks, NameReaderObject, NameWriterObject, ObtainRPCHandleAndConversation, ReleaseRPCHandleAndConversation, ReadBaseComponent, RemoteDirObject, ReportFSError, ReportNFSError, ReportRPCError, ResetNameReader, ServerDataObject, UnPinRemoteDirPath, VersionInfo], SunRPC USING [Error, Handle], SunRPCAuth USING [Conversation] ; SunNFSFSRemoteFileImpl: CEDAR PROGRAM IMPORTS ArpaExtras, Basics, BasicTime, FS, FSName, FSRemoteFileBackdoor, IO, RefText, Rope, SunNFSClient, SunNFSFSRemoteFile, SunRPC EXPORTS SunNFSFSRemoteFile ~ { OPEN SunNFSFSRemoteFile; <> FHandle: TYPE ~ SunNFS.FHandle; FType: TYPE ~ SunNFS.FType; GMT: TYPE ~ BasicTime.GMT; NameReader: TYPE ~ REF NameReaderObject; <> NameWriter: TYPE ~ REF NameWriterObject; <> RemoteDirHandle: TYPE ~ REF RemoteDirObject; <> ROPE: TYPE ~ Rope.ROPE; ServerData: TYPE ~ REF ServerDataObject; <> ServerHandle: TYPE ~ REF ServerObject; ServerObject: TYPE ~ FSRemoteFileBackdoor.ServerObject; Version: TYPE ~ FSBackdoor.Version; <> initialRemoteDirTTL: CARDINAL _ 600; serverBlocksize: CARDINAL _ SunNFS.maxData; nfsBufferBytes: CARDINAL _ 2048; -- would be serverBlocksize if gateways could handle it sunEpoch: GMT _ BasicTime.Pack[ [year~1970, month~January, day~1, hour~0, minute~0, second~0, zone~0, dst~no] ]; defaultCreateMode: CARD _ 0100600B; fsTempDirName: ROPE _ "fstmp"; fsLockPrefix: ROPE _ "fslock."; fsLockPrefixLen: CARDINAL _ Rope.Length[fsLockPrefix]; lockPauseMsec: CARD _ 500; lockRetries: CARDINAL _ 10; <> LookupSimple: PROC [sH: ServerHandle, dH: RemoteDirHandle, nameOnServer: ROPE] RETURNS [fH: FHandle, type: FType, bytes: CARD, created: GMT] ~ { <> <> <> reply: SunNFS.DirOpRes _ LookupThruSymLinks[sH, dH, nameOnServer]; SELECT reply.status FROM ok => { fH _ reply.file; type _ reply.attributes.type; bytes _ reply.attributes.size; created _ GMTFromSunTime[reply.attributes.mtime]; }; noent => { fH _ NIL; type _ non; bytes _ 0; created _ BasicTime.earliestGMT; }; ENDCASE => ReportNFSError[reply.status, sH, nameOnServer]; }; LookupVersionByCreatedTime: PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE, wantedCreatedTime: GMT] RETURNS [fH: FHandle, nameOnServer: ROPE, version: Version, type: FType, bytes: CARD] ~ { <> <> <> found: BOOL _ FALSE; EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL _ TRUE] -- ~ { entryNameWithoutVersion: ROPE; [entryNameWithoutVersion, version] _ DecodeVersionFromNFSName[entryName]; IF Rope.Equal[base, entryNameWithoutVersion, TRUE] AND (version # FSBackdoor.noVersion) THEN { created: GMT; [fH~fH, type~type, bytes~bytes, created~created] _ LookupSimple[sH, dH, entryName]; IF (fH # NIL) AND (created = wantedCreatedTime) THEN { nameOnServer _ entryName; found _ TRUE; RETURN[FALSE]; }; }; }; base _ FSName.BangVersionFile[base, FSBackdoor.noVersion]; EnumerateDirectory[sH, dH, EachDirEntry, FALSE]; IF NOT found THEN RETURN [NIL, base, FSBackdoor.noVersion, non, 0]; }; FindHighestVersion: PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE, staleOK: BOOL] RETURNS [highestVersion: Version, nameOnServer: ROPE, found: BOOL _ FALSE] ~ { EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL _ TRUE] -- ~ { entryNameWithoutVersion: ROPE; version: Version; [entryNameWithoutVersion, version] _ DecodeVersionFromNFSName[entryName]; IF Rope.Equal[base, entryNameWithoutVersion, TRUE] AND (version # FSBackdoor.noVersion) THEN { temp: CARDINAL ~ version; -- for coercion between Version and CARDINAL IF temp > highestVersion THEN { found _ TRUE; nameOnServer _ entryName; highestVersion _ [temp]; }; }; }; highestVersion _ [0]; base _ FSName.BangVersionFile[base, FSBackdoor.noVersion]; EnumerateDirectory[sH, dH, EachDirEntry, staleOK]; }; FindLowestVersion: PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE, staleOK: BOOL] RETURNS [lowestVersion: Version, nameOnServer: ROPE, found: BOOL _ FALSE] ~ { EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL _ TRUE] -- ~ { entryNameWithoutVersion: ROPE; version: Version; [entryNameWithoutVersion, version] _ DecodeVersionFromNFSName[entryName]; IF Rope.Equal[base, entryNameWithoutVersion, TRUE] AND (version # FSBackdoor.noVersion) THEN { temp: CARDINAL ~ version; -- for coercion between Version and CARDINAL IF temp < lowestVersion THEN { found _ TRUE; nameOnServer _ entryName; lowestVersion _ [temp]; }; }; }; lowestVersion _ [CARDINAL.LAST]; base _ FSName.BangVersionFile[base, FSBackdoor.noVersion]; EnumerateDirectory[sH, dH, EachDirEntry, staleOK]; }; OpenForRead: PROC [sH: ServerHandle, nR: NameReader, wantedCreatedTime: GMT] RETURNS [dH: RemoteDirHandle, fH: FHandle, nameOnServer: ROPE, version: Version, type: FType, bytes: CARD, created: GMT] ~ { base: ROPE; { ENABLE UNWIND => { IF dH # NIL THEN UnPinRemoteDirPath[dH]; }; isPattern: BOOL; vI: VersionInfo; found: BOOL; dH _ FollowDirPath[sH~sH, nR~nR, case~FALSE, create~FALSE]; [base~base, vI~vI, version~version, isPattern~isPattern] _ ReadBaseComponent[nR~nR, case~FALSE, stripVersion~TRUE]; IF isPattern THEN ReportFSError[patternNotAllowed, sH, base]; SELECT vI FROM missing => { ReportFSError[illegalName, sH, base, "fs file name requires version part"]; }; bang, bangH => { [version, nameOnServer, found] _ FindHighestVersion[sH, dH, base, FALSE]; IF NOT found THEN GOTO Out; }; bangL => { [version, nameOnServer, found] _ FindLowestVersion[sH, dH, base, FALSE]; IF NOT found THEN GOTO Out; }; bangStar => { IF (wantedCreatedTime = BasicTime.nullGMT) THEN ReportFSError[patternNotAllowed, sH, base, "Read version=* not allowed"]; found _ FALSE; }; bangNumber => { nameOnServer _ EncodeVersionInNFSName[base, version]; found _ TRUE; }; ENDCASE => ReportFSError[software, sH, base, "version?"]; IF found THEN [fH, type, bytes, created] _ LookupSimple[sH, dH, nameOnServer]; IF (wantedCreatedTime = BasicTime.nullGMT) OR ((fH # NIL) AND (wantedCreatedTime = created)) THEN GOTO Out; [fH, nameOnServer, version, type, bytes] _ LookupVersionByCreatedTime[sH, dH, base, wantedCreatedTime]; created _ wantedCreatedTime; -- okay even if lookup failed EXITS Out => NULL; }; UnPinRemoteDirPath[dH]; IF fH = NIL THEN { ReportFSError[unknownFile, sH, base]; }; IF type # reg THEN { ReportFSError[fileTypeMismatch, sH, base]; }; }; AtomicallyRename: PROC [sH: ServerHandle, dHFrom: RemoteDirHandle, fHFrom: FHandle, fromNameOnServer: ROPE, dHTo: RemoteDirHandle, baseTo: ROPE, desiredVersion: Version, deleteIfError: BOOL] RETURNS [version: Version] ~ { <> <> rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; status: SunNFS.Stat; toNameOnServer: ROPE; fromNameRefText: REF TEXT _ Rope.ToRefText[fromNameOnServer]; toNameRefText: REF TEXT; { ENABLE UNWIND => { IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[sH, rpcH, c]; IF deleteIfError THEN SilentlyRemove[sH, dHFrom, fromNameRefText]; }; [rpcH, c] _ ObtainRPCHandleAndConversation[sH]; SELECT desiredVersion FROM FSBackdoor.lowestVersion => ERROR; FSBackdoor.highestVersion, FSBackdoor.noVersion => { found: BOOL; vTemp: CARDINAL; [highestVersion~version, found~found] _ FindHighestVersion[sH~sH, dH~dHTo, base~baseTo, staleOK~TRUE]; FOR try: CARDINAL _ 0, try+1 DO IF try > lockRetries THEN ReportFSError[lockConflict, sH, baseTo, "Too many retries on create."]; IF (vTemp _ version) >= (CARDINAL.LAST - 1) THEN ReportFSError[noMoreVersions, sH, baseTo]; version _ [vTemp+1]; toNameOnServer _ EncodeVersionInNFSName[baseTo, version]; toNameRefText _ Rope.ToRefText[toNameOnServer]; status _ SunNFSClient.Link[rpcH, c, fHFrom, [dHTo.fHandle, toNameRefText] ! SunRPC.Error => ReportRPCError[code, sH, baseTo]]; SELECT status FROM ok => EXIT; exist => LOOP; ENDCASE => ReportNFSError[status, sH, baseTo]; ENDLOOP; status _ SunNFSClient.Remove[rpcH, c, [dHFrom.fHandle, fromNameRefText] ! SunRPC.Error => ReportRPCError[code, sH]]; SELECT status FROM ok => NULL; ENDCASE => ReportNFSError[status, sH, baseTo]; }; ENDCASE => { version _ desiredVersion; toNameOnServer _ EncodeVersionInNFSName[baseTo, version]; toNameRefText _ Rope.ToRefText[toNameOnServer]; status _ SunNFSClient.Rename[rpcH, c, [dHFrom.fHandle, fromNameRefText], [dHTo.fHandle, toNameRefText] ! SunRPC.Error => ReportRPCError[code, sH, baseTo]]; SELECT status FROM ok => NULL; ENDCASE => ReportNFSError[status, sH, baseTo]; }; ReleaseRPCHandleAndConversation[sH, rpcH, c]; rpcH _ NIL; }; }; SilentlyRemove: PROC [sH: ServerHandle, dH: RemoteDirHandle, base: REF TEXT] ~ { rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; { ENABLE SunRPC.Error, FS.Error => { IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[sH, rpcH, c]; CONTINUE; }; [rpcH, c] _ ObtainRPCHandleAndConversation[sH]; [] _ SunNFSClient.Remove[rpcH, c, [dH.fHandle, base]]; ReleaseRPCHandleAndConversation[sH, rpcH, c]; }; }; tempNamePrefix: ROPE _ NIL; CreateTempName: PROC [base: ROPE] RETURNS [tempName: ROPE] ~ { IF tempNamePrefix = NIL THEN { a: Arpa.Address ~ ArpaExtras.MyAddress[]; tempNamePrefix _ IO.PutFR["fs.%g.%g.%g.%g", [cardinal[a.a]], [cardinal[a.b]], [cardinal[a.c]], [cardinal[a.d]]]; }; tempName _ IO.PutFR["%g.%g.%g", [rope[tempNamePrefix]], [cardinal[LOOPHOLE[BasicTime.Now[], CARD]]], [cardinal[LOOPHOLE[BasicTime.GetClockPulses[], CARD]]] ]; }; caseFileNamePrefix: PUBLIC ROPE _ ".~case~"; caseFileNamePrefixLen: PUBLIC INT _ Rope.Length[caseFileNamePrefix]; CreateCaseFile: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE] ~ { rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; caseFileName: ROPE _ Rope.Concat[caseFileNamePrefix, base]; { ENABLE SunRPC.Error, FS.Error => { IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[sH, rpcH, c]; CONTINUE; }; [rpcH, c] _ ObtainRPCHandleAndConversation[sH]; [] _ SunNFSClient.Create[rpcH, c, [dH.fHandle, Rope.ToRefText[caseFileName]], SunNFS.SAttr[ mode ~ 0, uid~CARD.LAST, -- => default gid~CARD.LAST, -- => default size~CARD.LAST, -- => default atime~[CARD.LAST, CARD.LAST], -- => default mtime~[CARD.LAST, CARD.LAST] -- => default ] ]; ReleaseRPCHandleAndConversation[sH, rpcH, c]; rpcH _ NIL; }; }; CheckCaseFile: PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE] RETURNS [caseFileName: ROPE _ NIL] ~ { caselessCaseFileName: ROPE; EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL _ TRUE] -- ~ { IF Rope.Equal[caselessCaseFileName, entryName, FALSE] THEN { caseFileName _ entryName; RETURN[FALSE]; }; RETURN [TRUE]; }; caselessCaseFileName _ Rope.Concat[caseFileNamePrefix, base]; EnumerateDirectory[sH, dH, EachDirEntry, FALSE]; }; CollectCaseFile: PUBLIC PROC [sH: ServerHandle, dH: RemoteDirHandle, base: ROPE] ~ { caselessCaseFileName: ROPE; versionFound: BOOL _ FALSE; caseFileNames: LIST OF ROPE _ NIL; EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL _ TRUE] -- ~ { nameWithoutVersion: ROPE; caseOK: BOOL; IF Rope.Equal[caselessCaseFileName, entryName, FALSE] THEN { caseFileNames _ CONS[entryName, caseFileNames]; RETURN[TRUE]; }; [nameWithoutVersion, , caseOK] _ DecodeVersionFromNFSName[entryName, TRUE]; IF caseOK AND Rope.Equal[base, nameWithoutVersion, FALSE] THEN versionFound _ TRUE; RETURN [NOT versionFound]; }; caselessCaseFileName _ Rope.Concat[caseFileNamePrefix, base]; EnumerateDirectory[sH, dH, EachDirEntry, FALSE]; IF (NOT versionFound) THEN { FOR each: LIST OF ROPE _ caseFileNames, each.rest WHILE each # NIL DO SilentlyRemove[sH, dH, Rope.ToRefText[each.first]]; ENDLOOP; }; }; <> SunNFSDelete: PUBLIC FSRemoteFileBackdoor.DeleteProc -- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmProc] -- ~ { rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; dH: RemoteDirHandle; status: SunNFS.Stat; version: Version; nameOnServer: ROPE; nR: NameReader _ CreateNameReader[file]; [dH~dH, nameOnServer~nameOnServer, version~version] _ OpenForRead[h, nR, wantedCreatedTime]; IF (proc = NIL) OR proc[version] THEN { ENABLE UNWIND => { IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[h, rpcH, c]; UnPinRemoteDirPath[dH]; }; which: SunNFS.DirOpArgs; which.dir _ dH.fHandle; which.name _ Rope.ToRefText[nameOnServer]; [rpcH, c] _ ObtainRPCHandleAndConversation[h]; status _ SunNFSClient.Remove[rpcH, c, which ! SunRPC.Error => ReportRPCError[code, h, file]]; ReleaseRPCHandleAndConversation[h, rpcH, c]; rpcH _ NIL; SELECT status FROM ok => NULL; ENDCASE => ReportNFSError[status, h, file]; CollectCaseFile[h, dH, DecodeVersionFromNFSName[nameOnServer, FALSE].nameWithoutVersion]; }; UnPinRemoteDirPath[dH]; }; SunNFSGetInfo: PUBLIC FSRemoteFileBackdoor.GetInfoProc -- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] -- ~ { type: SunNFS.FType; dH: RemoteDirHandle; [dH~dH, fH~, version~version, type~type, bytes~bytes, created~created] _ OpenForRead[h, CreateNameReader[file], wantedCreatedTime]; UnPinRemoteDirPath[dH]; SELECT type FROM reg => NULL; dir => ReportNFSError[isdir, h, file]; ENDCASE => ReportFSError[unknownFile, h, file, "File is special."]; }; SunNFSRename: PUBLIC FSRemoteFileBackdoor.RenameProc -- [h: ServerHandle, fromFile: ROPE, fromCreated: GMT, toFile: ROPE, proc: ConfirmProc _ NIL] -- ~ { dHFrom, dHTo: RemoteDirHandle; { ENABLE UNWIND => { IF dHFrom # NIL THEN UnPinRemoteDirPath[dHFrom]; IF dHTo # NIL THEN UnPinRemoteDirPath[dHTo]; }; nRFrom, nRTo: NameReader; fromNameOnServer, fromNameWithoutVersion, baseTo: ROPE; isPattern, createCaseFile: BOOL; fHFrom: FHandle; version, specifiedVersion, createdVersion: Version; vI: VersionInfo; nRFrom _ CreateNameReader[fromFile]; [dH~dHFrom, nameOnServer~fromNameOnServer, fH~fHFrom, version~version] _ OpenForRead[h, nRFrom, fromCreated]; IF (proc = NIL) OR proc[version] THEN { nRTo _ CreateNameReader[toFile]; dHTo _ FollowDirPath[sH~h, nR~nRTo, case~FALSE, create~TRUE]; [base~baseTo, vI~vI, version~specifiedVersion, isPattern~isPattern] _ ReadBaseComponent[nR~nRTo, case~FALSE, stripVersion~TRUE]; IF isPattern THEN ReportFSError[patternNotAllowed, h, toFile]; SELECT vI FROM bang => specifiedVersion _ FSBackdoor.highestVersion; bangH, bangNumber => NULL; ENDCASE => ReportFSError[illegalName, h, toFile, "name illegal for create."]; createdVersion _ AtomicallyRename[h, dHFrom, fHFrom, fromNameOnServer, dHTo, baseTo, specifiedVersion, FALSE]; [nameWithoutVersion~fromNameWithoutVersion] _ DecodeVersionFromNFSName[fromNameOnServer, FALSE]; CollectCaseFile[h, dHFrom, fromNameWithoutVersion]; SELECT vI FROM bangNumber => createCaseFile _ (CheckCaseFile[h, dHTo, baseTo] = NIL); ENDCASE => createCaseFile _ (createdVersion = Version[1]); IF createCaseFile THEN { ResetNameReader[nRTo, -1]; [base~baseTo] _ ReadBaseComponent[nR~nRTo, case~TRUE, stripVersion~TRUE]; CreateCaseFile[h, dHTo, baseTo ! FS.Error => CONTINUE ]; }; UnPinRemoteDirPath[dHTo]; dHTo _ NIL; }; UnPinRemoteDirPath[dHFrom]; dHFrom _ NIL; }; }; SunNFSRetrieve: PUBLIC FSRemoteFileBackdoor.RetrieveProc -- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmRetrieveProc] -- ~ { fH: FHandle; bytesExpected: CARD; str: IO.STREAM; created: GMT; openedVersion: Version; buffer: REF TEXT; offset: CARD _ 0; rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; [fH~fH, version~openedVersion, bytes~bytesExpected, created~created] _ OpenForRead[h, CreateNameReader[file], wantedCreatedTime]; str _ proc[openedVersion, bytesExpected, created]; IF str = NIL THEN RETURN; [rpcH, c] _ ObtainRPCHandleAndConversation[h]; buffer _ RefText.ObtainScratch[MIN[bytesExpected, nfsBufferBytes]]; { ENABLE UNWIND => { ReleaseRPCHandleAndConversation[h, rpcH, c]; RefText.ReleaseScratch[buffer]; }; DO bytesWanted, delta: NAT; reply: SunNFS.AttrStat; bytesWanted _ buffer.maxLength; delta _ (offset + bytesWanted) MOD serverBlocksize; IF delta < bytesWanted THEN bytesWanted _ bytesWanted - delta; reply _ SunNFSClient.Read[rpcH, c, fH, offset, bytesWanted, 0, buffer ! SunRPC.Error => ReportRPCError[code, h, file]]; SELECT reply.status FROM ok => NULL; ENDCASE => ReportNFSError[reply.status, h, file]; IO.PutBlock[str, buffer]; offset _ offset + buffer.length; IF buffer.length < bytesWanted THEN EXIT; ENDLOOP; IF offset # bytesExpected THEN ReportNFSError[io, h, file]; }; ReleaseRPCHandleAndConversation[h, rpcH, c]; RefText.ReleaseScratch[buffer]; }; SunNFSStore: PUBLIC FSRemoteFileBackdoor.StoreProc -- [h: ServerHandle, file: ROPE, str: IO.STREAM, created: GMT, proc: ConfirmProc] -- ~ { dH: RemoteDirHandle; rpcH: SunRPC.Handle; c: SunRPCAuth.Conversation; buffer: REF TEXT; { ENABLE UNWIND => { IF dH # NIL THEN UnPinRemoteDirPath[dH]; IF rpcH # NIL THEN ReleaseRPCHandleAndConversation[h, rpcH, c]; IF buffer # NIL THEN RefText.ReleaseScratch[buffer]; }; tempNameOnServer, realBase: ROPE; isPattern, createCaseFile: BOOL; nR: NameReader; createReply: SunNFS.DirOpRes; reply: SunNFS.AttrStat; offset: CARD; version, specifiedVersion: Version; vI: VersionInfo; desiredMode: CARD; nR _ CreateNameReader[file]; dH _ FollowDirPath[sH~h, nR~nR, case~FALSE, create~TRUE]; [base~realBase, vI~vI, version~specifiedVersion, isPattern~isPattern] _ ReadBaseComponent[nR~nR, case~FALSE, stripVersion~TRUE]; IF isPattern THEN ReportFSError[patternNotAllowed, h, file, "Create, pattern not allowed"]; SELECT vI FROM missing => ReportFSError[illegalName, h, file, "Create, version part required"]; bang => specifiedVersion _ FSBackdoor.highestVersion; bangH, bangNumber => NULL; ENDCASE => ReportFSError[illegalName, h, file, "Create, version part error"]; desiredMode _ GetCreateMode[h, dH]; tempNameOnServer _ CreateTempName[realBase]; [rpcH, c] _ ObtainRPCHandleAndConversation[h]; createReply _ SunNFSClient.Create[rpcH, c, [dH.fHandle, Rope.ToRefText[tempNameOnServer]], SunNFS.SAttr[ mode ~ defaultCreateMode, 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, h, file, NIL]]; SELECT createReply.status FROM ok => NULL; ENDCASE => { ReportNFSError[createReply.status, h, file, NIL]; }; buffer _ RefText.ObtainScratch[nfsBufferBytes]; offset _ 0; DO bytesRead, bytesWanted, delta: NAT; bytesWanted _ buffer.maxLength; delta _ (offset + bytesWanted) MOD serverBlocksize; IF delta < bytesWanted THEN bytesWanted _ bytesWanted - delta; bytesRead _ IO.GetBlock[str, buffer, 0, bytesWanted]; IF bytesRead = 0 THEN EXIT; reply _ SunNFSClient.Write[rpcH, c, createReply.file, offset, bytesRead, 0, buffer ! SunRPC.Error => ReportRPCError[code, h, file]]; SELECT reply.status FROM ok => NULL; ENDCASE => ReportNFSError[reply.status, h, file]; offset _ offset + bytesRead; ENDLOOP; RefText.ReleaseScratch[buffer]; buffer _ NIL; reply _ SunNFSClient.Setattr[rpcH, c, createReply.file, SunNFS.SAttr[ mode ~ desiredMode, uid~CARD.LAST, -- => no change gid~CARD.LAST, -- => no change size~CARD.LAST, -- => no change atime~[CARD.LAST, CARD.LAST], -- => no change mtime~IF created # BasicTime.nullGMT THEN SunTimeFromGMT[created] -- the specified one ELSE [CARD.LAST, CARD.LAST] -- => no change ] ! SunRPC.Error => ReportRPCError[code, h, file]]; SELECT reply.status FROM ok => NULL; ENDCASE => ReportNFSError[reply.status, h, file]; ReleaseRPCHandleAndConversation[h, rpcH, c]; rpcH _ NIL; version _ AtomicallyRename[h, dH, createReply.file, tempNameOnServer, dH, realBase, specifiedVersion, TRUE]; SELECT vI FROM bangNumber => createCaseFile _ (CheckCaseFile[h, dH, realBase] = NIL); ENDCASE => createCaseFile _ (version = Version[1]); IF createCaseFile THEN { ResetNameReader[nR, -1]; [base~realBase] _ ReadBaseComponent[nR~nR, case~TRUE, stripVersion~TRUE]; CreateCaseFile[h, dH, realBase ! FS.Error => CONTINUE ]; }; UnPinRemoteDirPath[dH]; dH _ NIL; IF proc # NIL THEN [] _ proc[version]; }; }; SunNFSDoIO: PUBLIC FSRemoteFileBackdoor.DoIOProc -- [h: ServerHandle, cmd: ATOM, arg: REF, result: REF] -- ~ { FSRemoteFileBackdoor.ErrorNotImplemented[h, NIL, "SunNFSDo"]; }; <