Procs to provide PFS view
TCDelete: PFSClass.DeleteProc = {
PFSBackdoor.ProduceError[notImplemented, "Delete not allowed for TC servers"];
};
TCCopy: PFSClass.CopyProc = {
PFSBackdoor.ProduceError[notImplemented, "Copy not allowed for TC servers"];
};
TCEnumerateForInfo: PFSClass.EnumerateForInfoProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.InfoProc, lbound: PATH, hbound: PATH]
InfoProc: TYPE ~ PROC [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType]
RETURNS [continue: BOOL ← TRUE];
P:
PROC [file:
PATH, bytes:
INT, created:
GMT, version: Version]
RETURNS [continue:
BOOL] = {
continue ¬ proc[fullFName: file, attachedTo: NIL, uniqueID: MakeUID[created], bytes: bytes, mutability: immutable, fileType: PFS.tUnspecified]
};
InnerEnumerate[h, pattern, FALSE, P];
};
TCEnumerateForNames: PFSClass.EnumerateForNamesProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.NameProc, lbound: PATH, hbound: PATH]
NameProc: TYPE ~ PROC [name: PATH] RETURNS [continue: BOOL ← TRUE;
P:
PROC [file:
PATH, bytes:
INT, created:
GMT, version: Version]
RETURNS [continue:
BOOL] = {
continue ¬ proc[file]
};
InnerEnumerate[h, pattern, TRUE, P];
};
EnumerateProc:
TYPE ~
PROC [
--file:--
PATH,
--bytes:--
INT,
--created:--
GMT,
--version: -- Version]
RETURNS [
--continue:--
BOOL];
InnerEnumerate:
PROC [h: FSHandle, pattern:
PATH, namesOnly:
BOOL,
proc: EnumerateProc] = {
Listing: CrRPC.BulkDataSink = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
name: ROPE;
nameLength: INT16; -- if the name of the file is bigger than this we have a real problem.
created: INT; -- really BasicTime.GMT LOOPHOLEd
size: INT; -- number of bytes in the file
version: Version;
UNTIL
IO.EndOf[self: s]
DO
continue: BOOL ¬ FALSE;
i: INT ¬ 0;
Proc:
PROC
RETURNS [c:
CHAR] = {
c ¬ IO.GetChar[self: s ! IO.EndOfStream => GOTO Error];
EXITS Error => ERROR PFS.Error[[$environment, $brainDamage, "TC protocol error"]];
};
nameLength ¬ Basics.Int16FromH[IO.GetHWord[self: s ! IO.EndOfStream => EXIT]];
created ¬ Basics.Int32FromF[IO.GetFWord[self: s ! IO.EndOfStream => GOTO Error]];
size ¬ Basics.Int32FromF[IO.GetFWord[self: s ! IO.EndOfStream => GOTO Error]];
name ¬ Rope.FromProc[len: nameLength, p: Proc];
version ¬ VersionFromName[name: name];
continue ¬ proc[--file:-- PFS.PathFromRope[name], --bytes:-- size, --created:-- LOOPHOLE[created], -- version: -- version];
IF NOT continue THEN RETURN[TRUE];
IF checkAbort[h] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
EXITS Error => ERROR PFS.Error[[$environment, $brainDamage, "TC protocol error"]];
};
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
we need to prefix names with leadingDir, so that full path names are returned - this is a PFS thing
crH: CrRPC.Handle ¬ GetConnection[data];
patRope: ROPE ¬ PFS.RopeFromPath[pattern];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN patRope
ELSE Rope.Cat["/", vfs.fs, patRope];
{
-- for
ENABLE
--
ENABLE UNWIND => { ReturnConnection[crH] };
P:
PROC = {
TrickleChargeP9813V411.EnumerateForInfo[h: crH, pattern: toUse, info: Listing];
ReturnConnection[crH];
};
startEnum ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
endEnum ¬ BasicTime.Now[];
};
};
TCGetInfo: PFSClass.GetInfoProc ~ {
h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType
vfs: VFS ~ NARROW[h.data];
Generic Implementation
fullFName ¬ file.fullFName;
attachedTo ¬ file.attachedTo;
uniqueID ¬ file.uniqueID;
bytes ¬ file.bytes;
mutability ¬ file.mutability;
fileType ¬ file.fileType;
};
TCFileInfo: PFSClass.FileInfoProc = {
mutability ← immutable; -- to keep mimosa happy
bytes ← 0;
fileType ← PFS.tUnspecified;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "FileInfo not allowed for TC servers"];
};
TCFileInfo: PFSClass.FileInfoProc = {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID]
RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType]
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data];
{
-- for
ENABLE
--
ENABLE UNWIND => ReturnConnection[crH];
P:
PROC = {
created: GMT;
fullFName: ROPE; createInt: INT;
fileRope: ROPE ¬ PFS.RopeFromPath[file];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN fileRope
ELSE Rope.Cat["/", vfs.fs, fileRope];
[fullFName: fullFName, bytes: bytes, created: createInt] ¬ TrickleChargeP9813V411.FileInfo[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt]];
ReturnConnection[crH];
version ¬ VersionFromName[name: fullFName];
created ¬ LOOPHOLE[createInt];
uniqueID ¬ MakeUID[created];
mutability ¬ immutable;
fileType ¬ PFS.tUnspecified;
attachedTo ¬ NIL;
};
startFileInfo ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
endFileInfo ¬ BasicTime.Now[];
};
};
TCRename: PFSClass.RenameProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Rename not allowed for TC servers"];
};
TCRetrieve: PFSClass.RetrieveProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.RetrieveConfirmProc, checkFileType: BOOL ← FALSE, fileType: PFS.FileType]
RetrieveConfirmProc: TYPE ~ PROC [fullFName: PATH, bytes: INT, uniqueID: UniqueID]
RETURNS [STREAM];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data];
localStream: STREAM ¬ NIL;
Content: CrRPC.BulkDataSink = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
buffer: REF TEXT = RefText.ObtainScratch[512];
nBytes: NAT;
abort ¬ FALSE;
DO
IF s.EndOf[] THEN EXIT; -- seems to end this way sometimes
nBytes ¬ s.GetBlock[buffer, 0];
IF nBytes = 0 THEN EXIT;
localStream.PutBlock[buffer, 0, nBytes];
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
{
-- for
ENABLE
--
ENABLE UNWIND => ReturnConnection[crH];
P:
PROC = {
version: Version; bytes: INT; created: GMT;
fullFName: ROPE; createInt: INT;
fileRope: ROPE ¬ PFS.RopeFromPath[file];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN fileRope
ELSE Rope.Cat["/", vfs.fs, fileRope];
[fullFName: fullFName, bytes: bytes, created: createInt] ¬ TrickleChargeP9813V411.FileInfo[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt]];
version ¬ VersionFromName[name: fullFName];
created ¬ LOOPHOLE[createInt];
localStream ¬ proc[file, bytes, MakeUID[created]];
IF localStream #
NIL
THEN
TrickleChargeP9813V411.Retrieve[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt], data: Content];
ReturnConnection[crH];
};
startRetrieve ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
endRetrieve ¬ BasicTime.Now[];
};
};
TCStore: PFSClass.StoreProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Store not allowed for TC servers"];
};
TCSetAttributes: PFSClass.SetAttributesProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetAttributes not allowed for TC servers"];
};
TCSetByteCountAndUniqueID: PFSClass.SetByteCountAndUniqueIDProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetByteCountAndUniqueID not allowed for TC servers"];
};
TCSetClientProperty: PFSClass.SetClientPropertyProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetClientProperty not allowed for TC servers"];
};
TCGetClientProperty: PFSClass.GetClientPropertyProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "GetClientProperty not allowed for TC servers"];
};
TCEnumerateClientProperties: PFSClass.EnumerateClientPropertiesProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "EnumerateClientProperties not allowed for TC servers"];
};
TCRead: PFSClass.ReadProc = {
bytesRead ¬ 0;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Read not allowed for TC servers"];
};
TCWrite: PFSClass.WriteProc = {
bytesWritten ¬ 0;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Write not allowed for TC servers"];
};
TCOpen: PFSClass.OpenProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Open not allowed for TC servers"];
RETURN[NIL];
};
TCClose: PFSClass.CloseProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Close not allowed for TC servers"];
};
TCLookup: PFSClass.LookupNameProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "LookupName not allowed for TC servers"];
RETURN[file];
};
MakeUID:
PROC[gmt:
GMT]
RETURNS[uid:
PFS.UniqueID] =
{ uid.egmt.gmt ¬ gmt };
Registered with PFSClass
TCGetServer: PFSClass.GetHandleProc ~ {
[fs: ROPE, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE]
Returns [NIL, NIL] if server doesn't exist.
Returns [NIL, msg] if server exists but is down.
The flavorSpecified parameter is ignored.
vfs: VFS ~ NEW[VFSInstance ¬ [fs: fs, flavorSpecified: flavorSpecified, sD: NIL] ];
data: ServerData;
session: REF Session;
qualified: XNSCHName.Name; address: REF XNS.Address; name: ROPE;
crH: CrRPC.Handle;
opened: BOOL ¬ FALSE;
version: INT16 ¬ 0;
downMsg ¬ "can\'t connect";
{
ENABLE {
CrRPC.Error => {downMsg ¬ IO.PutFR["CrRPC Error: %g, %g", [rope[crRPCErrorRopes[errorReason]]], [rope[text]]]; CONTINUE};
TrickleChargeP9813V411.Error => {downMsg ¬ IO.PutFR1["Server reports %g", [rope[description]]]; CONTINUE};
};
add: XNS.Address;
[qualified, add] ¬ XNSCH.LookupAddressFromRope[fs];
IF add = XNS.unknownAddress THEN -- host not known -- RETURN[NIL, NIL];
name ¬ XNSCHName.RopeFromName[qualified];
add.socket ¬ XNSWKS.courier;
address ¬ NEW[XNS.Address ¬ add];
crH ¬ CrRPC.CreateClientHandle[$CMUX, address];
version ¬ TrickleChargeP9813V411.AnyBodyHome[h: crH];
CrRPC.DestroyClientHandle[crH];
opened ¬ TRUE;
};
IF NOT opened THEN RETURN [NIL, downMsg];
data ¬ NEW[ServerDataObject ¬ [qualified: qualified, serverName: name, address: address, opened: opened, version: version]];
vfs.sD ¬ data;
h ¬ NEW[PFSClass.FSObject ¬ vfsClass];
h.name ¬ fs;
h.data ¬ vfs;
RETURN [h, NIL];
};
TCSweep: PFSClass.SweepProc
-- [h:
FSHandle
, seconds: CARD] -- ~ {
doesn't need to do anything
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
session: REF Session ¬ NIL; address: REF XNS.Address;
};
TCValidate: PFSClass.ValidateProc ~ {
[h: FSHandle] RETURNS [obsolete: BOOL, downMsg: ROPE]
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
RETURN [FALSE, NIL];
};
Error Reporting and Catching
CallProtected:
PUBLIC
PROC[proc:
PROC, h: FSHandle, filePath:
PATH, uid:
PFS.UniqueID] ~ {
time: GMT ~ uid.egmt.gmt;
file: ROPE ~ PFS.RopeFromPath[filePath];
gName: ROPE ~ Rope.Cat["[", h.name, "]", file];
quotedGName: ROPE ~ Rope.Cat["\"", gName, "\""];
{
-- for
ENABLE
ENABLE {
XNSStream.ConnectionClosed => PFSBackdoor.ProduceError[resourceLimitExceeded, IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]; --???
CrRPC.Error => MakeCrRPCError[errorReason, text];
IO.EndOfStream => ERROR PFS.Error[[environment, $unexpectedClose,
IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]];
IO.Error => ERROR PFS.Error[[bug, $unexpectedError,
IO.PutFR1["File: %g unexpected IO.Error.", [rope[quotedGName]]]]];
TrickleChargeP9813V411.Error => ERROR PFS.Error[[environment, $notSure,
IO.PutFR["Server for %g reported %g.", [rope[quotedGName]], [rope[description]]]]];
TrickleChargeP9813V411.FileNotFound => UnknownFile[gName, time];
PFSClass.Error => ERROR PFS.Error[[environment, code, msg]];
};
proc[ ! XNSStream.Timeout => RESUME]; -- since the timeout is retryable
};
};
VersionFromName:
PROC[name:
ROPE]
RETURNS[Version] = {
path: PATH ~ PFS.PathFromRope[name];
RETURN[PFSNames.ShortName[path].version];
};
UnknownFile:
PUBLIC
PROC[name:
ROPE, createdTime:
GMT] = {
IF createdTime = nullGMT
THEN PFSBackdoor.ProduceError[unknownFile, IO.PutFR1["Could not find %g", [rope[name]]] ]
ELSE
PFSBackdoor.ProduceError[unknownUniqueID, IO.PutFR["Could not find \"%g\" created on %t", [rope[name]], [time[createdTime]]]];
};
REQUIREDROPE: TYPE ~ ROPE ¬;
crRPCErrorRopes:
REF
ARRAY CrRPC.ErrorReason
OF
REQUIREDROPE ~
NEW[
ARRAY CrRPC.ErrorReason
OF
REQUIREDROPE ¬ [
unknown: "Unknown",
unknownClass: "unknownClass",
courierVersionMismatch: "courierVersionMismatch",
rejectedNoSuchProgram: "rejectedNoSuchProgram",
rejectedNoSuchVersion: "rejectedNoSuchVersion",
rejectedNoSuchProcedure: "rejectedNoSuchProcedure",
rejectedInvalidArgument: "rejectedInvalidArgument",
rejectedUnspecified: "rejectedUnspecified",
remoteError: "remoteError",
cantConnectToRemote: "Can't Connect To Remote",
argsError: "Args Error",
resultsError: "Results Error",
bulkDataError: "Bulk DataError",
protocolError: "Protocol Error",
remoteClose: "Remote Close",
communicationFailure: "Communication Failure",
notImplemented: "notImplemented",
unknownOperation: "unknownOperation",
notServerHandle: "notServerHandle",
notClientHandle: "notClientHandle",
addressInappropriateForClass: "addressInappropriateForClass",
other: "Other"
]];
MakeCrRPCError:
PROC [errorReason: CrRPC.ErrorReason, text:
ROPE] = {
r: ROPE ~ IO.PutFR["CrRPC Error: %g, %g", [rope[crRPCErrorRopes[errorReason]]], [rope[text]]];
ERROR PFS.Error[[$environment, $CrRPCError, r]];
};
FromPath:
PROC[p:
PFS.
PATH]
RETURNS[
ROPE] ~
{ RETURN[PFS.RopeFromPath[p]] };
nullGMTRope:
ROPE ~ "nullGMT";
TCStats: Commander.CommandProc ~ {
cmd.out.PutF1["***** Client started at %g\n", [time[startTime]] ];
cmd.out.PutF["startEnum: %g\n\t enumEnd: %g\n",
IF startEnum = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startEnum]],
IF endEnum = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endEnum]] ];
cmd.out.PutF["startFileInfo: %g\n\t enumEnd: %g\n",
IF startFileInfo = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startFileInfo]],
IF endFileInfo = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endFileInfo]] ];
cmd.out.PutF["startRetrieve: %g\n\t endRetrieve: %g\n",
IF startRetrieve = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startRetrieve]],
IF endRetrieve = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endRetrieve]] ];
};