Full Disk Information

By far the most widely used storage mediums are the floppy disks and the fixed disks (hard disks). Floppy disks and hard disks come in various sizes and capacities but they all work basically in the same way: information is magnetically encoded on their surface in patterns. These patterns are determined by the disk drive and the software that controls the drive.
Although the type of storage device is important, it is the way the stored information is laid out and managed that concerns programmers most. In this chapter we would therefore focus our attention on how information is organized and stored on the disk.


The Disk Structure

As most of us know, the disk drives in DOS and Windows are organized as zero-based drives. That is, drive A is drive number 0, drive B is drive number 1, drive C is drive number 2, etc. The hard disk drive can be further partitioned into logical partitions. Each drive consists of four logical parts: Book Sector, File Allocation Table (FAT), Directory and Data space. Of these, the Boot Sector contains information about how the disk is organized. That is, how many sides does it contain, how many tracks are there on each side, how many sectors are there per track, how many bytes are there per sector, etc. The files and the directories are stored in the Data Space. The Directory contains information about the files like its attributes, name, size, etc. The FAT contains information about where the files and directories are stored in the data space. Figure 1 shows the four logical parts of a 1.44 MB disk.


Figure 1. Logical structure of a 1.44 MB disk.

When a file /directory is created on the disk, instead of allocating a sector for it, a group of sectors is allocated. This group of sectors is often known as a cluster. How many sectors together form one cluster depends upon the capacity of the disk. As the capacity goes on increasing, so also does the maximum cluster number. Accordingly, we have 12-bit, 16-bit or 32-bit FAT. In a 12-bit FAT each entry is of 12 bits. Since each entry in FAT represents a cluster number, the maximum cluster number possible in a 12-bit FAT is 212 (4096). Similarly, in case of a 16-bit FAT the maximum cluster number is 216 (65536). Also, for a 32-bit FAT the maximum cluster number is 228 (268435456. Only 28 of the 32 bits are used in this FAT). All FAT systems are not supported by all versions of Windows. For example, the 32-bit FAT system is supported only in Win 95 OSR2 version or later. There are differences in the organization of contents of Boot Sector, FAT and Directory in FAT12/ FAT16 system on one hand and FAT32 on the other. Some of these differences are discussed below.


The Boot Sector

The boot sector contains two parts: ‘Boot Parameters’ and ‘Disk Bootstrap Program’. The Boot Parameters are useful while performing read/write operations on the disk. Tables number 1 and 2 shows the break up of the boot parameters for the 12-bit and the16-bit FAT systems along with their typical values. As you can observe from Tables 1 and 2, the boot parameters basically contain information indicating how the disk has been organized. Apart from the first 11 bytes in the list of boot parameters, the rest form the ‘BIOS Parameter Block’ (BPB). The entry ‘Maximum root directory entries’ indicates how many maximum files can we create in the root directory of the disk. If one of these entries is a sub-directory name then there can be several more files and sub-sub-directories within it. The ‘Media descriptor’ byte indicates how the operating system refers to a particular type of disk internally. For example, a 1.44 MB disk has a media descriptor F0, whereas, a hard disk has a media descriptor F8.

Description

Length

Typical Values

Jump instruction

3

EB3C90

OEM name

8

MSWIN4.1

Bytes per sector

2

512

Sectors per cluster

1

1

Reserved sectors

2

1

Number of FAT copies

1

2

Max. root directory entries

2

224

Total sectors

2

2880

Media descriptor

1

F0

Sectors per FAT

2

9

Sectors per track

2

18

No. of sides

2

2

Hidden sectors

4

0

Huge sectors

4

0

BIOS drive number

1

0

Reserved sectors

1

0

Boot signature

1

41

Volume ID

4

349778522

Volume label

11

ICIT

File system type

8

FAT12

Table 1. Boot parameters of 12-bit FAT.

Description

Length

Typical Values

Jump instruction

3

EB3C90

OEM name

8

MSWIN4.1

Bytes per sector

2

512

Sectors per cluster

1

64

Reserved sectors

2

1

Number of FAT copies

1

2

Max. root directory entries

2

512

Total sectors

2

0

Media descriptor

1

F8

Sectors per FAT

2

256

