xref: /reactos/ntoskrnl/io/iomgr/arcname.c (revision 139a3d66)
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 INIT_FUNCTION
25 NTSTATUS
26 NTAPI
27 IopCreateArcNamesCd(IN PLOADER_PARAMETER_BLOCK LoaderBlock
28 );
29 
30 INIT_FUNCTION
31 NTSTATUS
32 NTAPI
33 IopCreateArcNamesDisk(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
34                       IN BOOLEAN SingleDisk,
35                       IN PBOOLEAN FoundBoot
36 );
37 
38 INIT_FUNCTION
39 NTSTATUS
40 NTAPI
41 IopCreateArcNames(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
42 {
43     SIZE_T Length;
44     NTSTATUS Status;
45     CHAR Buffer[128];
46     BOOLEAN SingleDisk;
47     BOOLEAN FoundBoot = FALSE;
48     UNICODE_STRING SystemDevice, LoaderPathNameW, BootDeviceName;
49     PARC_DISK_INFORMATION ArcDiskInfo = LoaderBlock->ArcDiskInformation;
50     ANSI_STRING ArcSystemString, ArcString, LanmanRedirector, LoaderPathNameA;
51 
52     /* Check if we only have one disk on the machine */
53     SingleDisk = ArcDiskInfo->DiskSignatureListHead.Flink->Flink ==
54                  (&ArcDiskInfo->DiskSignatureListHead);
55 
56     /* Create the global HAL partition name */
57     sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName);
58     RtlInitAnsiString(&ArcString, Buffer);
59     RtlAnsiStringToUnicodeString(&IoArcHalDeviceName, &ArcString, TRUE);
60 
61     /* Create the global system partition name */
62     sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
63     RtlInitAnsiString(&ArcString, Buffer);
64     RtlAnsiStringToUnicodeString(&IoArcBootDeviceName, &ArcString, TRUE);
65 
66     /* Allocate memory for the string */
67     Length = strlen(LoaderBlock->ArcBootDeviceName) + sizeof(ANSI_NULL);
68     IoLoaderArcBootDeviceName = ExAllocatePoolWithTag(PagedPool,
69                                                       Length,
70                                                       TAG_IO);
71     if (IoLoaderArcBootDeviceName)
72     {
73         /* Copy the name */
74         RtlCopyMemory(IoLoaderArcBootDeviceName,
75                       LoaderBlock->ArcBootDeviceName,
76                       Length);
77     }
78 
79     /* Check if we only found a disk, but we're booting from CD-ROM */
80     if ((SingleDisk) && strstr(LoaderBlock->ArcBootDeviceName, "cdrom"))
81     {
82         /* Then disable single-disk mode, since there's a CD drive out there */
83         SingleDisk = FALSE;
84     }
85 
86     /* Build the boot strings */
87     RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName);
88 
89     /* If we are doing remote booting */
90     if (IoRemoteBootClient)
91     {
92         /* Yes, we have found boot device */
93         FoundBoot = TRUE;
94 
95         /* Get NT device name */
96         RtlInitAnsiString(&LanmanRedirector, "\\Device\\LanmanRedirector");
97         Status = RtlAnsiStringToUnicodeString(&SystemDevice, &LanmanRedirector, TRUE);
98         if (!NT_SUCCESS(Status))
99         {
100             return Status;
101         }
102 
103         /* Get ARC booting device name (in net(0) something) */
104         sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
105         RtlInitAnsiString(&ArcString, Buffer);
106         Status = RtlAnsiStringToUnicodeString(&BootDeviceName, &ArcString, TRUE);
107         if (NT_SUCCESS(Status))
108         {
109             /* Map ARC to NT name */
110             IoAssignArcName(&BootDeviceName, &SystemDevice);
111             RtlFreeUnicodeString(&BootDeviceName);
112 
113             /* Now, get loader path name */
114             RtlInitAnsiString(&LoaderPathNameA, LoaderBlock->NtHalPathName);
115             Status = RtlAnsiStringToUnicodeString(&LoaderPathNameW, &LoaderPathNameA, TRUE);
116             if (!NT_SUCCESS(Status))
117             {
118                 RtlFreeUnicodeString(&SystemDevice);
119                 return Status;
120             }
121 
122             /* And set it has system partition */
123             IopStoreSystemPartitionInformation(&SystemDevice, &LoaderPathNameW);
124         }
125 
126         RtlFreeUnicodeString(&SystemDevice);
127 
128         /* Don't quit here, even if everything went fine!
129          * We need IopCreateArcNamesDisk to properly map
130          * devices with symlinks.
131          * It will return success if the mapping process went fine
132          * even if it didn't find boot device.
133          * It won't reset boot device finding status as well.
134          */
135     }
136 
137     /* Loop every disk and try to find boot disk */
138     Status = IopCreateArcNamesDisk(LoaderBlock, SingleDisk, &FoundBoot);
139     /* If it succeed but we didn't find boot device, try to browse Cds */
140     if (NT_SUCCESS(Status) && !FoundBoot)
141     {
142         Status = IopCreateArcNamesCd(LoaderBlock);
143     }
144 
145     /* Return success */
146     return Status;
147 }
148 
149 INIT_FUNCTION
150 NTSTATUS
151 NTAPI
152 IopCreateArcNamesCd(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
153 {
154     PIRP Irp;
155     KEVENT Event;
156     NTSTATUS Status;
157     PLIST_ENTRY NextEntry;
158     PFILE_OBJECT FileObject;
159     PDEVICE_OBJECT DeviceObject;
160     LARGE_INTEGER StartingOffset;
161     IO_STATUS_BLOCK IoStatusBlock;
162     PULONG PartitionBuffer = NULL;
163     CHAR Buffer[128], ArcBuffer[128];
164     BOOLEAN NotEnabledPresent = FALSE;
165     STORAGE_DEVICE_NUMBER DeviceNumber;
166     ANSI_STRING DeviceStringA, ArcNameStringA;
167     PWSTR SymbolicLinkList, lSymbolicLinkList;
168     PARC_DISK_SIGNATURE ArcDiskSignature = NULL;
169     UNICODE_STRING DeviceStringW, ArcNameStringW;
170     ULONG DiskNumber, CdRomCount, CheckSum, i, EnabledDisks = 0;
171     PARC_DISK_INFORMATION ArcDiskInformation = LoaderBlock->ArcDiskInformation;
172 
173     /* Get all the Cds present in the system */
174     CdRomCount = IoGetConfigurationInformation()->CdRomCount;
175 
176     /* Get enabled Cds and check if result matches
177      * For the record, enabled Cds (or even disk) are Cds/disks
178      * that have been successfully handled by MountMgr driver
179      * and that already own their device name. This is the "new" way
180      * to handle them, that came with NT5.
181      * Currently, Windows 2003 provides an arc names creation based
182      * on both enabled drives and not enabled drives (lack from
183      * the driver).
184      * Given the current ReactOS state, that's good for us.
185      * To sum up, this is NOT a hack or whatsoever.
186      */
187     Status = IopFetchConfigurationInformation(&SymbolicLinkList,
188                                               GUID_DEVINTERFACE_CDROM,
189                                               CdRomCount,
190                                               &EnabledDisks);
191     if (!NT_SUCCESS(Status))
192     {
193         NotEnabledPresent = TRUE;
194     }
195     /* Save symbolic link list address in order to free it after */
196     lSymbolicLinkList = SymbolicLinkList;
197     /* For the moment, we won't fail */
198     Status = STATUS_SUCCESS;
199 
200     /* Browse all the ARC devices trying to find the one matching boot device */
201     for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
202          NextEntry != &ArcDiskInformation->DiskSignatureListHead;
203          NextEntry = NextEntry->Flink)
204     {
205         ArcDiskSignature = CONTAINING_RECORD(NextEntry,
206                                              ARC_DISK_SIGNATURE,
207                                              ListEntry);
208 
209         if (strcmp(LoaderBlock->ArcBootDeviceName, ArcDiskSignature->ArcName) == 0)
210         {
211             break;
212         }
213 
214         ArcDiskSignature = NULL;
215     }
216 
217     /* Not found... Not booting from a Cd */
218     if (!ArcDiskSignature)
219     {
220         DPRINT("Failed finding a cd that could match current boot device\n");
221         goto Cleanup;
222     }
223 
224     /* Allocate needed space for reading Cd */
225     PartitionBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 2048, TAG_IO);
226     if (!PartitionBuffer)
227     {
228         DPRINT("Failed allocating resources!\n");
229         /* Here, we fail, BUT we return success, some Microsoft joke */
230         goto Cleanup;
231     }
232 
233     /* If we have more enabled Cds, take that into account */
234     if (EnabledDisks > CdRomCount)
235     {
236         CdRomCount = EnabledDisks;
237     }
238 
239     /* If we'll have to browse for none enabled Cds, fix higher count */
240     if (NotEnabledPresent && !EnabledDisks)
241     {
242         CdRomCount += 5;
243     }
244 
245     /* Finally, if in spite of all that work, we still don't have Cds, leave */
246     if (!CdRomCount)
247     {
248         goto Cleanup;
249     }
250 
251     /* Start browsing Cds */
252     for (DiskNumber = 0, EnabledDisks = 0; DiskNumber < CdRomCount; DiskNumber++)
253     {
254         /* Check if we have an enabled disk */
255         if (lSymbolicLinkList && *lSymbolicLinkList != UNICODE_NULL)
256         {
257             /* Create its device name using first symbolic link */
258             RtlInitUnicodeString(&DeviceStringW, lSymbolicLinkList);
259             /* Then, update symbolic links list */
260             lSymbolicLinkList += wcslen(lSymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
261 
262             /* Get its associated device object and file object */
263             Status = IoGetDeviceObjectPointer(&DeviceStringW,
264                                               FILE_READ_ATTRIBUTES,
265                                               &FileObject,
266                                               &DeviceObject);
267             /* Failure? Good bye! */
268             if (!NT_SUCCESS(Status))
269             {
270                 goto Cleanup;
271             }
272 
273             /* Now, we'll ask the device its device number */
274             Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
275                                                 DeviceObject,
276                                                 NULL,
277                                                 0,
278                                                 &DeviceNumber,
279                                                 sizeof(STORAGE_DEVICE_NUMBER),
280                                                 FALSE,
281                                                 &Event,
282                                                 &IoStatusBlock);
283             /* Failure? Good bye! */
284             if (!Irp)
285             {
286                 /* Dereference file object before leaving */
287                 ObDereferenceObject(FileObject);
288                 Status = STATUS_INSUFFICIENT_RESOURCES;
289                 goto Cleanup;
290             }
291 
292             /* Call the driver, and wait for it if needed */
293             KeInitializeEvent(&Event, NotificationEvent, FALSE);
294             Status = IoCallDriver(DeviceObject, Irp);
295             if (Status == STATUS_PENDING)
296             {
297                 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
298                 Status = IoStatusBlock.Status;
299             }
300             if (!NT_SUCCESS(Status))
301             {
302                 ObDereferenceObject(FileObject);
303                 goto Cleanup;
304             }
305 
306             /* Finally, build proper device name */
307             sprintf(Buffer, "\\Device\\CdRom%lu", DeviceNumber.DeviceNumber);
308             RtlInitAnsiString(&DeviceStringA, Buffer);
309             Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
310             if (!NT_SUCCESS(Status))
311             {
312                 ObDereferenceObject(FileObject);
313                 goto Cleanup;
314             }
315         }
316         else
317         {
318             /* Create device name for the cd */
319             sprintf(Buffer, "\\Device\\CdRom%lu", EnabledDisks++);
320             RtlInitAnsiString(&DeviceStringA, Buffer);
321             Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
322             if (!NT_SUCCESS(Status))
323             {
324                 goto Cleanup;
325             }
326 
327             /* Get its device object */
328             Status = IoGetDeviceObjectPointer(&DeviceStringW,
329                                               FILE_READ_ATTRIBUTES,
330                                               &FileObject,
331                                               &DeviceObject);
332             if (!NT_SUCCESS(Status))
333             {
334                 RtlFreeUnicodeString(&DeviceStringW);
335                 goto Cleanup;
336             }
337         }
338 
339         /* Initiate data for reading cd and compute checksum */
340         StartingOffset.QuadPart = 0x8000;
341         CheckSum = 0;
342         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
343                                            DeviceObject,
344                                            PartitionBuffer,
345                                            2048,
346                                            &StartingOffset,
347                                            &Event,
348                                            &IoStatusBlock);
349         if (Irp)
350         {
351             /* Call the driver, and wait for it if needed */
352             KeInitializeEvent(&Event, NotificationEvent, FALSE);
353             Status = IoCallDriver(DeviceObject, Irp);
354             if (Status == STATUS_PENDING)
355             {
356                 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
357                 Status = IoStatusBlock.Status;
358             }
359 
360             /* Reading succeed, compute checksum by adding data, 2048 bytes checksum */
361             if (NT_SUCCESS(Status))
362             {
363                 for (i = 0; i < 2048 / sizeof(ULONG); i++)
364                 {
365                     CheckSum += PartitionBuffer[i];
366                 }
367             }
368         }
369 
370         /* Dereference file object */
371         ObDereferenceObject(FileObject);
372 
373         /* If checksums are matching, we have the proper cd */
374         if (CheckSum + ArcDiskSignature->CheckSum == 0)
375         {
376             /* Create ARC name */
377             sprintf(ArcBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
378             RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
379             Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
380             if (NT_SUCCESS(Status))
381             {
382                 /* Create symbolic link */
383                 IoAssignArcName(&ArcNameStringW, &DeviceStringW);
384                 RtlFreeUnicodeString(&ArcNameStringW);
385                 DPRINT("Boot device found\n");
386             }
387 
388             /* And quit, whatever happens */
389             RtlFreeUnicodeString(&DeviceStringW);
390             goto Cleanup;
391         }
392 
393         /* Free string before trying another disk */
394         RtlFreeUnicodeString(&DeviceStringW);
395     }
396 
397 Cleanup:
398     if (PartitionBuffer)
399     {
400         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
401     }
402 
403     if (SymbolicLinkList)
404     {
405         ExFreePool(SymbolicLinkList);
406     }
407 
408     return Status;
409 }
410 
411 INIT_FUNCTION
412 NTSTATUS
413 NTAPI
414 IopCreateArcNamesDisk(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
415                       IN BOOLEAN SingleDisk,
416                       IN PBOOLEAN FoundBoot)
417 {
418     PIRP Irp;
419     PVOID Data;
420     KEVENT Event;
421     NTSTATUS Status;
422     PLIST_ENTRY NextEntry;
423     PFILE_OBJECT FileObject;
424     DISK_GEOMETRY DiskGeometry;
425     PDEVICE_OBJECT DeviceObject;
426     LARGE_INTEGER StartingOffset;
427     PULONG PartitionBuffer = NULL;
428     IO_STATUS_BLOCK IoStatusBlock;
429     CHAR Buffer[128], ArcBuffer[128];
430     BOOLEAN NotEnabledPresent = FALSE;
431     STORAGE_DEVICE_NUMBER DeviceNumber;
432     PARC_DISK_SIGNATURE ArcDiskSignature;
433     PWSTR SymbolicLinkList, lSymbolicLinkList;
434     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
435     UNICODE_STRING DeviceStringW, ArcNameStringW, HalPathStringW;
436     ULONG DiskNumber, DiskCount, CheckSum, i, Signature, EnabledDisks = 0;
437     PARC_DISK_INFORMATION ArcDiskInformation = LoaderBlock->ArcDiskInformation;
438     ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA, HalPathStringA;
439 
440     /* Initialise device number */
441     DeviceNumber.DeviceNumber = ULONG_MAX;
442     /* Get all the disks present in the system */
443     DiskCount = IoGetConfigurationInformation()->DiskCount;
444 
445     /* Get enabled disks and check if result matches */
446     Status = IopFetchConfigurationInformation(&SymbolicLinkList,
447                                               GUID_DEVINTERFACE_DISK,
448                                               DiskCount,
449                                               &EnabledDisks);
450     if (!NT_SUCCESS(Status))
451     {
452         NotEnabledPresent = TRUE;
453     }
454 
455     /* Save symbolic link list address in order to free it after */
456     lSymbolicLinkList = SymbolicLinkList;
457 
458     /* Build the boot strings */
459     RtlInitAnsiString(&ArcBootString, LoaderBlock->ArcBootDeviceName);
460     RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName);
461 
462     /* If we have more enabled disks, take that into account */
463     if (EnabledDisks > DiskCount)
464     {
465         DiskCount = EnabledDisks;
466     }
467 
468     /* If we'll have to browse for none enabled disks, fix higher count */
469     if (NotEnabledPresent && !EnabledDisks)
470     {
471         DiskCount += 20;
472     }
473 
474     /* Finally, if in spite of all that work, we still don't have disks, leave */
475     if (!DiskCount)
476     {
477         goto Cleanup;
478     }
479 
480     /* Start browsing disks */
481     for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
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(STORAGE_DEVICE_NUMBER),
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(DISK_GEOMETRY),
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 a 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             ObDereferenceObject(FileObject);
658             Status = STATUS_INSUFFICIENT_RESOURCES;
659             goto Cleanup;
660         }
661 
662         /* Call the driver to perform reading */
663         KeInitializeEvent(&Event, NotificationEvent, FALSE);
664         Status = IoCallDriver(DeviceObject, Irp);
665         if (Status == STATUS_PENDING)
666         {
667             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
668             Status = IoStatusBlock.Status;
669         }
670         if (!NT_SUCCESS(Status))
671         {
672             ExFreePool(DriveLayout);
673             ExFreePoolWithTag(PartitionBuffer, TAG_IO);
674             ObDereferenceObject(FileObject);
675             continue;
676         }
677 
678         ObDereferenceObject(FileObject);
679 
680         /* Calculate checksum, that's an easy computation, just adds read data */
681         for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
682         {
683             CheckSum += PartitionBuffer[i];
684         }
685 
686         /* Browse each ARC disk */
687         for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
688              NextEntry != &ArcDiskInformation->DiskSignatureListHead;
689              NextEntry = NextEntry->Flink)
690         {
691             ArcDiskSignature = CONTAINING_RECORD(NextEntry,
692                                                  ARC_DISK_SIGNATURE,
693                                                  ListEntry);
694 
695             /* If they matches, ie
696              * - There's only one disk for both BIOS and detected/enabled
697              * - Signatures are matching
698              * - Checksums are matching
699              * - This is MBR
700              */
701             if (((SingleDisk && DiskCount == 1) ||
702                 (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature) &&
703                  (ArcDiskSignature->CheckSum + CheckSum == 0))) &&
704                 (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
705             {
706                 /* Create device name */
707                 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", (DeviceNumber.DeviceNumber != ULONG_MAX) ? DeviceNumber.DeviceNumber : DiskNumber);
708                 RtlInitAnsiString(&DeviceStringA, Buffer);
709                 Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
710                 if (!NT_SUCCESS(Status))
711                 {
712                     goto Cleanup;
713                 }
714 
715                 /* Create ARC name */
716                 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
717                 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
718                 Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
719                 if (!NT_SUCCESS(Status))
720                 {
721                     RtlFreeUnicodeString(&DeviceStringW);
722                     goto Cleanup;
723                 }
724 
725                 /* Link both */
726                 IoAssignArcName(&ArcNameStringW, &DeviceStringW);
727 
728                 /* And release resources */
729                 RtlFreeUnicodeString(&ArcNameStringW);
730                 RtlFreeUnicodeString(&DeviceStringW);
731 
732                 /* Now, browse for every partition */
733                 for (i = 1; i <= DriveLayout->PartitionCount; i++)
734                 {
735                     /* Create device name */
736                     sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", (DeviceNumber.DeviceNumber != ULONG_MAX) ? DeviceNumber.DeviceNumber : DiskNumber, i);
737                     RtlInitAnsiString(&DeviceStringA, Buffer);
738                     Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
739                     if (!NT_SUCCESS(Status))
740                     {
741                         goto Cleanup;
742                     }
743 
744                     /* Create partial ARC name */
745                     sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, i);
746                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
747 
748                     /* Is that boot device? */
749                     if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
750                     {
751                         DPRINT("Found boot device\n");
752                         *FoundBoot = TRUE;
753                     }
754 
755                     /* Is that system partition? */
756                     if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
757                     {
758                         /* Create HAL path name */
759                         RtlInitAnsiString(&HalPathStringA, LoaderBlock->NtHalPathName);
760                         Status = RtlAnsiStringToUnicodeString(&HalPathStringW, &HalPathStringA, TRUE);
761                         if (!NT_SUCCESS(Status))
762                         {
763                             RtlFreeUnicodeString(&DeviceStringW);
764                             goto Cleanup;
765                         }
766 
767                         /* Then store those information to registry */
768                         IopStoreSystemPartitionInformation(&DeviceStringW, &HalPathStringW);
769                         RtlFreeUnicodeString(&HalPathStringW);
770                     }
771 
772                     /* Create complete ARC name */
773                     sprintf(ArcBuffer, "\\ArcName\\%spartition(%lu)", ArcDiskSignature->ArcName, i);
774                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
775                     Status = RtlAnsiStringToUnicodeString(&ArcNameStringW, &ArcNameStringA, TRUE);
776                     if (!NT_SUCCESS(Status))
777                     {
778                         RtlFreeUnicodeString(&DeviceStringW);
779                         goto Cleanup;
780                     }
781 
782                     /* Link device name & ARC name */
783                     IoAssignArcName(&ArcNameStringW, &DeviceStringW);
784 
785                     /* Release strings */
786                     RtlFreeUnicodeString(&ArcNameStringW);
787                     RtlFreeUnicodeString(&DeviceStringW);
788                 }
789             }
790             else
791             {
792                 /* In case there's a valid partition, a matching signature,
793                    BUT a none matching checksum, or there's a duplicate
794                    signature, or even worse a virus played with partition
795                    table */
796                 if (ArcDiskSignature->Signature == Signature &&
797                     (ArcDiskSignature->CheckSum + CheckSum != 0) &&
798                     ArcDiskSignature->ValidPartitionTable)
799                  {
800                      DPRINT("Be careful, or you have a duplicate disk signature, or a virus altered your MBR!\n");
801                  }
802             }
803         }
804 
805         /* Release memory before jumping to next item */
806         ExFreePool(DriveLayout);
807         DriveLayout = NULL;
808         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
809         PartitionBuffer = NULL;
810     }
811 
812     Status = STATUS_SUCCESS;
813 
814 Cleanup:
815     if (SymbolicLinkList)
816     {
817         ExFreePool(SymbolicLinkList);
818     }
819 
820     if (DriveLayout)
821     {
822         ExFreePool(DriveLayout);
823     }
824 
825     if (PartitionBuffer)
826     {
827         ExFreePoolWithTag(PartitionBuffer, TAG_IO);
828     }
829 
830     return Status;
831 }
832 
833 INIT_FUNCTION
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