LeafSubrImpl.Mesa
last edit December 30, 1982 2:11 pm
last edit May 23, 1983 3:21 pm, Russ Atkinson
DIRECTORY
CWF: TYPE USING [SWF1, WF0, WF1, WF2, WFCR],
DCSFileTypes: TYPE USING [tLeaderPage],
Directory: TYPE USING [CreateFile, Error, GetProps, Handle, ignore, Lookup],
Environment: TYPE USING [bytesPerPage, wordsPerPage],
File: TYPE USING [Capability, SetSize],
FileStream: TYPE USING [Create, SetLeaderPropertiesForCapability, SetLength],
IFSFile: TYPE USING [AccessFailure, CantOpen, Close, Completer, Error,
FileHandle, FSInstance, GetLength, GetTimes, Initialize, Login, Logout,
Open, OpenOptions, Problem, SetCreation, SetLength, StartRead,
StartWrite, UnableToLogin],
Inline: TYPE USING [LowHalf],
LeafSubr: TYPE USING [],
LongString: TYPE USING [EquivalentString],
Space: TYPE USING [CopyIn, CopyOut, Create, Delete, GetAttributes, Handle, Kill,
LongPointer, Map, nullHandle, virtualMemory],
Stream: TYPE USING [Delete, Handle],
Subr: TYPE USING [AbortMyself, AllocateString, Any, CopyString, FileError,
FreeString, GetNameandPassword, LongZone, Prefix, TTYProcs],
UserCredentialsUnsafe: TYPE USING[GetUserCredentials];
LeafSubrImpl: MONITOR
IMPORTS
CWF, Directory, File, FileStream, IFSFile, Inline, LongString, Space, Stream, Subr, UserCredentialsUnsafe
EXPORTS LeafSubr = {
MDS USAGE !!!
connseq: LONG POINTER TO ConnSeqRecord ← NIL;
endof MDS USAGE !!!
connection table
MAXCONNS: CARDINAL = 8;
we use the username and password from Profile.userName, userPassword
ConnSeqRecord: TYPE = RECORD[
user2: LONG STRINGNIL, -- connect name
password2: LONG STRINGNIL, -- connect password
size: CARDINAL ← 0, -- # of connections
body: SEQUENCE maxsize: CARDINAL OF ConnTable
];
ConnTable: TYPE = RECORD[
fsinstance: IFSFile.FSInstance ← NIL,
host: LONG STRINGNIL
];
Open: PUBLIC PROC
[host, sfn: LONG STRING, h: Subr.TTYProcs, openOptions: IFSFile.OpenOptions]
RETURNS[fh: IFSFile.FileHandle] = {
fsinstance: IFSFile.FSInstance ← Connect[host,h, NIL, NIL, NIL, NIL];
fh ← IFSFile.Open[fsinstance, sfn, openOptions
! IFSFile.CantOpen =>
SELECT reason FROM
ok => NULL -- can't happen -- ;
io, alreadyExists, other => ERROR IFSFile.CantOpen[reason];
accessDenied, accessConflict => {
SetOtherPass[h, "access Denied or Conflict"L];
fsinstance ← ForceClosed[fsinstance];
fsinstance ← Connect[host, h, NIL, NIL, connseq.user2, connseq.password2];
RETRY;
};
notFound, illegalFileName => ERROR Subr.FileError[notFound];
ENDCASE => ERROR
];
};
RemoteMap: PUBLIC PROC[space: Space.Handle, fh: IFSFile.FileHandle, base: CARDINAL] = {
base is # of page to start mapping on; note that here, base = 0 is the first page of data whereas in Pilot base = 1 is the first page; space may have arbitrary size (> 64k words)
problem: IFSFile.Problem;
notready: BOOL;
finished: CONDITION ← [timeout: 0];
DoRead: ENTRY PROC [bytebase: LONG CARDINAL, bytesize: CARDINAL,
buffer: LONG POINTER] = {
notready ← TRUE;
IFSFile.StartRead[fh, bytebase, bytesize, buffer, CallProc, 0];
WHILE notready DO
WAIT finished;
ENDLOOP;
SELECT problem FROM
ok => NULL;
io, resources, other, credentials =>
RETURN WITH ERROR IFSFile.Error[problem];
ENDCASE => ERROR;
};
CallProc: ENTRY IFSFile.Completer = {
problem ← outcome;
notready ← FALSE;
NOTIFY finished;
};
{
bytebase: LONG CARDINAL ← base*Environment.bytesPerPage;
buffer: LONG POINTER ← Space.LongPointer[space];
filelength: LONG CARDINAL ← IFSFile.GetLength[fh];
pagesize: CARDINAL;
bytesize: LONG CARDINAL;
Space.Map[space];
[size: pagesize] ← Space.GetAttributes[space];
bytesize ← LONG[pagesize] * Environment.bytesPerPage;
IF bytebase + bytesize > filelength THEN {
IF filelength >= bytebase THEN
bytesize ← filelength - bytebase
ELSE ERROR;  -- base is a page number bigger than the file
};
leaf can handle up to 64K in the protocol
the existing leaf servers can only handle up to 32K because of bugs
WHILE bytesize > 0 DO
thisTimeSize: CARDINAL = Inline.LowHalf[MIN[bytesize, LONG[63*512]]];
DoRead[bytebase, thisTimeSize, buffer];
bytesize ← bytesize - thisTimeSize;
bytebase ← bytebase + thisTimeSize;
buffer ← buffer + (thisTimeSize/2);
ENDLOOP;
}};
NPages: CARDINAL = 4;
NBytes: CARDINAL = NPages * Environment.bytesPerPage;
BytesAtATime: PROC[len: LONG CARDINAL] RETURNS[nb: CARDINAL] = INLINE {
RETURN[IF len > NBytes THEN NBytes ELSE Inline.LowHalf[len]];
};
RemoteCopy: PUBLIC PROC[fromHost, fromFilename, toHost, toFilename: LONG STRING,
h: Subr.TTYProcs] RETURNS[length: LONG CARDINAL] = {
createtime, len: LONG CARDINAL;
fhRead, fhWrite: IFSFile.FileHandle ← NIL;
buffer: ARRAY[0 .. NPages*Environment.wordsPerPage) OF WORD;
base: CARDINAL ← 0;
problem: IFSFile.Problem;
notready: BOOL;
finished: CONDITION ← [timeout: 0];
{
ENABLE UNWIND => {
IF fhRead ~= NIL THEN IFSFile.Close[fhRead]; fhRead ← NIL;
IF fhWrite ~= NIL THEN IFSFile.Close[fhWrite]; fhWrite ← NIL;
};
DoRead: ENTRY PROC = {
notready ← TRUE;
IFSFile.StartRead[fhRead, base*Environment.bytesPerPage,
BytesAtATime[len], @buffer, CallProc, 0];
WHILE notready DO
WAIT finished;
ENDLOOP;
SELECT problem FROM
ok => NULL;
io, resources, other, credentials =>
RETURN WITH ERROR IFSFile.Error[problem];
ENDCASE => ERROR;
};
DoWrite: ENTRY PROC = {
notready ← TRUE;
IFSFile.StartWrite[fhWrite, base*Environment.bytesPerPage,
BytesAtATime[len], @buffer, CallProc, 0];
WHILE notready DO
WAIT finished;
ENDLOOP;
SELECT problem FROM
ok => NULL;
io, resources, other, credentials =>
RETURN WITH ERROR IFSFile.Error[problem];
ENDCASE => ERROR;
};
CallProc: ENTRY IFSFile.Completer = {
problem ← outcome;
notready ← FALSE;
NOTIFY finished;
};
fhRead ← Open[fromHost, fromFilename, h, oldReadOnly];
fhWrite ← Open[toHost, toFilename, h, new];
[create: createtime] ← IFSFile.GetTimes[fhRead];
length ← len ← IFSFile.GetLength[fhRead];
IFSFile.SetLength[fhWrite, length];
WHILE len > 0 DO
DoRead[];
DoWrite[];
len ← len - BytesAtATime[len];
base ← base + NPages;
ENDLOOP;
IFSFile.SetCreation[fhWrite, createtime];
IFSFile.Close[fhRead];
fhRead ← NIL;
IFSFile.Close[fhWrite];
fhWrite ← NIL;
}};
Retrieve: PUBLIC PROC[remoteHost, remoteFilename, localFilename: LONG STRING,
h: Subr.TTYProcs]
RETURNS[length: LONG CARDINAL, cap: File.Capability] = {
npages, base: CARDINAL;
buffer: LONG POINTER;
len, create, read, write: LONG CARDINAL;
sh: Stream.Handle ← NIL;
fh: IFSFile.FileHandle ← NIL;
space: Space.Handle ← Space.nullHandle;
problem: IFSFile.Problem;
notready: BOOL;
finished: CONDITION ← [timeout: 0];
DoReadAndWrite: ENTRY PROC = {
base ← 0;
WHILE len > 0 DO
notready ← TRUE;
IFSFile.StartRead[fh, base*Environment.bytesPerPage,
BytesAtATime[len], buffer, CallProc, 0];
WHILE notready DO
WAIT finished;
ENDLOOP;
SELECT problem FROM
ok => NULL;
io, resources, other, credentials =>
RETURN WITH ERROR IFSFile.Error[problem];
ENDCASE => ERROR;
Space.CopyOut[space, [cap, base+1]];
base ← base + NPages;
len ← len - BytesAtATime[len];
Space.Kill[space];
ENDLOOP;
};
CallProc: ENTRY IFSFile.Completer = {
problem ← outcome;
notready ← FALSE;
NOTIFY finished;
};
{ENABLE UNWIND => {
IF sh ~= NIL THEN Stream.Delete[sh]; sh ← NIL;
IF space ~= Space.nullHandle THEN Space.Delete[space]; space ← Space.nullHandle;
IF fh ~= NIL THEN IFSFile.Close[fh]; fh ← NIL;
};
fh ← Open[remoteHost, remoteFilename, h, oldReadOnly];
len ← length ← IFSFile.GetLength[fh];
npages ← Inline.LowHalf[length/Environment.bytesPerPage] + 2;
cap ← Directory.CreateFile[localFilename, DCSFileTypes.tLeaderPage, npages
! Directory.Error => {
IF type = fileAlreadyExists THEN {
cap ← Directory.Lookup[fileName: localFilename,
permissions: Directory.ignore];
File.SetSize[cap, npages];
}
ELSE ERROR Subr.FileError[notFound];
CONTINUE
}];
space ← Space.Create[NPages, Space.virtualMemory];
buffer ← Space.LongPointer[space];
Space.Map[space];
DoReadAndWrite[];
Space.Delete[space];
space ← Space.nullHandle;
[read, write, create] ← IFSFile.GetTimes[fh];
sh ← FileStream.Create[cap];
FileStream.SetLength[sh, length];
FileStream.SetLeaderPropertiesForCapability[cap: cap,
create: LOOPHOLE[create], write: LOOPHOLE[write], read: LOOPHOLE[read]];
Stream.Delete[sh];
sh ← NIL;
IFSFile.Close[fh];
fh ← NIL;
}};
Store: PUBLIC PROC[localFilename, remoteHost, remoteFilename: LONG STRING,
h: Subr.TTYProcs] RETURNS[length: LONG CARDINAL] = {
base: CARDINAL;
buffer: LONG POINTER;
len, create: LONG CARDINAL;
fh: IFSFile.FileHandle ← NIL;
space: Space.Handle ← Space.nullHandle;
problem: IFSFile.Problem;
notready: BOOL;
finished: CONDITION ← [timeout: 0];
cap: File.Capability;
directoryfilename: LONG STRING ← Subr.AllocateString[100];
DoReadAndWrite: ENTRY PROC = {
base ← 0;
WHILE len > 0 DO
notready ← TRUE;
Space.CopyIn[space, [cap, base+1]];
IFSFile.StartWrite[fh, base*Environment.bytesPerPage,
BytesAtATime[len], buffer, CallProc, 0];
WHILE notready DO
WAIT finished;
ENDLOOP;
SELECT problem FROM
ok => NULL;
io, resources, other, credentials =>
RETURN WITH ERROR IFSFile.Error[problem];
ENDCASE => ERROR;
base ← base + NPages;
len ← len - BytesAtATime[len];
Space.Kill[space];
ENDLOOP;
};
CallProc: ENTRY IFSFile.Completer = {
problem ← outcome;
notready ← FALSE;
NOTIFY finished;
};
{ENABLE UNWIND => {
IF space ~= Space.nullHandle THEN Space.Delete[space]; space ← Space.nullHandle;
IF fh ~= NIL THEN IFSFile.Close[fh]; fh ← NIL;
Subr.FreeString[directoryfilename];
};
fh ← Open[remoteHost, remoteFilename, h, new];
space ← Space.Create[NPages, Space.virtualMemory];
buffer ← Space.LongPointer[space];
Space.Map[space];
cap ← Directory.Lookup[fileName: localFilename, permissions: Directory.ignore];
[byteLength: length, createDate: create] ← Directory.GetProps[cap, directoryfilename];
len ← length;
IFSFile.SetLength[fh, length];
DoReadAndWrite[];
Space.Delete[space];
space ← Space.nullHandle;
IFSFile.SetCreation[fh, create];
IFSFile.Close[fh];
fh ← NIL;
};
Subr.FreeString[directoryfilename];
};
PrintLeafProblem: PUBLIC PROC[problem: IFSFile.Problem] = {
CWF.WF2["Leaf Error: %s, #%u in IFSFile.Problem.\n"L,
(SELECT problem FROM
ok => "ok"L,
io => "io"L,
resources => "resources"L,
credentials => "credentials"L,
illegalIO => "illegalIO"L,
other => "other"L,
ENDCASE => ERROR),
@problem];
};
PrintLeafAccessFailure: PUBLIC PROC[reason: IFSFile.AccessFailure] = {
CWF.WF2["Leaf Error: %s, #%u in IFSFile.AccessFailure.\n"L,
(SELECT reason FROM
ok => "ok"L,
io => "io"L,
notFound => "notFound"L,
alreadyExists => "alreadyExists"L,
accessDenied => "accessDenied"L,
accessConflict => "accessConflict"L,
illegalFileName => "illegalFileName"L,
other => "other"L,
ENDCASE => ERROR),
@reason];
};
SetOtherPass: PROC[h: Subr.TTYProcs, error: STRING] = {
CheckStarted[];
CWF.WF1["Error '%s'\nEnter Connect "L, error];
Subr.GetNameandPassword[connect, connseq.user2, connseq.password2, h];
by convention, if username and password are both 0 length,
then we just abort the program
IF connseq.user2.length = 0 AND connseq.password2.length = 0 THEN {
IF h.Confirm[h.in, h.out, h.data,
"\nType y to retry with different login name, n to abort ", 'n] = 'n THEN {
CWF.WF0["No.\n"L];
SIGNAL Subr.AbortMyself;
};
CWF.WF0["Yes.\nEnter "L];
Subr.GetNameandPassword[login, NIL, NIL, h];
CWF.WFCR[];
};
};
Connect: PROC[host: LONG STRING, h: Subr.TTYProcs,
defaultUsername, defaultPassword, secondaryName, secondaryPassword: LONG STRING]
RETURNS[IFSFile.FSInstance] = {
StartLeaf[];
FOR i: CARDINAL IN [0..connseq.size) DO
IF connseq[i].host ~= NIL
AND LongString.EquivalentString[host, connseq[i].host]
THEN
RETURN[connseq[i].fsinstance];
ENDLOOP;
connseq[connseq.size].fsinstance ← MakeFSInstance[host, h,
defaultUsername, defaultPassword, secondaryName, secondaryPassword];
connseq[connseq.size].host ← Subr.CopyString[host];
IF connseq.size >= connseq.maxsize THEN {
CWF.WF0["Error - to many conns\n"L];
RETURN[NIL];
};
connseq.size ← connseq.size + 1;
RETURN[connseq[connseq.size-1].fsinstance];
};
ForceClosed: PROC[fsinstance: IFSFile.FSInstance] RETURNS[IFSFile.FSInstance] = {
FOR i: CARDINAL IN [0 .. connseq.size) DO
IF fsinstance = connseq[i].fsinstance THEN
CloseAndFree[i];
ENDLOOP;
RETURN[NIL];
};
CloseAndFree: PROC[i: CARDINAL] = {
CWF.WF1["Closing Leaf connection to %s ... "L, connseq[i].host];
IFSFile.Logout[connseq[i].fsinstance];
CWF.WF0["closed.\n"L];
Subr.FreeString[connseq[i].host];
connseq[i].host ← NIL;
connseq[i].fsinstance ← NIL;
};
MakeFSInstance: PROC[host: LONG STRING, h: Subr.TTYProcs,
defaultUsername, defaultPassword, secondaryName, secondaryPassword: LONG STRING]
RETURNS[fsinstance: IFSFile.FSInstance] = {
always returns NIL
IF host = NIL THEN {
CWF.WF0["Error: remote host has not been specified.\n"L];
RETURN[NIL];
};
CWF.WF1["Opening Leaf connection to %s.\n"L, host];
IF defaultUsername ~= NIL THEN {
fsinstance ← IFSFile.Login[host, defaultUsername, defaultPassword,
secondaryName, secondaryPassword
! IFSFile.UnableToLogin =>
IF reason = credentials THEN GOTO normal;
];
RETURN[fsinstance];
};
GOTO normal;
EXITS
normal => {
n: LONG STRING ← Subr.AllocateString[50];
p: LONG STRING ← Subr.AllocateString[50];
{ENABLE UNWIND => {Subr.FreeString[n]; Subr.FreeString[p]};
AddUserName[NIL, NIL, h];
UserCredentialsUnsafe.GetUserCredentials[name: n, password: p];
fsinstance ← IFSFile.Login[
host, n, p, secondaryName, secondaryPassword
! IFSFile.UnableToLogin => IF reason = credentials THEN {SetPass[h, NIL]; RETRY}
];
}; -- of ENABLE UNWIND
Subr.FreeString[n]; Subr.FreeString[p];
};
};
SetPass: PROC[h: Subr.TTYProcs, error: LONG STRING] = {
CheckStarted[];
IF error ~= NIL THEN CWF.WF1["Error '%s'\n"L,error];
CWF.WF0["Enter "L];
Subr.GetNameandPassword[login, NIL, NIL, h];
CWF.WFCR[];
};
AddUserName: PROC[sto: LONG STRING, spat: STRING, h: Subr.TTYProcs] = {
u: LONG STRING ← Subr.AllocateString[50];
p: LONG STRING ← Subr.AllocateString[50];
lengthzero: BOOL;
{ENABLE UNWIND => {Subr.FreeString[u]; Subr.FreeString[p]};
UserCredentialsUnsafe.GetUserCredentials[name: NIL, password: p];
lengthzero ← p = NIL OR p.length = 0;
DO
u ← NameAndPasswordOps.userName;
UserCredentialsUnsafe.GetUserCredentials[name: u, password: NIL];
IF u = NIL OR u.length = 0 OR Subr.Any[u,' ] OR Subr.Prefix[u, "CedarUser"L] OR lengthzero
THEN {
lengthzero ← FALSE;
CWF.WF0["Enter "L];
Subr.GetNameandPassword[login, NIL, NIL, h];
CWF.WFCR[];
}
ELSE EXIT;
ENDLOOP;
IF sto ~= NIL THEN CWF.SWF1[sto,spat,u];
}; -- of ENABLE UNWIND
Subr.FreeString[u]; Subr.FreeString[p];
};
StartLeaf: PROC = {
longzone: UNCOUNTED ZONE;
IF connseq ~= NIL THEN RETURN;
longzone ← Subr.LongZone[];
connseq ← longzone.NEW[ConnSeqRecord[MAXCONNS]];
connseq.user2 ← Subr.AllocateString[50];
connseq.password2 ← Subr.AllocateString[50];
};
StopLeaf: PUBLIC PROC = {
ENABLE UNWIND => connseq ← NIL;
longzone: UNCOUNTED ZONE;
IF connseq = NIL THEN RETURN;
longzone ← Subr.LongZone[];
FOR i: CARDINAL IN [0..connseq.size) DO
IF connseq[i].fsinstance ~= NIL THEN CloseAndFree[i];
ENDLOOP;
Subr.FreeString[connseq.user2];
Subr.FreeString[connseq.password2];
longzone.FREE[@connseq];
never called IFSFile.Finalize[];
};
CheckStarted: PROC = {
IF connseq = NIL THEN ERROR;
};
IFSFile.Initialize[];
}.