xref: /reactos/base/system/diskpart/partlist.c (revision b09b5584)
1 /*
2  * PROJECT:         ReactOS DiskPart
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            base/system/diskpart/partlist.c
5  * PURPOSE:         Manages all the partitions of the OS in an interactive way.
6  * PROGRAMMERS:     Eric Kohl
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "diskpart.h"
12 #include <ntddscsi.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #define InsertAscendingList(ListHead, NewEntry, Type, ListEntryField, SortField)\
18 {\
19   PLIST_ENTRY current;\
20 \
21   current = (ListHead)->Flink;\
22   while (current != (ListHead))\
23   {\
24     if (CONTAINING_RECORD(current, Type, ListEntryField)->SortField >=\
25         (NewEntry)->SortField)\
26     {\
27       break;\
28     }\
29     current = current->Flink;\
30   }\
31 \
32   InsertTailList(current, &((NewEntry)->ListEntryField));\
33 }
34 
35 /* We have to define it there, because it is not in the MS DDK */
36 #define PARTITION_LINUX 0x83
37 
38 #define PARTITION_TBL_SIZE 4
39 
40 #define MBR_MAGIC 0xAA55
41 
42 #include <pshpack1.h>
43 
44 typedef struct _PARTITION
45 {
46     unsigned char   BootFlags;        /* bootable?  0=no, 128=yes  */
47     unsigned char   StartingHead;     /* beginning head number */
48     unsigned char   StartingSector;   /* beginning sector number */
49     unsigned char   StartingCylinder; /* 10 bit nmbr, with high 2 bits put in begsect */
50     unsigned char   PartitionType;    /* Operating System type indicator code */
51     unsigned char   EndingHead;       /* ending head number */
52     unsigned char   EndingSector;     /* ending sector number */
53     unsigned char   EndingCylinder;   /* also a 10 bit nmbr, with same high 2 bit trick */
54     unsigned int  StartingBlock;      /* first sector relative to start of disk */
55     unsigned int  SectorCount;        /* number of sectors in partition */
56 } PARTITION, *PPARTITION;
57 
58 typedef struct _PARTITION_SECTOR
59 {
60     UCHAR BootCode[440];                     /* 0x000 */
61     ULONG Signature;                         /* 0x1B8 */
62     UCHAR Reserved[2];                       /* 0x1BC */
63     PARTITION Partition[PARTITION_TBL_SIZE]; /* 0x1BE */
64     USHORT Magic;                            /* 0x1FE */
65 } PARTITION_SECTOR, *PPARTITION_SECTOR;
66 
67 #include <poppack.h>
68 
69 
70 /* GLOBALS ********************************************************************/
71 
72 LIST_ENTRY DiskListHead;
73 LIST_ENTRY BiosDiskListHead;
74 LIST_ENTRY VolumeListHead;
75 
76 PDISKENTRY CurrentDisk = NULL;
77 PPARTENTRY CurrentPartition = NULL;
78 PVOLENTRY  CurrentVolume = NULL;
79 
80 
81 /* FUNCTIONS ******************************************************************/
82 
83 ULONGLONG
84 AlignDown(
85     _In_ ULONGLONG Value,
86     _In_ ULONG Alignment)
87 {
88     ULONGLONG Temp;
89 
90     Temp = Value / Alignment;
91 
92     return Temp * Alignment;
93 }
94 
95 static
96 VOID
97 GetDriverName(
98     PDISKENTRY DiskEntry)
99 {
100     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
101     WCHAR KeyName[32];
102     NTSTATUS Status;
103 
104     RtlInitUnicodeString(&DiskEntry->DriverName,
105                          NULL);
106 
107     StringCchPrintfW(KeyName, ARRAYSIZE(KeyName),
108                      L"\\Scsi\\Scsi Port %lu",
109                      DiskEntry->Port);
110 
111     RtlZeroMemory(&QueryTable,
112                   sizeof(QueryTable));
113 
114     QueryTable[0].Name = L"Driver";
115     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
116     QueryTable[0].EntryContext = &DiskEntry->DriverName;
117 
118     Status = RtlQueryRegistryValues(RTL_REGISTRY_DEVICEMAP,
119                                     KeyName,
120                                     QueryTable,
121                                     NULL,
122                                     NULL);
123     if (!NT_SUCCESS(Status))
124     {
125         DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
126     }
127 }
128 
129 static
130 NTSTATUS
131 NTAPI
132 DiskIdentifierQueryRoutine(
133     PWSTR ValueName,
134     ULONG ValueType,
135     PVOID ValueData,
136     ULONG ValueLength,
137     PVOID Context,
138     PVOID EntryContext)
139 {
140     PBIOSDISKENTRY BiosDiskEntry = (PBIOSDISKENTRY)Context;
141     UNICODE_STRING NameU;
142 
143     if (ValueType == REG_SZ &&
144         ValueLength == 20 * sizeof(WCHAR))
145     {
146         NameU.Buffer = (PWCHAR)ValueData;
147         NameU.Length = NameU.MaximumLength = 8 * sizeof(WCHAR);
148         RtlUnicodeStringToInteger(&NameU, 16, &BiosDiskEntry->Checksum);
149 
150         NameU.Buffer = (PWCHAR)ValueData + 9;
151         RtlUnicodeStringToInteger(&NameU, 16, &BiosDiskEntry->Signature);
152 
153         return STATUS_SUCCESS;
154     }
155 
156     return STATUS_UNSUCCESSFUL;
157 }
158 
159 static
160 NTSTATUS
161 NTAPI
162 DiskConfigurationDataQueryRoutine(
163     PWSTR ValueName,
164     ULONG ValueType,
165     PVOID ValueData,
166     ULONG ValueLength,
167     PVOID Context,
168     PVOID EntryContext)
169 {
170     PBIOSDISKENTRY BiosDiskEntry = (PBIOSDISKENTRY)Context;
171     PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
172     PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry;
173     ULONG i;
174 
175     if (ValueType != REG_FULL_RESOURCE_DESCRIPTOR ||
176         ValueLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
177         return STATUS_UNSUCCESSFUL;
178 
179     FullResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)ValueData;
180 
181     /* Hm. Version and Revision are not set on Microsoft Windows XP... */
182 #if 0
183     if (FullResourceDescriptor->PartialResourceList.Version != 1 ||
184         FullResourceDescriptor->PartialResourceList.Revision != 1)
185         return STATUS_UNSUCCESSFUL;
186 #endif
187 
188     for (i = 0; i < FullResourceDescriptor->PartialResourceList.Count; i++)
189     {
190         if (FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].Type != CmResourceTypeDeviceSpecific ||
191             FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize != sizeof(CM_DISK_GEOMETRY_DEVICE_DATA))
192             continue;
193 
194         DiskGeometry = (PCM_DISK_GEOMETRY_DEVICE_DATA)&FullResourceDescriptor->PartialResourceList.PartialDescriptors[i + 1];
195         BiosDiskEntry->DiskGeometry = *DiskGeometry;
196 
197         return STATUS_SUCCESS;
198     }
199 
200     return STATUS_UNSUCCESSFUL;
201 }
202 
203 static
204 NTSTATUS
205 NTAPI
206 SystemConfigurationDataQueryRoutine(
207     PWSTR ValueName,
208     ULONG ValueType,
209     PVOID ValueData,
210     ULONG ValueLength,
211     PVOID Context,
212     PVOID EntryContext)
213 {
214     PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
215     PCM_INT13_DRIVE_PARAMETER* Int13Drives = (PCM_INT13_DRIVE_PARAMETER*)Context;
216     ULONG i;
217 
218     if (ValueType != REG_FULL_RESOURCE_DESCRIPTOR ||
219         ValueLength < sizeof (CM_FULL_RESOURCE_DESCRIPTOR))
220         return STATUS_UNSUCCESSFUL;
221 
222     FullResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)ValueData;
223 
224     /* Hm. Version and Revision are not set on Microsoft Windows XP... */
225 #if 0
226     if (FullResourceDescriptor->PartialResourceList.Version != 1 ||
227         FullResourceDescriptor->PartialResourceList.Revision != 1)
228         return STATUS_UNSUCCESSFUL;
229 #endif
230 
231     for (i = 0; i < FullResourceDescriptor->PartialResourceList.Count; i++)
232     {
233         if (FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].Type != CmResourceTypeDeviceSpecific ||
234             FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize % sizeof(CM_INT13_DRIVE_PARAMETER) != 0)
235             continue;
236 
237         *Int13Drives = (CM_INT13_DRIVE_PARAMETER*)RtlAllocateHeap(RtlGetProcessHeap(), 0,
238                        FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize);
239         if (*Int13Drives == NULL)
240             return STATUS_NO_MEMORY;
241 
242         memcpy(*Int13Drives,
243                &FullResourceDescriptor->PartialResourceList.PartialDescriptors[i + 1],
244                FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize);
245         return STATUS_SUCCESS;
246     }
247 
248     return STATUS_UNSUCCESSFUL;
249 }
250 
251 
252 #define ROOT_NAME   L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter"
253 
254 static
255 VOID
256 EnumerateBiosDiskEntries(VOID)
257 {
258     RTL_QUERY_REGISTRY_TABLE QueryTable[3];
259     WCHAR Name[120];
260     ULONG AdapterCount;
261     ULONG DiskCount;
262     NTSTATUS Status;
263     PCM_INT13_DRIVE_PARAMETER Int13Drives;
264     PBIOSDISKENTRY BiosDiskEntry;
265 
266     memset(QueryTable, 0, sizeof(QueryTable));
267 
268     QueryTable[1].Name = L"Configuration Data";
269     QueryTable[1].QueryRoutine = SystemConfigurationDataQueryRoutine;
270     Int13Drives = NULL;
271     Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
272                                     L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System",
273                                     &QueryTable[1],
274                                     (PVOID)&Int13Drives,
275                                     NULL);
276     if (!NT_SUCCESS(Status))
277     {
278         DPRINT1("Unable to query the 'Configuration Data' key in '\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System', status=%lx\n", Status);
279         return;
280     }
281 
282     AdapterCount = 0;
283     while (1)
284     {
285         StringCchPrintfW(Name, ARRAYSIZE(Name),
286                          L"%s\\%lu", ROOT_NAME, AdapterCount);
287         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
288                                         Name,
289                                         &QueryTable[2],
290                                         NULL,
291                                         NULL);
292         if (!NT_SUCCESS(Status))
293         {
294             break;
295         }
296 
297         StringCchPrintfW(Name, ARRAYSIZE(Name),
298                          L"%s\\%lu\\DiskController", ROOT_NAME, AdapterCount);
299         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
300                                         Name,
301                                         &QueryTable[2],
302                                         NULL,
303                                         NULL);
304         if (NT_SUCCESS(Status))
305         {
306             while (1)
307             {
308                 StringCchPrintfW(Name, ARRAYSIZE(Name),
309                                  L"%s\\%lu\\DiskController\\0", ROOT_NAME, AdapterCount);
310                 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
311                                                 Name,
312                                                 &QueryTable[2],
313                                                 NULL,
314                                                 NULL);
315                 if (!NT_SUCCESS(Status))
316                 {
317                     RtlFreeHeap(RtlGetProcessHeap(), 0, Int13Drives);
318                     return;
319                 }
320 
321                 StringCchPrintfW(Name, ARRAYSIZE(Name),
322                                  L"%s\\%lu\\DiskController\\0\\DiskPeripheral", ROOT_NAME, AdapterCount);
323                 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
324                                                 Name,
325                                                 &QueryTable[2],
326                                                 NULL,
327                                                 NULL);
328                 if (NT_SUCCESS(Status))
329                 {
330                     QueryTable[0].Name = L"Identifier";
331                     QueryTable[0].QueryRoutine = DiskIdentifierQueryRoutine;
332                     QueryTable[1].Name = L"Configuration Data";
333                     QueryTable[1].QueryRoutine = DiskConfigurationDataQueryRoutine;
334 
335                     DiskCount = 0;
336                     while (1)
337                     {
338                         BiosDiskEntry = (BIOSDISKENTRY*)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BIOSDISKENTRY));
339                         if (BiosDiskEntry == NULL)
340                         {
341                             break;
342                         }
343 
344                         StringCchPrintfW(Name, ARRAYSIZE(Name),
345                                          L"%s\\%lu\\DiskController\\0\\DiskPeripheral\\%lu", ROOT_NAME, AdapterCount, DiskCount);
346                         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
347                                                         Name,
348                                                         QueryTable,
349                                                         (PVOID)BiosDiskEntry,
350                                                         NULL);
351                         if (!NT_SUCCESS(Status))
352                         {
353                             RtlFreeHeap(RtlGetProcessHeap(), 0, BiosDiskEntry);
354                             break;
355                         }
356 
357                         BiosDiskEntry->DiskNumber = DiskCount;
358                         BiosDiskEntry->Recognized = FALSE;
359 
360                         if (DiskCount < Int13Drives[0].NumberDrives)
361                         {
362                             BiosDiskEntry->Int13DiskData = Int13Drives[DiskCount];
363                         }
364                         else
365                         {
366                             DPRINT1("Didn't find int13 drive datas for disk %u\n", DiskCount);
367                         }
368 
369                         InsertTailList(&BiosDiskListHead, &BiosDiskEntry->ListEntry);
370 
371                         DPRINT("DiskNumber:        %lu\n", BiosDiskEntry->DiskNumber);
372                         DPRINT("Signature:         %08lx\n", BiosDiskEntry->Signature);
373                         DPRINT("Checksum:          %08lx\n", BiosDiskEntry->Checksum);
374                         DPRINT("BytesPerSector:    %lu\n", BiosDiskEntry->DiskGeometry.BytesPerSector);
375                         DPRINT("NumberOfCylinders: %lu\n", BiosDiskEntry->DiskGeometry.NumberOfCylinders);
376                         DPRINT("NumberOfHeads:     %lu\n", BiosDiskEntry->DiskGeometry.NumberOfHeads);
377                         DPRINT("DriveSelect:       %02x\n", BiosDiskEntry->Int13DiskData.DriveSelect);
378                         DPRINT("MaxCylinders:      %lu\n", BiosDiskEntry->Int13DiskData.MaxCylinders);
379                         DPRINT("SectorsPerTrack:   %d\n", BiosDiskEntry->Int13DiskData.SectorsPerTrack);
380                         DPRINT("MaxHeads:          %d\n", BiosDiskEntry->Int13DiskData.MaxHeads);
381                         DPRINT("NumberDrives:      %d\n", BiosDiskEntry->Int13DiskData.NumberDrives);
382 
383                         DiskCount++;
384                     }
385                 }
386 
387                 RtlFreeHeap(RtlGetProcessHeap(), 0, Int13Drives);
388                 return;
389             }
390         }
391 
392         AdapterCount++;
393     }
394 
395     RtlFreeHeap(RtlGetProcessHeap(), 0, Int13Drives);
396 }
397 
398 
399 static
400 VOID
401 AddPartitionToDisk(
402     ULONG DiskNumber,
403     PDISKENTRY DiskEntry,
404     ULONG PartitionIndex,
405     BOOLEAN LogicalPartition)
406 {
407     PPARTITION_INFORMATION PartitionInfo;
408     PPARTENTRY PartEntry;
409 
410     PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartitionIndex];
411     if (PartitionInfo->PartitionType == 0 ||
412         (LogicalPartition == TRUE && IsContainerPartition(PartitionInfo->PartitionType)))
413         return;
414 
415     PartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
416                                 HEAP_ZERO_MEMORY,
417                                 sizeof(PARTENTRY));
418     if (PartEntry == NULL)
419     {
420         return;
421     }
422 
423     PartEntry->DiskEntry = DiskEntry;
424 
425     PartEntry->StartSector.QuadPart = (ULONGLONG)PartitionInfo->StartingOffset.QuadPart / DiskEntry->BytesPerSector;
426     PartEntry->SectorCount.QuadPart = (ULONGLONG)PartitionInfo->PartitionLength.QuadPart / DiskEntry->BytesPerSector;
427 
428     PartEntry->BootIndicator = PartitionInfo->BootIndicator;
429     PartEntry->PartitionType = PartitionInfo->PartitionType;
430 
431     PartEntry->LogicalPartition = LogicalPartition;
432     PartEntry->IsPartitioned = TRUE;
433     PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
434     PartEntry->PartitionIndex = PartitionIndex;
435 
436     if (IsContainerPartition(PartEntry->PartitionType))
437     {
438         PartEntry->FormatState = Unformatted;
439 
440         if (LogicalPartition == FALSE && DiskEntry->ExtendedPartition == NULL)
441             DiskEntry->ExtendedPartition = PartEntry;
442     }
443     else if ((PartEntry->PartitionType == PARTITION_FAT_12) ||
444              (PartEntry->PartitionType == PARTITION_FAT_16) ||
445              (PartEntry->PartitionType == PARTITION_HUGE) ||
446              (PartEntry->PartitionType == PARTITION_XINT13) ||
447              (PartEntry->PartitionType == PARTITION_FAT32) ||
448              (PartEntry->PartitionType == PARTITION_FAT32_XINT13))
449     {
450 #if 0
451         if (CheckFatFormat())
452         {
453             PartEntry->FormatState = Preformatted;
454         }
455         else
456         {
457             PartEntry->FormatState = Unformatted;
458         }
459 #endif
460         PartEntry->FormatState = Preformatted;
461     }
462     else if (PartEntry->PartitionType == PARTITION_LINUX)
463     {
464 #if 0
465         if (CheckExt2Format())
466         {
467             PartEntry->FormatState = Preformatted;
468         }
469         else
470         {
471             PartEntry->FormatState = Unformatted;
472         }
473 #endif
474         PartEntry->FormatState = Preformatted;
475     }
476     else if (PartEntry->PartitionType == PARTITION_IFS)
477     {
478 #if 0
479         if (CheckNtfsFormat())
480         {
481             PartEntry->FormatState = Preformatted;
482         }
483         else if (CheckHpfsFormat())
484         {
485             PartEntry->FormatState = Preformatted;
486         }
487         else
488         {
489             PartEntry->FormatState = Unformatted;
490         }
491 #endif
492         PartEntry->FormatState = Preformatted;
493     }
494     else
495     {
496         PartEntry->FormatState = UnknownFormat;
497     }
498 
499     if (LogicalPartition)
500         InsertTailList(&DiskEntry->LogicalPartListHead,
501                        &PartEntry->ListEntry);
502     else
503         InsertTailList(&DiskEntry->PrimaryPartListHead,
504                        &PartEntry->ListEntry);
505 }
506 
507 
508 static
509 VOID
510 ScanForUnpartitionedDiskSpace(
511     PDISKENTRY DiskEntry)
512 {
513     ULONGLONG LastStartSector;
514     ULONGLONG LastSectorCount;
515     ULONGLONG LastUnusedSectorCount;
516     PPARTENTRY PartEntry;
517     PPARTENTRY NewPartEntry;
518     PLIST_ENTRY Entry;
519 
520     DPRINT("ScanForUnpartitionedDiskSpace()\n");
521 
522     if (IsListEmpty(&DiskEntry->PrimaryPartListHead))
523     {
524         DPRINT1("No primary partition!\n");
525 
526         /* Create a partition table that represents the empty disk */
527         NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
528                                        HEAP_ZERO_MEMORY,
529                                        sizeof(PARTENTRY));
530         if (NewPartEntry == NULL)
531             return;
532 
533         NewPartEntry->DiskEntry = DiskEntry;
534 
535         NewPartEntry->IsPartitioned = FALSE;
536         NewPartEntry->StartSector.QuadPart = (ULONGLONG)DiskEntry->SectorAlignment;
537         NewPartEntry->SectorCount.QuadPart = AlignDown(DiskEntry->SectorCount.QuadPart, DiskEntry->SectorAlignment) -
538                                              NewPartEntry->StartSector.QuadPart;
539 
540         DPRINT1("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
541         DPRINT1("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
542         DPRINT1("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
543 
544         NewPartEntry->FormatState = Unformatted;
545 
546         InsertTailList(&DiskEntry->PrimaryPartListHead,
547                        &NewPartEntry->ListEntry);
548 
549         return;
550     }
551 
552     /* Start partition at head 1, cylinder 0 */
553     LastStartSector = DiskEntry->SectorAlignment;
554     LastSectorCount = 0ULL;
555     LastUnusedSectorCount = 0ULL;
556 
557     Entry = DiskEntry->PrimaryPartListHead.Flink;
558     while (Entry != &DiskEntry->PrimaryPartListHead)
559     {
560         PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
561 
562         if (PartEntry->PartitionType != PARTITION_ENTRY_UNUSED ||
563             PartEntry->SectorCount.QuadPart != 0ULL)
564         {
565             LastUnusedSectorCount =
566                 PartEntry->StartSector.QuadPart - (LastStartSector + LastSectorCount);
567 
568             if (PartEntry->StartSector.QuadPart > (LastStartSector + LastSectorCount) &&
569                 LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
570             {
571                 DPRINT("Unpartitioned disk space %I64u sectors\n", LastUnusedSectorCount);
572 
573                 NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
574                                                HEAP_ZERO_MEMORY,
575                                                sizeof(PARTENTRY));
576                 if (NewPartEntry == NULL)
577                     return;
578 
579                 NewPartEntry->DiskEntry = DiskEntry;
580 
581                 NewPartEntry->IsPartitioned = FALSE;
582                 NewPartEntry->StartSector.QuadPart = LastStartSector + LastSectorCount;
583                 NewPartEntry->SectorCount.QuadPart = AlignDown(NewPartEntry->StartSector.QuadPart + LastUnusedSectorCount, DiskEntry->SectorAlignment) -
584                                                      NewPartEntry->StartSector.QuadPart;
585 
586                 DPRINT1("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
587                 DPRINT1("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
588                 DPRINT1("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
589 
590                 NewPartEntry->FormatState = Unformatted;
591 
592                 /* Insert the table into the list */
593                 InsertTailList(&PartEntry->ListEntry,
594                                &NewPartEntry->ListEntry);
595             }
596 
597             LastStartSector = PartEntry->StartSector.QuadPart;
598             LastSectorCount = PartEntry->SectorCount.QuadPart;
599         }
600 
601         Entry = Entry->Flink;
602     }
603 
604     /* Check for trailing unpartitioned disk space */
605     if ((LastStartSector + LastSectorCount) < DiskEntry->SectorCount.QuadPart)
606     {
607         LastUnusedSectorCount = AlignDown(DiskEntry->SectorCount.QuadPart - (LastStartSector + LastSectorCount), DiskEntry->SectorAlignment);
608 
609         if (LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
610         {
611             DPRINT1("Unpartitioned disk space: %I64u sectors\n", LastUnusedSectorCount);
612 
613             NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
614                                            HEAP_ZERO_MEMORY,
615                                            sizeof(PARTENTRY));
616             if (NewPartEntry == NULL)
617                 return;
618 
619             NewPartEntry->DiskEntry = DiskEntry;
620 
621             NewPartEntry->IsPartitioned = FALSE;
622             NewPartEntry->StartSector.QuadPart = LastStartSector + LastSectorCount;
623             NewPartEntry->SectorCount.QuadPart = AlignDown(NewPartEntry->StartSector.QuadPart + LastUnusedSectorCount, DiskEntry->SectorAlignment) -
624                                                  NewPartEntry->StartSector.QuadPart;
625 
626             DPRINT1("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
627             DPRINT1("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
628             DPRINT1("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
629 
630             NewPartEntry->FormatState = Unformatted;
631 
632             /* Append the table to the list */
633             InsertTailList(&DiskEntry->PrimaryPartListHead,
634                            &NewPartEntry->ListEntry);
635         }
636     }
637 
638     if (DiskEntry->ExtendedPartition != NULL)
639     {
640         if (IsListEmpty(&DiskEntry->LogicalPartListHead))
641         {
642             DPRINT1("No logical partition!\n");
643 
644             /* Create a partition table entry that represents the empty extended partition */
645             NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
646                                            HEAP_ZERO_MEMORY,
647                                            sizeof(PARTENTRY));
648             if (NewPartEntry == NULL)
649                 return;
650 
651             NewPartEntry->DiskEntry = DiskEntry;
652             NewPartEntry->LogicalPartition = TRUE;
653 
654             NewPartEntry->IsPartitioned = FALSE;
655             NewPartEntry->StartSector.QuadPart = DiskEntry->ExtendedPartition->StartSector.QuadPart + (ULONGLONG)DiskEntry->SectorAlignment;
656             NewPartEntry->SectorCount.QuadPart = DiskEntry->ExtendedPartition->SectorCount.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment;
657 
658             DPRINT1("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
659             DPRINT1("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
660             DPRINT1("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
661 
662             NewPartEntry->FormatState = Unformatted;
663 
664             InsertTailList(&DiskEntry->LogicalPartListHead,
665                            &NewPartEntry->ListEntry);
666 
667             return;
668         }
669 
670         /* Start partition at head 1, cylinder 0 */
671         LastStartSector = DiskEntry->ExtendedPartition->StartSector.QuadPart + (ULONGLONG)DiskEntry->SectorAlignment;
672         LastSectorCount = 0ULL;
673         LastUnusedSectorCount = 0ULL;
674 
675         Entry = DiskEntry->LogicalPartListHead.Flink;
676         while (Entry != &DiskEntry->LogicalPartListHead)
677         {
678             PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
679 
680             if (PartEntry->PartitionType != PARTITION_ENTRY_UNUSED ||
681                 PartEntry->SectorCount.QuadPart != 0ULL)
682             {
683                 LastUnusedSectorCount =
684                     PartEntry->StartSector.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment - (LastStartSector + LastSectorCount);
685 
686                 if ((PartEntry->StartSector.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment) > (LastStartSector + LastSectorCount) &&
687                     LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
688                 {
689                     DPRINT("Unpartitioned disk space %I64u sectors\n", LastUnusedSectorCount);
690 
691                     NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
692                                                    HEAP_ZERO_MEMORY,
693                                                    sizeof(PARTENTRY));
694                     if (NewPartEntry == NULL)
695                         return;
696 
697                     NewPartEntry->DiskEntry = DiskEntry;
698                     NewPartEntry->LogicalPartition = TRUE;
699 
700                     NewPartEntry->IsPartitioned = FALSE;
701                     NewPartEntry->StartSector.QuadPart = LastStartSector + LastSectorCount;
702                     NewPartEntry->SectorCount.QuadPart = AlignDown(NewPartEntry->StartSector.QuadPart + LastUnusedSectorCount, DiskEntry->SectorAlignment) -
703                                                          NewPartEntry->StartSector.QuadPart;
704 
705                     DPRINT("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
706                     DPRINT("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
707                     DPRINT("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
708 
709                     NewPartEntry->FormatState = Unformatted;
710 
711                     /* Insert the table into the list */
712                     InsertTailList(&PartEntry->ListEntry,
713                                    &NewPartEntry->ListEntry);
714                 }
715 
716                 LastStartSector = PartEntry->StartSector.QuadPart;
717                 LastSectorCount = PartEntry->SectorCount.QuadPart;
718             }
719 
720             Entry = Entry->Flink;
721         }
722 
723         /* Check for trailing unpartitioned disk space */
724         if ((LastStartSector + LastSectorCount) < DiskEntry->ExtendedPartition->StartSector.QuadPart + DiskEntry->ExtendedPartition->SectorCount.QuadPart)
725         {
726             LastUnusedSectorCount = AlignDown(DiskEntry->ExtendedPartition->StartSector.QuadPart + DiskEntry->ExtendedPartition->SectorCount.QuadPart - (LastStartSector + LastSectorCount),
727                                               DiskEntry->SectorAlignment);
728 
729             if (LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
730             {
731                 DPRINT("Unpartitioned disk space: %I64u sectors\n", LastUnusedSectorCount);
732 
733                 NewPartEntry = RtlAllocateHeap(RtlGetProcessHeap(),
734                                                HEAP_ZERO_MEMORY,
735                                                sizeof(PARTENTRY));
736                 if (NewPartEntry == NULL)
737                     return;
738 
739                 NewPartEntry->DiskEntry = DiskEntry;
740                 NewPartEntry->LogicalPartition = TRUE;
741 
742                 NewPartEntry->IsPartitioned = FALSE;
743                 NewPartEntry->StartSector.QuadPart = LastStartSector + LastSectorCount;
744                 NewPartEntry->SectorCount.QuadPart = AlignDown(NewPartEntry->StartSector.QuadPart + LastUnusedSectorCount, DiskEntry->SectorAlignment) -
745                                                      NewPartEntry->StartSector.QuadPart;
746 
747                 DPRINT("First Sector: %I64u\n", NewPartEntry->StartSector.QuadPart);
748                 DPRINT("Last Sector: %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
749                 DPRINT("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
750 
751                 NewPartEntry->FormatState = Unformatted;
752 
753                 /* Append the table to the list */
754                 InsertTailList(&DiskEntry->LogicalPartListHead,
755                                &NewPartEntry->ListEntry);
756             }
757         }
758     }
759 
760     DPRINT("ScanForUnpartitionedDiskSpace() done\n");
761 }
762 
763 
764 static
765 VOID
766 AddDiskToList(
767     HANDLE FileHandle,
768     ULONG DiskNumber)
769 {
770     DISK_GEOMETRY DiskGeometry;
771     SCSI_ADDRESS ScsiAddress;
772     PDISKENTRY DiskEntry;
773     IO_STATUS_BLOCK Iosb;
774     NTSTATUS Status;
775     PPARTITION_SECTOR Mbr;
776     PULONG Buffer;
777     LARGE_INTEGER FileOffset;
778     WCHAR Identifier[20];
779     ULONG Checksum;
780     ULONG Signature;
781     ULONG i;
782     PLIST_ENTRY ListEntry;
783     PBIOSDISKENTRY BiosDiskEntry;
784     ULONG LayoutBufferSize;
785     PDRIVE_LAYOUT_INFORMATION NewLayoutBuffer;
786 
787     Status = NtDeviceIoControlFile(FileHandle,
788                                    NULL,
789                                    NULL,
790                                    NULL,
791                                    &Iosb,
792                                    IOCTL_DISK_GET_DRIVE_GEOMETRY,
793                                    NULL,
794                                    0,
795                                    &DiskGeometry,
796                                    sizeof(DISK_GEOMETRY));
797     if (!NT_SUCCESS(Status))
798     {
799         return;
800     }
801 
802     if (DiskGeometry.MediaType != FixedMedia &&
803         DiskGeometry.MediaType != RemovableMedia)
804     {
805         return;
806     }
807 
808     Status = NtDeviceIoControlFile(FileHandle,
809                                    NULL,
810                                    NULL,
811                                    NULL,
812                                    &Iosb,
813                                    IOCTL_SCSI_GET_ADDRESS,
814                                    NULL,
815                                    0,
816                                    &ScsiAddress,
817                                    sizeof(SCSI_ADDRESS));
818     if (!NT_SUCCESS(Status))
819     {
820         return;
821     }
822 
823     Mbr = (PARTITION_SECTOR*)RtlAllocateHeap(RtlGetProcessHeap(),
824                                              0,
825                                              DiskGeometry.BytesPerSector);
826     if (Mbr == NULL)
827     {
828         return;
829     }
830 
831     FileOffset.QuadPart = 0;
832     Status = NtReadFile(FileHandle,
833                         NULL,
834                         NULL,
835                         NULL,
836                         &Iosb,
837                         (PVOID)Mbr,
838                         DiskGeometry.BytesPerSector,
839                         &FileOffset,
840                         NULL);
841     if (!NT_SUCCESS(Status))
842     {
843         RtlFreeHeap(RtlGetProcessHeap(), 0, Mbr);
844         DPRINT1("NtReadFile failed, status=%x\n", Status);
845         return;
846     }
847     Signature = Mbr->Signature;
848 
849     /* Calculate the MBR checksum */
850     Checksum = 0;
851     Buffer = (PULONG)Mbr;
852     for (i = 0; i < 128; i++)
853     {
854         Checksum += Buffer[i];
855     }
856     Checksum = ~Checksum + 1;
857 
858     StringCchPrintfW(Identifier, ARRAYSIZE(Identifier),
859                      L"%08x-%08x-A", Checksum, Signature);
860     DPRINT("Identifier: %S\n", Identifier);
861 
862     DiskEntry = RtlAllocateHeap(RtlGetProcessHeap(),
863                                 HEAP_ZERO_MEMORY,
864                                 sizeof(DISKENTRY));
865     if (DiskEntry == NULL)
866     {
867         return;
868     }
869 
870 //    DiskEntry->Checksum = Checksum;
871 //    DiskEntry->Signature = Signature;
872     DiskEntry->BiosFound = FALSE;
873 
874     /* Check if this disk has a valid MBR */
875     if (Mbr->Magic != MBR_MAGIC)
876         DiskEntry->NoMbr = TRUE;
877     else
878         DiskEntry->NoMbr = FALSE;
879 
880     /* Free Mbr sector buffer */
881     RtlFreeHeap(RtlGetProcessHeap(), 0, Mbr);
882 
883     ListEntry = BiosDiskListHead.Flink;
884     while (ListEntry != &BiosDiskListHead)
885     {
886         BiosDiskEntry = CONTAINING_RECORD(ListEntry, BIOSDISKENTRY, ListEntry);
887         /* FIXME:
888          *   Compare the size from bios and the reported size from driver.
889          *   If we have more than one disk with a zero or with the same signatur
890          *   we must create new signatures and reboot. After the reboot,
891          *   it is possible to identify the disks.
892          */
893         if (BiosDiskEntry->Signature == Signature &&
894             BiosDiskEntry->Checksum == Checksum &&
895             !BiosDiskEntry->Recognized)
896         {
897             if (!DiskEntry->BiosFound)
898             {
899                 DiskEntry->BiosDiskNumber = BiosDiskEntry->DiskNumber;
900                 DiskEntry->BiosFound = TRUE;
901                 BiosDiskEntry->Recognized = TRUE;
902             }
903             else
904             {
905             }
906         }
907         ListEntry = ListEntry->Flink;
908     }
909 
910     if (!DiskEntry->BiosFound)
911     {
912 #if 0
913         RtlFreeHeap(ProcessHeap, 0, DiskEntry);
914         return;
915 #else
916         DPRINT1("WARNING: Setup could not find a matching BIOS disk entry. Disk %d is not be bootable by the BIOS!\n", DiskNumber);
917 #endif
918     }
919 
920     InitializeListHead(&DiskEntry->PrimaryPartListHead);
921     InitializeListHead(&DiskEntry->LogicalPartListHead);
922 
923     DiskEntry->Cylinders = DiskGeometry.Cylinders.QuadPart;
924     DiskEntry->TracksPerCylinder = DiskGeometry.TracksPerCylinder;
925     DiskEntry->SectorsPerTrack = DiskGeometry.SectorsPerTrack;
926     DiskEntry->BytesPerSector = DiskGeometry.BytesPerSector;
927 
928     DPRINT("Cylinders %I64u\n", DiskEntry->Cylinders);
929     DPRINT("TracksPerCylinder %I64u\n", DiskEntry->TracksPerCylinder);
930     DPRINT("SectorsPerTrack %I64u\n", DiskEntry->SectorsPerTrack);
931     DPRINT("BytesPerSector %I64u\n", DiskEntry->BytesPerSector);
932 
933     DiskEntry->SectorCount.QuadPart = DiskGeometry.Cylinders.QuadPart *
934                                       (ULONGLONG)DiskGeometry.TracksPerCylinder *
935                                       (ULONGLONG)DiskGeometry.SectorsPerTrack;
936 
937 //    DiskEntry->SectorAlignment = DiskGeometry.SectorsPerTrack;
938 //    DiskEntry->CylinderAlignment = DiskGeometry.SectorsPerTrack * DiskGeometry.TracksPerCylinder;
939     DiskEntry->SectorAlignment = (1024 * 1024) / DiskGeometry.BytesPerSector;
940     DiskEntry->CylinderAlignment = (1024 * 1024) / DiskGeometry.BytesPerSector;
941 
942     DPRINT1("SectorCount: %I64u\n", DiskEntry->SectorCount);
943     DPRINT1("SectorAlignment: %lu\n", DiskEntry->SectorAlignment);
944     DPRINT1("CylinderAlignment: %lu\n", DiskEntry->CylinderAlignment);
945 
946     DiskEntry->DiskNumber = DiskNumber;
947     DiskEntry->Port = ScsiAddress.PortNumber;
948     DiskEntry->PathId = ScsiAddress.PathId;
949     DiskEntry->TargetId = ScsiAddress.TargetId;
950     DiskEntry->Lun = ScsiAddress.Lun;
951 
952     GetDriverName(DiskEntry);
953 
954     InsertAscendingList(&DiskListHead, DiskEntry, DISKENTRY, ListEntry, DiskNumber);
955 
956     /* Allocate a layout buffer with 4 partition entries first */
957     LayoutBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
958                        ((4 - ANYSIZE_ARRAY) * sizeof(PARTITION_INFORMATION));
959     DiskEntry->LayoutBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
960                                               HEAP_ZERO_MEMORY,
961                                               LayoutBufferSize);
962     if (DiskEntry->LayoutBuffer == NULL)
963     {
964         DPRINT1("Failed to allocate the disk layout buffer!\n");
965         return;
966     }
967 
968     for (;;)
969     {
970         DPRINT1("Buffer size: %lu\n", LayoutBufferSize);
971         Status = NtDeviceIoControlFile(FileHandle,
972                                        NULL,
973                                        NULL,
974                                        NULL,
975                                        &Iosb,
976                                        IOCTL_DISK_GET_DRIVE_LAYOUT,
977                                        NULL,
978                                        0,
979                                        DiskEntry->LayoutBuffer,
980                                        LayoutBufferSize);
981         if (NT_SUCCESS(Status))
982             break;
983 
984         if (Status != STATUS_BUFFER_TOO_SMALL)
985         {
986             DPRINT1("NtDeviceIoControlFile() failed (Status: 0x%08lx)\n", Status);
987             return;
988         }
989 
990         LayoutBufferSize += 4 * sizeof(PARTITION_INFORMATION);
991         NewLayoutBuffer = RtlReAllocateHeap(RtlGetProcessHeap(),
992                                             HEAP_ZERO_MEMORY,
993                                             DiskEntry->LayoutBuffer,
994                                             LayoutBufferSize);
995         if (NewLayoutBuffer == NULL)
996         {
997             DPRINT1("Failed to reallocate the disk layout buffer!\n");
998             return;
999         }
1000 
1001         DiskEntry->LayoutBuffer = NewLayoutBuffer;
1002     }
1003 
1004     DPRINT1("PartitionCount: %lu\n", DiskEntry->LayoutBuffer->PartitionCount);
1005 
1006 #ifdef DUMP_PARTITION_TABLE
1007     DumpPartitionTable(DiskEntry);
1008 #endif
1009 
1010     if (DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart != 0 &&
1011         DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionLength.QuadPart != 0 &&
1012         DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionType != 0)
1013     {
1014         if ((DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart / DiskEntry->BytesPerSector) % DiskEntry->SectorsPerTrack == 0)
1015         {
1016             DPRINT("Use %lu Sector alignment!\n", DiskEntry->SectorsPerTrack);
1017         }
1018         else if (DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart % (1024 * 1024) == 0)
1019         {
1020             DPRINT1("Use megabyte (%lu Sectors) alignment!\n", (1024 * 1024) / DiskEntry->BytesPerSector);
1021         }
1022         else
1023         {
1024             DPRINT1("No matching alignment found! Partition 1 starts at %I64u\n", DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart);
1025         }
1026     }
1027     else
1028     {
1029         DPRINT1("No valid partition table found! Use megabyte (%lu Sectors) alignment!\n", (1024 * 1024) / DiskEntry->BytesPerSector);
1030     }
1031 
1032 
1033     if (DiskEntry->LayoutBuffer->PartitionCount == 0)
1034     {
1035         DiskEntry->NewDisk = TRUE;
1036         DiskEntry->LayoutBuffer->PartitionCount = 4;
1037 
1038         for (i = 0; i < 4; i++)
1039             DiskEntry->LayoutBuffer->PartitionEntry[i].RewritePartition = TRUE;
1040     }
1041     else
1042     {
1043         for (i = 0; i < 4; i++)
1044         {
1045             AddPartitionToDisk(DiskNumber, DiskEntry, i, FALSE);
1046         }
1047 
1048         for (i = 4; i < DiskEntry->LayoutBuffer->PartitionCount; i += 4)
1049         {
1050             AddPartitionToDisk(DiskNumber, DiskEntry, i, TRUE);
1051         }
1052     }
1053 
1054     ScanForUnpartitionedDiskSpace(DiskEntry);
1055 }
1056 
1057 
1058 NTSTATUS
1059 CreatePartitionList(VOID)
1060 {
1061     OBJECT_ATTRIBUTES ObjectAttributes;
1062     SYSTEM_DEVICE_INFORMATION Sdi;
1063     IO_STATUS_BLOCK Iosb;
1064     ULONG ReturnSize;
1065     NTSTATUS Status;
1066     ULONG DiskNumber;
1067     WCHAR Buffer[MAX_PATH];
1068     UNICODE_STRING Name;
1069     HANDLE FileHandle;
1070 
1071     CurrentDisk = NULL;
1072     CurrentPartition = NULL;
1073 
1074 //    BootDisk = NULL;
1075 //    BootPartition = NULL;
1076 
1077 //    TempDisk = NULL;
1078 //    TempPartition = NULL;
1079 //    FormatState = Start;
1080 
1081     InitializeListHead(&DiskListHead);
1082     InitializeListHead(&BiosDiskListHead);
1083 
1084     EnumerateBiosDiskEntries();
1085 
1086     Status = NtQuerySystemInformation(SystemDeviceInformation,
1087                                       &Sdi,
1088                                       sizeof(SYSTEM_DEVICE_INFORMATION),
1089                                       &ReturnSize);
1090     if (!NT_SUCCESS(Status))
1091     {
1092         return Status;
1093     }
1094 
1095     for (DiskNumber = 0; DiskNumber < Sdi.NumberOfDisks; DiskNumber++)
1096     {
1097         StringCchPrintfW(Buffer, ARRAYSIZE(Buffer),
1098                          L"\\Device\\Harddisk%d\\Partition0",
1099                          DiskNumber);
1100 
1101         RtlInitUnicodeString(&Name,
1102                              Buffer);
1103 
1104         InitializeObjectAttributes(&ObjectAttributes,
1105                                    &Name,
1106                                    0,
1107                                    NULL,
1108                                    NULL);
1109 
1110         Status = NtOpenFile(&FileHandle,
1111                             FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1112                             &ObjectAttributes,
1113                             &Iosb,
1114                             FILE_SHARE_READ,
1115                             FILE_SYNCHRONOUS_IO_NONALERT);
1116         if (NT_SUCCESS(Status))
1117         {
1118             AddDiskToList(FileHandle, DiskNumber);
1119 
1120             NtClose(FileHandle);
1121         }
1122     }
1123 
1124 //    UpdateDiskSignatures(List);
1125 
1126 //    AssignDriveLetters(List);
1127 
1128     return STATUS_SUCCESS;
1129 }
1130 
1131 
1132 VOID
1133 DestroyPartitionList(VOID)
1134 {
1135     PDISKENTRY DiskEntry;
1136     PBIOSDISKENTRY BiosDiskEntry;
1137     PPARTENTRY PartEntry;
1138     PLIST_ENTRY Entry;
1139 
1140     CurrentDisk = NULL;
1141     CurrentPartition = NULL;
1142 
1143     /* Release disk and partition info */
1144     while (!IsListEmpty(&DiskListHead))
1145     {
1146         Entry = RemoveHeadList(&DiskListHead);
1147         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
1148 
1149         /* Release driver name */
1150         RtlFreeUnicodeString(&DiskEntry->DriverName);
1151 
1152         /* Release primary partition list */
1153         while (!IsListEmpty(&DiskEntry->PrimaryPartListHead))
1154         {
1155             Entry = RemoveHeadList(&DiskEntry->PrimaryPartListHead);
1156             PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
1157 
1158             RtlFreeHeap(RtlGetProcessHeap(), 0, PartEntry);
1159         }
1160 
1161         /* Release logical partition list */
1162         while (!IsListEmpty(&DiskEntry->LogicalPartListHead))
1163         {
1164             Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead);
1165             PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
1166 
1167             RtlFreeHeap(RtlGetProcessHeap(), 0, PartEntry);
1168         }
1169 
1170         /* Release layout buffer */
1171         if (DiskEntry->LayoutBuffer != NULL)
1172             RtlFreeHeap(RtlGetProcessHeap(), 0, DiskEntry->LayoutBuffer);
1173 
1174 
1175         /* Release disk entry */
1176         RtlFreeHeap(RtlGetProcessHeap(), 0, DiskEntry);
1177     }
1178 
1179     /* Release the bios disk info */
1180     while (!IsListEmpty(&BiosDiskListHead))
1181     {
1182         Entry = RemoveHeadList(&BiosDiskListHead);
1183         BiosDiskEntry = CONTAINING_RECORD(Entry, BIOSDISKENTRY, ListEntry);
1184 
1185         RtlFreeHeap(RtlGetProcessHeap(), 0, BiosDiskEntry);
1186     }
1187 }
1188 
1189 
1190 static
1191 VOID
1192 GetVolumeExtents(
1193     _In_ HANDLE VolumeHandle,
1194     _In_ PVOLENTRY VolumeEntry)
1195 {
1196     DWORD dwBytesReturned = 0, dwLength, i;
1197     PVOLUME_DISK_EXTENTS pExtents;
1198     BOOL bResult;
1199     DWORD dwError;
1200 
1201     dwLength = sizeof(VOLUME_DISK_EXTENTS);
1202     pExtents = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
1203     if (pExtents == NULL)
1204         return;
1205 
1206     bResult = DeviceIoControl(VolumeHandle,
1207                               IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
1208                               NULL,
1209                               0,
1210                               pExtents,
1211                               dwLength,
1212                               &dwBytesReturned,
1213                               NULL);
1214     if (!bResult)
1215     {
1216         dwError = GetLastError();
1217 
1218         if (dwError != ERROR_MORE_DATA)
1219         {
1220             RtlFreeHeap(RtlGetProcessHeap(), 0, pExtents);
1221             return;
1222         }
1223         else
1224         {
1225             dwLength = sizeof(VOLUME_DISK_EXTENTS) + ((pExtents->NumberOfDiskExtents - 1) * sizeof(DISK_EXTENT));
1226             RtlFreeHeap(RtlGetProcessHeap(), 0, pExtents);
1227             pExtents = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
1228             if (pExtents == NULL)
1229             {
1230                 return;
1231             }
1232 
1233             bResult = DeviceIoControl(VolumeHandle,
1234                                       IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
1235                                       NULL,
1236                                       0,
1237                                       pExtents,
1238                                       dwLength,
1239                                       &dwBytesReturned,
1240                                       NULL);
1241             if (!bResult)
1242             {
1243                 RtlFreeHeap(RtlGetProcessHeap(), 0, pExtents);
1244                 return;
1245             }
1246         }
1247     }
1248 
1249     for (i = 0; i < pExtents->NumberOfDiskExtents; i++)
1250         VolumeEntry->Size.QuadPart += pExtents->Extents[i].ExtentLength.QuadPart;
1251 
1252     VolumeEntry->pExtents = pExtents;
1253 }
1254 
1255 
1256 static
1257 VOID
1258 GetVolumeType(
1259     _In_ HANDLE VolumeHandle,
1260     _In_ PVOLENTRY VolumeEntry)
1261 {
1262     FILE_FS_DEVICE_INFORMATION DeviceInfo;
1263     IO_STATUS_BLOCK IoStatusBlock;
1264     NTSTATUS Status;
1265 
1266     Status = NtQueryVolumeInformationFile(VolumeHandle,
1267                                           &IoStatusBlock,
1268                                           &DeviceInfo,
1269                                           sizeof(FILE_FS_DEVICE_INFORMATION),
1270                                           FileFsDeviceInformation);
1271     if (!NT_SUCCESS(Status))
1272         return;
1273 
1274     switch (DeviceInfo.DeviceType)
1275     {
1276         case FILE_DEVICE_CD_ROM:
1277         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
1278             VolumeEntry->VolumeType = VOLUME_TYPE_CDROM;
1279             break;
1280 
1281         case FILE_DEVICE_DISK:
1282         case FILE_DEVICE_DISK_FILE_SYSTEM:
1283             if (DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
1284                 VolumeEntry->VolumeType = VOLUME_TYPE_REMOVABLE;
1285             else
1286                 VolumeEntry->VolumeType = VOLUME_TYPE_PARTITION;
1287             break;
1288 
1289         default:
1290             VolumeEntry->VolumeType = VOLUME_TYPE_UNKNOWN;
1291             break;
1292     }
1293 }
1294 
1295 
1296 static
1297 VOID
1298 AddVolumeToList(
1299     ULONG ulVolumeNumber,
1300     PWSTR pszVolumeName)
1301 {
1302     PVOLENTRY VolumeEntry;
1303     HANDLE VolumeHandle;
1304 
1305     DWORD dwError, dwLength;
1306     WCHAR szPathNames[MAX_PATH + 1];
1307     WCHAR szVolumeName[MAX_PATH + 1];
1308     WCHAR szFilesystem[MAX_PATH + 1];
1309 
1310     DWORD  CharCount            = 0;
1311     size_t Index                = 0;
1312 
1313     OBJECT_ATTRIBUTES ObjectAttributes;
1314     UNICODE_STRING Name;
1315     IO_STATUS_BLOCK Iosb;
1316     NTSTATUS Status;
1317 
1318     DPRINT("AddVolumeToList(%S)\n", pszVolumeName);
1319 
1320     VolumeEntry = RtlAllocateHeap(RtlGetProcessHeap(),
1321                                   HEAP_ZERO_MEMORY,
1322                                   sizeof(VOLENTRY));
1323     if (VolumeEntry == NULL)
1324         return;
1325 
1326     VolumeEntry->VolumeNumber = ulVolumeNumber;
1327     wcscpy(VolumeEntry->VolumeName, pszVolumeName);
1328 
1329     Index = wcslen(pszVolumeName) - 1;
1330 
1331     pszVolumeName[Index] = L'\0';
1332 
1333     CharCount = QueryDosDeviceW(&pszVolumeName[4], VolumeEntry->DeviceName, ARRAYSIZE(VolumeEntry->DeviceName));
1334 
1335     pszVolumeName[Index] = L'\\';
1336 
1337     if (CharCount == 0)
1338     {
1339         RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry);
1340         return;
1341     }
1342 
1343     DPRINT("DeviceName: %S\n", VolumeEntry->DeviceName);
1344 
1345     RtlInitUnicodeString(&Name, VolumeEntry->DeviceName);
1346 
1347     InitializeObjectAttributes(&ObjectAttributes,
1348                                &Name,
1349                                0,
1350                                NULL,
1351                                NULL);
1352 
1353     Status = NtOpenFile(&VolumeHandle,
1354                         SYNCHRONIZE,
1355                         &ObjectAttributes,
1356                         &Iosb,
1357                         0,
1358                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
1359     if (NT_SUCCESS(Status))
1360     {
1361         GetVolumeType(VolumeHandle, VolumeEntry);
1362         GetVolumeExtents(VolumeHandle, VolumeEntry);
1363         NtClose(VolumeHandle);
1364     }
1365 
1366     if (GetVolumeInformationW(pszVolumeName,
1367                               szVolumeName,
1368                               MAX_PATH + 1,
1369                               NULL, //  [out, optional] LPDWORD lpVolumeSerialNumber,
1370                               NULL, //  [out, optional] LPDWORD lpMaximumComponentLength,
1371                               NULL, //  [out, optional] LPDWORD lpFileSystemFlags,
1372                               szFilesystem,
1373                               MAX_PATH + 1))
1374     {
1375         VolumeEntry->pszLabel = RtlAllocateHeap(RtlGetProcessHeap(),
1376                                                 0,
1377                                                 (wcslen(szVolumeName) + 1) * sizeof(WCHAR));
1378         if (VolumeEntry->pszLabel)
1379             wcscpy(VolumeEntry->pszLabel, szVolumeName);
1380 
1381         VolumeEntry->pszFilesystem = RtlAllocateHeap(RtlGetProcessHeap(),
1382                                                      0,
1383                                                      (wcslen(szFilesystem) + 1) * sizeof(WCHAR));
1384         if (VolumeEntry->pszFilesystem)
1385             wcscpy(VolumeEntry->pszFilesystem, szFilesystem);
1386     }
1387     else
1388     {
1389         dwError = GetLastError();
1390         if (dwError == ERROR_UNRECOGNIZED_VOLUME)
1391         {
1392             VolumeEntry->pszFilesystem = RtlAllocateHeap(RtlGetProcessHeap(),
1393                                                          0,
1394                                                          (3 + 1) * sizeof(WCHAR));
1395             if (VolumeEntry->pszFilesystem)
1396                 wcscpy(VolumeEntry->pszFilesystem, L"RAW");
1397         }
1398     }
1399 
1400     if (GetVolumePathNamesForVolumeNameW(pszVolumeName,
1401                                          szPathNames,
1402                                          ARRAYSIZE(szPathNames),
1403                                          &dwLength))
1404     {
1405         VolumeEntry->DriveLetter = szPathNames[0];
1406     }
1407 
1408     InsertTailList(&VolumeListHead,
1409                    &VolumeEntry->ListEntry);
1410 }
1411 
1412 
1413 NTSTATUS
1414 CreateVolumeList(VOID)
1415 {
1416     HANDLE hVolume = INVALID_HANDLE_VALUE;
1417     WCHAR szVolumeName[MAX_PATH];
1418     ULONG ulVolumeNumber = 0;
1419     BOOL Success;
1420 
1421     CurrentVolume = NULL;
1422 
1423     InitializeListHead(&VolumeListHead);
1424 
1425     hVolume = FindFirstVolumeW(szVolumeName, ARRAYSIZE(szVolumeName));
1426     if (hVolume == INVALID_HANDLE_VALUE)
1427     {
1428 
1429         return STATUS_UNSUCCESSFUL;
1430     }
1431 
1432     AddVolumeToList(ulVolumeNumber++, szVolumeName);
1433 
1434     for (;;)
1435     {
1436         Success = FindNextVolumeW(hVolume, szVolumeName, ARRAYSIZE(szVolumeName));
1437         if (!Success)
1438         {
1439             break;
1440         }
1441 
1442         AddVolumeToList(ulVolumeNumber++, szVolumeName);
1443     }
1444 
1445     FindVolumeClose(hVolume);
1446 
1447     return STATUS_SUCCESS;
1448 }
1449 
1450 
1451 VOID
1452 DestroyVolumeList(VOID)
1453 {
1454     PLIST_ENTRY Entry;
1455     PVOLENTRY VolumeEntry;
1456 
1457     CurrentVolume = NULL;
1458 
1459     /* Release disk and partition info */
1460     while (!IsListEmpty(&VolumeListHead))
1461     {
1462         Entry = RemoveHeadList(&VolumeListHead);
1463         VolumeEntry = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
1464 
1465         if (VolumeEntry->pszLabel)
1466             RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pszLabel);
1467 
1468         if (VolumeEntry->pszFilesystem)
1469             RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pszFilesystem);
1470 
1471         if (VolumeEntry->pExtents)
1472             RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pExtents);
1473 
1474         /* Release disk entry */
1475         RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry);
1476     }
1477 }
1478 
1479 
1480 NTSTATUS
1481 WritePartitions(
1482     _In_ PDISKENTRY DiskEntry)
1483 {
1484     NTSTATUS Status;
1485     OBJECT_ATTRIBUTES ObjectAttributes;
1486     UNICODE_STRING Name;
1487     HANDLE FileHandle;
1488     IO_STATUS_BLOCK Iosb;
1489     ULONG BufferSize;
1490     PPARTITION_INFORMATION PartitionInfo;
1491     ULONG PartitionCount;
1492     PLIST_ENTRY ListEntry;
1493     PPARTENTRY PartEntry;
1494     WCHAR DstPath[MAX_PATH];
1495 
1496     DPRINT("WritePartitions() Disk: %lu\n", DiskEntry->DiskNumber);
1497 
1498     /* If the disk is not dirty, there is nothing to do */
1499     if (!DiskEntry->Dirty)
1500         return STATUS_SUCCESS;
1501 
1502     StringCchPrintfW(DstPath, ARRAYSIZE(DstPath),
1503                      L"\\Device\\Harddisk%lu\\Partition0",
1504                      DiskEntry->DiskNumber);
1505     RtlInitUnicodeString(&Name, DstPath);
1506 
1507     InitializeObjectAttributes(&ObjectAttributes,
1508                                &Name,
1509                                OBJ_CASE_INSENSITIVE,
1510                                NULL,
1511                                NULL);
1512 
1513     Status = NtOpenFile(&FileHandle,
1514                         GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
1515                         &ObjectAttributes,
1516                         &Iosb,
1517                         0,
1518                         FILE_SYNCHRONOUS_IO_NONALERT);
1519     if (!NT_SUCCESS(Status))
1520     {
1521         DPRINT1("NtOpenFile() failed (Status %lx)\n", Status);
1522         return Status;
1523     }
1524 
1525     //
1526     // FIXME: We first *MUST* use IOCTL_DISK_CREATE_DISK to initialize
1527     // the disk in MBR or GPT format in case the disk was not initialized!!
1528     // For this we must ask the user which format to use.
1529     //
1530 
1531     /* Save the original partition count to be restored later (see comment below) */
1532     PartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
1533 
1534     /* Set the new disk layout and retrieve its updated version with possibly modified partition numbers */
1535     BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
1536                  ((PartitionCount - 1) * sizeof(PARTITION_INFORMATION));
1537     Status = NtDeviceIoControlFile(FileHandle,
1538                                    NULL,
1539                                    NULL,
1540                                    NULL,
1541                                    &Iosb,
1542                                    IOCTL_DISK_SET_DRIVE_LAYOUT,
1543                                    DiskEntry->LayoutBuffer,
1544                                    BufferSize,
1545                                    DiskEntry->LayoutBuffer,
1546                                    BufferSize);
1547     NtClose(FileHandle);
1548 
1549     /*
1550      * IOCTL_DISK_SET_DRIVE_LAYOUT calls IoWritePartitionTable(), which converts
1551      * DiskEntry->LayoutBuffer->PartitionCount into a partition *table* count,
1552      * where such a table is expected to enumerate up to 4 partitions:
1553      * partition *table* count == ROUND_UP(PartitionCount, 4) / 4 .
1554      * Due to this we need to restore the original PartitionCount number.
1555      */
1556     DiskEntry->LayoutBuffer->PartitionCount = PartitionCount;
1557 
1558     /* Check whether the IOCTL_DISK_SET_DRIVE_LAYOUT call succeeded */
1559     if (!NT_SUCCESS(Status))
1560     {
1561         DPRINT1("IOCTL_DISK_SET_DRIVE_LAYOUT failed (Status 0x%08lx)\n", Status);
1562         return Status;
1563     }
1564 
1565     /* Update the partition numbers */
1566 
1567     /* Update the primary partition table */
1568     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
1569          ListEntry != &DiskEntry->PrimaryPartListHead;
1570          ListEntry = ListEntry->Flink)
1571     {
1572         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1573 
1574         if (PartEntry->IsPartitioned)
1575         {
1576             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1577             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
1578             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
1579         }
1580     }
1581 
1582     /* Update the logical partition table */
1583     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
1584          ListEntry != &DiskEntry->LogicalPartListHead;
1585          ListEntry = ListEntry->Flink)
1586     {
1587         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1588 
1589         if (PartEntry->IsPartitioned)
1590         {
1591             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1592             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
1593             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
1594         }
1595     }
1596 
1597     /* The layout has been successfully updated, the disk is not dirty anymore */
1598     DiskEntry->Dirty = FALSE;
1599 
1600     return Status;
1601 }
1602 
1603 
1604 static
1605 BOOLEAN
1606 IsEmptyLayoutEntry(
1607     IN PPARTITION_INFORMATION PartitionInfo)
1608 {
1609     if (PartitionInfo->StartingOffset.QuadPart == 0 &&
1610         PartitionInfo->PartitionLength.QuadPart == 0)
1611     {
1612         return TRUE;
1613     }
1614 
1615     return FALSE;
1616 }
1617 
1618 
1619 static
1620 BOOLEAN
1621 IsSamePrimaryLayoutEntry(
1622     IN PPARTITION_INFORMATION PartitionInfo,
1623     IN PDISKENTRY DiskEntry,
1624     IN PPARTENTRY PartEntry)
1625 {
1626     if ((PartitionInfo->StartingOffset.QuadPart == PartEntry->StartSector.QuadPart * DiskEntry->BytesPerSector) &&
1627         (PartitionInfo->PartitionLength.QuadPart == PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector))
1628     {
1629         return TRUE;
1630     }
1631 
1632     return FALSE;
1633 }
1634 
1635 
1636 ULONG
1637 GetPrimaryPartitionCount(
1638     _In_ PDISKENTRY DiskEntry)
1639 {
1640     PLIST_ENTRY Entry;
1641     PPARTENTRY PartEntry;
1642     ULONG Count = 0;
1643 
1644     for (Entry = DiskEntry->PrimaryPartListHead.Flink;
1645          Entry != &DiskEntry->PrimaryPartListHead;
1646          Entry = Entry->Flink)
1647     {
1648         PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
1649         if (PartEntry->IsPartitioned)
1650             Count++;
1651     }
1652 
1653     return Count;
1654 }
1655 
1656 
1657 static
1658 ULONG
1659 GetLogicalPartitionCount(
1660     _In_ PDISKENTRY DiskEntry)
1661 {
1662     PLIST_ENTRY ListEntry;
1663     PPARTENTRY PartEntry;
1664     ULONG Count = 0;
1665 
1666     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
1667          ListEntry != &DiskEntry->LogicalPartListHead;
1668          ListEntry = ListEntry->Flink)
1669     {
1670         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1671         if (PartEntry->IsPartitioned)
1672             Count++;
1673     }
1674 
1675     return Count;
1676 }
1677 
1678 
1679 static
1680 BOOLEAN
1681 ReAllocateLayoutBuffer(
1682     _In_ PDISKENTRY DiskEntry)
1683 {
1684     PDRIVE_LAYOUT_INFORMATION NewLayoutBuffer;
1685     ULONG NewPartitionCount;
1686     ULONG CurrentPartitionCount = 0;
1687     ULONG LayoutBufferSize;
1688     ULONG i;
1689 
1690     DPRINT1("ReAllocateLayoutBuffer()\n");
1691 
1692     NewPartitionCount = 4 + GetLogicalPartitionCount(DiskEntry) * 4;
1693 
1694     if (DiskEntry->LayoutBuffer)
1695         CurrentPartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
1696 
1697     DPRINT1("CurrentPartitionCount: %lu ; NewPartitionCount: %lu\n",
1698             CurrentPartitionCount, NewPartitionCount);
1699 
1700     if (CurrentPartitionCount == NewPartitionCount)
1701         return TRUE;
1702 
1703     LayoutBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
1704                        ((NewPartitionCount - ANYSIZE_ARRAY) * sizeof(PARTITION_INFORMATION));
1705     NewLayoutBuffer = RtlReAllocateHeap(RtlGetProcessHeap(),
1706                                         HEAP_ZERO_MEMORY,
1707                                         DiskEntry->LayoutBuffer,
1708                                         LayoutBufferSize);
1709     if (NewLayoutBuffer == NULL)
1710     {
1711         DPRINT1("Failed to allocate the new layout buffer (size: %lu)\n", LayoutBufferSize);
1712         return FALSE;
1713     }
1714 
1715     NewLayoutBuffer->PartitionCount = NewPartitionCount;
1716 
1717     /* If the layout buffer grows, make sure the new (empty) entries are written to the disk */
1718     if (NewPartitionCount > CurrentPartitionCount)
1719     {
1720         for (i = CurrentPartitionCount; i < NewPartitionCount; i++)
1721         {
1722             NewLayoutBuffer->PartitionEntry[i].RewritePartition = TRUE;
1723         }
1724     }
1725 
1726     DiskEntry->LayoutBuffer = NewLayoutBuffer;
1727 
1728     return TRUE;
1729 }
1730 
1731 
1732 VOID
1733 UpdateDiskLayout(
1734     _In_ PDISKENTRY DiskEntry)
1735 {
1736     PPARTITION_INFORMATION PartitionInfo;
1737     PPARTITION_INFORMATION LinkInfo = NULL;
1738     PLIST_ENTRY ListEntry;
1739     PPARTENTRY PartEntry;
1740     LARGE_INTEGER HiddenSectors64;
1741     ULONG Index;
1742     ULONG PartitionNumber = 1;
1743 
1744     DPRINT1("UpdateDiskLayout()\n");
1745 
1746     /* Resize the layout buffer if necessary */
1747     if (ReAllocateLayoutBuffer(DiskEntry) == FALSE)
1748     {
1749         DPRINT("ReAllocateLayoutBuffer() failed.\n");
1750         return;
1751     }
1752 
1753     /* Update the primary partition table */
1754     Index = 0;
1755     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
1756          ListEntry != &DiskEntry->PrimaryPartListHead;
1757          ListEntry = ListEntry->Flink)
1758     {
1759         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1760 
1761         if (PartEntry->IsPartitioned)
1762         {
1763             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1764 
1765             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
1766             PartEntry->PartitionIndex = Index;
1767 
1768             /* Reset the current partition number only for newly-created (unmounted) partitions */
1769             if (PartEntry->New)
1770                 PartEntry->PartitionNumber = 0;
1771 
1772             PartEntry->OnDiskPartitionNumber = (!IsContainerPartition(PartEntry->PartitionType) ? PartitionNumber : 0);
1773 
1774             if (!IsSamePrimaryLayoutEntry(PartitionInfo, DiskEntry, PartEntry))
1775             {
1776                 DPRINT1("Updating primary partition entry %lu\n", Index);
1777 
1778                 PartitionInfo->StartingOffset.QuadPart = PartEntry->StartSector.QuadPart * DiskEntry->BytesPerSector;
1779                 PartitionInfo->PartitionLength.QuadPart = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
1780                 PartitionInfo->HiddenSectors = PartEntry->StartSector.LowPart;
1781                 PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
1782                 PartitionInfo->PartitionType = PartEntry->PartitionType;
1783                 PartitionInfo->BootIndicator = PartEntry->BootIndicator;
1784                 PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
1785                 PartitionInfo->RewritePartition = TRUE;
1786             }
1787 
1788             if (!IsContainerPartition(PartEntry->PartitionType))
1789                 PartitionNumber++;
1790 
1791             Index++;
1792         }
1793     }
1794 
1795     ASSERT(Index <= 4);
1796 
1797     /* Update the logical partition table */
1798     Index = 4;
1799     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
1800          ListEntry != &DiskEntry->LogicalPartListHead;
1801          ListEntry = ListEntry->Flink)
1802     {
1803         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1804 
1805         if (PartEntry->IsPartitioned)
1806         {
1807             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1808 
1809             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
1810             PartEntry->PartitionIndex = Index;
1811 
1812             /* Reset the current partition number only for newly-created (unmounted) partitions */
1813             if (PartEntry->New)
1814                 PartEntry->PartitionNumber = 0;
1815 
1816             PartEntry->OnDiskPartitionNumber = PartitionNumber;
1817 
1818             DPRINT1("Updating logical partition entry %lu\n", Index);
1819 
1820             PartitionInfo->StartingOffset.QuadPart = PartEntry->StartSector.QuadPart * DiskEntry->BytesPerSector;
1821             PartitionInfo->PartitionLength.QuadPart = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
1822             PartitionInfo->HiddenSectors = DiskEntry->SectorAlignment;
1823             PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
1824             PartitionInfo->PartitionType = PartEntry->PartitionType;
1825             PartitionInfo->BootIndicator = FALSE;
1826             PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
1827             PartitionInfo->RewritePartition = TRUE;
1828 
1829             /* Fill the link entry of the previous partition entry */
1830             if (LinkInfo != NULL)
1831             {
1832                 LinkInfo->StartingOffset.QuadPart = (PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
1833                 LinkInfo->PartitionLength.QuadPart = (PartEntry->StartSector.QuadPart + DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
1834                 HiddenSectors64.QuadPart = PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment - DiskEntry->ExtendedPartition->StartSector.QuadPart;
1835                 LinkInfo->HiddenSectors = HiddenSectors64.LowPart;
1836                 LinkInfo->PartitionNumber = 0;
1837                 LinkInfo->PartitionType = PARTITION_EXTENDED;
1838                 LinkInfo->BootIndicator = FALSE;
1839                 LinkInfo->RecognizedPartition = FALSE;
1840                 LinkInfo->RewritePartition = TRUE;
1841             }
1842 
1843             /* Save a pointer to the link entry of the current partition entry */
1844             LinkInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index + 1];
1845 
1846             PartitionNumber++;
1847             Index += 4;
1848         }
1849     }
1850 
1851     /* Wipe unused primary partition entries */
1852     for (Index = GetPrimaryPartitionCount(DiskEntry); Index < 4; Index++)
1853     {
1854         DPRINT1("Primary partition entry %lu\n", Index);
1855 
1856         PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
1857 
1858         if (!IsEmptyLayoutEntry(PartitionInfo))
1859         {
1860             DPRINT1("Wiping primary partition entry %lu\n", Index);
1861 
1862             PartitionInfo->StartingOffset.QuadPart = 0;
1863             PartitionInfo->PartitionLength.QuadPart = 0;
1864             PartitionInfo->HiddenSectors = 0;
1865             PartitionInfo->PartitionNumber = 0;
1866             PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
1867             PartitionInfo->BootIndicator = FALSE;
1868             PartitionInfo->RecognizedPartition = FALSE;
1869             PartitionInfo->RewritePartition = TRUE;
1870         }
1871     }
1872 
1873     /* Wipe unused logical partition entries */
1874     for (Index = 4; Index < DiskEntry->LayoutBuffer->PartitionCount; Index++)
1875     {
1876         if (Index % 4 >= 2)
1877         {
1878             DPRINT1("Logical partition entry %lu\n", Index);
1879 
1880             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
1881 
1882             if (!IsEmptyLayoutEntry(PartitionInfo))
1883             {
1884                 DPRINT1("Wiping partition entry %lu\n", Index);
1885 
1886                 PartitionInfo->StartingOffset.QuadPart = 0;
1887                 PartitionInfo->PartitionLength.QuadPart = 0;
1888                 PartitionInfo->HiddenSectors = 0;
1889                 PartitionInfo->PartitionNumber = 0;
1890                 PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
1891                 PartitionInfo->BootIndicator = FALSE;
1892                 PartitionInfo->RecognizedPartition = FALSE;
1893                 PartitionInfo->RewritePartition = TRUE;
1894             }
1895         }
1896     }
1897 
1898     DiskEntry->Dirty = TRUE;
1899 }
1900 
1901 
1902 PPARTENTRY
1903 GetPrevUnpartitionedEntry(
1904     _In_ PPARTENTRY PartEntry)
1905 {
1906     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
1907     PPARTENTRY PrevPartEntry;
1908     PLIST_ENTRY ListHead;
1909 
1910     if (PartEntry->LogicalPartition)
1911         ListHead = &DiskEntry->LogicalPartListHead;
1912     else
1913         ListHead = &DiskEntry->PrimaryPartListHead;
1914 
1915     if (PartEntry->ListEntry.Blink != ListHead)
1916     {
1917         PrevPartEntry = CONTAINING_RECORD(PartEntry->ListEntry.Blink,
1918                                           PARTENTRY,
1919                                           ListEntry);
1920         if (!PrevPartEntry->IsPartitioned)
1921         {
1922             ASSERT(PrevPartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
1923             return PrevPartEntry;
1924         }
1925     }
1926 
1927     return NULL;
1928 }
1929 
1930 
1931 PPARTENTRY
1932 GetNextUnpartitionedEntry(
1933     _In_ PPARTENTRY PartEntry)
1934 {
1935     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
1936     PPARTENTRY NextPartEntry;
1937     PLIST_ENTRY ListHead;
1938 
1939     if (PartEntry->LogicalPartition)
1940         ListHead = &DiskEntry->LogicalPartListHead;
1941     else
1942         ListHead = &DiskEntry->PrimaryPartListHead;
1943 
1944     if (PartEntry->ListEntry.Flink != ListHead)
1945     {
1946         NextPartEntry = CONTAINING_RECORD(PartEntry->ListEntry.Flink,
1947                                           PARTENTRY,
1948                                           ListEntry);
1949         if (!NextPartEntry->IsPartitioned)
1950         {
1951             ASSERT(NextPartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
1952             return NextPartEntry;
1953         }
1954     }
1955 
1956     return NULL;
1957 }
1958 
1959 NTSTATUS
1960 DismountVolume(
1961     _In_ PPARTENTRY PartEntry)
1962 {
1963     NTSTATUS Status;
1964     NTSTATUS LockStatus;
1965     UNICODE_STRING Name;
1966     OBJECT_ATTRIBUTES ObjectAttributes;
1967     IO_STATUS_BLOCK IoStatusBlock;
1968     HANDLE PartitionHandle;
1969     WCHAR Buffer[MAX_PATH];
1970 
1971     /* Check whether the partition is valid and was mounted by the system */
1972     if (!PartEntry->IsPartitioned ||
1973         IsContainerPartition(PartEntry->PartitionType)   ||
1974         !IsRecognizedPartition(PartEntry->PartitionType) ||
1975         PartEntry->FormatState == UnknownFormat ||
1976         // NOTE: If FormatState == Unformatted but *FileSystem != 0 this means
1977         // it has been usually mounted with RawFS and thus needs to be dismounted.
1978 /*        !*PartEntry->FileSystem || */
1979         PartEntry->PartitionNumber == 0)
1980     {
1981         /* The partition is not mounted, so just return success */
1982         return STATUS_SUCCESS;
1983     }
1984 
1985     ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1986 
1987     /* Open the volume */
1988     StringCchPrintfW(Buffer, ARRAYSIZE(Buffer),
1989                      L"\\Device\\Harddisk%lu\\Partition%lu",
1990                      PartEntry->DiskEntry->DiskNumber,
1991                      PartEntry->PartitionNumber);
1992     RtlInitUnicodeString(&Name, Buffer);
1993 
1994     InitializeObjectAttributes(&ObjectAttributes,
1995                                &Name,
1996                                OBJ_CASE_INSENSITIVE,
1997                                NULL,
1998                                NULL);
1999 
2000     Status = NtOpenFile(&PartitionHandle,
2001                         GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
2002                         &ObjectAttributes,
2003                         &IoStatusBlock,
2004                         FILE_SHARE_READ | FILE_SHARE_WRITE,
2005                         FILE_SYNCHRONOUS_IO_NONALERT);
2006     if (!NT_SUCCESS(Status))
2007     {
2008         DPRINT1("ERROR: Cannot open volume %wZ for dismounting! (Status 0x%lx)\n", &Name, Status);
2009         return Status;
2010     }
2011 
2012     /* Lock the volume */
2013     LockStatus = NtFsControlFile(PartitionHandle,
2014                                  NULL,
2015                                  NULL,
2016                                  NULL,
2017                                  &IoStatusBlock,
2018                                  FSCTL_LOCK_VOLUME,
2019                                  NULL,
2020                                  0,
2021                                  NULL,
2022                                  0);
2023     if (!NT_SUCCESS(LockStatus))
2024     {
2025         DPRINT1("WARNING: Failed to lock volume! Operations may fail! (Status 0x%lx)\n", LockStatus);
2026     }
2027 
2028     /* Dismount the volume */
2029     Status = NtFsControlFile(PartitionHandle,
2030                              NULL,
2031                              NULL,
2032                              NULL,
2033                              &IoStatusBlock,
2034                              FSCTL_DISMOUNT_VOLUME,
2035                              NULL,
2036                              0,
2037                              NULL,
2038                              0);
2039     if (!NT_SUCCESS(Status))
2040     {
2041         DPRINT1("Failed to unmount volume (Status 0x%lx)\n", Status);
2042     }
2043 
2044     /* Unlock the volume */
2045     LockStatus = NtFsControlFile(PartitionHandle,
2046                                  NULL,
2047                                  NULL,
2048                                  NULL,
2049                                  &IoStatusBlock,
2050                                  FSCTL_UNLOCK_VOLUME,
2051                                  NULL,
2052                                  0,
2053                                  NULL,
2054                                  0);
2055     if (!NT_SUCCESS(LockStatus))
2056     {
2057         DPRINT1("Failed to unlock volume (Status 0x%lx)\n", LockStatus);
2058     }
2059 
2060     /* Close the volume */
2061     NtClose(PartitionHandle);
2062 
2063     return Status;
2064 }
2065 
2066 
2067 PVOLENTRY
2068 GetVolumeFromPartition(
2069     _In_ PPARTENTRY PartEntry)
2070 {
2071     PLIST_ENTRY Entry;
2072     PVOLENTRY VolumeEntry;
2073     ULONG i;
2074 
2075     if ((PartEntry == NULL) ||
2076         (PartEntry->DiskEntry == NULL))
2077         return NULL;
2078 
2079     Entry = VolumeListHead.Flink;
2080     while (Entry != &VolumeListHead)
2081     {
2082         VolumeEntry = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
2083 
2084         if (VolumeEntry->pExtents == NULL)
2085             return NULL;
2086 
2087         for (i = 0; i < VolumeEntry->pExtents->NumberOfDiskExtents; i++)
2088         {
2089             if (VolumeEntry->pExtents->Extents[i].DiskNumber == PartEntry->DiskEntry->DiskNumber)
2090             {
2091                 if ((VolumeEntry->pExtents->Extents[i].StartingOffset.QuadPart == PartEntry->StartSector.QuadPart * PartEntry->DiskEntry->BytesPerSector) &&
2092                     (VolumeEntry->pExtents->Extents[i].ExtentLength.QuadPart == PartEntry->SectorCount.QuadPart * PartEntry->DiskEntry->BytesPerSector))
2093                     return VolumeEntry;
2094             }
2095         }
2096 
2097         Entry = Entry->Flink;
2098     }
2099 
2100     return NULL;
2101 }
2102 
2103 
2104 VOID
2105 RemoveVolume(
2106     _In_ PVOLENTRY VolumeEntry)
2107 {
2108     if (VolumeEntry == NULL)
2109         return;
2110 
2111     if (VolumeEntry == CurrentVolume)
2112         CurrentVolume = NULL;
2113 
2114     RemoveEntryList(&VolumeEntry->ListEntry);
2115 
2116     if (VolumeEntry->pszLabel)
2117         RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pszLabel);
2118 
2119     if (VolumeEntry->pszFilesystem)
2120         RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pszFilesystem);
2121 
2122     if (VolumeEntry->pExtents)
2123         RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry->pExtents);
2124 
2125     /* Release disk entry */
2126     RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeEntry);
2127 }
2128 
2129 /* EOF */
2130