Está en la página 1de 7

The Partition Table

Written by Andrew Pitonyak

Introduction
I was becoming annoyed that I could not access my FAT32 partition from within OS/2 and I decided that I would write a driver that would allow me to do this. I have not come close to accomplishing this but Henk Kelder has, so if you have any interest in this then check out his beta driver at http://ourworld.compuserve.com/homepages/hkelder/. I did, however, start poking around my hard drive and studying my partition tables. I would like to say that this was an easy process, but it was not, so I decided to share my experiences with others to hopefully save them the aggravation that I faced. Another good resource can be found at http://www.uruk.org/~erich/, look for the GRUB stuff by Erich Boleyn ( erich@uruk.org). [Note: here is a link to the source code for this article. Ed.]

Reading The Partition Table


Before worrying about interpreting the partition table, I had to be able to read it. After browsing through the online help which came with the Borland compiler, I opted to use DosDevIOCtl() category 9, function 0x64h, read sectors. The call is essentially
DosDevIOCtl(handle, 9L, 0x64, parmList, parmSize, &szParam, data, szData, &szDataU);

I had some difficulties dealing with the parameter list so I encapsulated it in the class IOCtlParam. The structure is as follows:
Size Byte Word Word Word Word Bytes Description Command Information Head Cylinder First Sector Number of Sectors Track Layout Tables

The command information must be entirely zero except for the first bit where one means that the sectors are stored consecutively starting with sector one and a zero means that at least one of these conditions is not true. I must admit that I have no idea how to determine what the track layout is, and I always assume that the sector size is 512 bytes. Although these values have worked on every system which I have

size is 512 bytes. Although these values have worked on every system which I have tried, the existence of the variable parameters implies to me that this is not a safe assumption. Further information on this will be gladly accepted but this is not the thrust of this article. My initialization code is similar to the following:
command = 1; // Assume consecutive head = 0; cylinder = 0; firstSector = 0; numSectorsToDo = 1; for (WORD i=0; i<numSectors(); ++i) { bytes[2*i] = i+1; bytes[2*i+1] = 512; }

Note that the DosDevIOCtl function does not move the heads so you can not read more than a single track worth of information with one command. The primary boot partition is located at head zero, cylinder zero, sector zero which is the first sector on the drive.

The Partition Table


So now it is time to read the partition table. The program which I wrote, contains the ability to dump physical sectors on the hard drive. Note that physical drives are accessed with the numbers where drive 1 refers to the first physical drive, usually drive C:. The parameters to obtain a dump include "dump <physical drive> <first sector> <number of sectors>". To see the partition table, type the following:
[d:\myos2\devsrc\myprogs\drive\edm]driveinf DUMP 1 0 1 Drive 1: sector 0 0 - fa b8 30 00 8e d0 bc 00 01 fb 10 - 8e c0 be 00 7c bf 00 7e b9 00 20 - 50 c3 be cf 7e bb be 7f 80 7f <... data deleted ...> 190 - 7c bf 05 00 b8 00 00 cd 13 b8 1a0 - 4f 7f f1 c3 00 00 00 00 00 00 1b0 - 00 00 00 00 00 00 01 00 00 00 1c0 - 01 01 06 fe 7f 05 00 3f 00 00 1d0 - 41 06 05 fe bf 0f 86 39 40 00 1e0 - 01 00 0a fe 3f 00 3f 00 00 00 1f0 - 00 00 00 00 00 00 00 00 00 00 [d:\myos2\devsrc\myprogs\drive\edm] fc b8 00 00 8e d8 02 f3 a5 b8 22 7e 04 0a 74 45 83 c3 01 00 00 86 8a 82 00 02 00 00 fa 34 3e 00 cd 00 33 3f 41 00 00 13 00 cc 00 00 00 00 73 00 00 00 80 00 55 03 00 01 00 01 00 aa

The actual layout of the partition table is as follows:


Offset Description 0x000 Boot code 0x1BE 16 BYTE Entry in the 0x1CE 16 BYTE Entry in the 0x1DE 16 BYTE Entry in the 0x1EE 16 BYTE Entry in the

