windows的磁碟操作之二——初始化磁碟
上一節中我們介紹了一些基本概念和主要的API,本節開始我們將列舉並分析一些例項。本文中的所有程式碼我都在vs2008下測試過,讀者只需要替換少量的巨集定義即可編譯執行。
面對一塊新的磁碟,我們首先要做的就是對其初始化。在系統中通過windows的磁碟管理完成這一點非常容易,但在程式中實現略微複雜。本節的示例程式碼對一塊新硬碟初始化,並在上面建立分割槽。
程式碼如下:
/******************************************************************************
* Function: initialize the disk and create partitions
* input: disk, disk name
* parNum, partition number
* output: N/A
* return: Succeed, 0
* Fail, -1
******************************************************************************/
DWORD CreateDisk(DWORD disk, WORD partNum)
{
HANDLE hDevice; // handle to the drive to be examined
BOOL result; // results flag
DWORD readed; // discard results
DWORD ret;
WORD i;
CHAR diskPath[DISK_PATH_LEN];
DISK_GEOMETRY pdg;
DWORD sectorSize;
DWORD signature;
LARGE_INTEGER diskSize;
LARGE_INTEGER partSize;
BYTE actualPartNum;
DWORD layoutStructSize;
DRIVE_LAYOUT_INFORMATION_EX *dl;
CREATE_DISK newDisk;
sprintf(diskPath, "\\\\.\\PhysicalDrive%d", disk);
actualPartNum = 4;
if (partNum > actualPartNum)
{
return (WORD)-1;
}
hDevice = CreateFile(
diskPath,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, //default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
return DWORD(-1);
}
// Create primary partition MBR
newDisk.PartitionStyle = PARTITION_STYLE_MBR;
signature = (DWORD)time(NULL); //get signature from current time
newDisk.Mbr.Signature = signature;
result = DeviceIoControl(
hDevice,
IOCTL_DISK_CREATE_DISK,
&newDisk,
sizeof(CREATE_DISK),
NULL,
0,
&readed,
NULL
);
if (!result)
{
fprintf(stderr, "IOCTL_DISK_CREATE_DISK Error: %ld\n", GetLastError());
(void)CloseHandle(hDevice);
return DWORD(-1);
}
//fresh the partition table
result = DeviceIoControl(
hDevice,
IOCTL_DISK_UPDATE_PROPERTIES,
NULL,
0,
NULL,
0,
&readed,
NULL
);
if (!result)
{
fprintf(stderr, "IOCTL_DISK_UPDATE_PROPERTIES Error: %ld\n", GetLastError());
(void)CloseHandle(hDevice);
return DWORD(-1);
}
//Now create the partitions
ret = GetDriveGeometry(diskPath, &pdg);
if ((DWORD)-1 == ret)
{
return ret;
}
sectorSize = pdg.BytesPerSector;
diskSize.QuadPart = pdg.Cylinders.QuadPart * pdg.TracksPerCylinder *
pdg.SectorsPerTrack * pdg.BytesPerSector; //calculate the disk size;
partSize.QuadPart = diskSize.QuadPart / partNum;
layoutStructSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + (actualPartNum - 1) * sizeof(PARTITION_INFORMATION_EX);
dl = (DRIVE_LAYOUT_INFORMATION_EX*)malloc(layoutStructSize);
if (NULL == dl)
{
(void)CloseHandle(hDevice);
return (WORD)-1;
}
dl->PartitionStyle = (DWORD)PARTITION_STYLE_MBR;
dl->PartitionCount = actualPartNum;
dl->Mbr.Signature = signature;
//clear the unused partitions
for (i = 0; i < actualPartNum; i++){
dl->PartitionEntry[i].RewritePartition = 1;
dl->PartitionEntry[i].Mbr.PartitionType = PARTITION_ENTRY_UNUSED;
}
//set the profile of the partitions
for (i = 0; i < partNum; i++){
dl->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
dl->PartitionEntry[i].StartingOffset.QuadPart =
(partSize.QuadPart * i) + ((LONGLONG)(pdg.SectorsPerTrack) * (LONGLONG)(pdg.BytesPerSector)); //32256
dl->PartitionEntry[i].PartitionLength.QuadPart = partSize.QuadPart;
dl->PartitionEntry[i].PartitionNumber = i + 1;
dl->PartitionEntry[i].RewritePartition = TRUE;
dl->PartitionEntry[i].Mbr.PartitionType = PARTITION_IFS;
dl->PartitionEntry[i].Mbr.BootIndicator = FALSE;
dl->PartitionEntry[i].Mbr.RecognizedPartition = TRUE;
dl->PartitionEntry[i].Mbr.HiddenSectors =
pdg.SectorsPerTrack + (DWORD)((partSize.QuadPart / sectorSize) * i);
}
//execute the layout
result = DeviceIoControl(
hDevice,
IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
dl,
layoutStructSize,
NULL,
0,
&readed,
NULL
);
if (!result)
{
fprintf(stderr, "IOCTL_DISK_SET_DRIVE_LAYOUT_EX Error: %ld\n", GetLastError());
free(dl);
(void)CloseHandle(hDevice);
return DWORD(-1);
}
//fresh the partition table
result = DeviceIoControl(
hDevice,
IOCTL_DISK_UPDATE_PROPERTIES,
NULL,
0,
NULL,
0,
&readed,
NULL
);
if (!result)
{
fprintf(stderr, "IOCTL_DISK_UPDATE_PROPERTIES Error: %ld\n", GetLastError());
free(dl);
(void)CloseHandle(hDevice);
return DWORD(-1);
}
free(dl);
(void)CloseHandle(hDevice);
Sleep(3000); //wait the operations take effect
return 0;
}
函式CreateDisk包含兩個引數,
DWORD disk 填入物理驅動器號,參見第一節。
WORD partNum 表示需要建立的分割槽數,partNum <= 4。
函式的執行流程解釋如下:
/***************初始化磁碟*****************/
1. 根據disk建立裝置名稱,\\\\.\\PhysicalDriveX,這裡由於要轉義,所以”\”都寫為”\\”。
2. 呼叫CreateFile開啟裝置檔案,並獲得控制代碼。
3. 用操作碼IOCTL_DISK_CREATE_DISK呼叫DeviceIoControl函式,初始化磁碟並建立分割槽表。
使用IOCTL_DISK_CREATE_DISK操作碼時,lpInBuffer要填入一個CREATE_DISK結構引數,其中包括分割槽表型別和磁碟簽名等引數,詳見MSDN。本例中建立MBR分割槽表,簽名由當前時間產生。
4. 重新整理分割槽表。注意,程式中任何時候對磁碟的分割槽資訊進行了修改都需要呼叫操作碼為IOCTL_DISK_UPDATE_PROPERTIES的DeviceIoControl函式來重新整理分割槽表,是操作切實生效。
/****************建立分割槽*******************/
5. 呼叫GetDriveGeometry獲取磁碟資訊(GetDriveGeometry參見上一節http://cutebunny.blog.51cto.com/301216/624027)。由於建立分割槽時要填入分割槽大小資訊,我們此處先計算磁碟總大小,然後除以partNum將位元組數平均分配到各個分割槽。
6. 分配DRIVE_LAYOUT_INFORMATION_EX結構體空間。我們通過在這個結構體中填入資料來指定如何對硬碟進行分割槽。結構體定義如下
typedef struct _DRIVE_LAYOUT_INFORMATION_EX {
DWORD PartitionStyle;
DWORD PartitionCount;
union {
DRIVE_LAYOUT_INFORMATION_MBR Mbr;
DRIVE_LAYOUT_INFORMATION_GPT Gpt;
};
PARTITION_INFORMATION_EX PartitionEntry[1];
} DRIVE_LAYOUT_INFORMATION_EX,
*PDRIVE_LAYOUT_INFORMATION_EX;
其中PartitionCount為4的倍數,為簡化處理,我們這裡定死為4。
另外還要注意PARTITION_INFORMATION_EX型的陣列PartitionEntry[1]。雖然結構體中只定義了一個元素,但事實上必須在其後補足PartitionCount – 1個元素。所以程式碼中為DRIVE_LAYOUT_INFORMATION_EX *dl分配空間的時候加上了(actualPartNum - 1) * sizeof(PARTITION_INFORMATION_EX)。
7. 在DRIVE_LAYOUT_INFORMATION_EX結構體空間dl中填入資料。
先將所有分割槽都設為PARTITION_ENTRY_UNUSED,後面具體分配多少個分割槽再設定回來。
然後再迴圈體內對每個分割槽的PartitionEntry賦值,其中
StartingOffset除了跳過前面的分割槽已佔據的空間外,還要加上63個扇區空間(32256位元組)。
PartitionNumber從1開始。
Mbr.PartitionType = PARTITION_IFS表示NTFS格式。
Mbr.HiddenSectors MSDN上說The number of hidden sectors to be allocated when the partition table is created. 我理解得不是很深刻,歡迎補充。
8. 呼叫操作碼為IOCTL_DISK_SET_DRIVE_LAYOUT_EX的DeviceIoControl函式執行分割槽,引數需要填入剛才準備好的DRIVE_LAYOUT_INFORMATION_EX結構體和大小。
9. 重新整理分割槽表,原理同4。
另外,我在函式末尾加上了Sleep(3000)。這是因為我發現建立分割槽操作需要一定的執行時間,如果後續緊跟著其它相關操作(例如格式化該分割槽)可能會產生分割槽不存在的錯誤,所以此處等待3秒確保其執行完畢。
本節涉及的型別較多,但各型別具有很強的關聯性,讀者可隨時查閱MSDN獲得更詳細的說明。