DIRECTORY BasicTime USING [GMT, nullGMT], FS USING [InfoProc, NameProc], FSBackdoor USING [ErrorCode, ProduceError, Version], FSName USING [BangStarFile, BangVersionFile, VersionFromRope], FSRemoteFile USING [ConfirmProc, FTPTimeToGMT, Lookup, LookupResult], FSReport USING [UnknownFile], IO USING [STREAM], Process USING [Detach, Seconds, SecondsToTicks], Rope USING [Cat, Equal, Fetch, ROPE], STP USING [Close, ConfirmProcType, Create, Delete, DesiredProperties, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NoteFileProcType, Open, Rename, Retrieve, SetDesiredProperties, SetDirectory, Store, ValidProperties], UserCredentials USING [Get]; FSRemoteFileImpl: CEDAR MONITOR IMPORTS FSBackdoor, FSName, FSRemoteFile, FSReport, Process, Rope, STP , UserCredentials EXPORTS FSRemoteFile = BEGIN Delete: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, proc: FSRemoteFile.ConfirmProc] = BEGIN Confirm: STP.ConfirmProcType -- [file: Rope.ROPE] RETURNS [answer: {do, skip, abort}, localStream: IO.STREAM] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => BEGIN answer _ IF proc [FSName.VersionFromRope[info.version]] THEN do ELSE abort; matchFound _ TRUE; END; ENDCASE => answer _ skip; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Delete[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Delete[h, file, Confirm]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; EnumerateForInfo: PUBLIC PROC [server, pattern: Rope.ROPE, proc: FS.InfoProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; continue _ IF proc[MakeFullGName[server, file], NIL, created, info.size, 0] THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => IF code = noSuchFile THEN CONTINUE -- ignore no match ELSE {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, pattern, FALSE]; STP.Enumerate[h, pattern, Note]; EXITS ReportError => ReportSTPError[h, stpCode, server, pattern, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; EnumerateForNames: PUBLIC PROC [server, pattern: Rope.ROPE, proc: FS.NameProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN continue _ IF proc[MakeFullGName[server, file]] THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => IF code = noSuchFile THEN CONTINUE -- ignore no match ELSE {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, pattern, TRUE]; STP.Enumerate[h, pattern, Note]; EXITS ReportError => ReportSTPError[h, stpCode, server, pattern, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; Info: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT] RETURNS [version: FSBackdoor.Version, bytes: INT, created: BasicTime.GMT] = BEGIN ReportError: PROC [stpCode: STP.ErrorCode] = { ReportSTPError[NIL, stpCode, server, file, wantedCreatedTime] }; result: FSRemoteFile.LookupResult; [result, version, created, bytes] _ FSRemoteFile.Lookup[server, file]; SELECT result FROM noResponse => ReportError[noNameLookupResponse]; noSuchPort => NULL; noSuchFile => IF wantedCreatedTime = BasicTime.nullGMT THEN ReportError[noSuchFile]; noSuchServer => ReportError[noSuchHost]; ok => IF wantedCreatedTime = BasicTime.nullGMT OR wantedCreatedTime = created THEN RETURN; ENDCASE; [version, bytes, created] _ STPInfo[server, file, wantedCreatedTime]; END; Rename: PUBLIC PROC [server, fromFile: Rope.ROPE, fromCreated: BasicTime.GMT, toFile: Rope.ROPE, proc: FSRemoteFile.ConfirmProc] = BEGIN h: STP.Handle _ NIL; stpCode: STP.ErrorCode; version: FSBackdoor.Version = Info[server, fromFile, fromCreated].version; IF proc[version] THEN BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, fromFile, TRUE]; fromFile _ FSName.BangVersionFile[fromFile, version]; STP.Rename[h, fromFile, toFile]; EXITS ReportError => ReportSTPError[h, stpCode, server, toFile, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; Retrieve: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, proc: PROC[fullGName: Rope.ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [IO.STREAM]] = BEGIN Confirm: STP.ConfirmProcType -- [file: Rope.ROPE] RETURNS [answer: {do, skip, abort}, localStream: IO.STREAM] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => BEGIN localStream _ proc[MakeFullGName[server, file], info.size, created]; answer _ IF localStream = NIL THEN abort ELSE do; matchFound _ TRUE; END; ENDCASE => answer _ skip; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Retrieve[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Retrieve[h, file, Confirm]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; Store: PUBLIC PROC [server, file: Rope.ROPE, str: IO.STREAM, created: BasicTime.GMT, proc: FSRemoteFile.ConfirmProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN doIt: BOOLEAN = proc [ FSName.VersionFromRope[ STP.GetFileInfo[h].version ] ]; continue _ IF doIt THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, TRUE]; STP.Store[h, file, str, Note, unknown, created]; EXITS ReportError => ReportSTPError[h, stpCode, server, file, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; STPInfo: PROC[server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT] RETURNS [version: FSBackdoor.Version, bytes: INT, created: BasicTime.GMT] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created _ FSRemoteFile.FTPTimeToGMT[info.create]; IF NOT timeSearch OR wantedCreatedTime = created THEN BEGIN version _ FSName.VersionFromRope[info.version]; bytes _ info.size; matchFound _ TRUE; continue _ IF timeSearch THEN no ELSE yes; END ELSE continue _ yes; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Enumerate[h, file, Note ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Enumerate[h, file, Note]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; MyDesiredProperties: TYPE = PACKED ARRAY STP.ValidProperties OF BoolDefaultFalse; BoolDefaultFalse: TYPE = BOOLEAN _ FALSE; 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.ROPE, justNames: BOOLEAN] = BEGIN IF Rope.Fetch[nameBody, 0] # '< THEN BEGIN -- no directory, so turn off directory defaulting and Desired-Property properties STP.SetDirectory[h, " "]; STP.SetDesiredProperties[h, all]; END ELSE BEGIN STP.SetDirectory[h, NIL]; STP.SetDesiredProperties[h, IF justNames THEN namesOnly ELSE nameSizeCreated]; END; END; MakeFullGName: PROC[server, file: Rope.ROPE] RETURNS [Rope.ROPE] = { RETURN [ Rope.Cat[ "[", server, "]", file ] ] }; ReportSTPError: PUBLIC PROC [h: STP.Handle, stpCode: STP.ErrorCode, server, file: Rope.ROPE, time: BasicTime.GMT] = BEGIN gName: Rope.ROPE = MakeFullGName[server, file]; e1: Rope.ROPE _ "Server for \""; e2: Rope.ROPE _ "\"."; code: FSBackdoor.ErrorCode; IF stpCode = noSuchFile THEN BEGIN IF h # NIL THEN ReturnConnection[server, h]; FSReport.UnknownFile[gName, time]; END ELSE BEGIN IF h # NIL THEN STP.Close[h ! STP.Error => CONTINUE]; SELECT stpCode FROM noRouteToNetwork, noNameLookupResponse => { code _ serverInaccessible; e2 _ "\" is inaccessible." }; connectionRejected => { code _ connectionRejected; e2 _ "\" rejected the connection attempt." }; connectionTimedOut => { code _ connectionTimedOut; e2 _ "\" timed-out the connection." }; accessDenied => { code _ accessDenied; e2 _ "\" denied permission to access the file." }; requestRefused => { code _ quotaExceeded; e1 _ "No quota for storing \"" }; accessError => { code _ fileBusy; e1 _ "\""; e2 _ "\" is locked on the server." }; illegalUserName => { code _ badCredentials; e1 _ "Credentials rejected when accessing \"" }; illegalFileName => { code _ illegalName; e2 _ "\" says that the file name is illegal." }; noSuchHost => { code _ unknownServer; e1 _ "Couldn't find the server for \"" }; ENDCASE => ERROR; FSBackdoor.ProduceError[code, Rope.Cat[e1, gName, e2]]; END; END; maxSlots: CARDINAL = 4; -- only need enough for all used in last TimeOut seconds TimeOut: Process.Seconds _ 10; -- keep connection around for this long after use Slot: TYPE = RECORD [ server: Rope.ROPE, h: STP.Handle, used: BOOLEAN ]; emptySlot: Slot = [NIL, NIL, FALSE]; opened, reused, reopened: INT _ 0; -- statistics slot: ARRAY [0..maxSlots) OF Slot _ ALL [emptySlot]; haveSlotTimer: BOOLEAN _ FALSE; GetConnection: ENTRY PROC[server: Rope.ROPE] RETURNS [h: STP.Handle] = BEGIN ENABLE UNWIND => NULL; FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] # emptySlot AND Rope.Equal[slot[i].server, server, FALSE] THEN BEGIN reused _ reused + 1; h _ slot[i].h; IF NOT STP.IsOpen[h] THEN { [] _ STP.Open[h, slot[i].server]; reopened _ reopened + 1 }; slot[i] _ emptySlot; RETURN; END; ENDLOOP; BEGIN user, password: Rope.ROPE; h _ STP.Create[]; [user, password] _ UserCredentials.Get[]; STP.Login[h, user, password]; [] _ STP.Open[h, server]; opened _ opened + 1; END; END; ReturnConnection: ENTRY PROC [server: Rope.ROPE, h: STP.Handle] = BEGIN IF STP.IsOpen[h] THEN FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] = emptySlot THEN BEGIN slot[i] _ [server, h, TRUE]; IF NOT haveSlotTimer THEN TRUSTED BEGIN haveSlotTimer _ TRUE; Process.Detach[FORK SlotTimer[]]; END; RETURN; END; ENDLOOP; END; SlotTimer: ENTRY PROC = BEGIN ForTimeout: CONDITION _ [Process.SecondsToTicks[TimeOut]]; DO noFullSlot: BOOLEAN _ TRUE; FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] # emptySlot THEN BEGIN IF slot[i].used THEN BEGIN slot[i].used _ FALSE; noFullSlot _ FALSE; END ELSE BEGIN STP.Close[slot[i].h ! STP.Error => CONTINUE ]; slot[i] _ emptySlot; END; END; ENDLOOP; IF noFullSlot THEN EXIT; WAIT ForTimeout; ENDLOOP; haveSlotTimer _ FALSE; END; END. jFSRemoteFileImpl.mesa Last Edited by: Schroeder, November 15, 1983 1:37 pm Last Edited by: Levin, September 22, 1983 1:04 pm Exported to FSRemoteFile Internal procedures If version part was missing from client's file name, then Enumerate will produce all versions, but we only want the !H version Property stuff Internal procedures STP connection cacheing Κ@– "cedar" style˜Icode2šœ™K™4K™1code1šΟk ˜ Lšœ œœ ˜Lšœœ˜Lšœ œ$˜4Lšœœ2˜>Lšœ œ3˜ELšœ œ˜Lšœœœ˜Lšœœ#˜0Lšœœœ˜%Lšœœξ˜χLšœœ˜—šœœ˜Lšœ<œ˜XLšœ ˜Lšœ˜—™š Οnœœœœœ#˜qKš˜šœ œΟcSœ˜rKš˜Kšœœ œ˜(Kšœœ*˜@šœœ˜Kšœ˜šœ œ˜0Kš˜Kšœ œ-œœ˜KKšœ œ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜.Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœ˜OKš˜šœœŸ3œ˜PKš˜Kšœœ œ˜(Kšœ@˜@Kš œ œ#œœœ˜]Kšœ˜—Kšœœ œ˜Kšœ œ ˜š˜šœœ ˜Kšœ˜KšœœŸ˜ Kšœœ˜(—Kšœ˜Kšœ œ˜'Kšœ˜ šœ˜Kšœ?˜?—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœ˜PKš˜šœœŸ3œ˜PKš˜Kšœ œ#œœ˜AKšœ˜—Kšœœ œ˜Kšœ œ ˜š˜šœœ ˜Kšœ˜KšœœŸ˜ Kšœœ˜(—Kšœ˜Kšœ œ˜&Kšœ˜ šœ˜Kšœ?˜?—Kšœ˜—Kšœ˜Kš˜—šžœœœœœœ&œœ˜™Kš˜šœœ ˜,Kšœœ.˜B—Kšœ"˜"KšœF˜Fšœ˜šœ ˜ Kšœ"˜"—šœ ˜ Kšœ˜—šœ ˜ Kšœ&˜(Kšœ˜—šœ˜Kšœ˜—šœ˜Kšœ'œ˜GKšœœ˜ —Kšœ˜—KšœE˜EKšœ˜—š žœ œœœœ#˜‚Kš˜Kšœœ œ˜Kšœ œ ˜KšœJ˜JKšœ˜šœ˜ Kšœœœ˜7Kšœ˜Kšœ!œ˜'Kšœ5˜5Kšœ˜ šœ˜Kšœ>˜>—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœœœœ œœœœœ˜­Kš˜šœ œŸSœ˜rKš˜Kšœœ œ˜(Kšœœ*˜@šœœ˜Kšœ˜šœ œ˜0Kš˜KšœD˜DKš œ œœœœ˜1Kšœ œ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜/Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœœœœ#˜vKš˜šœœŸ3œ˜PKš˜Kšœœ"œ˜NKšœ œœœ˜$Kšœ˜—Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜#Kšœ-˜0šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜——™šžœœœœœ&œœ˜”Kš˜šœœŸ3œ˜PKš˜Kšœœ œ˜(Kšœ1˜1Kšœœ œ˜0šœ˜ Kšœ/˜/Kšœ˜Kšœ œ˜K™~Kšœ œ œœ˜*Kš˜—Kšœ˜Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜/Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜——™Kš œœœœœœ˜QKšœœœœ˜)Kšœ-œ œ œ˜RKš œ3œ œ œœœ˜vKšœœœœŸ/˜Wš žœœœœ œ˜SKš˜Kšœ˜šœœŸQ˜\Kšœ˜Kšœ˜!Kš˜—š ˜ Kšœœ˜Kšœœ œ œ˜NKšœ˜—Kšœ˜——™š ž œœœœœ˜BKšœœ*˜2—šžœœœœœœœ˜sKš˜Kšœ œ˜/Kšœ œ˜ Kšœ œ ˜Kšœ˜Kšœ˜šœ˜ Kšœœœ˜,Kšœ"˜"Kš˜—šœ˜ Kš œœœœ œ œ˜5šœ ˜šœ)˜)Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ-˜-—šœ˜Kšœ˜Kšœ&˜&—šœ˜Kšœ˜Kšœ2˜2—šœ˜Kšœ˜Kšœ!˜!—šœ˜Kšœ˜Kšœ0˜0—šœ˜Kšœ˜Kšœ0˜0—šœ˜Kšœ˜Kšœ0˜0—šœ ˜ Kšœ˜Kšœ)˜)—Kšœœ˜—Kšœ7˜7Kšœ˜—Kšœ˜——™Kšœ œŸ8˜PKšœŸ1˜Pšœœœ˜Kšœ œ˜Kšœœ˜Kšœ˜ Kšœ˜—Kšœœœœ˜$KšœœŸ ˜0Kšœœœœ ˜4Kšœœœ˜š ž œœœœœœ ˜FKšœœœœ˜šœœœ˜#Kšœœ$œ˜Dšœ˜ Kšœ˜Kšœ˜Kšœœœ ˜Kšœœ4˜CKšœ˜Kšœ˜Kšœ˜—Kšœ˜—š˜Kšœœ˜Kšœœ ˜Kšœ)˜)Kšœ˜Kšœœ˜Kšœ˜Kšœ˜—Kšœ˜—š žœœœœœ ˜AKš˜Kšœ ˜šœœœ˜(Kšœ˜š ˜ Kšœœ˜Kšœœ˜šœ ˜Kšœœ˜Kšœœ˜!Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—šž œœœ˜Kš˜Kšœ  œ%˜:š˜Kšœ œœ˜šœœœ˜#Kšœ˜šœ˜ Kšœ ˜šœ˜ Kšœœ˜Kšœ œ˜Kš˜—šœ˜ šœ˜Kšœœ œ˜—Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜Kšœ œœ˜Kšœ ˜—Kšœ˜—Kšœœ˜Kšœ˜——Kšœ˜—…—/@Aκ