Sectors per track

2

63

No. of sides

2

255

Hidden sectors

4

63

Huge sectors

4

1492902

BIOS drive number

1

128

Reserved sectors

1

1

Boot signature

1

41

Volume ID

4

4084677574

Volume label

11

ICIT

File system type

8

FAT16

Table 2. Boot parameters of 16-bit FAT.

Earlier, the BPB comprised of entries only up to the entry ‘Hidden sectors’ in Table 1. For example, it didn’t contain the ‘Volume label’ of the disk. This used to get stored as a directory entry in the directory sectors. However, now the BPB has been extended to include entries like ‘Volume label, ‘Volume ID’, ‘File system type’, etc. The BPB is now called ‘Extended BPB’. Whether the boot sector contains BPB or Extended BPB is indicated by the value of the entry ‘Boot signature’. If it contains a value 29h then the disk has an Extended BPB.

The ‘Volume ID’ entry is the serial number that is recorded on the disk while formatting it. This is the same number that is shown when we run the ‘Dir’ command on the disk.

A 1.44 MB disk supports a 12-bit FAT system. The total number of sectors on this disk is 2880. This value is stored in the entry ‘Total Sectors’. There is another entry in the boot parameters called ‘Huge Sectors’. For a 12-bit FAT System, this entry has a value 0. For bigger capacity hard disks the value of total number of sectors would be much more than what can be accommodated in the 2-byte entry called ‘Total Sectors’. Hence for such disks this entry contains a value 0 and the total number of sectors value is stored in the entry ‘Huge Sectors’.

Let us now take a look at the 32-bit FAT system’s boot sector contents. These are shown in Table 3.

Description

Length

Typical Values

Jump instruction

3

EB5890

OEM name

8

MSWIN4.1

Bytes per sector

2

512

Sectors per cluster

1

8

Reserved sectors

2

51

Number of FAT copies

1

2

Root directory entries

2

0

Total sectors

2

0

Media descriptor

1

F8

Sectors per FAT

2

0

Sectors per track

2

63

No. of sides

2

255

Hidden sectors

2

63

High word of hidden sectors

2

63

Huge sectors

2

1492902

High word of huge sectors

2

1492902

Sectors per FAT

2

4095

High word of sectors per FAT

2

4095

Drive description flag

2

0

File system version

2

0

Root directory starting cluster

2

2

High word of root directory Starting cluster

2

2

File system information sector

2

1

Back up boot sector

2

6

Reserved

6

0

BIOS drive number

1

128

Reserved

1

0

Boot signature

1

41

Volume ID

4

649825316

Volume label

11

ICIT

File system type

8

FAT32

Table 6-3. FAT32 Boot Sector.

There are significant changes in the contents of the boot sector of a 32-bit FAT system. The entries ‘Number of hidden sectors’ and ‘Huge sectors’ have now been made 4-byte entries. The first two bytes contain the low word of the value, whereas, the next two bytes contain the high word value. The actual value of the entry can be obtained by using bit wise operators as shown below:

Total number of sectors = high word of huge sector << 16 + huge sectors

The number of sectors per FAT in a 32-bit file system is likely to exceed what can be accommodated in two bytes. Hence the entry ‘Sectors per FAT’ for a disk with a 32-bit file system would typically have a value 0 . The value of ‘Sectors per FAT’ is now stored as a 4-byte entity, with the similar arrangement of low word and high word as discussed earlier.

The boot sector of a 32-bit FAT system also has new entries like ‘Drive description flag’, ‘File system version’ ‘Starting cluster number of the root directory’, ‘Sector number of the file system information sector’, and the sector number of the ‘Backup boot sector'.

The ‘Drive description flag’ is a two-byte entity. Bit 8 of this flag indicates whether or not the information written to the active FAT will be written to all copies of the FAT. The low four bits of this entry contains the 0-based FAT number of the active FAT. These bits are meaningful only if bit 8 is set.

In the entry ‘File system version number’ the high byte contains the major version number, whereas, the low byte contains the minor version number.

The entry ‘File system information sector’ contains a value indicating the sector number where the file system information is present. This file system information consists of the fields shown in Table 4.

Description

Length

File system signature

4

Total number of free clusters

4

