SunNFSRemoteFileImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Demers, November 8, 1987 10:48:27 am PST
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;
Copied Types
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;
Parameters
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;
File lookup and creation
LookupSimple: PROC [sH: ServerHandle, dH: RemoteDirHandle, nameOnServer: ROPE]
RETURNS [fH: FHandle, type: FType, bytes: CARD, created: GMT]
~ {
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.
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]
~ {
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.
found: BOOLFALSE;
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: BOOLFALSE] ~ {
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: BOOLFALSE] ~ {
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] ~ {
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.
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: ROPENIL;
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: ROPENIL] ~ {
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: BOOLFALSE;
caseFileNames: LIST OF ROPENIL;
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;
};
};
Registered with FSRemoteFile
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];
};
};
Time Conversion
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]];
};
}...