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