DIRECTORY BasicTime USING [GMT], File USING [Error, Handle, Info, PagesForWords, Read, wordsPerPage, Write], FS USING [Error, Lock, OpenFile, PagesForBytes], FSBackdoor USING [MakeFName, ProduceError, Version], FSFileOps USING [GetFileInfo, GetProps, SetBytesAndCreated, SetFilePages], FSLock USING [ActiveFile, GetAttachment, RecordREF, RemoveREF, ReportClose], FSReport USING [FileError, ReportCreation], Process USING [Detach], Rope USING [Concat, ROPE], SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ]; FSOpenFileImpl: CEDAR MONITOR LOCKS f USING f: OpenFile IMPORTS File, FS, FSBackdoor, FSFileOps, FSLock, FSReport, Process, Rope, SafeStorage EXPORTS FS, FSBackdoor, FSLock = { OpenFile: TYPE = REF OpenFileObj; OpenFileObj: TYPE = MONITORED RECORD [ opCount: CARDINAL, a: FSLock.ActiveFile]; ProcFile: TYPE = REF ProcFileObj; ProcFileObj: TYPE = RECORD [ fileProcs: REF FileProcs, clientFile: REF]; GetClass: PUBLIC PROC [file: FS.OpenFile] RETURNS [a: ATOM] = { WITH file SELECT FROM f: OpenFile => a _ $FS; p: ProcFile => IF p.fileProcs.GetClass = NIL THEN NotImplemented[] ELSE a _ p.fileProcs.GetClass[p.clientFile]; ENDCASE => InvalidOpenFile[]; }; SameFile: PUBLIC PROC [file1, file2: FS.OpenFile] RETURNS [same: BOOL] = { WITH file1 SELECT FROM f1: OpenFile => WITH file2 SELECT FROM f2: OpenFile => { a1, a2: FSLock.ActiveFile; a1 _ StartOp[f1, readOp]; EndOp[f1]; a2 _ StartOp[f2, readOp]; EndOp[f2]; RETURN [ a1.h = a2.h ]; }; p2: ProcFile => RETURN [FALSE]; ENDCASE; p1: ProcFile => WITH file2 SELECT FROM f2: OpenFile => RETURN [FALSE]; p2: ProcFile => IF p1.fileProcs = p2.fileProcs THEN { IF p1.fileProcs.SameFile = NIL THEN NotImplemented[] ELSE RETURN [p1.fileProcs.SameFile[p1.clientFile, p2.clientFile]]; } ELSE RETURN [FALSE]; ENDCASE; ENDCASE; InvalidOpenFile[]; }; GetName: PUBLIC PROC [file: FS.OpenFile] RETURNS [fullFName, attachedTo: Rope.ROPE] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile _ StartOp[f, readOp]; fullFName _ FSBackdoor.MakeFName[a.nameBody, a.version]; a _ FSLock.GetAttachment[a]; IF a # NIL THEN attachedTo _ FSBackdoor.MakeFName[a.nameBody, a.version] ELSE attachedTo _ NIL; EndOp[f]; }; p: ProcFile => IF p.fileProcs.GetName = NIL THEN NotImplemented[] ELSE [fullFName, attachedTo] _ p.fileProcs.GetName[p.clientFile]; ENDCASE => InvalidOpenFile[]; }; GetInfo: PUBLIC PROC [file: FS.OpenFile] RETURNS [keep: CARDINAL, pages, bytes: INT, created: BasicTime.GMT, lock: FS.Lock] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, readOp]; [bytes, keep, created] _ FSFileOps.GetProps[a.h ! FS.Error => EndOp[f] ]; pages _ File.Info[a.h ! File.Error => { EndOp[f]; FSReport.FileError[why] } ].size; lock _ IF a.fileLock = read THEN read ELSE write; EndOp[f]; }; p: ProcFile => IF p.fileProcs.GetInfo = NIL THEN NotImplemented[] ELSE [keep, pages, bytes, created, lock] _ p.fileProcs.GetInfo[p.clientFile]; ENDCASE => InvalidOpenFile[]; }; SetPageCount: PUBLIC PROC [file: FS.OpenFile, pages: INT] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, writeOp]; FSFileOps.SetFilePages[a.h, pages ! FS.Error => EndOp[f] ]; EndOp[f]; }; p: ProcFile => IF p.fileProcs.SetPageCount = NIL THEN NotImplemented[] ELSE p.fileProcs.SetPageCount[p.clientFile, pages]; ENDCASE => InvalidOpenFile[]; }; SetByteCountAndCreatedTime: PUBLIC PROC [file: FS.OpenFile, bytes: INT, created: BasicTime.GMT] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, writeOp]; { ENABLE FS.Error => EndOp[f]; IF bytes < -1 THEN FSBackdoor.ProduceError[$badByteCount, "Tried to set a negative byte count"]; IF (bytes > -1 AND FSFileOps.GetFileInfo[a.h].pages < FS.PagesForBytes[bytes]) THEN FSBackdoor.ProduceError[$badByteCount, "Tried to set byte count beyond the end of file"]; FSFileOps.SetBytesAndCreated[a.h, bytes, created]; }; EndOp[f]; }; p: ProcFile => IF p.fileProcs.SetByteCountAndCreatedTime = NIL THEN NotImplemented[] ELSE p.fileProcs.SetByteCountAndCreatedTime[p.clientFile, bytes, created]; ENDCASE => InvalidOpenFile[]; }; Read: PUBLIC PROC [file: FS.OpenFile, from, nPages: INT, to: LONG POINTER] = TRUSTED { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, readOp]; File.Read[a.h, [from], nPages, to ! File.Error => { EndOp[f]; FSReport.FileError[why] } ]; EndOp[f]; }; p: ProcFile => IF p.fileProcs.Read = NIL THEN NotImplemented[] ELSE p.fileProcs.Read[p.clientFile, from, nPages, to]; ENDCASE => InvalidOpenFile[]; }; Write: PUBLIC PROC [file: FS.OpenFile, to: INT, nPages: INT, from: LONG POINTER] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, writeOp]; File.Write[a.h, [to], nPages, from ! File.Error => { EndOp[f]; FSReport.FileError[why] } ]; EndOp[f]; }; p: ProcFile => IF p.fileProcs.Write = NIL THEN NotImplemented[] ELSE p.fileProcs.Write[p.clientFile, to, nPages, from]; ENDCASE => InvalidOpenFile[]; }; Close: PUBLIC PROC [file: FS.OpenFile] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = InnerClose[f]; IF a = NIL THEN InvalidOpenFile[TRUE]; IF a.fileLock=write THEN { fileName: Rope.ROPE = FSBackdoor.MakeFName[a.nameBody, a.version]; FSLock.ReportClose[a]; FSReport.ReportCreation[writeClose, fileName]; } ELSE FSLock.ReportClose[a]; }; p: ProcFile => IF p.fileProcs.Close = NIL THEN NotImplemented[] ELSE p.fileProcs.Close[p.clientFile]; ENDCASE => InvalidOpenFile[]; }; WordsForPages: PUBLIC PROC[pages: INT] RETURNS [words: INT] = { RETURN [File.wordsPerPage*pages] }; BytesForPages: PUBLIC PROC[pages: INT] RETURNS [bytes: INT] = { RETURN [File.wordsPerPage*pages*2] }; PagesForWords: PUBLIC PROC [words: INT] RETURNS [pages: INT] = { RETURN [File.PagesForWords[words]] }; PagesForBytes: PUBLIC PROC [bytes: INT] RETURNS [pages: INT] = { RETURN [File.PagesForWords[(bytes+1)/2]] }; FileProcs: PUBLIC TYPE = RECORD [ GetClass: PROC [REF] RETURNS [ATOM], SameFile: PROC [REF, REF] RETURNS [BOOL], GetName: PROC [REF] RETURNS [Rope.ROPE, Rope.ROPE], GetInfo: PROC [REF] RETURNS [CARDINAL, INT, INT, BasicTime.GMT, FS.Lock], SetPageCount: PROC [REF, INT], SetByteCountAndCreatedTime: PROC [REF, INT, BasicTime.GMT], Read: UNSAFE PROC [REF, INT, INT, LONG POINTER], Write: PROC [REF, INT, INT, LONG POINTER], Close: PROC [REF] ]; CreateFileProcs: PUBLIC PROC [ GetClass: PROC [REF] RETURNS [ATOM], SameFile: PROC [REF, REF] RETURNS [BOOL], GetName: PROC [REF] RETURNS [Rope.ROPE, Rope.ROPE], GetInfo: PROC [REF] RETURNS [CARDINAL, INT, INT, BasicTime.GMT, FS.Lock], SetPageCount: PROC [REF, INT], SetByteCountAndCreatedTime: PROC [REF, INT, BasicTime.GMT], Read: UNSAFE PROC [REF, INT, INT, LONG POINTER], Write: PROC [REF, INT, INT, LONG POINTER], Close: PROC [REF] ] RETURNS [REF FileProcs] = { RETURN [ NEW [ FileProcs _ [GetClass, SameFile, GetName, GetInfo, SetPageCount, SetByteCountAndCreatedTime, Read, Write, Close] ] ]; }; CreateProcsOpenFile: PUBLIC PROC [clientFile: REF, fileProcs: REF FileProcs] RETURNS [FS.OpenFile] = { RETURN [ [ NEW [ ProcFileObj _ [fileProcs, clientFile] ] ] ]; }; GetClientFileAndProcs: PUBLIC PROC [file: FS.OpenFile] RETURNS [clientFile: REF, fileProcs: REF FileProcs] = { clientFile _ fileProcs _ NIL; WITH file SELECT FROM p: ProcFile => { clientFile _ p.clientFile; fileProcs _ p.fileProcs; }; ENDCASE; }; GetFileHandle: PUBLIC PROC [file: FS.OpenFile] RETURNS [h: File.Handle] = { WITH file SELECT FROM f: OpenFile => { a: FSLock.ActiveFile = StartOp[f, readOp]; h _ a.h; EndOp[f]; }; p: ProcFile => NotImplemented[]; ENDCASE => InvalidOpenFile[]; }; NewOpenFile: PUBLIC PROC [a: FSLock.ActiveFile] RETURNS [FS.OpenFile] = { f: OpenFile; f _ NEW [ OpenFileObj _ [opCount: 0, a: a] ]; FSLock.RecordREF[f]; SafeStorage.EnableFinalization[f]; RETURN [ [f] ] }; NotImplemented: PROC [] = { FSBackdoor.ProduceError[ notImplemented, "Operation not implemented for this file"]; }; InvalidOpenFile: PROC [closed: BOOL _ FALSE] = { FSBackdoor.ProduceError[ invalidOpenFile, Rope.Concat["File presented ", IF closed THEN "is closed." ELSE "is invalid"] ] }; StartOp: PROC [f: OpenFile, op: {writeOp, readOp}] RETURNS [a: FSLock.ActiveFile] = { InnerStartOp: ENTRY PROC [f: OpenFile] = { a _ f.a; IF a = NIL THEN { open _ FALSE; RETURN }; IF op = writeOp AND a.fileLock = read THEN { correctLock _ FALSE; RETURN }; f.opCount _ f.opCount + 1; }; open, correctLock: BOOL _ TRUE; InnerStartOp[f]; IF NOT open THEN InvalidOpenFile[TRUE]; IF NOT correctLock THEN FSBackdoor.ProduceError[wrongLock, "This operation requires a write lock and only a read lock is held."]; }; lastOpFinished: CONDITION; EndOp: ENTRY PROC [f: OpenFile] = { f.opCount _ f.opCount - 1; IF f.a = NIL AND f.opCount = 0 THEN BROADCAST lastOpFinished; }; InnerClose: ENTRY PROC [f: OpenFile] RETURNS [a: FSLock.ActiveFile] = { a _ f.a; IF f.a # NIL THEN { f.a _ NIL; -- triggers BROADCASTs on lastOpFinished UNTIL f.opCount = 0 DO WAIT lastOpFinished ENDLOOP; }; }; fQ: SafeStorage.FinalizationQueue; Finalize: PROC = { DO f: OpenFile _ NARROW [ SafeStorage.FQNext[fQ] ]; FSLock.RemoveREF[f]; IF f.a # NIL THEN Close[ [f] ! FS.Error => CONTINUE]; f _ NIL; ENDLOOP; }; fQ _ SafeStorage.NewFQ[]; SafeStorage.EstablishFinalization[CODE[OpenFileObj], 1, fQ]; TRUSTED { Process.Detach[FORK Finalize[]] }; }. 0FSOpenFileImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Bob Hagmann February 4, 1985 9:41:38 am PST Schroeder, December 14, 1983 11:43 am Levin, August 9, 1983 11:29 am Russ Atkinson (RRA) May 13, 1985 8:48:39 pm PDT Types Exported to FS write close, so report creation Exported to FSBackdoor Exported to FSLock Internal procedures This condition is notified whenever EndOp discovers a NIL ActiveFile and the opCount goes to zero Finalization stuff Start code Bob Hagmann February 4, 1985 9:41:21 am PST changes to: Copyright สƒ– "cedar" style˜codešœ™Kšœ ฯmœ1™Kšœ˜K˜—šŸ œžœžœžœ˜HK˜šžœžœžœ˜Kšœžœฯc(˜3Kšžœžœžœžœ˜3Kšœ˜—Kšœ˜K˜——™K˜K˜"šŸœžœ˜šž˜Kšœžœ˜0Kšœ˜Kš žœžœžœžœ žœ˜5Kšœžœ˜Kšžœ˜—Kšœ˜K˜——™ K˜Kšœ"žœ˜