Логическая структура жесткого диска

В статье расcматривается логическая структура жесткого диска, соответствующая стандарту Microsoft - "основной раздел - расширенный раздел - разделы не-DOS", а также разбирается пример программы, осуществляющей чтение таблицы разделов.

[Bob (ubob AT mail.ru)]

Логическая структура жесткого диска

В статье расcматривается логическая структура жесткого диска, соответствующая стандарту Microsoft - "основной раздел - расширенный раздел - разделы не-DOS".

Пространство на жестком диске может быть организовано в виде одного или нескольких разделов, а разделы могут содержать один или несколько логических дисков.

На жестком диске по физическому адресу 0-0-1 располагается главная загрузочная запись (master boot record, MBR). В состав MBR входят:

  • внесистемный загрузчик (non-system bootstrap - NSB);
  • таблица описания разделов диска (таблица разделов, partition table, PT). Эта таблица расположена в MBR по смещению 0x1BE и занимает 64 байта;
  • сигнатура MBR. Последние два байта MBR должны содержать число 0xAA55.
Таблица разделов описывает размещение и характеристики имеющихся на винчестере разделов. Разделы диска могут быть двух типов - primary (первичный, основной) и extended (расширенный). Максимальное число primary-разделов равно четырем. Наличие на диске хотя бы одного primary-раздела является обязательным. Extended-раздел может быть разделен на большое количество подразделов - логических дисков.Упрощенно структура MBR представлена в таблице 1. Таблица разделов располагается в конце MBR, для описания раздела в таблице отводится 16 байт.
Таблица 1. 
Структура MBR
Смещение (offset)	Размер (Size)		Содержимое (contents)
-------------------------------------------------------------------------
	0		446		Программа анализа таблицы разделов
					и загрузки System Bootstrap
					с активного раздела
-------------------------------------------------------------------------
	0x1BE		16		Partition 1 entry ( элемент таблицы разделов)
-------------------------------------------------------------------------
	0x1CE		16		Partition 2 entry
-------------------------------------------------------------------------
	0x1DE		16		Partition 3 entry
-------------------------------------------------------------------------
	0x1EE		16		Partition 4 entry
-------------------------------------------------------------------------
	0x1FE		2		Сигнатура 0xAA55

Структура записи элемента таблицы разделов показана в таблице 2.
Таблица 2.
Структура записи элемента таблицы разделов

Смещение	Размер поля,		Содержание
		байт
-------------------------------------------------------------------------
0x00		1		Признак активности (0 - раздел не активный,
				0x80 - раздел активный)
--------------------------------------------------------------------------
0x01		1		Номер головки диска, с которой
				начинается раздел
---------------------------------------------------------------------------
0x02		2		Номер цилиндра и номер сектора, с которых
				начинается раздел
----------------------------------------------------------------------------
0x04		1		Код типа раздела System ID
----------------------------------------------------------------------------
0x05		1		Номер головки диска, на которой
				заканчивается раздел
----------------------------------------------------------------------------
0x06		2		Номер цилиндра и номер сектора, которыми
				заканчивается раздел
----------------------------------------------------------------------------
0x08		4		Абсолютный (логический) номер начального
				сектора раздела
----------------------------------------------------------------------------
0x0C		4		Размер раздела (число секторов)
Первым байтом в элементе раздела идет флаг активности раздела (0 - неактивен, 0x80 - активен). Он служит для определения, является ли раздел системным загрузочным и есть ли необходимость производить загрузку операционной системы с него при старте компьютера. Активным может быть только один раздел. За флагом активности раздела следуют координаты начала раздела - три байта, означающие номер головки, номер сектора и номер цилиндра. Номера цилиндра и сектора задаются в формате прерывания Int 0x13, т.е. биты 0-5 содержат номер сектора, биты 6-7 - старшие два бита 10-разрядного номера цилиндра, биты 8-15 - младшие восемь битов номера цилиндра.

Затем следует кодовый идентификатор System ID, указывающий на принадлежность данного раздела к той или иной операционной системе. Идентификатор занимает один байт. За системным идентификатором расположены координаты конца раздела - три байта, содержащие номера головки, сектора и цилиндра, соответственно. Следующие четыре байта - это число секторов перед разделом, и последние четыре байта - размер раздела в секторах.

