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