Exported to FSRemoteFile
Delete:
PUBLIC
PROC [server, file:
ROPE, wantedCreatedTime:
GMT, proc: FSRemoteFile.ConfirmProc] ~ {
pServer, psTranslation, serverPart, pathPart: ROPE;
h: ServerHandle;
pServer ← UnbracketServer[server];
psTranslation ← FSPseudoServers.TranslateForWrite[pServer];
[serverPart, pathPart] ← ParsePSTranslation[psTranslation];
h ← GetServer[serverPart];
file ← PrependPath[pathPart, InsertDefaultVersion[file, "!l", FALSE]];
h.procs.delete[h, file, wantedCreatedTime, FALSE, proc];
};
EnumerateForInfo:
PUBLIC
PROC [server, pattern:
ROPE, proc: FS.InfoProc] ~ {
pServer: ROPE ← UnbracketServer[server];
pServerB: ROPE ← Rope.Cat["[", pServer, "]"];
replicated: BOOL ← FSPseudoServers.AvoidRemoteCheck[pServer];
psTranslations: LIST OF ROPE;
gotOne: BOOL ← FALSE;
savedErrorDesc: FS.ErrorDesc;
h: ServerHandle;
serverPart, pathPart: ROPE;
ProcInner: FSRemoteFileBackdoor.InfoProc
-- [file, bytes, created, type] RETURNS [continue: BOOL] -- ~ {
fullFName: ROPE;
gotOne ← TRUE;
Process.CheckForAbort[];
fullFName ← Rope.Concat[pServerB, StripPath[pathPart, file]];
RETURN [proc[fullFName~fullFName, attachedTo~NIL, created~created, bytes~bytes, keep~0, fileType~type]];
};
psTranslations ← FSPseudoServers.TranslateForRead[pServer];
pattern ← InsertDefaultVersion[pattern, "!*", FALSE];
FOR each:
LIST
OF
ROPE ← psTranslations, each.rest
WHILE (each #
NIL)
DO
ENABLE
FS.Error => {
IF gotOne OR ((error.code = $unknownFile) AND replicated) THEN REJECT;
IF each = psTranslations THEN savedErrorDesc ← error;
LOOP;
};
[serverPart, pathPart] ← ParsePSTranslation[each.first];
h ← GetServer[serverPart];
h.procs.enumerateForInfo[h, PrependPath[pathPart, pattern], FALSE, ProcInner];
IF gotOne OR replicated THEN RETURN;
ENDLOOP;
IF savedErrorDesc.code # NIL THEN ERROR FS.Error[savedErrorDesc];
Should I raise FS.Error if NOT gotOne?
};
EnumerateForNames:
PUBLIC
PROC [server, pattern:
ROPE, proc: NameProc] ~ {
pServer: ROPE ← UnbracketServer[server];
pServerB: ROPE ← Rope.Cat["[", pServer, "]"];
replicated: BOOL ← FSPseudoServers.AvoidRemoteCheck[pServer];
psTranslations: LIST OF ROPE;
gotOne: BOOL ← FALSE;
savedErrorDesc: FS.ErrorDesc;
h: ServerHandle;
serverPart, pathPart: ROPE;
ProcInner: FSRemoteFileBackdoor.NameProc
-- [file] RETURNS [continue: BOOL] -- ~ {
fullFName: ROPE;
gotOne ← TRUE;
Process.CheckForAbort[];
fullFName ← Rope.Concat[pServerB, StripPath[pathPart, file]];
RETURN [proc[fullFName]];
};
psTranslations ← FSPseudoServers.TranslateForRead[pServer];
pattern ← InsertDefaultVersion[pattern, "!*", FALSE];
FOR each:
LIST
OF
ROPE ← psTranslations, each.rest
WHILE (each #
NIL)
DO
ENABLE
FS.Error => {
IF gotOne OR ((error.code = $unknownFile) AND replicated) THEN REJECT;
IF each = psTranslations THEN savedErrorDesc ← error;
LOOP;
};
[serverPart, pathPart] ← ParsePSTranslation[each.first];
h ← GetServer[serverPart];
h.procs.enumerateForNames[h, PrependPath[pathPart, pattern], FALSE, ProcInner];
IF gotOne OR replicated THEN RETURN;
ENDLOOP;
IF savedErrorDesc.code # NIL THEN ERROR FS.Error[savedErrorDesc];
Should I raise FS.Error if NOT gotOne?
};
Info:
PUBLIC
PROC [server, file:
ROPE, wantedCreatedTime:
GMT]
RETURNS [version: Version, bytes: INT, created: GMT, fileType: FileType] ~ {
pServer: ROPE ← UnbracketServer[server];
pServerB: ROPE ← Rope.Cat["[", pServer, "]"];
replicated: BOOL ← FSPseudoServers.AvoidRemoteCheck[pServer];
psTranslations: LIST OF ROPE;
savedErrorDesc: FS.ErrorDesc;
h: ServerHandle;
serverPart, pathPart: ROPE;
psTranslations ← FSPseudoServers.TranslateForRead[pServer];
file ← InsertDefaultVersion[file, "!h", FALSE];
FOR each:
LIST
OF
ROPE ← psTranslations, each.rest
WHILE (each #
NIL)
DO
ENABLE
FS.Error => {
IF (error.code = $unknownFile) AND replicated THEN REJECT;
IF each = psTranslations THEN savedErrorDesc ← error;
LOOP;
};
[serverPart, pathPart] ← ParsePSTranslation[each.first];
h ← GetServer[serverPart];
[version, bytes, created, fileType] ← h.procs.getInfo[h, PrependPath[pathPart, file], wantedCreatedTime, FALSE];
RETURN;
ENDLOOP;
IF savedErrorDesc.code = NIL THEN ERROR; -- can't happen
ERROR FS.Error[savedErrorDesc];
};
Rename:
PUBLIC
PROC [server, fromFile:
ROPE, fromCreated:
GMT, toFile:
ROPE, proc: ConfirmProc] ~ {
pServer, psTranslation, serverPart, pathPart: ROPE;
h: ServerHandle;
pServer ← UnbracketServer[server];
psTranslation ← FSPseudoServers.TranslateForWrite[pServer];
[serverPart, pathPart] ← ParsePSTranslation[psTranslation];
h ← GetServer[serverPart];
fromFile ← PrependPath[pathPart, InsertDefaultVersion[fromFile, "!h", FALSE]];
toFile ← PrependPath[pathPart, InsertDefaultVersion[toFile, "!", TRUE]];
h.procs.rename[h, fromFile, fromCreated, toFile, FALSE, proc];
};
Retrieve:
PUBLIC
PROC [
server, file: ROPE, wantedCreatedTime: GMT,
proc: PROC[fullGName: ROPE, bytes: INT, created: GMT] RETURNS [IO.STREAM],
checkFileType: BOOL, fileType: FileType
] ~ {
pServer: ROPE ← UnbracketServer[server];
pServerB: ROPE ← Rope.Cat["[", pServer, "]"];
replicated: BOOL ← FSPseudoServers.AvoidRemoteCheck[pServer];
psTranslations: LIST OF ROPE;
savedErrorDesc: FS.ErrorDesc;
h: ServerHandle;
serverPart, pathPart: ROPE;
ProcInner: FSRemoteFileBackdoor.ConfirmRetrieveProc
-- [file, bytes, created, type] RETURNS [IO.STREAM] -- ~ {
fullGName: ROPE;
IF checkFileType AND (type # fileType) THEN FSBackdoor.ProduceError[$fileTypeMismatch, "file on server has the wrong file type"];
fullGName ← Rope.Concat[pServerB, StripPath[pathPart, file]];
RETURN[proc[fullGName, bytes, created]];
};
psTranslations ← FSPseudoServers.TranslateForRead[pServer];
file ← InsertDefaultVersion[file, "!h", FALSE];
FOR each:
LIST
OF
ROPE ← psTranslations, each.rest
WHILE (each #
NIL)
DO
ENABLE
FS.Error => {
IF (error.code = $unknownFile) AND replicated THEN REJECT;
IF each = psTranslations THEN savedErrorDesc ← error;
LOOP;
};
[serverPart, pathPart] ← ParsePSTranslation[each.first];
h ← GetServer[serverPart];
h.procs.retrieve[h, PrependPath[pathPart, file], wantedCreatedTime, FALSE, ProcInner];
RETURN;
ENDLOOP;
IF savedErrorDesc.code = NIL THEN ERROR; -- can't happen
ERROR FS.Error[savedErrorDesc];
};
Store:
PUBLIC
PROC [server, file:
ROPE, str:
IO.
STREAM, created:
GMT, proc: FSRemoteFile.ConfirmProc] ~ {
pServer, psTranslation, serverPart, pathPart: ROPE;
h: ServerHandle;
pServer ← UnbracketServer[server];
psTranslation ← FSPseudoServers.TranslateForWrite[pServer];
[serverPart, pathPart] ← ParsePSTranslation[psTranslation];
h ← GetServer[serverPart];
file ← PrependPath[pathPart, InsertDefaultVersion[file, "!", TRUE]];
h.procs.store[h, file, FALSE, str, created, proc];
};
Finding a server (exported to FSRemoteFileBackdoor)
GetServerResponse: TYPE ~ REF GetServerResponseObject;
GetServerResponseObject:
TYPE ~
RECORD [
nRunning: CARDINAL ← 0,
wakeup: CONDITION,
handle: ServerHandle ← NIL,
downMsg: ROPE ← NIL
];
GetServer:
PUBLIC
PROC [server:
ROPE]
RETURNS [h: ServerHandle] ~ {
Raises FS.Error if server is down.
obsolete: BOOL;
downMsg: ROPE;
kids: LIST OF PROCESS ← NIL;
response: GetServerResponse;
theRegistration: Registration;
started: GMT;
MakeChild:
ENTRY
PROC [r: Registration]
RETURNS [p:
PROCESS] ~
--
INLINE
-- {
response.nRunning ← response.nRunning + 1;
p ← FORK DoGetServer[r, server, response];
};
WaitForResponse:
ENTRY
PROC ~
--INLINE-- {
set [h, downMsg] ...
WHILE (response.nRunning > 0)
AND (response.handle =
NIL)
AND (response.downMsg =
NIL)
DO
sinceStarted: INT ~ BasicTime.Period[from~started, to~BasicTime.Now[]];
IF sinceStarted >= waitForServerTimeout THEN EXIT;
TRUSTED { Process.SetTimeout[@response.wakeup, Process.MsecToTicks[1000*(waitForServerTimeout-sinceStarted)+500]] };
WAIT response.wakeup;
ENDLOOP;
h ← response.handle;
downMsg ← response.downMsg;
};
h ← NARROW[SymTab.Fetch[serverTab, server].val];
IF h #
NIL
THEN {
[obsolete, downMsg] ← h.procs.validate[h];
IF
NOT obsolete
THEN {
IF downMsg # NIL THEN ErrorDownServer[h, downMsg];
RETURN;
};
};
response ← NEW [GetServerResponseObject];
FOR theRegistration ← registrations, theRegistration.next
WHILE theRegistration #
NIL
DO
suffixLen, pos: INT;
suffixLen ← Rope.Length[theRegistration.nameSuffix];
pos ← Rope.Length[server] - suffixLen;
IF (pos >= 0)
AND Rope.EqualSubstrs[server, pos, suffixLen, theRegistration.nameSuffix, 0, suffixLen,
FALSE]
THEN { server ← Rope.Substr[server, 0, pos]; EXIT };
ENDLOOP;
IF theRegistration #
NIL
THEN {
DoGetServer[theRegistration, server, response];
h ← response.handle;
IF h # NIL THEN h.name ← Rope.Concat[h.name, theRegistration.nameSuffix];
downMsg ← response.downMsg;
}
ELSE {
started ← BasicTime.Now[];
TRUSTED { Process.EnableAborts[@response.wakeup] };
FOR each: Registration ← registrations, each.next
WHILE each #
NIL
DO
kids ← CONS [MakeChild[each], kids];
ENDLOOP;
WaitForResponse[];
FOR kid:
LIST
OF
PROCESS ← kids, kid.rest
WHILE kid #
NIL
DO
TRUSTED { Process.Abort[kid.first] };
TRUSTED { Process.Detach[kid.first] };
ENDLOOP;
};
IF h =
NIL
THEN {
IF downMsg = NIL THEN downMsg ← "unknown server";
h ← MakeNonexistentServer[server, downMsg];
};
[] ← SymTab.Store[serverTab, h.name, h];
IF downMsg # NIL THEN ErrorDownServer[h, downMsg];
};
DoGetServer:
PROC [r: Registration, server:
ROPE, response: GetServerResponse] ~ {
h: ServerHandle;
downMsg: ROPE;
NotifyDone:
ENTRY
PROC ~
--INLINE-- {
response.nRunning ← response.nRunning - 1;
IF (response.handle # NIL) OR (response.downMsg # NIL) THEN RETURN;
SELECT
TRUE
FROM
(h #
NIL)
OR (downMsg #
NIL) =>
-- found it -- {
response.handle ← h;
response.downMsg ← downMsg;
NOTIFY response.wakeup;
};
(response.nRunning = 0) =>
-- nobody will find it -- {
NOTIFY response.wakeup;
};
ENDCASE;
};
[h, downMsg] ← r.getServer[server ! FS.Error => CONTINUE];
NotifyDone[];
};
ErrorDownServer:
PROC [h: ServerHandle, downMsg:
ROPE] ~ {
FSBackdoor.ProduceError[FSBackdoor.ErrorCode.unknownServer, Rope.Cat["Server \"[", h.name, "]\" unavailable - ", downMsg]];
};
Nonexistent Server Implementation
NonexistentServerData: TYPE ~ REF NonexistentServerDataObject;
NonexistentServerDataObject:
TYPE ~
RECORD [
ttl: CARD ← nonexistentServerTTL,
downMsg: ROPE
];
nonexistentServerProcs: FSRemoteFileBackdoor.ServerProcs ←
NEW[FSRemoteFileBackdoor.ServerProcsObject ← [
sweep~SweepNonexistentServer,
validate~ValidateNonexistentServer,
delete~NIL,
enumerateForInfo~NIL,
enumerateForNames~NIL,
getInfo~NIL,
rename~NIL,
retrieve~NIL,
store~NIL,
do~NIL
]
];
MakeNonexistentServer:
PROC [name, downMsg:
ROPE]
RETURNS [h: ServerHandle] ~ {
d: NonexistentServerData ← NEW[NonexistentServerDataObject ← [downMsg~downMsg]];
h ← NEW[FSRemoteFileBackdoor.ServerObject ← [$nonexistentServer, name, nonexistentServerProcs, d]];
};
SweepNonexistentServer: FSRemoteFileBackdoor.SweepProc
-- [h: ServerHandle, seconds: CARD] -- ~ {
d: NonexistentServerData ← NARROW[h.data];
IF d.ttl > seconds THEN d.ttl ← d.ttl - seconds ELSE d.ttl ← 0;
};
ValidateNonexistentServer: FSRemoteFileBackdoor.ValidateProc
-- [h: ServerHandle] RETURNS [obsolete: BOOL, down: BOOL] -- ~ {
d: NonexistentServerData ← NARROW[h.data];
RETURN[(d.ttl = 0), d.downMsg];
};
Name manipulation
InsertDefaultVersion:
PROC [file, versionPart:
ROPE, smash:
BOOL ←
FALSE]
RETURNS [
ROPE] ~ {
pos: INT ← Rope.FindBackward[file, "!"];
IF pos < 0 THEN RETURN [Rope.Concat[file, versionPart]];
IF smash THEN RETURN [Rope.Concat[Rope.Substr[file, 0, pos], versionPart]];
RETURN [file];
};
ParsePSTranslation:
PROC [psTranslation:
ROPE]
RETURNS [server, path:
ROPE] ~ {
len, pos: INT;
len ← Rope.InlineLength[psTranslation];
IF (len = 0) OR (Rope.InlineFetch[psTranslation, 0] # '[)
THEN RETURN [psTranslation, NIL];
pos ← Rope.Find[psTranslation, "]"];
IF pos < 0 THEN RETURN [Rope.Substr[psTranslation, 1], NIL];
server ← Rope.Substr[psTranslation, 1, pos-1];
IF pos < (len-1) THEN path ← Rope.Substr[psTranslation, pos+1];
};
PrependPath:
PROC [path, file:
ROPE]
RETURNS [
ROPE] ~ {
pathLen: INT ← Rope.InlineLength[path];
fileLen: INT ← Rope.InlineLength[file];
IF pathLen = 0 THEN RETURN [file];
IF fileLen = 0 THEN RETURN [path];
IF Rope.InlineFetch[path, pathLen-1] # '> THEN path ← Rope.Concat[path, ">"];
SELECT
TRUE
FROM
Rope.IsPrefix["<>", file] => RETURN [Rope.Concat[path, Rope.Substr[file, 2]]];
Rope.IsPrefix["<", file] => RETURN [Rope.Concat[path, Rope.Substr[file, 1]]];
ENDCASE => RETURN [Rope.Concat[path, file]];
};
StripPath:
PROC [path, file:
ROPE]
RETURNS [
ROPE] ~ {
Caller asserts path is a prefix of file.
pathLen: INT ← Rope.InlineLength[path];
IF pathLen = 0 THEN RETURN [file];
IF Rope.InlineFetch[path, pathLen-1] # '>
THEN {
path ← Rope.Concat[path, ">"];
pathLen ← pathLen + 1;
};
IF NOT Rope.IsPrefix[path, file, FALSE] THEN ERROR; -- make this less drastic later!
IF Rope.Find[file, ">", pathLen] >= 0
THEN RETURN [Rope.Concat["<", Rope.Substr[file, pathLen]]]
ELSE RETURN [Rope.Concat["<>", Rope.Substr[file, pathLen]]];
};
UnbracketServer:
PROC[server:
ROPE]
RETURNS [
ROPE] ~
INLINE {
IF (Rope.InlineLength[server] = 0) OR (Rope.InlineFetch[server, 0] # '[)
THEN RETURN [server];
RETURN [ Rope.Flatten[server, 1, Rope.Length[server]-2]]
};
}...