Таким образом, элемент таблицы раздела можно описать при помощи следующей структуры:

    struct pt_struct {
	    u8 bootable;	// флаг активности раздела
	    u8 start_part[3];	// координаты начала раздела
	    u8 type_part;	// системный идентификатор
	    u8 end_part[3];	// координаты конца раздела	    
	    u32 sect_before;	// число секторов перед разделом    
	    u32 sect_total;	// число секторов в разделе
    };
Элемент первичного раздела указывает сразу на загрузочный сектор логического диска (в первичном разделе всегда имеется только один логический диск), а элемент расширенного раздела - на список логических дисков, составленный из структур, которые именуются вторичными MBR (Secondary MBR, SMBR).

Свой блок SMBR имеется у каждого диска расширенного раздела. SMBR имеет структуру, аналогичную MBR, но загрузочная запись у него отсутствует (заполнена нулями), а из четырех полей описателей разделов используются только два. Первый элемент раздела при этом указывает на логический диск, второй элемент указывает на следующую структуру SMBR в списке. Последний SMBR списка содержит во втором элементе нулевой код раздела.

Рассмотрим программу, отображающую информацию обо всех разделах (основных и логических), созданных на жестком диске. Программа функционирует под управлением ОС Linux.

Заголовочные файлы:

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <linux/types.h>
Сигнатура MBR:
    #define SIGNATURE 0xAA55
Файл устройства, с которого будет считываться информация о разделах:
    #define DEVICE "/dev/hda"
Размер элемента таблицы разделов (0x10):
    #define PT_SIZE 0x10
Следующий массив структур устанавоивает соответствие между кодом типа раздела и его символьным отображением:
    struct systypes {
        __u8 part_type;
        __u8 *part_name;
    };

    /* Этот массив взят из исходных текстов программы fdisk */
    struct systypes i386_sys_types[] = {
	{0x00, "Empty"},
	{0x01, "FAT12"},
	{0x04, "FAT16 <32M"},
	{0x05, "Extended"},
	{0x06, "FAT16"},
	{0x0b, "FAT32"},
	{0x0c, "FAT32 (LBA)"},
	{0x0e, "FAT16 (LBA)"},
	{0x0f, "Win Ext'd (LBA)"},
	{0x82, "Linux swap"},
	{0x83, "Linux"},
	{0x85, "Linux extended"},
	{0x07, "HPFS/NTFS"}
    };
Определим число элементов в массиве i386_sys_types при помощи макроса:
    #define PART_NUM (sizeof(i386_sys_types) / sizeof(i386_sys_types[0]))

    int hard;	// дескриптор файла устройства
    __u8 mbr[512];	// сюда считаем MBR
Установим ограничение на количество логических дисков:
    #define MAX_PART 20
Следующий массив структура будет содержать информацию о логических дисках на устройстве (жестком диске):
    struct pt_struct {
        __u8 bootable;
        __u8 start_part[3];
        __u8 type_part;
        __u8 end_part[3];
        __u32 sect_before;
        __u32 sect_total;
    } pt_t[MAX_PART];
Рассмотрим главную функцию.
    int main()
    {
        int i = 0;
        __u64 seek;

    /* Открываем файл устройства, считываем таблицу разделов и проверяем сигнатуру */

        hard = open(DEVICE, O_RDONLY);
        if(hard < 0) {
    	    perror("open");
	    exit(-1);
        }
    
        read_main_ptable();

        if(check_sign() < 0) {
    	    printf("Not valid signature!\n");
	    exit(-1);
        }

        /* Ищем идентификатор расширенного раздела. Если таковой имеется - вычисляем 
         * смещение к расширенному разделу и считываем информацию о логических дисках
         */
        for(; i < 4; i++) {
    	    if((pt_t[i].type_part == 0xF) || \
		(pt_t[i].type_part == 0x5) || \
		(pt_t[i].type_part == 0x0C)) {
	    	    seek = (__u64)pt_t[i].sect_before * 512;
		    read_ext_ptable(seek);
		    break;
	    }
	}

        /* Отображаем информацию о логических дисках */
        pt_info();
        return 0;
    }
Функция проверки сигнатуры 0xAA55 выглядит следующим образом:
    int check_sign()
    {
        __u16 sign = 0;

        memcpy((void *)&sign, (void *)(mbr + 0x1FE), 2);

        printf("Сигнатура - 0x%X\n", sign);

        if(sign != SIGNATURE) return -1;
        return 0;
    }
