xref: /reactos/ntoskrnl/fstub/disksup.c (revision 29615fee)
1 /*
2 * PROJECT:         ReactOS Kernel
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            ntoskrnl/fstub/disksup.c
5 * PURPOSE:         I/O HAL Routines for Disk Access
6 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7 *                  Eric Kohl
8 *                  Casper S. Hornstrup (chorns@users.sourceforge.net)
9 *                  Pierre Schweitzer
10 */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17 #include <internal/hal.h>
18 
19 const WCHAR DiskMountString[] = L"\\DosDevices\\%C:";
20 
21 #define AUTO_DRIVE         MAXULONG
22 
23 #define PARTITION_MAGIC    0xaa55
24 
25 #define EFI_PMBR_OSTYPE_EFI 0xEE
26 
27 #include <pshpack1.h>
28 
29 typedef struct _REG_DISK_MOUNT_INFO
30 {
31     ULONG Signature;
32     LARGE_INTEGER StartingOffset;
33 } REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO;
34 
35 #include <poppack.h>
36 
37 typedef enum _DISK_MANAGER
38 {
39     NoDiskManager,
40     OntrackDiskManager,
41     EZ_Drive
42 } DISK_MANAGER;
43 
44 typedef enum _PARTITION_TYPE
45 {
46     BootablePartition,
47     PrimaryPartition,
48     LogicalPartition,
49     FtPartition,
50     UnknownPartition,
51     DataPartition
52 } PARTITION_TYPE, *PPARTITION_TYPE;
53 
54 NTSTATUS
55 FASTCALL
HalpQueryDriveLayout(IN PUNICODE_STRING DeviceName,OUT PDRIVE_LAYOUT_INFORMATION * LayoutInfo)56 HalpQueryDriveLayout(IN PUNICODE_STRING DeviceName,
57                      OUT PDRIVE_LAYOUT_INFORMATION *LayoutInfo)
58 {
59     IO_STATUS_BLOCK StatusBlock;
60     PDEVICE_OBJECT DeviceObject = NULL;
61     PFILE_OBJECT FileObject;
62     KEVENT Event;
63     PIRP Irp;
64     NTSTATUS Status;
65     ULONG BufferSize;
66     PDRIVE_LAYOUT_INFORMATION Buffer;
67     PAGED_CODE();
68 
69     /* Get device pointers */
70     Status = IoGetDeviceObjectPointer(DeviceName,
71                                       FILE_READ_ATTRIBUTES,
72                                       &FileObject,
73                                       &DeviceObject);
74     if (!NT_SUCCESS(Status))
75     {
76         return Status;
77     }
78 
79     /* Get attached device object */
80     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
81     ObDereferenceObject(FileObject);
82 
83     /* Do not handle removable media */
84     if (BooleanFlagOn(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
85     {
86         ObDereferenceObject(DeviceObject);
87         return STATUS_NO_MEDIA;
88     }
89 
90     /* We'll loop until our buffer is big enough */
91     Buffer = NULL;
92     BufferSize = 0x1000;
93     KeInitializeEvent(&Event, NotificationEvent, FALSE);
94     do
95     {
96         /* If we already had a buffer, it means it's not big
97          * enough, so free and multiply size by two
98          */
99         if (Buffer != NULL)
100         {
101             ExFreePoolWithTag(Buffer, TAG_FSTUB);
102             BufferSize *= 2;
103         }
104 
105         /* Allocate buffer for output buffer */
106         Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_FSTUB);
107         if (Buffer == NULL)
108         {
109             Status = STATUS_NO_MEMORY;
110             break;
111         }
112 
113         /* Build the IRP to query drive layout */
114         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_LAYOUT,
115                                             DeviceObject,
116                                             NULL,
117                                             0,
118                                             Buffer,
119                                             BufferSize,
120                                             FALSE,
121                                             &Event,
122                                             &StatusBlock);
123         if (Irp == NULL)
124         {
125             Status = STATUS_INSUFFICIENT_RESOURCES;
126             break;
127         }
128 
129         /* Call the driver and wait if appropriate */
130         Status = IoCallDriver(DeviceObject, Irp);
131         if (Status == STATUS_PENDING)
132         {
133             KeWaitForSingleObject(&Event,
134                                   Executive,
135                                   KernelMode,
136                                   FALSE,
137                                   NULL);
138             Status = StatusBlock.Status;
139         }
140         /* If buffer is too small, keep looping */
141     } while (Status == STATUS_BUFFER_TOO_SMALL);
142 
143     /* We're done with the device */
144     ObDereferenceObject(DeviceObject);
145 
146     /* If querying worked, then return the buffer to the caller */
147     if (NT_SUCCESS(Status))
148     {
149         ASSERT(Buffer != NULL);
150         *LayoutInfo = Buffer;
151     }
152     /* Else, release the buffer if still allocated and fail */
153     else
154     {
155         if (Buffer != NULL)
156         {
157             ExFreePoolWithTag(Buffer, TAG_FSTUB);
158         }
159     }
160 
161     return Status;
162 }
163 
164 NTSTATUS
HalpQueryPartitionType(IN PUNICODE_STRING DeviceName,IN PDRIVE_LAYOUT_INFORMATION LayoutInfo,OUT PPARTITION_TYPE PartitionType)165 HalpQueryPartitionType(IN PUNICODE_STRING DeviceName,
166                        IN PDRIVE_LAYOUT_INFORMATION LayoutInfo,
167                        OUT PPARTITION_TYPE PartitionType)
168 {
169     USHORT i;
170     PIRP Irp;
171     KEVENT Event;
172     NTSTATUS Status;
173     PFILE_OBJECT FileObject;
174     PDEVICE_OBJECT DeviceObject;
175     IO_STATUS_BLOCK IoStatusBlock;
176     PARTITION_INFORMATION_EX PartitionInfo;
177 
178     PAGED_CODE();
179 
180     /* Get device pointers */
181     Status = IoGetDeviceObjectPointer(DeviceName,
182                                       FILE_READ_ATTRIBUTES,
183                                       &FileObject,
184                                       &DeviceObject);
185     if (!NT_SUCCESS(Status))
186     {
187         return Status;
188     }
189 
190     /* Get attached device object */
191     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
192     ObDereferenceObject(FileObject);
193 
194     /* Assume logical partition for removable devices */
195     if (BooleanFlagOn(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
196     {
197         ObDereferenceObject(DeviceObject);
198         *PartitionType = LogicalPartition;
199         return STATUS_SUCCESS;
200     }
201 
202     /* For the others, query partition info */
203     KeInitializeEvent(&Event, NotificationEvent, FALSE);
204     Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
205                                         DeviceObject,
206                                         NULL,
207                                         0,
208                                         &PartitionInfo,
209                                         sizeof(PartitionInfo),
210                                         FALSE,
211                                         &Event,
212                                         &IoStatusBlock);
213     if (Irp == NULL)
214     {
215         ObDereferenceObject(DeviceObject);
216         return STATUS_INSUFFICIENT_RESOURCES;
217     }
218 
219     Status = IoCallDriver(DeviceObject, Irp);
220     if (Status == STATUS_PENDING)
221     {
222         KeWaitForSingleObject(&Event,
223                               Executive,
224                               KernelMode,
225                               FALSE,
226                               NULL);
227         Status = IoStatusBlock.Status;
228     }
229 
230     /* We're done with the device */
231     ObDereferenceObject(DeviceObject);
232 
233     /* If we failed querying partition info, try to return something
234      * if caller didn't provide a precise layout, assume logical
235      * partition and fake success. Otherwise, just fail.
236      */
237     if (!NT_SUCCESS(Status))
238     {
239         if (LayoutInfo == NULL)
240         {
241             *PartitionType = LogicalPartition;
242             return STATUS_SUCCESS;
243         }
244 
245         return Status;
246     }
247 
248     /* First, handle non MBR style (easy cases) */
249     if (PartitionInfo.PartitionStyle != PARTITION_STYLE_MBR)
250     {
251         /* If not GPT, we don't know what it is */
252         if (PartitionInfo.PartitionStyle != PARTITION_STYLE_GPT)
253         {
254             *PartitionType = UnknownPartition;
255             return STATUS_SUCCESS;
256         }
257 
258         /* Check whether that's data partition */
259         if (RtlCompareMemory(&PartitionInfo.Gpt.PartitionType,
260                              &PARTITION_BASIC_DATA_GUID,
261                              sizeof(GUID)) == sizeof(GUID))
262         {
263             *PartitionType = DataPartition;
264             return STATUS_SUCCESS;
265         }
266 
267         /* Otherwise, we don't know */
268         *PartitionType = UnknownPartition;
269         return STATUS_SUCCESS;
270     }
271 
272     /* If we don't recognize partition type, return unknown */
273     if (!IsRecognizedPartition(PartitionInfo.Mbr.PartitionType))
274     {
275         *PartitionType = UnknownPartition;
276         return STATUS_SUCCESS;
277     }
278 
279     /* Check if that's a FT volume */
280     if (IsFTPartition(PartitionInfo.Mbr.PartitionType))
281     {
282         *PartitionType = FtPartition;
283         return STATUS_SUCCESS;
284     }
285 
286     /* If the caller didn't provide the complete layout, just return */
287     if (LayoutInfo == NULL)
288     {
289         *PartitionType = LogicalPartition;
290         return STATUS_SUCCESS;
291     }
292 
293     /* Now, evaluate the partition to the 4 in the input layout */
294     for (i = 0; i < 4; ++i)
295     {
296         /* If we find a partition matching */
297         if (LayoutInfo->PartitionEntry[i].StartingOffset.QuadPart == PartitionInfo.StartingOffset.QuadPart)
298         {
299             /* Return boot if boot flag is set */
300             if (PartitionInfo.Mbr.BootIndicator)
301             {
302                 *PartitionType = BootablePartition;
303             }
304             /* Primary otherwise */
305             else
306             {
307                 *PartitionType = PrimaryPartition;
308             }
309 
310             return STATUS_SUCCESS;
311         }
312     }
313 
314     /* Otherwise, assume logical */
315     *PartitionType = LogicalPartition;
316     return STATUS_SUCCESS;
317 }
318 
319 PULONG
IopComputeHarddiskDerangements(IN ULONG DiskCount)320 IopComputeHarddiskDerangements(IN ULONG DiskCount)
321 {
322     PIRP Irp;
323     KEVENT Event;
324     ULONG i, j, k;
325     PULONG Devices;
326     NTSTATUS Status;
327     WCHAR Buffer[100];
328     UNICODE_STRING ArcName;
329     PFILE_OBJECT FileObject;
330     PDEVICE_OBJECT DeviceObject;
331     IO_STATUS_BLOCK IoStatusBlock;
332     STORAGE_DEVICE_NUMBER DeviceNumber;
333 
334     /* No disks, nothing to do */
335     if (DiskCount == 0)
336     {
337         return NULL;
338     }
339 
340     /* Allocate a buffer big enough to hold all the disks */
341     Devices = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
342                                     sizeof(ULONG) * DiskCount,
343                                     TAG_FSTUB);
344     if (Devices == NULL)
345     {
346         return NULL;
347     }
348 
349     /* Now, we'll query all the disks */
350     for (i = 0; i < DiskCount; ++i)
351     {
352         /* Using their ARC name */
353         swprintf(Buffer, L"\\ArcName\\multi(0)disk(0)rdisk(%d)", i);
354         RtlInitUnicodeString(&ArcName, Buffer);
355         /* Get the attached DeviceObject */
356         if (NT_SUCCESS(IoGetDeviceObjectPointer(&ArcName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject)))
357         {
358             DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
359             ObDereferenceObject(FileObject);
360 
361             /* And query it for device number */
362             KeInitializeEvent(&Event, NotificationEvent, FALSE);
363             Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
364                                                 DeviceObject,
365                                                 NULL,
366                                                 0,
367                                                 &DeviceNumber,
368                                                 sizeof(DeviceNumber),
369                                                 FALSE,
370                                                 &Event,
371                                                 &IoStatusBlock);
372             if (Irp != NULL)
373             {
374                 Status = IoCallDriver(DeviceObject, Irp);
375                 if (Status == STATUS_PENDING)
376                 {
377                     KeWaitForSingleObject(&Event,
378                                           Executive,
379                                           KernelMode,
380                                           FALSE,
381                                           NULL);
382                     Status = IoStatusBlock.Status;
383                 }
384 
385                 ObDereferenceObject(DeviceObject);
386 
387                 /* In case of a success remember device number */
388                 if (NT_SUCCESS(Status))
389                 {
390                     Devices[i] = DeviceNumber.DeviceNumber;
391                     /* Move on, not to fall into our default case */
392                     continue;
393                 }
394             }
395             else
396             {
397                 ObDereferenceObject(DeviceObject);
398             }
399 
400             /* Default case, for failures, set -1 */
401             Devices[i] = -1;
402         }
403     }
404 
405     /* Now, we'll check all device numbers */
406     for (i = 0; i < DiskCount; ++i)
407     {
408         /* First of all, check if we're at the right place */
409         for (j = 0; j < DiskCount; ++j)
410         {
411             if (Devices[j] == i)
412             {
413                 break;
414             }
415         }
416 
417         /* If not, perform the change */
418         if (j >= DiskCount)
419         {
420             k = 0;
421             while (Devices[k] != -1)
422             {
423                 if (++k >= DiskCount)
424                 {
425                     break;
426                 }
427             }
428 
429             if (k < DiskCount)
430             {
431                 Devices[k] = i;
432             }
433         }
434     }
435 
436     /* Return our device derangement map */
437     return Devices;
438 }
439 
440 NTSTATUS
HalpNextMountLetter(IN PUNICODE_STRING DeviceName,OUT PUCHAR DriveLetter)441 HalpNextMountLetter(IN PUNICODE_STRING DeviceName,
442                     OUT PUCHAR DriveLetter)
443 {
444     PIRP Irp;
445     KEVENT Event;
446     NTSTATUS Status;
447     UNICODE_STRING MountMgr;
448     PFILE_OBJECT FileObject;
449     PDEVICE_OBJECT DeviceObject;
450     IO_STATUS_BLOCK IoStatusBlock;
451     PMOUNTMGR_DRIVE_LETTER_TARGET Target;
452     MOUNTMGR_DRIVE_LETTER_INFORMATION LetterInfo;
453 
454     /* To get next mount letter, we need the MountMgr */
455     RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
456     Status = IoGetDeviceObjectPointer(&MountMgr,
457                                       FILE_READ_ATTRIBUTES,
458                                       &FileObject,
459                                       &DeviceObject);
460     if (!NT_SUCCESS(Status))
461     {
462         return Status;
463     }
464 
465     /* Allocate our input buffer */
466     Target = ExAllocatePoolWithTag(PagedPool,
467                                    DeviceName->Length + FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName),
468                                    TAG_FSTUB);
469     if (Target == NULL)
470     {
471         ObDereferenceObject(FileObject);
472         return STATUS_INSUFFICIENT_RESOURCES;
473     }
474 
475     /* And fill it with the device hat needs a drive letter */
476     Target->DeviceNameLength = DeviceName->Length;
477     RtlCopyMemory(&Target->DeviceName[0], DeviceName->Buffer, DeviceName->Length);
478 
479     /* Call the mount manager */
480     KeInitializeEvent(&Event, NotificationEvent, FALSE);
481     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
482                                         DeviceObject,
483                                         Target,
484                                         DeviceName->Length + FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName),
485                                         &LetterInfo,
486                                         sizeof(LetterInfo),
487                                         FALSE,
488                                         &Event,
489                                         &IoStatusBlock);
490     if (Irp == NULL)
491     {
492         ExFreePoolWithTag(Target, TAG_FSTUB);
493         ObDereferenceObject(FileObject);
494         return STATUS_INSUFFICIENT_RESOURCES;
495     }
496 
497     Status = IoCallDriver(DeviceObject, Irp);
498     if (Status == STATUS_PENDING)
499     {
500         KeWaitForSingleObject(&Event,
501                               Executive,
502                               KernelMode,
503                               FALSE,
504                               NULL);
505         Status = IoStatusBlock.Status;
506     }
507 
508     ExFreePoolWithTag(Target, TAG_FSTUB);
509     ObDereferenceObject(FileObject);
510 
511     DPRINT("Done: %d %c\n", LetterInfo.DriveLetterWasAssigned,
512                             LetterInfo.CurrentDriveLetter);
513 
514     /* Return the drive letter the MountMgr potentially assigned */
515     *DriveLetter = LetterInfo.CurrentDriveLetter;
516 
517     /* Also return the success */
518     return Status;
519 }
520 
521 NTSTATUS
HalpSetMountLetter(IN PUNICODE_STRING DeviceName,UCHAR DriveLetter)522 HalpSetMountLetter(IN PUNICODE_STRING DeviceName,
523                    UCHAR DriveLetter)
524 {
525     PIRP Irp;
526     KEVENT Event;
527     NTSTATUS Status;
528     WCHAR Buffer[30];
529     ULONG InputBufferLength;
530     PFILE_OBJECT FileObject;
531     PDEVICE_OBJECT DeviceObject;
532     IO_STATUS_BLOCK IoStatusBlock;
533     UNICODE_STRING DosDevice, MountMgr;
534     PMOUNTMGR_CREATE_POINT_INPUT InputBuffer;
535 
536     /* Setup the DosDevice name */
537     swprintf(Buffer, L"\\DosDevices\\%c:", DriveLetter);
538     RtlInitUnicodeString(&DosDevice, Buffer);
539 
540     /* Allocate the input buffer for the MountMgr */
541     InputBufferLength = DosDevice.Length + DeviceName->Length + sizeof(MOUNTMGR_CREATE_POINT_INPUT);
542     InputBuffer = ExAllocatePoolWithTag(PagedPool, InputBufferLength, TAG_FSTUB);
543     if (InputBuffer == NULL)
544     {
545         return STATUS_INSUFFICIENT_RESOURCES;
546     }
547 
548     /* Fill the input buffer */
549     InputBuffer->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
550     InputBuffer->SymbolicLinkNameLength = DosDevice.Length;
551     InputBuffer->DeviceNameOffset = DosDevice.Length + sizeof(MOUNTMGR_CREATE_POINT_INPUT);
552     InputBuffer->DeviceNameLength = DeviceName->Length;
553     RtlCopyMemory(&InputBuffer[1], DosDevice.Buffer, DosDevice.Length);
554     RtlCopyMemory((PVOID)((ULONG_PTR)InputBuffer + InputBuffer->DeviceNameOffset),
555                   DeviceName->Buffer,
556                   DeviceName->Length);
557 
558     /* Get the MountMgr device pointer, to send the IOCTL */
559     RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
560     Status = IoGetDeviceObjectPointer(&MountMgr,
561                                       FILE_READ_ATTRIBUTES,
562                                       &FileObject,
563                                       &DeviceObject);
564     if (!NT_SUCCESS(Status))
565     {
566         ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
567         return Status;
568     }
569 
570     /* Call the MountMgr */
571     KeInitializeEvent(&Event, NotificationEvent, FALSE);
572     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CREATE_POINT,
573                                         DeviceObject,
574                                         InputBuffer,
575                                         InputBufferLength,
576                                         NULL,
577                                         0,
578                                         FALSE,
579                                         &Event,
580                                         &IoStatusBlock);
581     if (Irp == NULL)
582     {
583         ObDereferenceObject(FileObject);
584         ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
585         return STATUS_INSUFFICIENT_RESOURCES;
586     }
587 
588     Status = IoCallDriver(DeviceObject, Irp);
589     if (Status == STATUS_PENDING)
590     {
591         KeWaitForSingleObject(&Event,
592                               Executive,
593                               KernelMode,
594                               FALSE,
595                               NULL);
596         Status = IoStatusBlock.Status;
597     }
598 
599     ObDereferenceObject(FileObject);
600     ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
601 
602     /* Return the MountMgr status */
603     return Status;
604 }
605 
606 UCHAR
HalpNextDriveLetter(IN PUNICODE_STRING DeviceName,IN PSTRING NtDeviceName,OUT PUCHAR NtSystemPath,BOOLEAN IsRemovable)607 HalpNextDriveLetter(IN PUNICODE_STRING DeviceName,
608                     IN PSTRING NtDeviceName,
609                     OUT PUCHAR NtSystemPath,
610                     BOOLEAN IsRemovable)
611 {
612     UCHAR i;
613     WCHAR Buffer[40];
614     UCHAR DriveLetter;
615     UNICODE_STRING FloppyString, CdString, NtDeviceNameU, DosDevice;
616 
617     /* Quick path, ask directly the mount manager to assign the next
618      * free drive letter
619      */
620     if (NT_SUCCESS(HalpNextMountLetter(DeviceName, &DriveLetter)))
621     {
622         return DriveLetter;
623     }
624 
625     /* We'll allow MountMgr to fail only for non vital path */
626     if (NtDeviceName == NULL || NtSystemPath == NULL)
627     {
628         return -1;
629     }
630 
631     /* And for removable devices */
632     if (!IsRemovable)
633     {
634         return 0;
635     }
636 
637     /* Removable might be floppy or cdrom */
638     RtlInitUnicodeString(&FloppyString, L"\\Device\\Floppy");
639     RtlInitUnicodeString(&CdString, L"\\Device\\CdRom");
640 
641     /* If floppy, start at A */
642     if (RtlPrefixUnicodeString(&FloppyString, DeviceName, TRUE))
643     {
644         DriveLetter = 'A';
645     }
646     /* If CD start C */
647     else if (RtlPrefixUnicodeString(&CdString, DeviceName, TRUE))
648     {
649         DriveLetter = 'D';
650     }
651     /* For the rest start at C */
652     else
653     {
654         DriveLetter = 'C';
655     }
656 
657     /* Now, try to assign a drive letter manually with the MountMgr */
658     for (i = DriveLetter; i <= 'Z'; ++i)
659     {
660         if (NT_SUCCESS(HalpSetMountLetter(DeviceName, i)))
661         {
662             /* If it worked, if we were managing system path, update manually */
663             if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&NtDeviceNameU, NtDeviceName, TRUE)))
664             {
665                 if (RtlEqualUnicodeString(&NtDeviceNameU, DeviceName, TRUE))
666                 {
667                     *NtSystemPath = i;
668                 }
669 
670                 RtlFreeUnicodeString(&NtDeviceNameU);
671             }
672 
673             return i;
674         }
675     }
676 
677     /* Last fall back, we're not on a PnP device... */
678     for (i = DriveLetter; i <= 'Z'; ++i)
679     {
680         /* We'll link manually, without MountMgr knowing anything about the device */
681         swprintf(Buffer, L"\\DosDevices\\%c:", i);
682         RtlInitUnicodeString(&DosDevice, Buffer);
683 
684         /* If linking worked, then the letter was free ;-) */
685         if (NT_SUCCESS(IoCreateSymbolicLink(&DosDevice, DeviceName)))
686         {
687             /* If it worked, if we were managing system path, update manually */
688             if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&NtDeviceNameU, NtDeviceName, TRUE)))
689             {
690                 if (RtlEqualUnicodeString(&NtDeviceNameU, DeviceName, TRUE))
691                 {
692                     *NtSystemPath = i;
693                 }
694 
695                 RtlFreeUnicodeString(&NtDeviceNameU);
696             }
697 
698             return i;
699         }
700     }
701 
702     /* We're done, nothing happened */
703     return 0;
704 }
705 
706 BOOLEAN
HalpIsOldStyleFloppy(PUNICODE_STRING DeviceName)707 HalpIsOldStyleFloppy(PUNICODE_STRING DeviceName)
708 {
709     PIRP Irp;
710     KEVENT Event;
711     NTSTATUS Status;
712     MOUNTDEV_NAME DevName;
713     PFILE_OBJECT FileObject;
714     PDEVICE_OBJECT DeviceObject;
715     IO_STATUS_BLOCK IoStatusBlock;
716     PAGED_CODE();
717 
718     /* Get the attached device object to our device */
719     if (!NT_SUCCESS(IoGetDeviceObjectPointer(DeviceName,
720                                              FILE_READ_ATTRIBUTES,
721                                              &FileObject,
722                                              &DeviceObject)))
723     {
724         return FALSE;
725     }
726 
727     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
728     ObDereferenceObject(FileObject);
729 
730     /* Query its device name (ie, check floppy.sys implements MountMgr interface) */
731     KeInitializeEvent(&Event, NotificationEvent, FALSE);
732     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
733                                         DeviceObject,
734                                         NULL,
735                                         0,
736                                         &DevName,
737                                         sizeof(DevName),
738                                         FALSE,
739                                         &Event,
740                                         &IoStatusBlock);
741     if (Irp == NULL)
742     {
743         ObDereferenceObject(DeviceObject);
744         return FALSE;
745     }
746 
747     Status = IoCallDriver(DeviceObject, Irp);
748     if (Status == STATUS_PENDING)
749     {
750         KeWaitForSingleObject(&Event,
751                               Executive,
752                               KernelMode,
753                               FALSE,
754                               NULL);
755         Status = IoStatusBlock.Status;
756     }
757 
758     /* If status is not STATUS_BUFFER_OVERFLOW, it means
759      * it's pre-mountmgr driver, aka "Old style".
760      */
761     ObDereferenceObject(DeviceObject);
762     return (Status != STATUS_BUFFER_OVERFLOW);
763 }
764 
765 NTSTATUS
HalpDeleteMountLetter(UCHAR DriveLetter)766 HalpDeleteMountLetter(UCHAR DriveLetter)
767 {
768     PIRP Irp;
769     KEVENT Event;
770     NTSTATUS Status;
771     WCHAR Buffer[30];
772     ULONG InputBufferLength;
773     PFILE_OBJECT FileObject;
774     PDEVICE_OBJECT DeviceObject;
775     IO_STATUS_BLOCK IoStatusBlock;
776     PMOUNTMGR_MOUNT_POINT InputBuffer;
777     UNICODE_STRING DosDevice, MountMgr;
778     PMOUNTMGR_MOUNT_POINTS OutputBuffer;
779 
780     /* Setup the device name of the letter to delete */
781     swprintf(Buffer, L"\\DosDevices\\%c:", DriveLetter);
782     RtlInitUnicodeString(&DosDevice, Buffer);
783 
784     /* Allocate the input buffer for MountMgr */
785     InputBufferLength = DosDevice.Length + sizeof(MOUNTMGR_MOUNT_POINT);
786     InputBuffer = ExAllocatePoolWithTag(PagedPool, InputBufferLength, TAG_FSTUB);
787     if (InputBuffer == NULL)
788     {
789         return STATUS_INSUFFICIENT_RESOURCES;
790     }
791 
792     /* Fill it in */
793     RtlZeroMemory(InputBuffer, InputBufferLength);
794     InputBuffer->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
795     InputBuffer->SymbolicLinkNameLength = DosDevice.Length;
796     RtlCopyMemory(&InputBuffer[1], DosDevice.Buffer, DosDevice.Length);
797 
798     /* Allocate big enough output buffer (we don't care about the output) */
799     OutputBuffer = ExAllocatePoolWithTag(PagedPool, 0x1000, TAG_FSTUB);
800     if (OutputBuffer == NULL)
801     {
802         ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
803         return STATUS_INSUFFICIENT_RESOURCES;
804     }
805 
806     /* Get the device pointer to the MountMgr */
807     RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
808     Status = IoGetDeviceObjectPointer(&MountMgr,
809                                       FILE_READ_ATTRIBUTES,
810                                       &FileObject,
811                                       &DeviceObject);
812     if (!NT_SUCCESS(Status))
813     {
814         ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
815         ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
816         return Status;
817     }
818 
819     /* Call the mount manager to delete the drive letter */
820     KeInitializeEvent(&Event, NotificationEvent, FALSE);
821     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
822                                         DeviceObject,
823                                         InputBuffer,
824                                         InputBufferLength,
825                                         OutputBuffer,
826                                         0x1000,
827                                         FALSE,
828                                         &Event,
829                                         &IoStatusBlock);
830     if (Irp == NULL)
831     {
832         ObDereferenceObject(FileObject);
833         ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
834         ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
835         return STATUS_INSUFFICIENT_RESOURCES;
836     }
837 
838     Status = IoCallDriver(DeviceObject, Irp);
839     if (Status == STATUS_PENDING)
840     {
841         KeWaitForSingleObject(&Event,
842                               Executive,
843                               KernelMode,
844                               FALSE,
845                               NULL);
846         Status = IoStatusBlock.Status;
847     }
848 
849     ObDereferenceObject(FileObject);
850     ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
851     ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
852 
853     return Status;
854 }
855 
856 VOID
HalpEnableAutomaticDriveLetterAssignment(VOID)857 HalpEnableAutomaticDriveLetterAssignment(VOID)
858 {
859     PIRP Irp;
860     KEVENT Event;
861     NTSTATUS Status;
862     UNICODE_STRING MountMgr;
863     PFILE_OBJECT FileObject;
864     PDEVICE_OBJECT DeviceObject;
865     IO_STATUS_BLOCK IoStatusBlock;
866 
867     /* Get the device pointer to the MountMgr */
868     RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
869     Status = IoGetDeviceObjectPointer(&MountMgr,
870                                       FILE_READ_ATTRIBUTES,
871                                       &FileObject,
872                                       &DeviceObject);
873     if (!NT_SUCCESS(Status))
874     {
875         return;
876     }
877 
878     /* Just send an IOCTL to enable the feature */
879     KeInitializeEvent(&Event, NotificationEvent, FALSE);
880     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS,
881                                         DeviceObject,
882                                         NULL,
883                                         0,
884                                         NULL,
885                                         0,
886                                         FALSE,
887                                         &Event,
888                                         &IoStatusBlock);
889     if (Irp == NULL)
890     {
891         return;
892     }
893 
894     Status = IoCallDriver(DeviceObject, Irp);
895     if (Status == STATUS_PENDING)
896     {
897         KeWaitForSingleObject(&Event,
898                               Executive,
899                               KernelMode,
900                               FALSE,
901                               NULL);
902         Status = IoStatusBlock.Status;
903     }
904 
905     ObDereferenceObject(FileObject);
906 
907     return;
908 }
909 
910 VOID
911 FASTCALL
xHalIoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,IN PSTRING NtDeviceName,OUT PUCHAR NtSystemPath,OUT PSTRING NtSystemPathString)912 xHalIoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
913                          IN PSTRING NtDeviceName,
914                          OUT PUCHAR NtSystemPath,
915                          OUT PSTRING NtSystemPathString)
916 {
917     USHORT i;
918     PULONG Devices;
919     NTSTATUS Status;
920     WCHAR Buffer[50];
921     HANDLE FileHandle;
922     UCHAR DriveLetter;
923     BOOLEAN SystemFound;
924     IO_STATUS_BLOCK StatusBlock;
925     PARTITION_TYPE PartitionType;
926     ANSI_STRING StringA1, StringA2;
927     PSTR Buffer1, Buffer2, LoadOptions;
928     OBJECT_ATTRIBUTES ObjectAttributes;
929     PDRIVE_LAYOUT_INFORMATION LayoutInfo;
930     PCONFIGURATION_INFORMATION ConfigInfo;
931     UNICODE_STRING StringU1, StringU2, StringU3;
932     ULONG Increment, DiskCount, RealDiskCount, HarddiskCount, PartitionCount, SystemPartition;
933 
934     PAGED_CODE();
935 
936     /* Get our disk count */
937     ConfigInfo = IoGetConfigurationInformation();
938     DiskCount = ConfigInfo->DiskCount;
939     RealDiskCount = 0;
940 
941     /* Allocate two generic string buffers we'll use and reuser later on */
942     Buffer1 = ExAllocatePoolWithTag(NonPagedPool, 128, TAG_FSTUB);
943     Buffer2 = ExAllocatePoolWithTag(NonPagedPool, 64, TAG_FSTUB);
944     if (Buffer1 == NULL || Buffer2 == NULL)
945     {
946         KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
947     }
948 
949     /* In case of a remote boot, setup system path */
950     if (IoRemoteBootClient)
951     {
952         PSTR Last, Saved;
953 
954         /* Find last \ */
955         Last = strrchr(LoaderBlock->NtBootPathName, '\\');
956         Saved = NULL;
957         /* Misformed name, fail */
958         if (Last == NULL)
959         {
960             KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
961         }
962 
963         /* In case the name was terminated by a \... */
964         if (Last[1] == ANSI_NULL)
965         {
966             /* Erase it, save position and find the previous \ */
967             *Last = ANSI_NULL;
968             Saved = Last;
969             Last = strrchr(LoaderBlock->NtBootPathName, '\\');
970             *Saved = '\\';
971         }
972 
973         /* Misformed name, fail */
974         if (Last == NULL)
975         {
976             KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
977         }
978 
979         /* For a remote boot, assign X drive letter */
980         NtSystemPath[0] = 'X';
981         NtSystemPath[1] = ':';
982         /* And copy the end of the boot path */
983         strcpy((PSTR)&NtSystemPath[2], Last);
984 
985         /* If we had to remove the trailing \, remove it here too */
986         if (Saved != NULL)
987         {
988             NtSystemPath[strlen((PSTR)NtSystemPath) - 1] = ANSI_NULL;
989         }
990 
991         /* Setup output string */
992         RtlInitString(NtSystemPathString, (PSTR)NtSystemPath);
993     }
994 
995     /* For each of our disks, create the physical device DOS device */
996     Increment = 0;
997     if (DiskCount != 0)
998     {
999         for (i = 0; i < DiskCount; ++i)
1000         {
1001             /* Setup the origin name */
1002             sprintf(Buffer1, "\\Device\\Harddisk%d\\Partition%d", i, 0);
1003             RtlInitAnsiString(&StringA1, Buffer1);
1004             if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, &StringA1, TRUE)))
1005             {
1006                 /* We cannot fail */
1007                 KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
1008             }
1009 
1010             /* Open the device */
1011             InitializeObjectAttributes(&ObjectAttributes,
1012                                        &StringU1,
1013                                        OBJ_CASE_INSENSITIVE,
1014                                        NULL,
1015                                        NULL);
1016             Status = ZwOpenFile(&FileHandle,
1017                                 SYNCHRONIZE | FILE_READ_DATA,
1018                                 &ObjectAttributes,
1019                                 &StatusBlock,
1020                                 FILE_SHARE_READ,
1021                                 FILE_SYNCHRONOUS_IO_NONALERT);
1022             if (NT_SUCCESS(Status))
1023             {
1024                 /* If we managed, create the link */
1025                 sprintf(Buffer2, "\\DosDevices\\PhysicalDrive%d", i);
1026                 RtlInitAnsiString(&StringA2, Buffer2);
1027                 Status = RtlAnsiStringToUnicodeString(&StringU2, &StringA2, TRUE);
1028                 if (NT_SUCCESS(Status))
1029                 {
1030                     IoCreateSymbolicLink(&StringU2, &StringU1);
1031                     RtlFreeUnicodeString(&StringU2);
1032                 }
1033 
1034                 ZwClose(FileHandle);
1035 
1036                 RealDiskCount = i + 1;
1037             }
1038 
1039             RtlFreeUnicodeString(&StringU1);
1040 
1041             if (!NT_SUCCESS(Status))
1042             {
1043                 if (Increment < 50)
1044                 {
1045                     ++Increment;
1046                     ++DiskCount;
1047                 }
1048             }
1049         }
1050     }
1051 
1052     /* We done for our buffers */
1053     ExFreePoolWithTag(Buffer1, TAG_FSTUB);
1054     ExFreePoolWithTag(Buffer2, TAG_FSTUB);
1055 
1056     /* Upcase our load options, if any */
1057     if (LoaderBlock->LoadOptions != NULL)
1058     {
1059         LoadOptions = _strupr(LoaderBlock->LoadOptions);
1060     }
1061     else
1062     {
1063         LoadOptions = NULL;
1064     }
1065 
1066     /* If we boot with /MININT (system hive as volatile) option, assign X letter to boot device */
1067     if (LoadOptions != NULL &&
1068         strstr(LoadOptions, "MININT") != 0 &&
1069         NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, NtDeviceName, TRUE)))
1070     {
1071         if (NT_SUCCESS(HalpSetMountLetter(&StringU1, 'X')))
1072         {
1073             *NtSystemPath = 'X';
1074         }
1075 
1076         RtlFreeUnicodeString(&StringU1);
1077     }
1078 
1079     /* Compute our disks derangements */
1080     DiskCount -= Increment;
1081     if (RealDiskCount > DiskCount)
1082     {
1083         DiskCount = RealDiskCount;
1084     }
1085     Devices = IopComputeHarddiskDerangements(DiskCount);
1086 
1087     /* Now, start browsing all our disks for assigning drive letters
1088      * Here, we'll only handle boot partition and primary partitions
1089      */
1090     HarddiskCount = 0;
1091     for (i = 0; i < DiskCount; ++i)
1092     {
1093         /* Get device ID according to derangements map */
1094         if (Devices != NULL)
1095         {
1096             HarddiskCount = Devices[i];
1097         }
1098 
1099         /* Query disk layout */
1100         swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
1101         RtlInitUnicodeString(&StringU1, Buffer);
1102         if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
1103         {
1104             LayoutInfo = NULL;
1105         }
1106 
1107         /* Assume we didn't find system */
1108         SystemFound = FALSE;
1109         swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, 1);
1110         RtlInitUnicodeString(&StringU1, Buffer);
1111         /* Query partition info for our disk */
1112         if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1113         {
1114             /* It failed, retry for all the partitions */
1115             for (PartitionCount = 1; ; ++PartitionCount)
1116             {
1117                 swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1118                 RtlInitUnicodeString(&StringU1, Buffer);
1119                 if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1120                 {
1121                     break;
1122                 }
1123 
1124                 /* We found a primary partition, assign a drive letter */
1125                 if (PartitionType == PrimaryPartition)
1126                 {
1127                     HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
1128                     break;
1129                 }
1130             }
1131         }
1132         else
1133         {
1134             /* All right */
1135             for (PartitionCount = 2; ; ++PartitionCount)
1136             {
1137                 /* If our partition is bootable (MBR) or data (GPT), that's system partition */
1138                 if (PartitionType == BootablePartition || PartitionType == DataPartition)
1139                 {
1140                     SystemFound = TRUE;
1141 
1142                     /* Assign a drive letter and stop here if MBR */
1143                     HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
1144                     if (PartitionType == BootablePartition)
1145                     {
1146                         break;
1147                     }
1148                 }
1149 
1150                 /* Keep looping on all the partitions */
1151                 swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1152                 RtlInitUnicodeString(&StringU1, Buffer);
1153                 if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1154                 {
1155                     /* Mount every primary partition if we didn't find system */
1156                     if (!SystemFound)
1157                     {
1158                         for (PartitionCount = 1; ; ++PartitionCount)
1159                         {
1160                             swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1161                             RtlInitUnicodeString(&StringU1, Buffer);
1162                             if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1163                             {
1164                                 break;
1165                             }
1166 
1167                             if (PartitionType == PrimaryPartition)
1168                             {
1169                                 HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
1170                                 break;
1171                             }
1172                         }
1173                     }
1174 
1175                     break;
1176                 }
1177             }
1178         }
1179 
1180         /* Free layout, we'll reallocate it for next device */
1181         if (LayoutInfo != NULL)
1182         {
1183             ExFreePoolWithTag(LayoutInfo, TAG_FSTUB);
1184         }
1185 
1186         HarddiskCount = i + 1;
1187     }
1188 
1189     /* Now, assign logical partitions */
1190     for (i = 0; i < DiskCount; ++i)
1191     {
1192         /* Get device ID according to derangements map */
1193         if (Devices != NULL)
1194         {
1195             HarddiskCount = Devices[i];
1196         }
1197         else
1198         {
1199             HarddiskCount = i;
1200         }
1201 
1202         /* Query device layout */
1203         swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
1204         RtlInitUnicodeString(&StringU1, Buffer);
1205         if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
1206         {
1207             LayoutInfo = NULL;
1208         }
1209 
1210         /* And assign drive letter to logical partitions */
1211         for (PartitionCount = 1; ; ++PartitionCount)
1212         {
1213             swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1214             RtlInitUnicodeString(&StringU1, Buffer);
1215             if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1216             {
1217                 break;
1218             }
1219 
1220             if (PartitionType == LogicalPartition)
1221             {
1222                 HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
1223             }
1224         }
1225 
1226         /* Free layout, we'll reallocate it for next device */
1227         if (LayoutInfo != NULL)
1228         {
1229             ExFreePoolWithTag(LayoutInfo, 0);
1230         }
1231     }
1232 
1233     /* Now, assign drive letters to everything else */
1234     for (i = 0; i < DiskCount; ++i)
1235     {
1236         /* Get device ID according to derangements map */
1237         if (Devices != NULL)
1238         {
1239             HarddiskCount = Devices[i];
1240         }
1241         else
1242         {
1243             HarddiskCount = i;
1244         }
1245 
1246         /* Query device layout */
1247         swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
1248         RtlInitUnicodeString(&StringU1, Buffer);
1249         if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
1250         {
1251             LayoutInfo = NULL;
1252         }
1253 
1254         /* Save system partition if any */
1255         SystemPartition = 0;
1256         for (PartitionCount = 1; ; ++PartitionCount)
1257         {
1258             swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1259             RtlInitUnicodeString(&StringU1, Buffer);
1260             if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1261             {
1262                 break;
1263             }
1264 
1265             if ((PartitionType == BootablePartition || PartitionType == PrimaryPartition) && (SystemPartition == 0))
1266             {
1267                 SystemPartition = PartitionCount;
1268             }
1269         }
1270 
1271         /* And assign drive letter to anything but system partition */
1272         for (PartitionCount = 1; ; ++PartitionCount)
1273         {
1274             if (PartitionCount != SystemPartition)
1275             {
1276                 swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
1277                 RtlInitUnicodeString(&StringU1, Buffer);
1278                 if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
1279                 {
1280                     if (LayoutInfo != NULL)
1281                     {
1282                         ExFreePoolWithTag(LayoutInfo, 0);
1283                     }
1284 
1285                     break;
1286                 }
1287 
1288                 if (PartitionType == PrimaryPartition || PartitionType == FtPartition)
1289                 {
1290                     HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
1291                 }
1292             }
1293         }
1294     }
1295 
1296     /* We're done with disks, if we have a device map, free it */
1297     if (Devices != NULL)
1298     {
1299         ExFreePoolWithTag(Devices, TAG_FSTUB);
1300     }
1301 
1302     /* Now, assign drive letter to floppy drives */
1303     for (i = 0; i < ConfigInfo->FloppyCount; ++i)
1304     {
1305         swprintf(Buffer, L"\\Device\\Floppy%d", i);
1306         RtlInitUnicodeString(&StringU1, Buffer);
1307         if (HalpIsOldStyleFloppy(&StringU1))
1308         {
1309             HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, TRUE);
1310         }
1311     }
1312 
1313     /* And CD drives */
1314     for (i = 0; i < ConfigInfo->CdRomCount; ++i)
1315     {
1316         swprintf(Buffer, L"\\Device\\CdRom%d", i);
1317         RtlInitUnicodeString(&StringU1, Buffer);
1318         HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, TRUE);
1319     }
1320 
1321     /* If not remote boot, handle NtDeviceName */
1322     if (!IoRemoteBootClient && NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, NtDeviceName, TRUE)))
1323     {
1324         /* Assign it a drive letter */
1325         DriveLetter = HalpNextDriveLetter(&StringU1, NULL, NULL, TRUE);
1326         if (DriveLetter != 0)
1327         {
1328             if (DriveLetter != 0xFF)
1329             {
1330                 *NtSystemPath = DriveLetter;
1331             }
1332         }
1333         /* If it fails through mount manager, retry manually */
1334         else
1335         {
1336             RtlInitUnicodeString(&StringU2, L"\\Device\\Floppy");
1337             RtlInitUnicodeString(&StringU3, L"\\Device\\CdRom");
1338 
1339             if (RtlPrefixUnicodeString(&StringU2, &StringU1, TRUE))
1340             {
1341                 DriveLetter = 'A';
1342             }
1343             else if (RtlPrefixUnicodeString(&StringU3, &StringU1, TRUE))
1344             {
1345                 DriveLetter = 'D';
1346             }
1347             else
1348             {
1349                 DriveLetter = 'C';
1350             }
1351 
1352             /* Try any drive letter */
1353             while (HalpSetMountLetter(&StringU1, DriveLetter) != STATUS_SUCCESS)
1354             {
1355                 ++DriveLetter;
1356 
1357                 if (DriveLetter > 'Z')
1358                 {
1359                     break;
1360                 }
1361             }
1362 
1363             /* If we're beyond Z (ie, no slot left) */
1364             if (DriveLetter > 'Z')
1365             {
1366                 /* Delete Z, and reuse it for system */
1367                 HalpDeleteMountLetter('Z');
1368                 HalpSetMountLetter(&StringU1, 'Z');
1369                 *NtSystemPath = 'Z';
1370             }
1371             else
1372             {
1373                 /* Return matching drive letter */
1374                 *NtSystemPath = DriveLetter;
1375             }
1376         }
1377 
1378         RtlFreeUnicodeString(&StringU1);
1379     }
1380 
1381     /* Enable auto assignement for mountmgr */
1382     HalpEnableAutomaticDriveLetterAssignment();
1383 }
1384 
1385 /* PRIVATE FUNCTIONS *********************************************************/
1386 
1387 NTSTATUS
1388 NTAPI
HalpGetFullGeometry(IN PDEVICE_OBJECT DeviceObject,OUT PDISK_GEOMETRY_EX Geometry)1389 HalpGetFullGeometry(IN PDEVICE_OBJECT DeviceObject,
1390                     OUT PDISK_GEOMETRY_EX Geometry)
1391 {
1392     PIRP Irp;
1393     IO_STATUS_BLOCK IoStatusBlock;
1394     PKEVENT Event;
1395     NTSTATUS Status;
1396 
1397     PAGED_CODE();
1398 
1399     /* Allocate a non-paged event */
1400     Event = ExAllocatePoolWithTag(NonPagedPool,
1401                                   sizeof(KEVENT),
1402                                   TAG_FILE_SYSTEM);
1403     if (!Event) return STATUS_INSUFFICIENT_RESOURCES;
1404 
1405     /* Initialize it */
1406     KeInitializeEvent(Event, NotificationEvent, FALSE);
1407 
1408     /* Build the IRP */
1409     Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
1410                                         DeviceObject,
1411                                         NULL,
1412                                         0UL,
1413                                         Geometry,
1414                                         sizeof(DISK_GEOMETRY_EX),
1415                                         FALSE,
1416                                         Event,
1417                                         &IoStatusBlock);
1418     if (!Irp)
1419     {
1420         /* Fail, free the event */
1421         ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
1422         return STATUS_INSUFFICIENT_RESOURCES;
1423     }
1424 
1425     /* Call the driver and check if it's pending */
1426     Status = IoCallDriver(DeviceObject, Irp);
1427     if (Status == STATUS_PENDING)
1428     {
1429         /* Wait on the driver */
1430         KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
1431         Status = IoStatusBlock.Status;
1432     }
1433 
1434     /* Free the event and return the Status */
1435     ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
1436     return Status;
1437 }
1438 
1439 BOOLEAN
1440 NTAPI
HalpIsValidPartitionEntry(IN PPARTITION_DESCRIPTOR Entry,IN ULONGLONG MaxOffset,IN ULONGLONG MaxSector)1441 HalpIsValidPartitionEntry(IN PPARTITION_DESCRIPTOR Entry,
1442                           IN ULONGLONG MaxOffset,
1443                           IN ULONGLONG MaxSector)
1444 {
1445     ULONGLONG EndingSector;
1446     PAGED_CODE();
1447 
1448     /* Unused partitions are considered valid */
1449     if (Entry->PartitionType == PARTITION_ENTRY_UNUSED) return TRUE;
1450 
1451     /* Get the last sector of the partition */
1452     EndingSector = GET_STARTING_SECTOR(Entry) + GET_PARTITION_LENGTH(Entry);
1453 
1454     /* Check if it's more then the maximum sector */
1455     if (EndingSector > MaxSector)
1456     {
1457         /* Invalid partition */
1458         DPRINT1("FSTUB: entry is invalid\n");
1459         DPRINT1("FSTUB: offset %#08lx\n", GET_STARTING_SECTOR(Entry));
1460         DPRINT1("FSTUB: length %#08lx\n", GET_PARTITION_LENGTH(Entry));
1461         DPRINT1("FSTUB: end %#I64x\n", EndingSector);
1462         DPRINT1("FSTUB: max %#I64x\n", MaxSector);
1463         return FALSE;
1464     }
1465     else if (GET_STARTING_SECTOR(Entry) > MaxOffset)
1466     {
1467         /* Invalid partition */
1468         DPRINT1("FSTUB: entry is invalid\n");
1469         DPRINT1("FSTUB: offset %#08lx\n", GET_STARTING_SECTOR(Entry));
1470         DPRINT1("FSTUB: length %#08lx\n", GET_PARTITION_LENGTH(Entry));
1471         DPRINT1("FSTUB: end %#I64x\n", EndingSector);
1472         DPRINT1("FSTUB: maxOffset %#I64x\n", MaxOffset);
1473         return FALSE;
1474     }
1475 
1476     /* It's fine, return success */
1477     return TRUE;
1478 }
1479 
1480 VOID
1481 NTAPI
HalpCalculateChsValues(IN PLARGE_INTEGER PartitionOffset,IN PLARGE_INTEGER PartitionLength,IN CCHAR ShiftCount,IN ULONG SectorsPerTrack,IN ULONG NumberOfTracks,IN ULONG ConventionalCylinders,OUT PPARTITION_DESCRIPTOR PartitionDescriptor)1482 HalpCalculateChsValues(IN PLARGE_INTEGER PartitionOffset,
1483                        IN PLARGE_INTEGER PartitionLength,
1484                        IN CCHAR ShiftCount,
1485                        IN ULONG SectorsPerTrack,
1486                        IN ULONG NumberOfTracks,
1487                        IN ULONG ConventionalCylinders,
1488                        OUT PPARTITION_DESCRIPTOR PartitionDescriptor)
1489 {
1490     LARGE_INTEGER FirstSector, SectorCount;
1491     ULONG LastSector, Remainder, SectorsPerCylinder;
1492     ULONG StartingCylinder, EndingCylinder;
1493     ULONG StartingTrack, EndingTrack;
1494     ULONG StartingSector, EndingSector;
1495     PAGED_CODE();
1496 
1497     /* Calculate the number of sectors for each cylinder */
1498     SectorsPerCylinder = SectorsPerTrack * NumberOfTracks;
1499 
1500     /* Calculate the first sector, and the sector count */
1501     FirstSector.QuadPart = PartitionOffset->QuadPart >> ShiftCount;
1502     SectorCount.QuadPart = PartitionLength->QuadPart >> ShiftCount;
1503 
1504     /* Now calculate the last sector */
1505     LastSector = FirstSector.LowPart + SectorCount.LowPart - 1;
1506 
1507     /* Calculate the first and last cylinders */
1508     StartingCylinder = FirstSector.LowPart / SectorsPerCylinder;
1509     EndingCylinder = LastSector / SectorsPerCylinder;
1510 
1511     /* Set the default number of cylinders */
1512     if (!ConventionalCylinders) ConventionalCylinders = 1024;
1513 
1514     /* Normalize the values */
1515     if (StartingCylinder >= ConventionalCylinders)
1516     {
1517         /* Set the maximum to 1023 */
1518         StartingCylinder = ConventionalCylinders - 1;
1519     }
1520     if (EndingCylinder >= ConventionalCylinders)
1521     {
1522         /* Set the maximum to 1023 */
1523         EndingCylinder = ConventionalCylinders - 1;
1524     }
1525 
1526     /* Calculate the starting head and sector that still remain */
1527     Remainder = FirstSector.LowPart % SectorsPerCylinder;
1528     StartingTrack = Remainder / SectorsPerTrack;
1529     StartingSector = Remainder % SectorsPerTrack;
1530 
1531     /* Calculate the ending head and sector that still remain */
1532     Remainder = LastSector % SectorsPerCylinder;
1533     EndingTrack = Remainder / SectorsPerTrack;
1534     EndingSector = Remainder % SectorsPerTrack;
1535 
1536     /* Set cylinder data for the MSB */
1537     PartitionDescriptor->StartingCylinderMsb = (UCHAR)StartingCylinder;
1538     PartitionDescriptor->EndingCylinderMsb = (UCHAR)EndingCylinder;
1539 
1540     /* Set the track data */
1541     PartitionDescriptor->StartingTrack = (UCHAR)StartingTrack;
1542     PartitionDescriptor->EndingTrack = (UCHAR)EndingTrack;
1543 
1544     /* Update cylinder data for the LSB */
1545     StartingCylinder = ((StartingSector + 1) & 0x3F) |
1546                        ((StartingCylinder >> 2) & 0xC0);
1547     EndingCylinder = ((EndingSector + 1) & 0x3F) |
1548                      ((EndingCylinder >> 2) & 0xC0);
1549 
1550     /* Set the cylinder data for the LSB */
1551     PartitionDescriptor->StartingCylinderLsb = (UCHAR)StartingCylinder;
1552     PartitionDescriptor->EndingCylinderLsb = (UCHAR)EndingCylinder;
1553 }
1554 
1555 VOID
1556 FASTCALL
xHalGetPartialGeometry(IN PDEVICE_OBJECT DeviceObject,IN PULONG ConventionalCylinders,IN PLONGLONG DiskSize)1557 xHalGetPartialGeometry(IN PDEVICE_OBJECT DeviceObject,
1558                        IN PULONG ConventionalCylinders,
1559                        IN PLONGLONG DiskSize)
1560 {
1561     PDISK_GEOMETRY DiskGeometry = NULL;
1562     PIO_STATUS_BLOCK IoStatusBlock = NULL;
1563     PKEVENT Event = NULL;
1564     PIRP Irp;
1565     NTSTATUS Status;
1566 
1567     /* Set defaults */
1568     *ConventionalCylinders = 0;
1569     *DiskSize = 0;
1570 
1571     /* Allocate the structure in nonpaged pool */
1572     DiskGeometry = ExAllocatePoolWithTag(NonPagedPool,
1573                                          sizeof(DISK_GEOMETRY),
1574                                          TAG_FILE_SYSTEM);
1575     if (!DiskGeometry) goto Cleanup;
1576 
1577     /* Allocate the status block in nonpaged pool */
1578     IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool,
1579                                           sizeof(IO_STATUS_BLOCK),
1580                                           TAG_FILE_SYSTEM);
1581     if (!IoStatusBlock) goto Cleanup;
1582 
1583     /* Allocate the event in nonpaged pool too */
1584     Event = ExAllocatePoolWithTag(NonPagedPool,
1585                                   sizeof(KEVENT),
1586                                   TAG_FILE_SYSTEM);
1587     if (!Event) goto Cleanup;
1588 
1589     /* Initialize the event */
1590     KeInitializeEvent(Event, NotificationEvent, FALSE);
1591 
1592     /* Build the IRP */
1593     Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
1594                                         DeviceObject,
1595                                         NULL,
1596                                         0,
1597                                         DiskGeometry,
1598                                         sizeof(DISK_GEOMETRY),
1599                                         FALSE,
1600                                         Event,
1601                                         IoStatusBlock);
1602     if (!Irp) goto Cleanup;
1603 
1604     /* Now call the driver */
1605     Status = IoCallDriver(DeviceObject, Irp);
1606     if (Status == STATUS_PENDING)
1607     {
1608         /* Wait for it to complete */
1609         KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
1610         Status = IoStatusBlock->Status;
1611     }
1612 
1613     /* Check driver status */
1614     if (NT_SUCCESS(Status))
1615     {
1616         /* Return the cylinder count */
1617         *ConventionalCylinders = DiskGeometry->Cylinders.LowPart;
1618 
1619         /* Make sure it's not larger then 1024 */
1620         if (DiskGeometry->Cylinders.LowPart >= 1024)
1621         {
1622             /* Otherwise, normalize the value */
1623             *ConventionalCylinders = 1024;
1624         }
1625 
1626         /* Calculate the disk size */
1627         *DiskSize = DiskGeometry->Cylinders.QuadPart *
1628                     DiskGeometry->TracksPerCylinder *
1629                     DiskGeometry->SectorsPerTrack *
1630                     DiskGeometry->BytesPerSector;
1631     }
1632 
1633 Cleanup:
1634     /* Free all the pointers */
1635     if (Event) ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
1636     if (IoStatusBlock) ExFreePoolWithTag(IoStatusBlock, TAG_FILE_SYSTEM);
1637     if (DiskGeometry) ExFreePoolWithTag(DiskGeometry, TAG_FILE_SYSTEM);
1638     return;
1639 }
1640 
1641 VOID
1642 FASTCALL
xHalExamineMBR(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG MbrTypeIdentifier,OUT PVOID * MbrBuffer)1643 xHalExamineMBR(IN PDEVICE_OBJECT DeviceObject,
1644                IN ULONG SectorSize,
1645                IN ULONG MbrTypeIdentifier,
1646                OUT PVOID *MbrBuffer)
1647 {
1648     LARGE_INTEGER Offset;
1649     PUCHAR Buffer;
1650     ULONG BufferSize;
1651     KEVENT Event;
1652     IO_STATUS_BLOCK IoStatusBlock;
1653     PIRP Irp;
1654     PPARTITION_DESCRIPTOR PartitionDescriptor;
1655     NTSTATUS Status;
1656     PIO_STACK_LOCATION IoStackLocation;
1657 
1658     Offset.QuadPart = 0;
1659 
1660     /* Assume failure */
1661     *MbrBuffer = NULL;
1662 
1663     /* Normalize the buffer size */
1664     BufferSize = max(512, SectorSize);
1665 
1666     /* Allocate the buffer */
1667     Buffer = ExAllocatePoolWithTag(NonPagedPool,
1668                                    max(PAGE_SIZE, BufferSize),
1669                                    TAG_FILE_SYSTEM);
1670     if (!Buffer) return;
1671 
1672     /* Initialize the Event */
1673     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1674 
1675     /* Build the IRP */
1676     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
1677                                        DeviceObject,
1678                                        Buffer,
1679                                        BufferSize,
1680                                        &Offset,
1681                                        &Event,
1682                                        &IoStatusBlock);
1683     if (!Irp)
1684     {
1685         /* Failed */
1686         ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
1687         return;
1688     }
1689 
1690     /* Make sure to override volume verification */
1691     IoStackLocation = IoGetNextIrpStackLocation(Irp);
1692     IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1693 
1694     /* Call the driver */
1695     Status = IoCallDriver(DeviceObject, Irp);
1696     if (Status == STATUS_PENDING)
1697     {
1698         /* Wait for completion */
1699         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1700         Status = IoStatusBlock.Status;
1701     }
1702 
1703     /* Check driver Status */
1704     if (NT_SUCCESS(Status))
1705     {
1706         /* Validate the MBR Signature */
1707         if (*(PUINT16)&Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
1708         {
1709             /* Failed */
1710             ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
1711             return;
1712         }
1713 
1714         /* Get the partition entry */
1715         PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
1716 
1717         /* Make sure it's what the caller wanted */
1718         if (PartitionDescriptor->PartitionType != MbrTypeIdentifier)
1719         {
1720             /* It's not, free our buffer */
1721             ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
1722         }
1723         else
1724         {
1725             /* Check for OnTrack Disk Manager 6.0 / EZ-Drive partitions */
1726 
1727             if (PartitionDescriptor->PartitionType == PARTITION_DM)
1728             {
1729                 /* Return our buffer, but at sector 63 */
1730                 *(PULONG)Buffer = 63;
1731                 *MbrBuffer = Buffer;
1732             }
1733             else if (PartitionDescriptor->PartitionType == PARTITION_EZDRIVE)
1734             {
1735                 /* EZ-Drive, return the buffer directly */
1736                 *MbrBuffer = Buffer;
1737             }
1738             else
1739             {
1740                 /* Otherwise crash on debug builds */
1741                 ASSERT(PartitionDescriptor->PartitionType == PARTITION_EZDRIVE);
1742             }
1743         }
1744     }
1745 }
1746 
1747 VOID
1748 NTAPI
FstubFixupEfiPartition(IN PPARTITION_DESCRIPTOR PartitionDescriptor,IN ULONGLONG MaxOffset)1749 FstubFixupEfiPartition(IN PPARTITION_DESCRIPTOR PartitionDescriptor,
1750                        IN ULONGLONG MaxOffset)
1751 {
1752     ULONG PartitionMaxOffset, PartitionLength;
1753     PAGED_CODE();
1754 
1755     /* Compute partition length (according to MBR entry) */
1756     PartitionMaxOffset = GET_STARTING_SECTOR(PartitionDescriptor) + GET_PARTITION_LENGTH(PartitionDescriptor);
1757     /* In case the partition length goes beyond disk size... */
1758     if (PartitionMaxOffset > MaxOffset)
1759     {
1760         /* Resize partition to its maximum real length */
1761         PartitionLength = (ULONG)(PartitionMaxOffset - GET_STARTING_SECTOR(PartitionDescriptor));
1762         SET_PARTITION_LENGTH(PartitionDescriptor, PartitionLength);
1763     }
1764 }
1765 
1766 NTSTATUS
1767 FASTCALL
xHalIoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN BOOLEAN ReturnRecognizedPartitions,IN OUT PDRIVE_LAYOUT_INFORMATION * PartitionBuffer)1768 xHalIoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
1769                          IN ULONG SectorSize,
1770                          IN BOOLEAN ReturnRecognizedPartitions,
1771                          IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
1772 {
1773     KEVENT Event;
1774     IO_STATUS_BLOCK IoStatusBlock;
1775     PIRP Irp;
1776     PPARTITION_DESCRIPTOR PartitionDescriptor;
1777     CCHAR Entry;
1778     NTSTATUS Status;
1779     PPARTITION_INFORMATION PartitionInfo;
1780     PUCHAR Buffer = NULL;
1781     ULONG BufferSize = 2048, InputSize;
1782     PDRIVE_LAYOUT_INFORMATION DriveLayoutInfo = NULL;
1783     LONG j = -1, i = -1, k;
1784     DISK_GEOMETRY_EX DiskGeometryEx;
1785     LONGLONG EndSector, MaxSector, StartOffset;
1786     LARGE_INTEGER Offset, VolumeOffset;
1787     BOOLEAN IsPrimary = TRUE, IsEzDrive = FALSE, MbrFound = FALSE;
1788     BOOLEAN IsValid, IsEmpty = TRUE;
1789     PVOID MbrBuffer;
1790     PIO_STACK_LOCATION IoStackLocation;
1791     UCHAR PartitionType;
1792     LARGE_INTEGER HiddenSectors64;
1793 
1794     PAGED_CODE();
1795 
1796     VolumeOffset.QuadPart = Offset.QuadPart = 0;
1797 
1798     /* Allocate the buffer */
1799     *PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool,
1800                                              BufferSize,
1801                                              TAG_FILE_SYSTEM);
1802     if (!(*PartitionBuffer)) return STATUS_INSUFFICIENT_RESOURCES;
1803 
1804     /* Normalize the buffer size */
1805     InputSize = max(512, SectorSize);
1806 
1807     /* Check for EZ-Drive */
1808     HalExamineMBR(DeviceObject, InputSize, PARTITION_EZDRIVE, &MbrBuffer);
1809     if (MbrBuffer)
1810     {
1811         /* EZ-Drive found, bias the offset */
1812         IsEzDrive = TRUE;
1813         ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
1814         Offset.QuadPart = 512;
1815     }
1816 
1817     /* Get drive geometry */
1818     Status = HalpGetFullGeometry(DeviceObject, &DiskGeometryEx);
1819     if (!NT_SUCCESS(Status))
1820     {
1821         ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
1822         *PartitionBuffer = NULL;
1823         return Status;
1824     }
1825 
1826     /* Get the end and maximum sector */
1827     EndSector = DiskGeometryEx.DiskSize.QuadPart / DiskGeometryEx.Geometry.BytesPerSector;
1828     MaxSector = EndSector << 1;
1829     DPRINT("FSTUB: DiskSize = %#I64x, MaxSector = %#I64x\n",
1830             DiskGeometryEx.DiskSize, MaxSector);
1831 
1832     /* Allocate our buffer */
1833     Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, InputSize, TAG_FILE_SYSTEM);
1834     if (!Buffer)
1835     {
1836         /* Fail, free the input buffer */
1837         ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
1838         *PartitionBuffer = NULL;
1839         return STATUS_INSUFFICIENT_RESOURCES;
1840     }
1841 
1842     /* Start partition loop */
1843     do
1844     {
1845         /* Assume the partition is valid */
1846         IsValid = TRUE;
1847 
1848         /* Initialize the event */
1849         KeInitializeEvent(&Event, NotificationEvent, FALSE);
1850 
1851         /* Clear the buffer and build the IRP */
1852         RtlZeroMemory(Buffer, InputSize);
1853         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
1854                                            DeviceObject,
1855                                            Buffer,
1856                                            InputSize,
1857                                            &Offset,
1858                                            &Event,
1859                                            &IoStatusBlock);
1860         if (!Irp)
1861         {
1862             /* Failed */
1863             Status = STATUS_INSUFFICIENT_RESOURCES;
1864             break;
1865         }
1866 
1867         /* Make sure to disable volume verification */
1868         IoStackLocation = IoGetNextIrpStackLocation(Irp);
1869         IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1870 
1871         /* Call the driver */
1872         Status = IoCallDriver(DeviceObject, Irp);
1873         if (Status == STATUS_PENDING)
1874         {
1875             /* Wait for completion */
1876             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1877             Status = IoStatusBlock.Status;
1878         }
1879 
1880         /* Normalize status code and check for failure */
1881         if (Status == STATUS_NO_DATA_DETECTED) Status = STATUS_SUCCESS;
1882         if (!NT_SUCCESS(Status)) break;
1883 
1884         /* If we biased for EZ-Drive, unbias now */
1885         if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
1886 
1887         /* Make sure this is a valid MBR */
1888         if (*(PUINT16)&Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
1889         {
1890             /* It's not, fail */
1891             DPRINT1("FSTUB: (IoReadPartitionTable) No 0xaa55 found in "
1892                     "partition table %d\n", j + 1);
1893             break;
1894         }
1895 
1896         /* At this point we have a valid MBR */
1897         MbrFound = TRUE;
1898 
1899         /* Check if we weren't given an offset */
1900         if (!Offset.QuadPart)
1901         {
1902             /* Then read the signature off the disk */
1903             (*PartitionBuffer)->Signature = *(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET];
1904         }
1905 
1906         /* Get the partition descriptor array */
1907         PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
1908 
1909         /* Start looping partitions */
1910         j++;
1911         DPRINT("FSTUB: Partition Table %d:\n", j);
1912         for (Entry = 1, k = 0; Entry <= NUM_PARTITION_TABLE_ENTRIES; Entry++, PartitionDescriptor++)
1913         {
1914             /* Get the partition type */
1915             PartitionType = PartitionDescriptor->PartitionType;
1916 
1917             /* Print debug messages */
1918             DPRINT("Partition Entry %d,%d: type %#x %s\n",
1919                     j,
1920                     Entry,
1921                     PartitionType,
1922                     (PartitionDescriptor->ActiveFlag) ? "Active" : "");
1923             DPRINT("\tOffset %#08lx for %#08lx Sectors\n",
1924                     GET_STARTING_SECTOR(PartitionDescriptor),
1925                     GET_PARTITION_LENGTH(PartitionDescriptor));
1926 
1927             /* Check whether we're facing a protective MBR */
1928             if (PartitionType == EFI_PMBR_OSTYPE_EFI)
1929             {
1930                 /* Partition length might be bigger than disk size */
1931                 FstubFixupEfiPartition(PartitionDescriptor, DiskGeometryEx.DiskSize.QuadPart);
1932             }
1933 
1934             /* Make sure that the partition is valid, unless it's the first */
1935             if (!(HalpIsValidPartitionEntry(PartitionDescriptor,
1936                                             DiskGeometryEx.DiskSize.QuadPart,
1937                                             MaxSector)) && (j == 0))
1938             {
1939                 /* It's invalid, so fail */
1940                 IsValid = FALSE;
1941                 break;
1942             }
1943 
1944             /* Check if it's a container */
1945             if (IsContainerPartition(PartitionType))
1946             {
1947                 /* Increase the count of containers */
1948                 if (++k != 1)
1949                 {
1950                     /* More then one table is invalid */
1951                     DPRINT1("FSTUB: Multiple container partitions found in "
1952                             "partition table %d\n - table is invalid\n",
1953                             j);
1954                     IsValid = FALSE;
1955                     break;
1956                 }
1957             }
1958 
1959             /* Check if the partition is supposedly empty */
1960             if (IsEmpty)
1961             {
1962                 /* But check if it actually has a start and/or length */
1963                 if ((GET_STARTING_SECTOR(PartitionDescriptor)) ||
1964                     (GET_PARTITION_LENGTH(PartitionDescriptor)))
1965                 {
1966                     /* So then it's not really empty */
1967                     IsEmpty = FALSE;
1968                 }
1969             }
1970 
1971             /* Check if the caller wanted only recognized partitions */
1972             if (ReturnRecognizedPartitions)
1973             {
1974                 /* Then check if this one is unused, or a container */
1975                 if ((PartitionType == PARTITION_ENTRY_UNUSED) ||
1976                     IsContainerPartition(PartitionType))
1977                 {
1978                     /* Skip it, since the caller doesn't want it */
1979                     continue;
1980                 }
1981             }
1982 
1983             /* Increase the structure count and check if they can fit */
1984             if ((sizeof(DRIVE_LAYOUT_INFORMATION) +
1985                  (++i * sizeof(PARTITION_INFORMATION))) >
1986                 BufferSize)
1987             {
1988                 /* Allocate a new buffer that's twice as big */
1989                 DriveLayoutInfo = ExAllocatePoolWithTag(NonPagedPool,
1990                                                         BufferSize << 1,
1991                                                         TAG_FILE_SYSTEM);
1992                 if (!DriveLayoutInfo)
1993                 {
1994                     /* Out of memory, undo this extra structure */
1995                     --i;
1996                     Status = STATUS_INSUFFICIENT_RESOURCES;
1997                     break;
1998                 }
1999 
2000                 /* Copy the contents of the old buffer */
2001                 RtlMoveMemory(DriveLayoutInfo,
2002                               *PartitionBuffer,
2003                               BufferSize);
2004 
2005                 /* Free the old buffer and set this one as the new one */
2006                 ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
2007                 *PartitionBuffer = DriveLayoutInfo;
2008 
2009                 /* Double the size */
2010                 BufferSize <<= 1;
2011             }
2012 
2013             /* Now get the current structure being filled and initialize it */
2014             PartitionInfo = &(*PartitionBuffer)->PartitionEntry[i];
2015             PartitionInfo->PartitionType = PartitionType;
2016             PartitionInfo->RewritePartition = FALSE;
2017 
2018             /* Check if we're dealing with a partition that's in use */
2019             if (PartitionType != PARTITION_ENTRY_UNUSED)
2020             {
2021                 /* Check if it's bootable */
2022                 PartitionInfo->BootIndicator = PartitionDescriptor->
2023                                                ActiveFlag & 0x80 ?
2024                                                TRUE : FALSE;
2025 
2026                 /* Check if its' a container */
2027                 if (IsContainerPartition(PartitionType))
2028                 {
2029                     /* Then don't recognize it and use the volume offset */
2030                     PartitionInfo->RecognizedPartition = FALSE;
2031                     StartOffset = VolumeOffset.QuadPart;
2032                 }
2033                 else
2034                 {
2035                     /* Then recognize it and use the partition offset */
2036                     PartitionInfo->RecognizedPartition = TRUE;
2037                     StartOffset = Offset.QuadPart;
2038                 }
2039 
2040                 /* Get the starting offset */
2041                 PartitionInfo->StartingOffset.QuadPart =
2042                     StartOffset +
2043                     UInt32x32To64(GET_STARTING_SECTOR(PartitionDescriptor),
2044                                   SectorSize);
2045 
2046                 /* Calculate the number of hidden sectors */
2047                 HiddenSectors64.QuadPart = (PartitionInfo->
2048                                             StartingOffset.QuadPart -
2049                                             StartOffset) /
2050                                             SectorSize;
2051                 PartitionInfo->HiddenSectors = HiddenSectors64.LowPart;
2052 
2053                 /* Get the partition length */
2054                 PartitionInfo->PartitionLength.QuadPart =
2055                     UInt32x32To64(GET_PARTITION_LENGTH(PartitionDescriptor),
2056                                   SectorSize);
2057 
2058                 /* Get the partition number */
2059                 /* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
2060                 PartitionInfo->PartitionNumber = (!IsContainerPartition(PartitionType)) ? i + 1 : 0;
2061             }
2062             else
2063             {
2064                 /* Otherwise, clear all the relevant fields */
2065                 PartitionInfo->BootIndicator = FALSE;
2066                 PartitionInfo->RecognizedPartition = FALSE;
2067                 PartitionInfo->StartingOffset.QuadPart = 0;
2068                 PartitionInfo->PartitionLength.QuadPart = 0;
2069                 PartitionInfo->HiddenSectors = 0;
2070 
2071                 /* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
2072                 PartitionInfo->PartitionNumber = 0;
2073             }
2074         }
2075 
2076         /* Finish debug log, and check for failure */
2077         DPRINT("\n");
2078         if (!NT_SUCCESS(Status)) break;
2079 
2080         /* Also check if we hit an invalid entry here */
2081         if (!IsValid)
2082         {
2083             /* We did, so break out of the loop minus one entry */
2084             j--;
2085             break;
2086         }
2087 
2088         /* Reset the offset */
2089         Offset.QuadPart = 0;
2090 
2091         /* Go back to the descriptor array and loop it */
2092         PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
2093         for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; Entry++, PartitionDescriptor++)
2094         {
2095             /* Check if this is a container partition, since we skipped them */
2096             if (IsContainerPartition(PartitionDescriptor->PartitionType))
2097             {
2098                 /* Get its offset */
2099                 Offset.QuadPart = VolumeOffset.QuadPart +
2100                                   UInt32x32To64(
2101                                      GET_STARTING_SECTOR(PartitionDescriptor),
2102                                      SectorSize);
2103 
2104                 /* If this is a primary partition, this is the volume offset */
2105                 if (IsPrimary) VolumeOffset = Offset;
2106 
2107                 /* Also update the maximum sector */
2108                 MaxSector = GET_PARTITION_LENGTH(PartitionDescriptor);
2109                 DPRINT1("FSTUB: MaxSector now = %I64d\n", MaxSector);
2110                 break;
2111             }
2112         }
2113 
2114         /* Loop the next partitions, which are not primary anymore */
2115         IsPrimary = FALSE;
2116     } while (Offset.HighPart | Offset.LowPart);
2117 
2118     /* Check if this is a removable device that's probably a super-floppy */
2119     if ((DiskGeometryEx.Geometry.MediaType == RemovableMedia) &&
2120         (j == 0) && (MbrFound) && (IsEmpty))
2121     {
2122         PBOOT_SECTOR_INFO BootSectorInfo = (PBOOT_SECTOR_INFO)Buffer;
2123 
2124         /* Read the jump bytes to detect super-floppy */
2125         if ((BootSectorInfo->JumpByte[0] == 0xeb) ||
2126             (BootSectorInfo->JumpByte[0] == 0xe9))
2127         {
2128             /* Super floppes don't have typical MBRs, so skip them */
2129             DPRINT1("FSTUB: Jump byte %#x found along with empty partition "
2130                     "table - disk is a super floppy and has no valid MBR\n",
2131                     BootSectorInfo->JumpByte);
2132             j = -1;
2133         }
2134     }
2135 
2136     /* Check if we're still at partition -1 */
2137     if (j == -1)
2138     {
2139         /* The likely cause is the super floppy detection above */
2140         if ((MbrFound) || (DiskGeometryEx.Geometry.MediaType == RemovableMedia))
2141         {
2142             /* Print out debugging information */
2143             DPRINT1("FSTUB: Drive %#p has no valid MBR. Make it into a "
2144                     "super-floppy\n",
2145                     DeviceObject);
2146             DPRINT1("FSTUB: Drive has %I64d sectors and is %#016I64x "
2147                     "bytes large\n",
2148                     EndSector, DiskGeometryEx.DiskSize);
2149 
2150             /* We should at least have some sectors */
2151             if (EndSector > 0)
2152             {
2153                 /* Get the entry we'll use */
2154                 PartitionInfo = &(*PartitionBuffer)->PartitionEntry[0];
2155 
2156                 /* Fill it out with data for a super-floppy */
2157                 PartitionInfo->RewritePartition = FALSE;
2158                 PartitionInfo->RecognizedPartition = TRUE;
2159                 PartitionInfo->PartitionType = PARTITION_FAT_16;
2160                 PartitionInfo->BootIndicator = FALSE;
2161                 PartitionInfo->HiddenSectors = 0;
2162                 PartitionInfo->StartingOffset.QuadPart = 0;
2163                 PartitionInfo->PartitionLength = DiskGeometryEx.DiskSize;
2164 
2165                 /* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
2166                 PartitionInfo->PartitionNumber = 0;
2167 
2168                 /* Set the signature and set the count back to 0 */
2169                 (*PartitionBuffer)->Signature = 1;
2170                 i = 0;
2171             }
2172         }
2173         else
2174         {
2175             /* Otherwise, this isn't a super floppy, so set an invalid count */
2176             i = -1;
2177         }
2178     }
2179 
2180     /* Set the partition count */
2181     (*PartitionBuffer)->PartitionCount = ++i;
2182 
2183     /* If we have no count, delete the signature */
2184     if (!i) (*PartitionBuffer)->Signature = 0;
2185 
2186     /* Free the buffer and check for success */
2187     if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
2188     if (!NT_SUCCESS(Status))
2189     {
2190         ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
2191         *PartitionBuffer = NULL;
2192     }
2193 
2194     /* Return status */
2195     return Status;
2196 }
2197 
2198 NTSTATUS
2199 FASTCALL
xHalIoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG PartitionNumber,IN ULONG PartitionType)2200 xHalIoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,
2201                               IN ULONG SectorSize,
2202                               IN ULONG PartitionNumber,
2203                               IN ULONG PartitionType)
2204 {
2205     PIRP Irp;
2206     KEVENT Event;
2207     IO_STATUS_BLOCK IoStatusBlock;
2208     NTSTATUS Status;
2209     LARGE_INTEGER Offset, VolumeOffset;
2210     PUCHAR Buffer = NULL;
2211     ULONG BufferSize;
2212     ULONG i = 0;
2213     ULONG Entry;
2214     PPARTITION_DESCRIPTOR PartitionDescriptor;
2215     BOOLEAN IsPrimary = TRUE, IsEzDrive = FALSE;
2216     PVOID MbrBuffer;
2217     PIO_STACK_LOCATION IoStackLocation;
2218 
2219     PAGED_CODE();
2220 
2221     VolumeOffset.QuadPart = Offset.QuadPart = 0;
2222 
2223     /* Normalize the buffer size */
2224     BufferSize = max(512, SectorSize);
2225 
2226     /* Check for EZ-Drive */
2227     HalExamineMBR(DeviceObject, BufferSize, PARTITION_EZDRIVE, &MbrBuffer);
2228     if (MbrBuffer)
2229     {
2230         /* EZ-Drive found, bias the offset */
2231         IsEzDrive = TRUE;
2232         ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
2233         Offset.QuadPart = 512;
2234     }
2235 
2236     /* Allocate our partition buffer */
2237     Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, PAGE_SIZE, TAG_FILE_SYSTEM);
2238     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
2239 
2240     /* Initialize the event we'll use and loop partitions */
2241     KeInitializeEvent(&Event, NotificationEvent, FALSE);
2242     do
2243     {
2244         /* Reset the event since we reuse it */
2245         KeClearEvent(&Event);
2246 
2247         /* Build the read IRP */
2248         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
2249                                            DeviceObject,
2250                                            Buffer,
2251                                            BufferSize,
2252                                            &Offset,
2253                                            &Event,
2254                                            &IoStatusBlock);
2255         if (!Irp)
2256         {
2257             /* Fail */
2258             Status = STATUS_INSUFFICIENT_RESOURCES;
2259             break;
2260         }
2261 
2262         /* Make sure to disable volume verification */
2263         IoStackLocation = IoGetNextIrpStackLocation(Irp);
2264         IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2265 
2266         /* Call the driver */
2267         Status = IoCallDriver(DeviceObject, Irp);
2268         if (Status == STATUS_PENDING)
2269         {
2270             /* Wait for completion */
2271             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2272             Status = IoStatusBlock.Status;
2273         }
2274 
2275         /* Check for failure */
2276         if (!NT_SUCCESS(Status)) break;
2277 
2278         /* If we biased for EZ-Drive, unbias now */
2279         if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
2280 
2281         /* Make sure this is a valid MBR */
2282         if (*(PUINT16)&Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
2283         {
2284             /* It's not, fail */
2285             Status = STATUS_BAD_MASTER_BOOT_RECORD;
2286             break;
2287         }
2288 
2289         /* Get the partition descriptors and loop them */
2290         PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
2291         for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; Entry++, PartitionDescriptor++)
2292         {
2293             /* Check if it's unused or a container partition */
2294             if ((PartitionDescriptor->PartitionType == PARTITION_ENTRY_UNUSED) ||
2295                 (IsContainerPartition(PartitionDescriptor->PartitionType)))
2296             {
2297                 /* Go to the next one */
2298                 continue;
2299             }
2300 
2301             /* It's a valid partition, so increase the partition count */
2302             if (++i == PartitionNumber)
2303             {
2304                 /* We found a match, set the type */
2305                 PartitionDescriptor->PartitionType = (UCHAR)PartitionType;
2306 
2307                 /* Reset the reusable event */
2308                 KeClearEvent(&Event);
2309 
2310                 /* Build the write IRP */
2311                 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
2312                                                    DeviceObject,
2313                                                    Buffer,
2314                                                    BufferSize,
2315                                                    &Offset,
2316                                                    &Event,
2317                                                    &IoStatusBlock);
2318                 if (!Irp)
2319                 {
2320                     /* Fail */
2321                     Status = STATUS_INSUFFICIENT_RESOURCES;
2322                     break;
2323                 }
2324 
2325                 /* Disable volume verification */
2326                 IoStackLocation = IoGetNextIrpStackLocation(Irp);
2327                 IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2328 
2329                 /* Call the driver */
2330                 Status = IoCallDriver(DeviceObject, Irp);
2331                 if (Status == STATUS_PENDING)
2332                 {
2333                     /* Wait for completion */
2334                     KeWaitForSingleObject(&Event,
2335                                           Executive,
2336                                           KernelMode,
2337                                           FALSE,
2338                                           NULL);
2339                     Status = IoStatusBlock.Status;
2340                 }
2341 
2342                 /* We're done, break out of the loop */
2343                 break;
2344             }
2345         }
2346 
2347         /* If we looped all the partitions, break out */
2348         if (Entry <= NUM_PARTITION_TABLE_ENTRIES) break;
2349 
2350         /* Nothing found yet, get the partition array again */
2351         PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
2352         for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; Entry++, PartitionDescriptor++)
2353         {
2354             /* Check if this was a container partition (we skipped these) */
2355             if (IsContainerPartition(PartitionDescriptor->PartitionType))
2356             {
2357                 /* Update the partition offset */
2358                 Offset.QuadPart = VolumeOffset.QuadPart +
2359                                   GET_STARTING_SECTOR(PartitionDescriptor) *
2360                                   SectorSize;
2361 
2362                 /* If this was the primary partition, update the volume too */
2363                 if (IsPrimary) VolumeOffset = Offset;
2364                 break;
2365             }
2366         }
2367 
2368         /* Check if we already searched all the partitions */
2369         if (Entry > NUM_PARTITION_TABLE_ENTRIES)
2370         {
2371             /* Then we failed to find a good MBR */
2372             Status = STATUS_BAD_MASTER_BOOT_RECORD;
2373             break;
2374         }
2375 
2376         /* Loop the next partitions, which are not primary anymore */
2377         IsPrimary = FALSE;
2378     } while (i < PartitionNumber);
2379 
2380     /* Everything done, cleanup */
2381     if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
2382     return Status;
2383 }
2384 
2385 NTSTATUS
2386 FASTCALL
xHalIoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG SectorsPerTrack,IN ULONG NumberOfHeads,IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)2387 xHalIoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,
2388                           IN ULONG SectorSize,
2389                           IN ULONG SectorsPerTrack,
2390                           IN ULONG NumberOfHeads,
2391                           IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)
2392 {
2393     KEVENT Event;
2394     IO_STATUS_BLOCK IoStatusBlock;
2395     PIRP Irp;
2396     NTSTATUS Status = STATUS_SUCCESS;
2397     ULONG BufferSize;
2398     PUCHAR Buffer;
2399     PPTE Entry;
2400     PPARTITION_TABLE PartitionTable;
2401     LARGE_INTEGER Offset, NextOffset, ExtendedOffset, SectorOffset;
2402     LARGE_INTEGER StartOffset, PartitionLength;
2403     ULONG i, j;
2404     CCHAR k;
2405     BOOLEAN IsEzDrive = FALSE, IsSuperFloppy = FALSE, DoRewrite = FALSE, IsMbr;
2406     ULONG ConventionalCylinders;
2407     LONGLONG DiskSize;
2408     PDISK_LAYOUT DiskLayout = (PDISK_LAYOUT)PartitionBuffer;
2409     PVOID MbrBuffer;
2410     UCHAR PartitionType;
2411     PIO_STACK_LOCATION IoStackLocation;
2412     PPARTITION_INFORMATION PartitionInfo = PartitionBuffer->PartitionEntry;
2413     PPARTITION_INFORMATION TableEntry;
2414 
2415     PAGED_CODE();
2416 
2417     ExtendedOffset.QuadPart = NextOffset.QuadPart = Offset.QuadPart = 0;
2418 
2419     /* Normalize the buffer size */
2420     BufferSize = max(512, SectorSize);
2421 
2422     /* Get the partial drive geometry */
2423     xHalGetPartialGeometry(DeviceObject, &ConventionalCylinders, &DiskSize);
2424 
2425     /* Check for EZ-Drive */
2426     HalExamineMBR(DeviceObject, BufferSize, PARTITION_EZDRIVE, &MbrBuffer);
2427     if (MbrBuffer)
2428     {
2429         /* EZ-Drive found, bias the offset */
2430         IsEzDrive = TRUE;
2431         ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
2432         Offset.QuadPart = 512;
2433     }
2434 
2435     /* Get the number of bits to shift to multiply by the sector size */
2436     for (k = 0; k < 32; k++) if ((SectorSize >> k) == 1) break;
2437 
2438     /* Check if there's only one partition */
2439     if (PartitionBuffer->PartitionCount == 1)
2440     {
2441         /* Check if it has no starting offset or hidden sectors */
2442         if (!(PartitionInfo->StartingOffset.QuadPart) &&
2443             !(PartitionInfo->HiddenSectors))
2444         {
2445             /* Then it's a super floppy */
2446             IsSuperFloppy = TRUE;
2447 
2448             /* Which also means it must be non-bootable FAT-16 */
2449             if ((PartitionInfo->PartitionNumber) ||
2450                 (PartitionInfo->PartitionType != PARTITION_FAT_16) ||
2451                 (PartitionInfo->BootIndicator))
2452             {
2453                 /* It's not, so we fail */
2454                 return STATUS_INVALID_PARAMETER;
2455             }
2456 
2457             /* Check if it needs a rewrite, and disable EZ-Drive for sure */
2458             if (PartitionInfo->RewritePartition) DoRewrite = TRUE;
2459             IsEzDrive = FALSE;
2460         }
2461     }
2462 
2463     /* Count the number of partition tables */
2464     DiskLayout->TableCount = (PartitionBuffer->PartitionCount + NUM_PARTITION_TABLE_ENTRIES - 1) / NUM_PARTITION_TABLE_ENTRIES;
2465 
2466     /* Allocate our partition buffer */
2467     Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, PAGE_SIZE, TAG_FILE_SYSTEM);
2468     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
2469 
2470     /* Loop the entries */
2471     Entry = (PPTE)&Buffer[PARTITION_TABLE_OFFSET];
2472     for (i = 0; i < DiskLayout->TableCount; i++)
2473     {
2474         /* Set if this is the MBR partition */
2475         IsMbr= (BOOLEAN)!i;
2476 
2477         /* Initialize th event */
2478         KeInitializeEvent(&Event, NotificationEvent, FALSE);
2479 
2480         /* Build the read IRP */
2481         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
2482                                            DeviceObject,
2483                                            Buffer,
2484                                            BufferSize,
2485                                            &Offset,
2486                                            &Event,
2487                                            &IoStatusBlock);
2488         if (!Irp)
2489         {
2490             /* Fail */
2491             Status = STATUS_INSUFFICIENT_RESOURCES;
2492             break;
2493         }
2494 
2495         /* Make sure to disable volume verification */
2496         IoStackLocation = IoGetNextIrpStackLocation(Irp);
2497         IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2498 
2499         /* Call the driver */
2500         Status = IoCallDriver(DeviceObject, Irp);
2501         if (Status == STATUS_PENDING)
2502         {
2503             /* Wait for completion */
2504             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2505             Status = IoStatusBlock.Status;
2506         }
2507 
2508         /* Check for failure */
2509         if (!NT_SUCCESS(Status)) break;
2510 
2511         /* If we biased for EZ-Drive, unbias now */
2512         if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
2513 
2514         /* Check if this is a normal disk */
2515         if (!IsSuperFloppy)
2516         {
2517             /* Set the boot record signature */
2518             *(PUINT16)&Buffer[BOOT_SIGNATURE_OFFSET] = BOOT_RECORD_SIGNATURE;
2519 
2520             /* By default, don't require a rewrite */
2521             DoRewrite = FALSE;
2522 
2523             /* Check if we don't have an offset */
2524             if (!Offset.QuadPart)
2525             {
2526                 /* Check if the signature doesn't match */
2527                 if (*(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET] != PartitionBuffer->Signature)
2528                 {
2529                     /* Then write the signature and now we need a rewrite */
2530                     *(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET] = PartitionBuffer->Signature;
2531                     DoRewrite = TRUE;
2532                 }
2533             }
2534 
2535             /* Loop the partition table entries */
2536             PartitionTable = &DiskLayout->PartitionTable[i];
2537             for (j = 0; j < NUM_PARTITION_TABLE_ENTRIES; j++)
2538             {
2539                 /* Get the current entry and type */
2540                 TableEntry = &PartitionTable->PartitionEntry[j];
2541                 PartitionType = TableEntry->PartitionType;
2542 
2543                 /* Check if the entry needs a rewrite */
2544                 if (TableEntry->RewritePartition)
2545                 {
2546                     /* Then we need one too */
2547                     DoRewrite = TRUE;
2548 
2549                     /* Save the type and if it's a bootable partition */
2550                     Entry[j].PartitionType = TableEntry->PartitionType;
2551                     Entry[j].ActiveFlag = TableEntry->BootIndicator ? 0x80 : 0;
2552 
2553                     /* Make sure it's used */
2554                     if (PartitionType != PARTITION_ENTRY_UNUSED)
2555                     {
2556                         /* Make sure it's not a container (unless primary) */
2557                         if ((IsMbr) || !(IsContainerPartition(PartitionType)))
2558                         {
2559                             /* Use the partition offset */
2560                             StartOffset.QuadPart = Offset.QuadPart;
2561                         }
2562                         else
2563                         {
2564                             /* Use the extended logical partition offset */
2565                             StartOffset.QuadPart = ExtendedOffset.QuadPart;
2566                         }
2567 
2568                         /* Set the sector offset */
2569                         SectorOffset.QuadPart = TableEntry->
2570                                                 StartingOffset.QuadPart -
2571                                                 StartOffset.QuadPart;
2572 
2573                         /* Now calculate the starting sector */
2574                         StartOffset.QuadPart = SectorOffset.QuadPart >> k;
2575                         Entry[j].StartingSector = StartOffset.LowPart;
2576 
2577                         /* As well as the length */
2578                         PartitionLength.QuadPart = TableEntry->PartitionLength.
2579                                                    QuadPart >> k;
2580                         Entry[j].PartitionLength = PartitionLength.LowPart;
2581 
2582                         /* Calculate the CHS values */
2583                         HalpCalculateChsValues(&TableEntry->StartingOffset,
2584                                                &TableEntry->PartitionLength,
2585                                                k,
2586                                                SectorsPerTrack,
2587                                                NumberOfHeads,
2588                                                ConventionalCylinders,
2589                                                (PPARTITION_DESCRIPTOR)
2590                                                &Entry[j]);
2591                     }
2592                     else
2593                     {
2594                         /* Otherwise set up an empty entry */
2595                         Entry[j].StartingSector = 0;
2596                         Entry[j].PartitionLength = 0;
2597                         Entry[j].StartingTrack = 0;
2598                         Entry[j].EndingTrack = 0;
2599                         Entry[j].StartingCylinder = 0;
2600                         Entry[j].EndingCylinder = 0;
2601                     }
2602                 }
2603 
2604                 /* Check if this is a container partition */
2605                 if (IsContainerPartition(PartitionType))
2606                 {
2607                     /* Then update the offset to use */
2608                     NextOffset = TableEntry->StartingOffset;
2609                 }
2610             }
2611         }
2612 
2613         /* Check if we need to write back the buffer */
2614         if (DoRewrite)
2615         {
2616             /* We don't need to do this again */
2617             DoRewrite = FALSE;
2618 
2619             /* Initialize the event */
2620             KeInitializeEvent(&Event, NotificationEvent, FALSE);
2621 
2622             /* If we unbiased for EZ-Drive, rebias now */
2623             if (IsEzDrive && !Offset.QuadPart) Offset.QuadPart = 512;
2624 
2625             /* Build the write IRP */
2626             Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
2627                                                DeviceObject,
2628                                                Buffer,
2629                                                BufferSize,
2630                                                &Offset,
2631                                                &Event,
2632                                                &IoStatusBlock);
2633             if (!Irp)
2634             {
2635                 /* Fail */
2636                 Status = STATUS_INSUFFICIENT_RESOURCES;
2637                 break;
2638             }
2639 
2640             /* Make sure to disable volume verification */
2641             IoStackLocation = IoGetNextIrpStackLocation(Irp);
2642             IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2643 
2644             /* Call the driver */
2645             Status = IoCallDriver(DeviceObject, Irp);
2646             if (Status == STATUS_PENDING)
2647             {
2648                 /* Wait for completion */
2649                 KeWaitForSingleObject(&Event,
2650                                       Executive,
2651                                       KernelMode,
2652                                       FALSE,
2653                                       NULL);
2654                 Status = IoStatusBlock.Status;
2655             }
2656 
2657             /* Check for failure */
2658             if (!NT_SUCCESS(Status)) break;
2659 
2660             /* If we biased for EZ-Drive, unbias now */
2661             if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
2662         }
2663 
2664         /* Update the partition offset and set the extended offset if needed */
2665         Offset = NextOffset;
2666         if (IsMbr) ExtendedOffset = NextOffset;
2667     }
2668 
2669     /* If we had a buffer, free it, then return status */
2670     if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
2671     return Status;
2672 }
2673 
2674 /* PUBLIC FUNCTIONS **********************************************************/
2675 
2676 /*
2677  * @implemented
2678  */
2679 VOID
2680 FASTCALL
HalExamineMBR(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG MbrTypeIdentifier,OUT PVOID * MbrBuffer)2681 HalExamineMBR(IN PDEVICE_OBJECT DeviceObject,
2682               IN ULONG SectorSize,
2683               IN ULONG MbrTypeIdentifier,
2684               OUT PVOID *MbrBuffer)
2685 {
2686     HALDISPATCH->HalExamineMBR(DeviceObject,
2687                                SectorSize,
2688                                MbrTypeIdentifier,
2689                                MbrBuffer);
2690 }
2691 
2692 /*
2693  * @implemented
2694  */
2695 NTSTATUS
2696 FASTCALL
IoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN BOOLEAN ReturnRecognizedPartitions,IN OUT PDRIVE_LAYOUT_INFORMATION * PartitionBuffer)2697 IoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
2698                      IN ULONG SectorSize,
2699                      IN BOOLEAN ReturnRecognizedPartitions,
2700                      IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
2701 {
2702     return HALDISPATCH->HalIoReadPartitionTable(DeviceObject,
2703                                                 SectorSize,
2704                                                 ReturnRecognizedPartitions,
2705                                                 PartitionBuffer);
2706 }
2707 
2708 /*
2709  * @implemented
2710  */
2711 NTSTATUS
2712 FASTCALL
IoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG PartitionNumber,IN ULONG PartitionType)2713 IoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,
2714                           IN ULONG SectorSize,
2715                           IN ULONG PartitionNumber,
2716                           IN ULONG PartitionType)
2717 {
2718     return HALDISPATCH->HalIoSetPartitionInformation(DeviceObject,
2719                                                      SectorSize,
2720                                                      PartitionNumber,
2721                                                      PartitionType);
2722 }
2723 
2724 /*
2725  * @implemented
2726  */
2727 NTSTATUS
2728 FASTCALL
IoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,IN ULONG SectorSize,IN ULONG SectorsPerTrack,IN ULONG NumberOfHeads,IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)2729 IoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,
2730                       IN ULONG SectorSize,
2731                       IN ULONG SectorsPerTrack,
2732                       IN ULONG NumberOfHeads,
2733                       IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)
2734 {
2735     return HALDISPATCH->HalIoWritePartitionTable(DeviceObject,
2736                                                  SectorSize,
2737                                                  SectorsPerTrack,
2738                                                  NumberOfHeads,
2739                                                  PartitionBuffer);
2740 }
2741 
2742 /*
2743  * @implemented
2744  */
2745 VOID
2746 FASTCALL
IoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,IN PSTRING NtDeviceName,OUT PUCHAR NtSystemPath,OUT PSTRING NtSystemPathString)2747 IoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
2748                      IN PSTRING NtDeviceName,
2749                      OUT PUCHAR NtSystemPath,
2750                      OUT PSTRING NtSystemPathString)
2751 {
2752     HALDISPATCH->HalIoAssignDriveLetters(LoaderBlock,
2753                                          NtDeviceName,
2754                                          NtSystemPath,
2755                                          NtSystemPathString);
2756 }
2757 
2758 /* EOF */
2759