STPFSRemoteFileImpl.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, October 28, 1987 9:05:35 pm PST
DIRECTORY
BasicTime USING [GMT, Now, nullGMT, Pack, Unpack, Unpacked],
FS USING [Error, ErrorGroup, FileType, tUnspecified],
FSBackdoor USING [ErrorCode, ProduceError, Version],
FSName USING [BangStarFile, BangVersionFile, VersionFromRope],
FSRemoteFile USING [],
FSRemoteFileBackdoor USING [CloseProc, DeleteProc, DoProc, EnumerateForInfoProc, EnumerateForNamesProc, ErrorNotImplemented, GetInfoProc, GetServerProc, OpenProc, Register, RenameProc, RetrieveProc, RWProc, ServerHandle, ServerObject, ServerProcs, ServerProcsObject, StoreProc, SweepProc, ValidateProc],
FSReport USING [UnknownFile],
GVBasics USING [Connect],
GVNames USING [ConnectInfo, GetConnect],
IO USING [STREAM],
Process USING [CheckForAbort, Detach, EnableAborts],
Rope USING [Cat, Concat, Equal, Fetch, Find, FindBackward, 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],
STPFSRemoteFile USING [Lookup, LookupResult, ServerData, ServerDataObject],
UserCredentials USING [Get];
STPFSRemoteFileImpl: CEDAR MONITOR
IMPORTS BasicTime, FS, FSBackdoor, FSName, FSRemoteFileBackdoor, FSReport, GVNames, Process, Rope, RuntimeError, SafeStorage, STP, STPFSRemoteFile, UserCredentials
EXPORTS FSRemoteFile -- ought to be removed --, STPFSRemoteFile
~ {
Types
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
ServerData: TYPE ~ STPFSRemoteFile.ServerData;
ServerHandle: TYPE ~ FSRemoteFileBackdoor.ServerHandle;
ServerObject: TYPE ~ FSRemoteFileBackdoor.ServerObject;
STREAM: TYPE = IO.STREAM;
Version: TYPE = FSBackdoor.Version;
Parameters
myFlavor: ATOM ← $STP;
myServerProcs: FSRemoteFileBackdoor.ServerProcs ← NEW[FSRemoteFileBackdoor.ServerProcsObject ← [STPSweep, STPValidate, STPDelete, STPDo, STPEnumerateForInfo, STPEnumerateForNames, STPGetInfo, STPRename, STPRetrieve, STPStore, STPOpen, STPClose, STPReadWrite]];
initialConnectionTTL: CARD ← 8;
upServerTTL: CARD ← 0; -- irrelevant
downServerTTL: CARD ← 30;
busyServerTTL: CARD ← 10;
credentialsErrorTTL: CARD ← 30;
maxGrapevineCache: CARDINAL = 7;
Registered with FSRemoteFileBackdoor
STPDelete: FSRemoteFileBackdoor.DeleteProc -- [h: ServerHandle, file: ROPE, wantedCreatedTime: GMT, case: BOOL, proc: ConfirmProc] -- ~ {
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];
};
CheckUnimplemented[h, file, case];
IF InnerDelete[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, h, file, wantedCreatedTime];
};
STPDo: FSRemoteFileBackdoor.DoProc -- [h: ServerHandle, cmd: ATOM, arg: REF, result: REF] -- ~ {
FSRemoteFileBackdoor.ErrorNotImplemented[h, NIL, "STPDo"];
};
STPEnumerateForInfo: FSRemoteFileBackdoor.EnumerateForInfoProc -- [h: ServerHandle, pattern: ROPE, case: BOOL, 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, FS.tUnspecified]);
};
CheckUnimplemented[h, pattern, case];
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]) ] };
CheckUnimplemented[h, pattern, case];
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, case: BOOL] RETURNS [version: Version, bytes: INT, created: GMT, fileType: FS.FileType] -- ~ {
result: STPFSRemoteFile.LookupResult;
CheckUnimplemented[h, file, case];
fileType ← FS.tUnspecified;
[result, version, created, bytes] ← STPFSRemoteFile.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, case: BOOL, proc: ConfirmProc] -- ~ {
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];
};
CheckUnimplemented[h, fromFile, case];
CheckUnimplemented[h, toFile, case];
version ← STPGetInfo[h, fromFile, fromCreated, case].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, case: BOOL, 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];
SELECT TRUE FROM
matchFound => answer ← abort;
(NOT timeSearch) OR (wantedCreatedTime = created) => {
localStream ← proc[file, info.size, created, FS.tUnspecified];
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];
};
CheckUnimplemented[h, file, case];
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, unknown, created];
};
ReturnConnection[h, stpH];
};
CheckUnimplemented[h, file, case];
stpCode ← noSuchFile;
IF InnerStore[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, h, file, BasicTime.nullGMT];
};
STPOpen: FSRemoteFileBackdoor.OpenProc -- [h: ServerHandle, cmd: ATOM, file: ROPE, wantedCreatedTime: GMT, case: BOOL, proc: ConfirmRetrieveProc] RETURNS [FileHandle] -- ~ {
FSRemoteFileBackdoor.ErrorNotImplemented[h, NIL, "STPOpen"];
RETURN[NIL];
};
STPClose: FSRemoteFileBackdoor.CloseProc -- [h: ServerHandle, fileHandle: FileHandle] -- ~ {
FSRemoteFileBackdoor.ErrorNotImplemented[h, NIL, "STPClose"];
};
STPReadWrite: FSRemoteFileBackdoor.RWProc -- [h: ServerHandle, fileHandle: FileHandle, cmd: ATOM, bytePos: CARD, block: REF TEXT] RETURNS [bytesTransferred: CARD] -- ~ {
FSRemoteFileBackdoor.ErrorNotImplemented[h, NIL, "STPReadWrite"];
};
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;
Server cache
STPGetServer: FSRemoteFileBackdoor.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: STPFSRemoteFile.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[STPFSRemoteFile.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-- {
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 FSRemoteFileBackdoor.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] ~ {
data: ServerData ~ NARROW[h.data];
data.downMsg ← "got STP error";
data.ttl ← seconds;
};
dfq: SafeStorage.FinalizationQueue ~ SafeStorage.NewFQ[20];
STPSweep: FSRemoteFileBackdoor.SweepProc -- [h: ServerHandle, seconds: CARD] -- ~ {
data: ServerData ~ NARROW[h.data];
stpH: STP.Handle ← NIL;
STPSweepInner: ENTRY PROC ~ --INLINE-- {
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;
};
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
CheckUnimplemented: PROC [h: ServerHandle, file: ROPE, case: BOOL] ~ {
IF Rope.FindBackward[file, "!"] < 0 THEN ERROR; -- for now
IF case THEN ERROR; -- for now
};
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[STPFSRemoteFile.ServerDataObject], npr~0, fq~dfq];
FSRemoteFileBackdoor.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.