DIRECTORY BasicTime USING [Now, ToPupTime], IFSFile USING [ AccessFailure, Completer, defaultTime, Error, FileTime, OpenOptions, Position, Problem], IFSFilePrivate USING [ connectionTimeout, DoRead, DoWrite, FileAddressToPosition, FileHandle, fileLockTimeout, FileObject, fileTimeAddress, FileWatcher, FreeSequinForFS, FSInstance, FSObject, GetIORequestBlock, GetSequinForFS, IFSTIME, IFSTimes, InsertFile, IORequest, PositionToFileAddress, PurgeFile, ReleaseFile, ValidateFile], Leaf USING [ Answer, closeOp, deleteOp, FileAddress, IfsError, openOp, paramsOp, Request, RequestObject], PrincOps USING [zEXCH], PrincOpsUtils USING [LongCopy], Rope, Sequin USING [ Broken, Buffer, Get, GetEmptyBuffer, Put, ReleaseBuffer]; IFSFileImplB: MONITOR LOCKS file.LOCK USING file: IFSFilePrivate.FileHandle IMPORTS BasicTime, IFSFile, IFSFilePrivate, PrincOpsUtils, Rope, Sequin EXPORTS IFSFile, IFSFilePrivate = { OPEN IFSFilePrivate; ROPE: TYPE = Rope.ROPE; IOSynchRecord: TYPE = RECORD [ done: BOOL, outcome: IFSFile.Problem, file: FileHandle, finished: CONDITION _ [timeout: 0]]; IOSynch: TYPE = LONG POINTER TO IOSynchRecord; IllegalExtend: ERROR = CODE; IllegalTruncate: ERROR = CODE; PacketTooSmall: ERROR = CODE; FileObject: PUBLIC TYPE = IFSFilePrivate.FileObject; FSObject: PUBLIC TYPE = IFSFilePrivate.FSObject; Open: PUBLIC PROC [instance: FSInstance, name: ROPE, options: IFSFile.OpenOptions _ oldReadOnly] RETURNS [file: FileHandle] = { DoOpen: PROC [file: FileHandle] RETURNS [problem: IFSFile.AccessFailure] = { buffer: Sequin.Buffer; request: Leaf.Request; openOpOffset: CARDINAL _ 0; Flush: PROC = { Sequin.Put[file.sequin, buffer]; buffer _ Sequin.GetEmptyBuffer[]; request _ LOOPHOLE[buffer.data]; openOpOffset _ 0; }; MapError: PROC [leafError: Leaf.IfsError] RETURNS [IFSFile.AccessFailure] = { RETURN[ SELECT leafError FROM accessDenied, illegalDIFAccess, IN [userName .. connectPassword] => accessDenied, fileNotFound, dirNotFound => notFound, fileAlreadyExists => alreadyExists, IN [nameMalformed .. nameTooLong] => illegalFileName, fileBusy => accessConflict, ENDCASE => other]; }; DO fromCache: BOOL; { -- for EXITS serverDead [file.sequin, fromCache] _ GetSequinForFS[instance]; buffer _ Sequin.GetEmptyBuffer[]; request _ LOOPHOLE[buffer.data]; IF ~fromCache THEN { request^ _ [Leaf.paramsOp, params[packetDataBytes: buffer.maxBytes, fileLockTimeout: fileLockTimeout/5, connectionTimeout: connectionTimeout/5]]; buffer.nBytes _ openOpOffset _ Leaf.paramsOp.length; IF buffer.maxBytes - buffer.nBytes <= Leaf.openOp.length THEN Flush[! Sequin.Broken => GO TO serverDead] ELSE request _ LOOPHOLE[@buffer.data.words[SIZE[params Leaf.RequestObject]]]; }; request^ _ [Leaf.openOp, open[]]; {ENABLE PacketTooSmall => { IF openOpOffset ~= 0 THEN { Flush[! Sequin.Broken => GO TO serverDead]; RETRY}; }; fs: FSInstance = file.fs; WITH openReq: request SELECT open FROM open => { IF options ~= oldReadOnly THEN openReq.write _ openReq.extend _ TRUE; SELECT options FROM oldOrNew => openReq.create _ TRUE; new => {openReq.vDefault _ next; openReq.create _ TRUE}; ENDCASE; }; ENDCASE; buffer.nBytes _ buffer.nBytes + Leaf.openOp.length; buffer _ AddRopeToBuffer[buffer, fs.primaryName]; buffer _ AddRopeToBuffer[buffer, fs.primaryPassword]; buffer _ AddRopeToBuffer[buffer, fs.secondaryName]; buffer _ AddRopeToBuffer[buffer, fs.secondaryPassword]; buffer _ AddRopeToBuffer[buffer, name]; }; -- of handling PacketTooSmall request.op.length _ buffer.nBytes - openOpOffset; Sequin.Put[file.sequin, buffer ! Sequin.Broken => GO TO serverDead]; buffer _ Sequin.Get[file.sequin ! Sequin.Broken => GO TO serverDead]; { -- for cantOpen answer: Leaf.Answer _ LOOPHOLE[buffer.data]; IF ~fromCache THEN { IF answer.op.sense ~= reply THEN {problem _ other; GO TO cantOpen}; WITH ans: answer SELECT answer.op.type FROM error => {problem _ MapError[ans.error]; GO TO cantOpen}; params => NULL; ENDCASE => {problem _ other; GO TO cantOpen}; IF answer.op.length ~= buffer.nBytes THEN answer _ answer + answer.op.length/2 ELSE { Sequin.ReleaseBuffer[buffer]; buffer _ Sequin.Get[file.sequin ! Sequin.Broken => GO TO serverDead]; answer _ LOOPHOLE[buffer.data]; }; }; IF answer.op.sense ~= reply THEN {problem _ other; GO TO cantOpen}; WITH ans: answer SELECT answer.op.type FROM error => {problem _ MapError[ans.error]; GO TO cantOpen}; open => { problem _ ok; file.leafHandle _ ans.handle; file.length _ FileAddressToPosition[ans.eofAddress]; }; ENDCASE => {problem _ other; GO TO cantOpen}; EXITS cantOpen => FreeSequinForFS[instance, file.sequin, problem ~= other]; }; -- for cantOpen Sequin.ReleaseBuffer[buffer]; EXIT EXITS serverDead => { FreeSequinForFS[instance, file.sequin, FALSE]; IF fromCache THEN LOOP -- perhaps sequin had timed out ELSE {problem _ io; EXIT}; }; }; ENDLOOP; }; file _ InsertFile[instance, name, DoOpen]; file.watcher _ FORK FileWatcher[file]; }; CantOpen: PUBLIC ERROR [reason: IFSFile.AccessFailure] = CODE; Close: PUBLIC PROC [file: FileHandle] = { DoClose: PROC [file: FileHandle] = {CleanupFile[file, close]}; ValidateFile[file]; ReleaseFile[file, DoClose]; }; Abandon: PUBLIC PROC [file: FileHandle] = { DoAbandon: PROC [file: FileHandle] = {CleanupFile[file, abandon]}; ValidateFile[file]; ReleaseFile[file, DoAbandon]; }; Destroy: PUBLIC PROC [file: FileHandle] = { DoDestroy: PROC [file: FileHandle] = {CleanupFile[file, destroy]}; ValidateFile[file]; PurgeFile[file, DoDestroy]; }; GetLength: PUBLIC ENTRY PROC [file: FileHandle] RETURNS [IFSFile.Position] = { RETURN[file.length] }; SetLength: PUBLIC PROC [file: FileHandle, length: IFSFile.Position] = { oldLength: IFSFile.Position; ValidateFile[file]; oldLength _ GetLength[file]; IF oldLength ~= length THEN DoIO[file, PrepareSetLength[file, length]]; }; GetTimes: PUBLIC PROC [file: FileHandle] RETURNS [read, write, create: IFSFile.FileTime] = { times: IFSTimes; request: IORequest _ GetIORequestBlock[]; request^ _ [buffer: @times, address: fileTimeAddress, op: read, bytesToGo: 2*SIZE[IFSTimes]]; DoIO[file, request]; RETURN[ IFSToMesaTime[times.read], IFSToMesaTime[times.write], IFSToMesaTime[times.create]] }; SetCreation: PUBLIC PROC [ file: FileHandle, create: IFSFile.FileTime _ IFSFile.defaultTime] = { creation: IFSTIME; request: IORequest _ GetIORequestBlock[]; IF create = IFSFile.defaultTime THEN create _ BasicTime.ToPupTime[BasicTime.Now[]]; creation _ MesaToIFSTime[create]; request^ _ [ buffer: @creation, address: fileTimeAddress, op: write, bytesToGo: 2*SIZE[IFSTIME]]; DoIO[file, request]; }; AddRopeToBuffer: PUBLIC PROC [buffer: Sequin.Buffer, r: ROPE] RETURNS [Sequin.Buffer] = { flat: ROPE = Rope.Flatten[r]; s: LONG STRING _ LOOPHOLE[flat]; startWord: CARDINAL = (buffer.nBytes + 1)/2; nWords: CARDINAL; IF s = NIL THEN s _ ""L; nWords _ (s.length + 1)/2; IF buffer.maxBytes < 2*(startWord + nWords) THEN ERROR PacketTooSmall; buffer.data.words[startWord] _ s.length; PrincOpsUtils.LongCopy[ from: @s.text, to: @buffer.data.words[startWord + 1], nwords: nWords]; buffer.nBytes _ buffer.nBytes + 2*(nWords + 1); RETURN [buffer]; }; PrepareSetLength: ENTRY PROC [file: FileHandle, length: IFSFile.Position] RETURNS [request: IORequest] = { ENABLE UNWIND => NULL; request _ GetIORequestBlock[]; request^ _ [buffer: NIL, address: PositionToFileAddress[length, [write[newEOF: TRUE]]], op: write, bytesToGo: 0]; file.length _ length; }; CleanupFile: ENTRY PROC [file: FileHandle, op: {close, destroy, abandon}] = { ENABLE UNWIND => NULL; toCache: BOOL _ FALSE; file.state _ closing; BROADCAST file.ioSynch; UNTIL file.state = closed DO WAIT file.ioSynch ENDLOOP; IF (JOIN file.watcher) AND op ~= abandon THEN { buffer: Sequin.Buffer _ Sequin.GetEmptyBuffer[]; request: Leaf.Request _ LOOPHOLE[buffer.data]; IF op = close THEN { request^ _ [Leaf.closeOp, close[handle: file.leafHandle]]; buffer.nBytes _ Leaf.closeOp.length; } ELSE { request^ _ [Leaf.deleteOp, delete[handle: file.leafHandle]]; buffer.nBytes _ Leaf.deleteOp.length; }; Sequin.Put[file.sequin, buffer ! Sequin.Broken => GO TO ignore]; buffer _ Sequin.Get[file.sequin ! Sequin.Broken => GO TO ignore]; Sequin.ReleaseBuffer[buffer]; toCache _ TRUE; EXITS ignore => NULL; }; FreeSequinForFS[file.fs, file.sequin, toCache]; }; DoIO: PROC [file: FileHandle, request: IORequest] = { ioSynch: IOSynchRecord _ [done: FALSE, outcome: ok, file: file]; WaitForCompletion: ENTRY PROC [file: FileHandle] = INLINE {UNTIL ioSynch.done DO WAIT ioSynch.finished; ENDLOOP}; request.proc _ NoteCompletion; request.arg _ LOOPHOLE[LONG[@ioSynch]]; SELECT request.op FROM read => DoRead[file, request]; write => DoWrite[file, request]; ENDCASE; WaitForCompletion[file]; IF ioSynch.outcome ~= ok THEN ERROR IFSFile.Error[ioSynch.outcome]; }; NoteCompletion: IFSFile.Completer = { ioSynch: IOSynch = LOOPHOLE[arg, IOSynch]; DoNotify: ENTRY PROC [file: FileHandle] = INLINE { ioSynch.done _ TRUE; ioSynch.outcome _ outcome; NOTIFY ioSynch.finished; }; DoNotify[ioSynch.file]; }; IFSToMesaTime: PROC [IFSTIME] RETURNS [IFSFile.FileTime] = MACHINE CODE { PrincOps.zEXCH; }; MesaToIFSTime: PROC [IFSFile.FileTime] RETURNS [IFSTIME] = MACHINE CODE { PrincOps.zEXCH; }; }. 4IFSFileImplB.mesa Levin - 13-Jan-82 10:03:26 Russ Atkinson, November 9, 1983 11:44 pm Types -- Miscellaneous -- Procedures, Types, and Signals Exported to IFSFile -- send paramsOp only on fresh sequin process params response Procedures Exported to IFSFilePrivate -- Internal Procedures -- op = destroy Ê è˜šœ™Jšœ™J™(—J˜šÏk ˜ Jšœ œ˜!šœœ˜J˜X—šœœ˜J˜WJ˜PJšœ#œ"˜LJ˜=—šœœ˜ J˜CJ˜—Jšœ œ ˜Jšœœ ˜Jšœ˜šœœ˜J˜9—J˜—šœ˜Jšœœœ ˜5Jšœ@˜GJšœ˜#Jšœ˜J˜J˜Jšœ™J˜Jšœœœ˜J˜šœœœ˜Jšœœ˜ J˜J˜Jšœ œ˜$J˜—Jš œ œœœœ˜.J˜J˜Jšœ™J˜Jšœœœ˜Jšœœœ˜Jšœœœ˜J˜J˜Jšœ5™5J˜Jšœ œœ˜4Jšœ œœ˜0J˜šÏnœœ˜Jšœœ-˜NJšœ˜J˜šžœœœ%˜LJ˜J˜Jšœœ˜J˜šžœœ˜J˜ J˜!Jšœ œ˜ J˜Jšœ˜J˜—šžœœœ˜Mšœ˜šœ ˜J˜Jšœ/˜1J˜&J˜#Jšœ3˜5J˜Jšœ ˜——šœ˜J˜——š˜Jšœ œ˜šœÏc˜J˜4J˜!Jšœ œ˜ šœ œ˜Jšœ"™"˜J˜(J˜#J˜)—J˜4šœ6˜8Jšœœœ ˜/Jšœ œœ˜M—Jšœ˜—J˜!šœ˜šœ˜šœœ˜Jšœœœ ˜+Jšœ˜—Jšœ˜—J˜šœœ˜&šœ ˜ Jšœœ"œ˜Ešœ ˜Jšœœ˜"Jšœ2œ˜8Jšœ˜—Jšœ˜—Jšœ˜—J˜3Jšœ1˜1Jšœ5˜5Jšœ3˜3Jšœ7˜7Jšœ'˜'JšœŸ˜!—J˜1Jšœ2œœ ˜DJšœ3œœ ˜EšœŸ˜Jšœœ˜,šœ ˜Jšœ˜Jšœ™Jšœœœœ ˜Cšœ œ˜+Jšœ)œœ ˜9Jšœ œ˜Jšœœœ ˜-—Jšœ#œ%˜Nš˜Jšœ˜J˜Jšœ3œœ ˜EJšœ œ˜Jšœ˜—Jšœ˜—Jšœœœœ ˜Cšœ œ˜+Jšœ)œœ ˜9šœ ˜ J˜ J˜J˜4Jšœ˜—Jšœœœ ˜-—š˜J˜E—JšœŸ˜—J˜Jš˜š˜šœ˜Jšœ'œ˜.Jšœ œœŸ˜7Jšœœ˜Jšœ˜——Jšœ˜—Jšœ˜—Jšœ˜J˜—J˜*Jšœœ˜&Jšœ˜J˜—Jšœ œœ#œ˜>J˜šžœœœ˜)Jšžœœ1˜>J˜J˜Jšœ˜J˜—šžœœœ˜+Jšž œœ3˜BJ˜J˜Jšœ˜J˜—šžœœœ˜+Jšž œœ3˜BJ˜J˜Jšœ˜J˜—š ž œœœœœ˜NJšœ ˜Jšœ˜J˜—šž œœœ1˜GJ˜J˜J˜Jšœœ,˜GJšœ˜J˜—šžœœœ˜(Jšœ,˜3J˜J˜)˜5Jšœœ ˜'—J˜šœ˜J˜6J˜—Jšœ˜J˜—šž œœœ˜JšœE˜EJšœ œ˜J˜)Jšœœ/˜SJ˜!˜ JšœEœœ˜T—J˜Jšœ˜J˜J˜—Jšœ(™(J˜šžœœ˜Jšœœœ˜J˜—J˜Jšœ˜J˜—šž œœœ6˜MJšœœœ˜Jšœ œœ˜Jšœ˜Jš œ˜Jšœœœœ˜7šœœœœ˜/Jšœ0˜0Jšœœ˜.šœ œ˜J˜:J˜$Jšœ˜—šœ˜Jšœ ™ Jšœ<˜