4.2   MS-DOS-Compatible File System: dosFs

Diskettes formatted using the dosFs file system are compatible with MS-DOS diskettes up to and including release 6.2. Hard disks initialized by the two file systems have slightly different formats. However, the data itself is compatible and dosFs can be configured to use a disk formatted by MS-DOS.

The dosFs file system offers considerable flexibility appropriate to the varying demands of real-time applications. Major features include:

4.2.1   Disk Organization

The MS-DOS/dosFs file system provides the means for organizing disk data in a flexible manner. It maintains a hierarchical set of named directories, each containing files or other directories. Files can be appended; as they expand, new disk space is allocated automatically. The disk space allocated to a file is not necessarily contiguous, which results in a minimum of wasted space. However, to enhance its real-time performance, the dosFs file system allows contiguous space to be pre-allocated to files individually, thereby minimizing seek operations and providing more deterministic behavior.

The general organization of an MS-DOS/dosFs file system is shown in Figure 4-1 and the various elements are discussed in the following sections.

Clusters

The disk space allocated to a file in an MS-DOS/dosFs file system consists of one or more disk clusters. A cluster is a set of contiguous disk sectors.1 For floppy disks, two sectors generally make up a cluster; for fixed disks, there can be more sectors per cluster. A cluster is the smallest amount of disk space the file system can allocate at a time. A large number of sectors per cluster allows a larger disk to be described in a fixed-size File Allocation Table (FAT; see File Allocation Table), but can result in wasted disk space.

Boot Sector

The first sector on an MS-DOS/dosFs hard disk or diskette is called the boot sector. This sector contains a variety of configuration data. Some of the data fields describe the physical properties of the disk (such as the total number of sectors), and other fields describe file system variables (such as the size of the root directory).

The boot sector information is written to a disk when it is initialized. The dosFs file system can use diskettes that are initialized on another system (for example, using the FORMAT utility on an MS-DOS PC), or VxWorks can initialize the diskette, using the FIODISKINIT function of the ioctl( ) call.

As the MS-DOS standard has evolved, various fields have been added to the boot sector definition. Disks initialized under VxWorks use the boot sector fields defined by MS-DOS version 5.0.

When MS-DOS initializes a hard disk, it writes a partition table in addition to the boot sector. VxWorks does not create such a table. Therefore hard disks initialized by the two systems are not identical. VxWorks can read files from a disk formatted by MS-DOS if the block offset parameter in the device creation routine points beyond the partition table to the first byte of the data area.

File Allocation Table

Each MS-DOS/dosFs volume contains a File Allocation Table (FAT). The FAT contains an entry for each cluster on the disk that can be allocated to a file or directory. When a cluster is unused (available for allocation), its entry is zero. If a cluster is allocated to a file, its entry is the cluster number of the next portion of the file. If a cluster is the last in a file, its entry is -1. Thus, the representation of a file (or directory) consists of a linked list of FAT entries. In the example shown in Figure 4-2, one file consists of clusters 2, 300, and 500. Cluster 3 is unused.


*

NOTE: dosFs does not map bad disk sectors to the FAT.

The FAT uses either 12 or 16 bits per entry. Disk volumes that contain up to 4085 clusters use 12-bit entries; disks with more than 4085 clusters use 16-bit entries. The entries (particularly 12-bit entries) are encoded in a specific manner, done originally to take advantage of the Intel 8088 architecture. However, all FAT handling is done by the dosFs file system; thus the encoding and decoding is of no concern to VxWorks applications.

A volume typically contains multiple copies of the FAT. This redundancy allows data recovery in the event of a media error in the first FAT copy.


*

CAUTION: The dosFs file system maintains multiple FAT copies if that is the specified configuration; however, the copies are not automatically used in the event of an error.

The size of the FAT and the number of FAT copies are determined by fields in the boot sector. For disks initialized using the dosFs file system, these parameters are specified during the dosFsDevInit( ) call by setting fields in the volume configuration structure, DOS_VOL_CONFIG.

Root Directory

Each MS-DOS/dosFs volume contains a root directory. The root directory always occupies a set of contiguous disk sectors immediately following the FAT copies. The disk area occupied by the root directory is not described by entries in the FAT.

The root directory is of a fixed size; this size is specified by a field in the boot sector as the maximum allowed number of directory entries. For disks initialized using the dosFs file system, this size is specified during the dosFsDevInit( ) call, by setting a field in the volume configuration structure, DOS_VOL_CONFIG.

Because the root directory has a fixed size, an error is returned if the directory is full and an attempt is made to add entries to it.

For more information on the contents of the directory entry, see 4.2.13 Directory Entries.

Subdirectories

In addition to the root directory, MS-DOS/dosFs volumes sometimes contain a hierarchy of subdirectories. Like the root directory, subdirectories contain entries for files and other subdirectories; however, in other ways they differ from the root directory and resemble files:

Files

The disk space allocated to a file in the MS-DOS/dosFs file system is a set of clusters that are chained together through entries in the FAT. A file is not necessarily made up of contiguous clusters; the various clusters can be located anywhere on the disk and in any order.

Each file has a descriptive entry in the directory where it resides. This entry contains the file's name, size, last modification date and time, and a field giving several important attributes (read-only, system, hidden, modified since last archived). It also contains the starting cluster number for the file; subsequent clusters are located using the FAT.

Volume Label

An MS-DOS/dosFs disk can have a volume label associated with it. The volume label is a special entry in the root directory. Rather than containing the name of a file or subdirectory, the volume label entry contains a string used to identify the volume. This string can contain up to 11 characters. The volume label entry is identified by a special value of the file-attribute byte in the directory entry.

Note that a volume label entry is not reported using ls( ). However, it does occupy one of the fixed number of entries in the root directory.

The volume label can be added to a dosFs volume by using the ioctl( ) call with the FIOLABELSET function. This adds a label entry to the volume's root directory if none exists or changes the label string in an existing volume label entry. The volume label entry takes up one of the fixed number of root directory entries; attempting to add an entry when the root directory is full results in an error.

