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