dosFsLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:173k
- /* dosFsLib.c - MS-DOS media-compatible file system library */
- /* Copyright 1984-2002 Wind River Systems, Inc. */
- #include "copyright_wrs.h"
- /*
- modification history
- --------------------
- 02q,02may02,jkf Corrects SPR#76501, 72603. Avoids 65085 and 33221.
- and a performance improvement for FIOSYNC.
- 02p,30apr02,jkf SPR#62786, rename should preserve time and date fields
- 02o,30apr02,jkf SPR#76510, dosFsVolDescGet() should return NULL instead of
- the default device when the underlying iosDevFind() does.
- 02n,15Jan02,chn SPR#29751, removed path case sensitivity during creat phase
- of rename. Possible NFS interaction, see comments at change.
- 02m,18dec01,chn SPR#71105, correct file size in dosFsContigAlloc & comment
- 02l,12dec01,jkf SPR#72133, add FIOMOVE and fixing diab build warnings.
- 02k,10dec01,jkf SPR#24332, dont delete existing files or dirs in dosFsRename
- 02j,10dec01,jkf SPR#72039, various fixes from Mr. T. Johnson.
- 02i,09dec01,jkf SPR#71637, fix for SPR#68387 caused ready changed bugs.
- 02h,30nov01,jkf SPR#68203, updating last access date on open-read-close
- operation causes unwanted writes, increasing flash wear.
- 02g,30nov01,jkf SPR#33343, media unformatted or missing err better reported.
- SPR#69074, statfs to invalid media caused continuous errors.
- 02f,15nov01,jkf SPR#71720, avoid unaligned pointer access.
- clean up multiple errno's when mounting unformatted volume.
- 02e,13nov01,jkf correct typo in checkin comment.
- 02d,13nov01,jkf improve dosFsBootSecGet() error message, add a comment about
- nBlocks reporting total blocks, not last addressable block.
- 02c,10nov01,jkf SPR#67890, chkdsk writes to RDONLY device
- 02b,09nov01,jkf SPR#71633, dont set errno when DevCreate is called w/BLK_DEV
- SPR#32178, made dosFsVolDescGet public, cleaned up man page.
- SPR#33684, dosFsShow reporting incorrect verbosity setting.
- 02a,23oct01,rip SPR#65085: tasks in FIOSYNC interlocking (dup of #33221 cf 01v)
- SPR#30464: FIONCONTIG64 not shifting using 64bit math
- SPR#30540: FillGap assumes step over sector on FIOSEEK > EOF
- 01z,21aug01,jkf SPR#69031, common code for both AE & 5.x.
- 01y,26jul01,jkf T2 SPR#65009, T3 SPR#65271. dosFsLibInit returns OK or ERROR.
- 01x,14mar01,jkf SPR#34704,FAT12/FAT16 determination, SPR#62415 sig location.
- 01w,19sep00,csh Manual Merge From Alameda Branch To Sunnyvale Branch
- 01v,08sep00,nrj fixed SPR#33221, to avoid dead-lock because of multiple tasks
- doing FIOSYNC on opened files
- fixed SPR#33702, 33684, The autocheck verbosity is now
- initialized properly in volume descriptor.
- 01u,29feb00,jkf cleaning warning.
- 01t,29feb00,jkf T3 changes.
- 01s,28sep99,jkf fixed SPR#28554, now return error on write to full disk.
- 01r,31aug99,jkf changes for new CBIO API. Changed FIOTIMESET to allow
- utime to follow Solaris 2.6 behavior. If arg is NULL,
- the current time is used, added docs. SPR#28924
- 01q,06aug99,jkf delete existing file when dosFsOpen uses O_CREAT/O_TRUNC
- do not overflow read buffer on truncated files, SPR#28309
- 01p,31jul99,jkf Dont allow larger than 4GB file on anything but
- VXLONGNAMES directory entry files. SPR#27532.
- 01o,31jul99,jkf Added support for FSTYPE (0x36) in bootsec, SPR#28273
- 01n,31jul99,jkf T2 merge, tidiness & spelling.
- 01m,03dec98,vld fixed SPR #23692: added FIOTRUNC64 ioctl code;
- rd/wr time measurement excluded
- 01l,22nov98,vld included features required by NFS protocol:
- - added support for FIOFSTATFSGET ioctl code;
- - added support for creating files "with holes";
- - added dosFsSeekDir() routine and controlling
- of dd_cookie field within FIOREADDIR
- 01k,28sep98,vld gnu extensions dropped from DBG_MSG/ERR_MSG macros
- 01j,24sep98,vld added support for FIOTIMESET ioctl code
- 01i,16sep98,vld created separate routine dosFsChkDsk() to solve
- hen-and-egg problem during volume mounting and
- external call for disk check operation.
- 01h,16sep98,vld added support for read only devices
- 01j,11sep98,vld added support for non CBIO ptr argument in dosFsDevCreate().
- 01i,26aug98,vld ignore mode = S_IFDIR except with O_CREAT (SPR#22227)
- 01h,30jul98,wlf partial doc cleanup
- 01g,27jul98,vld fixed FIOWHERE64 return
- 01f,08jul98,vld print64Lib.h moved to h/private directory.
- 01e,08jul98,vld dosFsContigAlloc() (FIOCONTIG effected) changed
- not to zero allocated data and leave file size as 0
- 01d,08jul98,vld fixed bug in dosFsContigAlloc()
- added counting sectors per file count for CONTIG_MAX case.
- 01c,30jun98,lrn renamed dosFsInit to dosFsLibInit
- 01b,28jun98,vld tested, checked in, ready for EAR
- 01a,18jan98,vld written, preliminary
- */
- /*
- INTERNAL: MS-DOS is a registered trademark of Microsoft Corporation.
- DESCRIPTION
- This library implements the MS-DOS compatible file system.
- This is a multi-module library, which depends on sub-modules to
- perform certain parts of the file system functionality.
- A number of different file system format variations are supported.
- USING THIS LIBRARY
- The various routines provided by the VxWorks DOS file system (dosFs) may be
- separated into three broad groups: general initialization, device
- initialization, and file system operation.
- The dosFsLibInit() routine is the principal initialization function; it should
- be called once during system initialization, regardless of how many dosFs
- devices are to be used.
- Another dosFs routine is used for device initialization.
- For each dosFs device, dosFsDevCreate() must be called to install the
- device in VxWorks device list.
- In the case where partitioned disks are used, dosFsDevCreate() must be
- called for each partition that is anticipated, thereby it is associated
- with a logical device name, so it can be later accessed via the I/O
- system.
- In case of a removable disk, dosFsDevCreate() must be called during
- system initialization time, even if a cartridge or diskette may be
- absent from the drive at boot time. dosFsDevCreate() will only
- associate the device with a logical device name. Device access will be
- done only when the logical device is first accessed by the application.
- More detailed information on all of these routines is provided below.
- INITIALIZING DOSFSLIB
- To enable this file system in a particular VxWorks configuration,
- a library initialization routine must be called for each sub-module of
- the file system, as well as for the underlying disk cache, partition
- manager and drivers.
- This is usually done at system initialization time, within the
- .I usrRoot
- task context.
- Following is the list of initialization routines that need to be
- called:
- .IP dosFsLibInit
- (mandatory) initialize the principle dosFs module. Must be called first.
- .IP dosFsFatInit
- (mandatory) initialize the File Allocation Table handler, which supports
- 12-bit, 16-bit and 32-bit FATs.
- .IP dosVDirLibInit
- (choice) install the variable size directory handler
- supporting Windows-compatible Long File Names (VFAT) Directory
- Handler.
- .IP dosDirOldLibInit
- (choice) install the fixed size directory handler
- which supports old-fashioned 8.3
- MS-DOS file names, and Wind River Systems proprietary long file names
- (VXLONG).
- .IP dosFsFmtLibInit
- (optional) install the volume formatting module.
- .IP dosChkLibInit
- (optional) install the file system consistency checking module.
- .LP
- The two Directory handlers which are marked
- .I choice
- are installed in accordance with the system requirements, either one
- of these modules could be installed or both, in which case the VFAT will
- take precedence for MS-DOS compatible volumes.
- Also, at least one
- .I CBIO
- module must be initialized on a per-device basis prior to calling
- dosFsDevCreate().
- See the related documentation for more details and examples.
- DEFINING A DOSFS DEVICE
- The dosFsDevCreate() routine associates a device with the dosFsLib
- functions. It expects three parameters:
- .IP "(1)" 4
- A pointer to a name string, to be used to identify the device - logical
- device name.
- This will be part of the pathname for I/O operations which operate on the
- device. This name will appear in the I/O system device table, which may be
- displayed using the iosDevShow() routine.
- .IP "(2)"
- CBIO_DEV_ID - a pointer to the CBIO_DEV structure which provides interface
- to particular disk, via a disk cache, or a partition manager or a
- combination of a number of
- .I CBIO
- modules which are stacked on top of each other to form one of many
- configurations possible.
- .IP "(3)"
- A maximum number of files can be simultaneously opened on a particular device.
- .IP "(4)"
- Because volume integrity check utility can be automatically
- invoked every time a device is mounted,
- this parameter indicates whether the consistency check needs to be
- performed automatically on a given device, and on what level of
- verbosity is required.
- In any event, the consistency check may be invoked at a later time
- e.g. by calling chkdsk().
- See description for FIOCHKDSK ioctl command for more information.
- .LP
- For example:
- .CS
- dosFsDevCreate
- (
- "/sd0", /@ name to be used for volume @/
- pCbio, /@ pointer to device descriptor @/
- 10, /@ max no. of simultaneously open files @/
- DOS_CHK_REPAIR | DOS_CHK_VERB_1
- /@ check volume during mounting and repair @/
- /@ errors, and display volume statistics @/
- )
- .CE
- Once dosFsDevCreate() has been called, the device can be accessed
- using
- .I ioLib
- generic I/O routines: open(), read(), write(), close(),
- ioctl(), remove(). Also, the user-level utility functions may be used to
- access the device at a higher level (See usrFsLib reference page for
- more details).
- DEVICE AND PATH NAMES
- On true MS-DOS machines, disk device names are typically of the form "A:",
- that is, a single letter designator followed by a colon. Such names may be
- used with the VxWorks dosFs file system. However, it is possible (and
- desirable) to use longer, more mnemonic device names, such as "DOS1:",
- or "/floppy0".
- The name is specified during the dosFsDevCreate() call.
- The pathnames used to specify dosFs files and directories may use either
- forward slashes ("/") or backslashes ("e") as separators. These may be
- freely mixed. The choice of forward slashes or backslashes has absolutely
- no effect on the directory data written to the disk. (Note, however, that
- forward slashes are not allowed within VxWorks dosFs filenames, although
- they are normally legal for pure MS-DOS implementations.)
- For the sake of consistency however use of forward slashes ("/") is
- recommended at all times.
- The leading slash of a dosFs pathname following the device name is
- optional. For example, both "DOS1:newfile.new" and "DOS1:/newfile.new"
- refer to the same file.
- USING EXTENDED DIRECTORY STRUCTURE
- This library supports DOS4.0 standard file names which fit the restrictions
- of eight upper-case characters optionally followed by a three-character
- extension,
- as well as Windows style VFAT standard long file names
- that are stored mixed cased on disk, but are case insensitive when
- searched and matched (e.g. during open() call).
- The VFAT long file name is stored in a variable number of consecutive
- directory entries.
- Both standards restrict file size to 4 GB (32 bit value).
- To provide additional flexibility, this implementation of the
- DOS file system provides proprietary ling file name format (VXLONGNAMES),
- which uses a simpler directory structure: the directory entry is
- of fixed size. When this option is
- used, file names may consist of any sequence of up to 40 ASCII
- characters. No case conversion is performed,
- and file name match is case-sensitive.
- With this directory format the
- file maximum size is expanded to 1 Terabyte (40 bit value).
- .RS 4 4
- NOTE: Because special directory entries are used on the disk, disks
- which use the extended names are
- .I not
- compatible with other implementation of the
- MS-DOS systems, and cannot be read on MS-DOS or Windows machines.
- .RE
- To enable the extended file names, set the DOS_OPT_VXLONGNAMES flag
- when calling dosFsVolFormat().
- READING DIRECTORY ENTRIES
- Directories on VxWorks dosFs volumes may be searched using the opendir(),
- readdir(), rewinddir(), and closedir() routines. These calls allow the
- names of files and subdirectories to be determined.
- 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 dosFs
- directory entry.
- For more information, see the manual entry for dirLib.
- FILE DATE AND TIME
- Directory entries on dosFs volumes contain creation, last modification
- time and date, and the last access date for each file or subdirectory.
- Directory last modification time and date fields are set only when
- a new entry is created, but not when any directory entries are deleted.
- The last access date field indicates the date of the last read or write.
- The last access date field is an optional field, per Microsoft. By
- default, file open-read-close operations do not update the last access
- date field. This default avoids media writes (writing out the date field)
- during read only operations. In order to enable the updating of the
- optional last access date field for open-read-close operations, you must
- call dosFsLastAccessDateEnable(), passing it the volumes DOS_VOLUME_DESC_ID
- and TRUE.
- The dosFs file system uses the ANSI time() function, that returns
- system clock value to obtain date and time. It is recommended that the
- target system should set the system time during system initialization
- time from a network server or from an embedded Calendar / Clock
- hardware component, so that all files on the file system would be
- associated with a correct date and time.
- The file system consistency checker (see below) sets system clock to
- value following the latest date-time field stored on the disk, if it
- discovers, that function time() returns a date earlier then Jan 1,
- 1998, meaning that the target system does not have a source of valid
- date and time to synchronize with.
- See also the reference manual entry for ansiTime.
- FILE ATTRIBUTES
- Directory entries on dosFs volumes contain an attribute byte consisting
- of bit-flags which specify various characteristics of the entry. The
- attributes which are identified are: read-only file, hidden file,
- system file, volume label, directory, and archive. The VxWorks symbols
- for these attribute bit-flags are:
- .IP DOS_ATTR_RDONLY
- File is write-protected, can not be modified or deleted.
- .IP DOS_ATTR_HIDDEN
- this attribute is not used by VxWorks.
- .IP DOS_ATTR_SYSTEM
- this attribute is not used by VxWorks.
- .IP DOS_ATTR_VOL_LABEL
- directory entry describes a volume label,
- this attribute can not be set or used directly, see ioctl() command
- FIOLABELGET and FIOLABELSET below for volume label manipulation.
- .IP DOS_ATTR_DIRECTORY
- directory entry is a subdirectory,
- this attribute can not be set directly.
- .IP DOS_ATTR_ARCHIVE
- this attribute is not used by VxWorks.
- .LP
- All the flags in the attribute byte, except the directory and volume label
- flags, may be set or cleared using the ioctl() FIOATTRIBSET function. This
- function is called after opening the specific file whose attributes are to
- be changed. The attribute byte value specified in the FIOATTRIBSET call is
- copied directly. To preserve existing flag settings, the current attributes
- should first be determined via fstat(), and the appropriate
- flag(s) changed using bitwise AND or OR operations. For example, to make
- a file read-only, while leaving other attributes intact:
- .CS
- struct stat fileStat;
- fd = open ("file", O_RDONLY, 0); /@ open file @/
- fstat (fd, &fileStat); /@ get file status @/
- ioctl (fd, FIOATTRIBSET, (fileStat.st_attrib | DOS_ATTR_RDONLY));
- /@ set read-only flag @/
- close (fd); /@ close file @/
- .CE
- .LP
- See also the reference manual entry for attrib() and xattrib() for
- user-level utility routines which control the attributes of files or
- file hierarchy.
- CONTIGOUS FILE SUPPORT
- The VxWorks dosFs file system provides efficient files storage:
- space will be allocated in groups of clusters (also termed
- .I extents
- ) so that a file will be composed of relatively large contiguous units.
- This nearly contiguous allocation technique is designed to
- effectively eliminate the effects of disk space fragmentation,
- keeping throughput very close to the maximum of which the hardware is
- capable of.
- However dosFs provides mechanism to allocate truly contiguous files,
- meaning files which are made up of a consecutive series of disk sectors.
- This support includes both the ability to allocate contiguous space to a file
- and optimized access to such a file when it is used.
- Usually this will somewhat improve performance when compared to
- Nearly Contiguous allocation, at the price of disk space fragmentation.
- To allocate a contiguous area to a file, the file is first created in the
- normal fashion, using open() or creat(). The file descriptor returned
- during the creation of the file is then used to make an ioctl() call,
- specifying the FIOCONTIG or FIOCONTIG64 function.
- The last parameter to the FIOCONTIG function is the size of the requested
- contiguous area in bytes, If the FIOCONTIG64 is used, the last parameter
- is pointer to 64-bit integer variable, which contains the required file size.
- It is also possible to request that the largest contiguous free area on
- the disk be obtained. In this case, the size value CONTIG_MAX (-1)
- is used instead of an actual size. These ioctl() codes
- are not supported for directories.
- The volume is searched for a contiguous area of free space, which
- is assigned to the file. If a segment of contiguous free space
- large enough for the request was not found,
- ERROR is returned, with <errno> set to S_dosFsLib_NO_CONTIG_SPACE.
- When contiguous space is allocated to a file, the file remains empty,
- while the newly allocated space has not been initialized.
- The data should be then written to the file, and eventually, when
- all data has been written, the file is closed.
- When file is closed, its space is truncated to reflect the amount
- of data actually written to the file.
- This file may then be again opened and used for further
- I/O operations read() or write(),
- but it can not be guaranteed that appended data will be contiguous
- to the initially written data segment.
- For example, the following will create a file and allocate 85 Mbytes of
- contiguous space:
- .CS
- fd = creat ("file", O_RDWR, 0); /@ open file @/
- status = ioctl (fd, FIOCONTIG, 85*0x100000);/@ get contiguous area @/
- if (status != OK)
- ... /@ do error handling @/
- close (fd); /@ close file @/
- .CE
- In contrast, the following example will create a file and allocate the
- largest contiguous area on the disk to it:
- .CS
- fd = creat ("file", O_RDWR, 0); /@ open file @/
- status = ioctl (fd, FIOCONTIG, CONTIG_MAX); /@ get contiguous area @/
- if (status != OK)
- ... /@ do error handling @/
- close (fd); /@ close file @/
- .CE
- .IP NOTE
- the FIOCONTIG operation should take place right after the file has been
- created, before any data is written to the file.
- Directories may not be allocated a contiguous disk area.
- .LP
- To determine the actual amount of contiguous space obtained when CONTIG_MAX
- is specified as the size, use fstat() to examine the number of blocks
- and block size for the file.
- When any file is opened, it may be checked for contiguity.
- Use the extended flag DOS_O_CONTIG_CHK when calling open() to access an
- existing file which may have been allocated contiguous space.
- If a file is detected as contiguous, all subsequent operations on the
- file will not require access to the File Allocation Table, thus
- eliminating any disk Seek operations.
- The down side however is that if this option is used, open() will take
- an amount of time which is linearly proportional of the file size.
- CHANGING, UNMOUNTING, AND SYNCHRONIZING DISKS
- Buffering of disk data in RAM, synchronization of these
- buffers with the disk and detection of removable disk replacement are
- all handled by the disk cache. See reference manual on dcacheCbio
- for more details.
- If a disk is physically removed, the disk cache will cause dosFsLib to
- .I unmount
- the volume, which will mark all currently open file descriptors as
- .I obsolete.
- If a new disk is inserted, it will be automatically
- .I mounted
- on the next call to open() or creat().
- IOCTL FUNCTIONS
- The dosFs file system supports the following ioctl() functions. The
- functions listed are defined in the header ioLib.h. Unless stated
- otherwise, the file descriptor used for these functions may be any file
- descriptor which is opened to a file or directory on the volume or to
- the volume itself.
- There are some ioctl() commands, that expect a 32-bit integer result
- (FIONFREE, FIOWHERE, etc.).
- However, disks and files with are grater than 4GB are supported.
- In order to solve this problem, new ioctl() functions have been added
- to support 64-bit integer results.
- They have the same name as basic functions, but with suffix
- .I 64,
- namely: FIONFREE64, FIOWHERE64 and so on. These commands
- expect a pointer to a 64-bit integer, i.e.:
- .CS
- long long *arg ;
- .CE
- as the 3rd argument to the ioctl() function.
- If a value which is requested with a 32-bit ioctl() command is
- too large to be represented in the 32-bit variable, ioctl() will return
- ERROR, and <errno> will be set to S_dosFsLib_32BIT_OVERFLOW.
- .iP "FIODISKINIT"
- Reinitializes a DOS file system on the disk volume.
- This function calls dosFsVolFormat() to format the volume,
- so dosFsFmtLib must be installed for this to work.
- Third argument of ioctl() is passed as argument <opt> to
- dosFsVolFormat() routine.
- This routine does not perform a low level format,
- the physical media is expected to be already formatted.
- If DOS file system device has not been created yet for a particular device,
- only direct call to dosFsVolFormat() can be used.
- .CS
- fd = open ("DEV1:", O_WRONLY);
- status = ioctl (fd, FIODISKINIT, DOS_OPT_BLANK);
- .CE
- .iP "FIODISKCHANGE"
- Announces a media change. No buffers flushing is performed.
- This function may be called from interrupt level:
- .CS
- status = ioctl (fd, FIODISKCHANGE, 0);
- .CE
- .iP "FIOUNMOUNT"
- Unmounts a disk volume. It performs the same function as dosFsVolUnmount().
- This function must not be called from interrupt level:
- .CS
- status = ioctl (fd, FIOUNMOUNT, 0);
- .CE
- .iP "FIOGETNAME"
- Gets the file name of the file descriptor and copies it to the buffer <nameBuf>.
- Note that <nameBuf> must be large enough to contain the largest possible
- path name, which requires at least 256 bytes.
- .CS
- status = ioctl (fd, FIOGETNAME, &nameBuf );
- .CE
- .iP "FIORENAME"
- Renames the file or directory to the string <newname>:
- .CS
- fd = open( "oldname", O_RDONLY, 0 );
- status = ioctl (fd, FIORENAME, "newname");
- .CE
- .iP "FIOMOVE"
- Moves the file or directory to the string <newname>:
- .CS
- fd = open( "oldname", O_RDONLY, 0 );
- status = ioctl (fd, FIOMOVE, "newname");
- .CE
- .iP "FIOSEEK"
- Sets the current byte offset in the file to the position specified by
- <newOffset>. This function supports offsets in 32-bit value range.
- Use FIOSEEK64 for larger position values:
- .CS
- status = ioctl (fd, FIOSEEK, newOffset);
- .CE
- .iP "FIOSEEK64"
- Sets the current byte offset in the file to the position specified by
- <newOffset>. This function supports offsets in 64-bit value range:
- .CS
- long long newOffset;
- status = ioctl (fd, FIOSEEK64, (int) & newOffset);
- .CE
- .iP "FIOWHERE"
- Returns the current byte position in the file. This is the
- byte offset of
- the next byte to be read or written. This function returns a 32-bit value.
- It takes no additional argument:
- .CS
- position = ioctl (fd, FIOWHERE, 0);
- .CE
- .iP "FIOWHERE64"
- Returns the current byte position in the file. This is the
- byte offset of
- the next byte to be read or written. This function returns a 64-bit
- value in <position>:
- .CS
- long long position;
- status = ioctl (fd, FIOWHERE64, (int) & position);
- .CE
- .iP "FIOFLUSH"
- Flushes disk cache buffers. It guarantees that any output that has
- been requested is actually written to the device:
- .CS
- status = ioctl (fd, FIOFLUSH, 0);
- .CE
- .iP "FIOSYNC"
- Updates the FAT copy for the passed file descriptor, then
- flushes and invalidates the CBIO cache buffers for the file
- descriptor's volume. FIOSYNC ensures that any outstanding
- output requests for the passed file descriptor are written
- to the device and a subsequent I/O operation will fetch data
- directly from the physical medium. To safely sync a volume
- for shutdown, all open file descriptor's should at the least
- be FIOSYNC'd by the application. Better, all open FD's should
- be closed by the application and the volume should be unmounted
- via FIOUNMOUNT.
- .CS
- status = ioctl (fd, FIOSYNC, 0);
- .CE
- .iP "FIOTRUNC"
- Truncates the specified file's length to <newLength> bytes. Any disk
- clusters which had been allocated to the file but are now unused are
- deallocated, and the directory entry for the file is updated to reflect
- the new length. Only regular files may be truncated; attempts to use
- FIOTRUNC on directories will return an error.
- FIOTRUNC may only be used to make files shorter; attempting to specify
- a <newLength> larger than the current size of the file produces an
- error (setting errno to S_dosFsLib_INVALID_NUMBER_OF_BYTES).
- .CS
- status = ioctl (fd, FIOTRUNC, newLength);
- .CE
- .iP "FIOTRUNC64"
- Similar to FIOTRUNC, but can be used for files lager, than 4GB.
- .CS
- long long newLength = .....;
- status = ioctl (fd, FIOTRUNC, (int) & newLength);
- .CE
- .iP "FIONREAD"
- Copies to <unreadCount> the number of unread bytes in the file:
- .CS
- unsigned long unreadCount;
- status = ioctl (fd, FIONREAD, &unreadCount);
- .CE
- .iP "FIONREAD64"
- Copies to <unreadCount> the number of unread bytes in the file.
- This function returns a 64-bit integer value:
- .CS
- long long unreadCount;
- status = ioctl (fd, FIONREAD64, &unreadCount);
- .CE
- .iP "FIONFREE"
- Copies to <freeCount> the amount of free space, in bytes, on the volume:
- .CS
- unsigned long freeCount;
- status = ioctl (fd, FIONFREE, &freeCount);
- .CE
- .iP "FIONFREE64"
- Copies to <freeCount> the amount of free space, in bytes, on the volume.
- This function can return value in 64-bit range:
- .CS
- long long freeCount;
- status = ioctl (fd, FIONFREE64, &freeCount);
- .CE
- .iP "FIOMKDIR"
- Creates a new directory with the name specified as <dirName>:
- .CS
- status = ioctl (fd, FIOMKDIR, "dirName");
- .CE
- .iP "FIORMDIR"
- Removes the directory whose name is specified as <dirName>:
- .CS
- status = ioctl (fd, FIORMDIR, "dirName");
- .CE
- .iP "FIOLABELGET"
- Gets the volume label (located in root directory) and copies the string to
- <labelBuffer>. If the label contains DOS_VOL_LABEL_LEN significant
- characters, resulting string is not NULL terminated:
- .CS
- char labelBuffer [DOS_VOL_LABEL_LEN];
- status = ioctl (fd, FIOLABELGET, (int)labelBuffer);
- .CE
- .iP "FIOLABELSET"
- Sets the volume label to the string specified as <newLabel>. The string may
- consist of up to eleven ASCII characters:
- .CS
- status = ioctl (fd, FIOLABELSET, (int)"newLabel");
- .CE
- .iP "FIOATTRIBSET"
- Sets the file attribute byte in the DOS directory entry to the new value
- <newAttrib>. The file descriptor refers to the file whose entry is to be
- modified:
- .CS
- status = ioctl (fd, FIOATTRIBSET, newAttrib);
- .CE
- .iP "FIOCONTIG"
- Allocates contiguous disk space for a file or directory. The number of
- bytes of requested space is specified in <bytesRequested>. In general,
- contiguous space should be allocated immediately after the file is
- created:
- .CS
- status = ioctl (fd, FIOCONTIG, bytesRequested);
- .CE
- .iP "FIOCONTIG64"
- Allocates contiguous disk space for a file or directory. The number of
- bytes of requested space is specified in <bytesRequested>. In general,
- contiguous space should be allocated immediately after the file is
- created. This function accepts a 64-bit value:
- .CS
- long long bytesRequested;
- status = ioctl (fd, FIOCONTIG64, &bytesRequested);
- .CE
- .iP "FIONCONTIG"
- Copies to <maxContigBytes> the size of the largest contiguous free space,
- in bytes, on the volume:
- .CS
- status = ioctl (fd, FIONCONTIG, &maxContigBytes);
- .CE
- .iP "FIONCONTIG64"
- Copies to <maxContigBytes> the size of the largest contiguous free space,
- in bytes, on the volume. This function returns a 64-bit value:
- .CS
- long long maxContigBytes;
- status = ioctl (fd, FIONCONTIG64, &maxContigBytes);
- .CE
- .iP "FIOREADDIR"
- Reads the next directory entry. The argument <dirStruct> is a DIR
- directory descriptor. Normally, the readdir() routine is used to read a
- directory, rather than using the FIOREADDIR function directly. See dirLib.
- .CS
- DIR dirStruct;
- fd = open ("directory", O_RDONLY);
- status = ioctl (fd, FIOREADDIR, &dirStruct);
- .CE
- .iP "FIOFSTATGET"
- Gets file status information (directory entry data). The argument
- <statStruct> is a pointer to a stat structure that is filled with data
- describing the specified file. Normally, the stat() or fstat() routine is
- used to obtain file information, rather than using the FIOFSTATGET
- function directly. See dirLib.
- .CS
- struct stat statStruct;
- fd = open ("file", O_RDONLY);
- status = ioctl (fd, FIOFSTATGET, (int)&statStruct);
- .CE
- .iP "FIOTIMESET"
- Update time on a file. <arg> shall be a pointer to a utimbuf structure,
- see utime.h. If <arg> is value NULL, the current system time is used for
- both actime and modtime members. If <arg> is not NULL then the utimbuf
- structure members actime and modtime are used as passed. If actime is
- zero value, the file access time is not updated (the operation is ignored).
- If modtime is zero, the file modification time is not updated (the operation
- is ignored).
- See also utime()
- ..CS
- struct utimbuf newTimeBuf;;
- newTimeBuf.modtime = newTimeBuf.actime = fileNewTime;
- fd = open ("file", O_RDONLY);
- status = ioctl (fd, FIOTIMESET, (int)&newTimeBuf);
- .CE
- .iP "FIOCHKDSK"
- This function invokes the integral consistency checking.
- During the test, the file system will be blocked from application code
- access, and will emit messages describing any inconsistencies found on
- the disk, as well as some statistics, depending on the verbosity
- level in the <flags> argument.
- Depending on the repair permission value in <flags> argument,
- the inconsistencies will be repaired, and changes written to disk
- or only reported.
- Argument <flags> should be composed of bitwise or-ed
- verbosity level value and repair permission value.
- Possible repair levels are:
- .RS
- .iP "DOS_CHK_ONLY (1)"
- Only report errors, do not modify disk.
- .iP "DOS_CHK_REPAIR (2)"
- Repair any errors found.
- .LP
- Possible verbosity levels are:
- .iP "DOS_CHK_VERB_SILENT (0xff00)"
- Do not emit any messages, except errors encountered.
- .iP "DOS_CHK_VERB_1 (0x0100)"
- Display some volume statistics when done testing, as well
- .iP "DOS_CHK_VERB_2 (0x0200)"
- In addition to the above option, display path of every file, while it
- is being checked. This option may significantly slow down the test
- process.
- .IP "NOTE"
- In environments with reduced RAM size check disk uses reserved
- FAT copy as temporary buffer, it can cause respectively long
- time of execution on a slow CPU architectures..
- .LP
- .RE
- See also the reference manual usrFsLib for the chkdsk() user level
- utility which may be used to invoke the FIOCHKDSK ioctl().
- The volume root directory should be opened, and the resulting file
- descriptor should be used:
- .CS
- int fd = open (device_name, O_RDONLY, 0);
- status = ioctl (fd, FIOCHKDSK, DOS_CHK_REPAIR | DOS_CHK_VERB_1);
- close (fd);
- .CE
- .LP
- Any other ioctl() function codes are passed to the underlying
- .I CBIO
- modules for handling.
- INCLUDE FILES: dosFsLib.h
- SEE ALSO:
- ioLib, iosLib, dirLib, usrFsLib, dcacheCbio, dpartCbio, dosFsFmtLib,
- dosChkLib
- .I "Microsoft MS-DOS Programmer's Reference"
- (Microsoft Press),
- .I "Advanced MS-DOS Programming"
- (Ray Duncan, Microsoft Press),
- .I "VxWorks Programmer's Guide: I/O System, Local File Systems"
- INTERNAL:
- Note: To represent a backslash in documentation use "e", not "\".
- The double backslash sometimes works, but results may be unpredictable.
- */
- /* includes */
- #include "vxWorks.h"
- #include "private/dosFsVerP.h"
- #include "stat.h"
- #include "time.h"
- #include "dirent.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include "string.h"
- #include "taskLib.h"
- #include "tickLib.h"
- #include "semLib.h"
- #include "logLib.h"
- #include "errnoLib.h"
- #include "memLib.h"
- #include "utime.h"
- #include "blkIo.h"
- #include "private/print64Lib.h"
- #include "private/dosFsLibP.h"
- #include "private/dosDirLibP.h"
- /* defines */
- #if FALSE
- # undef FAT_ALLOC_ONE
- # define FAT_ALLOC_ONE (FAT_ALLOC | 8)
- #endif /* FALSE */
- /* macros */
- #undef DBG_MSG
- #undef ERR_MSG
- #undef NDEBUG
- #ifdef DEBUG
- # undef LOCAL
- # define LOCAL
- # undef ERR_SET_SELF
- # define ERR_SET_SELF
- # define DBG_MSG( lvl, fmt, arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8 )
- { if( (lvl) <= dosFsDebug )
- printErr( "%s : %d : " fmt,
- __FILE__, __LINE__, arg1,arg2,
- arg3,arg4,arg5,arg6,arg7,arg8 ); }
- # define ERR_MSG( lvl, fmt, a1,a2,a3,a4,a5,a6 )
- { logMsg( __FILE__ " : " fmt, (int)(a1), (int)(a2),
- (int)(a3), (int)(a4), (int)(a5), (int)(a6) ); }
- #else /* NO DEBUG */
- # define NDEBUG
- # define DBG_MSG(lvl,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) {}
- # define ERR_MSG( lvl, fmt, a1,a2,a3,a4,a5,a6 )
- { if( (lvl) <= dosFsDebug )
- logMsg( __FILE__ " : " fmt, (int)(a1), (int)(a2),
- (int)(a3), (int)(a4), (int)(a5), (int)(a6) ); }
- #endif /* DEBUG */
- #include "assert.h"
- #ifdef ERR_SET_SELF
- # define errnoSet( err ) errnoSetOut( __FILE__, __LINE__, #err, (err) )
- #endif /* ERR_SET_SELF */
- /* typedefs */
- /* globals */
- int dosFsDrvNum = ERROR; /* dosFs number in vxWorks driver table */
- u_int dosFsDebug = 1;
- /* handlers lists */
- DOS_HDLR_DESC dosFatHdlrsList[ DOS_MAX_HDLRS ] = {{0}};
- DOS_HDLR_DESC dosDirHdlrsList[ DOS_MAX_HDLRS ] = {{0}};
- STATUS (*dosFsChkRtn)( DOS_FILE_DESC_ID pFd ) = NULL;
- /* check disk routine */
- STATUS (*dosFsVolFormatRtn)( void * pDev, int opt,
- FUNCPTR pPromptFunc ) = NULL;
- /* volume format routine */
- /* locals */
- LOCAL STATUS dosFsRead( FAST DOS_FILE_DESC_ID pFd, char * pBuf,
- int maxBytes );
- LOCAL STATUS dosFsIoctl ( FAST DOS_FILE_DESC_ID pFd, int function, int arg );
- LOCAL DOS_FILE_DESC_ID dosFsOpen ( FAST DOS_VOLUME_DESC * pVolDesc,
- char * pPath, int flags, int mode );
- LOCAL STATUS dosFsDelete (DOS_VOLUME_DESC_ID pVolDesc, char * pPath);
- LOCAL STATUS dosFsClose (DOS_FILE_DESC_ID pFd);
- LOCAL STATUS dosFsRename (DOS_FILE_DESC_ID pFdOld, char * pNewName,
- BOOL allowOverwrite);
- #ifdef ERR_SET_SELF
- /*******************************************************************************
- * errnoSetOut - put error message
- *
- * This routine is called instead of errnoSet during debugging.
- */
- static VOID errnoSetOut(char * pFile, int line, const char * pStr, int errCode )
- {
- if( errCode != OK && strcmp( str, "errnoBuf" ) != 0 )
- printf( " %s : line %d : %s = %p, task %pn",
- pFile, line, pStr, (void *)errCode,
- (void *)taskIdSelf() );
- errno = errCode;
- }
- #endif /* ERR_SET_SELF */
- /*******************************************************************************
- *
- * dosSetVolCaseSens - set case sensitivity of volume
- *
- * Pass TRUE to setup a case sensitive volume.
- * Pass FALSE to setup a case insensitive volume.
- * Note this affects rename lookups only.
- *
- * RETURNS: TRUE if pVolDesc pointed to a DOS volume.
- */
- STATUS dosSetVolCaseSens
- (
- DOS_VOLUME_DESC_ID pVolDesc,
- BOOL sensitivity
- )
- {
- BOOL isValid; /* Validity flag */
- /* If the parameter is aligned OK */
- if ( TRUE == (isValid = _WRS_ALIGN_CHECK(pVolDesc, DOS_VOLUME_DESC)))
- {
- /* Aligned parameter, but is it a DOS volume? */
- if ( TRUE == (isValid = (pVolDesc->magic == DOS_FS_MAGIC )) )
- {
- pVolDesc->volIsCaseSens = sensitivity;
- }
- }
- /* Didn't work? Broken parameter then */
- if (FALSE == isValid)
- {
- /* Not a DOS volume or not aligned so that's an invalid parameter */
- errnoSet( S_dosFsLib_INVALID_PARAMETER );
- }
- /* If it was valid it got set. */
- return (isValid);
- }
- /*******************************************************************************
- *
- * dosFsIsValHuge - check if value is grater, than 4GB (max 32 bit).
- *
- * RETURNS: TRUE if is grater, else return FALSE.
- */
- LOCAL BOOL dosFsIsValHuge
- (
- fsize_t val
- )
- {
- return DOS_IS_VAL_HUGE( val );
- } /* dosFsIsValHuge() */
- /*******************************************************************************
- *
- * dosFsVolDescGet - convert a device name into a DOS volume descriptor pointer.
- *
- * This routine validates <pDevNameOrPVolDesc> to be a DOS volume
- * descriptor pointer else a path to a DOS device. This routine
- * uses the standard iosLib function iosDevFind() to obtain a pointer
- * to the device descriptor. If device is eligible, <ppTail> is
- * filled with the pointer to the first character following
- * the device name. Note that ppTail is passed to iosDevFind().
- * <ppTail> may be passed as NULL, in which case it is ignored.
- *
- * RETURNS: A DOS_VOLUME_DESC_ID or NULL if not a DOSFS device.
- *
- * ERRNO:
- * S_dosFsLib_INVALID_PARAMETER
- */
- DOS_VOLUME_DESC_ID dosFsVolDescGet
- (
- void * pDevNameOrPVolDesc, /* device name or pointer to dos vol desc */
- u_char ** ppTail /* return ptr for name, used in iosDevFind */
- )
- {
- DOS_VOLUME_DESC_ID pVolDesc; /* pointer to volume descriptor */
- char * pDevName = (pDevNameOrPVolDesc == NULL) ?
- "" : pDevNameOrPVolDesc;
- u_char * pNameTail;
- if( ppTail == NULL )
- ppTail = &pNameTail;
-
- *ppTail = NULL;
-
- pVolDesc = pDevNameOrPVolDesc;
-
- /* SPR#71720 NULL is presumed to be an invalid value */
-
- if (NULL == pVolDesc)
- {
- errnoSet( S_dosFsLib_INVALID_PARAMETER );
- return (NULL);
- }
- /* SPR#71720 ensure alignment and then check the magic cookie */
- if (TRUE == _WRS_ALIGN_CHECK(pVolDesc, DOS_VOLUME_DESC))
- {
- if (pVolDesc->magic == DOS_FS_MAGIC )
- {
- return (pVolDesc);
- }
- }
- /* attempt to extract volume descriptor ptr from device name */
-
- #ifdef _WRS_DOSFS2_VXWORKS_AE
- pVolDesc =
- (DOS_VOLUME_DESC_ID) iosDevFind (pDevName, (const char **)ppTail);
- #else
- pVolDesc =
- (DOS_VOLUME_DESC_ID) iosDevFind (pDevName, (char **) ppTail);
- #endif /* _WRS_DOSFS2_VXWORKS_AE */
- /*
- * SPR#76510, if iosDevFind() returned default device,
- * then the tail (ppTail) will point to the front of
- * the passed name and that is considered a lookup failure.
- */
- if ((NULL == pVolDesc) || ((char *) *ppTail == pDevName))
- {
- errnoSet( S_dosFsLib_INVALID_PARAMETER );
- return (NULL);
- }
- /* SPR#71720 ensure alignment and then check the magic cookie */
- if (TRUE == _WRS_ALIGN_CHECK(pVolDesc, DOS_VOLUME_DESC))
- {
- if (pVolDesc->magic == DOS_FS_MAGIC )
- {
- return (pVolDesc);
- }
- }
-
- errnoSet( S_dosFsLib_INVALID_PARAMETER );
-
- return (NULL);
- } /* dosFsVolDescGet() */
- /*******************************************************************************
- *
- * dosFsFSemTake - take file semaphore.
- *
- * RETURNS: STATUS as result of semTake.
- */
- LOCAL STATUS dosFsFSemTake
- (
- DOS_FILE_DESC_ID pFd,
- int timeout
- )
- {
- STATUS retStat;
- assert( pFd - pFd->pVolDesc->pFdList < pFd->pVolDesc->maxFiles );
- assert( pFd->pFileHdl - pFd->pVolDesc->pFhdlList <
- pFd->pVolDesc->maxFiles );
- retStat = semTake( *(pFd->pVolDesc->pFsemList +
- (pFd->pFileHdl - pFd->pVolDesc->pFhdlList)),
- timeout);
- assert( retStat == OK );
- return retStat;
- } /* dosFsFSemTake() */
-
- /*******************************************************************************
- *
- * dosFsFSemGive - release file semaphore.
- *
- * RETURNS: STATUS as result of semGive.
- */
- LOCAL STATUS dosFsFSemGive
- (
- DOS_FILE_DESC_ID pFd
- )
- {
- STATUS retStat;
-
- assert( pFd - pFd->pVolDesc->pFdList < pFd->pVolDesc->maxFiles );
- assert( pFd->pFileHdl - pFd->pVolDesc->pFhdlList <
- pFd->pVolDesc->maxFiles );
-
- retStat = semGive( *(pFd->pVolDesc->pFsemList +
- (pFd->pFileHdl - pFd->pVolDesc->pFhdlList)));
- assert( retStat == OK );
- return retStat;
- } /* dosFsFSemGive() */
-
- /*******************************************************************************
- *
- * dosFsVolUnmount - unmount a dosFs volume
- *
- * This routine is called when I/O operations on a volume are to be
- * discontinued. This is the preferred action prior to changing a
- * removable disk.
- *
- * All buffered data for the volume is written to the device
- * (if possible, with no error returned if data cannot be written),
- * any open file descriptors are marked as obsolete,
- * and the volume is marked as not currently mounted.
- *
- * When a subsequent open() operation is initiated on the device,
- * new volume will be mounted automatically.
- *
- * Once file descriptors have been marked as obsolete, any attempt to
- * use them for file operations will return an error. (An obsolete file
- * descriptor may be freed by using close(). The call to close() will
- * return an error, but the descriptor will in fact be freed).
- *
- * This routine may also be invoked by calling ioctl() with the
- * FIOUNMOUNT function code.
- *
- * This routine must not be called from interrupt level.
- *
- * RETURNS: OK, or ERROR if the volume was not mounted.
- *
- * NOMANUAL
- */
- STATUS dosFsVolUnmount
- (
- void * pDevNameOrPVolDesc /* device name or ptr to */
- /* volume descriptor */
- )
- {
- DOS_VOLUME_DESC_ID pVolDesc; /* pointer to volume descriptor */
- int i;
-
- /* get volume descriptor */
-
- pVolDesc = dosFsVolDescGet( pDevNameOrPVolDesc, NULL );
- if( pVolDesc == NULL )
- return ERROR;
-
- if( ! pVolDesc->mounted )
- return ERROR;
-
- /* acquire semaphore */
-
- if( semTake( pVolDesc->devSem, WAIT_FOREVER ) == ERROR )
- return ERROR;
-
- /* mark all opened file descriptors as obsolete */
- /* also synchronize the FAT and do not hold the */
- /* file semaphore, in certain operations like */
- /* rename-file the file semaphore is locked */
- /* before devSem. Trying to hold file semaphore */
- /* , after holding devSem, will cause dead-lock */
-
- for( i = 0; i < pVolDesc->maxFiles; i ++ )
- {
- if( pVolDesc->pFdList[ i ].busy )
- {
- /* Synchronize the FAT */
- pVolDesc->pFatDesc->flush(&pVolDesc->pFdList[i]);
- pVolDesc->pFdList[ i ].pFileHdl->obsolet = 1;
- }
- }
- pVolDesc->nBusyFd = 0;
-
- /* flush buffers */
-
- cbioIoctl( pVolDesc->pCbio, CBIO_CACHE_FLUSH, (void*)(-1) );
- cbioIoctl( pVolDesc->pCbio, CBIO_CACHE_INVAL, 0 );
-
- pVolDesc->mounted = FALSE; /* volume unmounted */
-
- semGive( pVolDesc->devSem );
- return OK;
- } /* dosFsVolUnmount() */
-
- /*******************************************************************************
- *
- * dosFsChkDsk - make volume integrity checking.
- *
- * This library does not makes integrity check process itself, but
- * instead uses routine provided by dosChkLib.
- * This routine prepares parameters and invokes checking routine
- * via preinitialized function pointer. If dosChkLib does not configured
- * into vxWorks, this routine returns ERROR.
- *
- * Ownership on device should be taken by an upper level routine.
- *
- * RETURNS: STATUS as returned by volume checking routine or
- * ERROR, if such routine does not installed.
- *
- * ERRNO:
- * S_dosFsLib_UNSUPPORTED.
- */
- STATUS dosFsChkDsk
- (
- FAST DOS_FILE_DESC_ID pFd, /* file descriptor of root dir */
- u_int params /* check level and verbosity */
- )
- {
- DOS_VOLUME_DESC_ID pVolDesc = pFd->pVolDesc;
- CBIO_DEV_ID cbio = pFd->pVolDesc->pCbio;
- STATUS retVal = ERROR;
-
- if( dosFsChkRtn == NULL )
- {
- errnoSet( S_dosFsLib_UNSUPPORTED );
- ERR_MSG(1,"Check disk utility not installedn", 0,0,0,0,0,0 );
- return ERROR;
- }
- /* prepare check disk parameters */
-
- if( (params & 0xff) == 0 )
- params |= DOS_CHK_ONLY;
-
- /* prevent attempts to write on read only volume */
-
- cbioIoctl(cbio, CBIO_STATUS_CHK, 0 );
- if( cbioModeGet(cbio) == O_RDONLY )
- {
- /* avoid writing to O_RDONLY devices */
- params = (params & ~0xff) | DOS_CHK_ONLY;
- }
- pVolDesc->chkLevel = min( params & 0xff, DOS_CHK_REPAIR );
- pVolDesc->chkVerbLevel =
- ( (params & 0xff00) == DOS_CHK_VERB_0 )? 0 : params >> 8;
-
- /* run disk check */
-
- retVal = dosFsChkRtn( pFd );
-
- if(params == ((params & ~0xff) | DOS_CHK_ONLY))
- {
- cbioIoctl( pVolDesc->pCbio, CBIO_CACHE_FLUSH, (void *)(-1) );
- cbioIoctl( pVolDesc->pCbio, CBIO_CACHE_INVAL, 0 );
- }
-
- pVolDesc->chkLevel = 0;
- pVolDesc->chkVerbLevel = 0;
-
- /* reset dcache driver to recount boot block checksum */
- if( TRUE == cbioRdyChgdGet (pVolDesc->pCbio) )
- {
- retVal = ERROR; /* volume was changed during check disk */
- }
- else
- {
- cbioIoctl( pVolDesc->pCbio, CBIO_RESET, NULL );
- }
-
- return retVal;
- } /* dosFsChkDsk() */
- /*******************************************************************************
- *
- * dosFsBadBootMsg - print error message while boot sector parsing.
- *
- * RETURNS: N/A.
- */
- LOCAL void dosFsBadBootMsg
- (
- u_int dbgLevel, /* message level */
- u_int offset, /* offset of invalid field */
- u_int val, /* invalid value */
- char * pExtraMsg, /* extra message */
- u_int line /* line number of error detecting code */
- )
- {
- #ifdef DEBUG
- printErr( "%s : %u. Malformed boot sector. Offset %u, value %u. %sn",
- __FILE__, __LINE__, offset, val,
- ( (pExtraMsg == NULL)? " " : pExtraMsg ) );
- #else /* not DEBUG */
- ERR_MSG( dbgLevel, "Malformed boot sector. Offset %u, value %u. %sn",
- offset, val, ( (pExtraMsg == NULL)? " " : pExtraMsg ), 0,0,0 );
- #endif /* DEBUG */
- }
-
- /*******************************************************************************
- *
- * dosFsBootSecGet - extract boot sector parameters.
- *
- * This routine reads boot sector from the disk and extracts
- * current volume parameters from of it.
- *
- * This routine also performs sanity check of those volume parameters,
- * that are mutually dependent or alternative.
- *
- * This routine also determines the FAT type: FAT32, FAT12, or FAT16.
- *
- * If read or damaged boot sector is encountered, this routine
- * searches for backup copy of boot sector and accepts volume
- * volume configuration from this copy.,
- *
- * RETURNS: OK or ERROR if disk read error or inconsistent
- * boot sector data or failure to obtain cbio parameters.
- *
- * ERRNO:
- * S_dosFsLib_UNKNOWN_VOLUME_FORMAT
- */
- LOCAL STATUS dosFsBootSecGet
- (
- DOS_VOLUME_DESC_ID pVolDesc /* pointer to volume descriptor */
- )
- {
- CBIO_PARAMS cbioParams;
- u_int work;
- u_char bootSec[ 512 ] = { 0 }; /* buffer for boot sector data */
- u_char pass = 0;
- u_char tmpType [ DOS_BOOT_FSTYPE_LEN + 1] = { 0 };
- pVolDesc->bootSecNum = DOS_BOOT_SEC_NUM; /* zero, per FAT standard */
- boot_get:
- /* reset the CBIO device in order to synchronize the boot sector */
- if( cbioIoctl(pVolDesc->pCbio, CBIO_RESET, 0 ) == ERROR )
- {
- return ERROR;
- }
- /* request the underlying CBIO device parameters */
- if (ERROR == cbioParamsGet (pVolDesc->pCbio, &cbioParams))
- {
- return (ERROR);
- }
- /* ensure bytesPerBlk is non-zero */
- if (0 == cbioParams.bytesPerBlk)
- {
- ERR_MSG( 1, "cbioParams.bytesPerBlk cannot be zero.n",
- 0,0,0,0,0,0 );
- if( TRUE == cbioRdyChgdGet (pVolDesc->pCbio) )
- {
- return (ERROR);
- }
- goto error;
- }
- /* read relevant boot sector data into bootSec[] */
- work = min( cbioParams.bytesPerBlk, (int) sizeof(bootSec) );
- if( cbioBytesRW(pVolDesc->pCbio, pVolDesc->bootSecNum, 0 /* offset */,
- (addr_t)bootSec, work, CBIO_READ, NULL ) == ERROR )
- {
- ERR_MSG( 1, "ERROR reading the device boot sectorn", 0,0,0,0,0,0 );
- ERR_MSG( 1, "media not formatted or not presentn", 0,0,0,0,0,0 );
- if( TRUE == cbioRdyChgdGet (pVolDesc->pCbio) )
- {
- return (ERROR);
- }
- goto error;
- }
- /* check for both acceptable Intel 80x86 `jmp' opcodes */
- if( bootSec[ DOS_BOOT_JMP ] != 0xe9 && bootSec[ DOS_BOOT_JMP ] != 0xeb )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_JMP, (u_int)bootSec[ DOS_BOOT_JMP ],
- NULL , __LINE__);
- goto error;
- }
- /*
- * Many FAT documents mistakenly state that the 0x55aa signature word
- * "occupies the last two bytes of the boot sector". That is only true
- * when the bytes-per-sector value is 512 bytes. Microsoft defines the
- * signature at offsets 510 and 511 (zero-based) regardless of the sector
- * size. It is acceptable, however to have the signature at the end of the
- * sector. We shall accept either location. SPR#62415.
- */
- /* read the ending 2 bytes of the sector to check signature */
- if( cbioBytesRW(pVolDesc->pCbio, pVolDesc->bootSecNum,
- cbioParams.bytesPerBlk - 2 /* off */,
- (addr_t)&work, 2, CBIO_READ, NULL ) == ERROR )
- {
- ERR_MSG( 1, "ERROR reading boot sectorn", 0,0,0,0,0,0 );
- if( TRUE == cbioRdyChgdGet (pVolDesc->pCbio) )
- {
- return (ERROR);
- }
- goto error;
- }
- work = DISK_TO_VX_16( &work );
- if (0xaa55 != work) /* why have a back door? "&& (work != 0xface)") */
- {
- /*
- * We did not find the signature word at the end of the sector,
- * so we will check at the 510/511 offset per the Microsoft FAT spec.
- * If we cannot find it there, then assume the boot sector is bad.
- */
- if ((bootSec[510] != 0x55) || (bootSec[511] != 0xaa))
- {
- dosFsBadBootMsg( 1, cbioParams.bytesPerBlk - 2,
- work, "At the end of boot sector", __LINE__ );
- goto error;
- }
- }
- /*
- * Start filling out and verifying the volume descriptor fields
- * using the data from the boot parameter block. Evaluate the validity
- * of the data, so that dosFsVolIsFat12() may be safely called.
- */
- /* evaluate bytes per sector */
-
- work = DISK_TO_VX_16( bootSec + DOS_BOOT_BYTES_PER_SEC );
- pVolDesc->bytesPerSec = work;
- if( work == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_BYTES_PER_SEC, work, NULL, __LINE__ );
- ERR_MSG(1, "bytesPerSec = 0n", 0,0,0,0,0,0 );
- goto error;
- }
- if( work != cbioParams.bytesPerBlk )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_BYTES_PER_SEC, work, NULL, __LINE__ );
- ERR_MSG(1, "cbioParams.bytesPerBlk %u != bytes-per-sec %un",
- cbioParams.bytesPerBlk, work, 0,0,0,0 );
- goto error;
- }
- /*
- * bytes-per-sector must be a power-of-two per Microsoft FAT specification
- * and at least 32 bytes, so a shift is used instead of multiplication in
- * operations with this value.
- */
- pVolDesc->secSizeShift = 0;
- for( work = 5; work < 16; work ++ )
- {
- if( (1 << work) == pVolDesc->bytesPerSec )
- {
- pVolDesc->secSizeShift = work;
- break;
- }
- }
- if( pVolDesc->secSizeShift == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_BYTES_PER_SEC,
- pVolDesc->bytesPerSec, NULL, __LINE__ );
- goto error;
- }
-
- /* evaluate the total number of sectors on this volume */
-
- work = DISK_TO_VX_16( bootSec + DOS_BOOT_NSECTORS );
- /*
- * When the volume has at least 0x10000 sectors, the 16 bit field
- * DOS_BOOT_NSECTORS is zero, and the alternate 32bit field
- * DOS_BOOT_LONG_NSECTORS is used to determine the number of
- * sectors on the volume.
- */
- if( work == 0 ) /* it is a large disk */
- {
- work = DISK_TO_VX_32( bootSec + DOS_BOOT_LONG_NSECTORS );
- if( work == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_LONG_NSECTORS, work,
- NULL, __LINE__ );
- goto error;
- }
- }
- pVolDesc->totalSec = work;
-
- /* number of sectors can be greater than cbioParams.nBlocks */
-
- if( work != cbioParams.nBlocks )
- {
- /*
- * XXX - An error here may indicate a problem with representing
- * a partition size correctly in the underlying CBIO layer.
- *
- * Also, an off by one error may mean a driver bug.
- * cbioParams.nBlocks is the number of blocks on the
- * CBIO device. Using the "last addressable LBA value"
- * in nBlocks can produce an off by one error, this is
- * considered a driver problem. bd_nBlocks shall be the
- * number of blocks (1-xxx) not the last addressable block.
- * Rather the driver should set nBlocks to the
- * (last addressable block + 1). DOSFS1 did not make this
- * check. DOSFS2 does make this check to avoid overrun.
- */
- if( work < cbioParams.nBlocks )
- {
- ERR_MSG(10,
- "WARNING: num-sectors %u < cbioParams.nBlocks %un",
- work, cbioParams.nBlocks, 0,0,0,0 );
- }
- else
- {
- dosFsBadBootMsg( 1, DOS_BOOT_LONG_NSECTORS, 0,
- NULL, __LINE__ );
- ERR_MSG(1, "num-sectors %u > cbioParams.nBlocks %un",
- work, cbioParams.nBlocks, 0,0,0,0 );
- goto error;
- }
- }
-
- /* evaluate the number of sectors per cluster */
-
- pVolDesc->secPerClust = bootSec[ DOS_BOOT_SEC_PER_CLUST ];
- if( pVolDesc->secPerClust == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_SEC_PER_CLUST, 0,
- NULL, __LINE__ );
- goto error;
- }
-
- /* evaluate the number of FAT copies */
-
- pVolDesc->nFats = bootSec[ DOS_BOOT_NFATS ];
- if( pVolDesc->nFats == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_NFATS, 0, NULL, __LINE__ );
- goto error;
- }
-
- /* get the number of hidden sectors */
-
- pVolDesc->nHiddenSecs = DISK_TO_VX_16( bootSec + DOS_BOOT_NHIDDEN_SECS);
-
- /* evaluate the number of reserved sectors */
-
- pVolDesc->nReservedSecs = DISK_TO_VX_16( bootSec + DOS_BOOT_NRESRVD_SECS);
- if( pVolDesc->nReservedSecs == 0 )
- {
- dosFsBadBootMsg( 1, DOS_BOOT_NRESRVD_SECS, 0,
- NULL, __LINE__ );
- goto error;
- }
- /* evaluate the number of sectors alloted to FAT table */
- pVolDesc->secPerFat = DISK_TO_VX_16( bootSec + DOS_BOOT_SEC_PER_FAT );
- /*
- * Now determine the volumes FAT type. FAT12, FAT16, and FAT32.
- * NOTE: The secPerFat field is zero on FAT32 DOSFS volumes.
- * This is how we determine if FAT32 will be used when mounting
- * this volume. If secPerFat is zero, it must be FAT32.
- * Else, we need to pick between FAT12 and FAT16.
- */
- if( pVolDesc->secPerFat != 0 ) /* then using either FAT12 or FAT16 */
- {
- /*
- * The maximum number of 16 bit FAT entries is 65536.
- * Anything greater is invalid. Check here.
- */
- if( pVolDesc->secPerFat > (ULONG)0x10000*2 / pVolDesc->bytesPerSec)
- {
- dosFsBadBootMsg( 1, DOS_BOOT_SEC_PER_FAT,
- pVolDesc->secPerFat, NULL, __LINE__ );
- ERR_MSG(1, "secPerFat 12/16 = %u, while BPS = %un",
- pVolDesc->secPerFat,pVolDesc->bytesPerSec,0,0,0,0 );
- goto error;
- }
-
- /*
- * Now we must decide if our volume is using FAT12 or FAT16.
- * If we choose the wrong FAT type, volume mounting will fail,
- * and/or data corruption on the volume will occur when its exercised.
- * See also: SPR#34704.
- * We will also check the MS FSTYPE field (offset 0x36 in the
- * boot sector) when determining the FAT type. If either of the
- * Microsoft defined strings exist, then we honor the boot sectors
- * wisdom. This presumes that the formatter of the volume knew what
- * they were doing when writing out these strings. This may not
- * be the case, but its seems the most compatible approach.
- * The FSTYPE string field is also intentionaly being honored, so
- * that either FAT type can be forced in the field. In the event
- * of a bad mount occuring in the field, a hack of writing the correct
- * string to the BPB FSTYPE field would force the mount to the desired
- * type. Many DOS implementations do not set these strings and that
- * is just fine. Copy FSTYPE string to tmpType.
- */
- bcopy ( (char *) bootSec + DOS_BOOT_FSTYPE_ID,
- (char *) tmpType, DOS_BOOT_FSTYPE_LEN);
- /*
- * Now calculate the FAT type (FAT12 vs. FAT16) per a formula
- * We warn the user when the FSTYPE string (if present) doesn't match
- * the calculation.
- */
- work = dosFsVolIsFat12(bootSec);
- if (ERROR == work)
- {
- ERR_MSG(1, "dosFsVolIsFat12 returned ERRORn", 0,0,0,0,0,0 );
- goto error;
- }
- if (TRUE == work) /* then calculated FAT12 */
- {
- /*
- * Check the FSTYPE field in the BPB to ensure the string
- * value matches our calculation. If not, the we assume
- * the formatter knew what they wanted, and we honor
- * the string value. We look for "FAT12 " or "FAT16 ".
- */
- if ((strcmp ((char *)tmpType, DOS_BOOT_FSTYPE_FAT16)) == 0)
- {
- pVolDesc->fatType = FAT16;
- printf("WARNING: FAT16 indicated by BPB FSTYPE string, "
- "cluster calculation was FAT12. Honoring string.n");
- }
- else
- {
- pVolDesc->fatType = FAT12;
- }
- }
- else /* we calculated FAT 16 */
- {
- /*
- * Check the FSTYPE field in the BPB to ensure the string
- * value matches our calculation. If not, the we assume
- * the formatter knew what they wanted, and we honor
- * the string value. We look for "FAT12 " or "FAT16 ".
- */
- if ((strcmp ((char *)tmpType, DOS_BOOT_FSTYPE_FAT12)) == 0)
- {
- pVolDesc->fatType = FAT12;
- printf("WARNING: FAT12 indicated by BPB FSTYPE string, "
- "cluster calculation was FAT16. Honoring string.n");
- }
- else
- {
- pVolDesc->fatType = FAT16;
- }
- }
- /* volume Id and label */
-
- pVolDesc->volIdOff = DOS_BOOT_VOL_ID;
- pVolDesc->volLabOff = DOS_BOOT_VOL_LABEL;
- }
- else /* Use FAT32 because (pVolDesc->secPerFat == 0) */
- {
- pVolDesc->fatType = FAT32;
-
- /* sectors per fat copy */
-
- pVolDesc->secPerFat = DISK_TO_VX_32( bootSec +
- DOS32_BOOT_SEC_PER_FAT );
- if( pVolDesc->secPerFat == 0 )
- {
- dosFsBadBootMsg( 1, DOS32_BOOT_SEC_PER_FAT, 0,
- "(FAT32)", __LINE__ );
- goto error;
- }
-
- /* volume Id and label */
-
- pVolDesc->volIdOff = DOS32_BOOT_VOL_ID;
- pVolDesc->volLabOff = DOS32_BOOT_VOL_LABEL;
- }
-
- /*
- * count sector number of data area start cluster.
- * This value can be corrected later by directory handler, if
- * root directory is not stored as regular directory
- * in clusters (FAT32), but instead resides contiguously
- * ahead first data cluster (FAT12/FAT16)
- */
- pVolDesc->dataStartSec = pVolDesc->nReservedSecs +
- pVolDesc->secPerFat * pVolDesc->nFats;
-
- /* volume Id and label */
-
- pVolDesc->volId = DISK_TO_VX_32( bootSec + pVolDesc->volIdOff );
- bcopy( (char *)bootSec + pVolDesc->volLabOff,
- (char *)pVolDesc->bootVolLab, DOS_VOL_LABEL_LEN );
- *(pVolDesc->bootVolLab + DOS_VOL_LABEL_LEN) = EOS;
-
- /* restore base version of boot sector */
- if( pVolDesc->bootSecNum != DOS_BOOT_SEC_NUM )
- {
- ERR_MSG( 1, "Try to reclaim original copy of boot sectorn",
- 0,0,0,0,0,0 );
- if( (pass == 0) &&
- (cbioBlkCopy(pVolDesc->pCbio, pVolDesc->bootSecNum,
- DOS_BOOT_SEC_NUM, 1 ) == OK) &&
- (cbioIoctl(pVolDesc->pCbio,
- CBIO_CACHE_FLUSH, (void *)(-1) ) == OK))
- {
- /* remount again */
- pass ++;
- pVolDesc->bootSecNum = DOS_BOOT_SEC_NUM;
- goto boot_get;
- }
- }
-
- /* currently it is enough for starting */
-
- return (OK);
-
- error: /* some data is inconsistent */
- pVolDesc->bootSecNum ++;
- if( pVolDesc->bootSecNum < (u_int)DOS_BOOT_SEC_NUM +
- cbioParams.blocksPerTrack )
- {
- /* try to find other boot block copy on next sector */
-
- if( pVolDesc->bootSecNum == DOS_BOOT_SEC_NUM + 1 )
- {
- ERR_MSG( 1, "Problem finding volume data, trying to "
- "use the next block as boot block.n",
- 0,0,0,0,0,0 );
- /*
- * SPR#69074 - only look at the next sector, this avoids
- * tons of error messages when mounting unformatted device
- * when we used to look at the remaining sectors in the track.
- */
- goto boot_get;
- }
- ERR_MSG( 1, "Ensure this device is formatted and partitions are "
- "properly handled.n",
- 0,0,0,0,0,0 );
- }
- if( errnoGet() == OK )
- errnoSet( S_dosFsLib_UNKNOWN_VOLUME_FORMAT );
- return ERROR;
- } /* dosFsBootSecGet() */
- /*******************************************************************************
- *
- * dosFsVolIsFat12 - determine if a MSDOS volume is FAT12 or FAT16
- *
- * This routine is the container for the logic which determines if a
- * dosFs volume is using FAT12 or FAT16. Two methods are implemented.
- * Both methods use information from the volumes boot parameter block
- * fields found in the boot sector.
- *
- * The first FAT determination method follows the recommendations outlined
- * in the Microsoft document:
- *
- * "Hardware White Paper
- * Designing Hardware for Microsoft