The File System: 5.6 Files: Bytewise File Operations

Up: GEOS SDK TechDocs | Up | Prev: 5.5 FileEnum()

There are several routines designed for working with files as a string of bytes. These routines may be used to work with DOS files or with GEOS byte files. You can open any file (including an executable file or a VM file) for byte-level access. This may be useful for such things as file-compression routines; however, be aware that if you make any changes to such files, you could invalidate them. For this reason, if you open a VM or executable file for byte-level access, you should open it for read-only use.

Opening and Closing Files

FileOpen(), FileCreate(), FileCreateTempFile(), FileClose(), FileAccessFlags

The GEOS file system provides several routines for opening files for byte-level access. If you are working with GEOS Virtual Memory files, you should use the appropriate VM routines to open and close the files (see the VM chapter). You should use the byte-level routines only if you are working with DOS files or with GEOS byte files. You may occasionally want to read a VM file or an executable file as a string of bytes. In this rare case, you must use the routines in this section. Note, however, that you should not change the VM file with these routines; it is safe only to open it for read-only access.

To open a file, call FileOpen() . This routine takes two arguments: a set of FileAccessFlags and a pointer to a null-terminated string. The string should specify the name of the file (either the virtual name or the native name may be used). It may simply be a file name, or it may be a relative or absolute path. The FileAccessFlags record specifies two things: what kind of access the caller wants, and what type of access is permitted to other geodes. A set of FileAccessFlags is thus a bit-wise OR of two different values. The first specifies what kind of access the calling geode wants and has the following values:

FILE_ACCESS_R
The geode will only be reading from the file.
FILE_ACCESS_W
The geode will write to the file but will not read from it.
FILE_ACCESS_RW
The geode will read from and write to the file.

The second part specifies what kind of access other geodes may have. Note that if you try to deny a permission which has already been given to another geode (e.g. you open a file with FILE_DENY_W when another geode has the file open for write-access), the call will fail. The following permissions can be used:

FILE_DENY_RW
No geode may open the file for any kind of access, whether read, write, or read/write.
FILE_DENY_R
No geode may open the file for read or read/write access.
FILE_DENY_W
No geode may open the file for write or read/write access.
FILE_DENY_NONE
Other geodes may open the file for any kind of access.

Two flags, one from each of these sets of values, are combined to make up a proper FileAccessFlags value. For example, to open the file for read-only access while prohibiting other geodes from writing to the file, you would pass the flags FILE_ACCESS_R and FILE_DENY_W as follows:

myHandle = FileOpen("MyFile",
		(FILE_ACCESS_R | FILE_DENY_W));

If successful, FileOpen() returns the file's handle. If it is unsuccessful, it returns a null handle and sets the thread's error value. The following error values are commonly returned:

ERROR_FILE_NOT_FOUND
No file with the specified name could be found in the appropriate directory.
ERROR_PATH_NOT_FOUND
A relative or absolute path had been passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileOpen() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.

Note that if you use the document control objects, they automatically make all appropriate calls to FileOpen() when the user requests it; you will automatically be passed the file's handle.

FileOpen() can only be called if the file already exists. In order to create a byte file, you must call FileCreate() . FileCreate() takes four arguments: a set of FileCreateFlags , a set of FileAccessFlags , a set of FileAttrs , and a pointer to a string containing a name for the file. As with FileOpen() , the name may be a name alone or a relative or absolute path. The FileCreateFlags specifies whether the file should be created if it already exists. The following flags are available:

FILE_CREATE_TRUNCATE
If a file with the given name exists, it should be opened and truncated; that is, all data should be deleted.
FILE_CREATE_NO_TRUNCATE
If the file exists, it should be opened without being truncated.
FILE_CREATE_ONLY
If the file exists, the routine should fail and set the thread's error value to ERROR_FILE_EXISTS.
FCF_NATIVE
This flag is combined with one of the above flags if the file should be created in the device's native format; e.g. if it should be a DOS file instead of a GEOS file. The name passed must be an acceptable native file name. If a GEOS file with the specified name already exists, FileCreate() will fail with error condition ERROR_FILE_FORMAT_MISMATCH.

