STPRemoteFileImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
HGM, February 8, 1984 11:55:19 pm PST
Bob Hagmann, January 16, 1987 8:40:18 am PST
Doug Wyatt, November 27, 1984 3:53:09 pm PST -- added allDown test to Info
Russ Atkinson (RRA) April 8, 1986 12:59:54 pm PST
Tim Diebert: February 11, 1986 9:39:22 am PST
Hal Murray, June 24, 1986 9:11:54 pm PDT
Demers, November 8, 1987 8:58:50 am PST
DIRECTORY
BasicTime USING [GMT, Now, nullGMT, Pack, Unpack, Unpacked],
FS USING [Error, ErrorGroup],
FSBackdoor USING [ErrorCode, ProduceError, Version],
FSName USING [BangStarFile, BangVersionFile, VersionFromRope],
FSRemoteFileBackdoor USING [DeleteProc, EnumerateForInfoProc, EnumerateForNamesProc, FSServerProcs, FSServerProcsObject, GetInfoProc, RenameProc, RetrieveProc, ServerHandle, StoreProc, viewFS],
FSReport USING [UnknownFile],
GVBasics USING [Connect],
GVNames USING [ConnectInfo, GetConnect],
IO USING [STREAM],
Process USING [CheckForAbort, Detach, EnableAborts],
RemoteFile USING [Error, GetProcsProc, GetServerProc, Register, ServerHandle, ServerObject, ServerProcs, ServerProcsObject, SweepProc, ValidateProc],
Rope USING [Cat, Concat, Equal, Fetch, Find, Length, Match, ROPE],
RuntimeError USING [UNCAUGHT],
SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQEmpty, FQNext, NewFQ],
STP USING [Close, ConfirmProcType, Continue, Create, Delete, DesiredProperties, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NoteFileProcType, Open, Rename, Retrieve, SetDesiredProperties, SetDirectory, Store, ValidProperties],
STPRemoteFile USING [Lookup, LookupResult, ServerData, ServerDataObject],
UserCredentials USING [Get];
STPRemoteFileImpl:
CEDAR
MONITOR
IMPORTS BasicTime, FS, FSBackdoor, FSName, FSRemoteFileBackdoor, FSReport, GVNames, Process, RemoteFile, Rope, RuntimeError, SafeStorage, STP, STPRemoteFile, UserCredentials
EXPORTS STPRemoteFile
~ {
Types
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
ServerData: TYPE ~ STPRemoteFile.ServerData;
ServerHandle: TYPE ~ RemoteFile.ServerHandle;
ServerObject: TYPE ~ RemoteFile.ServerObject;
STREAM: TYPE = IO.STREAM;
Version: TYPE = FSBackdoor.Version;
Parameters
myFlavor: ATOM ← $STP;
initialConnectionTTL: CARD ← 8;
upServerTTL: CARD ← 0; -- irrelevant
downServerTTL: CARD ← 30;
busyServerTTL: CARD ← 10;
credentialsErrorTTL: CARD ← 30;
maxGrapevineCache: CARDINAL = 7;
Procs to provide FS view
fsServerProcs: FSRemoteFileBackdoor.FSServerProcs ←
NEW[FSRemoteFileBackdoor.FSServerProcsObject ← [
delete~STPDelete,
enumerateForInfo~STPEnumerateForInfo,
enumerateForNames~STPEnumerateForNames,
getInfo~STPGetInfo,
rename~STPRename,
retrieve~STPRetrieve,
store~STPStore
]];
STPDelete: FSRemoteFileBackdoor.DeleteProc
-- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmProc ← NIL] -- ~ {
matchFound: BOOLEAN ← FALSE;
timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT);
stpCode: STP.ErrorCode ← noSuchFile;
stpH: STP.Handle;
Confirm:
STP.ConfirmProcType
-- [file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM] -- ~ {
info: STP.FileInfo = STP.GetFileInfo[stpH];
created: GMT = FTPTimeToGMT[info.create];
SELECT
TRUE
FROM
matchFound => answer ← abort;
(
NOT timeSearch)
OR (wantedCreatedTime = created) => {
answer ← IF (proc = NIL) OR (proc [FSName.VersionFromRope[info.version]])
THEN do ELSE abort;
matchFound ← TRUE;
};
ENDCASE => answer ← skip;
};
InnerDelete:
PROC
RETURNS [ok:
BOOL ←
TRUE] ~ {
stpH ← NIL;
{
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← GetConnection[h, file, FALSE];
STP.Delete[stpH, file, Confirm
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF
NOT matchFound
THEN {
IF timeSearch
THEN {
file ← FSName.BangStarFile[file];
STP.Delete[stpH, file, Confirm];
};
IF
NOT matchFound
THEN {
ReturnConnection[h, stpH];
stpCode ← noSuchFile;
RETURN [FALSE]};
};
};
ReturnConnection[h, stpH];
};
IF InnerDelete[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, h, file, wantedCreatedTime];
};
STPEnumerateForInfo: FSRemoteFileBackdoor.EnumerateForInfoProc
-- [h: ServerHandle, pattern: ROPE, proc: InfoProc] -- ~ {
NoteInfo: LocalEnumProc
-- [stpH: STP.Handle, file: ROPE] RETURNS [continue: BOOL] -- ~ {
info: STP.FileInfo ~ STP.GetFileInfo[stpH];
created: BasicTime.GMT ~ FTPTimeToGMT[info.create];
continue ← (proc = NIL) OR (proc[file, info.size, created]);
};
InnerEnumerate[h, pattern, NoteInfo, FALSE];
};
STPEnumerateForNames: FSRemoteFileBackdoor.EnumerateForNamesProc
-- [h: ServerHandle, pattern: ROPE, case: BOOL, proc: NameProc] -- ~ {
NoteName: LocalEnumProc -- [stpH: STP.Handle, file: ROPE] RETURNS [continue: BOOL] -- ~ { RETURN[ (proc = NIL) OR (proc[file]) ] };
InnerEnumerate[h, pattern, NoteName, TRUE];
};
LocalEnumProc: TYPE = PROC [stpH: STP.Handle, file: ROPE] RETURNS [continue: BOOL];
InnerEnumerate:
PROC [h: ServerHandle, pattern:
ROPE, note: LocalEnumProc, namesOnly:
BOOL] ~ {
This helpful procedure allows enumeration for either names or information.
stpH: STP.Handle;
InnerNote:
STP.NoteFileProcType ~ {
Process.CheckForAbort[];
RRA: this allows us to abort during an enumeration, which is non-trivial
IF
NOT Rope.Match["<*", file]
THEN file ← Rope.Concat["<>", file];
RRA: certain STP servers, like Nebula, do not prepend "<>" if they do not support directories. Since FS demands this syntax, we should make sure that it is here.
continue ← IF note[stpH, file] THEN yes ELSE no;
};
InnerBlock:
PROC ~ {
stpH ← NIL;
{
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← GetConnection[h, pattern, namesOnly];
STP.Enumerate[stpH, pattern, InnerNote];
};
ReturnConnection[h, stpH];
};
InnerBlock[
! STP.Error => ReportSTPError[code, h, pattern, BasicTime.nullGMT] ];
};
STPGetInfo: FSRemoteFileBackdoor.GetInfoProc
-- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] -- ~ {
result: STPRemoteFile.LookupResult;
[result, version, created, bytes] ← STPRemoteFile.Lookup[h, file];
SELECT result
FROM
ok => {
A response that the file was present.
IF wantedCreatedTime = BasicTime.nullGMT
THEN
RETURN;
We were requested to find any date, so the one we got was OK.
IF wantedCreatedTime = created
THEN
RETURN;
We were asked for a specific date, and we got it, so its all OK.
};
noSuchFile => {
A positive response that the named file was not present on the named server.
IF wantedCreatedTime = BasicTime.nullGMT
THEN ReportSTPError[noSuchFile, h, file, wantedCreatedTime];
};
ENDCASE => NULL;
This call is for the general case. It is more expensive, of course, since we have to grab a connection for it. However, STPInfo can handle bogus version # hints.
[version, bytes, created] ← STPInfoIgnoringVersionHint[h, file, wantedCreatedTime];
};
STPInfoIgnoringVersionHint:
PROC [h: ServerHandle, file:
ROPE, wantedCreatedTime:
GMT]
RETURNS [version: Version, bytes:
INT, created:
GMT] = {
stpH: STP.Handle ← NIL;
Note:
STP.NoteFileProcType
-- [file: ROPE] RETURNS [continue: Continue] -- ~ {
info: STP.FileInfo = STP.GetFileInfo[stpH];
created ← FTPTimeToGMT[info.create];
IF
NOT timeSearch
OR wantedCreatedTime = created
THEN {
version ← FSName.VersionFromRope[info.version];
bytes ← info.size;
matchFound ← TRUE;
If version part was missing from client's file name, then Enumerate will produce all versions, but we only want the !H version. This works only because the server enumerates by increasing order of version number ...
continue ← IF timeSearch THEN no ELSE yes;
}
ELSE continue ← yes;
};
matchFound: BOOL ← FALSE;
timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT);
stpCode: STP.ErrorCode ← noSuchFile;
InnerSTPInfo:
PROC
RETURNS [found:
BOOL ←
TRUE] ~ {
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← NIL;
stpH ← GetConnection[h, file, FALSE];
STP.Enumerate[stpH, file, Note
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF
NOT matchFound
THEN {
IF timeSearch
THEN {
file ← FSName.BangStarFile[file];
STP.Enumerate[stpH, file, Note];
};
IF NOT matchFound THEN {stpCode ← noSuchFile; found ← FALSE};
};
ReturnConnection[h, stpH];
};
IF InnerSTPInfo[ ! STP.Error => { stpCode ← code; CONTINUE }] THEN RETURN;
ReportSTPError[stpCode, h, file, wantedCreatedTime];
};
STPRename: FSRemoteFileBackdoor.RenameProc
-- [h: ServerHandle, fromFile: ROPE, fromCreated: GMT, toFile: ROPE, proc: ConfirmProc ← NIL] -- ~ {
stpCode: STP.ErrorCode ← noSuchFile;
version: Version;
InnerRename:
PROC
RETURNS [ok:
BOOL ←
TRUE] ~ {
stpH: STP.Handle ← NIL;
{
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← GetConnection[h, fromFile, TRUE];
fromFile ← FSName.BangVersionFile[fromFile, version];
STP.Rename[stpH, fromFile, toFile];
};
ReturnConnection[h, stpH];
};
version ← STPGetInfo[h, fromFile, fromCreated].version;
IF (proc =
NIL)
OR (proc[version])
THEN {
IF InnerRename[! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, h, toFile, BasicTime.nullGMT];
};
};
STPRetrieve: FSRemoteFileBackdoor.RetrieveProc
-- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmRetrieveProc] -- ~ {
matchFound: BOOL ← FALSE;
timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT);
stpH: STP.Handle ← NIL;
stpCode: STP.ErrorCode ← noSuchFile;
Confirm:
STP.ConfirmProcType ~ {
[file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM]
info: STP.FileInfo = STP.GetFileInfo[stpH];
created: GMT = FTPTimeToGMT[info.create];
version: Version ← FSName.VersionFromRope[info.version];
SELECT
TRUE
FROM
matchFound => answer ← abort;
(
NOT timeSearch)
OR (wantedCreatedTime = created) => {
localStream ← proc[version, info.size, created];
answer ← IF localStream = NIL THEN abort ELSE do;
matchFound ← TRUE;
};
ENDCASE => answer ← skip;
};
InnerRetrieve:
PROC
RETURNS [success:
BOOL ←
TRUE] ~ {
stpH ← NIL;
{
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← GetConnection[h, file, FALSE];
STP.Retrieve[stpH, file, Confirm
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF
NOT matchFound
THEN {
IF timeSearch
THEN {
file ← FSName.BangStarFile[file];
STP.Retrieve[stpH, file, Confirm];
};
IF NOT matchFound THEN {stpCode ← noSuchFile; success ← FALSE};
};
};
ReturnConnection[h, stpH];
};
IF InnerRetrieve[ ! STP.Error => { stpCode ← code; CONTINUE }] THEN RETURN;
ReportSTPError[stpCode, h, file, wantedCreatedTime];
};
STPStore: FSRemoteFileBackdoor.StoreProc
-- [h: ServerHandle, file: ROPE, case: BOOL, str: STREAM, created: GMT, proc: ConfirmProc] -- ~ {
stpH: STP.Handle ← NIL;
stpCode: STP.ErrorCode;
Note:
STP.NoteFileProcType
-- [file: ROPE] RETURNS [continue: Continue] -- ~ {
IF proc = NIL THEN RETURN [yes];
RETURN[ IF proc[FSName.VersionFromRope[STP.GetFileInfo[stpH].version]] THEN yes ELSE no ];
};
InnerStore:
PROC
RETURNS [ok:
BOOL ←
TRUE] ~ {
stpH ← NIL;
{
ENABLE UNWIND => IF stpH # NIL THEN ReturnConnection[h, stpH];
stpH ← GetConnection[h, file, TRUE];
STP.Store[stpH, file, str, Note, binary, created]; -- Historically the type was specified as "unknown", causing STP to read the file trying to figure out the type, then read it *again* to store it. We don't want the servers to do any translation processing (even on text files), and we always set the type of a retrieved file to FS.tUnspecified, so storing everything as binary is okay. (ajd+chh)
};
ReturnConnection[h, stpH];
};
stpCode ← noSuchFile;
IF InnerStore[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, h, file, BasicTime.nullGMT];
};
Time conversion
FTPTimeToGMT:
PUBLIC
PROC [t: Rope.
ROPE]
RETURNS [time: BasicTime.
GMT ← BasicTime.nullGMT] ~ {
adjust: NAT ← 0;
Digit:
PROC [index:
INT]
RETURNS [
CARDINAL] = {
c: CHAR = Rope.Fetch[t, index-adjust];
SELECT c
FROM
' => RETURN [0];
IN ['0..'9] => RETURN [c-'0];
ENDCASE => ERROR BadTime;
};
DoubleDigit:
PROC [index:
INT]
RETURNS [
CARDINAL] = {
RETURN [Digit[index]*10 + Digit[index+1]];
};
Char:
PROC [index:
INT]
RETURNS [
CHAR] = {
c: CHAR = Rope.Fetch[t, index];
SELECT c
FROM
'-, '+, ' , IN ['a..'z], IN ['0..'9] => RETURN [c];
IN ['A..'Z] => RETURN [c + ('a-'A)];
ENDCASE => ERROR BadTime;
};
Inner:
PROC = {
uT: BasicTime.Unpacked;
yr: CARDINAL = DoubleDigit[7];
SELECT yr
FROM
< 50 => uT.year ← yr + 2000;
ENDCASE => uT.year ← yr + 1900;
IF Char[1]
IN ['0..'9]
THEN uT.day ← DoubleDigit[0]
ELSE {
In this case we have a single-character day of month with no leading blank. We accept this number, as well as adjusting the indexing.
uT.day ← Digit[0];
adjust ← 1};
uT.day ← DoubleDigit[0];
uT.hour ← DoubleDigit[10];
uT.minute ← DoubleDigit[13];
uT.second ← DoubleDigit[16];
uT.month ←
SELECT Char[3]
FROM
'j => IF Char[4] = 'a THEN January ELSE IF Char[5] = 'n THEN June ELSE July,
'f => February,
'm => IF Char[5] = 'r THEN March ELSE May,
'a => IF Char[4] = 'p THEN April ELSE August,
's => September,
'o => October,
'n => November,
'd => December,
ENDCASE => ERROR BadTime;
IF t.Length[] < 19
THEN {
Yetch, FTPServer on AltoGateway does this. Use local zone info.
temp: BasicTime.Unpacked ← BasicTime.Unpack[BasicTime.Now[]];
uT.zone ← temp.zone;
uT.dst ← temp.dst;
}
ELSE {
uT.zone ← 60 * (
SELECT Char[19]
FROM
'g => 0,
'e => 5,
'c => 6,
'm => 7,
'p => 8,
'+ => Digit[20],
'- => - Digit[20],
ENDCASE => ERROR BadTime) ;
uT.dst ← IF (Char[20] = 'd) THEN yes ELSE no;
};
time ← BasicTime.Pack[uT];
};
Inner[ ! RuntimeError.UNCAUGHT => CONTINUE];
};
BadTime: ERROR = CODE;
Registered with RemoteFile
myServerProcs: RemoteFile.ServerProcs ←
NEW[RemoteFile.ServerProcsObject ← [
sweep~STPSweep,
validate~STPValidate,
getProcs~STPGetProcs]];
STPGetServer: RemoteFile.GetServerProc
-- [server: ROPE] RETURNS [h: ServerHandle, downMsg: ROPE ← NIL] -- ~ {
Returns [NIL, NIL] if server doesn't exist.
Returns [NIL, msg] if server exists but is down.
serverPupName: ROPE;
data: ServerData;
stpH: STP.Handle;
stpErrorCode: STP.ErrorCode;
opened: BOOL ← FALSE;
IF (serverPupName ← GetServerPupName[server]) = NIL THEN RETURN [NIL, NIL];
{
ENABLE STP.Error => { stpErrorCode ← code; CONTINUE };
user, password: ROPE;
stpH ← STP.Create[];
[user, password] ← UserCredentials.Get[];
STP.Login[stpH, user, password];
[] ← STP.Open[stpH, serverPupName];
opened ← TRUE;
};
IF
NOT opened
THEN
SELECT stpErrorCode
FROM
noSuchHost, noNameLookupResponse => RETURN [NIL, NIL];
ENDCASE => RETURN[NIL, "can't connect"];
data ← NEW[STPRemoteFile.ServerDataObject ← [ttl~upServerTTL, downMsg~NIL, connectionTTL~initialConnectionTTL, stpH~stpH, serverPupName~serverPupName]];
SafeStorage.EnableFinalization[data];
h ← NEW[ServerObject ← [flavor~myFlavor, name~server, procs~myServerProcs, data~data]];
RETURN [h, NIL];
};
GetConnection:
PROC [h: ServerHandle, pattern:
ROPE, justNames:
BOOL]
RETURNS [stpH:
STP.Handle] ~ {
data: ServerData ~ NARROW[h.data];
GetConnectionInner:
ENTRY
PROC ~
--INLINE-- {
stpH ← data.stpH; data.stpH ← NIL;
};
GetConnectionInner[];
IF stpH =
NIL
THEN {
user, password: ROPE;
stpH ← STP.Create[];
[user, password] ← UserCredentials.Get[];
STP.Login[stpH, user, password];
};
IF
NOT
STP.IsOpen[stpH]
THEN {
[] ← STP.Open[stpH, data.serverPupName];
};
ConditionConnection[stpH, pattern, justNames];
};
ReturnConnection:
PROC [h: ServerHandle, stpH:
STP.Handle] ~ {
data: ServerData ~ NARROW[h.data];
ReturnConnectionInner:
ENTRY
PROC ~
--INLINE-- {
ENABLE UNWIND => NULL;
IF data.stpH = NIL THEN { data.stpH ← stpH; stpH ← NIL };
data.connectionTTL ← initialConnectionTTL;
};
ReturnConnectionInner[];
IF stpH #
NIL
THEN {
STP.Close[stpH ! STP.Error => CONTINUE ];
};
};
STPValidate:
ENTRY RemoteFile.ValidateProc
-- [h: ServerHandle] RETURNS [obsolete: BOOL, down: BOOL] -- ~ {
data: ServerData ~ NARROW[h.data];
IF (data.ttl = 0) AND data.downMsg # NIL THEN RETURN [TRUE, data.downMsg];
RETURN [FALSE, data.downMsg];
};
SetServerDown:
ENTRY
PROC [h: ServerHandle, seconds:
CARD] ~ {
ENABLE UNWIND => NULL;
data: ServerData ~ NARROW[h.data];
data.downMsg ← "got STP error";
data.ttl ← seconds;
};
dfq: SafeStorage.FinalizationQueue ~ SafeStorage.NewFQ[20];
STPSweep: RemoteFile.SweepProc
-- [h: ServerHandle, seconds: CARD] -- ~ {
data: ServerData ~ NARROW[h.data];
stpH: STP.Handle ← NIL;
STPSweepInner:
ENTRY
PROC ~
--INLINE-- {
ENABLE UNWIND => NULL;
IF data.ttl > seconds THEN data.ttl ← data.ttl - seconds ELSE data.ttl ← 0;
IF data.connectionTTL > seconds
THEN data.connectionTTL ← data.connectionTTL - seconds
ELSE {
IF data.connectionTTL > 0 THEN stpH ← data.stpH;
data.stpH ← NIL;
data.connectionTTL ← 0;
};
};
STPSweepInner[];
IF (stpH # NIL) AND (STP.IsOpen[stpH]) THEN STP.Close[stpH ! STP.Error => CONTINUE];
Finalization ...
WHILE
NOT SafeStorage.FQEmpty[dfq]
DO
droppedData: ServerData ~ NARROW[SafeStorage.FQNext[dfq]];
IF ((stpH ← droppedData.stpH) #
NIL)
AND (
STP.IsOpen[stpH])
THEN STP.Close[stpH ! STP.Error => CONTINUE];
ENDLOOP;
};
STPGetProcs: RemoteFile.GetProcsProc
-- [h: ServerHandle, view: ATOM] RETURNS [procs: REF] -- ~ {
SELECT view
FROM
FSRemoteFileBackdoor.viewFS => procs ← fsServerProcs;
ENDCASE => ERROR RemoteFile.Error[$notImplemented, "view not implemented"];
};
Grapevine Cache
This is PUBLIC for use by e.g. STPServer. It's a FIFO cache from which nothing ever times out ... so if we ever get back a wrong answer from Grapevine the only way to flush it is to look up a lot (maxGrapevineCache) of different names.
GrapevineCacheEntry:
TYPE =
RECORD[
name: ROPE ← NIL,
connect: GVBasics.Connect
];
GrapevineCacheArray: TYPE = ARRAY [0..maxGrapevineCache) OF GrapevineCacheEntry;
grapevineCache: REF GrapevineCacheArray ← NEW[GrapevineCacheArray];
grapevineCachePut: CARDINAL ← 0; -- next victim
FindInGrapevineCache:
ENTRY
PROC [name:
ROPE]
RETURNS [found:
BOOL ←
FALSE, connect: GVBasics.Connect ←
NIL] = {
ENABLE UNWIND => NULL;
FOR i:
CARDINAL
IN [0..maxGrapevineCache)
DO
IF Rope.Equal[name, grapevineCache[i].name, FALSE] THEN RETURN[TRUE, grapevineCache[i].connect];
ENDLOOP;
};
AddToGrapevineCache:
ENTRY
PROC [name:
ROPE, connect: GVBasics.Connect] = {
ENABLE UNWIND => NULL;
grapevineCache[grapevineCachePut].name ← name;
grapevineCache[grapevineCachePut].connect ← connect;
grapevineCachePut ← (IF grapevineCachePut < (maxGrapevineCache-1) THEN grapevineCachePut+1 ELSE 0);
};
The Detach-FORK below is to protect GVNames.GetConnect from being ABORTed
GetConnectResult: TYPE ~ REF GetConnectResultObject;
GetConnectResultObject:
TYPE ~
RECORD [
done: BOOL ← FALSE,
waitForDone: CONDITION,
info: GVNames.ConnectInfo,
connect: GVBasics.Connect
];
WaitForDone:
ENTRY
PROC [r: GetConnectResult] ~ {
ENABLE UNWIND => NULL;
WHILE NOT r.done DO WAIT r.waitForDone ENDLOOP;
};
NotifyDone:
ENTRY
PROC [r: GetConnectResult] ~ {
ENABLE UNWIND => NULL;
r.done ← TRUE; NOTIFY r.waitForDone;
};
GetServerPupName:
PUBLIC
PROC [server:
ROPE]
RETURNS [pupServer:
ROPE] ~ {
IF Rope.Find[server, ".", 0] < 0 THEN RETURN [server];
Names with "." are GVNames (Grapevine names), so ask Grapevine to look them up
{
foundInCache: BOOL;
connect: GVBasics.Connect;
r: GetConnectResult;
[foundInCache, connect] ← FindInGrapevineCache[server];
IF foundInCache THEN RETURN[connect];
r ← NEW[GetConnectResultObject];
TRUSTED {
Process.EnableAborts[@r.waitForDone];
Process.Detach[FORK DoGetConnect[server, r]]; -- sets r.info, r.connect
};
WaitForDone[r];
If successful, use the connect as the server name for STP.Open ...
IF (r.info = group)
OR (r.info = individual)
THEN {
AddToGrapevineCache[name~server, connect~r.connect];
RETURN[r.connect];
};
};
};
DoGetConnect:
PROC [server:
ROPE, r: GetConnectResult] ~ {
[r.info, r.connect] ← GVNames.GetConnect[server];
NotifyDone[r];
};
Property stuff
MyDesiredProperties: TYPE = PACKED ARRAY STP.ValidProperties OF BoolDefaultFalse;
BoolDefaultFalse: TYPE = BOOL ← FALSE;
namesOnly: MyDesiredProperties = [directory: TRUE, nameBody: TRUE, version: TRUE];
nameSizeCreated: MyDesiredProperties = [
directory: TRUE, nameBody: TRUE, version: TRUE, createDate: TRUE, size: TRUE];
all: STP.DesiredProperties = ALL[TRUE]; -- stops sending of Desired-Property properties
ConditionConnection:
PROC[h:
STP.Handle, nameBody:
ROPE, justNames:
BOOL] = {
IF
NOT Rope.Match["<*", nameBody]
THEN {
no directory, so turn off directory defaulting and Desired-Property properties
STP.SetDirectory[h, " "];
STP.SetDesiredProperties[h, all];
}
ELSE {
STP.SetDirectory[h, NIL];
STP.SetDesiredProperties[h, IF justNames THEN namesOnly ELSE nameSizeCreated];
};
};
Internal procedures
ReportSTPError:
PROC [stpCode:
STP.ErrorCode, h: ServerHandle, file:
ROPE, time:
GMT] ~ {
gName: ROPE ← Rope.Cat["[", h.name, "]", file];
quotedGName: ROPE ← Rope.Cat["\"", gName, "\""];
OldError:
PROC [code: FSBackdoor.ErrorCode, e1, e2:
ROPE, downTime:
CARD] ~ {
IF downTime > 0 THEN SetServerDown[h, downTime];
FSBackdoor.ProduceError[code, Rope.Cat[(IF e1 # NIL THEN e1 ELSE "Server for "), quotedGName, e2]];
};
NewError:
PROC [group:
FS.ErrorGroup, code:
ATOM, e1, e2:
ROPE, downTime:
CARD] ~ {
IF downTime > 0 THEN SetServerDown[h, downTime];
ERROR FS.Error[[group, code, Rope.Cat[(IF e1 # NIL THEN e1 ELSE "Server for "), quotedGName, e2]]];
};
SELECT stpCode
FROM
noRouteToNetwork, noNameLookupResponse =>
OldError[serverInaccessible, NIL, " is inaccessible", downServerTTL];
connectionClosed =>
OldError[wentOffline, NIL, " connection closed unexpectedly (wentOffline)", downServerTTL];
connectionRejected =>
OldError[connectionRejected, NIL, " rejected the connection attempt", busyServerTTL];
connectionTimedOut =>
OldError[connectionTimedOut, NIL, " timed-out the connection", 0];
accessDenied =>
OldError[accessDenied, NIL, " denied file access permission", 0];
requestRefused =>
OldError[quotaExceeded, "Request refused (possibily no quota for storing) for ", NIL, 0];
accessError =>
OldError[fileBusy, "File ", " is locked on the server", 0];
illegalUserName =>
OldError[badCredentials, "Credentials rejected when accessing ", NIL, credentialsErrorTTL];
illegalFileName =>
OldError[illegalName, NIL, " says that the file name is illegal", 0];
noSuchHost =>
OldError[unknownServer, "Couldn't find the server for ", NIL, downServerTTL];
alreadyAConnection =>
NewError[bug, $alreadyAConnection, NIL, " already had a connection", 0];
noConnection =>
NewError[bug, $noConnection, NIL, " gave a noConnection error", 0];
illegalUserPassword =>
NewError[environment, $illegalUserPassword, NIL, " had an illegal user password", credentialsErrorTTL];
illegalUserAccount =>
NewError[environment, $illegalUserAccount, NIL, " had an illegal user account", credentialsErrorTTL];
illegalConnectName =>
NewError[environment, $illegalConnectName, NIL, " had an illegal connect name", credentialsErrorTTL];
illegalConnectPassword =>
NewError[environment, $illegalConnectPassword, NIL, " had an illegal connect password", credentialsErrorTTL];
credentailsMissing =>
NewError[environment, $credentailsMissing, NIL, " had missing credentails", credentialsErrorTTL];
protocolError =>
NewError[bug, $protocolError, NIL, " gave a protocol error to STP", 0];
noSuchFile => {
FSReport.UnknownFile[gName, time]; -- raises FS.Error
};
undefinedError =>
NewError[bug, $undefinedError, NIL, " gave STP an undefinedError", 0];
ENDCASE => ERROR;
};
Initialization
SafeStorage.EstablishFinalization[type~CODE[STPRemoteFile.ServerDataObject], npr~0, fq~dfq];
RemoteFile.Register[myFlavor, STPGetServer];
}.