天天看點

Windows上面讀磁盤分區表1. 概述 2. 代碼 3. 典型問題

1. 概述

在 GPT分區表 一文中,講解了MBR分區表的一些概念。本文進一步給出了Windows系統上讀取MBR的示例代碼,以及MBR的具體資料,可進一步加深對MBR的了解。

在參考網上一些文章&代碼的基礎上,本文給出了下面的展示MBR的示例代碼;同時最後針對幾個典型問題,給出了一些解釋和參考文檔。

2. 代碼

下面是代碼:

/**
 * ref: http://noyesno.net/page/it/20100701-172
 * 
 */
#include <stdio.h>
#include <windows.h>

struct CHS {
    unsigned int header;
    unsigned int sector; 
    unsigned int cylinder;
};

struct partition_record {
    unsigned char boot_indicator;
    unsigned char os_type;
    struct CHS starting_chs;
    struct CHS ending_chs;
    unsigned int starting_LBA;
    unsigned int size_in_LBA;
};

void dump_partition_record(const partition_record& record) {
    const double DISK_SECTOR_SIZE = 512.0;
    const size_t GB = 1024 * 1024 * 1024;

    printf(
        "\tboot indicator: 0x%02x\n"
        "\tOS Type: 0x%02x\n"
        "\tStarting CHS: (%d, %d, %d)\n"
        "\tEnding_CHS: (%d, %d, %d)\n"
        "\tSize in LBA: %d\n"
        "\tSize:%.2f\n\n",
        record.boot_indicator, 
        record.os_type, 
        record.starting_chs.cylinder,
        record.starting_chs.header,
        record.starting_chs.sector,
        record.ending_chs.cylinder,
        record.ending_chs.header,
        record.ending_chs.sector,
        record.size_in_LBA,
        record.size_in_LBA * DISK_SECTOR_SIZE / GB);
}

// transform the 16-bytes buffer to partition record
void get_partition_record(unsigned char buffer[], partition_record& record) {
    record.boot_indicator = buffer[0];

    record.starting_chs.header = buffer[1];
    record.starting_chs.sector = buffer[2] & 0x3f;
    record.starting_chs.cylinder = (((buffer[2] & 0xff) >> 6) << 8) + buffer[3];

    record.os_type = buffer[4];

    record.ending_chs.header = buffer[5];
    record.ending_chs.sector = buffer[6] & 0x3f;
    record.ending_chs.cylinder = (((buffer[6] & 0xff) >> 6) << 8) + buffer[7];

    record.size_in_LBA = *(unsigned int*)(buffer + 12);
}

void read_mbr(const char *devname){
  HANDLE hDevice = CreateFile(devname, GENERIC_READ,   
                  FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);  
  if (hDevice == INVALID_HANDLE_VALUE){  
      printf("Open %s failed, the error code is %d.\n", devname, GetLastError());  
      return;  
  }

  DWORD dwRead = 0;  
  const size_t DISK_SECTOR_SIZE = 512;
  unsigned char buffer[DISK_SECTOR_SIZE];  

  BOOL bSuccess = ReadFile(hDevice, buffer, DISK_SECTOR_SIZE, &dwRead, NULL);  
  CloseHandle(hDevice);

  if ( !bSuccess || DISK_SECTOR_SIZE != dwRead)  {  
     printf("Error: read error! Read size = %d\n",  dwRead);  
     return; 
  }  

  size_t i, j;
  printf("%10s 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F \n", " ");  
  for (i = 0; i < 32; i++) {  
      printf("%.8XH: ", i);  
      for (j = 0; j < 16; j++) {
          printf("%.2X ", buffer[i * 16 + j] & 0xFF);  
      }
      printf("\n");  
  }

  printf("====MBR Partition Entries====\n");
  partition_record record = {0};
  for(i = 0x01be; i < 0x01FE; i += 16){
      get_partition_record(buffer + i, record);
      dump_partition_record(record);
  }
}

int main(int argc, char *argv[]){
    const char* LOG_FILE = "./log.txt";
    freopen(LOG_FILE, "a", stdout); 
    setbuf(stdout, NULL);

    read_mbr("\\\\.\\PhysicalDrive0");

    return 0;
}
           

運作結果如下:

