VxWorks uses a standard I/O interface between the file system and the device driver. This allows multiple file systems, of the same or different types, to operate within a single VxWorks system. By following these standard interfaces, you can write your own file system for VxWorks, and freely mix file systems and device drivers.
This chapter discusses the VxWorks file systems, listed below, describing how they are organized, configured, and used.
VxWorks also provides support for flash memory devices through the optional product TrueFFS. For more information, see 8. Flash Memory Block Device Driver.
The dosFs file system is an MS-DOS-compatible file system that offers considerable flexibility appropriate to the multiple demands of real-time applications. The primary features are:
This section summarizes the process of creating a dosFs file system, outlining the steps involved and diagramming where this process fits into the VxWorks system.
The process described in these steps corresponds to building the layers of VxWorks components between the hardware (such as a SCSI disk) and the I/O system, as illustrated in the sections within the dotted line in Figure 5-1.
Configure your kernel with the dosFs, CBIO, and block device components. This step is described in 5.2.2 Configuring Your System.
This step is done automatically if you have included the required components in your project. This step is described in 5.2.3 Initializing the dosFs File System
Create either a block device or a CBIO driver device (ramDiskCbio). This step is described in 5.2.4 Creating a Block Device.
Creating a disk cache is optional. Disk cache is intended only for rotational media. This step is described in 5.2.5 Creating a Disk Cache.
Creating and mounting partitions is optional. This step is described in 5.2.6 Creating and Using Partitions.
Create the dosFs device. You can safely create the device whether or not you are using a pre-formatted disk. This step is described in 5.2.7 Creating a dosFs Device.
If you are not using pre-formatted disks, format the volumes. This step is described in 5.2.8 Formatting the Volume.
Optionally, check the disk for volume integrity using dosFsChkDsk( ). Disk checking large disks can be time-consuming. The parameters you pass to dosFsDevCreate( ) determine whether disk checking happens automatically. For details, see the entry for dosFsDevCreate( ) in the VxWorks Reference Manual.
A disk volume is mounted automatically, generally during the first open( ) or creat( ) operation for a file or directory on the disk. This step is described in 5.2.9 Mounting Volumes.
And, either one or both of the following components are required:
In addition, you need to include the appropriate component for your block device; for example, INCLUDE_SCSI or INCLUDE_ATA. Finally, add any related components that are required for your particular system.
Before any other operations can be performed, the dosFs file system library, dosFsLib, must be initialized. This happens automatically, triggered by the required dosFs components that were included in the system.
Initializing the file system invokes iosDrvInstall( ), which adds the driver to the I/O system driver table. The driver number assigned to the dosFs file system is recorded in a global variable, dosFsDrvNum. The table specifies the entry points for the dosFs file operations that are accessed by the devices using dosFs.
Next, create one or more block devices. To create the device, call the routine appropriate for that device driver. The format for this routine is xxxDevCreate( ) where xxx represents the device driver type; for example, scsiBlkDevCreate( ) or ataDevCreate( ).
The driver routine returns a pointer to a block device descriptor structure, BLK_DEV. This structure describes the physical attributes of the device and specifies the routines that the device driver provides to a file system. For more information on block devices, see 4.9.4 Block Devices.
If you have included the INCLUDE_DISK_CACHE component in your system, you can use dcacheDevCreate( ) to create a disk cache for each block device. Disk cache is intended to reduce the impact of seek times on rotational media, and is not used for RAM disks or TrueFFS devices. Example 5-1 creates a disk cache.
If you have included the INCLUDE_DISK_PART component in your system, you can create partitions on a disk and mount volumes atop the partitions. Use the usrFdiskPartCreate( ) and dpartDevCreate( ) routines to do this.
The following two examples create and use partitions. The first example creates, partitions, and formats a disk. The second example uses the partitioned disk, from the first example, to create the partition handler.
Example 5-1: Creating and Partitioning a Disk and Creating Volumes
This example takes a pointer to a block device, creates three partitions, creates the partition handler for these partitions, and creates the dosFs device handler for them. Then, it formats the partitions using dosFsVolFormat( ), which is discussed in the next section.
STATUS usrPartDiskFsInit
(
void * blkDevId /* CBIO_DEV_ID or BLK_DEV* */
)
{
const char * devNames[] = { "/sd0a", "/sd0b", "/sd0c" };
int dcacheSize = 0x30000 ;
CBIO_DEV_ID cbio, cbio1 ;
/* create disk cache */
if((cbio = dcacheDevCreate(blkDevId, NULL, dcacheSize, "/sd0"))
== NULL )
return ERROR ;
/* create partitions */
if((usrFdiskPartCreate (cbio,3,50,45)) == ERROR)
return ERROR;
/* create partition manager with FDISK style decoder, up to 3 parts */
if((cbio1 = dpartDevCreate( cbio, 3, usrFdiskPartRead )) == NULL)
return ERROR;
/* create the 1st file system, 8 simult. open files, with CHKDSK */
if(dosFsDevCreate( devNames[0], dpartPartGet(cbio1,0), 8, 0 ) == ERROR)
return ERROR;
/* create the 2nd file system, 6 simult. open files, with CHKDSK */
if(dosFsDevCreate( devNames[1], dpartPartGet(cbio1,1), 6, 0 ) == ERROR)
return ERROR;
/* create the 3rd file system, 4 simultaneously open files, no CHKDSK */
if(dosFsDevCreate( devNames[2], dpartPartGet(cbio1,2), 4, NONE )
== ERROR)
return ERROR;
/* Formatting the first partition */
if(dosFsVolFormat (devNames[0], 2,0) == ERROR)
return ERROR;
/* Formatting the second partition */
if(dosFsVolFormat (devNames[1], 2,0) == ERROR)
return ERROR;
/* Formatting the third partition */
if(dosFsVolFormat (devNames[2], 2,0) == ERROR)
return ERROR;
return OK;
}
Example 5-2: Accessing a Partitioned Disk
The following example configures a partitioned disk with three already existing partitions. Note that the ATA hard disk component allows for auto-mounting as many partitions as are referenced within its name parameter.
STATUS usrPartDiskFsInit
(
void * blkDevId /* CBIO_DEV_ID or BLK_DEV*/
)
{
const char * devNames[] = { "/sd0a", "/sd0b", "/sd0c" };
int dcacheSize = 0x30000 ;
CBIO_DEV_ID cbio, cbio1 ;
/* create disk cache */
if((cbio = dcacheDevCreate(blkDevId, NULL, dcacheSize, "/sd0"))
== NULL )
return ERROR ;
/* create partition manager with FDISK style decoder, up to 3 parts */
if((cbio1 = dpartDevCreate( cbio, 3, usrFdiskPartRead )) == NULL)
return ERROR;
/* create the 1st file system, 8 simultaneously open files
* with CHKDSK
*/
if(dosFsDevCreate( devNames[0], dpartPartGet(cbio1,0), 8, 0 )
== ERROR)
return ERROR;
/* create the 2nd file sys, 6 simultaneously open files, with CHKDSK */
if(dosFsDevCreate( devNames[1], dpartPartGet(cbio1,1), 6, 0 ) == ERROR)
return ERROR;
/* create the 3rd file system, 4 simultaneously open files, no CHKDSK */
if(dosFsDevCreate( devNames[2], dpartPartGet(cbio1,2), 4, 0)
==ERROR)
return ERROR;
return OK;
}
Create a dosFs device using dosFsDevCreate( ), which calls iosDrvAdd( ) internally. This step simply adds the device to the I/O system; however, it does not invoke any I/O operations and, therefore, does not mount the disk.
This disk is not mounted until the first I/O operation occurs. For more information, see 5.4.4 Mounting Volumes.
A volume FAT format is set during disk formatting, according to either the volume size (by default), or the per-user defined settings passed to dosFsVolFormat( ). FAT options are summarized in Table 5-1:
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Uses case-insensitive long filenames, with up to 254 characters. This format accepts disks created with short names. MSFT Long Names1 is the default directory format.
Case-insensitive MS-DOS-style filenames (8.3), with eight uppercase characters for the name itself and three for the extension.
Wind River's proprietary VxWorks long name support, introduced prior to MSFT Long Names and used for backward compatibility with old dosFs VxLong disks. Allows case-sensitive filenames of up to 40 characters (consisting of any ASCII characters). The dot character (.), which indicates a file-name extension in MS-DOS, has no special significance in dosFs VxLong.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
This section provides examples of the steps discussed in the sections above. These examples use a variety of configurations and device types. They are meant to be relatively generic and applicable to most block devices.
The first example uses an ATA disk, and includes detailed descriptions of the commands run from the shell, displaying both user input and command-line output. The second example lists the required steps to create and format a RAM disk volume. The last example demonstrates how to initialize a pre-formatted SCSI disk.
|
|
|||||||||||||||||||
Create a block device (BLK_DEV) that controls the master ATA hard disk (drive zero) on the primary ATA controller (controller zero). This block device uses the entire disk.
-> pAta = ataDevCreate (0,0,0,0) new symbol "pAta" added to symbol table. pAta = 0x3fff334: value = 67105604 = 0x3fff344 = pAta + 0x10
Above, pAta is now a block device pointer (BLK_DEV *). The routine ataDevCreate( ) returns a valid value. A return value of NULL (0x0) indicates an error in ataDevCreate( ). Such an error usually indicates a BSP configuration or hardware configuration error.
This step is appropriate for any BLK_DEV device; for example, flash, SCSI, and so on. For related information, see the reference entry for ataDevCreate( ).
Next, create an (optional) disk data cache CBIO layer atop this disc:
-> pCbioCache = dcacheDevCreate (pAta,0,0,"ATA Hard Disk Cache") new symbol "pCbioCache" added to symbol table. pCbioCache = 0x3ffdbd0: value = 67105240 = 0x3fff1d8
Above, pCbioCache is a CBIO_DEV_ID, which is the handle for controlling the CBIO disk cache layer. For more information, see the reference entry for dcacheDevCreate( ).
Then, create two partitions on this disk device, specifying 50% of the disk space for the second partition, leaving 50% for the first partition. This step should only be performed once, when the disk is first initialized. If partitions are already written to the disk, this step should not be performed since it destroys data:
-> usrFdiskPartCreate (pCbioCache, 2, 50, 0, 0) value = 0 = 0x0
For more information, see the entry for usrFdiskPartLibCbio( ) in the VxWorks Reference Manual.
In this step, the block device pointer pAta could have been passed instead of pCbioCache. Doing so would cause usrFdiskPartCreate( ) to use the BLK_DEV routines via a wrapper internally created by cbioWrapBlkDev( ).
Now, you can optionally display the partitions created with usrFdiskPartShow( ):
-> usrFdiskPartShow (pAta)
Master Boot Record - Partition Table -------------------------------------- Partition Entry number 00 Partition Entry offset 0x1be Status field = 0x80 Primary (bootable) Partition Type 0x06: MSDOS 16-bit FAT >=32M Partition Partition start LCHS: Cylinder 0000, Head 001, Sector 01 Partition end LCHS: Cylinder 0245, Head 017, Sector 39 Sectors offset from MBR partition 0x00000027 Number of sectors in partition 0x00262c17 Sectors offset from start of disk 0x00000027
Master Boot Record - Partition Table -------------------------------------- Partition Entry number 01 Partition Entry offset 0x1ce Status field = 0x00 Non-bootable Partition Type 0x06: MSDOS 16-bit FAT >=32M Partition Partition start LCHS: Cylinder 0000, Head 018, Sector 01 Partition end LCHS: Cylinder 0233, Head 067, Sector 39 Sectors offset from MBR partition 0x00262c3e Number of sectors in partition 0x00261d9e Sectors offset from start of disk 0x00262c3e
Master Boot Record - Partition Table -------------------------------------- Partition Entry number 02 Partition Entry offset 0x1de Status field = 0x00 Non-bootable Partition Type 0x00: Empty (NULL) Partition
Master Boot Record - Partition Table -------------------------------------- Partition Entry number 03 Partition Entry offset 0x1ee Status field = 0x00 Non-bootable Partition Type 0x00: Empty (NULL) Partition
value = 0 = 0x0 ->
Note above that two partitions have been created upon the disc, and the remaining two partition table entries are blank.
|
|
|||||||||||||||||||
Next, create a partition handler/mounter upon this disk. When using a disk cache layer, the partition handler code should always be instantiated after the disk cache layer:
-> pCbioParts = dpartDevCreate (pCbioCache,2, usrFdiskPartRead) new symbol "pCbioParts" added to symbol table. pCbioParts = 0x3ffd92c: value = 67099276 = 0x3ffda8c = pCbioParts + 0x160 ->
The call to dpartDevCreate( ) informs the partition layer to expect two partitions on the disk (24 is the maximum number of partitions the mounter can handle.) We have also instructed the partition manager (dpartCbio) to use the FDISK style partition mounter code usrFdiskPartRead( ).
For more information, see the reference entry for dpartDevCreate( ).
Create dosFs2 file systems atop each partition. The last argument specifies the integrated chkdsk configuration.
-> dosFsDevCreate ("/DOSA", dpartPartGet (pCbioParts,0), 16, 0)
value = 0 = 0x0
-> dosFsDevCreate ("/DOSB", dpartPartGet (pCbioParts,1), 16, -1)
value = 0 = 0x0
->
-> devs drv name 0 /null 1 /tyCo/0 1 /tyCo/1 6 ahostname: 3 /DOSA <---- First Partition 3 /DOSB <---- Second Partition value = 25 = 0x19 ->
This step defines the volume parameters and adds them to the IO system; it does not format the volumes. No disk I/O is performed during this step. The volumes are not mounted at this stage.
For more information, see the entry for dosFsLib in the VxWorks Reference Manual.
Next, format the DOS volumes. This step need only be done once, when the volumes are first initialized. If the DOS volumes have already been initialized (formatted), then omit this step. The example formats the file system volumes with default options:
-> dosFsVolFormat ("/DOSA",0,0)
Retrieved old volume params with %100 confidence:
Volume Parameters: FAT type: FAT16, sectors per cluster 32
2 FAT copies, 0 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId (null) , serial number 8120000
Label:" " ...
Disk with 2501655 sectors of 512 bytes will be formatted with:
Volume Parameters: FAT type: FAT16, sectors per cluster 64
2 FAT copies, 39082 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId VXDOS16 , serial number 8120000
Label:" " ...
value = 0 = 0x0
-> dosFsVolFormat ("/DOSB",0,0)
Retrieved old volume params with %100 confidence:
Volume Parameters: FAT type: FAT16, sectors per cluster 32
2 FAT copies, 0 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId (null) , serial number 9560000
Label:" " ...
Disk with 2497950 sectors of 512 bytes will be formatted with: Volume Parameters: FAT type: FAT16, sectors per cluster 64 2 FAT copies, 39024 clusters, 153 sectors per FAT Sectors reserved 1, hidden 39, FAT sectors 306 Root dir entries 512, sysId VXDOS16 , serial number 9560000 Label:" " ... value = 0 = 0x0 ->
For more information, see the entry for dosFsFmtLib in the VxWorks Reference Manual.
Now, the dosFs volumes are ready to access. Note that /DOSA will start a default chkdsk code and that /DOSB will not. Autochk is set via the fourth argument to dosFsDevCreate( ).
-> ll "/DOSA" /DOSA/ - disk check in progress ... /DOSA/ - Volume is OK
total # of clusters: 39,085 # of free clusters: 39,083 # of bad clusters: 0 total free space: 1,221 Mb max contiguous free space: 1,280,671,744 bytes # of files: 0 # of folders: 0 total bytes in files: 0 # of lost chains: 0 total bytes in lost chains: 0
Listing Directory /DOSA: value = 0 = 0x0
-> ll "/DOSB"
Listing Directory /DOSB: value = 0 = 0x0 ->
-> dosFsShow "/DOSB"
volume descriptor ptr (pVolDesc): 0x3f367c0 cache block I/O descriptor ptr (cbio): 0x3f37be0 auto disk check on mount: NOT ENABLED max # of simultaneously open files: 18 file descriptors in use: 0 # of different files in use: 0 # of descriptors for deleted files: 0 # of obsolete descriptors: 0
current volume configuration: - volume label: NO LABEL ; (in boot sector: ) - volume Id: 0x9560000 - total number of sectors: 2,497,950 - bytes per sector: 512 - # of sectors per cluster: 64 - # of reserved sectors: 1 - FAT entry size: FAT16 - # of sectors per FAT copy: 153 - # of FAT table copies: 2 - # of hidden sectors: 39 - first cluster is in sector # 339 - Update last access date for open-read-close = FALSE - directory structure: VFAT - root dir start sector: 307 - # of sectors per root: 32 - max # of entries in root: 512
FAT handler information: ------------------------ - allocation group size: 4 clusters - free space on volume: 1,278,771,200 bytes value = 0 = 0x0 ->
Above, we can see the Volume parameters for the /DOSB volume. The file system volumes are now mounted and ready to be exercised.
If you are working with an ATA hard disk or a CD-ROM file system from an ATAPI CD-ROM drive, you can, alternatively, use usrAtaConfig( ). This routine processes several steps at once. For more information, see the reference entry.
Example 5-4: Creating and Formatting a RAM Disk Volume
The following example creates a RAM disk of a certain size, and formats it for use with the dosFs file system. This example uses the ramDiskCbio module, which is intended for direct use with dosFsLib:
STATUS usrRamDiskInit
(
void /* no argument */
)
{
int ramDiskSize = 128 * 1024 ; /* 128KB, 128 bytes per sector */
char *ramDiskDevName = "/ram0" ;
CBIO_DEV_ID cbio ;
/* 128 bytes/sec, 17 secs/track, auto-allocate */
cbio = ramDiskDevCreate(NULL, 128, 17, ramDiskSize/128, 0) ;
if( cbio == NULL )
return ERROR ;
/* create the file system, 4 simultaneously open files, no CHKDSK */
dosFsDevCreate( ramDiskDevName, cbio, 4, NONE );
/* format the RAM disk, ignore memory contents */
dosFsVolFormat( cbio, DOS_OPT_BLANK | DOS_OPT_QUIET, NULL );
return OK;
}
Example 5-5: Initializing a SCSI Disk Drive
This example initializes a SCSI disk as a single file system volume (and assumes that the disk is already formatted).
STATUS usrScsiDiskInit
(
int scsiId /* SCSI id */
)
{
int dcacheSize = 128 * 1024 ; /* 128KB disk cache */
char *diskDevName = "/sd0" ; /* disk device name */
CBIO_DEV_ID cbio; /* pointer to a CBIO_DEV */
BLK_DEV *pBlk; /* pointer to a BLK_DEV */
SCSI_PHYS_DEV *pPhys ; /* pointer to a SCSI physical device */
/* Create the SCSI physical device */
if ((pPhys = scsiPhysDevCreate
(pSysScsiCtrl, scsiId, 0, 0, NONE, 0,0, 0))== NULL)
{
printErr ("usrScsiDiskInit: scsiPhysDevCreate SCSI ID %d failed.\n",
scsiId, 0, 0, 0, 0, 0);
return ERROR;
}
/* Create the block device */
if( (pblk = scsiBlkDevCreate(pPhys, 0, NONE )) == NULL )
return ERROR;
/*
* works for ids less than 10
* append SCSI id to make the device name unique
*/
diskDevName[strlen(diskDevName)-1] += scsiId ;
/* create disk cache */
if((cbio = dcacheDevCreate(pblk, NULL, dcacheSize, diskDevName))
== NULL )
return ERROR ;
/* create the file system, 10 simultaneously open files, with CHKDSK */
dosFsDevCreate( diskDevName, cbio, 10, 0 );
return OK;
}
This section discusses issues related to disks and volumes.
For more information about ioctl( ) support functions, see 5.2.16 I/O Control Functions Supported by dosFsLib.
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, a disk should be synchronized before it is removed. For more information, see the entries for close( ) and dosFsVolUnmount( ) in the VxWorks Reference Manual.
For FAT32, subdirectories can be created in any directory at any time. For FAT12 and FAT16, subdirectories can be created in any directory at any time, except in the root directory once it reaches its maximum entry count. Subdirectories can be created in the following ways:
You can programmatically search directories on dosFs volumes using the opendir( ), readdir( ), rewinddir( ), and closedir( ) routines.
To obtain more detailed information about a specific file, use the fstat( ) or stat( ) routine. 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 entry for dirLib in the VxWorks API Reference.
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( ). For more information, see 4.3 Basic I/O, and the ioLib entries in the VxWorks API Reference.
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 5-2.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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 5-6: Setting DosFs File Attributes
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);
return (OK);
}
|
|
|||||||||||||||||||
The dosFs file system allocates disk space using one of the following methods. The first two methods are selected based upon the size of the write operation. The last method must be manually specified.
Single cluster allocation uses a single cluster, which is the minimum allocation unit. This method is automatically used when the write operation is smaller than the size of a single cluster.
Cluster group allocation uses adjacent (contiguous) groups of clusters, called extents. Cluster group allocation is nearly contiguous allocation and is the default method used when files are written in units larger than the size of a disk's cluster.
Absolutely contiguous allocation uses only absolutely contiguous clusters. Because this type of allocation is dependent upon the existence of such space, it is specified under only two conditions: immediately after a new file is created and when reading from a file assumed to have been allocated to a contiguous space. Using this method risks disk fragmentation.
For any allocation method, you can deallocate unused reserved bytes by using the POSIX-compatible routine ftruncate( ) or the ioctl( ) function FIOTRUNC.
Under most circumstances, cluster group allocation is preferred to absolutely contiguous file access. Because it is nearly contiguous file access, it achieves a nearly optimal access speed. Cluster group allocation also significantly minimizes the risk of fragmentation posed by absolutely contiguous allocation.
Absolutely contiguous allocation attains raw disk throughput levels, however this speed is only slightly faster than nearly contiguous file access. Moreover, fragmentation is likely to occur over time. This is because after a disk has been in use for some period of time, it becomes impossible to allocate contiguous space. Thus, there is no guarantee that new data, appended to a file created or opened with absolutely continuous allocation, will be contiguous to the initially written data segment.
It is recommended that for a performance-sensitive operation, the application regulate disk space utilization, limiting it to 90% of the total disk space. Fragmentation is unavoidable when filling in the last free space on a disk, which has a serious impact on performance.
The dosFs file system defines the size of a cluster group based on the media's physical characteristics. That size is fixed for each particular media. Since seek operations are an overhead that reduces performance, it is desirable to arrange files so that sequential portions of a file are located in physically contiguous disk clusters. Cluster group allocation occurs when the cluster group size is considered sufficiently large so that the seek time is negligible compared to the read/write time. This technique is sometimes referred to as "nearly contiguous" file access because seek time between consecutive cluster groups is significantly reduced.
Because all large files on a volume are expected to have been written as a group of extents, removing them frees a number of extents to be used for new files subsequently created. Therefore, as long as free space is available for subsequent file storage, there are always extents available for use. Thus, cluster group allocation effectively prevents fragmentation (where a file is allocated in small units spread across distant locations on the disk). Access to fragmented files can be extremely slow, depending upon the degree of fragmentation.
A contiguous file is made up of a series of consecutive disk sectors. Absolutely contiguous allocation is intended to allocate contiguous space to a specified file (or directory) and, by so doing, optimize access to that file. You can specify absolutely contiguous allocation either when creating a file, or when opening a file previously created in this manner.
For more information on the ioctl( ) functions, see 5.2.16 I/O Control Functions Supported by dosFsLib.
The FAT is then searched for a suitable section of the disk. If found, this space is assigned to the new file. The file can then be closed, or it can be used for further I/O operations. The file descriptor used for calling ioctl( ) should be the only descriptor open to the file. Always perform the ioctl( ) FIOCONTIG operation before writing any data to the file.
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);
A directory must be empty (except for the "." and ".." entries) when it has contiguous space allocated to it.
Fragmented files require following cluster chains in the FAT. However, if a file is recognized as contiguous, the system can use an enhanced method that improves performance. This applies to all contiguous files, whether or not they were explicitly created using FIOCONTIG. Whenever a file is opened, it is checked for contiguity. If it is found to be contiguous, the file system registers the necessary information about that file to avoid the need for subsequent access to the FAT table. This enhances performance when working with the file by eliminating seek operations.
When you are opening a contiguous file, you can explicitly indicate that the file is contiguous by specifying the DOS_O_CONTIG_CHK flag with open( ). This prompts the file system to retrieve the section of contiguous space, allocated for this file, from the FAT table.
Example 5-7: 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).
STATUS contigTest
(
void /* no argument */
)
{
int count; /* size of maximum contiguous area in bytes */
int fd; /* file descriptor */
/* 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);
return (OK);
}
|
|
|||||||||||||||||||
The inconsistencies occur because the file system data for a single file is stored in three separate regions of the disk. The data stored in these regions are:
Since all three regions cannot be always updated before an interruption, dosFs includes an optional integrated consistency-checking mechanism to detect and recover from inconsistencies. For example, if a disk is removed when a file is being deleted, a consistency check completes the file deletion operation. Or, if a file is being created when an interruption occurs, then the file is un-created. In other words, the consistency checker either rolls forward or rolls back the operation that has the inconsistency, making whichever correction is possible.
To use consistency checking, specify the autoChkLevel parameter to dosFsDevCreate( ), invoke it manually, or call the chkdsk( ) utility. If configured, consistency checking is invoked under the following conditions:
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
VxWorks can be booted from a local SCSI device. Before you can boot from SCSI, you must make a new boot application ROM that contains the SCSI library. Include INCLUDE_SCSI, INCLUDE_SCSI_BOOT, and SYS_SCSI_CONFIG in your boot application project.
After burning the SCSI boot application ROM, 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 system image, place it on your SCSI boot device, and boot the new VxWorks system. These steps are shown in more detail below.
Create the SCSI device using scsiPhysDevCreate( ) (see SCSI Drivers), and initialize the disk with a dosFs file system (see 5.2.3 Initializing the dosFs File System). Modify the file installDir/target/bspName/sysScsi.c to reflect your SCSI configuration.
Copy the file vxWorks to the drive. Below, a VxWorks task spawns the copy( ) routine, passing it two arguments.
The first argument is the source file for the copy( ) command. The source file is the VxWorks runtime image, vxWorks. The source host name is tiamat:, the source filename is C:/vxWorks. These are passed to copy( ) in concatenated form, as the string "tiamat:C:/vxWorks."
The second argument is the destination file for the copy( ) command. The dosFs file system, on the local target SCSI disk device, is named /sd0, and the target file name is vxWorks. These are, similarly, passed to copy( ) in concatenated form, as the string "/sd0/vxWorks." When booting the target from the SCSI device, the bootrom image should specify the runtime file as "/sd0/vxWorks".
-> sp (copy, "tiamat:c:/vxWorks","/sd0/vxWorks") task spawned: id = 0x3f2a200, name = t2 value = 66232832 = 0x3f2a200
Copy OK: 1065570 bytes copied
Depending upon image configuration, the vxWorks.sym file for the system symbol table may also be needed. Therefore, in similar fashion, copy the vxWorks.sym file. The runtime image, vxWorks, downloads the vxWorks.sym file from the same location.
-> sp (copy, "tiamat:c:/vxWorks.sym","/sd0/vxWorks.sym") task spawned: id = 0x3f2a1bc, name = t3 value = 66232764 = 0x3f2a1bc
Copy OK: 147698 bytes copied
Now, list the files to ensure that the files were correctly copied.
-> sp (ll, "/sd0") task spawned: id = 0x3f2a1a8, name = t4 value = 66232744 = 0x3f2a1a8 ->
Listing Directory /sd0: -rwxrwxrwx 1 0 0 1065570 Oct 26 2001 vxWorks -rwxrwxrwx 1 0 0 147698 Oct 26 2001 vxWorks.sym
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.
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
VxWorks provides a minimal "file system," rawFs, for use in systems that require only the most basic disk I/O functions. The rawFs file system, implemented in rawFsLib, treats the entire disk volume much like a single large file.
Although the dosFs file system provides this ability to varying degrees, the rawFs file system offers advantages in size and performance if more complex functions are not required.
To use the rawFs file system in a VxWorks-based system, include the INCLUDE_RAWFS component in the kernel, and set the NUM_RAWFS_FILES parameter to the desired maximum open file descriptor count.
The rawFs file system imposes no organization of the data on the disk. It maintains no directory information; thus there is no division of the disk area into specific files. All open( ) operations on rawFs devices specify only the device name; no additional filenames are possible.
The entire disk area is treated as a single file and is available to any file descriptor that is open for the device. All read and write operations to the disk use a byte-offset relative to the start of the first block on the disk.
Before any other operations can be performed, the rawFs library, rawFsLib, must be initialized by calling rawFsInit( ). This routine takes a single parameter, the maximum number of rawFs file descriptors that can be open at one time. This count is used to allocate a set of descriptors; a descriptor is used each time a rawFs device is opened.
The rawFsInit( ) routine also makes an entry for the rawFs file system in the I/O system driver table (with iosDrvInstall( )). This entry specifies the entry points for rawFs file operations, for all devices that use the rawFs file system. The driver number assigned to the rawFs file system is placed in a global variable, rawFsDrvNum.
The rawFsInit( ) routine is normally called by the usrRoot( ) task after starting the VxWorks system.
After the rawFs 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 in the device driver that a file system can call. For more information on block devices, see 4.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 rawFs, the already-created block device must be associated with rawFs and a name must be assigned to it. This is done with the rawFsDevInit( ) routine. Its parameters are the name to be used to identify the device and a pointer to the block device descriptor structure (BLK_DEV):
RAW_VOL_DESC *pVolDesc;
BLK_DEV *pBlkDev;
pVolDesc = rawFsDevInit ("DEV1:", pBlkDev);
The rawFsDevInit( ) call assigns the specified name to the device and enters the device in the I/O system device table (with iosDevAdd( )). It also allocates and initializes the file system's volume descriptor for the device. It returns a pointer to the volume descriptor to the caller; this pointer is used to identify the volume during certain file system calls.
Note that initializing the device for use with rawFs does not format the disk. That is done using an ioctl( ) call with the FIODISKFORMAT function.
|
|
|||||||||||||||||||
A disk volume is mounted automatically, generally during the first open( ) or creat( ) operation. (Certain ioctl( ) functions also cause the disk to be mounted.) The volume is again mounted automatically on the first disk access following a ready-change operation (see 5.4.6 Changing Disks).
|
|
|||||||||||||||||||
To begin I/O operations upon a rawFs device, first open the device using the standard open( ) function. (The creat( ) function can be used instead, although nothing is actually "created.") Data on the rawFs device is written and read using the standard I/O routines write( ) and read( ). For more information, see 4.3 Basic I/O.
The character pointer associated with a file descriptor (that is, the byte offset where the read and write operations take place) can be set by using ioctl( ) with the FIOSEEK function.
Multiple file descriptors can be open simultaneously for a single device. These must be carefully managed to avoid modifying data that is also being used by another file descriptor. In most cases, such multiple open descriptors use FIOSEEK to set their character pointers to separate disk areas.
The first method of announcing a disk change is to call rawFsVolUnmount( ) prior to removing the disk. This call flushes all modified file descriptor buffers if possible (see Synchronizing Volumes) and also marks any open file descriptors as obsolete. The next I/O operation remounts the disk. Calling ioctl( ) with FIOUNMOUNT is equivalent to using rawFsVolUnmount( ). Any open file descriptor to the device can be used in the ioctl( ) call.
Attempts to use obsolete file descriptors for further I/O operations produce an S_rawFsLib_FD_OBSOLETE error. To free an obsolete descriptor, use close( ), as usual. This frees the descriptor even though it produces the same error.
ISRs must not call rawFsVolUnmount( ) directly, because the call can pend while the device becomes available. The ISR can instead give a semaphore that prompts a task to un-mount the volume. (Note that rawFsReadyChange( ) can be called directly from ISRs; see Announcing Disk Changes with Ready-Change.)
When rawFsVolUnmount( ) 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 rawFsReadyChange( ), as described in Announcing Disk Changes with Ready-Change.
If rawFsVolUnmount( ) 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. An error is not returned by rawFsVolUnmount( ); to avoid lost data in this situation, explicitly synchronize the disk before removing it (see Synchronizing Volumes).
The second method of announcing that a disk change is taking place is with the ready-change mechanism. A change in the disk's ready-status is interpreted by rawFsLib to indicate that the disk must be remounted during the next I/O call.
The ready-change announcement does not cause buffered data to be flushed to the disk. It merely marks the volume as needing remounting. As a result, data written to files can be lost. This can be avoided by synchronizing the disk before asserting ready-change. The combination of synchronizing and asserting ready-change provides all the functionality of rawFsVolUnmount( ) except for marking file descriptors as obsolete.
Ready-change can be called from an ISR, 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) is useful for asserting ready-change for devices that only detect a disk change after the new disk is inserted. This routine is called at the beginning of each open( ) or creat( ), before the file system checks for ready-change.
If it is not possible for a ready-change to be announced each time the disk is changed, close all file descriptors for the volume before changing the disk.
When a disk is synchronized, all buffered data that is modified is written to the physical device so that the disk is up to date. For the rawFs file system, the only such data is that contained in open file descriptor buffers.
To avoid loss of data, synchronize a disk before removing it. You may need to explicitly synchronize a disk, depending on when (or if) the rawFsVolUnmount( ) call is issued.
When rawFsVolUnmount( ) is called, an attempt is made to synchronize the device before un-mounting. If this disk is still present and writable at the time of the call, synchronization takes place automatically; there is no need to synchronize the disk explicitly.
However, if the rawFsVolUnmount( ) call is made after a disk is removed, it is obviously too late to synchronize, and rawFsVolUnmount( ) discards the buffered data. Therefore, make a separate ioctl( ) call with the FIOSYNC function before removing the disk. (For example, this could be done in response to an operator command.) Any open file descriptor to the device can be used during the ioctl( ) call. This call writes all modified file descriptor buffers for the device out to the disk.
The rawFs file system supports the ioctl( ) functions shown in Table 5-4. The functions listed are defined in the header file ioLib.h. For more information, see the manual entries for rawFsLib and for ioctl( ) in ioLib.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
The tapeFs library, tapeFsLib, provides basic services for tape devices that do not use a standard file or directory structure on tape. The tape volume is treated much like a raw device where the entire volume is a large file. Any data organization on this large file is the responsibility of a higher-level layer.
To configure VxWorks with tapeFs, include the INCLUDE_TAPEFS component in the kernel.
|
|
NOTE:
The tape file system must be configured with SCSI-2 enabled. See SCSI Drivers for configuration details.
|
||||||||||||||||||
The tapeFs file system imposes no organization of the data on the tape volume. It maintains no directory information; there is no division of the tape area into specific files; and no filenames are used. An open( ) operation on the tapeFs device specifies only the device name; no additional filenames are allowed.
The entire tape area is available to any file descriptor open for the device. All read and write operations to the tape use a location offset relative to the current location of the tape head. When a file is configured as a rewind device and first opened, tape operations begin at the beginning-of-medium (BOM); see Initializing a Device for Use With tapeFs. Thereafter, all operations occur relative to where the tape head is located at that instant of time. No location information, as such, is maintained by tapeFs.
The tapeFs file system must be initialized, and a tape device created, in order to make the physical tape device available to the tape file system. At this point, normal I/O system operations can be performed.
The tapeFs library, tapeFsLib, is initialized by calling tapeFsInit( ). Each tape file system can handle multiple tape devices. However, each tape device is allowed only one file descriptor. Thus, you cannot open two files on the same tape device.
The tapeFsInit( ) routine also makes an entry for the tapeFs file system in the I/O system driver table (with iosDrvInstall( )). This entry specifies function pointers to carry out tapeFs file operations on devices that use the tapeFs file system. The driver number assigned to the tapeFs file system is placed in a global variable, tapeFsDrvNum.
When initializing a tape device, tapeFsInit( ) is called automatically if tapeFsDevInit( ) is called; thus, the tape file system does not require explicit initialization.
Once the tapeFs file system has been initialized, the next step is to create one or more devices that can be used with it. This is done using the sequential device creation routine, scsiSeqDevCreate( ). The driver routine returns a pointer to a sequential device descriptor structure, SEQ_DEV. The SEQ_DEV structure describes the physical aspects of the device and specifies the routines in the device driver that tapeFs can call. For more information on sequential devices, see the manual entry for scsiSeqDevCreate( ), Configuring SCSI Drivers and 4.9.4 Block Devices.
Immediately after its creation, the sequential device has neither a name nor a file system associated with it. To initialize a sequential device for use with tapeFs, call tapeFsDevInit( ) to assign a name and declare a file system. Its parameters are the volume name--for identifying the device; a pointer to SEQ_DEV--the sequential device descriptor structure; and a pointer to an initialized tape configuration structure TAPE_CONFIG. This structure has the following form:
typedef struct /* TAPE_CONFIG tape device config structure */
{
int blkSize; /* block size; 0 => var. block size */
BOOL rewind; /* TRUE => a rewind device; FALSE => no rewind */
int numFileMarks; /* not used */
int density; /* not used */
} TAPE_CONFIG;
In the preceding definition of TAPE_CONFIG, only two fields, blkSize and rewind, are currently in use. If rewind is TRUE, then a tape device is rewound to the beginning-of-medium (BOM) upon closing a file with close( ). However, if rewind is FALSE, then closing a file has no effect on the position of the read/write head on the tape medium.
The blkSize field specifies the block size of the physical tape device. Having set the block size, each read or write operation has a transfer unit of blkSize. Tape devices can perform fixed or variable block transfers, a distinction also captured in the blkSize field.
For more information on initializing a tapeFs device, see the VxWorks API Reference entry for tapeFsDevInit( ).
A tape file system can be created for fixed block size transfers or variable block size transfers, depending on the capabilities of the underlying physical device. The type of data transfer (fixed block or variable block) is usually decided when the tape device is being created in the file system, that is, before the call to tapeFsDevInit( ). A block size of zero represents variable block size data transfers.
Once the block size has been set for a particular tape device, it is usually not modified. To modify the block size, use the ioctl( ) functions FIOBLKSIZESET and FIOBLKSIZEGET to set and get the block size on the physical device.
Note that for fixed block transfers, the tape file system buffers a block of data. If the block size of the physical device is changed after a file is opened, the file should first be closed and then re-opened in order for the new block size to take effect.
Example 5-8: Tape Device Configuration
There are many ways to configure a tape device. In this code example, a tape device is configured with a block size of 512 bytes and the option to rewind the device at the end of operations.
/* global variables assigned elsewhere */
SCSI_PHYS_DEV * pScsiPhysDev;
/* local variable declarations */
TAPE_VOL_DESC * pTapeVol;
SEQ_DEV * pSeqDev;
TAPE_CONFIG tapeConfig;
/* initialization code */
tapeConfig.blkSize = 512;
tapeConfig.rewind = TRUE;
pSeqDev = scsiSeqDevCreate (pScsiPhysDev);
pTapeVol = tapeFsDevInit ("/tape1", pSeqDev, tapeConfig);
The tapeFsDevInit( ) call assigns the specified name to the device and enters the device in the I/O system device table (with iosDevAdd( )). The return value of this routine is a pointer to a volume descriptor structure that contains volume-specific configuration and state information.
|
|
|||||||||||||||||||
To begin I/O to a tapeFs device, the device is first opened using open( ). Data on the tapeFs device is written and read using the standard I/O routines write( ) and read( ). For more information, see 4.7.7 Block Devices.
End-of-file markers can be written using ioctl( ) with the MTWEOF function. For more information, see 5.5.6 I/O Control Functions Supported by tapeFsLib.
The tapeFs file system should be notified when removable media are changed (for example, when tapes are swapped). The tapeFsVolUnmount( ) routine controls the mechanism to un-mount a tape volume.
A tape should be un-mounted before it is removed. Prior to un-mounting a tape volume, an open file descriptor must be closed. Closing an open file flushes any buffered data to the tape, thus synchronizing the file system with the data on the tape. To flush or synchronize data before closing the file, call ioctl( ) with the FIOFLUSH or FIOSYNC functions.
After closing any open file, call tapeFsVolUnmount( ) before removing the tape. Once a tape has been un-mounted, the next I/O operation must remount the tape using open( ).
Interrupt handlers must not call tapeFsVolUnmount( ) directly, because it is possible for the call to pend while the device becomes available. The interrupt handler can instead give a semaphore that prompts a task to un-mount the volume.
The tapeFs file system supports the ioctl( ) functions shown in Table 5-5. The functions listed are defined in the header files ioLib.h, seqIo.h, and tapeFsLib.h. For more information, see the VxWorks API Reference entries for tapeFsLib, ioLib, and ioctl( ).
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
Performs a UNIX-like MTIO operation to the tape device. The type of operation and operation count is set in an MTIO structure passed to the ioctl( ) routine. The MTIO operations are defined in Table 5-6.
|
|||||||||||||||||||
|
|
|||||||||||||||||||
The MTIOCTOP operation is compatible with the UNIX MTIOCTOP operation. The argument passed to ioctl( ) with MTIOCTOP is a pointer to an MTOP structure that contains the following two fields:
typedef struct mtop
{
short mt_op; /* operation */
int mt_count; /* number of operations */
} MTOP;
The mt_op field contains the type of MTIOCTOP operation to perform. These operations are defined in Table 5-6. The mt_count field contains the number of times the operation defined in mt_op should be performed.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
The cdromFs library, cdromFsLib, lets applications read any CD-ROM that is formatted in accordance with ISO 9660 file system standards. To configure VxWorks with cdromFs, add the INCLUDE_CDROMFS component to the kernel.
After initializing cdromFs and mounting it on a CD-ROM block device, you can access data on that device using the standard POSIX I/O calls: open( ), close( ), read( ), ioctl( ), readdir( ), and stat( ). The write( ) call always returns an error.
The cdromFs utility supports multiple drives, multiple open files, and concurrent file access. When you specify a pathname, cdromFS accepts both "/" and "\". However, the backslash is not recommended because it might not be supported in future releases.
CdromFs provides access to CD-ROM file systems using any standard BLK_DEV structure. The basic initialization sequence is similar to installing a dosFs file system on a SCSI device.
For information on using cdromFs( ), see the VxWorks API Reference entry for cdromFsLib.
Add the component INCLUDE_CDROMFS in your project. Add other required components (like SCSI/ATA depending on the type of device). For more information, see 5.2.2 Configuring Your System.
If you are using an ATAPI device, make appropriate modifications to the ataDrv, ataResources[ ] structure array (if needed). This must be configured appropriately for your hardware platform.
Based on the type of device, use the appropriate create routine and create a block device. Following is an example for an ATAPI master device upon the secondary ATA controller:
-> pBlkd = ataDevCreate(1, 0, 0, 0) new symbol "pBlkd" added to symbol table. pBlkd = 0x3fff334: value = 67105604 = 0x3fff344 = pBlkd + 0x10
A block device must already have been created. Call cdromFsDevCreate( ), which calls iosDrvInstall( ) internally. This enters the appropriate driver routines in the I/O driver table.
-> cdromFsDevCreate("/cd", pBlkd)
value = 67105456 = 0x3fff2b0
-> devs drv name 0 /null 1 /tyCo/0 1 /tyCo/1 5 ala-petrient: 6 /vio 7 /cd value = 25 = 0x19
-> cd "/cd" value = 0 = 0x0
The cd command changes the current working directory, without performing I/O, and, therefore, can be called before the media is mounted.
Now, run cdromFsVolConfigShow( ) to indicate whether the device (created above) is mounted. Executing this command shows that the device is not mounted.
-> cdromFsVolConfigShow "/cd"
device config structure ptr 0x3fff2b0 device name /cd bytes per blkDevDrv sector 2048 no volume mounted value = 18 = 0x12
Mount the device in order to access it. Because cdromFs is mounted during the first open( ) operation, a call to open( ) or any function that uses open( ) will mount the device. The ls command below both mounts the device and lists its contents.
-> ls "/cd" /cd/. /cd/.. /cd/INDEX.HTML;1 /cd/INSTRUCT.HTML;1 /cd/MPF /cd/README.TXT;1 value = 0 = 0x0
You can check the CD-ROM configuration using cdromFsVolConfigShow( ):
-> cdromFsVolConfigShow "/cd"
device config structure ptr 0x3fff2b0 device name /cd bytes per blkDevDrv sector 2048
Primary directory hierarchy:
standard ID :CD001 volume descriptor version :1 system ID :LINUX volume ID :MPF_CD volume size :611622912 = 583 MB number of logical blocks :298644 = 0x48e94 volume set size :1 volume sequence number :1 logical block size :2048 path table size (bytes) :3476 path table entries :238 volume set ID :
volume publisher ID :WorldWide Technolgies
volume data preparer ID :Kelly Corday
volume application ID :mkisofs v1.04
copyright file name :mkisofs v1.04 abstract file name :mkisofs v1.04 bibliographic file name :mkisofs v1.04 creation date :13.11.1998 14:36:49:00 modification date :13.11.1998 14:36:49:00 expiration date :00.00.0000 00:00:00:00 effective date :13.11.1998 14:36:49:00 value = 0 = 0x0
The Target Server File System (TSFS) is designed for development and diagnostic purposes. It is a full-featured VxWorks file system, but the files are actually located on the host system.
The TSFS provides all of the I/O features of the network driver for remote file access (netDrv), without requiring any target resources (except those required for communication between the target system and the target server on the host). The TSFS uses a WDB driver to transfer requests from the VxWorks I/O system to the target server. The target server reads the request and executes it using the host file system. When you open a file with TSFS, the file being opened is actually on the host. Subsequent read( ) and write( ) calls on the file descriptor obtained from the open( ) call read from and write to the opened host file.
The TSFS VIO driver is oriented toward file I/O rather than toward console operations as is the Tornado 1.0 VIO driver. TSFS provides all the I/O features that netDrv provides, without requiring any target resource beyond what is already configured to support communication between target and target server. It is possible to access host files randomly without copying the entire file to the target, to load an object module from a virtual file source, and to supply the filename to routines such as moduleLoad( ) and copy( ).
Each I/O request, including open( ), is synchronous; the calling target task is blocked until the operation is complete. This provides flow control not available in the console VIO implementation. In addition, there is no need for WTX protocol requests to be issued to associate the VIO channel with a particular host file; the information is contained in the name of the file.
Consider a read( ) call. The driver transmits the ID of the file (previously established by an open( ) call), the address of the buffer to receive the file data, and the desired length of the read to the target server. The target server responds by issuing the equivalent read( ) call on the host and transfers the data read to the target program. The return value of read( ) and any errno that might arise are also relayed to the target, so that the file appears to be local in every way.
For detailed information, see the VxWorks API Reference entry for wdbTsfsDrv.
TSFS sockets are operated on in a similar way to other TSFS files, using open( ), close( ), read( ), write( ), and ioctl( ). To open a TSFS socket, use one of the following forms of filename:
"TCP:hostIP:port" "TCP:hostname:port"
The flags and permissions arguments are ignored. The following examples show how to use these filenames:
fd = open("/tgtsvr/TCP:phobos:6164"0,0) /* open socket and connect */
/* to server phobos */
fd = open("/tgtsvr/TCP:150.50.50.50:6164",0,0) /* open socket and */
/*connect to server */
/* 150.50.50.50 */
The result of this open( ) call is to open a TCP socket on the host and connect it to the target server socket at hostname or hostIP awaiting connections on port. The resultant socket is non-blocking. Use read( ) and write( ) to read and write to the TSFS socket. Because the socket is non-blocking, the read( ) call returns immediately with an error and the appropriate errno if there is no data available to read from the socket. The ioctl( ) usage specific to TSFS sockets is discussed in the VxWorks API Reference entry for wdbTsfsDrv. This socket configuration allows VxWorks to use the socket facility without requiring sockLib and the networking modules on the target.
Errors can arise at various points within TSFS and are reported back to the original caller on the target, along with an appropriate error code. The error code returned is the VxWorks errno which most closely matches the error experienced on the host. If a WDB error is encountered, a WDB error message is returned rather than a VxWorks errno.
To use the TSFS, your VxWorks-based system must be configured with the INCLUDE_WDB_TSFS component in the kernel. This creates the /tgtsvr file system.
The target server on the host system must also be configured for TSFS. This involves assigning a root directory on your host to TSFS (see the discussion of the target server -R option in Security Considerations). For example, on a PC host you could set the TSFS root to c:\myTarget\logs.
Having done so, opening the file /tgtsvr/logFoo on the target causes c:\myTarget\logs\logFoo to be opened on the host by the target server. A new file descriptor representing that file is returned to the caller on the target.
While TSFS has much in common with netDrv, the security considerations are different. With TSFS, the host file operations are done on behalf of the user that launched the target server. The user name given to the target as a boot parameter has no effect. In fact, none of the boot parameters have any effect on the access privileges of TSFS.
In this environment, it is less clear to the user what the privilege restrictions to TSFS actually are, since the user ID and host machine that start the target server may vary from invocation to invocation. By default, any Tornado tool that connects to a target server which is supporting TSFS has access to any file with the same authorizations as the user that started that target server. However, the target server can be locked (with the -L option) to restrict access to the TSFS.
The options which have been added to the target server startup routine to control target access to host files using TSFS include:
|
|
|||||||||||||||||||
1: The MSFT Long Names (VFAT) format supports 32-bit file size fields, limiting the file size to a 4 GB maximum.