The first three flags (FILE_CREATE_...) are mutually exclusive; exactly one of them must be passed to FileCreate() . That flag may or may not be combined with FCF_NATIVE.

The FileAccessFlags are the same as described in FileOpen() . Note, however, that you must request either write access or read/write access when you use FileCreate() .

Every file has a set of attributes. These record certain information about the file. If you create a file, you will need to specify values for these attributes. The attributes are described above in the section on FEA_FILE_ATTR .

If FileCreate() is successful, it will open the file and return its handle. If it fails, it will return a null handle and set the thread's error value. It may return any of the FileOpen() errors. It may also return the following errors:

ERROR_FILE_EXISTS
Returned if FileCreate() was called with FILE_CREATE_ONLY and a file with the specified name already exists.
ERROR_FORMAT_MISMATCH
Returned if FileCreate() was called with FILE_CREATE_TRUNCATE or FILE_CREATE_NO_TRUNCATE and a file exists in a different format than desired; i.e. you passed FCF_NATIVE and the file already exists in the GEOS format, or vice versa.

It is often useful to create temporary files which are not seen by the user. In these cases, you generally don't care about the file's name since you will most likely be deleting the file on exit. For these situations GEOS provides the routine FileCreateTempFile() . FileCreateTempFile() is passed a directory; it chooses a unique name for the file. This routine takes two arguments:

If successful, FileCreateTempFile() will open the temporary file and return its handle. It will also write the file's name to the end of the string passed. You will need to know the name to delete the file. The name is also useful if GEOS shuts down while a temporary file is open; the geode will need to know the temporary file's name in order to reopen it.

When you are done with a file, you should close it by calling FileClose() . This releases any restrictions you may have placed on the file and allows the file to be moved or deleted. It is passed two arguments: the file handle and a Boolean value which should be set to true (i.e. non-zero) if the geode cannot handle error messages; it will cause FileClose() to fatal-error if it cannot successfully close the file. (This should only be used during development; the flag should never be passed in a finished program.) The routine returns zero if successful; otherwise, it returns a FileError value.

Reading From and Writing To Files

FileRead(), FileWrite(), FilePos(), FileCommit()

There are a few specific operations you are allowed to perform on data in a byte-file. You can copy data from the file into memory; you can copy data from memory into the file, overwriting the file's contents; you can write data to the end of a file; and you can cut data from the end of the file. If you want to perform more elaborate manipulations on a byte-file, you may wish to create a temporary VM file and copy the data there (see the VM chapter).

Every file handle has a file position associated with it. All read and write operations begin at that position; they may also change the position. The first byte in a file is considered to be at position zero. If the file is a GEOS byte file, position zero is immediately after the GEOS header; thus, the header cannot be accessed or altered via the read and write operations.

To read data from a file, call FileRead() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer. FileRead() will copy the requested number of bytes from the file to the buffer. The third is the number of bytes to read. The fourth is a Boolean indicating whether the caller can handle errors. (This is true if the geode cannot handle error messages; it will cause FileRead() to fatal-error if it cannot successfully read the data. This should only be used during development; the flag should never be passed in a finished program.) FileRead() returns the number of bytes actually read. This may be less than the number requested, if the end of file is reached; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileRead() was unable to gain access to the file, it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED. In any event, the file position will be incremented by the number of bytes read; thus, it will point to the first byte after the data read.

To write data to a file, call FileWrite() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer in memory. The third is the number of bytes to write. The fourth is a Boolean indicating whether the caller can handle errors. FileWrite() will copy the specified number of bytes from the buffer to the file, starting at the current position and expanding the file as necessary. It will also increment the current position by the number of bytes written. If the current position is not at the end of the file, FileWrite() will overwrite the file's existing data. FileWrite() returns the number of bytes written. This may be less than the number requested, if the disk ran out of space; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileWrite() could not get access to the file (as, for example, if the geode had read-only access to the file), it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED.

If a file is on a removable disk, the kernel will make sure that the disk is in the appropriate drive before reading from or writing to it. If the disk is not in the drive, the kernel will prompt the user to insert it. The user will have the option of aborting the operation; this will result in the file-access routine failing with error condition ERROR_DISK_UNAVAILABLE.