partition partition partition partition

table table table table

0x1EE 16 BYTE Entry in the partition table 0x1FE 2 BYTE IDcode(0xAA55) identifies partition sector

For my purposes, the first 0x1BE bytes are not very interesting since I am not currently looking at the boot code. If you have any interest in this, check out http://www.uruk.org/~erich/. Each table entry is represented as follows:
Offset Description BYTE Partition status, 0x80 = Active, 0x00 = inactive BYTE First head used by partition BYTE[2] First sector and cylinder used by partition BYTE Partition type BYTE Last head used by partition BYTE[2] Last sector and cylinder used by partition ULONG Location of boot sector ULONG Number of sectors for partition

These items are encapsulated with the following structures.


typedef struct { BYTE status_; BYTE startHead_; BYTE startSecCyl_[2]; BYTE type_; BYTE endHead_; BYTE endSecCyl_[2]; ULONG bootSec_; ULONG numSec_; } PartitionEntryStruct; typedef struct { BYTE bootCode_[0x1BE]; PartitionEntryStruct entries_[4]; WORD iDCode_; } PartitionSectorStruct;

The order that the partition table entries appear in the partition table is not defined, so do not assume that they appear in any particular order. The example shown above has the following values:
Status ====== 00 00 80 00 Hd sec/cyl Typ Hd sec/cyl Boot Sector Num Sectors == ======= === == ======= =========== =========== 01 01 01 06 fe 7f 05 00 3f 00 00 86 fa 3f 00 00 41 06 05 fe bf 0f 86 39 40 00 8a 34 41 00 01 01 00 0a fe 3f 00 3f 00 00 00 82 3e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Which correspond to:


Partition at offset 0 Stat Type Head Cyl Sect Head Cyl Sect Boot Sct Num Sct ==== ==================== ==== ==== ==== ==== ==== ==== ======== ======= 0x00 0x06=DOS >32M 1 1 1 254 261 63 16128 4192902 0x00 0x05=Extended 0 262 1 254 527 63 4209030 4273290 0x80 0x0a=Boot Mgr 1 0 1 254 0 63 63 16002 0x00 0x00=Empty 0 0 0 0 0 0 0 0