Sector number of the next free cluster

4

Reserved

6

Table 4. Contents of file system information sector.

The entry ‘File information sector’ contains a value OFFFFh if there is no such sector. The entry ‘Backup boot sector’ contains a value 0FFFFh is there is no backup boot sector. Otherwise this value is any non-zero value less than the reserved sector count.

The Root Directory

The root directory of a 1.44 MB disk containing a 12-bit FAT system occupies 14 sectors. For other FAT systems this number may vary. The directory sectors contain 32-byte entries for various files/sub-directories present in the root directory. Since each entry is of 32 bytes, one directory sector can accommodate 16 such entries. As there are 14 directory sectors on a 1.44 MB disk, there can be a maximum of 224 entries (16 *14) in the root directory. This value is stored in one of the boot parameters.

Each 32-byte entry contains either a filename or a sub-directory name. If it is a file entry then it contains information about file’s name, its size, attributes, starting location on the disk, etc. This information is organized as shown is Table 6-5.

Description

Length

Filename

8

Extension

3

Attributes

1

Reserved

1

Number of 10 msec intervals in 2 seconds

1

Creation time

2

Creation date

2

Last access date

2

High word of starting cluster number

2

Last modification time

2

Last modification date

2

Starting cluster of file/directory

2

File size

4

Table 5. Contents of a directory entry.

Note the following points.
  1. File name can be less than or equal to 8 characters. If it is less than 8 characters long then it is padded with blanks on the right. Since Windows 95 we are permitted to use long file names. How these are accommodated is a 32-byte entry is discussed in the next section.
  1. Extension or the family name can be maximum 3 characters long. If it less than 3 characters it is padded with blanks. While a filename must have at least one character, the extension can be all blanks.
  2. In the attribute byte each bit represents either the type of the file or whether the entry is a sub-directory entry. The meaning of each bit is given in Figure 2.


Figure 2. Contents of the attribute byte.

If bit 0 is set to 1 then the file can only be read, it cannot be modified or deleted.

If bit 1 is 1 then the file is hidden and will not be shown in the directory. Vice versa, if bit 1 is 0, then the file will be shown in the directory as a normal file.

If bit 2 is 1, it means the file is an operating system file. Bit 4 identifies the entry as a sub-directory name.

Bit 5 is called an archive bit. It helps avoid taking backups of files that have already been backed up. OS sets the archive bit to 1 whenever a file is created or modified. On backing up this file using the BACKUP command, its archive bit is set to 0. Suppose we do not modify this file before we take the next backup. So its archive bit would remain 0. If we now take a backup this file won’t be backed up (saving precious time) since its archive bit is still 0. However, if we modify the file before the next backup, its archive bit would be set to 1 and would now qualify for backing up.

  1. Following the attribute byte, there are 6 unused bytes set aside for possible future use. All six bytes are usually set to 0.
  2. Each directory entry maintains three dates—date of creation, date of modification and date of last access. There are also entries showing time of creation or last modification. All these entries occupy 2 bytes each. Suppose the date of creation of a file is 10/12/99 and time of creation is 10:30:45. Such a date and time can be stored as two strings of 8 bytes each. However, in the 32-byte entry (refer Table 6-5) these are shown to occupy 2 bytes each. How do these 8-byte strings get converted into 2-byte entries? The operating system applies formulae on the date and time to reduce them to two byte entities. The conversion formula for the date is given below:

  3. Date = 512 * ( year – 80 ) + 32 * month + day

    Suppose a file is created on 09/03/99 then on conversion the date will be

    Date = 512 * ( 99 – 80 ) + 32 * 3 + 9 = 9833

    The binary equivalent of 9833 is 10011001101001. The binary value is placed in the date field in the directory entry of the file. If you observe carefully, in this binary number the distribution of year, month and day is as shown in Figure 3.





    Figure 3. Bit distribution of the date field.

    Just to verify this bit distribution let us take the bits representing the month, i.e. 0011. This binary value when converted to decimal gives 3. On similar lines you can verify the values of the year and the day fields shown in Figure 3.

    The distribution for the time field is shown in Figure 4.





    Figure 4. Bit distribution of the time field.

  4. The starting cluster number indicates the place where the file begins on the data space of the disk. This field occupies 2 bytes in the 32-byte directory entry. In a 32-bit FAT system the starting cluster number is likely to be bigger than what can be accommodated in a 2-byte entry. Hence for 32-byte FAT system the starting cluster number entry is broken up into two parts of two bytes each. One part is stored in the ‘Starting cluster number’ field. The other part is stored in the 2-byte field immediately following the field ‘Last access date’. This field contains the high word of the starting cluster number.
  5. The file size field contains the exact file size in bytes.