When you write changes to a file, either the GEOS file system or the underlying DOS may choose to cache those changes to save time. All cached changes will be written to the disk when the file is closed. However, you can force the cached changes to be written immediately by calling FileCommit() . This routine takes two arguments. The first is the file's handle. The second is a Boolean indicating whether the caller can handle errors. The routine returns zero if the operation was successful; otherwise it returns an error code.

To change the current file position, call FilePos() . This routine takes three arguments. The first is the file handle. The second is a member of the FilePosMode enumerated type; this value indicates how the new position is specified. The third argument is a number of bytes; it specifies how far the file position will be moved. FilePosMode has the following possible values:

FILE_POS_START
The file position is set to a specified number of bytes after the start of the file. Passing this mode with an offset of zero will set the file position to the start of the file (i.e. immediately after the header information).
FILE_POS_RELATIVE
The file position is incremented or decremented by a specified number of bytes.
FILE_POS_END
The file position is set to a specified number of bytes before the end of the file. Passing this mode with an offset of zero will set the file position to the end of the file.

FilePos() returns a 32-bit integer. This integer specifies the file position after the move (relative to the start of the file). To find out the current file position without changing it, call FilePos() with mode FILE_POS_RELATIVE and offset zero.

Getting and Setting Information about a Byte File

FileGetDateAndTime(), FileSetDateAndTime(), FileGetAttributes(), FileSetAttributes()

GEOS provides several routines to get information about files. To get information about a GEOS file, you would ordinarily use one of the extended attributes routines (see GEOS Extended Attributes ). These routines are ordinarily used for non-GEOS files. Nevertheless, all of the following routines can be used on GEOS files.

FileGetDateAndTime() and FileSetDateAndTime() are used to get and set the file's modification time. To access a GEOS file's modification time, you would ordinarily call an extended attribute routine, passing FEA_MODIFICATION. However, special-purpose routines are provided specifically for changing a file's modification time. Note that these routines may be used for GEOS or non-GEOS files. Similarly, you can change the FEA_MODIFICATION attribute even for non-GEOS files. To find out the modification time, call FileGetDateAndTime() . This routine is passed the file's handle and returns a FileDateAndTime value (as described above on FEA_MODIFICATION ). To change the modification time, call FileSetDateAndTime() . This routine is passed the file's handle and a FileDateAndTime value. If successful, it returns zero; otherwise, it returns an error code. You must have write permission to change the modification time; otherwise, FileSetDateAndTime() will fail with condition ERROR_ACCESS_DENIED. The TimerGetFileDateTime() routine returns the current date and time in a FileDateAndTime structure.

To find out a DOS file's attributes, call FileGetAttributes() . This routine is passed a file's path. It returns the file's FileAttrs record (as described on FEA_FILE_ATTR ). To change the file's attributes, call FileSetAttributes() . This routine takes two arguments: the address of a null-terminated path string and a FileAttrs record. It returns zero if it was successful; otherwise, it returns an error condition. Note that a file's attributes cannot be changed if the file is open.

Data-Access Synchronization

FileLockRecord(), FileUnlockRecord()

GEOS provides routines to help threads synchronize file access. This functionality is very elaborate for VM files. For byte files it is less so. Several threads can synchronize their access to a single handle by using HandleP() and HandleV() , described in the Memory Management chapter. If they want to use the file at the same time, they should use FileLockRecord() and FileUnlockRecord() .

FileLockRecord() takes three arguments: the file handle, a dword specifying the start of the region to be locked, and a dword specifying the length (in bytes) of the region to be locked. If there are no locks on any part of that region, FileLockRecord() returns zero; otherwise, it returns the error code ERROR_ALREADY_LOCKED. Note that there is nothing to stop another thread or geode from reading or writing to that region. The lock simply prevents anyone from locking that region with FileLockRecord() . The file's users have to remember to lock any part of the file before accessing it.

To release a lock on a part of a file, call FileUnlockRecord() . This routine takes the same arguments as FileLockRecord() .


Up: GEOS SDK TechDocs | Up | Prev: 5.5 FileEnum()