The status byte of the partition can have two values, 0, which means the partition is not the active primary partition, and 0x80 (high bit set) which means that this is the active primary partition. The next byte indicates on which head the partition starts. The next two bytes indicate the sector and the cylinder in a compressed format. The sector is held in the first six bits (bits 0 through five) and bits 6 through 15 contain the cylinder. The sector is given by (*startSecCyl_) & (0x3F) and the packed cylinder is obtained with (startSecCyl_[1]+((startSecCyl_[0] & 0xC0) << 2). The type byte indicates what the partition really is. I do not know what all of the valid values are, but I have gleaned the following types:
Value 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x0A 0x0B 0x64 0x75 0xDB 0xFF Type Empty FAT-12 XENIX XENIX FAT-16 Extended Partition DOS >32Meg OS/2 HPFS Boot Manager FAT-32 Novell PCIX CPM/Concurrent BBT

The next three bytes indicate the end head, sector, and cylinder of the partition. The following four bytes contain the boot sector and the final four contain a count on the number of sectors used. Although this is not required, partitions generally start with head 0 and sector 1. This leaves some unused sectors. These extra sectors can hide either a boot sector virus or boot manager code. An extended partition table can contain as many as two secondary partition entries (logical drives), and two extended partitions. If there are three partitions inside of a single extended partition, then extra partition tables will be used. These extra partition tables will be referenced as extended partitions inside of an existing extended partition. In the primary partition table, the boot sector indicates where the extended partition is located (for extended partitions). If the entry is already inside of an extended partition then the referenced extended partition is at the location of the stated boot sector plus the offset of the main enclosing extended partition. Consider the following pseudo code to traverse the partition tables.
printPartitionInformation(absoluteSector, offset) { table = readSector(absoluteSector); PrintThisInformation(table); for (i=0; i<4; ++i)

for (i=0; i<4; ++i) if (table->entries_[i].type_ == 0x05) { sect = table->entries[i].bootSec_; newOffset = offset == 0 ? sect : offset; printPartitionInformation(sect + offset, newOffset); } }

An initial call of printPartitionInformation(0, 0) gets things started. Note that for the first partition table, the offset is zero. The offset is then changed once to the start of the first partition table. Any subsequent embedded partition tables will still use the same offset.

Source Files
I created five source files which I have attempted to document. I wanted to have a nice method of printing error messages so I created errprnt.hpp and errprnt.cpp. These files contain a single function
printErrorMessage(APIRET error, char *msg)

This routine accepts a standard error number and it prints both the associated text message and a supplied text message. By using this routine, I was not forced to continually lookup error codes. The file drvstrct.hpp contains structures which are useful for certain low level routines. This includes the PartitionEntryStruct and the PartitionSectorStruct for example. DriveInf.hpp and DriveInf.cpp contain the primary code used for these examples. I used the Borland C++ compiler for OS/2 version 2. Strangely, it reports itself as Version 4.11. I use the following command line to compile my code:
bcc driveinf.cpp errPrnt.cpp

or
icc /sp1 driveinf.cpp errPrnt.cpp

I lost my Visual Age stuff during a move from Germany, so I do not have my documentation, and thus I have not done much testing with the IBM Visual Age compiler. If you want to use this with the IBM compiler, be certain to use the /SP1 compile option to align the data on 1 byte boundaries as opposed to the default four byte boundaries. You could also use compiler directives. After the program has been compiled, you can determine how many partitionable drives are present in the system.

[d:\test]driveinf num physical Number of partitionable drives = 2

You can print the partition tables for a physical drive on the system.
[d:\test]driveinf partition print 2: Partition info for physical drive 2 ********************************************************* Partition at offset 0 Stat Type Head Cyl Sect Head Cyl Sect Boot Sct Num Sct ==== ==================== ==== ==== ==== ==== ==== ==== ======== ======== 0x00 0x05=Extended 0 1 1 63 531 32 2048 1087488 0x00 0x00=Empty 0 0 0 0 0 0 0 0 0x00 0x00=Empty 0 0 0 0 0 0 0 0 0x00 0x00=Empty 0 0 0 0 0 0 0 0 Partition at offset 2048 Stat Type Head Cyl Sect Head Cyl Sect Boot Sct Num Sct ==== ==================== ==== ==== ==== ==== ==== ==== ======== ======== 0x00 0x0b=FAT-32 1 1 1 63 531 32 32 1087456 0x00 0x00=Empty 0 0 0 0 0 0 0 0 0x00 0x00=Empty 0 0 0 0 0 0 0 0 0x00 0x00=Empty 0 0 0 0 0 0 0 0

There is also an option to query the device parameters for a physical drive. This uses DosDevIOCtl( ) category 9 function 0x63. This will fail if the drive can not be locked.
[d:\test]driveinf.exe params physical 1: ********************************************************* queryPhysicalDeviceParams() for physical drive 1 cyl = 0 heads = 0 sect = 0 Error: (5) ACCESS_DENIED from queryPhysicalDeviceParams() [d:\test]driveinf.exe params physical 2: ********************************************************* queryPhysicalDeviceParams() for physical drive 2 cyl = 528 heads = 255 sect = 63

There is also a dump command which takes three parameters, the logical drive, the first sector to read and the number of sectors. An example of this is shown early on as
driveinf DUMP 1 0 1

which reads from the first logical drive numbered 1, the first sector numbered 0, and one sector is read.

one sector is read.

Wrap-up
The code samples are pretty straight forward and should give you a good start on reading and interpreting your own partition tables. In the development of this code, I used the Gamma Tech Utilities to poke around my harddisks; this is highly recommended. I also used the book "PC Intern: The Encyclopedia of System Programming", the sixth edition by Michael Tischer and Bruno Jennrich published by Abacus. If you have any questions or comments on the code, drop me a line at andyp@primatech.com.

También podría gustarte