update for HEAD-2003091401
[reactos.git] / subsys / system / usetup / partlist.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            subsys/system/usetup/partlist.c
23  * PURPOSE:         Partition list functions
24  * PROGRAMMER:      Eric Kohl
25  *                  Casper S. Hornstrup (chorns@users.sourceforge.net)
26  */
27
28 #include <ddk/ntddk.h>
29 #include <ddk/ntddscsi.h>
30
31 #include <ntdll/rtl.h>
32
33 #include <ntos/minmax.h>
34
35 #include "usetup.h"
36 #include "console.h"
37 #include "partlist.h"
38 #include "drivesup.h"
39
40
41 /* FUNCTIONS ****************************************************************/
42
43 static VOID
44 GetDriverName (PDISKENTRY DiskEntry)
45 {
46   RTL_QUERY_REGISTRY_TABLE QueryTable[2];
47   WCHAR KeyName[32];
48   NTSTATUS Status;
49
50   RtlInitUnicodeString (&DiskEntry->DriverName,
51                         NULL);
52
53   swprintf (KeyName,
54             L"\\Scsi\\Scsi Port %lu",
55             DiskEntry->Port);
56
57   RtlZeroMemory (&QueryTable,
58                  sizeof(QueryTable));
59
60   QueryTable[0].Name = L"Driver";
61   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
62   QueryTable[0].EntryContext = &DiskEntry->DriverName;
63
64   Status = RtlQueryRegistryValues (RTL_REGISTRY_DEVICEMAP,
65                                    KeyName,
66                                    QueryTable,
67                                    NULL,
68                                    NULL);
69   if (!NT_SUCCESS (Status))
70     {
71       DPRINT1 ("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
72     }
73 }
74
75
76 static VOID
77 AssignDriverLetters (PPARTLIST List)
78 {
79   PDISKENTRY DiskEntry;
80   PPARTENTRY PartEntry;
81   PLIST_ENTRY Entry1;
82   PLIST_ENTRY Entry2;
83   CHAR Letter;
84
85   Letter = 'C';
86
87   /* Assign drive letters to primary partitions */
88   Entry1 = List->DiskListHead.Flink;
89   while (Entry1 != &List->DiskListHead)
90     {
91       DiskEntry = CONTAINING_RECORD (Entry1, DISKENTRY, ListEntry);
92
93       if (!IsListEmpty (&DiskEntry->PartListHead))
94         {
95           PartEntry = CONTAINING_RECORD (DiskEntry->PartListHead.Flink,
96                                          PARTENTRY,
97                                          ListEntry);
98
99           PartEntry->DriveLetter = 0;
100
101           if (PartEntry->Unpartitioned == FALSE &&
102               !IsContainerPartition (PartEntry->PartInfo[0].PartitionType))
103             {
104               if (IsRecognizedPartition (PartEntry->PartInfo[0].PartitionType) ||
105                   (PartEntry->PartInfo[0].PartitionType == PARTITION_ENTRY_UNUSED &&
106                    PartEntry->PartInfo[0].PartitionLength.QuadPart != 0LL))
107                 {
108                   if (Letter <= 'Z')
109                     {
110                       PartEntry->DriveLetter = Letter;
111                       Letter++;
112                     }
113                 }
114             }
115         }
116
117       Entry1 = Entry1->Flink;
118     }
119
120
121   /* Assign drive letters to logical drives */
122   Entry1 = List->DiskListHead.Flink;
123   while (Entry1 != &List->DiskListHead)
124     {
125       DiskEntry = CONTAINING_RECORD (Entry1, DISKENTRY, ListEntry);
126
127       Entry2 = DiskEntry->PartListHead.Flink;
128       if (Entry2 != &DiskEntry->PartListHead)
129         {
130           Entry2 = Entry2->Flink;
131           while (Entry2 != &DiskEntry->PartListHead)
132             {
133               PartEntry = CONTAINING_RECORD (Entry2,
134                                              PARTENTRY,
135                                              ListEntry);
136
137               PartEntry->DriveLetter = 0;
138
139               if (PartEntry->Unpartitioned == FALSE &&
140                   !IsContainerPartition (PartEntry->PartInfo[0].PartitionType))
141                 {
142                   if (IsRecognizedPartition (PartEntry->PartInfo[0].PartitionType) ||
143                       (PartEntry->PartInfo[0].PartitionType == PARTITION_ENTRY_UNUSED &&
144                        PartEntry->PartInfo[0].PartitionLength.QuadPart != 0LL))
145                     {
146                       if (Letter <= 'Z')
147                         {
148                           PartEntry->DriveLetter = Letter;
149                           Letter++;
150                         }
151                     }
152                 }
153
154               Entry2 = Entry2->Flink;
155             }
156         }
157
158       Entry1 = Entry1->Flink;
159     }
160 }
161
162
163 static VOID
164 UpdatePartitionNumbers (PDISKENTRY DiskEntry)
165 {
166   PPARTENTRY PartEntry;
167   PLIST_ENTRY Entry;
168   ULONG PartNumber;
169   ULONG i;
170
171   PartNumber = 1;
172   Entry = DiskEntry->PartListHead.Flink;
173   while (Entry != &DiskEntry->PartListHead)
174     {
175       PartEntry = CONTAINING_RECORD (Entry,
176                                      PARTENTRY,
177                                      ListEntry);
178
179       if (PartEntry->Unpartitioned == TRUE)
180         {
181           for (i = 0; i < 4; i++)
182             {
183               PartEntry->PartInfo[i].PartitionNumber = 0;
184             }
185         }
186       else
187         {
188           for (i = 0; i < 4; i++)
189             {
190               if (IsContainerPartition (PartEntry->PartInfo[i].PartitionType))
191                 {
192                   PartEntry->PartInfo[i].PartitionNumber = 0;
193                 }
194               else if (PartEntry->PartInfo[i].PartitionType == PARTITION_ENTRY_UNUSED &&
195                        PartEntry->PartInfo[i].PartitionLength.QuadPart == 0ULL)
196                 {
197                   PartEntry->PartInfo[i].PartitionNumber = 0;
198                 }
199               else
200                 {
201                   PartEntry->PartInfo[i].PartitionNumber = PartNumber;
202                   PartNumber++;
203                 }
204             }
205         }
206
207       Entry = Entry->Flink;
208     }
209 }
210
211
212 static VOID
213 AddPartitionToList (ULONG DiskNumber,
214                     PDISKENTRY DiskEntry,
215                     DRIVE_LAYOUT_INFORMATION *LayoutBuffer)
216 {
217   PPARTENTRY PartEntry;
218   ULONG i;
219   ULONG j;
220
221   for (i = 0; i < LayoutBuffer->PartitionCount; i += 4)
222     {
223       for (j = 0; j < 4; j++)
224         {
225           if (LayoutBuffer->PartitionEntry[j].PartitionType != PARTITION_ENTRY_UNUSED ||
226               LayoutBuffer->PartitionEntry[j].PartitionLength.QuadPart != 0ULL)
227             {
228               break;
229             }
230         }
231       if (j >= 4)
232         {
233           continue;
234         }
235
236       PartEntry = (PPARTENTRY)RtlAllocateHeap (ProcessHeap,
237                                                0,
238                                                sizeof(PARTENTRY));
239       if (PartEntry == NULL)
240         {
241           return;
242         }
243
244       RtlZeroMemory (PartEntry,
245                      sizeof(PARTENTRY));
246
247       PartEntry->Unpartitioned = FALSE;
248
249       for (j = 0; j < 4; j++)
250         {
251           RtlCopyMemory (&PartEntry->PartInfo[j],
252                          &LayoutBuffer->PartitionEntry[i+j],
253                          sizeof(PARTITION_INFORMATION));
254         }
255
256       if (IsContainerPartition(PartEntry->PartInfo[0].PartitionType))
257         {
258           PartEntry->FormatState = Unformatted;
259         }
260       else if ((PartEntry->PartInfo[0].PartitionType == PARTITION_FAT_12) ||
261                (PartEntry->PartInfo[0].PartitionType == PARTITION_FAT_16) ||
262                (PartEntry->PartInfo[0].PartitionType == PARTITION_HUGE) ||
263                (PartEntry->PartInfo[0].PartitionType == PARTITION_XINT13) ||
264                (PartEntry->PartInfo[0].PartitionType == PARTITION_FAT32) ||
265                (PartEntry->PartInfo[0].PartitionType == PARTITION_FAT32_XINT13))
266         {
267 #if 0
268           if (CheckFatFormat())
269             {
270               PartEntry->FormatState = Preformatted;
271             }
272           else
273             {
274               PartEntry->FormatState = Unformatted;
275             }
276 #endif
277           PartEntry->FormatState = Preformatted;
278         }
279       else if (PartEntry->PartInfo[0].PartitionType == PARTITION_IFS)
280         {
281 #if 0
282           if (CheckNtfsFormat())
283             {
284               PartEntry->FormatState = Preformatted;
285             }
286           else if (CheckHpfsFormat())
287             {
288               PartEntry->FormatState = Preformatted;
289             }
290           else
291             {
292               PartEntry->FormatState = Unformatted;
293             }
294 #endif
295           PartEntry->FormatState = Preformatted;
296         }
297       else
298         {
299           PartEntry->FormatState = Unknown;
300         }
301
302       InsertTailList (&DiskEntry->PartListHead,
303                       &PartEntry->ListEntry);
304     }
305 }
306
307
308 static VOID
309 ScanForUnpartitionedDiskSpace (PDISKENTRY DiskEntry)
310 {
311   ULONGLONG LastStartingOffset;
312   ULONGLONG LastPartitionLength;
313   ULONGLONG LastUnusedPartitionLength;
314   PPARTENTRY PartEntry;
315   PPARTENTRY NewPartEntry;
316   PLIST_ENTRY Entry;
317   ULONG i;
318   ULONG j;
319
320   if (IsListEmpty (&DiskEntry->PartListHead))
321     {
322       /* Create a partition table that represents the empty disk */
323       PartEntry = (PPARTENTRY)RtlAllocateHeap (ProcessHeap,
324                                                0,
325                                                sizeof(PARTENTRY));
326       if (PartEntry == NULL)
327         return;
328
329       RtlZeroMemory (PartEntry,
330                      sizeof(PARTENTRY));
331
332       PartEntry->Unpartitioned = TRUE;
333       PartEntry->UnpartitionedOffset = 0ULL;
334       PartEntry->UnpartitionedLength = DiskEntry->DiskSize;
335
336       PartEntry->FormatState = Unformatted;
337
338       InsertTailList (&DiskEntry->PartListHead,
339                       &PartEntry->ListEntry);
340     }
341   else
342     {
343       /* Start partition at head 1, cylinder 0 */
344       LastStartingOffset = DiskEntry->TrackSize;
345       LastPartitionLength = 0ULL;
346       LastUnusedPartitionLength = 0ULL;
347
348       i = 0;
349       Entry = DiskEntry->PartListHead.Flink;
350       while (Entry != &DiskEntry->PartListHead)
351         {
352           PartEntry = CONTAINING_RECORD (Entry, PARTENTRY, ListEntry);
353
354           for (j = 0; j < 4; j++)
355             {
356               if ((!IsContainerPartition (PartEntry->PartInfo[j].PartitionType)) &&
357                   (PartEntry->PartInfo[j].PartitionType != PARTITION_ENTRY_UNUSED ||
358                    PartEntry->PartInfo[j].PartitionLength.QuadPart != 0LL))
359                 {
360                   LastUnusedPartitionLength =
361                     PartEntry->PartInfo[j].StartingOffset.QuadPart -
362                     (LastStartingOffset + LastPartitionLength);
363
364                   if (LastUnusedPartitionLength >= DiskEntry->CylinderSize)
365                     {
366                       DPRINT ("Unpartitioned disk space %I64u\n", LastUnusedPartitionLength);
367
368                       NewPartEntry = (PPARTENTRY)RtlAllocateHeap (ProcessHeap,
369                                                                   0,
370                                                                   sizeof(PARTENTRY));
371                       if (NewPartEntry == NULL)
372                         return;
373
374                       RtlZeroMemory (NewPartEntry,
375                                      sizeof(PARTENTRY));
376
377                       NewPartEntry->Unpartitioned = TRUE;
378                       NewPartEntry->UnpartitionedOffset = LastStartingOffset + LastPartitionLength;
379                       NewPartEntry->UnpartitionedLength = LastUnusedPartitionLength;
380                       if (j == 0)
381                         NewPartEntry->UnpartitionedLength -= DiskEntry->TrackSize;
382
383                       NewPartEntry->FormatState = Unformatted;
384
385                       /* Insert the table into the list */
386                       InsertTailList (&PartEntry->ListEntry,
387                                       &NewPartEntry->ListEntry);
388                     }
389
390                   LastStartingOffset = PartEntry->PartInfo[j].StartingOffset.QuadPart;
391                   LastPartitionLength = PartEntry->PartInfo[j].PartitionLength.QuadPart;
392                 }
393             }
394
395           i += 4;
396           Entry = Entry->Flink;
397         }
398
399       /* Check for trailing unpartitioned disk space */
400       if (DiskEntry->DiskSize > (LastStartingOffset + LastPartitionLength))
401         {
402           /* Round-down to cylinder size */
403           LastUnusedPartitionLength =
404             ROUND_DOWN (DiskEntry->DiskSize - (LastStartingOffset + LastPartitionLength),
405                         DiskEntry->CylinderSize);
406
407           if (LastUnusedPartitionLength >= DiskEntry->CylinderSize)
408             {
409               DPRINT ("Unpartitioned disk space %I64u\n", LastUnusedPartitionLength);
410
411               NewPartEntry = (PPARTENTRY)RtlAllocateHeap (ProcessHeap,
412                                                           0,
413                                                           sizeof(PARTENTRY));
414               if (NewPartEntry == NULL)
415                 return;
416
417               RtlZeroMemory (NewPartEntry,
418                              sizeof(PARTENTRY));
419
420               NewPartEntry->Unpartitioned = TRUE;
421               NewPartEntry->UnpartitionedOffset = LastStartingOffset + LastPartitionLength;
422               NewPartEntry->UnpartitionedLength = LastUnusedPartitionLength;
423
424               /* Append the table to the list */
425               InsertTailList (&DiskEntry->PartListHead,
426                               &NewPartEntry->ListEntry);
427             }
428         }
429     }
430 }
431
432
433 static VOID
434 AddDiskToList (HANDLE FileHandle,
435                ULONG DiskNumber,
436                PPARTLIST List)
437 {
438   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
439   DISK_GEOMETRY DiskGeometry;
440   SCSI_ADDRESS ScsiAddress;
441   PDISKENTRY DiskEntry;
442   IO_STATUS_BLOCK Iosb;
443   NTSTATUS Status;
444
445   Status = NtDeviceIoControlFile (FileHandle,
446                                   NULL,
447                                   NULL,
448                                   NULL,
449                                   &Iosb,
450                                   IOCTL_DISK_GET_DRIVE_GEOMETRY,
451                                   NULL,
452                                   0,
453                                   &DiskGeometry,
454                                   sizeof(DISK_GEOMETRY));
455   if (!NT_SUCCESS (Status))
456     {
457       return;
458     }
459
460   if (DiskGeometry.MediaType != FixedMedia)
461     {
462       return;
463     }
464
465   Status = NtDeviceIoControlFile (FileHandle,
466                                   NULL,
467                                   NULL,
468                                   NULL,
469                                   &Iosb,
470                                   IOCTL_SCSI_GET_ADDRESS,
471                                   NULL,
472                                   0,
473                                   &ScsiAddress,
474                                   sizeof(SCSI_ADDRESS));
475   if (!NT_SUCCESS(Status))
476     {
477       return;
478     }
479
480   DiskEntry = (PDISKENTRY)RtlAllocateHeap (ProcessHeap,
481                                            0,
482                                            sizeof(DISKENTRY));
483   if (DiskEntry == NULL)
484     {
485       return;
486     }
487
488   InitializeListHead (&DiskEntry->PartListHead);
489
490   DiskEntry->Cylinders = DiskGeometry.Cylinders.QuadPart;
491   DiskEntry->TracksPerCylinder = DiskGeometry.TracksPerCylinder;
492   DiskEntry->SectorsPerTrack = DiskGeometry.SectorsPerTrack;
493   DiskEntry->BytesPerSector = DiskGeometry.BytesPerSector;
494
495   DPRINT ("Cylinders %d\n", DiskEntry->Cylinders);
496   DPRINT ("TracksPerCylinder %d\n", DiskEntry->TracksPerCylinder);
497   DPRINT ("SectorsPerTrack %d\n", DiskEntry->SectorsPerTrack);
498   DPRINT ("BytesPerSector %d\n", DiskEntry->BytesPerSector);
499
500   DiskEntry->DiskSize =
501     DiskGeometry.Cylinders.QuadPart *
502     (ULONGLONG)DiskGeometry.TracksPerCylinder *
503     (ULONGLONG)DiskGeometry.SectorsPerTrack *
504     (ULONGLONG)DiskGeometry.BytesPerSector;
505   DiskEntry->CylinderSize =
506     (ULONGLONG)DiskGeometry.TracksPerCylinder *
507     (ULONGLONG)DiskGeometry.SectorsPerTrack *
508     (ULONGLONG)DiskGeometry.BytesPerSector;
509   DiskEntry->TrackSize =
510     (ULONGLONG)DiskGeometry.SectorsPerTrack *
511     (ULONGLONG)DiskGeometry.BytesPerSector;
512
513   DiskEntry->DiskNumber = DiskNumber;
514   DiskEntry->Port = ScsiAddress.PortNumber;
515   DiskEntry->Bus = ScsiAddress.PathId;
516   DiskEntry->Id = ScsiAddress.TargetId;
517
518   GetDriverName (DiskEntry);
519
520   InsertTailList (&List->DiskListHead,
521                   &DiskEntry->ListEntry);
522
523   LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap (ProcessHeap,
524                                                              0,
525                                                              8192);
526   if (LayoutBuffer == NULL)
527     {
528       return;
529     }
530
531   Status = NtDeviceIoControlFile (FileHandle,
532                                   NULL,
533                                   NULL,
534                                   NULL,
535                                   &Iosb,
536                                   IOCTL_DISK_GET_DRIVE_LAYOUT,
537                                   NULL,
538                                   0,
539                                   LayoutBuffer,
540                                   8192);
541   if (NT_SUCCESS (Status))
542     {
543       if (LayoutBuffer->PartitionCount == 0)
544         {
545           DiskEntry->NewDisk = TRUE;
546         }
547
548       AddPartitionToList (DiskNumber,
549                           DiskEntry,
550                           LayoutBuffer);
551
552       ScanForUnpartitionedDiskSpace (DiskEntry);
553     }
554
555   RtlFreeHeap (ProcessHeap,
556                0,
557                LayoutBuffer);
558 }
559
560
561 PPARTLIST
562 CreatePartitionList (SHORT Left,
563                      SHORT Top,
564                      SHORT Right,
565                      SHORT Bottom)
566 {
567   PPARTLIST List;
568   OBJECT_ATTRIBUTES ObjectAttributes;
569   SYSTEM_DEVICE_INFORMATION Sdi;
570   DISK_GEOMETRY DiskGeometry;
571   IO_STATUS_BLOCK Iosb;
572   ULONG ReturnSize;
573   NTSTATUS Status;
574   ULONG DiskNumber;
575   WCHAR Buffer[MAX_PATH];
576   UNICODE_STRING Name;
577   HANDLE FileHandle;
578
579   List = (PPARTLIST)RtlAllocateHeap (ProcessHeap,
580                                      0,
581                                      sizeof (PARTLIST));
582   if (List == NULL)
583     return NULL;
584
585   List->Left = Left;
586   List->Top = Top;
587   List->Right = Right;
588   List->Bottom = Bottom;
589
590   List->Line = 0;
591
592   List->TopDisk = (ULONG)-1;
593   List->TopPartition = (ULONG)-1;
594
595   List->CurrentDisk = NULL;
596   List->CurrentPartition = NULL;
597
598   InitializeListHead (&List->DiskListHead);
599
600   Status = NtQuerySystemInformation (SystemDeviceInformation,
601                                      &Sdi,
602                                      sizeof(SYSTEM_DEVICE_INFORMATION),
603                                      &ReturnSize);
604   if (!NT_SUCCESS (Status))
605     {
606       RtlFreeHeap (ProcessHeap, 0, List);
607       return NULL;
608     }
609
610   for (DiskNumber = 0; DiskNumber < Sdi.NumberOfDisks; DiskNumber++)
611     {
612       swprintf (Buffer,
613                 L"\\Device\\Harddisk%d\\Partition0",
614                 DiskNumber);
615       RtlInitUnicodeString (&Name,
616                             Buffer);
617
618       InitializeObjectAttributes (&ObjectAttributes,
619                                   &Name,
620                                   0,
621                                   NULL,
622                                   NULL);
623
624       Status = NtOpenFile (&FileHandle,
625                            FILE_GENERIC_READ,
626                            &ObjectAttributes,
627                            &Iosb,
628                            FILE_SHARE_READ,
629                            FILE_SYNCHRONOUS_IO_NONALERT);
630       if (NT_SUCCESS(Status))
631         {
632           AddDiskToList (FileHandle,
633                          DiskNumber,
634                          List);
635
636           NtClose(FileHandle);
637         }
638     }
639
640   AssignDriverLetters (List);
641
642   List->TopDisk = 0;
643   List->TopPartition = 0;
644
645   /* Search for first usable disk and partition */
646   if (IsListEmpty (&List->DiskListHead))
647     {
648       List->CurrentDisk = NULL;
649       List->CurrentPartition = NULL;
650     }
651   else
652     {
653       List->CurrentDisk =
654         CONTAINING_RECORD (List->DiskListHead.Flink,
655                            DISKENTRY,
656                            ListEntry);
657
658       if (IsListEmpty (&List->CurrentDisk->PartListHead))
659         {
660           List->CurrentPartition = 0;
661         }
662       else
663         {
664           List->CurrentPartition =
665             CONTAINING_RECORD (List->CurrentDisk->PartListHead.Flink,
666                                PARTENTRY,
667                                ListEntry);
668         }
669     }
670
671   return List;
672 }
673
674
675 VOID
676 DestroyPartitionList (PPARTLIST List)
677 {
678   PDISKENTRY DiskEntry;
679   PPARTENTRY PartEntry;
680   PLIST_ENTRY Entry;
681
682   /* Release disk and partition info */
683   while (!IsListEmpty (&List->DiskListHead))
684     {
685       Entry = RemoveHeadList (&List->DiskListHead);
686       DiskEntry = CONTAINING_RECORD (Entry, DISKENTRY, ListEntry);
687
688       /* Release driver name */
689       RtlFreeUnicodeString(&DiskEntry->DriverName);
690
691       /* Release partition array */
692       while (!IsListEmpty (&DiskEntry->PartListHead))
693         {
694           Entry = RemoveHeadList (&DiskEntry->PartListHead);
695           PartEntry = CONTAINING_RECORD (Entry, PARTENTRY, ListEntry);
696
697           RtlFreeHeap (ProcessHeap,
698                        0,
699                        PartEntry);
700         }
701
702       /* Release disk entry */
703       RtlFreeHeap (ProcessHeap, 0, DiskEntry);
704     }
705
706   /* Release list head */
707   RtlFreeHeap (ProcessHeap, 0, List);
708 }
709
710
711 static VOID
712 PrintEmptyLine (PPARTLIST List)
713 {
714   COORD coPos;
715   ULONG Written;
716   USHORT Width;
717   USHORT Height;
718
719   Width = List->Right - List->Left - 1;
720   Height = List->Bottom - List->Top - 1;
721
722   if (List->Line < 0 || List->Line > Height)
723     return;
724
725   coPos.X = List->Left + 1;
726   coPos.Y = List->Top + 1 + List->Line;
727
728   FillConsoleOutputAttribute (0x17,
729                               Width,
730                               coPos,
731                               &Written);
732
733   FillConsoleOutputCharacter (' ',
734                               Width,
735                               coPos,
736                               &Written);
737
738   List->Line++;
739 }
740
741
742 static VOID
743 PrintPartitionData (PPARTLIST List,
744                     PDISKENTRY DiskEntry,
745                     PPARTENTRY PartEntry)
746 {
747   CHAR LineBuffer[128];
748   COORD coPos;
749   ULONG Written;
750   USHORT Width;
751   USHORT Height;
752
753   ULONGLONG PartSize;
754   PCHAR Unit;
755   UCHAR Attribute;
756   PCHAR PartType;
757
758   Width = List->Right - List->Left - 1;
759   Height = List->Bottom - List->Top - 1;
760
761   if (List->Line < 0 || List->Line > Height)
762     return;
763
764   coPos.X = List->Left + 1;
765   coPos.Y = List->Top + 1 + List->Line;
766
767   if (PartEntry->Unpartitioned == TRUE)
768     {
769 #if 0
770       if (PartEntry->UnpartitionledLength >= 0x280000000ULL) /* 10 GB */
771         {
772           PartSize = (PartEntry->UnpartitionedLength + (1 << 29)) >> 30;
773           Unit = "GB";
774         }
775       else
776 #endif
777       if (PartEntry->UnpartitionedLength >= 0xA00000ULL) /* 10 MB */
778         {
779           PartSize = (PartEntry->UnpartitionedLength + (1 << 19)) >> 20;
780           Unit = "MB";
781         }
782       else
783         {
784           PartSize = (PartEntry->UnpartitionedLength + (1 << 9)) >> 10;
785           Unit = "KB";
786         }
787
788       sprintf (LineBuffer,
789                "    Unpartitioned space              %6I64u %s",
790                PartSize,
791                Unit);
792     }
793   else
794     {
795       /* Determine partition type */
796       PartType = NULL;
797       if (PartEntry->New == TRUE)
798         {
799           PartType = "New (Unformatted)";
800         }
801       else if (PartEntry->Unpartitioned == FALSE)
802         {
803           if ((PartEntry->PartInfo[0].PartitionType == PARTITION_FAT_12) ||
804               (PartEntry->PartInfo[0].PartitionType == PARTITION_FAT_16) ||
805               (PartEntry->PartInfo[0].PartitionType == PARTITION_HUGE) ||
806               (PartEntry->PartInfo[0].PartitionType == PARTITION_XINT13))
807             {
808               PartType = "FAT";
809             }
810           else if ((PartEntry->PartInfo[0].PartitionType == PARTITION_FAT32) ||
811                    (PartEntry->PartInfo[0].PartitionType == PARTITION_FAT32_XINT13))
812             {
813               PartType = "FAT32";
814             }
815           else if (PartEntry->PartInfo[0].PartitionType == PARTITION_IFS)
816             {
817               PartType = "NTFS"; /* FIXME: Not quite correct! */
818             }
819         }
820
821 #if 0
822       if (PartEntry->PartInfo[0].PartitionLength.QuadPart >= 0x280000000ULL) /* 10 GB */
823         {
824           PartSize = (PartEntry->PartInfo[0].PartitionLength.QuadPart + (1 << 29)) >> 30;
825           Unit = "GB";
826         }
827       else
828 #endif
829       if (PartEntry->PartInfo[0].PartitionLength.QuadPart >= 0xA00000ULL) /* 10 MB */
830         {
831           PartSize = (PartEntry->PartInfo[0].PartitionLength.QuadPart + (1 << 19)) >> 20;
832           Unit = "MB";
833         }
834       else
835         {
836           PartSize = (PartEntry->PartInfo[0].PartitionLength.QuadPart + (1 << 9)) >> 10;
837           Unit = "KB";
838         }
839
840       if (PartType == NULL)
841         {
842           sprintf (LineBuffer,
843                    "%c%c  Type %-3lu                         %6I64u %s",
844                    (PartEntry->DriveLetter == 0) ? '-' : PartEntry->DriveLetter,
845                    (PartEntry->DriveLetter == 0) ? '-' : ':',
846                    PartEntry->PartInfo[0].PartitionType,
847                    PartSize,
848                    Unit);
849         }
850       else
851         {
852           sprintf (LineBuffer,
853                    "%c%c  %-24s         %6I64u %s",
854                    (PartEntry->DriveLetter == 0) ? '-' : PartEntry->DriveLetter,
855                    (PartEntry->DriveLetter == 0) ? '-' : ':',
856                    PartType,
857                    PartSize,
858                    Unit);
859         }
860     }
861
862   Attribute = (List->CurrentDisk == DiskEntry &&
863                List->CurrentPartition == PartEntry) ? 0x71 : 0x17;
864
865   FillConsoleOutputCharacter (' ',
866                               Width,
867                               coPos,
868                               &Written);
869
870   coPos.X += 4;
871   Width -= 8;
872   FillConsoleOutputAttribute (Attribute,
873                               Width,
874                               coPos,
875                               &Written);
876
877   coPos.X++;
878   Width -= 2;
879   WriteConsoleOutputCharacters (LineBuffer,
880                                 min (strlen (LineBuffer), Width),
881                                 coPos);
882
883   List->Line++;
884 }
885
886
887 static VOID
888 PrintDiskData (PPARTLIST List,
889                PDISKENTRY DiskEntry)
890 {
891   PPARTENTRY PartEntry;
892   PLIST_ENTRY Entry;
893   CHAR LineBuffer[128];
894   COORD coPos;
895   ULONG Written;
896   USHORT Width;
897   USHORT Height;
898   ULONGLONG DiskSize;
899   PCHAR Unit;
900   SHORT PartIndex;
901
902   Width = List->Right - List->Left - 1;
903   Height = List->Bottom - List->Top - 1;
904
905   if (List->Line < 0 || List->Line > Height)
906     return;
907
908   coPos.X = List->Left + 1;
909   coPos.Y = List->Top + 1 + List->Line;
910
911 #if 0
912   if (DiskEntry->DiskSize >= 0x280000000ULL) /* 10 GB */
913     {
914       DiskSize = (DiskEntry->DiskSize + (1 << 29)) >> 30;
915       Unit = "GB";
916     }
917   else
918 #endif
919     {
920       DiskSize = (DiskEntry->DiskSize + (1 << 19)) >> 20;
921       if (DiskSize == 0)
922         DiskSize = 1;
923       Unit = "MB";
924     }
925
926   if (DiskEntry->DriverName.Length > 0)
927     {
928       sprintf (LineBuffer,
929                "%6I64u %s  Harddisk %lu  (Port=%hu, Bus=%hu, Id=%hu) on %wZ",
930                DiskSize,
931                Unit,
932                DiskEntry->DiskNumber,
933                DiskEntry->Port,
934                DiskEntry->Bus,
935                DiskEntry->Id,
936                &DiskEntry->DriverName);
937     }
938   else
939     {
940       sprintf (LineBuffer,
941                "%6I64u %s  Harddisk %lu  (Port=%hu, Bus=%hu, Id=%hu)",
942                DiskSize,
943                Unit,
944                DiskEntry->DiskNumber,
945                DiskEntry->Port,
946                DiskEntry->Bus,
947                DiskEntry->Id);
948     }
949
950   FillConsoleOutputAttribute (0x17,
951                               Width,
952                               coPos,
953                               &Written);
954
955   FillConsoleOutputCharacter (' ',
956                               Width,
957                               coPos,
958                               &Written);
959
960   coPos.X++;
961   WriteConsoleOutputCharacters (LineBuffer,
962                                 min (strlen (LineBuffer), Width - 2),
963                                 coPos);
964
965   List->Line++;
966
967   /* Print separator line */
968   PrintEmptyLine (List);
969
970   /* Print partition lines*/
971   Entry = DiskEntry->PartListHead.Flink;
972   while (Entry != &DiskEntry->PartListHead)
973     {
974       PartEntry = CONTAINING_RECORD (Entry, PARTENTRY, ListEntry);
975
976       /* Print disk entry */
977       PrintPartitionData (List,
978                           DiskEntry,
979                           PartEntry);
980
981       Entry = Entry->Flink;
982     }
983
984   /* Print separator line */
985   PrintEmptyLine (List);
986 }
987
988
989 VOID
990 DrawPartitionList (PPARTLIST List)
991 {
992   PLIST_ENTRY Entry;
993   PDISKENTRY DiskEntry;
994   CHAR LineBuffer[128];
995   COORD coPos;
996   ULONG Written;
997   SHORT i;
998   SHORT DiskIndex;
999
1000   /* draw upper left corner */
1001   coPos.X = List->Left;
1002   coPos.Y = List->Top;
1003   FillConsoleOutputCharacter (0xDA, // '+',
1004                               1,
1005                               coPos,
1006                               &Written);
1007
1008   /* draw upper edge */
1009   coPos.X = List->Left + 1;
1010   coPos.Y = List->Top;
1011   FillConsoleOutputCharacter (0xC4, // '-',
1012                               List->Right - List->Left - 1,
1013                               coPos,
1014                               &Written);
1015
1016   /* draw upper right corner */
1017   coPos.X = List->Right;
1018   coPos.Y = List->Top;
1019   FillConsoleOutputCharacter (0xBF, // '+',
1020                               1,
1021                               coPos,
1022                               &Written);
1023
1024   /* draw left and right edge */
1025   for (i = List->Top + 1; i < List->Bottom; i++)
1026     {
1027       coPos.X = List->Left;
1028       coPos.Y = i;
1029       FillConsoleOutputCharacter (0xB3, // '|',
1030                                   1,
1031                                   coPos,
1032                                   &Written);
1033
1034       coPos.X = List->Right;
1035       FillConsoleOutputCharacter (0xB3, //'|',
1036                                   1,
1037                                   coPos,
1038                                   &Written);
1039     }
1040
1041   /* draw lower left corner */
1042   coPos.X = List->Left;
1043   coPos.Y = List->Bottom;
1044   FillConsoleOutputCharacter (0xC0, // '+',
1045                               1,
1046                               coPos,
1047                               &Written);
1048
1049   /* draw lower edge */
1050   coPos.X = List->Left + 1;
1051   coPos.Y = List->Bottom;
1052   FillConsoleOutputCharacter (0xC4, // '-',
1053                               List->Right - List->Left - 1,
1054                               coPos,
1055                               &Written);
1056
1057   /* draw lower right corner */
1058   coPos.X = List->Right;
1059   coPos.Y = List->Bottom;
1060   FillConsoleOutputCharacter (0xD9, // '+',
1061                               1,
1062                               coPos,
1063                               &Written);
1064
1065   /* print list entries */
1066   List->Line = 0;
1067
1068   Entry = List->DiskListHead.Flink;
1069   while (Entry != &List->DiskListHead)
1070     {
1071       DiskEntry = CONTAINING_RECORD (Entry, DISKENTRY, ListEntry);
1072
1073       /* Print disk entry */
1074       PrintDiskData (List,
1075                      DiskEntry);
1076
1077       Entry = Entry->Flink;
1078     }
1079 }
1080
1081
1082 VOID
1083 ScrollDownPartitionList (PPARTLIST List)
1084 {
1085   PDISKENTRY DiskEntry;
1086   PPARTENTRY PartEntry;
1087   PLIST_ENTRY Entry1;
1088   PLIST_ENTRY Entry2;
1089
1090   /* Check for empty disks */
1091   if (IsListEmpty (&List->DiskListHead))
1092     return;
1093
1094   /* Check for next usable entry on current disk */
1095   if (List->CurrentPartition != NULL)
1096     {
1097       Entry2 = List->CurrentPartition->ListEntry.Flink;
1098       while (Entry2 != &List->CurrentDisk->PartListHead)
1099         {
1100           PartEntry = CONTAINING_RECORD (Entry2, PARTENTRY, ListEntry);
1101
1102 //        if (PartEntry->HidePartEntry == FALSE)
1103             {
1104               List->CurrentPartition = PartEntry;
1105               DrawPartitionList (List);
1106               return;
1107             }
1108           Entry2 = Entry2->Flink;
1109         }
1110     }
1111
1112   /* Check for first usable entry on next disk */
1113   if (List->CurrentDisk != NULL)
1114     {
1115       Entry1 = List->CurrentDisk->ListEntry.Flink;
1116       while (Entry1 != &List->DiskListHead)
1117         {
1118           DiskEntry = CONTAINING_RECORD (Entry1, DISKENTRY, ListEntry);
1119
1120           Entry2 = DiskEntry->PartListHead.Flink;
1121           while (Entry2 != &DiskEntry->PartListHead)
1122             {
1123               PartEntry = CONTAINING_RECORD (Entry2, PARTENTRY, ListEntry);
1124
1125 //            if (PartEntry->HidePartEntry == FALSE)
1126                 {
1127                   List->CurrentDisk = DiskEntry;
1128                   List->CurrentPartition = PartEntry;
1129                   DrawPartitionList (List);
1130                   return;
1131                 }
1132
1133               Entry2 = Entry2->Flink;
1134             }
1135
1136           Entry1 = Entry1->Flink;
1137         }
1138     }
1139 }
1140
1141
1142 VOID
1143 ScrollUpPartitionList (PPARTLIST List)
1144 {
1145   PDISKENTRY DiskEntry;
1146   PPARTENTRY PartEntry;
1147   PLIST_ENTRY Entry1;
1148   PLIST_ENTRY Entry2;
1149   ULONG i;
1150
1151   /* Check for empty disks */
1152   if (IsListEmpty (&List->DiskListHead))
1153     return;
1154
1155   /* check for previous usable entry on current disk */
1156   if (List->CurrentPartition != NULL)
1157     {
1158       Entry2 = List->CurrentPartition->ListEntry.Blink;
1159       while (Entry2 != &List->CurrentDisk->PartListHead)
1160         {
1161           PartEntry = CONTAINING_RECORD (Entry2, PARTENTRY, ListEntry);
1162
1163 //        if (PartEntry->HidePartEntry == FALSE)
1164             {
1165               List->CurrentPartition = PartEntry;
1166               DrawPartitionList (List);
1167               return;
1168             }
1169           Entry2 = Entry2->Blink;
1170         }
1171     }
1172
1173
1174   /* check for last usable entry on previous disk */
1175   if (List->CurrentDisk != NULL)
1176     {
1177       Entry1 = List->CurrentDisk->ListEntry.Blink;
1178       while (Entry1 != &List->DiskListHead)
1179         {
1180           DiskEntry = CONTAINING_RECORD (Entry1, DISKENTRY, ListEntry);
1181
1182           Entry2 = DiskEntry->PartListHead.Blink;
1183           while (Entry2 != &DiskEntry->PartListHead)
1184             {
1185               PartEntry = CONTAINING_RECORD (Entry2, PARTENTRY, ListEntry);
1186
1187 //            if (PartEntry->HidePartEntry == FALSE)
1188                 {
1189                   List->CurrentDisk = DiskEntry;
1190                   List->CurrentPartition = PartEntry;
1191                   DrawPartitionList (List);
1192                   return;
1193                 }
1194
1195               Entry2 = Entry2->Blink;
1196             }
1197
1198           Entry1 = Entry1->Blink;
1199         }
1200     }
1201 }
1202
1203
1204 static PPARTENTRY
1205 GetPrevPartitionedEntry (PDISKENTRY DiskEntry,
1206                          PPARTENTRY CurrentEntry)
1207 {
1208   PPARTENTRY PrevEntry;
1209   PLIST_ENTRY Entry;
1210
1211   if (CurrentEntry->ListEntry.Blink == &DiskEntry->PartListHead)
1212     return NULL;
1213
1214   Entry = CurrentEntry->ListEntry.Blink;
1215   while (Entry != &DiskEntry->PartListHead)
1216     {
1217       PrevEntry = CONTAINING_RECORD (Entry,
1218                                      PARTENTRY,
1219                                      ListEntry);
1220       if (PrevEntry->Unpartitioned == FALSE)
1221         return PrevEntry;
1222
1223       Entry = Entry->Blink;
1224     }
1225
1226   return NULL;
1227 }
1228
1229
1230 static PPARTENTRY
1231 GetNextPartitionedEntry (PDISKENTRY DiskEntry,
1232                          PPARTENTRY CurrentEntry)
1233 {
1234   PPARTENTRY NextEntry;
1235   PLIST_ENTRY Entry;
1236
1237   if (CurrentEntry->ListEntry.Flink == &DiskEntry->PartListHead)
1238     return NULL;
1239
1240   Entry = CurrentEntry->ListEntry.Flink;
1241   while (Entry != &DiskEntry->PartListHead)
1242     {
1243       NextEntry = CONTAINING_RECORD (Entry,
1244                                      PARTENTRY,
1245                                      ListEntry);
1246       if (NextEntry->Unpartitioned == FALSE)
1247         return NextEntry;
1248
1249       Entry = Entry->Flink;
1250     }
1251
1252   return NULL;
1253 }
1254
1255
1256 static PPARTENTRY
1257 GetPrevUnpartitionedEntry (PDISKENTRY DiskEntry,
1258                            PPARTENTRY PartEntry)
1259 {
1260   PPARTENTRY PrevPartEntry;
1261
1262   if (PartEntry->ListEntry.Blink != &DiskEntry->PartListHead)
1263     {
1264       PrevPartEntry = CONTAINING_RECORD (PartEntry->ListEntry.Blink,
1265                                          PARTENTRY,
1266                                          ListEntry);
1267       if (PrevPartEntry->Unpartitioned == TRUE)
1268         return PrevPartEntry;
1269     }
1270
1271   return NULL;
1272 }
1273
1274
1275 static PPARTENTRY
1276 GetNextUnpartitionedEntry (PDISKENTRY DiskEntry,
1277                            PPARTENTRY PartEntry)
1278 {
1279   PPARTENTRY NextPartEntry;
1280
1281   if (PartEntry->ListEntry.Flink != &DiskEntry->PartListHead)
1282     {
1283       NextPartEntry = CONTAINING_RECORD (PartEntry->ListEntry.Flink,
1284                                          PARTENTRY,
1285                                          ListEntry);
1286       if (NextPartEntry->Unpartitioned == TRUE)
1287         return NextPartEntry;
1288     }
1289
1290   return NULL;
1291 }
1292
1293
1294 VOID
1295 CreateNewPartition (PPARTLIST List,
1296                     ULONGLONG PartitionSize,
1297                     BOOLEAN AutoCreate)
1298 {
1299   PDISKENTRY DiskEntry;
1300   PPARTENTRY PartEntry;
1301   PPARTENTRY PrevPartEntry;
1302   PPARTENTRY NextPartEntry;
1303   PPARTENTRY NewPartEntry;
1304
1305   if (List == NULL ||
1306       List->CurrentDisk == NULL ||
1307       List->CurrentPartition == NULL ||
1308       List->CurrentPartition->Unpartitioned == FALSE)
1309     {
1310       return;
1311     }
1312
1313   DiskEntry = List->CurrentDisk;
1314   PartEntry = List->CurrentPartition;
1315
1316   if (AutoCreate == TRUE ||
1317       PartitionSize == PartEntry->UnpartitionedLength)
1318     {
1319       /* Convert current entry to 'new (unformatted)' */
1320       PartEntry->FormatState = Unformatted;
1321       PartEntry->PartInfo[0].StartingOffset.QuadPart =
1322         PartEntry->UnpartitionedOffset + DiskEntry->TrackSize;
1323       PartEntry->PartInfo[0].PartitionLength.QuadPart =
1324         PartEntry->UnpartitionedLength - DiskEntry->TrackSize;
1325       PartEntry->PartInfo[0].PartitionType = PARTITION_ENTRY_UNUSED;
1326       PartEntry->PartInfo[0].BootIndicator = FALSE; /* FIXME */
1327       PartEntry->PartInfo[0].RewritePartition = TRUE;
1328       PartEntry->PartInfo[1].RewritePartition = TRUE;
1329       PartEntry->PartInfo[2].RewritePartition = TRUE;
1330       PartEntry->PartInfo[3].RewritePartition = TRUE;
1331
1332       /* Get previous and next partition entries */
1333       PrevPartEntry = GetPrevPartitionedEntry (DiskEntry,
1334                                                PartEntry);
1335       NextPartEntry = GetNextPartitionedEntry (DiskEntry,
1336                                                PartEntry);
1337
1338       if (PrevPartEntry != NULL && NextPartEntry != NULL)
1339         {
1340           /* Current entry is in the middle of the list */
1341
1342           /* Copy previous container partition data to current entry */
1343           RtlCopyMemory (&PartEntry->PartInfo[1],
1344                          &PrevPartEntry->PartInfo[1],
1345                          sizeof(PARTITION_INFORMATION));
1346           PartEntry->PartInfo[1].RewritePartition = TRUE;
1347
1348           /* Update previous container partition data */
1349
1350           PrevPartEntry->PartInfo[1].StartingOffset.QuadPart =
1351             PartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1352
1353           if (DiskEntry->PartListHead.Flink == &PrevPartEntry->ListEntry)
1354             {
1355               /* Special case - previous partition is first partition */
1356               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1357                 DiskEntry->DiskSize - PrevPartEntry->PartInfo[1].StartingOffset.QuadPart;
1358             }
1359           else
1360             {
1361               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1362                 PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize;
1363             }
1364
1365           PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1366         }
1367       else if (PrevPartEntry == NULL && NextPartEntry != NULL)
1368         {
1369           /* Current entry is the first entry */
1370           return;
1371         }
1372       else if (PrevPartEntry != NULL && NextPartEntry == NULL)
1373         {
1374           /* Current entry is the last entry */
1375
1376           PrevPartEntry->PartInfo[1].StartingOffset.QuadPart =
1377             PartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1378
1379           if (DiskEntry->PartListHead.Flink == &PrevPartEntry->ListEntry)
1380             {
1381               /* Special case - previous partition is first partition */
1382               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1383                 DiskEntry->DiskSize - PrevPartEntry->PartInfo[1].StartingOffset.QuadPart;
1384             }
1385           else
1386             {
1387               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1388                 PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize;
1389             }
1390
1391           if ((PartEntry->PartInfo[1].StartingOffset.QuadPart +
1392                PartEntry->PartInfo[1].PartitionLength.QuadPart) <
1393                (1024ULL * 255ULL * 63ULL * 512ULL))
1394             {
1395               PrevPartEntry->PartInfo[1].PartitionType = PARTITION_EXTENDED;
1396             }
1397           else
1398             {
1399               PrevPartEntry->PartInfo[1].PartitionType = PARTITION_XINT13_EXTENDED;
1400             }
1401
1402           PrevPartEntry->PartInfo[1].BootIndicator = FALSE;
1403           PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1404         }
1405
1406       PartEntry->AutoCreate = AutoCreate;
1407       PartEntry->New = TRUE;
1408       PartEntry->Unpartitioned = FALSE;
1409       PartEntry->UnpartitionedOffset = 0ULL;
1410       PartEntry->UnpartitionedLength = 0ULL;
1411     }
1412   else
1413     {
1414       /* Insert an initialize a new partition entry */
1415       NewPartEntry = (PPARTENTRY)RtlAllocateHeap (ProcessHeap,
1416                                                   0,
1417                                                   sizeof(PARTENTRY));
1418       if (NewPartEntry == NULL)
1419         return;
1420
1421       RtlZeroMemory (NewPartEntry,
1422                      sizeof(PARTENTRY));
1423
1424       /* Insert the new entry into the list */
1425       InsertTailList (&PartEntry->ListEntry,
1426                       &NewPartEntry->ListEntry);
1427
1428       NewPartEntry->New = TRUE;
1429
1430       NewPartEntry->FormatState = Unformatted;
1431       NewPartEntry->PartInfo[0].StartingOffset.QuadPart =
1432         PartEntry->UnpartitionedOffset + DiskEntry->TrackSize;
1433       NewPartEntry->PartInfo[0].PartitionLength.QuadPart =
1434         PartitionSize - DiskEntry->TrackSize;
1435       NewPartEntry->PartInfo[0].PartitionType = PARTITION_ENTRY_UNUSED;
1436       NewPartEntry->PartInfo[0].BootIndicator = FALSE; /* FIXME */
1437       NewPartEntry->PartInfo[0].RewritePartition = TRUE;
1438       NewPartEntry->PartInfo[1].RewritePartition = TRUE;
1439       NewPartEntry->PartInfo[2].RewritePartition = TRUE;
1440       NewPartEntry->PartInfo[3].RewritePartition = TRUE;
1441
1442       /* Get previous and next partition entries */
1443       PrevPartEntry = GetPrevPartitionedEntry (DiskEntry,
1444                                                NewPartEntry);
1445       NextPartEntry = GetNextPartitionedEntry (DiskEntry,
1446                                                NewPartEntry);
1447
1448       if (PrevPartEntry != NULL && NextPartEntry != NULL)
1449         {
1450           /* Current entry is in the middle of the list */
1451
1452           /* Copy previous container partition data to current entry */
1453           RtlCopyMemory (&NewPartEntry->PartInfo[1],
1454                          &PrevPartEntry->PartInfo[1],
1455                          sizeof(PARTITION_INFORMATION));
1456           NewPartEntry->PartInfo[1].RewritePartition = TRUE;
1457
1458           /* Update previous container partition data */
1459
1460           PrevPartEntry->PartInfo[1].StartingOffset.QuadPart =
1461             NewPartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1462
1463           if (DiskEntry->PartListHead.Flink == &PrevPartEntry->ListEntry)
1464             {
1465               /* Special case - previous partition is first partition */
1466               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1467                 DiskEntry->DiskSize - PrevPartEntry->PartInfo[1].StartingOffset.QuadPart;
1468             }
1469           else
1470             {
1471               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1472                 NewPartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize;
1473             }
1474
1475           PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1476         }
1477       else if (PrevPartEntry == NULL && NextPartEntry != NULL)
1478         {
1479           /* Current entry is the first entry */
1480           return;
1481         }
1482       else if (PrevPartEntry != NULL && NextPartEntry == NULL)
1483         {
1484           /* Current entry is the last entry */
1485
1486           PrevPartEntry->PartInfo[1].StartingOffset.QuadPart =
1487             NewPartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1488
1489           if (DiskEntry->PartListHead.Flink == &PrevPartEntry->ListEntry)
1490             {
1491               /* Special case - previous partition is first partition */
1492               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1493                 DiskEntry->DiskSize - PrevPartEntry->PartInfo[1].StartingOffset.QuadPart;
1494             }
1495           else
1496             {
1497               PrevPartEntry->PartInfo[1].PartitionLength.QuadPart =
1498                 NewPartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize;
1499             }
1500
1501           if ((PartEntry->PartInfo[1].StartingOffset.QuadPart +
1502                PartEntry->PartInfo[1].PartitionLength.QuadPart) <
1503                (1024ULL * 255ULL * 63ULL * 512ULL))
1504             {
1505               PrevPartEntry->PartInfo[1].PartitionType = PARTITION_EXTENDED;
1506             }
1507           else
1508             {
1509               PrevPartEntry->PartInfo[1].PartitionType = PARTITION_XINT13_EXTENDED;
1510             }
1511
1512           PrevPartEntry->PartInfo[1].BootIndicator = FALSE;
1513           PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1514         }
1515
1516       /* Update offset and size of the remaining unpartitioned disk space */
1517       PartEntry->UnpartitionedOffset += PartitionSize;
1518       PartEntry->UnpartitionedLength -= PartitionSize;
1519     }
1520
1521   DiskEntry->Modified = TRUE;
1522
1523   UpdatePartitionNumbers (DiskEntry);
1524
1525   AssignDriverLetters (List);
1526 }
1527
1528
1529 VOID
1530 DeleteCurrentPartition (PPARTLIST List)
1531 {
1532   PDISKENTRY DiskEntry;
1533   PPARTENTRY PartEntry;
1534   PPARTENTRY PrevPartEntry;
1535   PPARTENTRY NextPartEntry;
1536
1537   if (List == NULL ||
1538       List->CurrentDisk == NULL ||
1539       List->CurrentPartition == NULL ||
1540       List->CurrentPartition->Unpartitioned == TRUE)
1541     {
1542       return;
1543     }
1544
1545   DiskEntry = List->CurrentDisk;
1546   PartEntry = List->CurrentPartition;
1547
1548   /* Adjust container partition entries */
1549
1550   /* Get previous and next partition entries */
1551   PrevPartEntry = GetPrevPartitionedEntry (DiskEntry,
1552                                            PartEntry);
1553   NextPartEntry = GetNextPartitionedEntry (DiskEntry,
1554                                            PartEntry);
1555
1556   if (PrevPartEntry != NULL && NextPartEntry != NULL)
1557     {
1558       /* Current entry is in the middle of the list */
1559
1560       /*
1561        * The first extended partition can not be deleted
1562        * as long as other extended partitions are present.
1563        */
1564       if (PrevPartEntry->ListEntry.Blink == &DiskEntry->PartListHead)
1565         return;
1566
1567       /* Copy previous container partition data to current entry */
1568       RtlCopyMemory (&PrevPartEntry->PartInfo[1],
1569                      &PartEntry->PartInfo[1],
1570                      sizeof(PARTITION_INFORMATION));
1571       PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1572     }
1573   else if (PrevPartEntry == NULL && NextPartEntry != NULL)
1574     {
1575       /*
1576        * A primary partition can not be deleted as long as
1577        * extended partitions are present.
1578        */
1579       return;
1580     }
1581   else if (PrevPartEntry != NULL && NextPartEntry == NULL)
1582     {
1583       /* Current entry is the last entry */
1584       RtlZeroMemory (&PrevPartEntry->PartInfo[1],
1585                      sizeof(PARTITION_INFORMATION));
1586       PrevPartEntry->PartInfo[1].RewritePartition = TRUE;
1587     }
1588
1589
1590   /* Adjust unpartitioned disk space entries */
1591
1592   /* Get pointer to previous and next unpartitioned entries */
1593   PrevPartEntry = GetPrevUnpartitionedEntry (DiskEntry,
1594                                              PartEntry);
1595
1596   NextPartEntry = GetNextUnpartitionedEntry (DiskEntry,
1597                                              PartEntry);
1598
1599   if (PrevPartEntry != NULL && NextPartEntry != NULL)
1600     {
1601       /* Merge previous, current and next unpartitioned entry */
1602
1603       /* Adjust the previous entries length */
1604       PrevPartEntry->UnpartitionedLength += 
1605         (PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize +
1606          NextPartEntry->UnpartitionedLength);
1607
1608       /* Remove the current entry */
1609       RemoveEntryList (&PartEntry->ListEntry);
1610       RtlFreeHeap (ProcessHeap,
1611                    0,
1612                    PartEntry);
1613
1614       /* Remove the next entry */
1615       RemoveEntryList (&NextPartEntry->ListEntry);
1616       RtlFreeHeap (ProcessHeap,
1617                    0,
1618                    NextPartEntry);
1619
1620       /* Update current partition */
1621       List->CurrentPartition = PrevPartEntry;
1622     }
1623   else if (PrevPartEntry != NULL && NextPartEntry == NULL)
1624     {
1625       /* Merge current and previous unpartitioned entry */
1626
1627       /* Adjust the previous entries length */
1628       PrevPartEntry->UnpartitionedLength += 
1629         (PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize);
1630
1631       /* Remove the current entry */
1632       RemoveEntryList (&PartEntry->ListEntry);
1633       RtlFreeHeap (ProcessHeap,
1634                    0,
1635                    PartEntry);
1636
1637       /* Update current partition */
1638       List->CurrentPartition = PrevPartEntry;
1639     }
1640   else if (PrevPartEntry == NULL && NextPartEntry != NULL)
1641     {
1642       /* Merge current and next unpartitioned entry */
1643
1644       /* Adjust the next entries offset and length */
1645       NextPartEntry->UnpartitionedOffset = 
1646         PartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1647       NextPartEntry->UnpartitionedLength += 
1648         (PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize);
1649
1650       /* Remove the current entry */
1651       RemoveEntryList (&PartEntry->ListEntry);
1652       RtlFreeHeap (ProcessHeap,
1653                    0,
1654                    PartEntry);
1655
1656       /* Update current partition */
1657       List->CurrentPartition = NextPartEntry;
1658     }
1659   else
1660     {
1661       /* Nothing to merge but change current entry */
1662       PartEntry->New = FALSE;
1663       PartEntry->Unpartitioned = TRUE;
1664       PartEntry->UnpartitionedOffset = 
1665         PartEntry->PartInfo[0].StartingOffset.QuadPart - DiskEntry->TrackSize;
1666       PartEntry->UnpartitionedLength = 
1667         PartEntry->PartInfo[0].PartitionLength.QuadPart + DiskEntry->TrackSize;
1668
1669       /* Wipe the partition table */
1670       RtlZeroMemory (&PartEntry->PartInfo,
1671                      sizeof(PartEntry->PartInfo));
1672     }
1673
1674   DiskEntry->Modified = TRUE;
1675
1676   UpdatePartitionNumbers (DiskEntry);
1677
1678   AssignDriverLetters (List);
1679 }
1680
1681
1682 VOID
1683 CheckActiveBootPartition (PPARTLIST List)
1684 {
1685   PDISKENTRY DiskEntry;
1686   PPARTENTRY PartEntry;
1687
1688   /* Check for empty disk list */
1689   if (IsListEmpty (&List->DiskListHead))
1690     {
1691       List->ActiveBootDisk = NULL;
1692       List->ActiveBootPartition = NULL;
1693       return;
1694     }
1695
1696 #if 0
1697   if (List->ActiveBootDisk != NULL &&
1698       List->ActiveBootPartition != NULL)
1699     {
1700       /* We already have an active boot partition */
1701       return;
1702     }
1703 #endif
1704
1705   DiskEntry = CONTAINING_RECORD (List->DiskListHead.Flink,
1706                                  DISKENTRY,
1707                                  ListEntry);
1708
1709   /* Check for empty partition list */
1710   if (IsListEmpty (&DiskEntry->PartListHead))
1711     {
1712       List->ActiveBootDisk = NULL;
1713       List->ActiveBootPartition = NULL;
1714       return;
1715     }
1716
1717   PartEntry = CONTAINING_RECORD (DiskEntry->PartListHead.Flink,
1718                                  PARTENTRY,
1719                                  ListEntry);
1720
1721   /* Set active boot partition */
1722   if ((DiskEntry->NewDisk == TRUE) ||
1723       (PartEntry->PartInfo[0].BootIndicator == FALSE &&
1724        PartEntry->PartInfo[1].BootIndicator == FALSE &&
1725        PartEntry->PartInfo[2].BootIndicator == FALSE &&
1726        PartEntry->PartInfo[3].BootIndicator == FALSE))
1727     {
1728       PartEntry->PartInfo[0].BootIndicator = TRUE;
1729       PartEntry->PartInfo[0].RewritePartition = TRUE;
1730       DiskEntry->Modified = TRUE;
1731     }
1732
1733   /* FIXME: Might be incorrect if partitions were created by Linux FDISK */
1734   List->ActiveBootDisk = DiskEntry;
1735   List->ActiveBootPartition = PartEntry;
1736 }
1737
1738
1739 BOOLEAN
1740 CheckForLinuxFdiskPartitions (PPARTLIST List)
1741 {
1742   PDISKENTRY DiskEntry;
1743   PPARTENTRY PartEntry;
1744   PLIST_ENTRY Entry1;
1745   PLIST_ENTRY Entry2;
1746   ULONG PartitionCount;
1747   ULONG i;
1748
1749   Entry1 = List->DiskListHead.Flink;
1750   while (Entry1 != &List->DiskListHead)
1751     {
1752       DiskEntry = CONTAINING_RECORD (Entry1,
1753                                      DISKENTRY,
1754                                      ListEntry);
1755
1756       Entry2 = DiskEntry->PartListHead.Flink;
1757       while (Entry2 != &DiskEntry->PartListHead)
1758         {
1759           PartEntry = CONTAINING_RECORD (Entry2,
1760                                          PARTENTRY,
1761                                          ListEntry);
1762
1763           if (PartEntry->Unpartitioned == FALSE)
1764             {
1765               PartitionCount = 0;
1766
1767               for (i = 0; i < 4; i++)
1768                 {
1769                   if (!IsContainerPartition (PartEntry->PartInfo[i].PartitionType) &&
1770                       PartEntry->PartInfo[i].PartitionLength.QuadPart != 0ULL)
1771                     {
1772                       PartitionCount++;
1773                     }
1774                 }
1775
1776               if (PartitionCount > 1)
1777                 {
1778                   return TRUE;
1779                 }
1780             }
1781
1782           Entry2 = Entry2->Flink;
1783         }
1784
1785       Entry1 = Entry1->Flink;
1786     }
1787
1788   return FALSE;
1789 }
1790
1791
1792 BOOLEAN
1793 WritePartitionsToDisk (PPARTLIST List)
1794 {
1795   PDRIVE_LAYOUT_INFORMATION DriveLayout;
1796   OBJECT_ATTRIBUTES ObjectAttributes;
1797   IO_STATUS_BLOCK Iosb;
1798   WCHAR SrcPath[MAX_PATH];
1799   WCHAR DstPath[MAX_PATH];
1800   UNICODE_STRING Name;
1801   HANDLE FileHandle;
1802   PDISKENTRY DiskEntry;
1803   PPARTENTRY PartEntry;
1804   PLIST_ENTRY Entry1;
1805   PLIST_ENTRY Entry2;
1806   ULONG PartitionCount;
1807   ULONG DriveLayoutSize;
1808   ULONG Index;
1809   NTSTATUS Status;
1810
1811   if (List == NULL)
1812     {
1813       return TRUE;
1814     }
1815
1816   Entry1 = List->DiskListHead.Flink;
1817   while (Entry1 != &List->DiskListHead)
1818     {
1819       DiskEntry = CONTAINING_RECORD (Entry1,
1820                                      DISKENTRY,
1821                                      ListEntry);
1822
1823       if (DiskEntry->Modified == TRUE)
1824         {
1825           /* Count partitioned entries */
1826           PartitionCount = 0;
1827           Entry2 = DiskEntry->PartListHead.Flink;
1828           while (Entry2 != &DiskEntry->PartListHead)
1829             {
1830               PartEntry = CONTAINING_RECORD (Entry2,
1831                                              PARTENTRY,
1832                                              ListEntry);
1833               if (PartEntry->Unpartitioned == FALSE)
1834                 {
1835                   PartitionCount += 4;
1836                 }
1837
1838               Entry2 = Entry2->Flink;
1839             }
1840
1841           if (PartitionCount > 0)
1842             {
1843               DriveLayoutSize = sizeof (DRIVE_LAYOUT_INFORMATION) +
1844                 ((PartitionCount - 1) * sizeof (PARTITION_INFORMATION));
1845               DriveLayout = (PDRIVE_LAYOUT_INFORMATION)RtlAllocateHeap (ProcessHeap,
1846                                                                         0,
1847                                                                         DriveLayoutSize);
1848               if (DriveLayout == NULL)
1849                 {
1850                   DPRINT1 ("RtlAllocateHeap() failed\n");
1851                   return FALSE;
1852                 }
1853
1854               RtlZeroMemory (DriveLayout,
1855                              DriveLayoutSize);
1856
1857               DriveLayout->PartitionCount = PartitionCount;
1858
1859               Index = 0;
1860               Entry2 = DiskEntry->PartListHead.Flink;
1861               while (Entry2 != &DiskEntry->PartListHead)
1862                 {
1863                   PartEntry = CONTAINING_RECORD (Entry2,
1864                                                  PARTENTRY,
1865                                                  ListEntry);
1866                   if (PartEntry->Unpartitioned == FALSE)
1867                     {
1868                       RtlCopyMemory (&DriveLayout->PartitionEntry[Index],
1869                                      &PartEntry->PartInfo[0],
1870                                      4 * sizeof (PARTITION_INFORMATION));
1871                       Index += 4;
1872                     }
1873
1874                   Entry2 = Entry2->Flink;
1875                 }
1876
1877               swprintf (DstPath,
1878                         L"\\Device\\Harddisk%d\\Partition0",
1879                         DiskEntry->DiskNumber);
1880               RtlInitUnicodeString (&Name,
1881                                     DstPath);
1882               InitializeObjectAttributes (&ObjectAttributes,
1883                                           &Name,
1884                                           0,
1885                                           NULL,
1886                                           NULL);
1887
1888               Status = NtOpenFile (&FileHandle,
1889                                    FILE_ALL_ACCESS,
1890                                    &ObjectAttributes,
1891                                    &Iosb,
1892                                    0,
1893                                    FILE_SYNCHRONOUS_IO_NONALERT);
1894               if (!NT_SUCCESS (Status))
1895                 {
1896                   DPRINT1 ("NtOpenFile() failed (Status %lx)\n", Status);
1897                   return FALSE;
1898                 }
1899
1900               Status = NtDeviceIoControlFile (FileHandle,
1901                                               NULL,
1902                                               NULL,
1903                                               NULL,
1904                                               &Iosb,
1905                                               IOCTL_DISK_SET_DRIVE_LAYOUT,
1906                                               DriveLayout,
1907                                               DriveLayoutSize,
1908                                               NULL,
1909                                               0);
1910               if (!NT_SUCCESS (Status))
1911                 {
1912                   DPRINT1 ("NtDeviceIoControlFile() failed (Status %lx)\n", Status);
1913                   NtClose (FileHandle);
1914                   return FALSE;
1915                 }
1916
1917               RtlFreeHeap (ProcessHeap,
1918                            0,
1919                            DriveLayout);
1920
1921               NtClose (FileHandle);
1922
1923               /* Install MBR code if the disk is new */
1924               if (DiskEntry->NewDisk == TRUE)
1925                 {
1926                   wcscpy (SrcPath, SourceRootPath.Buffer);
1927                   wcscat (SrcPath, L"\\loader\\dosmbr.bin");
1928
1929                   DPRINT1 ("Install MBR bootcode: %S ==> %S\n",
1930                            SrcPath, DstPath);
1931
1932                   /* Install MBR bootcode */
1933                   Status = InstallMbrBootCodeToDisk (SrcPath,
1934                                                      DstPath);
1935                   if (!NT_SUCCESS (Status))
1936                     {
1937                       DPRINT1 ("InstallMbrBootCodeToDisk() failed (Status %lx)\n",
1938                                Status);
1939                       return FALSE;
1940                     }
1941
1942                   DiskEntry->NewDisk = FALSE;
1943                 }
1944             }
1945         }
1946
1947       Entry1 = Entry1->Flink;
1948     }
1949
1950   return TRUE;
1951 }
1952
1953 /* EOF */