The current volume label string for a volume can be obtained by calling the ioctl( ) call with the FIOLABELGET function. If the volume has no label, this call returns ERROR and sets errno to S_dosFsLib_NO_LABEL.

Disks initialized under VxWorks or under MS-DOS 5.0 (or later) also contain the volume label string within a boot sector field.

4.2.2   Initializing the dosFs File System

Note that before any other operations can be performed, the dosFs file system library, dosFsLib, must be initialized by calling dosFsInit( ). This routine takes a single parameter, the maximum number of dosFs file descriptors that can be open at one time. That number of file descriptors is allocated during initialization; a descriptor is used each time your application opens a file, directory, or the file system device.

The dosFsInit( ) routine also makes an entry for the file system in the I/O system driver table (with iosDrvInstall( )). This entry specifies entry points for dosFs file operations and is used for all devices that use the dosFs file system. The driver number assigned to the dosFs file system is recorded in a global variable dosFsDrvNum.

The dosFsInit( ) routine is normally called by the usrRoot( ) task after starting the VxWorks system. To use this initialization, select INCLUDE_DOSFS for inclusion in the project facility VxWorks view, and set NUM_DOSFS_FILES to the desired maximum open file count on the Params properties tab.

4.2.3   Initializing a Device for Use with dosFs

After the dosFs file system is initialized, the next step is to create one or more devices. Devices are created by the device driver's device creation routine (xxDevCreate( )). The driver routine returns a pointer to a block device descriptor structure (BLK_DEV). The BLK_DEV structure describes the physical aspects of the device and specifies the routines that the device driver provides to a file system. For more information on block devices, see 3.9.4 Block Devices.

Immediately after its creation, the block device has neither a name nor a file system associated with it. To initialize a block device for use with the dosFs file system, the already-created block device must be associated with dosFs and a name must be assigned to it. This is done with the dosFsDevInit( ) routine. Its parameters are the name to be used to identify the device, a pointer to the block device descriptor structure (BLK_DEV), and a pointer to the volume configuration structure DOS_VOL_CONFIG (see 4.2.4 Volume Configuration). For example:

DOS_VOL_DESC *pVolDesc; 
DOS_VOL_CONFIG configStruct; 
pVolDesc = dosFsDevInit ("DEV1:", pBlkDev, &configStruct);

The dosFsDevInit( ) call performs the following tasks:

Initializing the device for use with dosFs does not format the disk, nor does it initialize the disk with MS-DOS structures (root directory, FAT, and so on). This permits using dosFsDevInit( )with disks that already have data in an existing MS-DOS file system; see 4.2.6 Using an Already Initialized Disk. Formatting and DOS disk initialization can be done using the ioctl( ) functions FIODISKFORMAT and FIODISKINIT, respectively.

The dosFsMkfs( ) call provides an easier method of initializing a dosFs device; it does the following:

The routine dosFsMkfs( ) by default does not enable any dosFs-specific volume options (DOS_OPT_CHANGENOWARN, DOS_OPT_AUTOSYNC, DOS_OPT_LONGNAMES, DOS_OPT_LOWERCASE, or DOS_OPT_EXPORT). To enable any combination of these options, use dosFsMkfsOptionsSet( ) before calling dosFsMkfs( ) to initialize the disk. For more information on the default configuration values, see the manual entry for dosFsMkfs( ).

4.2.4   Volume Configuration

The volume configuration structure, DOS_VOL_CONFIG, is used during the dosFsDevInit( ) call. This structure contains various dosFs file system variables describing the layout of data on the disk. Most of the fields in the structure correspond to those in the boot sector. Table 4-1 lists the fields in the DOS_VOL_CONFIG structure.

Table 4-1:  DOS_VOL_CONFIG Fields


Field
Description

dosvc_mediaByte  
Media-descriptor byte 
dosvc_secPerClust  
Number of sectors per cluster 
dosvc_nResrvd  
Number of reserved sectors that precede the first FAT copy; the minimum is 1 (the boot sector) 
dosvc_nFats  
Number of FAT copies 
dosvc_secPerFat  
Number of sectors per FAT copy 
dosvc_maxRootEnts  
Maximum number of entries in root directory 
dosvc_nHidden  
Number of hidden sectors, normally 0 
dosvc_options  
VxWorks-specific file system options 
dosvc_reserved  
Reserved for future use by Wind River Systems 

