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