xref: /reactos/ntoskrnl/io/iomgr/arcname.c (revision a6726659)
1 /*
2 * PROJECT:         ReactOS Kernel
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            ntoskrnl/io/iomgr/arcname.c
5 * PURPOSE:         ARC Path Initialization Functions
6 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7 *                  Eric Kohl
8 *                  Pierre Schweitzer (pierre.schweitzer@reactos.org)
9 */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS *******************************************************************/
18 
19 UNICODE_STRING IoArcHalDeviceName, IoArcBootDeviceName;
20 PCHAR IoLoaderArcBootDeviceName;
21 extern BOOLEAN IoRemoteBootClient;
22 
23 /* FUNCTIONS *****************************************************************/
24 
25 INIT_FUNCTION
26 NTSTATUS
27 NTAPI
28 IopCreateArcNamesCd(IN PLOADER_PARAMETER_BLOCK LoaderBlock
29 );
30 
31 INIT_FUNCTION
32 NTSTATUS
33 NTAPI
34 IopCreateArcNamesDisk(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
35                       IN BOOLEAN SingleDisk,
36                       IN PBOOLEAN FoundBoot
37 );
38 
39 INIT_FUNCTION
40 NTSTATUS
41 NTAPI
42 IopCreateArcNames(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
43 {
44     SIZE_T Length;
45     NTSTATUS Status;
46     CHAR Buffer[128];
47     BOOLEAN SingleDisk;
48     BOOLEAN FoundBoot = FALSE;
49     UNICODE_STRING SystemDevice, LoaderPathNameW, BootDeviceName;
50     PARC_DISK_INFORMATION ArcDiskInfo = LoaderBlock->ArcDiskInformation;
51     ANSI_STRING ArcSystemString, ArcString, LanmanRedirector, LoaderPathNameA;
52 
53     /* Check if we only have one disk on the machine */
54     SingleDisk = ArcDiskInfo->DiskSignatureListHead.Flink->Flink ==
55                  (&ArcDiskInfo->DiskSignatureListHead);
56 
57     /* Create the global HAL partition name */
58     sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName);
59     RtlInitAnsiString(&ArcString, Buffer);
60     RtlAnsiStringToUnicodeString(&IoArcHalDeviceName, &ArcString, TRUE);
61 
62     /* Create the global system partition name */
63     sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
64     RtlInitAnsiString(&ArcString, Buffer);
65     RtlAnsiStringToUnicodeString(&IoArcBootDeviceName, &ArcString, TRUE);
66 
67     /* Allocate memory for the string */
68     Length = strlen(LoaderBlock->ArcBootDeviceName) + sizeof(ANSI_NULL);
69     IoLoaderArcBootDeviceName = ExAllocatePoolWithTag(PagedPool,
70                                                       Length,
71                                                       TAG_IO);
72     if (IoLoaderArcBootDeviceName)
73     {
74         /* Copy the name */
75         RtlCopyMemory(IoLoaderArcBootDeviceName,
76                       LoaderBlock->ArcBootDeviceName,
77                       Length);
78     }
79 
80     /* Check if we only found a disk, but we're booting from CD-ROM */
81     if ((SingleDisk) && strstr(LoaderBlock->ArcBootDeviceName, "cdrom"))
82     {
83         /* Then disable single-disk mode, since there's a CD drive out there */
84         SingleDisk = FALSE;
85     }
86 
87     /* Build the boot strings */
88     RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName);
89 
90     /* If we are doing remote booting */
91     if (IoRemoteBootClient)
92     {
93         /* Yes, we have found boot device */
94         FoundBoot = TRUE;
95 
96         /* Get NT device name */
97         RtlInitAnsiString(&LanmanRedirector, "\\Device\\LanmanRedirector");
98         Status = RtlAnsiStringToUnicodeString(&SystemDevice, &LanmanRedirector, TRUE);
99         if (!NT_SUCCESS(Status))
100         {
101             return Status;
102         }
103 
104         /* Get ARC booting device name (in net(0) something) */
105         sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
106         RtlInitAnsiString(&ArcString, Buffer);
107         Status = RtlAnsiStringToUnicodeString(&BootDeviceName, &ArcString, TRUE);
108         if (NT_SUCCESS(Status))
109         {
110             /* Map ARC to NT name */
111             IoAssignArcName(&BootDeviceName, &SystemDevice);
112             RtlFreeUnicodeString(&BootDeviceName);
113 
114             /* Now, get loader path name */
115             RtlInitAnsiString(&LoaderPathNameA, LoaderBlock->NtHalPathName);
116             Status = RtlAnsiStringToUnicodeString(&LoaderPathNameW, &LoaderPathNameA, TRUE);
117             if (!NT_SUCCESS(Status))
118             {
119                 RtlFreeUnicodeString(&SystemDevice);
120                 return Status;
121             }
122 
123             /* And set it has system partition */
124             IopStoreSystemPartitionInformation(&SystemDevice, &LoaderPathNameW);
125         }
126 
127         RtlFreeUnicodeString(&SystemDevice);
128 
129         /* Don't quit here, even if everything went fine!
130          * We need IopCreateArcNamesDisk to properly map
131          * devices with symlinks.
132          * It will return success if the mapping process went fine
133          * even if it didn't find boot device.
134          * It won't reset boot device finding status as well.
135          */
136     }
137 
138     /* Loop every disk and try to find boot disk */
139     Status = IopCreateArcNamesDisk(LoaderBlock, SingleDisk, &FoundBoot);
140     /* If it succeed but we didn't find boot device, try to browse Cds */
141     if (NT_SUCCESS(Status) && !FoundBoot)
142     {
143         Status = IopCreateArcNamesCd(LoaderBlock);
144     }
145 
146     /* Return success */
147     return Status;
148 }
149 
150 INIT_FUNCTION
151 NTSTATUS
152 NTAPI
153 IopCreateArcNamesCd(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
154 {
155     PIRP Irp;
156     KEVENT Event;
157     NTSTATUS Status;
158     PLIST_ENTRY NextEntry;
159     PFILE_OBJECT FileObject;
160     PDEVICE_OBJECT DeviceObject;
161     LARGE_INTEGER StartingOffset;
162     IO_STATUS_BLOCK IoStatusBlock;
163     PULONG PartitionBuffer = NULL;
164     CHAR Buffer[128], ArcBuffer[128];
165     BOOLEAN NotEnabledPresent = FALSE;
166     STORAGE_DEVICE_NUMBER DeviceNumber;
167     ANSI_STRING DeviceStringA, ArcNameStringA;
168     PWSTR SymbolicLinkList, lSymbolicLinkList;
169     PARC_DISK_SIGNATURE ArcDiskSignature = NULL;
170     UNICODE_STRING DeviceStringW, ArcNameStringW;
171     ULONG DiskNumber, CdRomCount, CheckSum, i, EnabledDisks = 0;
172     PARC_DISK_INFORMATION ArcDiskInformation = LoaderBlock->ArcDiskInformation;
173 
174     /* Get all the Cds present in the system */
175     CdRomCount = IoGetConfigurationInformation()->CdRomCount;
176 
177     /* Get enabled Cds and check if result matches
178      * For the record, enabled Cds (or even disk) are Cds/disks
179      * that have been successfully handled by MountMgr driver
180      * and that already own their device name. This is the "new" way
181      * to handle them, that came with NT5.
182      * Currently, Windows 2003 provides an arc names creation based
183      * on both enabled drives and not enabled drives (lack from
184      * the driver).
185      * Given the current ReactOS state, that's good for us.
186      * To sum up, this is NOT a hack or whatsoever.
187      */
188     Status = IopFetchConfigurationInformation(&SymbolicLinkList,
189                                               GUID_DEVINTERFACE_CDROM,
190                                               CdRomCount,
191                                               &EnabledDisks);
192     if (!NT_SUCCESS(Status))
193     {
194         NotEnabledPresent = TRUE;
195     }
196     /* Save symbolic link list address in order to free it after */
197     lSymbolicLinkList = SymbolicLinkList;
198     /* For the moment, we won't fail */
199     Status = STATUS_SUCCESS;
200 
201     /* Browse all the ARC devices trying to find the one matching boot device */
202     for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
203          NextEntry != &ArcDiskInformation->DiskSignatureListHead;
204          NextEntry = NextEntry->Flink)
205     {
206         ArcDiskSignature = CONTAINING_RECORD(NextEntry,
207                                              ARC_DISK_SIGNATURE,
208                                              ListEntry);
209 
210         if (strcmp(LoaderBlock->ArcBootDeviceName, ArcDiskSignature->ArcName) == 0)
211         {
212             break;
213         }
214 
215         ArcDiskSignature = NULL;
216     }
217 
218     /* Not found... Not booting from a Cd */
219     if (!ArcDiskSignature)
220     {
221         DPRINT("Failed finding a cd that could match current boot device\n");
222         goto Cleanup;
223     }
224 
225     /* Allocate needed space for reading Cd */
226     PartitionBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 2048, TAG_IO);
227     if (!PartitionBuffer)
228     {
229         DPRINT("Failed allocating resources!\n");
230         /* Here, we fail, BUT we return success, some Microsoft joke */
231         goto Cleanup;
232     }
233 
234     /* If we have more enabled Cds, take that into account */
235     if (EnabledDisks > CdRomCount)
236     {
237         CdRomCount = EnabledDisks;
238     }
239 
240     /* If we'll have to browse for none enabled Cds, fix higher count */
241     if (NotEnabledPresent && !EnabledDisks)
242     {
243         CdRomCount += 5;
244     }
245 
246     /* Finally, if in spite of all that work, we still don't have Cds, leave */
247     if (!CdRomCount)
248     {
249         goto Cleanup;
250     }
251 
252     /* Start browsing Cds */
253     for (DiskNumber = 0, EnabledDisks = 0; DiskNumber < CdRomCount; DiskNumber++)
254     {
255         /* Check if we have an enabled disk */
256         if (lSymbolicLinkList && *lSymbolicLinkList != UNICODE_NULL)
257         {
258             /* Create its device name using first symbolic link */
259             RtlInitUnicodeString(&DeviceStringW, lSymbolicLinkList);
260             /* Then, update symbolic links list */
261             lSymbolicLinkList += wcslen(lSymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
262 
263             /* Get its associated device object and file object */
264             Status = IoGetDeviceObjectPointer(&DeviceStringW,
265                                               FILE_READ_ATTRIBUTES,
266                                               &FileObject,
267                                               &DeviceObject);
268             /* Failure? Good bye! */
269             if (!NT_SUCCESS(Status))
270             {
271                 goto Cleanup;
272             }
273 
274             /* Now, we'll ask the device its device number */
275             Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
276                                                 DeviceObject,
277                                                 NULL,
278                                                 0,
279                                                 &DeviceNumber,
280                                                 sizeof(STORAGE_DEVICE_NUMBER),
281                                                 FALSE,
282                                                 &Event,
283                                                 &IoStatusBlock);
284             /* Failure? Good bye! */
285             if (!Irp)
286             {
287                 /* Dereference file object before leaving */
288                 ObDereferenceObject(FileObject);
289                 Status = STATUS_INSUFFICIENT_RESOURCES;
290                 goto Cleanup;
291             }
292 
293             /* Call the driver, and wait for it if needed */
294             KeInitializeEvent(&Event, NotificationEvent, FALSE);
295             Status = IoCallDriver(DeviceObject, Irp);
296             if (Status == STATUS_PENDING)
297             {
298                 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
299                 Status = IoStatusBlock.Status;
300             }
301             if (!NT_SUCCESS(Status))
302             {
303                 ObDereferenceObject(FileObject);
304                 goto Cleanup;
305             }
306 
307             /* Finally, build proper device name */
308             sprintf(Buffer, "\\Device\\CdRom%lu", DeviceNumber.DeviceNumber);
309             RtlInitAnsiString(&DeviceStringA, Buffer);
310             Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
311             if (!NT_SUCCESS(Status))
312             {
313                 ObDereferenceObject(FileObject);
314                 goto Cleanup;
315             }
316         }
317         else
318         {
319             /* Create device name for the cd */
320             sprintf(Buffer, "\\Device\\CdRom%lu", EnabledDisks++);
321             RtlInitAnsiString(&DeviceStringA, Buffer);
322             Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
323             if (!NT_SUCCESS(Status))
324             {
325                 goto Cleanup;
326             }
327 
328             /* Get its device object */
329             Status = IoGetDeviceObjectPointer(&DeviceStringW,
330                                               FILE_READ_ATTRIBUTES,
331                                               &FileObject,
332                                               &DeviceObject);
333             if (!NT_SUCCESS(Status))
334             {
335                 RtlFreeUnicodeString(&DeviceStringW);
336                 goto Cleanup;
337             }
338         }
339 
340         /* Initiate data for reading cd and compute checksum */
341         StartingOffset.QuadPart = 0x8000;
342         CheckSum = 0;
343         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
344                                            DeviceObject,
345                                            PartitionBuffer,
346                                            2048,
347                                            &StartingOffset,
348                                            &Event,
349                                            &IoStatusBlock);
350         if (Irp)
351         {
352             /* Call the driver, and wait for it if needed */
353             KeInitializeEvent(&Event, NotificationEvent, FALSE);
354             Status = IoCallDriver(DeviceObject, Irp);
355             if (Status == STATUS_PENDING)
356             {
357                 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
358                 Status = IoStatusBlock.Status;
359             }
360 
361             /* Reading succeed, compute checksum by adding data, 2048 bytes checksum */
362             if (NT_SUCCESS(Status))
363             {
364                 for (i = 0; i < 2048 / sizeof(ULONG); i++)
365                 {
366                     CheckSum += PartitionBuffer[i];
367                 }
368             }
369         }
370 
371         /* Dereference file object */
372         ObDereferenceObject(FileObject);
373 
374         /* If checksums are matching, we have the proper cd */
375         if (CheckSum + ArcDiskSignature->CheckSum == 0)
376         {
377             /* Create ARC name */
378             sprintf(ArcBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
379             RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
380             Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
381             if (NT_SUCCESS(Status))
382             {
383                 /* Create symbolic link */
384                 IoAssignArcName(&ArcNameStringW, &DeviceStringW);
385                 RtlFreeUnicodeString(&ArcNameStringW);
386                 DPRINT("Boot device found\n");
387             }
388 
389             /* And quit, whatever happens */
390             RtlFreeUnicodeString(&DeviceStringW);
391             goto Cleanup;
392         }
393 
394         /* Free string before trying another disk */
395         RtlFreeUnicodeString(&DeviceStringW);
396     }
397 
398 Cleanup:
399     if (PartitionBuffer)
400     {
401         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
402     }
403 
404     if (SymbolicLinkList)
405     {
406         ExFreePool(SymbolicLinkList);
407     }
408 
409     return Status;
410 }
411 
412 INIT_FUNCTION
413 NTSTATUS
414 NTAPI
415 IopCreateArcNamesDisk(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
416                       IN BOOLEAN SingleDisk,
417                       IN PBOOLEAN FoundBoot)
418 {
419     PIRP Irp;
420     PVOID Data;
421     KEVENT Event;
422     NTSTATUS Status;
423     PLIST_ENTRY NextEntry;
424     PFILE_OBJECT FileObject;
425     DISK_GEOMETRY DiskGeometry;
426     PDEVICE_OBJECT DeviceObject;
427     LARGE_INTEGER StartingOffset;
428     PULONG PartitionBuffer = NULL;
429     IO_STATUS_BLOCK IoStatusBlock;
430     CHAR Buffer[128], ArcBuffer[128];
431     BOOLEAN NotEnabledPresent = FALSE;
432     STORAGE_DEVICE_NUMBER DeviceNumber;
433     PARC_DISK_SIGNATURE ArcDiskSignature;
434     PWSTR SymbolicLinkList, lSymbolicLinkList;
435     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
436     UNICODE_STRING DeviceStringW, ArcNameStringW, HalPathStringW;
437     ULONG DiskNumber, DiskCount, CheckSum, i, Signature, EnabledDisks = 0;
438     PARC_DISK_INFORMATION ArcDiskInformation = LoaderBlock->ArcDiskInformation;
439     ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA, HalPathStringA;
440 
441     /* Initialise device number */
442     DeviceNumber.DeviceNumber = ULONG_MAX;
443     /* Get all the disks present in the system */
444     DiskCount = IoGetConfigurationInformation()->DiskCount;
445 
446     /* Get enabled disks and check if result matches */
447     Status = IopFetchConfigurationInformation(&SymbolicLinkList,
448                                               GUID_DEVINTERFACE_DISK,
449                                               DiskCount,
450                                               &EnabledDisks);
451     if (!NT_SUCCESS(Status))
452     {
453         NotEnabledPresent = TRUE;
454     }
455 
456     /* Save symbolic link list address in order to free it after */
457     lSymbolicLinkList = SymbolicLinkList;
458 
459     /* Build the boot strings */
460     RtlInitAnsiString(&ArcBootString, LoaderBlock->ArcBootDeviceName);
461     RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName);
462 
463     /* If we have more enabled disks, take that into account */
464     if (EnabledDisks > DiskCount)
465     {
466         DiskCount = EnabledDisks;
467     }
468 
469     /* If we'll have to browse for none enabled disks, fix higher count */
470     if (NotEnabledPresent && !EnabledDisks)
471     {
472         DiskCount += 20;
473     }
474 
475     /* Finally, if in spite of all that work, we still don't have disks, leave */
476     if (!DiskCount)
477     {
478         goto Cleanup;
479     }
480 
481     /* Start browsing disks */
482     for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
483     {
484         /* Check if we have an enabled disk */
485         if (lSymbolicLinkList && *lSymbolicLinkList != UNICODE_NULL)
486         {
487             /* Create its device name using first symbolic link */
488             RtlInitUnicodeString(&DeviceStringW, lSymbolicLinkList);
489             /* Then, update symbolic links list */
490             lSymbolicLinkList += wcslen(lSymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
491 
492             /* Get its associated device object and file object */
493             Status = IoGetDeviceObjectPointer(&DeviceStringW,
494                                               FILE_READ_ATTRIBUTES,
495                                               &FileObject,
496                                               &DeviceObject);
497             if (NT_SUCCESS(Status))
498             {
499                 /* Now, we'll ask the device its device number */
500                 Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
501                                                     DeviceObject,
502                                                     NULL,
503                                                     0,
504                                                     &DeviceNumber,
505                                                     sizeof(STORAGE_DEVICE_NUMBER),
506                                                     FALSE,
507                                                     &Event,
508                                                     &IoStatusBlock);
509                 /* Missing resources is a shame... No need to go farther */
510                 if (!Irp)
511                 {
512                     ObDereferenceObject(FileObject);
513                     Status = STATUS_INSUFFICIENT_RESOURCES;
514                     goto Cleanup;
515                 }
516 
517                 /* Call the driver, and wait for it if needed */
518                 KeInitializeEvent(&Event, NotificationEvent, FALSE);
519                 Status = IoCallDriver(DeviceObject, Irp);
520                 if (Status == STATUS_PENDING)
521                 {
522                     KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
523                     Status = IoStatusBlock.Status;
524                 }
525 
526                 /* If we didn't get the appriopriate data, just skip that disk */
527                 if (!NT_SUCCESS(Status))
528                 {
529                    ObDereferenceObject(FileObject);
530                    continue;
531                 }
532             }
533 
534             /* End of enabled disks enumeration */
535             if (NotEnabledPresent && *lSymbolicLinkList == UNICODE_NULL)
536             {
537                 /* No enabled disk worked, reset field */
538                 if (DeviceNumber.DeviceNumber == ULONG_MAX)
539                 {
540                     DeviceNumber.DeviceNumber = 0;
541                 }
542 
543                 /* Update disk number to enable the following not enabled disks */
544                 if (DeviceNumber.DeviceNumber > DiskNumber)
545                 {
546                     DiskNumber = DeviceNumber.DeviceNumber;
547                 }
548 
549                 /* Increase a bit more */
550                 DiskCount = DiskNumber + 20;
551             }
552         }
553         else
554         {
555             /* Create device name for the disk */
556             sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
557             RtlInitAnsiString(&DeviceStringA, Buffer);
558             Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
559             if (!NT_SUCCESS(Status))
560             {
561                 goto Cleanup;
562             }
563 
564             /* Get its device object */
565             Status = IoGetDeviceObjectPointer(&DeviceStringW,
566                                               FILE_READ_ATTRIBUTES,
567                                               &FileObject,
568                                               &DeviceObject);
569 
570             RtlFreeUnicodeString(&DeviceStringW);
571             /* This is a security measure, to ensure DiskNumber will be used */
572             DeviceNumber.DeviceNumber = ULONG_MAX;
573         }
574 
575         /* Something failed somewhere earlier, just skip the disk */
576         if (!NT_SUCCESS(Status))
577         {
578             continue;
579         }
580 
581         /* Let's ask the disk for its geometry */
582         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
583                                             DeviceObject,
584                                             NULL,
585                                             0,
586                                             &DiskGeometry,
587                                             sizeof(DISK_GEOMETRY),
588                                             FALSE,
589                                             &Event,
590                                             &IoStatusBlock);
591         /* Missing resources is a shame... No need to go farther */
592         if (!Irp)
593         {
594             ObDereferenceObject(FileObject);
595             Status = STATUS_INSUFFICIENT_RESOURCES;
596             goto Cleanup;
597         }
598 
599         /* Call the driver, and wait for it if needed */
600         KeInitializeEvent(&Event, NotificationEvent, FALSE);
601         Status = IoCallDriver(DeviceObject, Irp);
602         if (Status == STATUS_PENDING)
603         {
604             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
605             Status = IoStatusBlock.Status;
606         }
607         /* Failure, skip disk */
608         if (!NT_SUCCESS(Status))
609         {
610             ObDereferenceObject(FileObject);
611             continue;
612         }
613 
614         /* Read the partition table */
615         Status = IoReadPartitionTableEx(DeviceObject,
616                                         &DriveLayout);
617         if (!NT_SUCCESS(Status))
618         {
619             ObDereferenceObject(FileObject);
620             continue;
621         }
622 
623         /* Ensure we have at least 512 bytes per sector */
624         if (DiskGeometry.BytesPerSector < 512)
625         {
626             DiskGeometry.BytesPerSector = 512;
627         }
628 
629         /* Check MBR type against EZ Drive type */
630         StartingOffset.QuadPart = 0;
631         HalExamineMBR(DeviceObject, DiskGeometry.BytesPerSector, 0x55, &Data);
632         if (Data)
633         {
634             /* If MBR is of the EZ Drive type, we'll read after it */
635             StartingOffset.QuadPart = DiskGeometry.BytesPerSector;
636             ExFreePool(Data);
637         }
638 
639         /* Allocate for reading enough data for checksum */
640         PartitionBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, DiskGeometry.BytesPerSector, TAG_IO);
641         if (!PartitionBuffer)
642         {
643             ObDereferenceObject(FileObject);
644             Status = STATUS_INSUFFICIENT_RESOURCES;
645             goto Cleanup;
646         }
647 
648         /* Read a sector for computing checksum */
649         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
650                                            DeviceObject,
651                                            PartitionBuffer,
652                                            DiskGeometry.BytesPerSector,
653                                            &StartingOffset,
654                                            &Event,
655                                            &IoStatusBlock);
656         if (!Irp)
657         {
658             ObDereferenceObject(FileObject);
659             Status = STATUS_INSUFFICIENT_RESOURCES;
660             goto Cleanup;
661         }
662 
663         /* Call the driver to perform reading */
664         KeInitializeEvent(&Event, NotificationEvent, FALSE);
665         Status = IoCallDriver(DeviceObject, Irp);
666         if (Status == STATUS_PENDING)
667         {
668             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
669             Status = IoStatusBlock.Status;
670         }
671         if (!NT_SUCCESS(Status))
672         {
673             ExFreePool(DriveLayout);
674             ExFreePoolWithTag(PartitionBuffer, TAG_IO);
675             ObDereferenceObject(FileObject);
676             continue;
677         }
678 
679         ObDereferenceObject(FileObject);
680 
681         /* Calculate checksum, that's an easy computation, just adds read data */
682         for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
683         {
684             CheckSum += PartitionBuffer[i];
685         }
686 
687         /* Browse each ARC disk */
688         for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
689              NextEntry != &ArcDiskInformation->DiskSignatureListHead;
690              NextEntry = NextEntry->Flink)
691         {
692             ArcDiskSignature = CONTAINING_RECORD(NextEntry,
693                                                  ARC_DISK_SIGNATURE,
694                                                  ListEntry);
695 
696             /* If they matches, ie
697              * - There's only one disk for both BIOS and detected/enabled
698              * - Signatures are matching
699              * - Checksums are matching
700              * - This is MBR
701              */
702             if (((SingleDisk && DiskCount == 1) ||
703                 (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature) &&
704                  (ArcDiskSignature->CheckSum + CheckSum == 0))) &&
705                 (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
706             {
707                 /* Create device name */
708                 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", (DeviceNumber.DeviceNumber != ULONG_MAX) ? DeviceNumber.DeviceNumber : DiskNumber);
709                 RtlInitAnsiString(&DeviceStringA, Buffer);
710                 Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
711                 if (!NT_SUCCESS(Status))
712                 {
713                     goto Cleanup;
714                 }
715 
716                 /* Create ARC name */
717                 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
718                 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
719                 Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
720                 if (!NT_SUCCESS(Status))
721                 {
722                     RtlFreeUnicodeString(&DeviceStringW);
723                     goto Cleanup;
724                 }
725 
726                 /* Link both */
727                 IoAssignArcName(&ArcNameStringW, &DeviceStringW);
728 
729                 /* And release resources */
730                 RtlFreeUnicodeString(&ArcNameStringW);
731                 RtlFreeUnicodeString(&DeviceStringW);
732 
733                 /* Now, browse for every partition */
734                 for (i = 1; i <= DriveLayout->PartitionCount; i++)
735                 {
736                     /* Create device name */
737                     sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", (DeviceNumber.DeviceNumber != ULONG_MAX) ? DeviceNumber.DeviceNumber : DiskNumber, i);
738                     RtlInitAnsiString(&DeviceStringA, Buffer);
739                     Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
740                     if (!NT_SUCCESS(Status))
741                     {
742                         goto Cleanup;
743                     }
744 
745                     /* Create partial ARC name */
746                     sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, i);
747                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
748 
749                     /* Is that boot device? */
750                     if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
751                     {
752                         DPRINT("Found boot device\n");
753                         *FoundBoot = TRUE;
754                     }
755 
756                     /* Is that system partition? */
757                     if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
758                     {
759                         /* Create HAL path name */
760                         RtlInitAnsiString(&HalPathStringA, LoaderBlock->NtHalPathName);
761                         Status = RtlAnsiStringToUnicodeString(&HalPathStringW, &HalPathStringA, TRUE);
762                         if (!NT_SUCCESS(Status))
763                         {
764                             RtlFreeUnicodeString(&DeviceStringW);
765                             goto Cleanup;
766                         }
767 
768                         /* Then store those information to registry */
769                         IopStoreSystemPartitionInformation(&DeviceStringW, &HalPathStringW);
770                         RtlFreeUnicodeString(&HalPathStringW);
771                     }
772 
773                     /* Create complete ARC name */
774                     sprintf(ArcBuffer, "\\ArcName\\%spartition(%lu)", ArcDiskSignature->ArcName, i);
775                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
776                     Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
777                     if (!NT_SUCCESS(Status))
778                     {
779                         RtlFreeUnicodeString(&DeviceStringW);
780                         goto Cleanup;
781                     }
782 
783                     /* Link device name & ARC name */
784                     IoAssignArcName(&ArcNameStringW, &DeviceStringW);
785 
786                     /* Release strings */
787                     RtlFreeUnicodeString(&ArcNameStringW);
788                     RtlFreeUnicodeString(&DeviceStringW);
789                 }
790             }
791             else
792             {
793                 /* In case there's a valid partition, a matching signature,
794                    BUT a none matching checksum, or there's a duplicate
795                    signature, or even worse a virus played with partition
796                    table */
797                 if (ArcDiskSignature->Signature == Signature &&
798                     (ArcDiskSignature->CheckSum + CheckSum != 0) &&
799                     ArcDiskSignature->ValidPartitionTable)
800                  {
801                      DPRINT("Be careful, or you have a duplicate disk signature, or a virus altered your MBR!\n");
802                  }
803             }
804         }
805 
806         /* Release memory before jumping to next item */
807         ExFreePool(DriveLayout);
808         DriveLayout = NULL;
809         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
810         PartitionBuffer = NULL;
811     }
812 
813     Status = STATUS_SUCCESS;
814 
815 Cleanup:
816     if (SymbolicLinkList)
817     {
818         ExFreePool(SymbolicLinkList);
819     }
820 
821     if (DriveLayout)
822     {
823         ExFreePool(DriveLayout);
824     }
825 
826     if (PartitionBuffer)
827     {
828         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
829     }
830 
831     return Status;
832 }
833 
834 INIT_FUNCTION
835 NTSTATUS
836 NTAPI
837 IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
838                       OUT PANSI_STRING NtBootPath)
839 {
840     OBJECT_ATTRIBUTES ObjectAttributes;
841     NTSTATUS Status;
842     CHAR Buffer[256], AnsiBuffer[256];
843     WCHAR ArcNameBuffer[64];
844     ANSI_STRING TargetString, ArcString, TempString;
845     UNICODE_STRING LinkName, TargetName, ArcName;
846     HANDLE LinkHandle;
847 
848     /* Create the Unicode name for the current ARC boot device */
849     sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
850     RtlInitAnsiString(&TargetString, Buffer);
851     Status = RtlAnsiStringToUnicodeString(&TargetName, &TargetString, TRUE);
852     if (!NT_SUCCESS(Status)) return FALSE;
853 
854     /* Initialize the attributes and open the link */
855     InitializeObjectAttributes(&ObjectAttributes,
856                                &TargetName,
857                                OBJ_CASE_INSENSITIVE,
858                                NULL,
859                                NULL);
860     Status = NtOpenSymbolicLinkObject(&LinkHandle,
861                                       SYMBOLIC_LINK_ALL_ACCESS,
862                                       &ObjectAttributes);
863     if (!NT_SUCCESS(Status))
864     {
865         /* We failed, free the string */
866         RtlFreeUnicodeString(&TargetName);
867         return FALSE;
868     }
869 
870     /* Query the current \\SystemRoot */
871     ArcName.Buffer = ArcNameBuffer;
872     ArcName.Length = 0;
873     ArcName.MaximumLength = sizeof(ArcNameBuffer);
874     Status = NtQuerySymbolicLinkObject(LinkHandle, &ArcName, NULL);
875     if (!NT_SUCCESS(Status))
876     {
877         /* We failed, free the string */
878         RtlFreeUnicodeString(&TargetName);
879         return FALSE;
880     }
881 
882     /* Convert it to Ansi */
883     ArcString.Buffer = AnsiBuffer;
884     ArcString.Length = 0;
885     ArcString.MaximumLength = sizeof(AnsiBuffer);
886     Status = RtlUnicodeStringToAnsiString(&ArcString, &ArcName, FALSE);
887     AnsiBuffer[ArcString.Length] = ANSI_NULL;
888 
889     /* Close the link handle and free the name */
890     ObCloseHandle(LinkHandle, KernelMode);
891     RtlFreeUnicodeString(&TargetName);
892 
893     /* Setup the system root name again */
894     RtlInitAnsiString(&TempString, "\\SystemRoot");
895     Status = RtlAnsiStringToUnicodeString(&LinkName, &TempString, TRUE);
896     if (!NT_SUCCESS(Status)) return FALSE;
897 
898     /* Open the symbolic link for it */
899     InitializeObjectAttributes(&ObjectAttributes,
900                                &LinkName,
901                                OBJ_CASE_INSENSITIVE,
902                                NULL,
903                                NULL);
904     Status = NtOpenSymbolicLinkObject(&LinkHandle,
905                                       SYMBOLIC_LINK_ALL_ACCESS,
906                                       &ObjectAttributes);
907     if (!NT_SUCCESS(Status)) return FALSE;
908 
909     /* Destroy it */
910     NtMakeTemporaryObject(LinkHandle);
911     ObCloseHandle(LinkHandle, KernelMode);
912 
913     /* Now create the new name for it */
914     sprintf(Buffer, "%s%s", ArcString.Buffer, LoaderBlock->NtBootPathName);
915 
916     /* Copy it into the passed parameter and null-terminate it */
917     RtlCopyString(NtBootPath, &ArcString);
918     Buffer[strlen(Buffer) - 1] = ANSI_NULL;
919 
920     /* Setup the Unicode-name for the new symbolic link value */
921     RtlInitAnsiString(&TargetString, Buffer);
922     InitializeObjectAttributes(&ObjectAttributes,
923                                &LinkName,
924                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
925                                NULL,
926                                NULL);
927     Status = RtlAnsiStringToUnicodeString(&ArcName, &TargetString, TRUE);
928     if (!NT_SUCCESS(Status)) return FALSE;
929 
930     /* Create it */
931     Status = NtCreateSymbolicLinkObject(&LinkHandle,
932                                         SYMBOLIC_LINK_ALL_ACCESS,
933                                         &ObjectAttributes,
934                                         &ArcName);
935 
936     /* Free all the strings and close the handle and return success */
937     RtlFreeUnicodeString(&ArcName);
938     RtlFreeUnicodeString(&LinkName);
939     ObCloseHandle(LinkHandle, KernelMode);
940     return TRUE;
941 }
942 
943 BOOLEAN
944 NTAPI
945 IopVerifyDiskSignature(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout,
946                        IN PARC_DISK_SIGNATURE ArcDiskSignature,
947                        OUT PULONG Signature)
948 {
949     /* First condition: having a valid partition table */
950     if (!ArcDiskSignature->ValidPartitionTable)
951     {
952         return FALSE;
953     }
954 
955     /* If that partition table is the MBR */
956     if (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR)
957     {
958         /* Then check MBR signature */
959         if (DriveLayout->Mbr.Signature == ArcDiskSignature->Signature)
960         {
961             /* And return it */
962             if (Signature)
963             {
964                 *Signature = DriveLayout->Mbr.Signature;
965             }
966 
967             return TRUE;
968         }
969     }
970     /* If that partition table is the GPT */
971     else if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
972     {
973         /* Check we are using GPT and compare GUID */
974         if (ArcDiskSignature->IsGpt &&
975             (((PULONG)ArcDiskSignature->GptSignature)[0] == DriveLayout->Gpt.DiskId.Data1 &&
976              ((PUSHORT)ArcDiskSignature->GptSignature)[2] == DriveLayout->Gpt.DiskId.Data2 &&
977              ((PUSHORT)ArcDiskSignature->GptSignature)[3] == DriveLayout->Gpt.DiskId.Data3 &&
978              ((PULONGLONG)ArcDiskSignature->GptSignature)[1] == ((PULONGLONG)DriveLayout->Gpt.DiskId.Data4)[0]))
979         {
980             /* There's no signature to give, so we just zero output */
981             if (Signature)
982             {
983                 *Signature = 0;
984             }
985             return TRUE;
986         }
987     }
988 
989     /* If we fall here, it means that something went wrong, so return that */
990     return FALSE;
991 }
992 
993 /* EOF */
994