Long Filenames

Since the advent of Windows 95 the barrier of 8.3 based filenames has been lifted. Hence now we can use filenames like ‘This is my seminar file’. This is all right from the point of view of convenience, but how do we accommodate such long filenames in a 32-byte entry? While storing long filenames, the name is distributed over multiple 32-bytes entries. The last of these entries contains a short filename (alias). For example, if the long filename is ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.TXT’, then out of the several 32-byte entries that would be required to accommodate such a name the last entry would contain ‘ABCDEF~1.TXT’. Let us now understand what is present in other entries except the last. The breakup of these entries is shown in Table 6.

Description

Length

Sequence byte

1

Unicode characters of name

10

Attributes

1

Long entry type

1

Checksum for matching short name

1

More Unicode characters of name

12

Reserved

2

More Unicode characters of name

4

Table 6. Contents of a long directory entry.

As can be seen from Table 6 every 32-byte entry required for a long filename can accommodate 13 characters ((10 + 12 + 4) / 2 ). These characters are stored as Unicode characters and not as ASCII characters. Thus, to accommodate filename ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.TXT’ we would need 40/13, i.e. 4 entries (one more entry would be required for a short file name (alias of the filename). The contents of these entries are shown in Figure 5.

T

FFFF

FFFF

12345

67890.

TX

NOPQR

STUVWX

YZ

ABCDE

FGHIJK

LM

ABCDEF~1

TXT

Figure 5. Entries of a long directory name.

While displaying the directory containing a long filename how would the OS come to know over how many 32-bytes entries is the filename distributed? This is simple to determine. All the entries belonging to a long filename would have in their attribute byte a value Fh. Also, all the entries belonging to this filename would have the same check sum. In our case, the sequence byte of the four directory entries required for the filename would have the values D, 3, 2 and 1 respectively. Note that the first of these values is obtained by ORing it with 64.

What is Unicode?

Windows 3.1 operating system used a code-page model to represent characters. Each code page was limited to 256 characters. Each character was 8-bits long. The US and Western European versions of Windows 3.1 used code page 1252. There were other code pages available for Greek, Turkish, Thai, Arabic, Hebrew, etc. The code-page model became more complex for languages like Japanese, Korean, Chinese, etc. since these languages used a double-byte character set. In this system some characters were represented by 1-byte values and the rest by 2-byte values. Using and programming a code page was complex if we were to use characters from different code pages in our application. Also, it was not possible to support a language for which there was no code page.

To overcome these difficulties a ‘Unicode’ model was suggested. In this model each character is represented by a 16-bit code. This code covers majority of world’s written scripts, publishing characters, technical symbols, geometric shapes, etc. In addition to modern languages, Unicode covers languages like literary Chinese, Classical Greek, Hebrew, Pali and Sanskrit. Windows 98 and its predecessors used 8-bit ANSI character set, which is similar to the ASCII character set familiar to many programmers. Windows NT and Windows 2000 use the 16-bit UNICODE character set which is a super set of ANSI character set. Windows NT and Windows 2000 supports programs compiled with ANSI character set. However, these applications would run slower because Windows NT and Windows 2000 have to perform ANSI-to-Unicode conversion. Unicode applications won’t run on Windows 95 and Windows 98, unless you convert every character string from Unicode to ANSI format.

Using Unicode character set is simple. If we use the string "Nagpur" the compiler uses ANSI character set. To make the compiler use the Unicode character set we should simply precede the string with a L as shown below:

L"Nagpur"

Alternatively, we can use MFC’s _T macro, like this:

_T ( "Nagpur" )

The compiler will now use Unicode characters if the preprocessor symbol _UNICODE is defined, and ANSI characters if it is not. This way we can make our application character-set neutral. Additionally to keep them neutral we must do the following:

continued…

…continued

Declare characters to be of type TCHAR rather than char. If the _UNICODE symbol is defined, TCHAR evaluates to wchar_t, which is a 16-bit Unicode character. If _UNICODE is not defined TCHAR becomes a simple char.

Use TCHAR * instead of char * while declaring pointers to strings. Or, better still, use LPTSTR (pointer to TCHAR string).

Don’t assume that one character is equal to one byte. To convert a buffer length expressed in byte to a buffer size in characters divided the buffer length by size of (TCHAR ).

Replace calls to string functions like strcpy( ), strlen( ) with the corresponding macros from the windows header file ‘TCHAR.H’.

Given Below is the code that uses the ANSI character set:

char str[ ] = "Nagpur";

d.TextOut ( 10,10, str, size of ( str ) ) ;

int l = strlen ( str ) ;

Let us now convert the same code to make it character-set neutral. Here is how the code would look like…

TCHAR str[ ] = _T ( "Nagpur" ) ;

d.TextOut ( 10, 10, str, sizeof ( str ) / sizeof ( TCHAR ) ;

int l = _tcslen ( str ) ;

Note that this code uses a more general TCHAR data type. Also, it makes no assumptions about the size of the character and uses the TCHAR compatible string length function _tcslen( ) in place of the ANSI character set-dependent strlen( ).

The Data Space

All files and sub-directories are stored in this area. It occupies the last and largest part of each disk. OS allocates space to files one cluster at a time. Remember that a cluster is nothing but a group of sectors that OS allocates to a file at a time. How many sectors, form a cluster depends upon how the disk has been formatted.

As a file is being created, or an existing file is extended, the file’s allocated space grows. When more space is needed, OS allocates another cluster for the file.

Under ideal conditions a file is stored under one contiguous block of space. However, a file might be broken into several non-contiguous blocks. This happens if information is added to an existing file or a new file is stored in the space left by an erased file. So don’t be surprised if one file is scattered throughout the disk. This file fragmentation slows down the access to the file’s contents to some degree.

The File Allocation Table

The File Allocation Table (FAT) maps the usage of the data space of the disk. It contains information about the space used by each individual file, the unused disk space and the space that is unusable due to defects in the disk. Since FAT contains vital information, two copies of FAT are usually stored on the disk. In case one gets destroyed, the other can be used. A typical FAT entry can contain any of the following:

  • Unused cluster
  • Reserved cluster
  • Bad cluster
  • Last cluster in the file
  • Next cluster number in the file

There is one entry in the FAT for each cluster in the file area. If the value in a FAT entry doesn’t mark an unused, reserved or defective cluster, then the cluster corresponding to the FAT entry is part of a file, and the value in the FAT entry would indicate the next cluster in the file.

This means that the space that belongs to a given file is mapped by a chain of FAT entries. Each FAT entry points to the next entry in the chain. The first cluster number in the chain is the starting cluster number in the file’s directory entry. When a file is created or extended, a cluster is allocated to the file by searching the FAT for unused clusters and adding them to the chain. Vice versa, when a file is deleted, the cluster that has been allocated to the file is freed by clearing corresponding FAT entries (by setting them to 0). The FAT chain for a file ends with an entry FFFFh in the FAT. Figure 6 shows a FAT chain for a file called ICIT.PRG.


Figure 6. File Allocation Table.

This file occupies cluster number 3, 5, 6 and 8 on the disk. Hence the starting cluster number in the directory entry for the file is 3. Suppose this file is to be loaded into memory then OS would first load starting cluster number—3’s contents into memory. To find out the next cluster belonging to this file OS looks at entry number 3 in FAT where it finds a value 5. Therefore, now it loads the contents of cluster number 5 into memory. Once again OS looks at the FAT and finds in entry number 5 a value 6, hence it loads the contents of cluster 6 into memory. This process goes on till the OS finds an entry FFFFh in FAT, which indicates that there are no more clusters belonging to the file. Hence the process stops.

Now that we have understood how the FAT chain is traversed, let’s dig a little deeper into the FAT. The entries present in FAT are 12, 16 or 32 bits long depending on the storage capacity of the disk. Though a 12-bit FAT can handle 4096 clusters only 4078 clusters are available for use since some values are reserved. Similarly, for a 16-bit FAT out of the possible 65536 clusters that it can handle only 65518 are available for use.

In a 12-bit FAT three bytes form two entries. The first two entries (0 and 1) in the FAT are rese8 raved for use by the OS. This means that first 3 bytes in a 12-bit FAT, first 4 bytes in 16-bit FAT and first bytes in a 32-bit FAT are not used for storing cluster numbers. Out of these 3 (or 4, or 8) bytes, the first byte is the media descriptor byte and the balance contain the value FFh. These balance bytes remain unused. The media descriptor byte specifies the type of the disk. It typically has a value FDh, F9h, F0h, F8h for a 360 KB, 1.2 MB, 1.44 MB and a hard disk respectively. The contents of a FAT entry are interpreted as shown in Table 7.

Values

Meaning

12-bit

16-bit

32-bit

000h

0000h

0000000h

Cluster available

FF0h–FF6h

FFFFh–
FFFF6h

FFFFFFFh–FFFFFF6h

Reserved cluster

FF7h

FFF7h

FFFFFF7h

Bad cluster if not part of chain

FF8h–FFFh

FFF8h–
FFFFh

FFFFFF8h–FFFFFFFh

Last cluster of file

xxx

xxxx

xxxxxxx

Next cluster in file

Table 7. Meaning of FAT entries.

As we saw earlier, two identical copies of FAT are maintained on the disk. All copies are updated simultaneously whenever files are modified. If access to a FAT fails due to a read error, the OS tries the other copy. Thus, if one copy of the FAT becomes unreadable due to wear or a software accident, the other copy may still make it possible to salvage the files/directories on the disk.

Since we want to display the contents of boot sector, FAT and directory systematically we must create structures to represent the various elements of these logical parts of the disk. To this effect we have created the structures given in Table 8.

Structure name

Size

Purpose

DIOC_REGISTERS

28

To set up values in CPU registers while issuing an interrupt

DISKIO

10

To store the starting sector number and number of sectors to read

Boot

512

To store the contents of the boot sector of a 12-bit or a 16-bit FAT system

Boot32

512

To store the contents of the boot sector of a 32-bit FAT system

Bigfatbootfsinfo

24

To store the file system information like total number of clusters, number of the next free cluster, etc.

Directory

32

To store information about file or sub-directory

Longdirectory

32

To store information about a long filename

Table 8. Various structures used in the program.

The #pragma pack directive

Some programs need to exercise precise control over the memory areas where data is placed. For example, suppose we wish to read the contents of the boot sector into a structure. For this the byte arrangement of the structure elements must match the arrangement of various fields in the boot sector of the disk. The
#pragma pack directives offer a way to fulfill this requirement. The #pragma pack directive specifies packing alignment for structure and union members. The pragma takes effect at the first structure or union declaration after the pragma is seen.

Consider the following structure:

#pragma pack (1)

struct emp
{

int a ;
float s ;
char ch ;

} ;

#pragma pack( )

Here, #pragma pack ( 1 ) lets each structure element to begin on a 1-byte boundary. Hence the size of the structure will be 9. (int - 4, float - 4, char - 1). If we use #pragma pack ( 2 ) each structure element can begin on a 2-byte boundary. Hence the size of the structure will be 10. (int - 4, float - 4, char - 2). If we use #pragma pack ( 8 ) the size of the structure will be 12 bytes (int - 4, float - 4, char - 4). Think why is this so?

On executing the program a menu appears showing two menu items ‘Drive A’ and ‘Drive C’. The ‘File’ menu contains several submenu items.

On selecting the drive (either ‘A’ or ‘C’) the contents of the boot sector, FAT and root directory are displayed for that drive. Using the ‘File’ menu we can either print this information on the printer or save it on the disk.

Reading Sectors

In MS-DOS, reading sectors from a disk was a simple matter of issuing interrupt 13h or interrupt 25h. Alternatively, we could also use standard library functions like absread( ) and biosdisk( ) (in Turbo C, Turbo C++). Other compilers had equivalent library functions with different names. Reading disk sectors in Windows 95 is quite different. In Windows 95 interrupt 13h hooks are broken. Reading disk sectors in Windows 95 involves an ‘open’ and ‘read’ approach. We have to open a VXD through a call to :CreateFile( ) and then do the reading through ::DeviceIoControl( ) (similar to ioctl( )).:

The disk class contains member functions that can read the information of boot sector, FAT and directory. The mydoc class contains a disk object as its private data member. When the framework creates the document object the disk object would also come into existence. When the disk object is built its constructor gets called. In the constructor we have called the ::CreateFile( ). The CreateFile( ) function opens the file ‘VWIN32.VXD’. While opening a VXD, we must specify the name in the following form:

\\.\VXDName

If the VXD exists and it supports the device IOCTL interface, the function returns a device handle. Although ::CreateFile( ) needs several parameters, only the name of the file and the flags parameters are useful while opening a VXD. Hence for all other parameters we have passed either a 0 or a NULL. The flag FILE_FLAG_DELETE_ON_CLOSE ensures that the operating system removes the VXD from memory when we close the last instance of the VXD. This flag is relevant if we open a dynamically loadable VXD. Static VXDs cannot be removed from memory.

The device handle returned by ::CreateFile( ) is used in subsequent calls to the ::DeviceIoControl( ) function. These calls are made from three member functions of the disk class— getmediaid( ), readabsolutesectors( ) & readabsolutesectors32( ). These functions are called when we select ‘Drive A’ or ‘Drive C’ from the menu. Each of these functions first fill the CPU registers with appropriate values and then calls the ::DeviceIoControl( ) function. The meaning of the values set up in the CPU registers is given in Table 10.

Function

Description

getmediaid( )

Calls the MS-DOS device I/O control function (Interrupt 21h Function 440Dh)

Registers

eax = Service number

ebx = Drive number (A=1, B=2, etc.)

ecx = Get Media ID command

edx = Pointer to buffer

flags = Assume error (set carry flag)

readabsolutesectors( )

Performs the Absolute Disk Read command (Interrupt 25h)

Registers

eax = Drive number (A=0, B=1, etc.)

ebx = Pointer to buffer

ecx = If set to FFFFh, it means a structure is being used to specify starting sector and number of sectors to read

flags = Assume error (set carry flag)

readabsolutesectors32( )

Performs the Extended Absolute Read Command (Interrupt 21h, Function 7305h)

Registers

eax = Service number

ebx = Pointer to buffer

ecx = If set to FFFFh, it means a structure is being used to specify starting sector and number of sectors to read

edx = Drive number (A=1, B=2, etc.)

esi = 0 (read operation)

flags = Assume error (set carry flag)

Table 10. Services used for reading sectors.

In the functions readabsolutesectors( ) or readabsolutesectors32( ) the ecx register is set to FFFFh. This indicates that the starting sector number, number of sectors to read and the buffer would be provided by a separate structure. This structure is shown below:

struct DISKIO
{

DWORD startsector ;
WORD sectorsnum ;
DWORD buff
;

} ;

A pointer to this structure is set up in the ebx register.

The disk member functions getmediaid( ), readabsolutesectors( ) and readabsolutesectors32( ) are called from the myview class’s member functions. To facilitate these calls we have set up the address of the disk object in the pds pointer in the myview::OnInitialUpdate( ) function.

Displaying Boot Sector And FAT Information

Once the information about the boot sector and root directory is read it is displayed on the screen using functions display_media_id( ), display_boot_info( ) and display_dir_info( ). For a 32-bit FAT, for displaying the boot sector information the function display_boot_info_32( ) is called.

Each disk contains two copies of FAT. In the function myview::fat_info( ) the starting sector of each copy of FAT is determined. Next, the function read_fat_info( ) is called for reading and displaying contents of each FAT copy. Since each copy contains several entries, we have displayed only the first 16 entries for a 12-bit & 16-bit FAT and first 64 entries for a 32-bit FAT. The organization of the FAT types is shown in Figure 8.

12-bit FAT


8 bits

8 bits

8 bits
















E2 E3 O3 E1 O1 O2


16-bit FAT


8 bits

8 bits

8 bits

8 bits




















E3 E4 E1 E2 O3 O4 O1 O2


32-bit FAT


8 bits

8 bits

8 bits

8 bits

8 bits

8 bits

8 bits

8 bits

































E7 E8 E5 E6 E3 E4 E1 E2 O7 O8 O5 O6 O3 O4 O1 O2

Figure 8. Organization of different FAT systems.

For a 32-bit FAT the seven nibbles (a nibble is a group of 4 bits) E1-E2-E3-E4-E5-E6-E7-E8 form the even entry. Note that the arrangement of these nibbles is E7-E8-E5-E6-E3-E4-E1-E2 because the lower byte is always stored in memory earlier than the higher byte. This means if the value of the 4-byte FAT entry is ABCD, it would be stored as DCBA. The odd entry is represented using the set of nibbles O1-O2-O3-O4-O5-O6-O7-O8. In reality the nibble E8 and O8 don’t contribute to the cluster number since each entry in the 32-bit FAT is only 28 bits long.

On similar lines in a 16-bit FAT the four nibbles E1-E2-E3-E4 form the even entry whereas the set O1-O2-O3-O4 form the odd entry. Similarly, the even and odd entries in a 12-bit FAT are formed by E1-E2-E3 and O1-O2-O3 respectively. Picking up the values present in odd or even entries from a 32-bit FAT or a 16-bit FAT a relatively simple job. However, to pick up the values from a 12-bit FAT we have to use bit wise operators to discard one nibble out of a group of 4 nibbles. This is done in our program through the functions getfat_12( ).

The file system information is displayed only for a 32-bit FAT system using the function file_sys_info( ).

Displaying Directory Information

In Widows 95 each directory entry contains three dates and two times—creation, access & modification dates and creation and modification times. Each of these are stored as a 2-byte integers. For the bit distribution of these integers refer Figure 3 and 4.

For displaying the various fields of a directory entry it is first ascertained whether the disk uses a 32-bit FAT or not. This is necessary because the calculation of sector number where the directory begins is different for a 32-bit and a non-32-bit FAT. Once this is determined either readabsolutesectors( ) or readabsolutesectors32( ) is called. Once the information about the directory is read its contents should be displayed. While doing so it is first checked whether the entry represents a deleted file (a deleted file begins with a character E5h). If not, then it is ascertained whether the entry represents a short or a long file name. Accordingly, the values in the various fields are accessed and displayed. For understanding the logic for displaying long file names please refer to the discussion on long file names in the earlier section of this chapter.

The Actual Display

On careful observation you can realize that in functions display_media_id( ), display_boot_info( ), display_dir_info( ), display_boot_info_32( ) and read_fat_info( ) we didn’t actually display the information present in boot sector, directory, and FAT. Instead, we merely built strings representing each item of information and kept on adding it to the list of strings being maintained by a CStringList object. To carry out this addition we used the function CStringList::AddTail( ). We didn’t display the strings immediately for the following reasons:

  1. Imagine a situation where we display the strings and then minimize the window. On maximizing the window the displayed information would vanish. If we want it again we will have to regenerate it by reading it from the disk again.
  2. If we display the information and then want to print it on the printer or save it to a file we would not be able to do so unless we have all the strings available with us.
  3. Since the information displayed is more than one screen-full we need to provide a scrolling facility. When we scroll the screen, unless we have the strings available with us we would not be able to repaint them on the screen.
  4. While printing the information on the printer unless we have all the strings available we would not be able to decide the number of pages required for printing.
  5. For proper serialization and de-serialization we need to have all the strings available with us.

Once we have read all the information from the disk and stored it in a CStringList object we need to display it in the scrollable view window. To achieve this we have simply called the function Invalidate( ). This raises the WM_PAINT message which is fielded by the OnDraw( ) function. The OnDraw( ) function simply outputs the strings present in the CStringList object. Before invalidating the view we need to set the vertical scroll size. For this we have called the function setview( ), which in turn calls SetScrollSizes( ). The scroll size expectedly depends upon the number of strings present in the string list.

Serializing The Information

This is one of the simplest job in this program. We simply have to mention one line in mydoc :: Serialize( ):

lines.Serialize ( ar ) ;

Here, lines being a CStringList object it gets serialized automatically through CStringList::Serialize( ) function.

No comments: