# 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:

- fst_BitMap. Indicates which fields of the structure contain valid data.

- fst_CreateTime. Indicates when the filesystem was created. This field is currently never valid and always contains 0.

- fst_BlockSize. Indicates the nominal size of data blocks in the filesystem. To determine the block size to use when reading and writing files, query the file's status and extract the block size from that information.

- fst_Size. Indicates the total size of the filesystem in blocks.

- fst_MaxFileSize. Indicates the maximum size of a file in the filesystem

- fst_Free. Indicates the total number of blocks currently not in use on the filesystem

- fst_Used. Indicates the total number of blocks currently in use on the filesystem.

## 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:

- fs.ds_DeviceBlockSize. The block size to use when reading or writing this file.
- fs.ds_DeviceBlockCount. The number of blocks of data in the file.
- fs_ByteCount. The number of bytes currently in the file.

## 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.