Calling dosFsConfigInit( )is a convenient way to initialize DOS_VOL_CONFIG. It takes the configuration variables as parameters and fills in the structure. This is useful for initializing devices interactively from the Tornado shell (see the Tornado User's Guide: Shell). The DOS_VOL_CONFIG structure must be allocated before dosFsConfigInit( ) is called.

DOS_VOL_CONFIG Fields

All but the last two DOS_VOL_CONFIG fields in Table 4-1 describe standard MS-DOS characteristics. The field dosvc_options is specific to the dosFs file system. Possible options for this field are shown in Table 4-2.

Table 4-2:  dosFs Volume Options


Option
Hex Value
Description

DOS_OPT_CHANGENOWARN  
0x1
Disk may be changed without warning. 
DOS_OPT_AUTOSYNC  
0x2
Synchronize disk during I/O. 
DOS_OPT_LONGNAMES  
0x4
Use case-sensitive file names not restricted to 8.3 convention. 
DOS_OPT_EXPORT 
0x8
Allow exporting using NFS. 
DOS_OPT_LOWERCASE 
0x40
Use lower case filenames on disk. 

The first two options specify the action used to synchronize the disk buffers with the physical device. The remaining options involve extensions to dosFs capabilities.

DOS_OPT_CHANGENOWARN
Set this option if the device is a disk that can be replaced without being unmounted or having its change in ready-status declared. In this situation, check the disk regularly to determine whether it has changed. This causes significant overhead; thus, we recommend that you provide a mechanism that always synchronizes and unmounts a disk before it is removed, or at least announces a change in ready-status. If such a mechanism is in place, or if the disk is not removable, do not set this option. Auto-sync mode is enabled automatically when DOS_OPT_CHANGENOWARN is set (see the description for DOS_OPT_AUTOSYNC, next). For more information on DOS_OPT_CHANGENOWARN, see 4.2.17 Changing Disks.

DOS_OPT_AUTOSYNC
Set this option to assure that directory and FAT data in the disk's buffers are written to the physical device as soon as possible after modification, rather than only when the file is closed. This can be desirable in situations where it is important that data be stored on the physical medium as soon as possible so as to avoid loss in the event of a system crash. There is a significant performance penalty incurred when using auto-sync mode; limit its use, therefore, to circumstances where there is a threat to data integrity.

However, DOS_OPT_AUTOSYNC does not make dosFs automatically write data to disk immediately after every write( ); doing so implies an extreme performance penalty. If your application requires this effect, use the ioctl( )function FIOFLUSH after every call to write( ).

Note that auto-sync mode is automatically enabled whenever DOS_OPT_CHANGENOWARN is set. For more information on auto-sync mode, see 4.2.17 Changing Disks.

DOS_OPT_LONGNAMES
Set this option to allow the use of case-sensitive file names, with name lengths not restricted to MS-DOS's 8.3 convention. For more information on this option, see 4.2.18 Long Name Support.

DOS_OPT_EXPORT
Set this option to initialize file systems that you intend to export using NFS. With this option, dosFs initialization creates additional in-memory data structures that are required to support the NFS protocol. While this option is necessary to initialize a file system that can be exported, it does not actually export the file system. See VxWorks Network Programmer's Guide: File Access Applications.

DOS_OPT_LOWERCASE
Set this option to force filenames created by dosFs to use lowercase alphabetical characters. (Normally, filenames are created using uppercase characters, unless the DOS_OPT_LONGNAMES option is enabled.) This option may be required if the dosFs volume is mounted by a PC-based NFS client. This option has no effect if DOS_OPT_LONGNAMES is also specified.

Calculating Configuration Values

The values for dosvc_secPerClust and dosvc_secPerFat in the DOS_VOL_CONFIG structure must be calculated based on the particular device being used.

dosvc_secPerClust
This field specifies how many contiguous disk sectors make up a single cluster. Because a cluster is the smallest amount of disk space that can be allocated at a time, the size of a cluster determines how finely the disk allocation can be controlled. A large number of sectors per cluster causes more sectors to be allocated at a time and reduces the overall efficiency of disk space usage. For this reason, it is generally preferable to use the smallest possible number of sectors per cluster, although having less than two sectors per cluster is generally not necessary.

The maximum size of a FAT entry is 16 bits; thus, there is a maximum of 65,536 (64KB, or 0x10000) clusters that can be described. This is therefore the maximum number of clusters for a device. To determine the appropriate number of sectors per cluster, divide the total number of sectors on the disk (the bd_nBlocks field in the device's BLK_DEV structure) by 0x10000 (64KB). Round up the resulting value to the next whole number. The final result is the number of sectors per cluster; place this value in the dosvc_secPerClust field in the DOS_VOL_CONFIG structure.

dosvc_secPerFat
This field specifies the number of sectors required on the disk for each copy of the FAT. To calculate this value, first determine the total number of clusters on the disk. The total number of clusters is equal to the total number of sectors (bd_nBlocks in the BLK_DEV structure) divided by the number of sectors per cluster. As mentioned previously, the maximum number of clusters on a disk is 64KB.

The cluster count must then be multiplied by the size of each FAT entry: if the total number of clusters is 4085 or less, each FAT entry requires 12 bits (11\xda 2 bytes); if the number of clusters is greater than 4085, each FAT entry requires 16 bits (2 bytes). The result of this multiplication is the total number of bytes required by each copy of the FAT. This byte count is then divided by the size of each sector (the bd_bytesPerBlk field in the BLK_DEV structure) to determine the number of sectors required for each FAT copy; if there is any remainder, add one (1) to the result. Place this final value in the dosvc_secPerFat field.

Assuming 512-byte sectors, the largest possible FAT (with entries describing 64KB clusters) occupies 256 sectors per copy, calculated as follows:

Standard Disk Configurations

For floppy disks, a number of standard disk configurations are used in MS-DOS systems. In general, these are uniquely identified by the media-descriptor byte value (at least for a given size of floppy disk), although some manufacturers have used duplicate values for different formats. Some widely used configurations are summarized in Table 4-3.

Fixed disks do not use standard disk configurations because they are rarely attached to a foreign system. Usually fixed disks use a media format byte of 0xF8.

Table 4-3:  MS-DOS Floppy Disk Configurations


Capacity
160KB 
180KB 
320KB 
360KB 
1.2MB 
720KB 
1.44MB 
Size
5.25" 
5.25" 
5.25" 
5.25" 
5.25" 
3.5" 
3.5" 
Sides
Tracks
40 
40 
40 
40 
80 
80 
80 
Sectors/Track
15 
18 
Bytes/Sector
512 
512 
512 
512 
512 
512 
512 
secPerClust
nResrvd
nFats
maxRootEnts
64 
64 
112 
112 
224 
112 
224 
mediaByte
0xFE 
0xFC 
0xFF 
0xFD 
0xF9 
0xF9 
0xF0 
secPerFat
nHidden
0 

4.2.5   Changes In Volume Configuration

As mentioned previously, various disk configuration parameters are specified when the dosFs file system device is first initialized using dosFsDevInit( ). These parameters are kept in the volume descriptor, DOS_VOL_DESC, for the device. However, it is possible for a disk with different parameter values to be placed in a drive after the device is already initialized. If another disk is substituted for the one with the configuration parameters that were last entered into the volume descriptor, the configuration parameters of the new disk must be obtained before it can be used.

When a disk is mounted, the boot sector information is read from the disk. This data is used to update the configuration data in the volume descriptor. Note that this happens the first time the disk is accessed, and again after the volume is unmounted (using dosFsVolUnmount( )) or a ready-change operation is performed. For more information, see 4.2.17 Changing Disks.

This automatic re-initialization of the configuration data has an important implication. The volume descriptor data is used when initializing a disk (with FIODISKINIT); thus, the disk is initialized with the configuration of the most recently mounted disk, regardless of the original specification during dosFsDevInit( ). Therefore, we recommend that you use FIODISKINIT immediately after dosFsDevInit( ), before any disk is mounted. (The device is opened in raw mode, the FIODISKINIT ioctl( ) function is performed, and the device is closed.)

4.2.6   Using an Already Initialized Disk

If you are using a disk that is already initialized with an MS-DOS boot sector, FAT, and root directory (for example, by using the FORMAT utility in MS-DOS), it is not necessary to provide the volume configuration data during dosFsDevInit( ).

You can omit the MS-DOS configuration data by specifying a NULL pointer instead of the address of a DOS_VOL_CONFIG structure during dosFsDevInit( ). However, only use this method if you are sure that the first use of the volume is with a properly formatted and initialized disk.

When mounting an already initialized disk, all standard MS-DOS configuration values are obtained from the disk's boot sector. However, the options that are specific to dosFs must be determined differently.

Disks that are already initialized with the DOS_OPT_LONGNAMES (case-sensitive file names not restricted to 8.3 convention) option are recognized automatically by a specific volume ID string that is placed in the boot sector.

The DOS_OPT_CHANGENOWARN, DOS_OPT_AUTOSYNC, DOS_OPT_LOWERCASE, and DOS_OPT_EXPORT options are recorded only in memory, not on disk. Therefore they cannot be detected when you initialize a disk with NULL in place of the DOS_VOL_CONFIG structure pointer; you must re-enable them each time you mount a disk. You can set default values for these options with the dosFsDevInitOptionsSet( )routine: the defaults apply to any dosFs file systems you initialize with dosFsDevInit( ) thereafter, unless you supply explicit DOS_VOL_CONFIG information.

You can also enable the DOS_OPT_CHANGENOWARN and DOS_OPT_AUTOSYNC options dynamically during disk operation, rather than during initialization, with the dosFsVolOptionsSet( ) routine.

4.2.7   Accessing Volume Configuration Information

Disk configuration information is available using dosFsConfigShow( )2 and dosFsConfigGet( ) from the Tornado shell. See the Tornado User's Guide: Shell.

Use dosFsConfigShow( ) to display configuration information such as the largest contiguous area and the device name. For example:

    -> dosFsConfigShow "/RAM1/" 
    value = 0 = 0x0

The output is sent to the standard output device, and looks like the following:

    device name:                    /RAM1/ 
    total number of sectors:        400 
    bytes per sector:               512 
    media byte:                     0xf0 
    # of sectors per cluster:       2 
    # of reserved sectors:          1 
    # of FAT tables:                2 
    # of sectors per FAT:           1  
    max # of root dir entries:      112 
    # of hidden sectors:            0 
    removable medium:               FALSE 
    disk change w/out warning:      not enabled 
    auto-sync mode:                 not enabled 
    long file names:                not enabled 
    exportable file system:         not enabled 
    volume mode:                    O_RDWR (read/write) 
    available space:                199680 bytes 
    max avail. contig space:        199680 bytes

The dosFsConfigGet( ) routine stores the disk configuration information in a DOS_VOL_CONFIG structure. This can be useful if you have a pre-existing disk and want to initialize a new disk with the same parameters, or if you initialized the dosFs file system on the disk using dosFsMkfs( ) and need to obtain the actual configuration values that were calculated.

4.2.8   Mounting Volumes

A disk volume is mounted automatically, generally during the first open( ) or creat( ) operation for a file or directory on the disk. (Certain ioctl( ) calls also cause the disk to be mounted.) If a NULL pointer is specified instead of the address of a DOS_VOL_CONFIG structure during the dosFsDevInit( ) call, the disk is mounted immediately to obtain the configuration values.

When a disk is mounted, the boot sector, FAT, and directory data are read from the disk. The volume descriptor, DOS_VOL_DESC, is updated to reflect the configuration of the newly mounted disk.

Automatic mounting occurs on the first file access following dosFsVolUnmount( )or a ready-change operation (see 4.2.17 Changing Disks), or periodically if the disk is defined during the dosFsDevInit( ) call with the option DOS_OPT_CHANGENOWARN set. Automatic mounting does not occur when a disk is opened in raw mode; see 4.2.10 Opening the Whole Device (Raw Mode).


*

CAUTION: Because device names are recognized by the I/O system using simple substring matching, file systems should not use a slash (/) alone as a name; unexpected results may occur.

It is possible to mount a volume with usrFdConfig( ), but this routine does not return the DOS_VOL_DESC structure. A volume mounted with usrFdConfig( ) can not be operated on with most dosFs commands, including dosFsVolUnmount( ). However, the dosFs ioctl( ) commands, including FIOUNMOUNT, access the volume information through the fd, so they can be used with usrFdConfig( ).

4.2.9   File I/O

Files on a dosFs file system device are created, deleted, written, and read using the standard VxWorks I/O routines: creat( ), remove( ), write( ), and read( ). See 3.3 Basic I/O for more information.

4.2.10   Opening the Whole Device (Raw Mode)

It is possible to open an entire dosFs volume. This is done by specifying only the device name during the open( ) or creat( ) call. A file descriptor is returned, as when a regular file is opened; however, operations on that file descriptor affect the entire device. Opening the entire volume in this manner is called raw mode.

The most common reason for opening the entire device is to obtain a file descriptor for an ioctl( ) function that does not pertain to an individual file. An example is the FIONFREE function, which returns the number of available bytes on the volume. However, for many of these functions, the file descriptor can be any open file descriptor to the volume, even one for a specific file.

When a disk is initialized with MS-DOS data structures (boot sector, empty root directory, FAT), open the device in raw mode. The ioctl( ) function FIODISKINIT performs the initialization.

You can both read and write data on a disk in raw mode. In this mode, the entire disk data area (that is, the disk portion following the boot sector, root directory, and FAT) is treated much like a single large file. No directory entry is made to describe any data written using raw mode.

For low-level I/O to an entire device, including the area used by MS-DOS data structures, see 4.4 Raw File System: rawFs and the online reference for rawFsLib under VxWorks Reference Manual>Libraries.

4.2.11   Creating Subdirectories

Subdirectories can be created in any directory at any time, except in the root directory if it has reached its maximum entry count. Subdirectories can be created in two ways:

  1. Using ioctl( ) with the FIOMKDIR function: The name of the directory to be created is passed as a parameter to ioctl( ). The file descriptor used for the ioctl( ) call is acquired either through opening the entire volume (raw mode), a regular file, or another directory on the volume.

  1. Using open( ): To create a directory, the O_CREAT option must be set in the flags parameter to open, and the FSTAT_DIR option must be set in the mode parameter. The open( ) call returns a file descriptor that describes the new directory. Use this file descriptor for reading only and close it when it is no longer needed.

When creating a directory using either method, the new directory name must be specified. This name can be either a full path name or a path name relative to the current working directory.

4.2.12   Removing Subdirectories

A directory that is to be deleted must be empty (except for the "." and ".." entries). The root directory can never be deleted. There are two methods for removing directories:

  1. Using ioctl( ) call with the FIORMDIR function, specifying the name of the directory. Again, the file descriptor used can refer to any file or directory on the volume, or to the entire volume itself.

  1. Using the remove( ) function, specifying the name of the directory.

4.2.13   Directory Entries

Each dosFs directory contains a set of entries describing its files and immediate subdirectories. Each entry contains the following information about a file or subdirectory:

file name
an 8-byte string (padded with spaces, if necessary) specifying the base name of the file. (Names can be up to 40 characters; for details see 4.2.18 Long Name Support.)

file extension
a 3-byte string (space-padded) specifying an optional extension to the file or subdirectory name. (If case-sensitive file names not restricted to the 8.3 convention are selected, the extension concept is not applicable.)

file attribute
a one-byte field specifying file characteristics; see 4.2.15 File Attributes.

time
the encoded creation or modification time for the file.

date
the encoded creation or modification date for the file.

cluster number
the number of the starting cluster within the file. Subsequent clusters are found by searching the FAT.

file size
the size of the file, in bytes. This field is always 0 for entries describing subdirectories.

4.2.14   Reading Directory Entries

Directories on dosFs volumes can be searched using the opendir( ), readdir( ), rewinddir( ), and closedir( ) routines. These calls can be used to determine the names of files and subdirectories.

To obtain more detailed information about a specific file, use the fstat( ) or stat( ) function. Along with standard file information, the structure used by these routines also returns the file-attribute byte from a directory entry.

For more information, see the manual entry for dirLib.

4.2.15   File Attributes

The file-attribute byte in a dosFs directory entry consists of a set of flag bits, each indicating a particular file characteristic. The characteristics described by the file-attribute byte are shown in Table 4-4.

Table 4-4:  Flags in the File-Attribute Byte


VxWorks Flag Name
Hex value
Description

DOS_ATTR_RDONLY  
0x01
read-only file 
DOS_ATTR_HIDDEN  
0x02
hidden file 
DOS_ATTR_SYSTEM  
0x04
system file 
DOS_ATTR_VOL_LABEL  
0x08
volume label 
DOS_ATTR_DIRECTORY  
0x10
subdirectory 
DOS_ATTR_ARCHIVE  
0x20
file is subject to archiving 

DOS_ATTR_RDONLY is checked when a file is opened for O_WRONLY or O_RDWR. If the flag is set, open( )returns ERROR and sets errno to S_dosFsLib_READ_ONLY.


*

CAUTION: The MS-DOS hidden file and system file flags, DOS_ATTR_HIDDEN and DOS_ATTR_SYSTEM, are ignored by dosFsLib. If present, they are kept intact, but they produce no special handling (for example, entries with these flags are reported when searching directories).

The volume label flag, DOS_ATTR_VOL_LABEL, indicates that a directory entry contains the dosFs volume label for the disk. A label is not required. If used, there can be only one volume label entry per volume, in the root directory. The volume label entry is not reported when reading the contents of a directory (using readdir( )). It can only be determined using the ioctl( ) function FIOLABELGET. The volume label can be set (or reset) to any string of 11 or fewer characters, using the ioctl( ) function FIOLABELSET. Any file descriptor open to the volume can be used during these ioctl( ) calls.

The directory flag, DOS_ATTR_DIRECTORY, indicates that this entry is not a regular file, but a subdirectory.

The archive flag, DOS_ATTR_ARCHIVE, is set when a file is created or modified. This flag is intended for use by other programs that search a volume for modified files and selectively archive them. Such a program must clear the archive flag since VxWorks does not.

All the flags in the attribute byte, except the directory and volume label flags, can be set or cleared using the ioctl( ) function FIOATTRIBSET. This function is called after the opening of the specific file with the attributes to be changed. The attribute-byte value specified in the FIOATTRIBSET call is copied directly; to preserve existing flag settings, determine the current attributes using stat( ) or fstat( ), then change them using bitwise and and or operations.

Example 4-1:  Setting DosFs File Attributes

This example makes a dosFs file read-only, and leaves other attributes intact.

#include "vxWorks.h" 
#include "ioLib.h" 
#include "dosFsLib.h" 
#include "sys/stat.h" 
#include "fcntl.h"
STATUS changeAttributes (void) { int         fd; struct stat statStruct;
/* open file */
if ((fd = open ("file", O_RDONLY, 0)) == ERROR) return (ERROR);
/* get directory entry data */ if (fstat (fd, &statStruct) == ERROR) return (ERROR);
/* set read-only flag on file */
if (ioctl (fd, FIOATTRIBSET, (statStruct.st_attrib | DOS_ATTR_RDONLY)) == ERROR) return (ERROR);
/* close file */
close (fd); }

4.2.16   File Date and Time

Directory entries contain a time and date for each file or directory. This time is set when the file is created, and it is updated when a file that was modified is closed. Entries describing subdirectories are not updated--they always contain the creation date and time for the subdirectory.

The dosFsLib library maintains the date and time in an internal structure. While there is currently no mechanism for automatically advancing the date or time, two different methods for setting the date and time are provided.

The first method involves using two routines, dosFsDateSet( ) and dosFsTimeSet( ). The following examples illustrate their use:

dosFsDateSet (1990, 12, 25);        /* set date to Dec-25-1990 */ 
dosFsTimeSet (14, 30, 22);          /* set time to 14:30:22    */

These routines must be called periodically to update the time and date values.

The second method requires a user-supplied hook routine. If a time and date hook routine is installed using dosFsDateTimeInstall( ), that routine is called whenever dosFsLib requires the current date and time. You can use this to take advantage of hardware time-of-day clocks that can be read to obtain the current time. It can also be used with other applications that maintain actual time and date.

Define the date/time hook routine as follows (the name dateTimeHook is an example; the actual routine name can be anything):

    void dateTimeHook 
        ( 
        DOS_DATE_TIME * pDateTime   /* ptr to dosFs date & time struct */ 
        )

On entry to the hook routine, the DOS_DATE_TIME structure contains the last time and date set in dosFsLib. Next, the hook routine fills the structure with the correct values for the current time and date. Unchanged fields in the structure retain their previous values.

The MS-DOS specification provides only for 2-second granularity in file time-stamps. If the number of seconds in the time specified during dosFsTimeSet( ) or the date/time hook routine is odd, it is rounded down to the next even number.

The date and time used by dosFsLib is initially Jan-01-1980, 00:00:00.

4.2.17   Changing Disks

To increase performance, the dosFs file system keeps in memory copies of directory entries and the file allocation table (FAT) for each mounted volume. While this greatly speeds up access to files, it requires that dosFsLib be notified when removable disks are changed (for example, when floppies are swapped). Two different notification methods are provided: (1) dosFsVolUnmount( ) and (2) the ready-change mechanism. The following sections are not generally applicable for non-removable media (although dosFsVolUnmount( ) can be useful in system shutdown situations).

Unmounting Volumes

The preferred method of announcing a disk change is to call dosFsVolUnmount( ) prior to removing the disk. This call flushes all modified data structures to disk if possible (see Synchronizing Volumes) and also marks any open file descriptors as obsolete. During the next I/O operation, the disk is remounted. The ioctl( ) call can also be used to initiate dosFsVolUnmount( ), by specifying the FIOUNMOUNT function code. Any open file descriptor to the device can be used in the ioctl( ) call.

Subsequent attempts to use obsolete file descriptors for I/O operations return an S_dosFsLib_FD_OBSOLETE error. To free such file descriptors, use close( ), as usual. This returns S_dosFsLib_FD_OBSOLETE as well, but it successfully frees the descriptor. File descriptors acquired when opening the entire volume (raw mode) are not marked as obsolete during dosFsVolUnmount( ) and can still be used.

ISRs must not call dosFsVolUnmount( ) directly, because it is possible for the call to pend while the device becomes available. The ISR can instead give a semaphore that prompts a task to unmount the volume. (Note that dosFsReadyChange( ) can be called directly from ISRs; see Announcing Disk Changes with Ready-Change.)

When dosFsVolUnmount( ) is called, it attempts to write buffered data out to the disk. Its use is therefore inappropriate for situations where the disk-change notification does not occur until a new disk is inserted, because the old buffered data would be written to the new disk. In this case, use dosFsReadyChange( ), which is described in Announcing Disk Changes with Ready-Change.

If dosFsVolUnmount( ) is called after the disk is physically removed, the data-flushing portion of its operation fails. However, the file descriptors are still marked as obsolete and the disk is marked as requiring remounting. In this situation, dosFsVolUnmount( ) does not return an error. To avoid lost data, explicitly synchronize the disk before removing it (see Synchronizing Volumes).


*

CAUTION: Do not attempt to unmount a volume that was mounted with usrFdConfig( ) using dosFsVolUnmount( ). This routine does not return the DOS_VOL_CONFIG structure required by dosFsVolUnmount( ). You can use ioctl( ) with FIOUNMOUNT, which accesses volume information through the fd.

Announcing Disk Changes with Ready-Change

The second method of informing dosFsLib that a disk change is taking place is with the ready-change mechanism. A change in the disk's ready-status is interpreted by dosFsLib as indicating that the disk must be remounted before the next I/O operation.

There are three ways to announce a ready-change:

The ready-change mechanism does not provide the ability to flush data structures to the disk. It merely marks the volume as needing remounting. Thus, buffered data (data written to files, directory entries, or FAT changes) can be lost. This can be avoided by synchronizing the disk before asserting ready-change (see Synchronizing Volumes). The combination of synchronizing and asserting ready-change provides all the functionality of dosFsVolUnmount( ), except for marking file descriptors as obsolete.

Ready-change can be used in ISRs, because it does not attempt to flush data or perform other operations that could cause delay.

The block device driver status-check routine (identified by the bd_statusChk field in the BLK_DEV structure) can be useful for asserting ready-change for devices that detect a disk change only after the new disk is inserted. This routine is called at the beginning of each open( ) or creat( ) operation, before the file system checks for ready-change. See 3.9.4 Block Devices.

Disks with No Change Notification

If it is not possible for dosFsVolUnmount( ) to be called or a ready-change to be announced, then each time the disk is changed, the device must be specially identified when it is initialized for use with the file system. This is done by setting DOS_OPT_CHANGENOWARN in the dosvc_options field of the DOS_VOL_CONFIG structure when calling dosFsDevInit( ); see 4.2.4 Volume Configuration.

This configuration option results in a significant performance penalty, because the disk configuration data must be read in regularly from the physical disk (in case it was removed and a new one inserted). In addition, setting DOS_OPT_CHANGENOWARN also enables auto-sync mode; see Auto-Sync Mode. Note that all that is required for disk change notification is that either the dosFsVolUnmount( ) call or ready-change be issued each time the disk is changed. It is not necessary that it be called from the device driver or an ISR. For example, if your application provided a user interface through which an operator could enter a command resulting in an dosFsVolUnmount( ) call before removing the disk, that would be sufficient, and DOS_OPT_CHANGENOWARN does not need to be set. However, it is important that the operator follow such a procedure strictly.

Synchronizing Volumes

When a disk is synchronized, all modified buffered data is physically written to the disk, so that the disk is up to date. This includes data written to files, updated directory information, and the FAT.

To avoid loss of data, synchronize a disk before removing it. You may need to explicitly synchronize a disk, depending on when (or if) dosFsVolUnmount( ) is called. If your application does not call this routine, or it is called after the disk is removed, use ioctl( ) to explicitly write the data to the device.

When dosFsVolUnmount( ) is called, an attempt is made to synchronize the device before unmounting. If the disk is still present and writable at the time of the call, synchronization takes place, and no further action is required to protect the integrity of the data written to it before it is dismounted. However, if the dosFsVolUnmount( ) call is made after a disk is removed, it is obviously too late to synchronize, and dosFsVolUnmount( ) discards the buffered data.

To explicitly synchronize a disk before it is removed, use ioctl( ) specifying the FIOSYNC function. (This could be done in response to an operator command.) Do this if the dosFsVolUnmount( ) call is made after a disk is removed or if the routine dosFsVolUnmount( ) is never called. The file descriptor used during the ioctl( ) call is obtained when the whole volume (raw mode) is opened.

Auto-Sync Mode

dosFsLib provides a modified mode of synchronization called auto-sync. When this option is enabled, data for modified directories and the FAT are physically written to these devices as soon as they are logically altered. (Otherwise, such changes are not necessarily written out until the involved file is closed.)

Auto-sync mode is enabled by setting DOS_OPT_AUTOSYNC in the dosvc_options field of the DOS_VOL_CONFIG structure when dosFsDevInit( ) is called; see 4.2.4 Volume Configuration. Auto-sync mode is automatically enabled if the volume does not have disk change notification (that is, if DOS_OPT_CHANGENOWARN is set by dosFsDevInit( )).

Auto-sync results in a performance penalty, but it provides the highest level of data security, because it minimizes the period during which directory and FAT data are not up to date on the disk. Auto-sync is often desirable for applications where data integrity is threatened by events such as a system crash.

4.2.18   Long Name Support

The dosFs long name support allows the use of case-sensitive file names longer than MS-DOS's 8.3 convention. These names can be up to 40 characters long and can be made up of any ASCII characters. In addition, a dot ( . ), which in MS-DOS indicates a file-name extension, has no special significance.

Long name support is enabled by setting DOS_OPT_LONGNAMES in the dosvc_options field of the DOS_VOL_CONFIG structure when calling dosFsDevInit( ).


*

WARNING: If you use this feature, the disk is no longer MS-DOS compatible. Use long name support only for storing data local to VxWorks, on a disk that is initialized on a VxWorks system using dosFsDevInit( ) or dosFsMkfs( ).

4.2.19   Contiguous File Support

The dosFs file system provides efficient handling of contiguous files. A contiguous file is made up of a series of consecutive disk sectors. This capability includes both the allocation of contiguous space to a specified file (or directory) and optimized access to such a file.

To allocate a contiguous area to a file, first create the file in the normal fashion, using open( ) or creat( ). Then use the file descriptor returned during the creation of the file to make the ioctl( ) call, specifying the FIOCONTIG function. The parameter to ioctl( ) with the FIOCONTIG function is the size of the requested contiguous area, in bytes. The FAT is searched for a suitable section of the disk, and if found, it is assigned to the file. (If there is no contiguous area on the volume large enough to satisfy the request, an error is returned.) The file can then be closed, or it can be used for further I/O operations.

Example 4-2:  Creating a DosFs Contiguous File

This example creates a dosFs file and allocates 0x10000 contiguous bytes to it.

#include "vxWorks.h" 
#include "ioLib.h" 
#include "fcntl.h"
STATUS fileContigTest (void) { int fd; STATUS status;
/* open file */
if ((fd = creat ("file", O_RDWR)) == ERROR) return (ERROR);
/* get contiguous area */
status = ioctl (fd, FIOCONTIG, 0x10000); if (status != OK)
/* do error handling */
printf ("ERROR");
/* use file */
/* close file */
close (fd); }

It is also possible to request the largest available contiguous space. Use CONTIG_MAX for the size of the contiguous area. For example:

status = ioctl (fd, FIOCONTIG, CONTIG_MAX);

It is important that the file descriptor used for the ioctl( ) call be the only descriptor open to the file. Furthermore, because a file can be assigned a different area of the disk than is originally allocated, perform the ioctl( ) FIOCONTIG operation before any data is written to the file.

To deallocate unused reserved bytes, use the POSIX-compatible routine ftruncate( ) or the ioctl( ) function FIOTRUNC.

Subdirectories can also be allocated a contiguous disk area in the same manner. If the directory is created using the ioctl( ) function FIOMKDIR, it must be explicitly opened to obtain a file descriptor to it; if the directory is created using options to open( ), the returned file descriptor from that call can be used. A directory must be empty (except for the "." and ".." entries) when it has contiguous space allocated to it.

When any file is opened, it is checked for contiguity. If a file is recognized as contiguous, a more efficient technique for locating specific sections of the file is used, rather than following cluster chains in the FAT, as must be done for fragmented files. This enhanced handling of contiguous files takes place regardless of whether the space is explicitly allocated using FIOCONTIG.

To find the maximum contiguous area on a device, use the ioctl( ) function FIONCONTIG. This information can also be displayed by dosFsConfigShow( ).

Example 4-3:  Finding the Maximum Contiguous Area on a DosFs Device

In this example, the size (in bytes) of the largest contiguous area is copied to the integer pointed to by the third parameter to ioctl( ) (count).

#include "vxWorks.h" 
#include "fcntl.h" 
#include "ioLib.h"
STATUS contigTest (void) { int count; int fd;
/* open device in raw mode */ if ((fd = open ("/DEV1/", O_RDONLY, 0)) == ERROR) return (ERROR);
/* find max contiguous area */ ioctl (fd, FIONCONTIG, &count);
/* close device and display size of largest contiguous area */ close (fd); printf ("largest contiguous area = %d\n", count); }

4.2.20   I/O Control Functions Supported by dosFsLib

The dosFs file system supports the ioctl( ) functions listed in Table 4-5. These functions are defined in the header file ioLib.h. For more information, see the manual entries for dosFsLib and for ioctl( ) in ioLib.

Table 4-5:  I/O Control Functions Supported by dosFsLib  


Function
Decimal Value
Description

FIOATTRIBSET  
35
Set the file-attribute byte in the dosFs directory entry. 
FIOCONTIG  
36
Allocate contiguous disk space for a file or directory. 
FIODISKCHANGE  
13
Announce a media change. 
FIODISKFORMAT  
5
Format the disk (device driver function). 
FIODISKINIT  
6
Initialize a dosFs file system on a disk volume. 
FIOFLUSH  
2
Flush the file output buffer. 
FIOFSTATGET  
38
Get file status information (directory entry data). 
FIOGETNAME  
18
Get the file name of the fd
FIOLABELGET  
33
Get the volume label. 
FIOLABELSET  
34
Set the volume label. 
FIOMKDIR  
31
Create a new directory. 
FIONCONTIG  
41
Get the size of the maximum contiguous area on a device. 
FIONFREE  
30
Get the number of free bytes on the volume. 
FIONREAD  
1
Get the number of unread bytes in a file. 
FIOREADDIR  
37
Read the next directory entry. 
FIORENAME  
10
Rename a file or directory. 
FIORMDIR  
32
Remove a directory. 
FIOSEEK  
7
Set the current byte offset in a file. 
FIOSYNC  
21
Same as FIOFLUSH, but also re-reads buffered file data. 
FIOTRUNC  
42
Truncate a file to a specified length. 
FIOUNMOUNT  
39
Unmount a disk volume. 
FIOWHERE  
8
Return the current byte position in a file. 

4.2.21   Booting from a Local dosFs File System Using SCSI

VxWorks can be booted from a local SCSI device. Before you can boot from SCSI, you must make new boot ROMs that contain the SCSI library. Define INCLUDE_SCSI in the project facility and INCLUDE_SCSI_BOOT in config.h and rebuild bootrom.hex. (For configuration information, see 8.5 Configuring VxWorks; INCLUDE_SCSI_BOOT can only be configured in config.h. For boot ROM information, see 8.9 Creating Bootable Applications.)

After burning the SCSI boot ROMs, you can prepare the dosFs file system for use as a boot device. The simplest way to do this is to partition the SCSI device so that a dosFs file system starts at block 0. You can then make the new vxWorks image, place it on your SCSI boot device, and boot the new VxWorks system. These steps are shown in more detail below.


*

WARNING: For use as a boot device, the directory name for the dosFs file system must begin and end with slashes (as with /sd0/ used in the following example). This is an exception to the usual naming convention for dosFs file systems and is incompatible with the NFS requirement that device names not end in a slash.

  1. Create the SCSI device using scsiPhysDevCreate( ) (see SCSI Drivers), and initialize the disk with a dosFs file system (see 4.2.2 Initializing the dosFs File System). Modify the file installDir/target/src/config/usrScsiConfig.c to reflect your SCSI configuration. The following example creates a SCSI device with a dosFs file system spanning the full device:

pPhysDev = scsiPhysDevCreate (pSysScsiCtrl, 2, 0, 0, -1, 0, 0, 0); 
pBlkDev = scsiBlkDevCreate (pPhysDev, 0, 0); 
dosFsDevInit ("/sd0/", pBlkDev, 0);

  1. Remake VxWorks and copy the new kernel to the drive:3

-> copy "unixHost:/usr/wind/target/config/bspname/vxWorks", \ 
"/sd0/vxWorks" 

  1. Reboot the system, and then change the boot parameters. Boot device parameters for SCSI devices follow this format:

    scsi=id,lun 
where id is the SCSI ID of the boot device, and lun is its Logical Unit Number (LUN). To enable use of the network, include the on-board Ethernet device (for example, ln for LANCE) in the other field. The following example boots from a SCSI device with a SCSI ID of 2 and a LUN of 0.

    [VxWorks Boot]: @ 
    boot device            : scsi=2,0 
    processor number       : 0 
    host name              : host 
    file name              : /sd0/vxWorks 
    inet on ethernet (e)   : 147.11.1.222:ffffff00 
    host inet (h)          : 147.11.1.3 
    user (u)               : jane 
    flags (f)              : 0x0 
    target name (tn)       : t222 
    other                  : ln 
    Attaching to scsi device... done. 
    Loading /sd0/vxWorks... 378060 + 27484 + 21544 
    Starting at 0x1000...

1:  In this and subsequent sections covering the dosFs file system, the term sector refers to the minimum addressable unit on a disk, because this is the term used by most MS-DOS documentation. In VxWorks, the units are normally referred to as blocks, and a disk device is called a block device.

2:  dosFsConfigShow( ) is automatically when dosFs is included in your VxWorks image.

3:  If you are using the target shell and have selected INCLUDE_NET_SYM_TBL for inclusion in the project facility VxWorks view, you must also copy the symbol table to the drive, as follows:
-> copy "unixHost:/usr/wind/target/config/bspname/vxWorks.sym", "/sd0/vxWorks.sym"