Chapter 17. File Table

Once a file has been opened it is represented by an open file object. These are allocated from an array of available file objects. User code accesses these open file objects via a second array of pointers which is indexed by small integer offsets. This gives the usual Unix file descriptor functionality, complete with the various duplication mechanisms.

A file table entry has the following structure:

struct CYG_FILE_TAG
{
    cyg_uint32	                f_flag;		/* file state                   */
    cyg_uint16                  f_ucount;       /* use count                    */
    cyg_uint16                  f_type;		/* descriptor type              */
    cyg_uint32                  f_syncmode;     /* synchronization protocol     */
    struct CYG_FILEOPS_TAG      *f_ops;         /* file operations              */
    off_t       	        f_offset;       /* current offset               */
    CYG_ADDRWORD	        f_data;		/* file or socket               */
    CYG_ADDRWORD                f_xops;         /* extra type specific ops      */
    cyg_mtab_entry              *f_mte;         /* mount table entry            */
};

The f_flag field contains some FILEIO control bits and some bits propagated from the flags argument of the open() call (defined by CYG_FILE_MODE_MASK).

The f_ucount field contains a use count that controls when a file will be closed. Each duplicate in the file descriptor array counts for one reference here. It is also incremented around each I/O operation to ensure that the file cannot be closed while it has current I/O operations.

The f_type field indicates the type of the underlying file object. Some of the possible values here are CYG_FILE_TYPE_FILE, CYG_FILE_TYPE_SOCKET or CYG_FILE_TYPE_DEVICE.

The f_syncmode field is copied from the syncmode field of the implementing filesystem. Its use is described in Chapter 19.

The f_offset field records the current file position. It is the responsibility of the file operation functions to keep this field up to date.

The f_data field contains private data placed here by the underlying filesystem. Normally this will be a pointer to, or handle on, the filesystem object that implements this file.

The f_xops field contains a pointer to any extra type specific operation functions. For example, the socket I/O system installs a pointer to a table of functions that implement the standard socket operations.

The f_mte field contains a pointer to the parent mount table entry for this file. It is used mainly to implement the synchronization protocol. This may contain a pointer to some other data structure in file objects not derived from a filesystem.

The f_ops field contains a pointer to a table of file I/O operations. This has the following structure:

struct CYG_FILEOPS_TAG
{
        int	(*fo_read)      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
        int	(*fo_write)     (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
        int     (*fo_lseek)     (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
        int	(*fo_ioctl)     (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
                                 CYG_ADDRWORD data);
        int	(*fo_select)    (struct CYG_FILE_TAG *fp, int which, CYG_ADDRWORD info);
        int     (*fo_fsync)     (struct CYG_FILE_TAG *fp, int mode );        
        int	(*fo_close)     (struct CYG_FILE_TAG *fp);
        int     (*fo_fstat)     (struct CYG_FILE_TAG *fp, struct stat *buf );
        int     (*fo_getinfo)   (struct CYG_FILE_TAG *fp, int key, char *buf, int len );
        int     (*fo_setinfo)   (struct CYG_FILE_TAG *fp, int key, char *buf, int len );
};

It should be obvious from the names of most of these functions what their responsibilities are. The fo_getinfo() and fo_setinfo() function pointers, like their counterparts in the filesystem structure, implement minor control and info functions such as fpathconf().

The second argument to the fo_read() and fo_write() function pointers is a pointer to a UIO structure:

struct CYG_UIO_TAG
{
    struct CYG_IOVEC_TAG *uio_iov;	/* pointer to array of iovecs */
    int	                 uio_iovcnt;	/* number of iovecs in array */
    off_t       	 uio_offset;	/* offset into file this uio corresponds to */
    ssize_t     	 uio_resid;	/* residual i/o count */
    enum cyg_uio_seg     uio_segflg;    /* see above */
    enum cyg_uio_rw      uio_rw;        /* see above */
};

struct CYG_IOVEC_TAG
{
    void           *iov_base;           /* Base address. */
    ssize_t        iov_len;             /* Length. */
};

This structure encapsulates the parameters of any data transfer operation. It provides support for scatter/gather operations and records the progress of any data transfer. It is also compatible with the I/O operations of any BSD-derived network stacks and filesystems.

When a file is opened (or a file object created by some other means, such as socket() or accept()) it is the responsibility of the filesystem open operation to initialize all the fields of the object except the f_ucount, f_syncmode and f_mte fields. Since the f_flag field will already contain bits belonging to the FILEIO infrastructure, any changes to it must be made with the appropriate logical operations.