d00867fcd92c4eafad4cdda8051f850034e56936
[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 /*
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  */
26
27 #include <ddk/ntddk.h>
28 #include <ddk/ntddscsi.h>
29
30 #include <ntdll/rtl.h>
31
32 #include <ntos/minmax.h>
33
34 #include "usetup.h"
35 #include "console.h"
36 #include "partlist.h"
37
38
39 /* FUNCTIONS ****************************************************************/
40
41 PPARTLIST
42 CreatePartitionList(SHORT Left,
43                     SHORT Top,
44                     SHORT Right,
45                     SHORT Bottom)
46 {
47   PPARTLIST List;
48   OBJECT_ATTRIBUTES ObjectAttributes;
49   SYSTEM_DEVICE_INFORMATION Sdi;
50   DISK_GEOMETRY DiskGeometry;
51   ULONG ReturnSize;
52   NTSTATUS Status;
53   ULONG DiskCount;
54   IO_STATUS_BLOCK Iosb;
55   WCHAR Buffer[MAX_PATH];
56   UNICODE_STRING Name;
57   HANDLE FileHandle;
58   DRIVE_LAYOUT_INFORMATION *LayoutBuffer;
59   SCSI_ADDRESS ScsiAddress;
60   ULONG i;
61
62   List = (PPARTLIST)RtlAllocateHeap(ProcessHeap, 0, sizeof(PARTLIST));
63   if (List == NULL)
64     return(NULL);
65
66   List->Left = Left;
67   List->Top = Top;
68   List->Right = Right;
69   List->Bottom = Bottom;
70
71   List->Line = 0;
72
73   List->TopDisk = (ULONG)-1;
74   List->TopPartition = (ULONG)-1;
75
76   List->CurrentDisk = (ULONG)-1;
77   List->CurrentPartition = (ULONG)-1;
78
79   List->DiskCount = 0;
80   List->DiskArray = NULL;
81
82
83   Status = NtQuerySystemInformation(SystemDeviceInformation,
84                                     &Sdi,
85                                     sizeof(SYSTEM_DEVICE_INFORMATION),
86                                     &ReturnSize);
87   if (!NT_SUCCESS(Status))
88     {
89       RtlFreeHeap(ProcessHeap, 0, List);
90       return(NULL);
91     }
92
93   List->DiskArray = (PDISKENTRY)RtlAllocateHeap(ProcessHeap,
94                                                 0,
95                                                 Sdi.NumberOfDisks * sizeof(DISKENTRY));
96   List->DiskCount = Sdi.NumberOfDisks;
97
98   for (DiskCount = 0; DiskCount < Sdi.NumberOfDisks; DiskCount++)
99     {
100       swprintf(Buffer,
101                L"\\Device\\Harddisk%d\\Partition0",
102                DiskCount);
103       RtlInitUnicodeString(&Name,
104                            Buffer);
105
106       InitializeObjectAttributes(&ObjectAttributes,
107                                  &Name,
108                                  0,
109                                  NULL,
110                                  NULL);
111
112       Status = NtOpenFile(&FileHandle,
113                           0x10001,
114                           &ObjectAttributes,
115                           &Iosb,
116                           1,
117                           FILE_SYNCHRONOUS_IO_NONALERT);
118       if (NT_SUCCESS(Status))
119         {
120           Status = NtDeviceIoControlFile(FileHandle,
121                                          NULL,
122                                          NULL,
123                                          NULL,
124                                          &Iosb,
125                                          IOCTL_DISK_GET_DRIVE_GEOMETRY,
126                                          NULL,
127                                          0,
128                                          &DiskGeometry,
129                                          sizeof(DISK_GEOMETRY));
130           if (NT_SUCCESS(Status))
131             {
132               if (DiskGeometry.MediaType == FixedMedia)
133                 {
134                   Status = NtDeviceIoControlFile(FileHandle,
135                                                  NULL,
136                                                  NULL,
137                                                  NULL,
138                                                  &Iosb,
139                                                  IOCTL_SCSI_GET_ADDRESS,
140                                                  NULL,
141                                                  0,
142                                                  &ScsiAddress,
143                                                  sizeof(SCSI_ADDRESS));
144
145
146                   List->DiskArray[DiskCount].DiskSize = 
147                     DiskGeometry.Cylinders.QuadPart *
148                     (ULONGLONG)DiskGeometry.TracksPerCylinder *
149                     (ULONGLONG)DiskGeometry.SectorsPerTrack *
150                     (ULONGLONG)DiskGeometry.BytesPerSector;
151                   List->DiskArray[DiskCount].DiskNumber = DiskCount;
152                   List->DiskArray[DiskCount].Port = ScsiAddress.PortNumber;
153                   List->DiskArray[DiskCount].Bus = ScsiAddress.PathId;
154                   List->DiskArray[DiskCount].Id = ScsiAddress.TargetId;
155
156                   List->DiskArray[DiskCount].FixedDisk = TRUE;
157
158
159                   LayoutBuffer = (DRIVE_LAYOUT_INFORMATION*)RtlAllocateHeap(ProcessHeap, 0, 8192);
160
161                   Status = NtDeviceIoControlFile(FileHandle,
162                                                  NULL,
163                                                  NULL,
164                                                  NULL,
165                                                  &Iosb,
166                                                  IOCTL_DISK_GET_DRIVE_LAYOUT,
167                                                  NULL,
168                                                  0,
169                                                  LayoutBuffer,
170                                                  8192);
171                   if (NT_SUCCESS(Status))
172                     {
173
174                       List->DiskArray[DiskCount].PartArray = (PPARTENTRY)RtlAllocateHeap(ProcessHeap,
175                                                                                          0,
176                                                                                          LayoutBuffer->PartitionCount * sizeof(PARTENTRY));
177                       List->DiskArray[DiskCount].PartCount = LayoutBuffer->PartitionCount;
178
179                       for (i = 0; i < LayoutBuffer->PartitionCount; i++)
180                         {
181                           if ((LayoutBuffer->PartitionEntry[i].PartitionType != PARTITION_ENTRY_UNUSED) &&
182                               !IsContainerPartition(LayoutBuffer->PartitionEntry[i].PartitionType))
183                             {
184                               List->DiskArray[DiskCount].PartArray[i].PartSize = LayoutBuffer->PartitionEntry[i].PartitionLength.QuadPart;
185                               List->DiskArray[DiskCount].PartArray[i].PartNumber = LayoutBuffer->PartitionEntry[i].PartitionNumber,
186                               List->DiskArray[DiskCount].PartArray[i].PartType = LayoutBuffer->PartitionEntry[i].PartitionType;
187                               List->DiskArray[DiskCount].PartArray[i].Used = TRUE;
188                             }
189                           else
190                             {
191                               List->DiskArray[DiskCount].PartArray[i].Used = FALSE;
192                             }
193                         }
194                     }
195
196                   RtlFreeHeap(ProcessHeap, 0, LayoutBuffer);
197                 }
198               else
199                 {
200                   /* mark removable disk entry */
201                   List->DiskArray[DiskCount].FixedDisk = FALSE;
202                   List->DiskArray[DiskCount].PartCount = 0;
203                   List->DiskArray[DiskCount].PartArray = NULL;
204                 }
205             }
206
207           NtClose(FileHandle);
208         }
209     }
210
211   List->TopDisk = 0;
212   List->TopPartition = 0;
213
214   /* FIXME: search for first usable disk and partition */
215   List->CurrentDisk = 0;
216   List->CurrentPartition = 0;
217
218   DrawPartitionList(List);
219
220   return(List);
221 }
222
223
224 VOID
225 DestroyPartitionList(PPARTLIST List)
226 {
227   ULONG i;
228 #if 0
229   COORD coPos;
230   USHORT Width;
231
232   /* clear occupied screen area */
233   coPos.X = List->Left;
234   Width = List->Right - List->Left + 1;
235   for (coPos.Y = List->Top; coPos.Y <= List->Bottom; coPos.Y++)
236     {
237       FillConsoleOutputAttribute(0x17,
238                                  Width,
239                                  coPos,
240                                  &i);
241
242       FillConsoleOutputCharacter(' ',
243                                  Width,
244                                  coPos,
245                                  &i);
246     }
247 #endif
248
249   /* free disk and partition info */
250   if (List->DiskArray != NULL)
251     {
252       /* free partition arrays */
253       for (i = 0; i < List->DiskCount; i++)
254         {
255           if (List->DiskArray[i].PartArray != NULL)
256             {
257               RtlFreeHeap(ProcessHeap, 0, List->DiskArray[i].PartArray);
258               List->DiskArray[i].PartCount = 0;
259               List->DiskArray[i].PartArray = NULL;
260             }
261         }
262
263       /* free disk array */
264       RtlFreeHeap(ProcessHeap, 0, List->DiskArray);
265       List->DiskCount = 0;
266       List->DiskArray = NULL;
267     }
268
269   /* free list head */
270   RtlFreeHeap(ProcessHeap, 0, List);
271 }
272
273
274 static VOID
275 PrintEmptyLine(PPARTLIST List)
276 {
277   COORD coPos;
278   ULONG Written;
279   USHORT Width;
280   USHORT Height;
281
282   Width = List->Right - List->Left - 1;
283   Height = List->Bottom - List->Top - 1;
284
285   if (List->Line < 0 || List->Line > Height)
286     return;
287
288   coPos.X = List->Left + 1;
289   coPos.Y = List->Top + 1 + List->Line;
290
291   FillConsoleOutputAttribute(0x17,
292                              Width,
293                              coPos,
294                              &Written);
295
296   FillConsoleOutputCharacter(' ',
297                              Width,
298                              coPos,
299                              &Written);
300
301   List->Line++;
302 }
303
304
305 static VOID
306 PrintPartitionData(PPARTLIST List,
307                    SHORT DiskIndex,
308                    SHORT PartIndex)
309 {
310   PPARTENTRY PartEntry;
311   CHAR LineBuffer[128];
312   COORD coPos;
313   ULONG Written;
314   USHORT Width;
315   USHORT Height;
316
317   ULONGLONG PartSize;
318   PCHAR Unit;
319   PCHAR PartType;
320   UCHAR Attribute;
321
322
323   Width = List->Right - List->Left - 1;
324   Height = List->Bottom - List->Top - 1;
325
326   if (List->Line < 0 || List->Line > Height)
327     return;
328
329   coPos.X = List->Left + 1;
330   coPos.Y = List->Top + 1 + List->Line;
331
332   PartEntry = &List->DiskArray[DiskIndex].PartArray[PartIndex];
333
334   if ((PartEntry->PartType == PARTITION_FAT_12) ||
335       (PartEntry->PartType == PARTITION_FAT_16) ||
336       (PartEntry->PartType == PARTITION_HUGE) ||
337       (PartEntry->PartType == PARTITION_XINT13))
338     {
339       PartType = "FAT";
340     }
341   else if ((PartEntry->PartType == PARTITION_FAT32) ||
342            (PartEntry->PartType == PARTITION_FAT32_XINT13))
343     {
344       PartType = "FAT32";
345     }
346   else if (PartEntry->PartType == PARTITION_IFS)
347     {
348       PartType = "NTFS"; /* FIXME: Not quite correct! */
349     }
350   else
351     {
352       PartType = "Unknown";
353     }
354
355   if (PartEntry->PartSize >= 0x280000000ULL) /* 10 GB */
356     {
357       PartSize = (PartEntry->PartSize + (1 << 29)) >> 30;
358       Unit = "GB";
359     }
360   else if (PartEntry->PartSize >= 0xA00000ULL) /* 10 MB */
361     {
362       PartSize = (PartEntry->PartSize + (1 << 19)) >> 20;
363       Unit = "MB";
364     }
365   else
366     {
367       PartSize = (PartEntry->PartSize + (1 << 9)) >> 10;
368       Unit = "kB";
369     }
370
371   sprintf(LineBuffer,
372           "%d: nr: %d type: %x (%s)  %I64u %s",
373           PartIndex,
374           PartEntry->PartNumber,
375           PartEntry->PartType,
376           PartType,
377           PartSize,
378           Unit);
379
380
381   Attribute = (List->CurrentDisk == DiskIndex &&
382                List->CurrentPartition == PartIndex) ? 0x71 : 0x17;
383
384   FillConsoleOutputCharacter(' ',
385                              Width,
386                              coPos,
387                              &Written);
388
389   coPos.X += 4;
390   Width -= 8;
391   FillConsoleOutputAttribute(Attribute,
392                              Width,
393                              coPos,
394                              &Written);
395
396   coPos.X++;
397   Width -= 2;
398   WriteConsoleOutputCharacters(LineBuffer,
399                                min(strlen(LineBuffer), Width),
400                                coPos);
401
402   List->Line++;
403 }
404
405
406 static VOID
407 PrintDiskData(PPARTLIST List,
408               SHORT DiskIndex)
409 {
410   PDISKENTRY DiskEntry;
411   CHAR LineBuffer[128];
412   COORD coPos;
413   ULONG Written;
414   USHORT Width;
415   USHORT Height;
416   ULONGLONG DiskSize;
417   PCHAR Unit;
418   SHORT PartIndex;
419   BOOL PartPrinted;
420
421   DiskEntry = &List->DiskArray[DiskIndex];
422
423   Width = List->Right - List->Left - 1;
424   Height = List->Bottom - List->Top - 1;
425
426   if (List->Line < 0 || List->Line > Height)
427     return;
428
429   coPos.X = List->Left + 1;
430   coPos.Y = List->Top + 1 + List->Line;
431
432   if (DiskEntry->DiskSize >= 0x280000000ULL) /* 10 GB */
433     {
434       DiskSize = (DiskEntry->DiskSize + (1 << 29)) >> 30;
435       Unit = "GB";
436     }
437   else if (DiskEntry->DiskSize >= 0xA00000ULL) /* 10 MB */
438     {
439       DiskSize = (DiskEntry->DiskSize + (1 << 19)) >> 20;
440       Unit = "MB";
441     }
442   else
443     {
444       DiskSize = (DiskEntry->DiskSize + (1 << 9)) >> 10;
445       Unit = "kB";
446     }
447
448   sprintf(LineBuffer,
449           "%I64u %s Harddisk %lu  (Port=%hu, Bus=%hu, Id=%hu)",
450           DiskSize,
451           Unit,
452           DiskEntry->DiskNumber,
453           DiskEntry->Port,
454           DiskEntry->Bus,
455           DiskEntry->Id);
456
457   FillConsoleOutputAttribute(0x17,
458                              Width,
459                              coPos,
460                              &Written);
461
462   FillConsoleOutputCharacter(' ',
463                              Width,
464                              coPos,
465                              &Written);
466
467   coPos.X++;
468   WriteConsoleOutputCharacters(LineBuffer,
469                                min(strlen(LineBuffer), Width - 2),
470                                coPos);
471
472   List->Line++;
473
474   /* Print separator line */
475   PrintEmptyLine(List);
476
477
478   PartPrinted = FALSE;
479
480   /* Print partition lines*/
481   for (PartIndex = 0; PartIndex < List->DiskArray[DiskIndex].PartCount; PartIndex++)
482     {
483       if (List->DiskArray[DiskIndex].PartArray[PartIndex].Used == TRUE)
484         {
485           PrintPartitionData(List,
486                              DiskIndex,
487                              PartIndex);
488           PartPrinted = TRUE;
489         }
490     }
491
492   /* Print separator line */
493   if (PartPrinted == TRUE)
494     {
495       PrintEmptyLine(List);
496     }
497 }
498
499
500 VOID
501 DrawPartitionList(PPARTLIST List)
502 {
503   CHAR LineBuffer[128];
504   COORD coPos;
505   ULONG Written;
506   SHORT i;
507   SHORT DiskIndex;
508
509   /* draw upper left corner */
510   coPos.X = List->Left;
511   coPos.Y = List->Top;
512   FillConsoleOutputCharacter(0xDA, // '+',
513                              1,
514                              coPos,
515                              &Written);
516
517   /* draw upper edge */
518   coPos.X = List->Left + 1;
519   coPos.Y = List->Top;
520   FillConsoleOutputCharacter(0xC4, // '-',
521                              List->Right - List->Left - 1,
522                              coPos,
523                              &Written);
524
525   /* draw upper right corner */
526   coPos.X = List->Right;
527   coPos.Y = List->Top;
528   FillConsoleOutputCharacter(0xBF, // '+',
529                              1,
530                              coPos,
531                              &Written);
532
533   /* draw left and right edge */
534   for (i = List->Top + 1; i < List->Bottom; i++)
535     {
536       coPos.X = List->Left;
537       coPos.Y = i;
538       FillConsoleOutputCharacter(0xB3, // '|',
539                                  1,
540                                  coPos,
541                                  &Written);
542
543       coPos.X = List->Right;
544       FillConsoleOutputCharacter(0xB3, //'|',
545                                  1,
546                                  coPos,
547                                  &Written);
548     }
549
550   /* draw lower left corner */
551   coPos.X = List->Left;
552   coPos.Y = List->Bottom;
553   FillConsoleOutputCharacter(0xC0, // '+',
554                              1,
555                              coPos,
556                              &Written);
557
558   /* draw lower edge */
559   coPos.X = List->Left + 1;
560   coPos.Y = List->Bottom;
561   FillConsoleOutputCharacter(0xC4, // '-',
562                              List->Right - List->Left - 1,
563                              coPos,
564                              &Written);
565
566   /* draw upper right corner */
567   coPos.X = List->Right;
568   coPos.Y = List->Bottom;
569   FillConsoleOutputCharacter(0xD9, // '+',
570                              1,
571                              coPos,
572                              &Written);
573
574   /* print list entries */
575   List->Line = 0;
576   for (DiskIndex = 0; DiskIndex < List->DiskCount; DiskIndex++)
577     {
578       if (List->DiskArray[DiskIndex].FixedDisk == TRUE)
579         {
580           /* Print disk entry */
581           PrintDiskData(List,
582                         DiskIndex);
583         }
584     }
585 }
586
587
588 VOID
589 ScrollDownPartitionList(PPARTLIST List)
590 {
591   ULONG i;
592   ULONG j;
593
594   /* check for available disks */
595   if (List->DiskCount == 0)
596     return;
597
598   /* check for next usable entry on current disk */
599   for (i = List->CurrentPartition + 1; i < List->DiskArray[List->CurrentDisk].PartCount; i++)
600     {
601       if (List->DiskArray[List->CurrentDisk].PartArray[i].Used == TRUE)
602         {
603           List->CurrentPartition = i;
604           DrawPartitionList(List);
605           return;
606         }
607     }
608
609   /* check for first usable entry on next disk */
610   for (j = List->CurrentDisk + 1; j < List->DiskCount; j++)
611     {
612       for (i = 0; i < List->DiskArray[j].PartCount; i++)
613         {
614           if (List->DiskArray[j].PartArray[i].Used == TRUE)
615             {
616               List->CurrentDisk = j;
617               List->CurrentPartition = i;
618               DrawPartitionList(List);
619               return;
620             }
621         }
622     }
623 }
624
625
626 VOID
627 ScrollUpPartitionList(PPARTLIST List)
628 {
629   ULONG i;
630   ULONG j;
631
632   /* check for available disks */
633   if (List->DiskCount == 0)
634     return;
635
636   /* check for previous usable entry on current disk */
637   for (i = List->CurrentPartition - 1; i != (ULONG)-1; i--)
638     {
639       if (List->DiskArray[List->CurrentDisk].PartArray[i].Used == TRUE)
640         {
641           List->CurrentPartition = i;
642           DrawPartitionList(List);
643           return;
644         }
645     }
646
647   /* check for last usable entry on previous disk */
648   for (j = List->CurrentDisk - 1; j != (ULONG)-1; j--)
649     {
650       for (i = List->DiskArray[j].PartCount - 1; i != (ULONG)-1; i--)
651         {
652           if (List->DiskArray[j].PartArray[i].Used == TRUE)
653             {
654               List->CurrentDisk = j;
655               List->CurrentPartition = i;
656               DrawPartitionList(List);
657               return;
658             }
659         }
660     }
661 }
662
663
664 BOOL
665 GetPartitionData(PPARTLIST List,
666                  PPARTDATA Data)
667 {
668   if (List->CurrentDisk >= List->DiskCount)
669     return(FALSE);
670
671   if (List->DiskArray[List->CurrentDisk].FixedDisk == FALSE)
672     return(FALSE);
673
674   if (List->CurrentPartition >= List->DiskArray[List->CurrentDisk].PartCount)
675     return(FALSE);
676
677   if (List->DiskArray[List->CurrentDisk].PartArray[List->CurrentPartition].Used == FALSE)
678     return(FALSE);
679
680   Data->DiskSize = List->DiskArray[List->CurrentDisk].DiskSize;
681   Data->DiskNumber = List->DiskArray[List->CurrentDisk].DiskNumber;
682   Data->Port = List->DiskArray[List->CurrentDisk].Port;
683   Data->Bus = List->DiskArray[List->CurrentDisk].Bus;
684   Data->Id = List->DiskArray[List->CurrentDisk].Id;
685
686   Data->PartSize = List->DiskArray[List->CurrentDisk].PartArray[List->CurrentPartition].PartSize;
687   Data->PartNumber = List->DiskArray[List->CurrentDisk].PartArray[List->CurrentPartition].PartNumber;
688   Data->PartType = List->DiskArray[List->CurrentDisk].PartArray[List->CurrentPartition].PartType;
689
690   return(TRUE);
691 }
692
693 /* EOF */