Функция чтения таблицы разделов:
    void read_main_ptable()
    {
        if(read(hard, mbr, 512) < 0) {
	    perror("read");
    	    close(hard);
	    exit(-1);
	}

	memset((void *)pt_t, 0, (PT_SIZE * 4));
	memcpy((void *)pt_t, mbr + 0x1BE, (PT_SIZE * 4));

	return;
    }
Функция чтения расширенной таблицы разделов:
    void read_ext_ptable(__u64 seek)
    {
        int num = 4; // начиная с этой позиции, массив структур pt_t будет
    		     // заполняться информацией о логических дисках
        __u8 smbr[512];

        /* Функция принимает один параметр seek - смещение (в байтах) к расширеному разделу
         * от начала диска. Для получения информации о логических дисках организуем цикл
         */
        for(;;num++) {

    	    /* Считываем SMBR, находящуюся по смещению seek от начала диска */
    	    memset((void *)smbr, 0, 512);
	    pread64(hard, smbr, 512, seek);

	    /* Заполняем два элемента таблицы pt_t, начиная с num. Первый элемент будет
	     * указывать на логический диск, а второй - на следующую структуру SMBR
	     */
	    memset((void *)&pt_t[num], 0, PT_SIZE * 2);
	    memcpy((void *)&pt_t[num], smbr + 0x1BE, PT_SIZE * 2);

	    /* Вносим поправку в поле "Номер начального сектора" - 
	     * отсчет ведется от начала диска
	     */
	    pt_t[num].sect_before += (seek / 512);

	    /* Если код типа раздела равен нулю, то больше логических дисков нет */
    	    if(!(pt_t[num + 1].type_part)) break;

	    /* Вычисляем смещение к следующей SMBR */
	    seek = ((__u64)(pt_t[num].sect_before + pt_t[num].sect_total)) * 512;
        }
	return;
    }
Функция pt_info() отображает информацию о найденых логических дисках на устройстве:
    void pt_info()
    {
        int i, n;

	/* Информация о разделах на устройстве ATA-0 */
	printf("\nNum\tBootable\tStart\tTotal\t\tId\tType\n");

	for(i = 0; i < MAX_PART; i++) {

	    if(!pt_t[i].type_part) {
		if(i < 4) continue;
		else break;
	    }

	    printf("%d\t", i + 1);

	    if(pt_t[i].bootable == 0x80) printf("*\t");
	    else printf("\t");

	    printf("%12u\t", pt_t[i].sect_before);
	    printf("%8u\t", pt_t[i].sect_total);
	    printf("0x%.2X\t", pt_t[i].type_part);

    	    for(n = 0; n < PART_NUM; n++) {
		if(pt_t[i].type_part == i386_sys_types[n].part_type) {
		    printf("%s\n", i386_sys_types[n].part_name);
		    break;
		}
	    }
	    if(n == PART_NUM) printf("unknown type\n");
	}
	return;
    }
Рассмотрим пример функционирования программы.

Имеется жесткий диск, подключеный как Primary Master. На диске созданы три основных раздела (FAT32 и два EXT2 раздела) и расширенный раздел в составе одного логического диска с файловой системой FAT32.

Результат работы программы:

    # ./part_view

Сигнатура - 0xAA55

    Num	Bootable	Start	Total		Id	Type
    1	*	          63	 4096512	0x0B	FAT32
    2		     4096575	30732345	0x0F	Win Ext'd (LBA)
    3		    34828920	12289725	0x83	Linux
    4		    47118645	31037580	0x83	Linux
    5		     4096638	30732282	0x0B	FAT32
Для контроля получим информацию о логических дисках при помощи fdisk:
    # fdisk -l -u

    Disk /dev/hda: 40.0 GB, 40020664320 bytes
    255 heads, 63 sectors/track, 4865 cylinders, total 78165360 sectors
    Units = sectors of 1 * 512 = 512 bytes

       Device Boot    Start       End    Blocks   Id  System
    /dev/hda1   *        63   4096574   2048256    b  Win95 FAT32
    /dev/hda2       4096575  34828919  15366172+   f  Win95 Ext'd (LBA)
    /dev/hda3      34828920  47118644   6144862+  83  Linux
    /dev/hda4      47118645  78156224  15518790   83  Linux
    /dev/hda5       4096638  34828919  15366141    b  Win95 FAT32

Статья взята с сайта OpenNet.

[ опубликовано 31/05/2005 ]

Bob (ubob AT mail.ru) - Логическая структура жесткого диска   Версия для печати