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, GetInfoProc, RenameProc, RetrieveProc, StoreProc], IO USING [GetBlock, PutBlock, PutFR, STREAM], RefText USING [ObtainScratch, ReleaseScratch], RemoteFile USING [ServerHandle], 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], SunNFSRemoteFile 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] ; SunNFSRemoteFileImpl: CEDAR PROGRAM IMPORTS ArpaExtras, Basics, BasicTime, FS, FSName, IO, RefText, Rope, SunNFSClient, SunNFSRemoteFile, SunRPC EXPORTS SunNFSRemoteFile ~ { OPEN SunNFSRemoteFile; 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 ~ RemoteFile.ServerHandle; 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; defaultCaseFileMode: CARD _ 0100444B; 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 ~ defaultCaseFileMode, 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]; }; }; GMTFromSunTime: PUBLIC PROC [sunTime: SunNFS.TimeVal] RETURNS [gmt: GMT] ~ { RETURN [BasicTime.Update[sunEpoch, INT[sunTime.seconds]]]; }; SunTimeFromGMT: PUBLIC PROC [gmt: GMT] RETURNS [sunTime: SunNFS.TimeVal] ~ { RETURN [ [seconds~BasicTime.Period[from~sunEpoch, to~gmt], useconds~0] ]; }; CompareSunTimes: PUBLIC 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]]; }; }... ΜSunNFSRemoteFileImpl.mesa Copyright Σ 1987 by Xerox Corporation. All rights reserved. Demers, November 9, 1987 12:35:34 pm PST Copied Types Parameters File lookup and creation Return info for named file if it's present and we can stat it successfully. Return fH=NIL if named file is nonexistent. Raise FS.Error otherwise. Return info for file if it's present and we can stat it successfully. Return fH=NIL if no file exists with given name and created time. Raise FS.Error for other errors. Determine a version number for to file; rename the from file to that version. If "deleteIfError" is TRUE, the "from" file will disappear even if the rename operation fails. Registered with FSRemoteFile Time Conversion Κd˜codešœ™K™<™(K˜——šΟk ˜ Kšœœ ˜Kšœ œ ˜Kšœœ˜'Kšœ œœ&˜WKšœœ ˜Kšœ œ5˜EKšœœ˜Kšœœ@˜ZKšœœœ˜-Kšœœ!˜.Kšœ œ˜ Kšœœœ ˜4KšœœZ˜fKšœ œ6˜HKšœœš˜°Kšœœ˜Kšœ œ˜Kšœ˜K˜—šΟnœœ˜#Kšœ œ œ7˜lKšœ˜K˜Kšœ˜head™ Kšœ œ˜Kšœœ˜Kšœœ œ˜Kšœ œœ˜(Kšœ œœ˜(Kšœœœ˜,Kšœœœ˜Kšœ œœ˜(Kšœœ˜-Kšœ œ˜#—™ Kšœœ˜$Kšœœ˜+Kšœœ Οc7˜Xšœ œ˜K˜MK˜—Kšœœ ˜#Kšœœ ˜%Kšœœ ˜Kšœœ ˜Kšœœ˜6Kšœœ˜Kšœ œ˜—™šž œœ7œ˜NKšœ#œ œ˜=Kšœ˜K™KK™+K™K˜KšœB˜Bšœ˜˜K˜K˜K˜K˜1K˜—šœ ˜ Kšœœ˜ K˜ K˜ K˜ K˜—Kšœ3˜:—K˜K˜—šžœœ/œœ˜lKšœœ(œ˜UK˜K™EK™AK™ K˜Kšœœœ˜šž œŸ7œ˜ZKšœœ˜KšœI˜Išœ+œœ"œ˜^Kšœ œ˜ KšœS˜Sšœœœœ˜6Kšœ˜Kšœœ˜ Kšœœ˜K˜—K˜—K˜—Kšœ:˜:Kšœ)œ˜0Kš œœœœœ&˜CK˜K˜—šžœœ/œ œ˜[Kšœ)œ œœ˜Nšž œŸ7œ˜ZKšœœ˜K˜KšœI˜Išœ+œœ"œ˜^Kšœœ Ÿ,˜Fšœœ˜Kšœœ˜ Kšœ˜Kšœ˜K˜—K˜—K˜—Kšœ˜Kšœ:˜:Kšœ2˜2K˜K˜—šžœœ/œ œ˜ZKšœ(œ œœ˜Mšž œŸ7œ˜ZKšœœ˜K˜KšœI˜Išœ+œœ"œ˜^Kšœœ Ÿ,˜Fšœœ˜Kšœœ˜ Kšœ˜Kšœ˜K˜—K˜—K˜—Kšœœœ˜ Kšœ:˜:Kšœ2˜2K˜K˜—šž œœ7œœ2œ(œ œ˜ΙKšœœ˜ ˜šœœ˜Kšœœœ˜(K˜—Kšœ œ˜Kšœ˜Kšœœ˜ K˜Kšœ&œ œ˜;KšœYœœ˜sKšœ œ,˜=šœ˜šœ ˜ KšœK˜KK˜—˜KšœBœ˜IKšœœœœ˜K˜—˜ KšœAœ˜HKšœœœœ˜K˜—˜ Kšœ)œJ˜yKšœœ˜K˜—˜Kšœ5˜5Kšœœ˜ K˜—Kšœ2˜9—KšœœA˜Nšœ)œœœ˜\Kšœœ˜—Kšœg˜gKšœŸ˜:š˜Kšœœ˜ —K˜—Kšœ˜šœœœ˜Kšœ%˜%K˜—šœ œ˜Kšœ*˜*K˜—K˜K˜—š žœœPœ!œ*œ˜ΎKšœ˜K™MK™^K˜K˜Kšœ˜Kšœœ˜Kšœœœ$˜=Kšœœœ˜˜šœœ˜Kšœœœ.˜@Kšœœ-˜BK˜—K˜/šœ˜Kšœœ˜"šœ4˜4Kšœœ˜ Kšœœ˜Kšœ`œ˜fšœœ ˜KšœœH˜aKšœœœœ+˜[K˜Kšœ9˜9Kšœ/˜/šœI˜IKšœ4˜4—šœ˜Kšœœ˜ Kšœ ˜Kšœ'˜.—Kšœ˜—šœG˜GKšœ,˜,—šœ˜Kšœœ˜ Kšœ'˜.—K˜—šœ˜ Kšœ˜Kšœ9˜9Kšœ/˜/šœf˜fKšœ4˜4—šœ˜Kšœœ˜ Kšœ'˜.—K˜——Kšœ5œ˜9K˜—K˜K˜—šžœœ/œœ˜PK˜K˜šœ˜šœ˜"Kšœœœ.˜@Kšœ˜ K˜—J˜/J˜6Kšœ-˜-K˜—K˜K˜—Kšœœœ˜K˜š žœœœœ œ˜>šœœœ˜K˜)Kšœp˜pK˜—Kš œ œ5œœœœ˜žK˜K˜—Kšœœœ ˜,Kšœœœ#˜DK˜šžœ œ/œ˜SK˜K˜Kšœœ)˜;šœ˜šœ˜"Kšœœœ.˜@Kšœ˜ K˜—J˜/šœ!˜!Kšœ+˜+šœ ˜ Kšœ˜KšœœœŸ ˜KšœœœŸ ˜KšœœœŸ ˜Kš œœœœœŸ ˜+Kš œœœœœŸ ˜*Kšœ˜—Kšœ˜—Kšœ5œ˜9K˜—K˜K˜—š ž œœ/œœœœ˜nKšœœ˜šž œŸ7œ˜Zšœ-œœ˜œ˜YK˜—Kšœ˜K˜K˜—šž œœ"Ÿpœ˜«K˜K˜Kšœƒ˜ƒJšœ˜šœ˜Jšœœ˜ Jšœ&˜&Jšœ<˜C—K˜K˜—šž œœ!Ÿ`œ˜™Kšœ˜˜šœœ˜Kšœ œœ˜0Kšœœœ˜,K˜—K˜Kšœ2œ˜7Kšœœ˜ K˜K˜3K˜Kšœ$˜$Kšœm˜mšœ œœœ˜'Kšœ ˜ Kšœ)œ œ˜=Kšœfœœ˜€Kšœ œ-˜>šœ˜Kšœ5˜5Kšœœ˜KšœF˜M—Kšœgœ˜nKšœYœ˜`Kšœ3˜3šœ˜KšœAœ˜FKšœ3˜:—šœœ˜Kšœ˜Kšœ0œœ˜IKšœ!œ œ˜8K˜—Kšœ!œ˜%K˜—Kšœ%œ˜)K˜—K˜K˜—šžœœ#ŸVœ˜“K˜ Kšœœ˜Kšœœœ˜Kšœ œ˜ K˜Kšœœœ˜Kšœœ˜K˜K˜K˜Kšœ˜Kšœ2˜2Kšœœœœ˜K˜.Kšœœ!˜C˜šœœ˜Kšœ,˜,Kšœ˜K˜—š˜Kšœœ˜Kšœ˜Kšœ˜Kšœœ˜3Kšœœ#˜>Kšœw˜wšœ˜Kšœœ˜ Kšœ*˜1—Kšœ˜K˜ Kšœœœ˜)Kšœ˜—Kšœœ˜;K˜—K˜,K˜K˜K˜—šž œœ ŸTœ˜‹Kšœ˜K˜K˜Kšœœœ˜K˜˜šœœ˜Kšœœœ˜(Kšœœœ-˜?Kšœ œœ ˜4K˜—Kšœœ˜!Kšœœ˜ Kšœ˜K˜K˜Kšœœ˜ K˜#K˜Kšœ œ˜Kšœ˜Kšœ%œ œ˜9Kšœfœœ˜€Kšœ œJ˜[šœ˜JšœP˜PJšœ5˜5Jšœœ˜JšœF˜M—Kšœ#˜#K˜,K˜.šœ*˜*Kšœ/˜/šœ ˜ Kšœ˜KšœœœŸ ˜KšœœœŸ ˜KšœœœŸ ˜Kš œœœœœŸ ˜+Kš œœœœœŸ ˜*K˜—Kšœ0œ˜6—šœ˜Kšœœ˜ šœ˜ Kšœ,œ˜1K˜——Kšœ/˜/Kšœ ˜ š˜Kšœœ˜#Kšœ˜Kšœœ˜3Kšœœ#˜>Kšœ œ'˜5Kšœœœ˜Kšœ„˜„šœ˜Kšœœ˜ Kšœ*˜1—Kšœ˜Kšœ˜—Kšœ)œ˜-šœ7˜7šœ ˜ Kšœ˜KšœœœŸ˜KšœœœŸ˜KšœœœŸ˜Kš œœœœœŸ˜-šœœ˜$KšœŸ˜1Kš œœœœœŸ˜+—K˜—K˜1—šœ˜Kšœœ˜ Kšœ*˜1—Kšœ4œ˜8Kšœfœ˜lšœ˜KšœAœ˜FKšœ,˜3—šœœ˜Kšœ˜Kšœ0œœ˜IKšœ!œ œ˜8K˜—Kšœœ˜!Kšœœœ˜&K˜—K˜——™š žœœœœœ˜LKšœœ˜:K˜K˜—š žœœœœœ˜LKšœC˜IK˜K˜—šžœœœœ˜Ušœœ ˜Kšœ˜Kšœ˜Kšœ2˜9—K˜——K˜—J˜—…—R(jX