Portable I/O module
This is also known as the virtual files module (hence the VF prefix on
routines).
Introduction
CTC requires a file handling module, primarily to handle the multiple passes
over the data required by PGP and clones. This may be extremely efficiency
critical on machines with poorly optimised file systems. Ultimately the
aim is to reduce the number of physical passes through the data by maximising
the Virtual files provides the following facilities:-
-
Carriage control massaging. It is sometimes necessary to change the carriage
control used in the data. Most notably text files are often converted to
a canonical format (PC carriage control) for consistency of message digest
calculation. If this is done transparently in the file handler it can be
implemented in a single place without requiring a extra pass through the
data.
-
File slicing and concatenating. A number of operations require that data
from a number of sources be assembled into a single file. The purpose of
these facilities is to allow pseudofiles consisting of a number of whole
or partial files concatenated together to be generated without actually
copying the data.
-
To provide a layer through which all file access will go at allow any additional
processing that may be necessary or advantageous on some platforms.
Whereas these facilities could be highly machine dependent it is intended
that they should also be implementable using the C <stdio.h> library,
with the anticipation that they will be efficient if the underlying library
is efficient.
File Types
The Virtual File system supports the following types of file:-
Disc File
These are named permanent files out in the machines filing system. They
may be declared as read-only, write-only or read-write. They are used as
the final input or final output. The standard Virtual Filing system does
not include primitives for opening, closing, creating or deleting these.
Such operations are machine and filing system dependent. However, there
are example operations which should be implemented by machines using the
C <stdio.h> interface. The Virtual File system does include an operation
to wipe files by filling them with pseudorandom data .
Buffer
This is a temporary file. The Virtual Files implementation may store some,
all or none of the information in a buffer on disc. The data in the file
will ultimately be discarded. A buffer may be declared sensitive if it
contains data that should not be revealed. The storage of a sensitive file
should wipe before it is release. For efficiency, buffers that have not
been declared sensitive should not be wiped. A buffer may either be read-write
or write-then-read. If of the later type it is write only until the first
read operation then it is read only. A compound is a file made up of one
or more parts of one or more buffers and disc files. Compound files are
read-only. Once a buffer or disc file has been incorporated into a compound
file it also becomes read-only . VF References Each Virtual File regardless
of type is referenced via a DataFile object. This object is a structure
with contents known only to the VF module. As such calling modules do not
even know the size of this structure, all references are in terms of pointers
to this type normally referenced as the pointer type DataFileP. Each Virtual
File has a reference count indicating the number of times that the file
is point. This should represent the number of valid DataFilePs in existance
for the VF. If this falls to zero, the file is closed. When a Disc file
closes, the VF module calls the O/S specific vf_disc_close routine which
must be provided to close the disc file. If a buffer is closed, it is wiped
if declared sensitive, and then all its storage is released. If a compound
file is closed, the reference count of each component is decremented, and
they may be themselves closed as a consequence. The definitions of all
VF module routines must specify the effect of the routine on the reference
count. If a VF is passed to a routine there may be one of three semantics.
-
Borrowed Ref. [B]
-
The subroutine uses the DataFileP reference for the duration of the call
only and responsibility for closing the file remains with the calling program.
-
Copied Ref. [C]
-
The subroutine may increment the reference count for the file and keep
a copy of the reference.
-
Assumed Ref [A]
-
The subroutine may taken over responsiblity for the reference. The calling
program may not use the reference again.
Access Modes
There are three access modes. There are:-
-
Ascii Mode
-
In ASCII mode, the calling program reads or writes a line at a time. The
read operations return a null terminated string with no trailing carriage
control. The write operations accept similar strings. The carriage control
characters (if any) on a write operation are stripped and replaced by carriage
control characters specific to the file.
-
Caller Buffer Mode
-
This is a binary access mode. The calling program passes the VF module
a buffer to be written to the file or read from it. This is the same as
ASCII mode but without any carriage control conversion.
-
VF Buffer Mode
-
This is also a binary access mode. However in this mode the buffer is provided
by the VF module. The calling program requests a buffer from the VF module.
If it is a read mode the buffer is filled with data from the file. Once
the access to the buffer is complete, the calling program releases the
buffer, and if it is a write mode the data is written to the file. No other
VF operations may be carried out on the file (or its components) while
such a buffer is in use. (The main purpose of this mode is to allow small
files to implemented in-memory without copy operations.)
DataTypes
-
DataFile
-
This is a private structure that contains the details of an open file.
Objects of this type cannot be declared by calling programs as its size
is private.
-
DataFileP
-
This is a pointer to DataFile and is the type declared in calling programs
to manipulate files.
-
CCmode
-
Carriage Control mode: This defines the why in which new-line characters
are handled when writing text lines to the file. Currently this always
consists of appending a specific control-character or characters to the
end of the line. However other formats, like prefixing line length, would
also possible if required. The following values are defined:-
-
CANONICAL
-
This is the PGP canonical form. Text files are converted to this form for
both encryption or signing. In practice this is the same as PC
-
LOCAL
-
This is the form of carriage control used on the machine that the program
is built to run on. Whereas this is typically the same as one of the other
formats, it could be radically different from any of them. There is not
even any guarantee that end-of-line is indicated by imbedded control characters.
Other mechanisms (e.g. prefixed line-length) are also possible.
-
PC
-
As per MS-DOS, i.e. <CR><LF>
-
UNIX
-
As per Unix, i.e. <LF>
-
MAC
-
As per MacOS, i.e. <CR>
-
accessMode
-
This defines the mode in which a file will be accessed. Note that this
more complex than most filing systems will implement. Implementations of
this module may but need not enforce access to comply appropriate access
mode. The module must make best endeavours to destroy all data on closure
if the file is in a "wipe" mode. However note that the operation of many
operating system facilities (e.g. Virtual Memory) makes it impractical
for many implementations to guarantee this. (This data-type is actually
defined in
abstract.h.)
-
READ
-
Read only
-
READWIPE
-
Read only then wipe
-
WRITE
-
Write only
-
UPDATE
-
Read/write (may mix)
-
UPDATEWIPE
-
Read/write then wipe on close
-
WRITEREAD
-
Write then read: the file becomes read-only on the first read.
-
WRITEREADWIPE
-
Write then read then wipe
-
fileType
-
This defines the purpose of a file. (This data-type is actually defined
in
abstract.h.)
-
TEXTCYPHER
-
Text (armoured) cyphertext
-
BINCYPHER
-
Binary cyphertext
-
TEXTPLAIN
-
Text plaintextfileType
-
BINPLAIN
-
Binary plaintext
-
PUBLICRING
-
Public keyring
-
SECRETRING
-
Secret keyring
-
vf_error
-
ILLEGAL
-
Illegal operation attempted
-
UNIMPLEMENTED
-
Requested operation is not implemented in the version.
-
ENDOFFILE
-
End of file encountered
-
OSERROR
-
Operating system reported an error
-
WRONGMO
-
too many of something, typically files open
-
NO_MEMORY
-
memory allocation refused
Functions
Whereas these routines will be the normal method of opening and closing
files in many environments they need not be the only method nor necessarily
available at all in some environments
Optional Entry Points
These entry points are optional. Their specifications are provided to provide
consistency across applications that do handle files in this way.
-
DataFileP vf_open(char * name, accessMode
mode, fileType type);
-
This opens a file by name. This routine is not practical to provide with
some filing systems and accordingly should NOT be called by CTClib code.
-
byte * vf_buffer(long * size, DataFileP[B]
file);
-
This function and vf_release() were in the original specification, but
have not in fact been used. They provide direct access to a file by locking
it in memory.
On entry *size is the requested size of the buffer. This is
ignore if the file mode is read-only. On exit *size is the size
of the buffer returned. If the file mode is write-only this should unchanged.
If the file mode is read-only it should be the file size. If the file mode
is read/write it should be the larger of the two. The file image
is locked and the address of the start of the buffer is returned. The function
returns NULL if it fails.
Note if the file mode is read-only, it is a program error to update
the buffer. For the duration that the file is accessible in this way, it
is not allowed to access it in any other way.
-
long vf_release(long size, DataFileP[B]
file);
-
This function releases a buffer acquired by vf_buffer(). If the file mode
is writable, the size of the file is set to size and the file
is updated to consist of the first size bytes of the buffer. (N.B.
size
must be less than or equal to the *size value returned by vf_buffer().)
File Operations
-
DataFileP vf_toReplace(DataFileP[B]
old);
-
This routine and vf_replaceWith are intended to be called as a pair. They
are used to where a new file is being written to replace an existing file,
and it is necessary to refer to the unmodified original file in the course
of writing the new file. (This is necessary in CTC for rewriting public
key-rings.)
Creates a new potentially permanent but as yet unnamed (or temporary
named) file, in the same area (same partition/directory) as old.
The fileType of the result is inherited from old.
A file created with vf_toReplace() should be deleted if it is closed
with vf_close(), and will continue to exist with the old file's name if
it is closed as second argument to vf_replaceWith() can also be applied
to files that have been opened vf_open().
-
void vf_replaceWith(DataFileP[A]
oldFile, DataFileP[A] newFile);
-
This function closes both files. It renames 'old' to a backup name (.bak
or similar), renames 'new' to the former name of 'old' and closes both.
Although not necessarily in that order. vf_replaceWith is not guaranteed
to work unless oldfile was previous used to call vf_toReplace()
and that call returned newfile.
-
void vf_close(DataFileP[A]
file);
-
Closes the file reference and releases any associated resources. The file
reference must not be used again and attempting to do so may cause serious
errors.
-
DataFileP vf_tempfile(long expected_size);
-
The expected_size argument is a hint to the vf_tempfile routine. It does
not matter if it is wrong however ideally it should be a value that is
unlikely to be exceeded, but not greatly in excess of the requirement.
An expected_size of zero indicates that the caller has no idea
how large the file will grow.
-
DataFileP vf_staticfile(byte * buffer,
long length);
-
This function creates a temporary read-only file with contains defined
by
buffer and length.
-
DataFileP vf_copyRef(DataFileP[C]
file);
-
This copies a reference to an open file. Each copy will have its own independant
file position.
Text Operations
These operations treat the file as a text file. However note that these
operations may (and sometimes are) mixed with binary file operations, at
least where the CCmode is LOCAL. Accordingly in a
number of environments it may be necessary to treat the file as a pseudo-binary
file. This is especially important for the generation of file signatures,
where the appropriate binary image for the text must be generated.
It is recognised that on any machine that stores text as something other
than lines directly concatenated separately by one or two control characters
this may be a problem. [This is probably true of some types of IBM data-sets.]
On such machines, there may have to be quiet radical differences in the
manipulation of a file depending on its Carriage-Control format. In general
CANONICAL
must be the pseudo-binary, and
LOCAL should be the
native text format. How UNIX, PC & MAC are handled in these circumstances
is left to the judgement of the implementor.
-
long vf_readline(char * buffer, long
buffer_size, DataFileP[B] file);
-
This reads the next line from the file, returning the line as
a NULL terminated string in buffer. Where the line is longer than
the buffer, the first buffer_size-1 bytes from the line. The file
pointer is then positioned to the first byte in the line that was not returned.
The next call will return a "line" starting with that character. Some THINK_C-specific
code remains in the sample implementation of this module, unmaintained,
as an example of how to handle line-based I/O with a byte-stream style
of input.
-
long vf_writeline(char * buffer, DataFileP[B]
file);
-
This function writes the contents of the buffer followed by the
appropriate carriage control characters to the file
-
long vf_writeline_xt(char * buffer,
DataFileP[B] file);
-
This function is very similar to vf_writeline().
The only difference is that this version does not write any carriage
control characters to the file. In practice this behaviour is
only important when writing CANONICAL mode files
for signature. The PGP signature format does not have a terminator
on the final line. Accordingly this routine is used, instead of
vf_writeline(),
to write the finalline in such cases. At present this is the only use made
of this routine. Many implementations will be able to implement this as:-
long vf_writeline_xt(char * buffer, DataFileP file)
{ return vf_write(buffer, strlen(buffer), file);}
-
void vf_CCmode(CCmode mode, DataFileP[B]
file);
-
This function sets the Carriage control mode of file to mode.
Binary Operations
-
long vf_read(void * buffer, long buffer_size,
DataFileP[B] file);
-
Reads buffer_size bytes from the file and puts them in
buffer.
It returns the number of bytes read. This should be equal to buffer_size
unless an error occurred or end-of-file was encountered.
-
long vf_write(void * buffer, long buffer_size,
DataFileP[B] file);
-
Writes buffer_size from buffer to the file starting
from the current position. It returns the number of bytes written. This
should be equal to buffer_size unless an error has occurred.
Common Operations
-
boolean vf_movepos(DataFileP[B]
file, long offset);
-
Moves the cur