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 USING [Flatten, 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; }; }. fIFSFileImplB.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Levin - 13-Jan-82 10:03:26 Russ Atkinson, March 7, 1985 2:17:42 pm PST 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 Κ Ν˜codešœ™Kšœ Οmœ1™K˜šŸœžœžœ˜)KšŸœžœ1˜>K˜K˜Kšœ˜K˜—šŸœžœžœ˜+KšŸ œžœ3˜BK˜K˜Kšœ˜K˜—šŸœžœžœ˜+KšŸ œžœ3˜BK˜K˜Kšœ˜K˜—š Ÿ œžœžœžœžœ˜NKšžœ ˜Kšœ˜K˜—šŸ œžœžœ1˜GK˜K˜K˜Kšžœžœ,˜GKšœ˜K˜—šŸœžœžœžœ,˜\K˜K˜)˜5Kšœžœ ˜'—K˜šžœ˜K˜6K˜—Kšœ˜K˜—šŸ œžœžœ˜KšœE˜EKšœ žœ˜K˜)Kšžœžœ/˜SK˜!˜ KšœEžœžœ˜T—K˜Kšœ˜K˜K˜——šœ%™%K˜š Ÿœžœžœžœžœ˜YKšœžœ˜Kšœžœžœžœ˜ Kšœ žœ˜,Kšœžœ˜Kšžœžœžœ ˜K˜Kšžœ*žœžœ˜FK˜(šœ˜KšœF˜F—K˜/Kšžœ ˜Kšœ˜K˜K˜——šœ™K˜šŸœžœžœ-žœ˜jKšžœžœžœ˜K˜šœžœ˜Kšœ6žœ˜>K˜—K˜Kšžœ˜K˜—šŸ œžœžœ6˜MKšžœžœžœ˜Kšœ žœžœ˜Kšœ˜Kšž œ˜Kšžœžœžœžœ˜7šžœžœžœžœ˜/Kšœ0˜0Kšœžœ˜.šžœ žœ˜K˜:K˜$Kšœ˜—šžœ˜Kšœ ™ Kšœ<˜