FSOpenFileImpl.mesa
Copyright © 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
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
= {
Exported to FS
GetClass:
PUBLIC
PROC [file:
FS.OpenFile]
RETURNS [a:
ATOM] = {
WITH file
SELECT
FROM
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 {
write close, so report creation
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]]
};
Exported to FSBackdoor
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[];
};
Internal procedures
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;
This condition is notified whenever EndOp discovers a NIL ActiveFile and the opCount goes to zero
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;
};
};
Start code
fQ ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[CODE[OpenFileObj], 1, fQ];
TRUSTED { Process.Detach[FORK Finalize[]] };
}.