-- LeafSubrImpl.Mesa, last edit December 30, 1982 2:11 pm -- Pilot 6.0/ Mesa 7.0 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 STRING ← NIL, -- connect name password2: LONG STRING ← NIL, -- connect password size: CARDINAL ← 0, -- # of connections body: SEQUENCE maxsize: CARDINAL OF ConnTable ]; ConnTable: TYPE = RECORD[ fsinstance: IFSFile.FSInstance ← NIL, host: LONG STRING ← NIL ]; 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 ]; }; -- 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) RemoteMap: PUBLIC PROC[space: Space.Handle, fh: IFSFile.FileHandle, base: CARDINAL] = { 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]; { 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; }; 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; }; 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: STRING ← [100]; { ENABLE UNWIND => { IF space ~= Space.nullHandle THEN Space.Delete[space]; space ← Space.nullHandle; IF fh ~= NIL THEN IFSFile.Close[fh]; fh ← NIL; }; 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; }; 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; }}; 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; }; -- always returns NIL MakeFSInstance: PROC[host: LONG STRING, h: Subr.TTYProcs, defaultUsername, defaultPassword, secondaryName, secondaryPassword: LONG STRING] RETURNS[fsinstance: IFSFile.FSInstance] = { 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: STRING ← [50]; p: STRING ← [50]; AddUserName[NIL, NIL, h]; UserCredentialsUnsafe.GetUserCredentials[name: n, password: p]; fsinstance ← IFSFile.Login[host, -- NameAndPasswordOps.userName -- n, -- NameAndPasswordOps.userPassword -- p, secondaryName, secondaryPassword ! IFSFile.UnableToLogin => IF reason = credentials THEN { SetPass[h, NIL]; RETRY; } ]; }; }; 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: STRING; -- p: STRING ← NameAndPasswordOps.userPassword; u: STRING ← [50]; p: STRING ← [50]; lengthzero: BOOL; 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]; }; StartLeaf: PROC = { longzone: UNCOUNTED ZONE; IF connseq ~= NIL THEN RETURN; longzone ← Subr.LongZone[]; connseq ← longzone.NEW[ConnSeqRecord[MAXCONNS]]; connseq.user2 ← Subr.AllocateString[40]; connseq.password2 ← Subr.AllocateString[40]; }; 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[]; }.