121a628c29ed7f6747e4992563893c5dabf72ff4
[reactos.git] / subsys / system / usetup / partlist.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 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 AddPartitionList(ULONG DiskNumber,
45                  PDISKENTRY DiskEntry,
46                  DRIVE_LAYOUT_INFORMATION *LayoutBuffer)
47 {
48   PPARTENTRY PartEntry;
49   ULONG i;
50   ULONG EntryCount;
51   BOOLEAN LastEntryWasUnused;
52   ULONGLONG LastStartingOffset;
53   ULONGLONG LastPartitionSize;
54   ULONGLONG LastUnusedPartitionSize;
55   ULONG LastUnusedEntry;
56
57   /*
58    * FIXME:
59    * Determine required number of partiton entries.
60    * This must include entries for unused disk space.
61    */
62
63   /* Check for unpartitioned disk */
64   if (LayoutBuffer->PartitionCount == 0)
65     {
66       EntryCount = 1;
67     }
68   else
69     {
70       EntryCount = LayoutBuffer->PartitionCount;
71     }
72
73
74   DiskEntry->PartArray = (PPARTENTRY)RtlAllocateHeap(ProcessHeap,
75                                                      0,
76                                                      EntryCount * sizeof(PARTENTRY));
77   DiskEntry->PartCount = EntryCount;
78
79   RtlZeroMemory(DiskEntry->PartArray,
80                 EntryCount * sizeof(PARTENTRY));
81
82   if (LayoutBuffer->PartitionCount == 0)
83     {
84       /* Initialize an 'Unpartitioned space' entry */
85       PartEntry = &DiskEntry->PartArray[0];
86
87       PartEntry->Unpartitioned = TRUE;
88       // Start partition at head 1, cylinder 0
89       PartEntry->StartingOffset = DiskEntry->SectorsPerTrack * DiskEntry->BytesPerSector;
90       PartEntry->PartSize = DiskEntry->DiskSize - PartEntry->StartingOffset;
91       PartEntry->Used = FALSE;
92       PartEntry->HidePartEntry = FALSE;
93       PartEntry->PartNumber = 1;
94     }
95   else
96     {
97       LastEntryWasUnused = FALSE;
98       // Start partition at head 1, cylinder 0
99       LastStartingOffset = DiskEntry->SectorsPerTrack * DiskEntry->BytesPerSector;
100       LastPartitionSize = 0;
101       LastUnusedEntry = -1;
102       LastUnusedPartitionSize = 0;
103       for (i = 0; i < LayoutBuffer->PartitionCount; i++)
104         {
105           PartEntry = &DiskEntry->PartArray[i];
106
107           if ((LayoutBuffer->PartitionEntry[i].PartitionType != PARTITION_ENTRY_UNUSED) &&
108               (!IsContainerPartition(LayoutBuffer->PartitionEntry[i].PartitionType)))
109             {
110         LastUnusedPartitionSize = LayoutBuffer->PartitionEntry[i].StartingOffset.QuadPart
111           - (LastStartingOffset + LastPartitionSize);
112         if (LastUnusedEntry != -1)
113           {
114             DiskEntry->PartArray[LastUnusedEntry].StartingOffset = LastStartingOffset + LastPartitionSize;
115             DiskEntry->PartArray[LastUnusedEntry].PartSize = LastUnusedPartitionSize;
116             DiskEntry->PartArray[LastUnusedEntry].PartNumber = LastUnusedEntry + 1; /* FIXME: Is this always correct? */
117           }
118         LastStartingOffset = LayoutBuffer->PartitionEntry[i].StartingOffset.QuadPart;
119         LastPartitionSize = LayoutBuffer->PartitionEntry[i].PartitionLength.QuadPart;
120
121         PartEntry->StartingOffset = LayoutBuffer->PartitionEntry[i].StartingOffset.QuadPart;
122               PartEntry->PartSize = LayoutBuffer->PartitionEntry[i].PartitionLength.QuadPart;
123               PartEntry->PartNumber = LayoutBuffer->PartitionEntry[i].PartitionNumber,
124               PartEntry->PartType = LayoutBuffer->PartitionEntry[i].PartitionType;
125               PartEntry->Active = LayoutBuffer->PartitionEntry[i].BootIndicator;
126
127               PartEntry->DriveLetter = GetDriveLetter(DiskNumber,
128                                                       LayoutBuffer->PartitionEntry[i].PartitionNumber);
129
130               PartEntry->Unpartitioned = FALSE;
131
132               PartEntry->Used = TRUE;
133         PartEntry->HidePartEntry = FALSE;
134         LastEntryWasUnused = FALSE;
135         LastUnusedEntry = -1;
136             }
137           else
138             {
139         if (LastEntryWasUnused)
140           {
141             /* Group unused entries into one unpartitioned disk space area */
142             PartEntry->HidePartEntry = TRUE;
143             PartEntry->PartSize = 0;
144           }
145         else
146           {
147             LastUnusedEntry = i;
148           }
149
150         PartEntry->Unpartitioned = TRUE;
151
152               PartEntry->Used = FALSE;
153         LastEntryWasUnused = TRUE;
154             }
155         }
156       LastUnusedPartitionSize = DiskEntry->DiskSize
157         - (LastStartingOffset + LastPartitionSize);
158       if (LastUnusedEntry != -1)
159         {
160           DiskEntry->PartArray[LastUnusedEntry].StartingOffset = LastStartingOffset + LastPartitionSize;
161           DiskEntry->PartArray[LastUnusedEntry].PartSize = LastUnusedPartitionSize;
162           DiskEntry->PartArray[LastUnusedEntry].PartNumber = LastUnusedEntry + 1; /* FIXME: Is this always correct? */
163         }
164     }
165 }
166
167
168 static VOID
169 GetDriverName(PDISKENTRY DiskEntry)
170 {
171   RTL_QUERY_REGISTRY_TABLE QueryTable[2];
172   WCHAR KeyName[32];
173   NTSTATUS Status;
174
175   RtlInitUnicodeString(&DiskEntry->DriverName,
176                        NULL);
177
178   swprintf(KeyName,
179            L"\\Scsi\\Scsi Port %lu",
180            DiskEntry->Port);
181
182   RtlZeroMemory(&QueryTable,
183                 sizeof(QueryTable));
184
185   QueryTable[0].Name = L"Driver";
186   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
187   QueryTable[0].EntryContext = &DiskEntry->DriverName;
188
189   Status = RtlQueryRegistryValues(RTL_REGISTRY_DEVICEMAP,
190                                   KeyName,
191                                   QueryTable,
192                                   NULL,
193                                   NULL);
194   if (!NT_SUCCESS(Status))
195     {
196       DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
197     }
198 }
199
200
201 PPARTLIST
202 CreatePartitionListNoGUI()
203 {
204   PPARTLIST List;
205   OBJECT_ATTRIBUTES ObjectAttributes;
206   SYSTEM_DEVICE_INFORMATION Sdi;
207   DISK_GEOMETRY DiskGeometry;
208   IO_STATUS_BLOCK Iosb;
209   ULONG ReturnSize;
210   NTSTATUS Status;
211   ULONG DiskNumber;
212   WCHAR Buffer[MAX_PATH];
213   UNICODE_STRING Name;
214   HANDLE FileHandle;
215   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
216   SCSI_ADDRESS ScsiAddress;
217
218   List = (PPARTLIST)RtlAllocateHeap(ProcessHeap, 0, sizeof(PARTLIST));
219   if (List == NULL)
220     return(NULL);
221
222   List->Left = 0;
223   List->Top = 0;
224   List->Right = 0;
225   List->Bottom = 0;
226
227   List->Line = 0;
228
229   List->TopDisk = (ULONG)-1;
230   List->TopPartition = (ULONG)-1;
231
232   List->CurrentDisk = (ULONG)-1;
233   List->CurrentPartition = (ULONG)-1;
234
235   List->DiskCount = 0;
236   List->DiskArray = NULL;
237
238
239   Status = NtQuerySystemInformation(SystemDeviceInformation,
240                                     &Sdi,
241                                     sizeof(SYSTEM_DEVICE_INFORMATION),
242                                     &ReturnSize);
243   if (!NT_SUCCESS(Status))
244     {
245       RtlFreeHeap(ProcessHeap, 0, List);
246       return(NULL);
247     }
248
249   List->DiskArray = (PDISKENTRY)RtlAllocateHeap(ProcessHeap,
250                                                 0,
251                                                 Sdi.NumberOfDisks * sizeof(DISKENTRY));
252   List->DiskCount = Sdi.NumberOfDisks;
253
254   for (DiskNumber = 0; DiskNumber < Sdi.NumberOfDisks; DiskNumber++)
255     {
256       swprintf(Buffer,
257                L"\\Device\\Harddisk%d\\Partition0",
258                DiskNumber);
259       RtlInitUnicodeString(&Name,
260                            Buffer);
261
262       InitializeObjectAttributes(&ObjectAttributes,
263                                  &Name,
264                                  0,
265                                  NULL,
266                                  NULL);
267
268       Status = NtOpenFile(&FileHandle,
269                           0x10001,
270                           &ObjectAttributes,
271                           &Iosb,
272                           1,
273                           FILE_SYNCHRONOUS_IO_NONALERT);
274       if (NT_SUCCESS(Status))
275         {
276           Status = NtDeviceIoControlFile(FileHandle,
277                                          NULL,
278                                          NULL,
279                                          NULL,
280                                          &Iosb,
281                                          IOCTL_DISK_GET_DRIVE_GEOMETRY,
282                                          NULL,
283                                          0,
284                                          &DiskGeometry,
285                                          sizeof(DISK_GEOMETRY));
286           if (NT_SUCCESS(Status))
287             {
288               if (DiskGeometry.MediaType == FixedMedia)
289                 {
290                   Status = NtDeviceIoControlFile(FileHandle,
291                                                  NULL,
292                                                  NULL,
293                                                  NULL,
294                                                  &Iosb,
295                                                  IOCTL_SCSI_GET_ADDRESS,
296                                                  NULL,
297                                                  0,
298                                                  &ScsiAddress,
299                                                  sizeof(SCSI_ADDRESS));
300
301
302                   List->DiskArray[DiskNumber].Cylinders = DiskGeometry.Cylinders.QuadPart;
303                   List->DiskArray[DiskNumber].TracksPerCylinder = DiskGeometry.TracksPerCylinder;
304                   List->DiskArray[DiskNumber].SectorsPerTrack = DiskGeometry.SectorsPerTrack;
305                   List->DiskArray[DiskNumber].BytesPerSector = DiskGeometry.BytesPerSector;
306
307       DPRINT("Cylinders %d\n", List->DiskArray[DiskNumber].Cylinders);
308       DPRINT("TracksPerCylinder %d\n", List->DiskArray[DiskNumber].TracksPerCylinder);
309       DPRINT("SectorsPerTrack %d\n", List->DiskArray[DiskNumber].SectorsPerTrack);
310       DPRINT("BytesPerSector %d\n", List->DiskArray[DiskNumber].BytesPerSector);
311
312                   List->DiskArray[DiskNumber].DiskSize = 
313                     DiskGeometry.Cylinders.QuadPart *
314                     (ULONGLONG)DiskGeometry.TracksPerCylinder *
315                     (ULONGLONG)DiskGeometry.SectorsPerTrack *
316                     (ULONGLONG)DiskGeometry.BytesPerSector;
317                   List->DiskArray[DiskNumber].DiskNumber = DiskNumber;
318                   List->DiskArray[DiskNumber].Port = ScsiAddress.PortNumber;
319                   List->DiskArray[DiskNumber].Bus = ScsiAddress.PathId;
320                   List->DiskArray[DiskNumber].Id = ScsiAddress.TargetId;
321
322                   List->DiskArray[DiskNumber].FixedDisk = TRUE;
323
324                   GetDriverName(&List->DiskArray[DiskNumber]);
325
326                   LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap(ProcessHeap, 0, 8192);
327
328                   Status = NtDeviceIoControlFile(FileHandle,
329                                                  NULL,
330                                                  NULL,
331                                                  NULL,
332                                                  &Iosb,
333                                                  IOCTL_DISK_GET_DRIVE_LAYOUT,
334                                                  NULL,
335                                                  0,
336                                                  LayoutBuffer,
337                                                  8192);
338                   if (NT_SUCCESS(Status))
339                     {
340                       AddPartitionList(DiskNumber,
341                                        &List->DiskArray[DiskNumber],
342                                        LayoutBuffer);
343                     }
344
345                   RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
346                 }
347               else
348                 {
349                   /* mark removable disk entry */
350                   List->DiskArray[DiskNumber].FixedDisk = FALSE;
351                   List->DiskArray[DiskNumber].PartCount = 0;
352                   List->DiskArray[DiskNumber].PartArray = NULL;
353                 }
354             }
355
356           NtClose(FileHandle);
357         }
358     }
359
360   List->TopDisk = 0;
361   List->TopPartition = 0;
362
363   /* FIXME: search for first usable disk and partition */
364   List->CurrentDisk = 0;
365   List->CurrentPartition = 0;
366
367   return(List);
368 }
369
370 PPARTLIST
371 CreatePartitionList(SHORT Left,
372                     SHORT Top,
373                     SHORT Right,
374                     SHORT Bottom)
375 {
376   PPARTLIST List;
377
378   List = CreatePartitionListNoGUI();
379   if (List == NULL)
380     return(NULL);
381
382   List->Left = Left;
383   List->Top = Top;
384   List->Right = Right;
385   List->Bottom = Bottom;
386
387   DrawPartitionList(List);
388
389   return(List);
390 }
391
392
393 PPARTENTRY
394 GetPartitionInformation(PPARTLIST List,
395                         ULONG DiskNumber,
396                         ULONG PartitionNumber,
397                         PULONG PartEntryNumber)
398 {
399   PPARTENTRY PartEntry;
400   ULONG i;
401
402   if (List->DiskArray == NULL)
403     {
404       return(FALSE);
405     }
406
407   if (DiskNumber >= List->DiskCount)
408     {
409       return(FALSE);
410     }
411
412   if (PartitionNumber >= List->DiskArray[DiskNumber].PartCount)
413     {
414       return(FALSE);
415     }
416
417   if (List->DiskArray[DiskNumber].FixedDisk != TRUE)
418     {
419       return(FALSE);
420     }
421
422   for (i = 0; i < List->DiskArray[DiskNumber].PartCount; i++)
423     {
424       PartEntry = &List->DiskArray[DiskNumber].PartArray[i];
425       if (PartEntry->PartNumber == PartitionNumber)
426         {
427           *PartEntryNumber = i;
428           return PartEntry;
429         }
430     }
431   return NULL;
432 }
433
434 BOOLEAN
435 MarkPartitionActive(ULONG DiskNumber,
436                         ULONG PartitionNumber,
437                         PPARTDATA ActivePartition)
438 {
439   PPARTLIST List;
440   PPARTENTRY PartEntry;
441   ULONG PartEntryNumber;
442   OBJECT_ATTRIBUTES ObjectAttributes;
443   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
444   IO_STATUS_BLOCK Iosb;
445   NTSTATUS Status;
446   WCHAR Buffer[MAX_PATH];
447   UNICODE_STRING Name;
448   HANDLE FileHandle;
449
450   List = CreatePartitionListNoGUI();
451   if (List == NULL)
452     {
453       return(FALSE);
454     }
455
456   PartEntry = GetPartitionInformation(List,
457                         DiskNumber,
458                         PartitionNumber,
459                         &PartEntryNumber);
460   if (List == NULL)
461     {
462       DestroyPartitionList(List);
463       return(FALSE);
464     }
465
466
467   swprintf(Buffer,
468     L"\\Device\\Harddisk%d\\Partition0",
469     DiskNumber);
470   RtlInitUnicodeString(&Name, Buffer);
471
472   InitializeObjectAttributes(&ObjectAttributes,
473     &Name,
474     0,
475     NULL,
476     NULL);
477
478   Status = NtOpenFile(&FileHandle,
479     0x10001,
480     &ObjectAttributes,
481     &Iosb,
482     1,
483     FILE_SYNCHRONOUS_IO_NONALERT);
484   if (NT_SUCCESS(Status))
485     {
486           LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap(ProcessHeap, 0, 8192);
487
488           Status = NtDeviceIoControlFile(FileHandle,
489                 NULL,
490                 NULL,
491                 NULL,
492                 &Iosb,
493                 IOCTL_DISK_GET_DRIVE_LAYOUT,
494                 NULL,
495                 0,
496                 LayoutBuffer,
497                 8192);
498           if (!NT_SUCCESS(Status))
499             {
500           NtClose(FileHandle);
501           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
502           DestroyPartitionList(List);
503           return FALSE;
504         }
505
506
507       LayoutBuffer->PartitionEntry[PartEntryNumber].BootIndicator = TRUE;
508
509       Status = NtDeviceIoControlFile(FileHandle,
510         NULL,
511         NULL,
512         NULL,
513         &Iosb,
514         IOCTL_DISK_SET_DRIVE_LAYOUT,
515         LayoutBuffer,
516         8192,
517         NULL,
518         0);
519       if (!NT_SUCCESS(Status))
520         {
521           NtClose(FileHandle);
522           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
523           DestroyPartitionList(List);
524           return FALSE;
525         }
526     }
527   else
528     {
529       NtClose(FileHandle);
530       RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
531       DestroyPartitionList(List);
532       return FALSE;
533     }
534
535   NtClose(FileHandle);
536   RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
537
538   PartEntry->Active = TRUE;
539   if (!GetActiveBootPartition(List, ActivePartition))
540   {
541     DestroyPartitionList(List);
542     DPRINT("GetActiveBootPartition() failed\n");
543     return FALSE;
544   }
545
546   DestroyPartitionList(List);
547
548   return TRUE;
549 }
550
551
552 VOID
553 DestroyPartitionList(PPARTLIST List)
554 {
555   ULONG i;
556 #if 0
557   COORD coPos;
558   USHORT Width;
559
560   /* clear occupied screen area */
561   coPos.X = List->Left;
562   Width = List->Right - List->Left + 1;
563   for (coPos.Y = List->Top; coPos.Y <= List->Bottom; coPos.Y++)
564     {
565       FillConsoleOutputAttribute(0x17,
566                                  Width,
567                                  coPos,
568                                  &i);
569
570       FillConsoleOutputCharacter(' ',
571                                  Width,
572                                  coPos,
573                                  &i);
574     }
575 #endif
576
577   /* Release disk and partition info */
578   if (List->DiskArray != NULL)
579     {
580       for (i = 0; i < List->DiskCount; i++)
581         {
582           /* Release driver name */
583           RtlFreeUnicodeString(&List->DiskArray[i].DriverName);
584
585           /* Release partition array */
586           if (List->DiskArray[i].PartArray != NULL)
587             {
588               RtlFreeHeap(ProcessHeap, 0, List->DiskArray[i].PartArray);
589               List->DiskArray[i].PartCount = 0;
590               List->DiskArray[i].PartArray = NULL;
591             }
592         }
593
594       /* Release disk array */
595       RtlFreeHeap(ProcessHeap, 0, List->DiskArray);
596       List->DiskCount = 0;
597       List->DiskArray = NULL;
598     }
599
600   /* Release list head */
601   RtlFreeHeap(ProcessHeap, 0, List);
602 }
603
604
605 static VOID
606 PrintEmptyLine(PPARTLIST List)
607 {
608   COORD coPos;
609   ULONG Written;
610   USHORT Width;
611   USHORT Height;
612
613   Width = List->Right - List->Left - 1;
614   Height = List->Bottom - List->Top - 1;
615
616   if (List->Line < 0 || List->Line > Height)
617     return;
618
619   coPos.X = List->Left + 1;
620   coPos.Y = List->Top + 1 + List->Line;
621
622   FillConsoleOutputAttribute(0x17,
623                              Width,
624                              coPos,
625                              &Written);
626
627   FillConsoleOutputCharacter(' ',
628                              Width,
629                              coPos,
630                              &Written);
631
632   List->Line++;
633 }
634
635
636 static VOID
637 PrintPartitionData(PPARTLIST List,
638                    SHORT DiskIndex,
639                    SHORT PartIndex)
640 {
641   PPARTENTRY PartEntry;
642   CHAR LineBuffer[128];
643   COORD coPos;
644   ULONG Written;
645   USHORT Width;
646   USHORT Height;
647
648   ULONGLONG PartSize;
649   PCHAR Unit;
650   UCHAR Attribute;
651   PCHAR PartType;
652
653   Width = List->Right - List->Left - 1;
654   Height = List->Bottom - List->Top - 1;
655
656   if (List->Line < 0 || List->Line > Height)
657     return;
658
659   coPos.X = List->Left + 1;
660   coPos.Y = List->Top + 1 + List->Line;
661
662   PartEntry = &List->DiskArray[DiskIndex].PartArray[PartIndex];
663
664   /* Determine partition type */
665   PartType = NULL;
666   if (PartEntry->Unpartitioned == FALSE)
667     {
668       if ((PartEntry->PartType == PARTITION_FAT_12) ||
669           (PartEntry->PartType == PARTITION_FAT_16) ||
670           (PartEntry->PartType == PARTITION_HUGE) ||
671           (PartEntry->PartType == PARTITION_XINT13))
672         {
673           PartType = "FAT";
674         }
675       else if ((PartEntry->PartType == PARTITION_FAT32) ||
676                (PartEntry->PartType == PARTITION_FAT32_XINT13))
677         {
678           PartType = "FAT32";
679         }
680      else if (PartEntry->PartType == PARTITION_IFS)
681         {
682           PartType = "NTFS"; /* FIXME: Not quite correct! */
683         }
684     }
685
686
687 #if 0
688   if (PartEntry->PartSize >= 0x280000000ULL) /* 10 GB */
689     {
690       PartSize = (PartEntry->PartSize + (1 << 29)) >> 30;
691       Unit = "GB";
692     }
693   else
694 #endif
695   if (PartEntry->PartSize >= 0xA00000ULL) /* 10 MB */
696     {
697       PartSize = (PartEntry->PartSize + (1 << 19)) >> 20;
698       Unit = "MB";
699     }
700   else
701     {
702       PartSize = (PartEntry->PartSize + (1 << 9)) >> 10;
703       Unit = "KB";
704     }
705
706
707   if (PartEntry->Unpartitioned == TRUE)
708     {
709       sprintf(LineBuffer,
710               "    Unpartitioned space           %I64u %s",
711               PartSize,
712               Unit);
713     }
714   else if (PartEntry->DriveLetter != (CHAR)0)
715     {
716       if (PartType == NULL)
717         {
718           sprintf(LineBuffer,
719                   "%c:  Type %-3lu                        %I64u %s",
720                   PartEntry->DriveLetter,
721                   PartEntry->PartType,
722                   PartSize,
723                   Unit);
724         }
725       else
726         {
727           sprintf(LineBuffer,
728                   "%c:  %s                         %I64u %s",
729                   PartEntry->DriveLetter,
730                   PartType,
731                   PartSize,
732                   Unit);
733         }
734
735 #if 0
736       sprintf(LineBuffer,
737               "%c:  %s  (%d: nr: %d type: %x)  %I64u %s",
738               PartEntry->DriveLetter,
739               PartType,
740               PartIndex,
741               PartEntry->PartNumber,
742               PartEntry->PartType,
743               PartSize,
744               Unit);
745 #endif
746     }
747   else
748     {
749       sprintf(LineBuffer,
750               "--  %s  (%d: nr: %d type: %x)  %I64u %s",
751               PartEntry->FileSystemName,
752               PartIndex,
753               PartEntry->PartNumber,
754               PartEntry->PartType,
755               PartSize,
756               Unit);
757     }
758
759   Attribute = (List->CurrentDisk == DiskIndex &&
760                List->CurrentPartition == PartIndex) ? 0x71 : 0x17;
761
762   FillConsoleOutputCharacter(' ',
763                              Width,
764                              coPos,
765                              &Written);
766
767   coPos.X += 4;
768   Width -= 8;
769   FillConsoleOutputAttribute(Attribute,
770                              Width,
771                              coPos,
772                              &Written);
773
774   coPos.X++;
775   Width -= 2;
776   WriteConsoleOutputCharacters(LineBuffer,
777                                min(strlen(LineBuffer), Width),
778                                coPos);
779
780   List->Line++;
781 }
782
783
784 static VOID
785 PrintDiskData(PPARTLIST List,
786               SHORT DiskIndex)
787 {
788   PDISKENTRY DiskEntry;
789   CHAR LineBuffer[128];
790   COORD coPos;
791   ULONG Written;
792   USHORT Width;
793   USHORT Height;
794   ULONGLONG DiskSize;
795   PCHAR Unit;
796   SHORT PartIndex;
797
798   DiskEntry = &List->DiskArray[DiskIndex];
799
800   Width = List->Right - List->Left - 1;
801   Height = List->Bottom - List->Top - 1;
802
803   if (List->Line < 0 || List->Line > Height)
804     return;
805
806   coPos.X = List->Left + 1;
807   coPos.Y = List->Top + 1 + List->Line;
808
809 #if 0
810   if (DiskEntry->DiskSize >= 0x280000000ULL) /* 10 GB */
811     {
812       DiskSize = (DiskEntry->DiskSize + (1 << 29)) >> 30;
813       Unit = "GB";
814     }
815   else
816 #endif
817     {
818       DiskSize = (DiskEntry->DiskSize + (1 << 19)) >> 20;
819       if (DiskSize == 0)
820         DiskSize = 1;
821       Unit = "MB";
822     }
823
824   if (DiskEntry->DriverName.Length > 0)
825     {
826       sprintf(LineBuffer,
827               "%I64u %s  Harddisk %lu  (Port=%hu, Bus=%hu, Id=%hu) on %wZ",
828               DiskSize,
829               Unit,
830               DiskEntry->DiskNumber,
831               DiskEntry->Port,
832               DiskEntry->Bus,
833               DiskEntry->Id,
834               &DiskEntry->DriverName);
835     }
836   else
837     {
838       sprintf(LineBuffer,
839               "%I64u %s  Harddisk %lu  (Port=%hu, Bus=%hu, Id=%hu)",
840               DiskSize,
841               Unit,
842               DiskEntry->DiskNumber,
843               DiskEntry->Port,
844               DiskEntry->Bus,
845               DiskEntry->Id);
846     }
847
848   FillConsoleOutputAttribute(0x17,
849                              Width,
850                              coPos,
851                              &Written);
852
853   FillConsoleOutputCharacter(' ',
854                              Width,
855                              coPos,
856                              &Written);
857
858   coPos.X++;
859   WriteConsoleOutputCharacters(LineBuffer,
860                                min(strlen(LineBuffer), Width - 2),
861                                coPos);
862
863   List->Line++;
864
865   /* Print separator line */
866   PrintEmptyLine(List);
867
868
869   /* Print partition lines*/
870   for (PartIndex = 0; PartIndex < DiskEntry->PartCount; PartIndex++)
871     {
872       if (!DiskEntry->PartArray[PartIndex].HidePartEntry)
873         {
874           PrintPartitionData(List,
875                              DiskIndex,
876                              PartIndex);
877         }
878     }
879
880   /* Print separator line */
881   PrintEmptyLine(List);
882 }
883
884
885 VOID
886 DrawPartitionList(PPARTLIST List)
887 {
888   CHAR LineBuffer[128];
889   COORD coPos;
890   ULONG Written;
891   SHORT i;
892   SHORT DiskIndex;
893
894   /* draw upper left corner */
895   coPos.X = List->Left;
896   coPos.Y = List->Top;
897   FillConsoleOutputCharacter(0xDA, // '+',
898                              1,
899                              coPos,
900                              &Written);
901
902   /* draw upper edge */
903   coPos.X = List->Left + 1;
904   coPos.Y = List->Top;
905   FillConsoleOutputCharacter(0xC4, // '-',
906                              List->Right - List->Left - 1,
907                              coPos,
908                              &Written);
909
910   /* draw upper right corner */
911   coPos.X = List->Right;
912   coPos.Y = List->Top;
913   FillConsoleOutputCharacter(0xBF, // '+',
914                              1,
915                              coPos,
916                              &Written);
917
918   /* draw left and right edge */
919   for (i = List->Top + 1; i < List->Bottom; i++)
920     {
921       coPos.X = List->Left;
922       coPos.Y = i;
923       FillConsoleOutputCharacter(0xB3, // '|',
924                                  1,
925                                  coPos,
926                                  &Written);
927
928       coPos.X = List->Right;
929       FillConsoleOutputCharacter(0xB3, //'|',
930                                  1,
931                                  coPos,
932                                  &Written);
933     }
934
935   /* draw lower left corner */
936   coPos.X = List->Left;
937   coPos.Y = List->Bottom;
938   FillConsoleOutputCharacter(0xC0, // '+',
939                              1,
940                              coPos,
941                              &Written);
942
943   /* draw lower edge */
944   coPos.X = List->Left + 1;
945   coPos.Y = List->Bottom;
946   FillConsoleOutputCharacter(0xC4, // '-',
947                              List->Right - List->Left - 1,
948                              coPos,
949                              &Written);
950
951   /* draw lower right corner */
952   coPos.X = List->Right;
953   coPos.Y = List->Bottom;
954   FillConsoleOutputCharacter(0xD9, // '+',
955                              1,
956                              coPos,
957                              &Written);
958
959   /* print list entries */
960   List->Line = 0;
961   for (DiskIndex = 0; DiskIndex < List->DiskCount; DiskIndex++)
962     {
963       if (List->DiskArray[DiskIndex].FixedDisk == TRUE)
964         {
965           /* Print disk entry */
966           PrintDiskData(List,
967                         DiskIndex);
968         }
969     }
970 }
971
972
973 VOID
974 ScrollDownPartitionList(PPARTLIST List)
975 {
976   ULONG i;
977   ULONG j;
978
979   /* check for available disks */
980   if (List->DiskCount == 0)
981     return;
982
983   /* check for next usable entry on current disk */
984   for (i = List->CurrentPartition + 1; i < List->DiskArray[List->CurrentDisk].PartCount; i++)
985     {
986       if (!List->DiskArray[List->CurrentDisk].PartArray[i].HidePartEntry)
987         {
988           List->CurrentPartition = i;
989           DrawPartitionList(List);
990           return;
991         }
992     }
993
994   /* check for first usable entry on next disk */
995   for (j = List->CurrentDisk + 1; j < List->DiskCount; j++)
996     {
997       for (i = 0; i < List->DiskArray[j].PartCount; i++)
998         {
999     if (!List->DiskArray[j].PartArray[i].HidePartEntry)
1000       {
1001               List->CurrentDisk = j;
1002               List->CurrentPartition = i;
1003               DrawPartitionList(List);
1004               return;
1005       }
1006         }
1007     }
1008 }
1009
1010
1011 VOID
1012 ScrollUpPartitionList(PPARTLIST List)
1013 {
1014   ULONG i;
1015   ULONG j;
1016
1017   /* check for available disks */
1018   if (List->DiskCount == 0)
1019     return;
1020
1021   /* check for previous usable entry on current disk */
1022   for (i = List->CurrentPartition - 1; i != (ULONG)-1; i--)
1023     {
1024       if (!List->DiskArray[List->CurrentDisk].PartArray[i].HidePartEntry)
1025         {
1026           List->CurrentPartition = i;
1027           DrawPartitionList(List);
1028           return;
1029         }
1030     }
1031
1032   /* check for last usable entry on previous disk */
1033   for (j = List->CurrentDisk - 1; j != (ULONG)-1; j--)
1034     {
1035       for (i = List->DiskArray[j].PartCount - 1; i != (ULONG)-1; i--)
1036         {
1037     if (!List->DiskArray[j].PartArray[i].HidePartEntry)
1038             {
1039               List->CurrentDisk = j;
1040               List->CurrentPartition = i;
1041               DrawPartitionList(List);
1042               return;
1043             }
1044         }
1045     }
1046 }
1047
1048
1049 BOOL
1050 GetSelectedPartition(PPARTLIST List,
1051                      PPARTDATA Data)
1052 {
1053   PDISKENTRY DiskEntry;
1054   PPARTENTRY PartEntry;
1055
1056   if (List->CurrentDisk >= List->DiskCount)
1057     return(FALSE);
1058
1059   DiskEntry = &List->DiskArray[List->CurrentDisk];
1060
1061   if (DiskEntry->FixedDisk == FALSE)
1062     return(FALSE);
1063
1064   if (List->CurrentPartition >= DiskEntry->PartCount)
1065     return(FALSE);
1066
1067   PartEntry = &DiskEntry->PartArray[List->CurrentPartition];
1068
1069   if (PartEntry->Used == FALSE)
1070     {
1071       return(FALSE);
1072     }
1073
1074   /* Copy disk-specific data */
1075   Data->DiskSize = DiskEntry->DiskSize;
1076   Data->DiskNumber = DiskEntry->DiskNumber;
1077   Data->Port = DiskEntry->Port;
1078   Data->Bus = DiskEntry->Bus;
1079   Data->Id = DiskEntry->Id;
1080
1081   /* Copy driver name */
1082   RtlInitUnicodeString(&Data->DriverName,
1083                        NULL);
1084   if (DiskEntry->DriverName.Length != 0)
1085     {
1086       Data->DriverName.Buffer = RtlAllocateHeap(ProcessHeap,
1087                                                 0,
1088                                                 DiskEntry->DriverName.MaximumLength);
1089       if (Data->DriverName.Buffer != NULL)
1090         {
1091           Data->DriverName.MaximumLength = DiskEntry->DriverName.MaximumLength;
1092           Data->DriverName.Length = DiskEntry->DriverName.Length;
1093           RtlCopyMemory(Data->DriverName.Buffer,
1094                         DiskEntry->DriverName.Buffer,
1095                         DiskEntry->DriverName.MaximumLength);
1096         }
1097     }
1098
1099   /* Copy partition-specific data */
1100   Data->CreatePartition = FALSE;
1101   Data->NewPartSize = 0;
1102   Data->PartSize = PartEntry->PartSize;
1103   Data->PartNumber = PartEntry->PartNumber;
1104   Data->PartType = PartEntry->PartType;
1105   Data->DriveLetter = PartEntry->DriveLetter;
1106   return(TRUE);
1107 }
1108
1109
1110 BOOL
1111 GetActiveBootPartition(PPARTLIST List,
1112                        PPARTDATA Data)
1113 {
1114   PDISKENTRY DiskEntry;
1115   PPARTENTRY PartEntry;
1116   ULONG i;
1117
1118   if (List->CurrentDisk >= List->DiskCount)
1119     return(FALSE);
1120
1121   DiskEntry = &List->DiskArray[List->CurrentDisk];
1122
1123   if (DiskEntry->FixedDisk == FALSE)
1124   {
1125     return(FALSE);
1126   }
1127
1128   for (i = 0; i < DiskEntry->PartCount; i++)
1129     {
1130       if (DiskEntry->PartArray[i].Active)
1131         {
1132           PartEntry = &DiskEntry->PartArray[i];
1133
1134           if (PartEntry->Used == FALSE)
1135       {
1136             return(FALSE);
1137       }
1138
1139           /* Copy disk-specific data */
1140           Data->DiskSize = DiskEntry->DiskSize;
1141           Data->DiskNumber = DiskEntry->DiskNumber;
1142           Data->Port = DiskEntry->Port;
1143           Data->Bus = DiskEntry->Bus;
1144           Data->Id = DiskEntry->Id;
1145
1146           /* Copy driver name */
1147           RtlInitUnicodeString(&Data->DriverName,
1148                                NULL);
1149           if (DiskEntry->DriverName.Length != 0)
1150             {
1151               Data->DriverName.Buffer = RtlAllocateHeap(ProcessHeap,
1152                                                         0,
1153                                                         DiskEntry->DriverName.MaximumLength);
1154               if (Data->DriverName.Buffer != NULL)
1155                 {
1156                   Data->DriverName.MaximumLength = DiskEntry->DriverName.MaximumLength;
1157                   Data->DriverName.Length = DiskEntry->DriverName.Length;
1158                   RtlCopyMemory(Data->DriverName.Buffer,
1159                                 DiskEntry->DriverName.Buffer,
1160                                 DiskEntry->DriverName.MaximumLength);
1161                 }
1162             }
1163
1164           /* Copy partition-specific data */
1165           Data->PartSize = PartEntry->PartSize;
1166           Data->PartNumber = PartEntry->PartNumber;
1167           Data->PartType = PartEntry->PartType;
1168           Data->DriveLetter = PartEntry->DriveLetter;
1169
1170           return(TRUE);
1171         }
1172     }
1173
1174   return(FALSE);
1175 }
1176
1177
1178 BOOL
1179 CreateSelectedPartition(PPARTLIST List,
1180   ULONG PartType,
1181   ULONGLONG NewPartSize)
1182 {
1183   PDISKENTRY DiskEntry;
1184   PPARTENTRY PartEntry;
1185   ULONG PartEntryNumber;
1186   OBJECT_ATTRIBUTES ObjectAttributes;
1187   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
1188   IO_STATUS_BLOCK Iosb;
1189   NTSTATUS Status;
1190   WCHAR Buffer[MAX_PATH];
1191   UNICODE_STRING Name;
1192   HANDLE FileHandle;
1193   LARGE_INTEGER li;
1194
1195   DiskEntry = &List->DiskArray[List->CurrentDisk];
1196   PartEntry = &DiskEntry->PartArray[List->CurrentPartition];
1197   PartEntry->PartType = PartType;
1198   PartEntryNumber = List->CurrentPartition;
1199
1200   DPRINT("NewPartSize %d (%d MB)\n", NewPartSize, NewPartSize / (1024 * 1024));
1201   DPRINT("PartEntry->StartingOffset %d\n", PartEntry->StartingOffset);
1202   DPRINT("PartEntry->PartSize %d\n", PartEntry->PartSize);
1203   DPRINT("PartEntry->PartNumber %d\n", PartEntry->PartNumber);
1204   DPRINT("PartEntry->PartType 0x%x\n", PartEntry->PartType);
1205   DPRINT("PartEntry->FileSystemName %s\n", PartEntry->FileSystemName);
1206
1207   swprintf(Buffer,
1208     L"\\Device\\Harddisk%d\\Partition0",
1209     DiskEntry->DiskNumber);
1210   RtlInitUnicodeString(&Name, Buffer);
1211
1212   InitializeObjectAttributes(&ObjectAttributes,
1213     &Name,
1214     0,
1215     NULL,
1216     NULL);
1217
1218   Status = NtOpenFile(&FileHandle,
1219     0x10001,
1220     &ObjectAttributes,
1221     &Iosb,
1222     1,
1223     FILE_SYNCHRONOUS_IO_NONALERT);
1224   if (NT_SUCCESS(Status))
1225     {
1226           LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap(ProcessHeap, 0, 8192);
1227
1228           Status = NtDeviceIoControlFile(FileHandle,
1229                 NULL,
1230                 NULL,
1231                 NULL,
1232                 &Iosb,
1233                 IOCTL_DISK_GET_DRIVE_LAYOUT,
1234                 NULL,
1235                 0,
1236                 LayoutBuffer,
1237                 8192);
1238           if (!NT_SUCCESS(Status))
1239             {
1240           DPRINT("IOCTL_DISK_GET_DRIVE_LAYOUT failed() 0x%.08x\n", Status);
1241           NtClose(FileHandle);
1242           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1243           return FALSE;
1244         }
1245
1246       li.QuadPart = PartEntry->StartingOffset;
1247       LayoutBuffer->PartitionEntry[PartEntryNumber].StartingOffset = li;
1248       li.QuadPart = NewPartSize;
1249       LayoutBuffer->PartitionEntry[PartEntryNumber].PartitionLength = li;
1250       LayoutBuffer->PartitionEntry[PartEntryNumber].HiddenSectors = 0;  /* FIXME: ? */
1251       LayoutBuffer->PartitionEntry[PartEntryNumber].PartitionType = PartType;
1252       LayoutBuffer->PartitionEntry[PartEntryNumber].RecognizedPartition = TRUE;
1253       LayoutBuffer->PartitionEntry[PartEntryNumber].RewritePartition = TRUE;
1254
1255       Status = NtDeviceIoControlFile(FileHandle,
1256         NULL,
1257         NULL,
1258         NULL,
1259         &Iosb,
1260         IOCTL_DISK_SET_DRIVE_LAYOUT,
1261         LayoutBuffer,
1262         8192,
1263         NULL,
1264         0);
1265       if (!NT_SUCCESS(Status))
1266         {
1267           DPRINT("IOCTL_DISK_SET_DRIVE_LAYOUT failed() 0x%.08x\n", Status);
1268           NtClose(FileHandle);
1269           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1270           return FALSE;
1271         }
1272     }
1273   else
1274     {
1275       DPRINT("NtOpenFile failed() 0x%.08x\n", Status);
1276       NtClose(FileHandle);
1277       RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1278       return FALSE;
1279     }
1280
1281   NtClose(FileHandle);
1282   RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1283
1284   return TRUE;
1285 }
1286
1287
1288 BOOL
1289 DeleteSelectedPartition(PPARTLIST List)
1290 {
1291   PDISKENTRY DiskEntry;
1292   PPARTENTRY PartEntry;
1293   ULONG PartEntryNumber;
1294   OBJECT_ATTRIBUTES ObjectAttributes;
1295   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
1296   IO_STATUS_BLOCK Iosb;
1297   NTSTATUS Status;
1298   WCHAR Buffer[MAX_PATH];
1299   UNICODE_STRING Name;
1300   HANDLE FileHandle;
1301   LARGE_INTEGER li;
1302
1303   DiskEntry = &List->DiskArray[List->CurrentDisk];
1304   PartEntry = &DiskEntry->PartArray[List->CurrentPartition];
1305   PartEntry->PartType = PARTITION_ENTRY_UNUSED;
1306   PartEntryNumber = List->CurrentPartition;
1307
1308   DPRINT1("DeleteSelectedPartition(PartEntryNumber = %d)\n", PartEntryNumber);
1309   DPRINT1("PartEntry->StartingOffset %d\n", PartEntry->StartingOffset);
1310   DPRINT1("PartEntry->PartSize %d\n", PartEntry->PartSize);
1311   DPRINT1("PartEntry->PartNumber %d\n", PartEntry->PartNumber);
1312   DPRINT1("PartEntry->PartType 0x%x\n", PartEntry->PartType);
1313   DPRINT1("PartEntry->FileSystemName %s\n", PartEntry->FileSystemName);
1314
1315   swprintf(Buffer,
1316     L"\\Device\\Harddisk%d\\Partition0",
1317     DiskEntry->DiskNumber);
1318   RtlInitUnicodeString(&Name, Buffer);
1319
1320   InitializeObjectAttributes(&ObjectAttributes,
1321     &Name,
1322     0,
1323     NULL,
1324     NULL);
1325
1326   Status = NtOpenFile(&FileHandle,
1327     0x10001,
1328     &ObjectAttributes,
1329     &Iosb,
1330     1,
1331     FILE_SYNCHRONOUS_IO_NONALERT);
1332   if (NT_SUCCESS(Status))
1333     {
1334           LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap(ProcessHeap, 0, 8192);
1335
1336           Status = NtDeviceIoControlFile(FileHandle,
1337                 NULL,
1338                 NULL,
1339                 NULL,
1340                 &Iosb,
1341                 IOCTL_DISK_GET_DRIVE_LAYOUT,
1342                 NULL,
1343                 0,
1344                 LayoutBuffer,
1345                 8192);
1346           if (!NT_SUCCESS(Status))
1347             {
1348           DPRINT("IOCTL_DISK_GET_DRIVE_LAYOUT failed() 0x%.08x\n", Status);
1349           NtClose(FileHandle);
1350           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1351           return FALSE;
1352         }
1353
1354       li.QuadPart = 0;
1355       LayoutBuffer->PartitionEntry[PartEntryNumber].StartingOffset = li;
1356       li.QuadPart = 0;
1357       LayoutBuffer->PartitionEntry[PartEntryNumber].PartitionLength = li;
1358       LayoutBuffer->PartitionEntry[PartEntryNumber].HiddenSectors = 0;
1359       LayoutBuffer->PartitionEntry[PartEntryNumber].PartitionType = 0;
1360       LayoutBuffer->PartitionEntry[PartEntryNumber].RecognizedPartition = FALSE;
1361       LayoutBuffer->PartitionEntry[PartEntryNumber].RewritePartition = TRUE;
1362
1363       Status = NtDeviceIoControlFile(FileHandle,
1364         NULL,
1365         NULL,
1366         NULL,
1367         &Iosb,
1368         IOCTL_DISK_SET_DRIVE_LAYOUT,
1369         LayoutBuffer,
1370         8192,
1371         NULL,
1372         0);
1373       if (!NT_SUCCESS(Status))
1374         {
1375           DPRINT("IOCTL_DISK_SET_DRIVE_LAYOUT failed() 0x%.08x\n", Status);
1376           NtClose(FileHandle);
1377           RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1378           return FALSE;
1379         }
1380     }
1381   else
1382     {
1383       DPRINT("NtOpenFile failed() 0x%.08x\n", Status);
1384       NtClose(FileHandle);
1385       RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1386       return FALSE;
1387     }
1388
1389   NtClose(FileHandle);
1390   RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
1391
1392   return TRUE;
1393 }
1394
1395 /* EOF */