0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
00000000H: 33 FF BE 00 02 8E D7 BC 00 7A BB A0 07 8B CE 8E 
00000001H: DB 8E C3 F3 A4 EA 5D 00 A0 07 10 00 01 00 00 7A 
00000002H: 00 00 00 00 00 00 00 00 00 00 07 66 8B 55 08 B4 
00000003H: 42 C6 06 1F 00 7C 32 C0 66 89 16 22 00 BE 1A 00 
00000004H: B2 80 CD 13 0F 82 CD 00 81 3E FE 03 55 AA C3 AC 
00000005H: 0A C0 74 FA B4 0E BB 07 00 CD 10 EB F2 8B E9 8B 
00000006H: D9 C6 06 2A 00 0C BF EE 01 B9 04 00 38 6D 04 74 
00000007H: 37 E8 B7 FF 75 1F 66 B8 52 45 43 4F 66 39 06 03 
00000008H: 02 74 0C 66 39 06 F0 03 75 0B C6 06 2A 00 07 8B 
00000009H: DF C6 45 04 12 8A 45 04 3C 07 74 0A 3C 0B 74 06 
0000000AH: 24 F5 3C 04 75 02 8B EF 88 2D 83 EF 10 E2 BD 0B 
0000000BH: DB 74 3D 0B ED 74 39 8B FB F6 06 6D 01 04 75 76 
0000000CH: F6 06 6D 01 02 75 73 B4 11 CD 16 75 5C 8A 16 6C 
0000000DH: 01 0A D2 74 65 FE CA 78 17 36 8A 0E 6C 04 80 C1 
0000000EH: 12 B4 11 CD 16 75 42 36 3A 0E 6C 04 75 F3 EB E5 
0000000FH: 66 33 D2 E8 39 FF B1 04 BF BE 03 80 3D 80 74 5E 
00000010H: 83 C7 10 E2 F6 B1 04 BF BE 03 80 7D 04 00 75 4E 
00000011H: 83 C7 10 E2 F5 8B 36 70 01 E8 33 FF 8B 36 72 01 
00000012H: E8 2C FF B4 00 CD 16 CD 18 B4 10 CD 16 3C 72 74 
00000013H: 05 80 FC 85 75 BA 8B EF EB 07 F6 06 6D 01 10 74 
00000014H: 06 A0 2A 00 88 45 04 8B FD C6 05 80 80 26 6D 01 
00000015H: F9 66 33 D2 C6 06 1F 00 7A B4 43 E8 D8 FE E8 CA 
00000016H: FE 8B 36 6E 01 75 B2 EA 00 7C 00 00 01 11 76 01 
00000017H: 7B 01 82 01 87 01 45 72 72 32 00 0D 0A 45 72 72 
00000018H: 31 00 45 72 72 33 00 0D 0A 50 72 65 73 73 20 46 
00000019H: 31 31 20 66 6F 72 20 45 6D 65 72 67 65 6E 63 79 
0000001AH: 20 52 65 63 6F 76 65 72 79 20 00 73 20 61 20 6B 
0000001BH: 65 79 0D 0A 00 00 6C 01 32 F2 49 15 00 00 80 01 
0000001CH: 01 00 07 EF FF FF 3F 00 00 00 D0 88 81 1D 00 EF 
0000001DH: FF FF 07 EF FF FF 00 90 81 1D 00 60 07 1C 00 EF 
0000001EH: FF FF 07 EF FF FF 10 F1 88 39 80 57 AF 00 00 00 
0000001FH: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA 
====MBR Partition Entries====
	boot indicator: 0x80
	OS Type: 0x07
	Starting CHS: (0, 1, 1)
	Ending_CHS: (1023, 239, 63)
	Size in LBA: 495028432
	Size:236.05

	boot indicator: 0x00
	OS Type: 0x07
	Starting CHS: (1023, 239, 63)
	Ending_CHS: (1023, 239, 63)
	Size in LBA: 470245376
	Size:224.23

	boot indicator: 0x00
	OS Type: 0x07
	Starting CHS: (1023, 239, 63)
	Ending_CHS: (1023, 239, 63)
	Size in LBA: 11491200
	Size:5.48

	boot indicator: 0x00
	OS Type: 0x00
	Starting CHS: (0, 0, 0)
	Ending_CHS: (0, 0, 0)
	Size in LBA: 0
	Size:0.00
           

下圖是實際的硬碟中各分區的資料:

Windows上面讀磁盤分區表1. 概述 2. 代碼 3. 典型問題

3. 典型問題

3.1 Vista上傳回錯誤碼5

vista上通路硬體時會有權限問題,比如上面代碼CreateFile()在IDE中執行失敗,傳回錯誤碼5. 規避的方法就是直接在資料總管中運作exe程式,并選擇“以管理者身份運作”。

Windows上面讀磁盤分區表1. 概述 2. 代碼 3. 典型問題

3.2 超過8GB的CHS表示形式

在MBR的每個分區記錄(partition record, partition entry)中,CHS(Cylinder Header Sector)部分是3個位元組,如此最大表示8G的磁盤空間(粗略計算:8x3=24, 每個扇區512位元組, 512 * 2^24 = 8GB)。但現在的磁盤空間最少都是幾百G,是以CHS無法表示出正确的取值。為此,MBR通正常定0xfeffff表示非法的CHS,或無效的CHS;但UEFI規範規定的是0xffffff表示無效值,但如同上面代碼運作結果顯示的,在示例代碼運作的系統上,卻是0xefffff表示無效取值。

在這種情況下,都取Partition Entry中的起始LBA(StartingLBA)、以及LBA個數(SizeInLBA)兩個字段來計算每個分區的起始位置、以及該分區的實際大小。

相關的兩個連結提及了chs的特殊取值:

  • gdisk - Interactive GUID partition table (GPT) manipulator
  • MBR/EBR Partition Tables :系統地介紹了分區表的概念,以及CHS的計算方法

3.3 把列印重定向到檔案

在上面的示例代碼中,main()一開始把标準輸出重定向到一個檔案中。可以參考Android源碼bootable/recovery/recovery.cpp的main()函數:

int
main(int argc, char **argv) {
    time_t start = time(NULL);

    // If these fail, there's not really anywhere to complain...
    freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
    freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
           

關于如何下載下傳Android源碼,請參考 The steps for download android source code。

3.4 磁盤管理相關的知識、以及MSDN相關的API

  • 參考入口連結:http://msdn.microsoft.com/en-us/library/windows/desktop/aa363978%28v=vs.85%29.aspx

3.5 編碼風格

Windows上面編碼的風格和Linux及其他語言有相當大的差異,比如Windows的函數名是駝峰式、首字母大寫;但Linux、以及STL等的命名均是小寫字母、單詞之間是下劃線等風格。

為此,以上的示例代碼主要側重于Linux的編碼習慣,如此會和Windows SDK API的風格存在差異。以此說明。