The File Pseudo-Device


The File folio, in cooperation with the various file systems, provides the services needed to access filesystem-oriented external storage; for example, reading files from a CD-ROM, or writing files to the 3DO NVRAM (non-volatile RAM).

When you open a file using the File folio OpenDiskFile() function, the item that is returned serves as a handle to the file. The Item is of type Device. This enables communication to the filesystem that controls this file using the standard Portfolio I/O model.

When you send a command to a file device, the file system responsible for the file wakes up and executes the command. Therefore, the file device is only a pseudo-device, serving as a gateway to the underlying filesystem.

The File folio provides many high-level functions to control file systems. For example, you can create files using the CreateFile() function, or you can delete files using the DeleteFile() function. These File folio functions are merely wrappers for file system commands. The functions internally create file devices and send commands to the device to perform work.

This section explains the various commands you can send to a file device. It is often much easier to use the higher-level File folio functions to interact with the file system. However, there are some operations that can only be performed by interfacing to the file device directly.

Getting Filesystem Status

Once you open a file, you can obtain information about the filesystem the file resides on. This is done by using the FILECMD_FSSTAT command.

IOInfo        ioInfo;
FileSystemStat fsStat;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                        = FILECMD_FSSTAT;
ioInfo.ioi_Recv.iob_Buffer                        = &fsStat;
ioInfo.ioi_Recv.iob_Len                        = sizeof(FileSystemStat);

Once the command returns successfully, you can look at the fields in the fsStat structure for the status information. The fst_BitMap field of the FileSystemStat structure indicates which fields in the rest of the structure are valid and can be examined. Different file systems cannot always provide all the information in a FileSystemStat structure. For example, if the FSSTAT_SIZE bit is set in fst_BitMap, it means the fst_Size field of the FileSystemStat structure is valid.

The fields in the FileSystemStat structure are:

Getting File Status

Once you open a file, you can obtain information about the file by using the CMD_STATUS command.

IOInfo    ioInfo;
FileStatus fStat;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                        = CMD_STATUS;
ioInfo.ioi_Recv.iob_Buffer                = &fStat;
ioInfo.ioi_Recv.iob_Len                   = sizeof(FileStatus);

Once the command completes successfully, you can look at the fields in the fStat structure for the status information. Fields of interest are:

Creating and Deleting Files

To create a file, use the File folio's CreateFile() function. You supply the path name of the file to create, and the function does the necessary work to create a new file entry on the filesystem.

To delete an existing file, use the File folio's DeleteFile() function. You supply it the path name of the file to delete, and the function does the necessary work to remove the file's entry from the filesystem.

Allocating Blocks for a File

Before you can write data to a file, you must allocate enough free blocks in the file to hold the data to be written. This is done by using the FILECMD_ALLOCBLOCKS command.

IOInfo ioInfo;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                = FILECMD_ALLOCBLOCKS;
ioInfo.ioi_Offset                = numBlocks;

The numBlocks variable contains the number of blocks by which to extend the file. If this value is negative, the size of the file is reduced by the specified number of blocks.

Writing Data to a File

You must use the CMD_WRITE command to write data to a file. All write operations must be performed in full blocks. The size of the blocks can be obtained by sending a CMD_STATUS command to the file device. The write operation must also be aligned on a block boundary within the file.

IOInfo ioInfo;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                               = CMD_WRITE;
ioInfo.ioi_Offset                                = blockOffset;
ioInfo.ioi_Send.iob_Buffer                       = dataAddress;
ioInfo.ioi_Send.iob_Len                          = numBytes;

The blockOffset field indicates the offset in blocks from the beginning of the file where the data is to be written. The dataAddress value points to the data that is to be written. Finally, the numBytes value indicates the number of bytes to write out. This value must be an even multiple of the block size for the file.

Marking the End of a File

Once you are done writing data to a file, you must mark the end of the file using the FILECMD_SETEOF command. This command tells the filesystem how many useful bytes of data are in the file. Because you can only transfer data in terms of blocks, sending this command tells the filesystem how many bytes of the last written block are useful bytes.

IOInfo ioInfo;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                            = FILECMD_SETEOF;
ioInfo.ioi_Offset                             = numBytesInFile;

Reading Data From a File

Reading information from a file is done much the same way you write information to a file. You must supply the offset in blocks where to start reading data, and the number of bytes of data to read.

IOInfo ioInfo;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                                    = CMD_READ;
ioInfo.ioi_Offset                                     = blockOffset;
ioInfo.ioi_Recv.iob_Buffer                            = dataAddress;
ioInfo.ioi_Recv.iob_Len                               = numBytes;

The blockOffset value indicates the offset in blocks from the beginning of the file from which data is read. dataAddress indicates an address in memory where the data will be copied once read from the filesystem. Finally, numBytes contains the number of bytes to read. This number must be an even multiple of the block size of the file.

Getting Directory Information

The OpenDiskFile() function opens a directories. You can then use the FILECMD_READDIR command to scan the directory and obtain information about files in the directory.

IOInfo        ioInfo;
DirectoryEntry dirEntry;

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                                = FILECMD_READDIR;
ioInfo.ioi_Offset                                 = fileNum;
ioInfo.ioi_Recv.iob_Buffer                        = &dirEntry;
ioInfo.ioi_Recv.iob_Len                           = sizeof(DirectoryEntry);

The fileNum value indicates the number of the file within the directory to read. You start with file 0, and keep going until the I/O cannot be completed because there are no more files. The dirEntry structure will be filled out with information about the specified file.

Getting the Path of a File

Once you have an open file, you may need to determine the exact path to reach this file. The FILECMD_GETPATH command determines the exact path to an open file.

IOInfo ioInfo;

char   path[FILESYSTEM_MAX_PATH_LEN];

memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command                        = FILECMD_GETPATH;
ioInfo.ioi_Recv.iob_Buffer                = path;
ioInfo.ioi_Recv.iob_Len                   = sizeof(path);

Once the command completes successfully, the path variable contains a complete unambiguous path to reach the file. You can then use this path to issue another OpenDiskFile() command or a DeleteFile() command.