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: BOOLEANFALSE;
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: BOOLTRUE] ~ {
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: BOOLFALSE;
timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT);
stpCode: STP.ErrorCode ← noSuchFile;
InnerSTPInfo: PROC RETURNS [found: BOOLTRUE] ~ {
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: BOOLTRUE] ~ {
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: BOOLFALSE;
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: BOOLTRUE] ~ {
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: BOOLTRUE] ~ {
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: BOOLFALSE;
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: ROPENIL,
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: BOOLFALSE, 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: BOOLFALSE,
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 = BOOLFALSE;
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];
}.
Bob Hagmann April 29, 1985 9:12:49 am PDT
changes to: DIRECTORY, GetConnection
Bob Hagmann April 29, 1985 10:03:03 am PDT
changes to: DIRECTORY, GetConnection, GetServerPupName, LookForConnection, FSRemoteFileImpl
Bob Hagmann May 6, 1985 4:13:27 pm PDT
changes to: InnerEnumerate
Bob Hagmann August 2, 1985 3:25:38 pm PDT
changes to: maxSlots, TimeOut, GetServerPupName, FindInGrapevineCache, AddToGrapevineCache, LookForConnection, ForTimeout, GrapevineCacheEntry, maxGVineCache, grapevineCachePut, grapevineCache, GetConnection
Bob Hagmann August 2, 1985 4:37:37 pm PDT
changes to: grapevineCachePut
Demers, October 3, 1987 5:07:19 pm PDT
Reworked for new FSRemoteFileBackdoor registration mechanism (multiple file server protocols).
changes to: nearly everything, sigh.