PFS.mesa
Copyright Ó 1985, 1986, 1987, 1991 by Xerox Corporation. All rights reserved.
File system defined by M. D. Schroeder
File stream creation facilities defined by Mark Brown
Carl Hauser, June 8, 1989 1:05:33 pm PDT
Willie-s, August 20, 1991 4:05 pm PDT
Doug Wyatt, August 23, 1991 12:31 pm PDT
Michael Plass, November 22, 1991 3:46 pm PST
Interface for the PortableCedar File System; see also PFSBackdoor.mesa.
DIRECTORY
Basics USING [RawBytes],
BasicTime USING [ExtendedGMT, GMT, nullGMT],
IO USING [STREAM],
PFSNames USING [PATH],
Rope USING [ROPE];
PFS: CEDAR DEFINITIONS
=
BEGIN
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
PATH: TYPE ~ PFSNames.PATH;
File Names
PFS procedures that operate on named files accept an argument of TYPE PATH. PATHs are a generic representation for file names on hierarchically organized file systems. See PFSNames.mesa for syntactic operations on PATHs. Clients that just want to convert ROPEs to PATHs and v.v. can use the following procedures:
NameFormat: TYPE ~ {slashes, brackets};
RopeFromPath:
PROC [path:
PATH, format: NameFormat ¬ slashes]
RETURNS [rope:
ROPE];
The format argument determines whether the result has slashes as separators, i.e. "/.../.../..." or Cedar-style brackets, i.e. "[...]<...>...".
PathFromRope:
PROC [rope:
ROPE]
RETURNS [path:
PATH];
Accepts a rope separated either by slashes or brackets and parses it to form path.
Working Directory
A working directory is associated with each Cedar PROCESS. Relative names presented to PFS procedures are interpreted relative to the calling PROCESS's working directory.
DoInWDir:
PROC [ wDir:
PATH, inner:
PROC ];
Execute inner with wDir as the PROCESS's working directory. The existing working directory is restored when inner returns.
GetWDir: PROC RETURNS [ wDir: PATH ];
AbsoluteName:
PROC [short:
PATH, wDir:
PATH ¬
NIL]
RETURNS [absolute:
PATH];
If short is not, itself, an absolute name, prepends WDir if non-NIL else prepends GetWDir[].
Prefix Map
All names presented to procedures below are first passed through prefix map translation by the PFS implentation and names generated within the PFS implementation are back-translated before being revealed to clients. See the PFSPrefixMap interface for details of the translation features available.
Errors
Clients of PFS should only have to catch the single ERROR PFS.Error. Other packages that hand out PFS.OpenFile's (see PFSBackdoor.CreateProcsOpenFile) are expected to raise PFS.Error.
ErrorGroup:
TYPE = {
ok,
-- initial group for a new
PFS.ErrorDesc
bug,
-- caused by an internal bug
environment,
-- something's wrong in the environment; human intervention required
client,
-- illegal operation, probably due to bug in client program
user
-- illegal operation, probably due to user action
};
PFS error codes are partitioned into four groups: bugs, environment errors, client errors, and user errors. A bug usually indicates that something unexpected has gone wrong inside the implementation of PFS or lower packages. An environment error means that something in the external environment has changed that requires human intervention. A client error is an attempted illegal operation that probably is caused by a bug in a client program. A user error is an attempted illegal operation that probably is caused by a human user's actions. The distinction between the last two groups is somewhat arbitrary.
ErrorDesc:
TYPE =
RECORD [group: ErrorGroup, code:
ATOM, explanation:
ROPE, info:
REF]
¬ [ok,
NIL,
NIL,
NIL];
An error description reports the group, as well as a specific code ATOM indicating exactly what went wrong. In the descriptions of procedures that follow, all codes from the error groups "client" and "user" that can be generated are specifically listed. Errors in the other groups should be expected as appropriate. See the section ERROR CODES at the end of this document for a complete list of the code ATOM's that the PFS implementation can generate. The "explanation" is intended to be a description of the error suitable for human consumption; it frequently contains the name of the file being operated upon. The "info" is intended to contain information of use to someone debugging the low-level cause of an unexpected error.
Error:
ERROR [error: ErrorDesc];
Catching all PFS.Error's with error.group = user and reporting the error.explanation to the human user is usually sufficient to deal with file names that are mistyped by humans or that do not correspond to existing files.
The PFS implementation is careful never to raise PFS.Error while monitor locks are held. Any PFS procedure can be called from a catch phrase for PFS.Error.
AtomFromErrorGroup: PROC[ErrorGroup] RETURNS [ATOM];
ErrorGroupFromAtom: PROC [ATOM] RETURNS [ErrorGroup];
File Attributes
File attributes provided and interpreted by PFS are byte count, uniqueID, file type, and mutability. See PFSDoc for current implementation state of these features. The byte count is the number of meaningful bytes in a file; the byte count is kept up-to-date by explicit client calls on PFS procedures. The uniqueID is the date and time when the contents of the file were created (or changed) together with some identification of the creating process and/or processor. The file type describes the nature of the content or attributes of the file in order to communicate to potential users of the file how the file is to be interpreted. File types may be defined by the user. PFS does not interpret the meaning of the file type. The mutability of a file characterizes whether the contents of the file may be changed.
UniqueID:
TYPE =
RECORD [
egmt: BasicTime.ExtendedGMT ¬ [BasicTime.nullGMT, 0],
host: RECORD [a, b: CARD32] ¬ [0, 0]
];
nullUniqueID: UniqueID ~ [ [BasicTime.nullGMT, 0], [0, 0] ];
Mutability: TYPE = MACHINE DEPENDENT {immutable(0), mutable(1)};
FileType: TYPE = MACHINE DEPENDENT RECORD [CARD32];
tUnspecified: FileType = [0];
tDirectory: FileType = [1];
tText: FileType = [2];
Client file properties are name-value pairs that can be associated with any file on a file system supporting the notion. The property names and the values are ROPE's.
File systems upon which PFS is implemented may or may not support some attributes and properties. For example file type and mutability attributes, together with client properties may not be supported on some file systems. The implementor of a PFS file system class should make a best effort to implement these in the interest of maintaining PFS client portability.
Versions and Keep-ing
Version numbers allow for multiple instances of a file to exist at one time. PFS enforces the policy that the version on any newly created file is larger than the largest existing version, The first available new version is the so-called "next" or !N version. When accessing an existing file, the version part of an PATH may be a number, may be a variable, or may be omitted. The variables allowed are !L, meaning the lowest existing version number, and !H, meaning the highest existing version number. When omitted, the version part defaults to !L or !H, depending on the operation being performed.
Keep processing occurs when creating a new version of a file. PFS operations that may create a new file take creatOptions as an argument. These optionally specify how many versions to keep. In this case, PFS will enumerate existing versions in decreasing order from !H. After (keep - 1) versions are encountered in this enumeration, additional versions will be deleted. If consistently specified from one create to the next, the keep is the steady state count of versions to be retained. Calling PFS.SetAttributes will have the side effect of deleting files that are obsolete relative to the keep.
Implementors of PFS classes should do their best to implement keep processing on file systems, in the interests of client portability and user convenience.
File Naming Using Unique IDs
All of the PFS procedures that accept a name argument also include a "uniqueID" argument. This provides an alternative way to name a file. If "uniqueID" is not equal to nullUniqueID, then the file to be operated upon is determined by a search for the version with the specified uniqueID. Any version part included in the name is treated as a hint. The uniqueID of a file is treated as a unique identifier of the content.
Note that it likely not possible to find a file given only its uniqueID.
Attachments
PFS includes facilities for attaching multiple names to files. For example, making an attachment is a way to give a working name to a released file. "BringOver" and "SModel" are the primary clients of the attachment mechanism. See PFSDoc for the implementation state of this feature.
Attachment to immutable file: the uniqueID is a fundamental part of the attachment.
Attachment to mutable file: disallowed? uniqueID ignored?
mutability copied to the attach point?
Attachments are created using PFS.Attach. For most PFS procedures, an attachment behaves as though the attached file had, in fact, been copied into a file having the name of the attachment when PFS.Attach was invoked. There are three exceptions. First, the various procedures for obtaining information about files inform the client when an attachment is encountered and provides the name of the attached file. Second, opening an attachment involves opening the attached file; if the attached file has been deleted, then the open will fail; Third, when an attachment is opened for writing, the association with the attached file is broken and its contents are copied into another file; the new file is given the same name the attachment had.
File Information
FileInfo:
PROC [
name:
PATH,
wantedUniqueID: UniqueID ¬ nullUniqueID
]
RETURNS [
fullFName, attachedTo:
PATH,
uniqueID: UniqueID,
bytes:
INT,
mutability: Mutability,
fileType: FileType
];
Returns information about the file specified by "name" and "wantedUniqueID". A missing version part defaults to highest. If "wantedUniqueID" is not nullUniqueID then any version number in "name" is treated as a hint; the information returned is for the file with the wanted uniqueID, found by searching all versions of the named file as necessary.
If the matching filename is not that of an attachment, then the full FName including version part is returned as "fullFName". The keep, byte count, uniqueID and file type also are returned. "attachedTo" will be NIL.
If the matching filename is that of an attachment, then the full FName of the attachment including version part is returned as "fullFName". The full FName of the attached file is returned as "attachedTo". The byte count, uniqueID and file type of the attached file also are returned. Any PFS.Error's encountered when trying to determine the byte count of an attachment are suppressed and "bytes" is reported as -1. Whenever a valid byte count for an attachment is reported then the version part in the "attachedTo" name is the true version number that corresponds to the uniqueID for the attachment; otherwise this version part will be whatever was recorded by PFS.Attach when the attachment was made.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed
Enumeration
Enumerations are specified with a "pattern" argument, which is an FName in which the character "*", meaning "match anything", may appear zero or more times. The version part of a "pattern" may be higest, lowest, all, or a valid version number; or may be omitted (defaults to all). The order of the enumeration may be different on different file systems.
Monitor locks are not held during an enumeration. Thus, the client may call PFS.Delete from a PFS.InfoProc or a PFS.NameProc. Fine point: deleting or creating (using PFS.Store or PFS.Open) ahead of the current position in an enumeration can produce unexpected results; the change may not be reflected in the enumeration.
InfoProc: TYPE = PROC [
fullFName, attachedTo: PATH,
uniqueID: UniqueID,
bytes: INT,
mutability: Mutability,
fileType: FileType
] RETURNS [continue: BOOL ¬ TRUE];
EnumerateForInfo:
PROC [pattern:
PATH, proc: InfoProc, lbound:
PATH ¬
NIL, hbound:
PATH ¬
NIL ];
"proc" is called for each FName selected by the pattern, lbound and hbound. Returning "continue" as FALSE stops the enumeration. The values passed to the InfoProc are the same as would be reported by a call to PFS.FileInfo for each of the file names encountered.
Files are enumerated only in the open interval (lbound..hbound), with the convention that as a lower bound NIL is less than any actual path and as an upper bound it is greater than any actual path. Thus the default is to enumerate all files matching the pattern.
Client errors: none
User errors: $unknownClass
NameProc: TYPE = PROC [name: PATH] RETURNS [continue: BOOL ¬ TRUE];
EnumerateForNames:
PROC [pattern:
PATH, proc: NameProc, lbound:
PATH ¬
NIL, hbound:
PATH ¬
NIL];
Because only names are provided to "proc", this enumeration may go faster than the previous one.
Client errors: none
User errors: $unknownClass
FileLookup:
PROC [name:
PATH, tryExtensions:
LIST
OF
ROPE]
RETURNS [fullPath:
PATH];
Searches for the file named by path; if path has no extension, also searches for name with each of the extensions, in the order given. If no matching file is found returns NIL.
Client errors: none
User errors: $unknownClass
FileSearch:
PROC [name:
PATH, searchRules:
LIST
OF
-- directory --
PATH]
RETURNS [fullPath:
PATH];
Searches for the file named by path; if path is relative, uses the searchRules to supply directory information. If no matching file is found returns NIL.
Client errors: none
User errors: $nonCedarVolume, $unknownServer, $unknownVolume, $illegalName
Opening Files
A PFS.OpenFile may be presented to other procedures to get access to the file's contents and properties.
Other packages can include procedures that return PFS.OpenFile's. In such cases, the arguments to the opening procedures and the detailed semantics of operations on the client-provided PFS.OpenFile's are defined by that package. (The operations are defined by providing procedures to PFSBackdoor.OpenFileFromProcs).
OpenFile: TYPE = REF OpenFileObject;
OpenFileObject: TYPE;
nullOpenFile: OpenFile = NIL;
AttributeSource: TYPE = {inherit, set};
CreateOptions:
TYPE =
RECORD [
keep:
CARDINAL ¬ 0,
-- no keep processing
setMutability: AttributeSource ¬ inherit, mutability: Mutability ¬ immutable,
setFileType: AttributeSource ¬ inherit, fileType: FileType ¬ tUnspecified
];
defaultCreateOptions: CreateOptions ~ [];
AccessOptions: TYPE = {read, write, create, append};
Open:
PROC [name:
PATH,
access: AccessOptions ¬ read,
wantedUniqueID: UniqueID ¬ nullUniqueID,
checkFileType:
BOOL ¬
FALSE, fileType: FileType ¬ tUnspecified,
createOptions: CreateOptions ¬ defaultCreateOptions
]
RETURNS [OpenFile];
A PFS.FileInfo[name, wantedUniqueID] is done. If a PFS.Error does not occur, then the file described by the result of PFS.FileInfo is opened. If an "attachedTo" is returned then that attached file is the one opened. When an attachment is opened for writing, the attachment is broken and the contents of the attached file are copied into a new file, which new file assumes the name of the attachment. If checkFileType is TRUE, an error occurs if the file's type does not match fileType. Multiple calls on PFS.Open produce different PFS.OpenFile's.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $fileTypeMismatch
Store and Retrieve
Some classes of servers may not support read/write access to their files. Others may support stream store and retrieve which may be faster than the same operations implemented with read and write RPCs. Store and Retrieve give the client access to this special case.
RetrieveConfirmProc: TYPE = PROC[fullFName: PATH, bytes: INT, uniqueID: UniqueID] RETURNS [IO.STREAM];
Retrieve:
PROCEDURE [name:
PATH,
wantedUniqueID: UniqueID ¬ nullUniqueID,
proc: RetrieveConfirmProc,
checkFileType:
BOOL ¬
FALSE, fileType: FileType ¬ tUnspecified
];
Tries to find the file specified by "file" and "wantedUniqueID". If the file is located then "proc" is called with the full name, bytes and uniqueID. If NIL is returned then Retrieve aborts and returns to its caller. Otherwise, the data bytes from the remote file are put into the IO.STREAM; the client must close the IO.STREAM.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $fileTypeMismatch
StoreConfirmProc:
TYPE =
PROC [fullName:
PATH]
RETURNS [continue:
BOOLEAN ¬
TRUE];
Reports the full name, including version, of the file to be stored into. Returning FALSE stops the action, TRUE allows it to continue.
Store:
PROCEDURE [name:
PATH,
wantedUniqueID: UniqueID,
str:
IO.
STREAM,
proc: StoreConfirmProc ¬
NIL,
createOptions: CreateOptions ¬ defaultCreateOptions
];
Tries to store the bytes obtained from "str" into the named file. Sets the uniqueID of the remote file to "wantedUniqueID". "proc" is called with the name of the file being created (including version number if any). Defaulting proc to NIL allows store to proceed unconditionally.
Client errors: none
User errors: $unknownClass, $patternNotAllowed, $cantCreate
Attach:
PROC [attachment, attachedFile:
PATH,
keep:
CARDINAL ¬ 0,
-- no keep processing
wantedUniqueID: UniqueID ¬ nullUniqueID,
remoteCheck:
BOOL ¬
TRUE
]
RETURNS [toFName:
PATH];
make the contents referenced by "attachedFile" accessible using "attachment". IF remoteCheck make sure that the "attachedFile" exists.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $versionSpecified (on "attachment" name)
NameConfirmProc: TYPE ~ PROC [fullName: PATH, uniqueID: UniqueID] RETURNS [continue: BOOL ¬ FALSE];
Delete:
PROC [name:
PATH,
wantedUniqueID: UniqueID ¬ nullUniqueID,
confirmProc: NameConfirmProc ¬
NIL
];
A missing version part defaults to !L. If "name" names an attachment, then the attachment is broken and the name removed; the attached file is not deleted.
Deleting the highest version is generally a bad thing (except when it is also the lowest version). Defaulting the confirmProc to NIL allows deletion to proceed unconditionally.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed
Rename:
PROC [from, to:
PATH,
wantedUniqueID: UniqueID ¬ nullUniqueID,
createOptions: CreateOptions ¬ defaultCreateOptions,
confirmProc: NameConfirmProc ¬
NIL
];
A missing version part defaults to !H for the "from" name. Renaming implying copying from one file system to another is allowed. Defaulting the confirmProc to NIL allows renaming to proceed unconditionally.
Client errors: none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $versionSpecified (on "to" name)
Copy:
PROC [from, to:
PATH,
wantedUniqueID: UniqueID ¬ nullUniqueID,
createOptions: CreateOptions ¬ defaultCreateOptions,
confirmProc: NameConfirmProc
];
A missing version part defaults to !H for the "from" name. Copying within a file system may be implemented without moving the bits (in the case of an immutable file). Note that copying need not be a primitive notion in the interface but two factors suggest it should be here: the need to deal gracefully with attributes and properties and the ability of a class implementation to implement Copy within a file system without actually copying the bits.
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $versionSpecified (on "to" name)
Operations On Open Files
Warning: the PFS implementation currently provides no synchronization of operations on open files. Since the operations defined here are not implemented atomically, concurrent invocations on a single open file may produce unexpected results. See PFSDoc for up-to-date information on this problem.
GetClass:
PROC [file: OpenFile]
RETURNS [
ATOM];
Returns an ATOM that identifies the implementation for operations on "file". If "file" was obtained by calling PFS.Open, PFS.Create, or PFS.OpenOrCreate, then "$PFS" is returned. If "file" was obtained by calling some other package that manufactures PFS.OpenFiles via FSBackdoor.CreateProcsOpenFile, then the ATOM returned is that provided by that package.
Client errors: $invalidOpenFile, $notImplemented
User errors: none
GetName:
PROC [file: OpenFile]
RETURNS [fullFName, attachedTo:
PATH];
The full name of the opened file is returned as "fullFName". If the name that was opened was that of an attachment, then the name of the attached file is returned as "attachedTo", which otherwise is NIL.
Client errors: $invalidOpenFile, $notImplemented
User errors: none
GetInfo:
PROC [file: OpenFile]
RETURNS [
fullFName, attachedTo:
PATH,
uniqueID: UniqueID,
bytes:
INT,
mutability: Mutability,
fileType: FileType
];
Returns the same information as FileInfo[GetName[file]];
Client errors: $invalidOpenFile, $notImplemented
User errors: none
SetAttributes:
PROC [file: OpenFile, attributes: CreateOptions];
Sets the specified attributes of the file.
Client errors: $invalidOpenFile, $notImplemented
User errors: none
SetByteCountAndUniqueID:
PROC [
file: OpenFile,
bytes:
INT ¬ -1,
uniqueID: UniqueID ¬ nullUniqueID
];
Sets the byte count and uniqueID properties of "file". If "uniqueID" is nullUniqueID then the uniqueID of the file is not altered. If "bytes" is -1 then the byte count is not altered.
Client errors: $badByteCount, $invalidOpenFile, $notImplemented
User errors: none
Read:
UNSAFE
PROC [file: OpenFile, filePosition:
CARD, nBytes:
CARD,
to:
POINTER
TO Basics.RawBytes, toStart:
CARD ¬ 0
]
RETURNS [bytesRead:
INT];
Copies "nBytes" bytes from the file starting at "filePosition" to virtual memory starting at to[toStart]. The "first" data byte in the file is numbered "0". Note that ERROR's caused by "to" being an invalid address are not mapped to PFS.Error's.
Client errors: $positionNotInFile, $invalidOpenFile, $notImplemented
User errors: none
Write:
PROC [file: OpenFile, filePosition:
CARD, nBytes:
CARD,
from:
POINTER
TO Basics.RawBytes, fromStart:
CARD ¬ 0
]
RETURNS [bytesWritten:
INT];
Copies "nBytes" bytes from virtual memory starting at from[fromStart] to the file starting at "filePosition". The "first" data byte in the file is numbered "0". Note that ERROR's caused by "from" being an invalid address are not mapped to PFS.Error's.
Client errors: $positionNotInFile, $invalidOpenFile, $notImplemented
User errors: none
Close:
PROC [file: OpenFile];
Releases resources associated with the open file. Any further operations on copies of this PFS.OpenFile will produce a PFS.Error[client, $invalidOpenFile]. The resources will also be released by finalization when all copies of the PFS.OpenFile disappear, but this occurs at an unspecified future time; thus, in most cases it is preferable to explicitly Close a file, rather than to rely on finalization.
Client errors: $invalidOpenFile, $notImplemented
User errors: none
SetKeep, ChangeFileType: subsumed by SetAttributes.
SetClientProperty:
PROC [file: OpenFile, propertyName:
ROPE, propertyValue:
ROPE];
Changes the specified client property for the named file. A propertyValue of NIL removes a property. The amount of space for client properties is implementation dependent (and may be 0 on some file systems).
Client errors: $notImplemented, $outOfPropertySpace
User errors: none
GetClientProperty:
PROC [file: OpenFile, propertyName:
ROPE]
RETURNS [propertyValue:
ROPE];
Gets the specified client property for the named file. A propertyValue of NIL indicates that there is no property for the file.
Client errors: $notImplemented
User errors: none
PropProc: TYPE = PROC [
propertyName: ROPE,
propertyValue: ROPE
] RETURNS [continue: BOOL ¬ TRUE];
EnumerateClientProperties:
PROC [file: OpenFile, proc: PropProc];
"proc" is called for each client property that has a non-NIL value. The enumeration is done over the properties and their values that existed when the call is made to EnumerateClientProperties, so updates to the properties will not be seen by the enumerator during the enumeration.
Client errors: $notImplemented
User errors: none
PFSNameToUnixName:
PROC [file:
PATH]
RETURNS [
ROPE];
For views that use the unix file system, returns the unix name corresponding to file. The implementor should try to avoid file system access; however, to implement the translation of file!H file system access will, of course, be needed. If no unix name can be deduced, returns NIL.
CaseSensitive:
PROC [file:
PATH]
RETURNS [
BOOL];
Returns TRUE iff the case of any character in the file name is significant. If the class doesn't implement the proc, raises Error[notImplemented];
Files as Ropes
RopeOpen:
PROC [fileName:
PATH, wantedUniqueID: UniqueID ¬ nullUniqueID, includeFormatting:
BOOL ¬
FALSE, checkMutability:
BOOL ¬
TRUE]
RETURNS [rope:
ROPE, fullFName:
PATH, uniqueID: UniqueID];
Creates a ROPE from the contents of the file, assuming that the file is immutable. See the RopeFile interface for further information. If checkMutability is TRUE, the contents will be loaded into memory unless the file is immutable.
File Streams
Conceptual File Stream Model
A file is a sequence [0..byteLength) of mutable bytes; the length (byteLength) of a file can be changed.
File: TYPE = [
byteLength: CARD,
data: SEQUENCE COMPUTED CARD OF BYTE -- computed length is byteLength
];
FileStream: TYPE = [
file: File,
streamIndex: CARD, -- IN [0..file.byteLength]
closed: BOOL,
accessRights: AccessOptions,
... more stuff
];
The state of a file stream is a file (perhaps opened under a transaction trans) with length byteLength, an index streamIndex IN [0..byteLength], plus a number of readonly flags (accessRights, streamOptions) and a BOOL variable closed which jointly determine the effect of stream procs.
Here is what the generic stream operations do when applied to a file stream:
IO.GetChar: PROC [self: STREAM] RETURNS [CHAR]
If streamIndex = byteLength then ERROR IO.EndOfStream. Else return file[streamIndex], and set streamIndex ¬ streamIndex + 1.
IO.CharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT]
Return INT.LAST.
IO.EndOf: PROC [self: STREAM] RETURNS [BOOL]
Return streamIndex = file.byteLength.
IO.PutChar: PROC [self: STREAM, char: CHAR]
If streamIndex = file.byteLength then file.byteLength ¬ file.byteLength + 1. Then set file[streamIndex] ¬ char, streamIndex ¬ streamIndex + 1.
IO.Flush: PROC [self: STREAM]
Write current value of file to disk.
IO.Reset: PROC [self: STREAM]
If accessRights = $read then set streamIndex ¬ fileLen. If accessRights = $create OR accessRights = openOrCreate then set streamIndex ¬ fileLen ¬ 0, otherwise
IO.Close: PROC [self: STREAM, abort: BOOL ¬ FALSE]
If NOT abort, then write current value of file[0..fileLen) to be written to disk and write fileLen to disk. Invalidate self (all operations on self other than Reset and Close will raise ERROR IO.Error[StreamClosed]; these two do nothing).
IO.GetIndex: PROC [self: STREAM] RETURNS [INT]
Return streamIndex.
IO.SetIndex: PROC [self: STREAM, index: INT]
ERROR IO.EndOfStream if index > fileLen. Set streamIndex ¬ index.
IO.GetLength: PROC [self: STREAM] RETURNS [INT]
Return fileLen.
IO.SetLength: PROC [self: STREAM, length: INT]
Set fileLen ¬ length, then set streamIndex ¬ MIN[streamIndex, fileLen]. The contents of file[oldFileLen .. fileLen) are undefined.
Concurrency
File streams provide no interlocks of their own to control concurrent access to files.
Individual file streams are independent objects, so there is never any need to synchronize calls to their procedures. If two processes are to share a single file stream, they must synchronize their accesses to that stream at a level above the file stream calls; individual file stream calls (PutChar, PutBlock, etc.) are not guaranteed to be atomic.
Creating File Streams
StreamOptions:
TYPE =
PACKED
ARRAY StreamOption
OF
BOOL
¬ [includeFormatting: FALSE, closeFSOpenFileOnClose: TRUE];
StreamOption:
TYPE = {
includeFormatting,
stream will include the Tioga formatting following the data part of the file.
closeFSOpenFileOnClose
each IO.Close call performs PFS.Close on the underlying PFS.OpenFile.
};
defaultStreamOptions: StreamOptions = [
includeFormatting: FALSE,
closeFSOpenFileOnClose: TRUE
];
StreamBufferParms:
TYPE =
RECORD [
bytesPerBuffer: INT [0 .. 63*LONG[1024]] ¬ 4*1024,
nBuffers: INT [0 .. 64] ¬ 2
];
defaultStreamBufferParms: StreamBufferParms = [];
minimumStreamBufferParms: StreamBufferParms = [bytesPerBuffer: 512, nBuffers: 1];
Controls the buffering strategy used by a stream.
ByteCount: TYPE = INT;
StreamOpen:
PROC [fileName:
PATH,
accessOptions: AccessOptions ¬ read,
wantedUniqueID: UniqueID ¬ nullUniqueID,
checkFileType:
BOOL ¬
FALSE,
fileType: FileType ¬ tUnspecified,
createOptions: CreateOptions ¬ defaultCreateOptions,
streamOptions: StreamOptions ¬ defaultStreamOptions,
streamBufferParms: StreamBufferParms ¬ defaultStreamBufferParms
]
RETURNS [
STREAM];
Create a new stream by (1) generating an PFS.OpenFile from fileName then (2) creating a stream on this PFS.OpenFile.
The value of accessOptions determines the set of operations allowed on the stream (disallowed operations raise IO.Error[$NotImplementedForThisStream, stream] when called), and the initial streamIndex, as follows:
read: PutChar, PutBlock, UnsafePutBlock, and SetLength are disallowed, and the initial streamIndex is 0.
create: all operations are allowed, the length is set to zero at stream creation time, and the initial streamIndex is 0.
append: all operations are allowed, and the initial streamIndex is the file's byte count.
write: all operations are allowed, and the initial streamIndex is 0.
Client errors: $none
User errors: $unknownClass, $unknownFile, $unknownUniqueID, $patternNotAllowed, $versionSpecified, $cantUpdateTiogaFile, $fileTypeMismatch
StreamFromOpenStream:
PROC [self:
STREAM]
RETURNS [
STREAM];
Given an existing file stream open for writing (accessRights=$write), creates a new stream on the same file with accessRights=$read. The two streams are then independently positionable, but are internally linked together so that the reader sees data written by the writer. At most one read stream may be linked in this way with any given write stream.
Client errors: $notImplemented
User errors: none
InitialPosition: TYPE = {start, end};
StreamFromOpenFile:
PROC [openFile: OpenFile,
accessOptions: AccessOptions ¬ read,
streamOptions: StreamOptions ¬ defaultStreamOptions,
streamBufferParms: StreamBufferParms ¬ defaultStreamBufferParms
]
RETURNS [
STREAM];
Create a new stream on the open file. If AccessRights = $read then PutChar, PutBlock, UnsafePutBlock, and SetLength are disallowed on the resulting stream.
User errors: $cantUpdateTiogaFile
OpenFileFromStream:
PROC [self:
STREAM]
RETURNS [OpenFile];
Will raise IO.Error[$NotImplementedForThisStream, self] if self is not a stream on an PFS.OpenFile.
ErrorFromStream:
PROC [self:
STREAM]
RETURNS [ErrorDesc];
Will raise IO.Error[$NotImplementedForThisStream, self] if self is not a stream on an PFS.OpenFile.
A client can call this procedure after self raises IO.Error[$Failure, self] to get more details about the error.
Error Codes
Error codes are ATOM's so that packages implementing PFS.OpenFile's via FSBackdoor.CreateProcsOpenFile can extend the set of codes. Following is a complete list of the codes generated by the PFS implementation. The list is partitioned by the PFS.ErrorGroup associated for each code.
Bugs
$inconsistent -- an invariant of the file system code has been violated
Environment Errors
$volumeFull -- no more space on a file system
$noMoreVersions -- next version number would be too big
$accessDenied -- remote server access controls succeeded
$quotaExceeded -- disk space quota on remote server exceeded
$invalidPropertyStorage -- unrecognized property storage format for a file
Client Errors
$badByteCount -- tried to set a byte count that is beyond end of file or negative
$positionNotInFile -- tried to read or write beyond end of the file
$invalidOpenFile -- PFS.OpenFile is closed, is PFS.nullOpenFile, or is an unrecognized TYPE
$notImplemented -- called procedure not provided for this PFS.OpenFile
$fileTypeMismatch -- the file type of the file did not match the type specified.
$outOfPropertySpace -- property space for this file has been exhausted
User Errors
$unknownClass -- no implementation for the class has been registered.
$unknownFile -- FName not in the implied directory
$unknownUniqueID -- no version of the given FName had the specified uniqueID
$patternNotAllowed -- name presented contained a "*" and procedure doesn't allow patterns
$versionSpecified -- tried to create an FName with a specific version
$cantUpdateTiogaFile -- tried open a file stream to write on a .tioga file
$invalidNameSyntax -- for name parsers when they can't parse a ROPE into a PATH