3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/io/arcname.c
6 * PURPOSE: Creates ARC names for boot devices
8 * PROGRAMMERS: Eric Kohl (ekohl@rz-online.de)
11 /* INCLUDES *****************************************************************/
17 /* GLOBALS *******************************************************************/
19 UNICODE_STRING IoArcHalDeviceName
, IoArcBootDeviceName
;
20 PCHAR IoLoaderArcBootDeviceName
;
22 /* FUNCTIONS ****************************************************************/
27 IopApplyRosCdromArcHack(IN ULONG i
)
29 ULONG DeviceNumber
= -1;
30 OBJECT_ATTRIBUTES ObjectAttributes
;
31 UNICODE_STRING DeviceName
;
32 WCHAR Buffer
[MAX_PATH
];
33 CHAR AnsiBuffer
[MAX_PATH
];
34 FILE_BASIC_INFORMATION FileInfo
;
38 /* Only ARC Name left - Build full ARC Name */
39 p
= strstr(KeLoaderBlock
->ArcBootDeviceName
, "cdrom");
42 /* Try to find the installer */
43 swprintf(Buffer
, L
"\\Device\\CdRom%lu\\reactos\\ntoskrnl.exe", i
);
44 RtlInitUnicodeString(&DeviceName
, Buffer
);
45 InitializeObjectAttributes(&ObjectAttributes
,
50 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &FileInfo
);
51 if (NT_SUCCESS(Status
)) DeviceNumber
= i
;
53 /* Try to find live CD boot */
55 L
"\\Device\\CdRom%lu\\reactos\\system32\\ntoskrnl.exe",
57 RtlInitUnicodeString(&DeviceName
, Buffer
);
58 InitializeObjectAttributes(&ObjectAttributes
,
63 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &FileInfo
);
64 if (NT_SUCCESS(Status
)) DeviceNumber
= i
;
67 sprintf(p
, "cdrom(%lu)", DeviceNumber
);
69 /* Adjust original command line */
74 strcpy(AnsiBuffer
, q
);
75 sprintf(p
, "cdrom(%lu)", DeviceNumber
);
76 strcat(p
, AnsiBuffer
);
80 /* Return whether this is the CD or not */
81 if (DeviceNumber
!= 1) return TRUE
;
88 IopGetDiskInformation(IN ULONG i
,
91 OUT PULONG PartitionCount
,
92 OUT PDEVICE_OBJECT
*DiskDeviceObject
)
95 ANSI_STRING TempString
;
97 UNICODE_STRING DeviceName
;
99 PDEVICE_OBJECT DeviceObject
;
100 PFILE_OBJECT FileObject
;
101 DISK_GEOMETRY DiskGeometry
;
102 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
105 IO_STATUS_BLOCK StatusBlock
;
106 LARGE_INTEGER PartitionOffset
;
107 PPARTITION_SECTOR PartitionBuffer
;
110 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", i
);
112 /* Convert it to Unicode */
113 RtlInitAnsiString(&TempString
, Buffer
);
114 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
115 if (!NT_SUCCESS(Status
)) return FALSE
;
117 /* Get the device pointer */
118 Status
= IoGetDeviceObjectPointer(&DeviceName
,
122 *DiskDeviceObject
= DeviceObject
;
124 /* Free the string */
125 RtlFreeUnicodeString(&DeviceName
);
127 /* Move on if we failed */
128 if (!NT_SUCCESS(Status
)) return FALSE
;
130 /* Build an IRP to determine the sector size */
131 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
132 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY
,
137 sizeof(DISK_GEOMETRY
),
144 ObDereferenceObject(FileObject
);
148 /* Call the driver and check if we have to wait on it */
149 Status
= IoCallDriver(DeviceObject
, Irp
);
150 if (Status
== STATUS_PENDING
)
152 /* Wait on the driver */
153 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
154 Status
= StatusBlock
.Status
;
157 /* Check if we failed */
158 if (!NT_SUCCESS(Status
))
161 ObDereferenceObject(FileObject
);
165 /* Read the partition table */
166 Status
= IoReadPartitionTable(DeviceObject
,
167 DiskGeometry
.BytesPerSector
,
171 /* Dereference the file object */
172 ObDereferenceObject(FileObject
);
173 if (!NT_SUCCESS(Status
)) return FALSE
;
175 /* Set the offset to 0 */
176 PartitionOffset
.QuadPart
= 0;
178 /* Allocate a buffer for the partition */
179 PartitionBuffer
= ExAllocatePoolWithTag(NonPagedPool
,
180 DiskGeometry
.BytesPerSector
,
182 if (!PartitionBuffer
) return FALSE
;
184 /* Build an IRP to read the partition sector */
185 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
186 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
189 DiskGeometry
.BytesPerSector
,
194 /* Call the driver and check if we have to wait */
195 Status
= IoCallDriver(DeviceObject
, Irp
);
196 if (Status
== STATUS_PENDING
)
198 /* Wait for completion */
199 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
200 Status
= StatusBlock
.Status
;
203 /* Check if we failed */
204 if (!NT_SUCCESS(Status
))
207 ExFreePool(PartitionBuffer
);
208 ExFreePool(DriveLayout
);
212 /* Calculate the MBR checksum */
214 for (j
= 0; j
< 128; j
++)
216 Checksum
+= ((PULONG
)PartitionBuffer
)[j
];
219 /* Save the signature and checksum */
220 *CheckSum
= ~Checksum
+ 1;
221 *Signature
= DriveLayout
->Signature
;
222 *PartitionCount
= DriveLayout
->PartitionCount
;
224 /* Free the buffer */
225 ExFreePool(PartitionBuffer
);
226 ExFreePool(DriveLayout
);
233 IopAssignArcNamesToCdrom(IN PULONG Buffer
,
237 ANSI_STRING TempString
, ArcNameString
;
238 UNICODE_STRING DeviceName
, ArcName
;
240 LARGE_INTEGER PartitionOffset
;
242 IO_STATUS_BLOCK IoStatusBlock
;
244 ULONG i
, CheckSum
= 0;
245 PDEVICE_OBJECT DeviceObject
;
246 PFILE_OBJECT FileObject
;
248 /* Build the device name */
249 sprintf(ArcBuffer
, "\\Device\\CdRom%lu", DiskNumber
);
251 /* Convert it to Unicode */
252 RtlInitAnsiString(&TempString
, ArcBuffer
);
253 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
254 if (!NT_SUCCESS(Status
)) return FALSE
;
256 /* Get the device for it */
257 Status
= IoGetDeviceObjectPointer(&DeviceName
,
258 FILE_READ_ATTRIBUTES
,
261 if (!NT_SUCCESS(Status
))
263 /* Free the string and fail */
264 RtlFreeUnicodeString(&DeviceName
);
268 /* Setup the event */
269 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
271 /* Set the offset and build the read IRP */
272 PartitionOffset
.QuadPart
= 0x8000;
273 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
282 /* Free the string and fail */
283 RtlFreeUnicodeString(&DeviceName
);
287 /* Call the driver and check if we have to wait on it */
288 Status
= IoCallDriver(DeviceObject
, Irp
);
289 if (Status
== STATUS_PENDING
)
291 /* Wait for completion */
292 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
293 Status
= IoStatusBlock
.Status
;
296 /* Dereference the file object */
297 ObDereferenceObject(FileObject
);
298 if (!NT_SUCCESS(Status
)) return FALSE
;
300 /* Now calculate the checksum */
301 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++) CheckSum
+= Buffer
[i
];
304 * FIXME: In normal conditions, NTLDR/FreeLdr sends the *proper* CDROM
305 * ARC Path name, and what happens here is a comparision of both checksums
306 * in order to see if this is the actual boot CD.
308 * In ReactOS this doesn't currently happen, instead we have a hack on top
309 * of this file which scans the CD for the ntoskrnl.exe file, then modifies
310 * the LoaderBlock's ARC Path with the right CDROM path. Consequently, we
311 * get the same state as if NTLDR had properly booted us, except that we do
312 * not actually need to check the signature, since the hack already did the
313 * check for ntoskrnl.exe, which is just as good.
315 * The signature code stays however, because eventually FreeLDR will work
316 * like NTLDR, and, conversly, we do want to be able to be booted by NTLDR.
318 if (IopApplyRosCdromArcHack(DiskNumber
))
320 /* This is the boot CD-ROM, build the ARC name */
321 sprintf(ArcBuffer
, "\\ArcName\\%s", KeLoaderBlock
->ArcBootDeviceName
);
323 /* Convert it to Unicode */
324 RtlInitAnsiString(&ArcNameString
, ArcBuffer
);
325 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcNameString
, TRUE
);
326 if (!NT_SUCCESS(Status
)) return FALSE
;
328 /* Create the symbolic link and free the strings */
329 IoAssignArcName(&ArcName
, &DeviceName
);
330 RtlFreeUnicodeString(&ArcName
);
331 RtlFreeUnicodeString(&DeviceName
);
333 /* Let caller know that we've found the boot CD */
337 /* No boot CD found */
343 IoCreateArcNames(VOID
)
345 PLOADER_PARAMETER_BLOCK LoaderBlock
= KeLoaderBlock
;
346 PCONFIGURATION_INFORMATION ConfigInfo
= IoGetConfigurationInformation();
347 PARC_DISK_INFORMATION ArcDiskInfo
= LoaderBlock
->ArcDiskInformation
;
348 CHAR ArcBuffer
[256], Buffer
[256];
349 ANSI_STRING ArcBootString
, ArcSystemString
, ArcString
, TempString
, BootString
;
350 UNICODE_STRING ArcName
, BootPath
, DeviceName
;
353 PDEVICE_OBJECT DeviceObject
;
354 ULONG Signature
, Checksum
, PartitionCount
;
355 PLIST_ENTRY NextEntry
;
356 PARC_DISK_SIGNATURE ArcDiskEntry
;
358 BOOLEAN FoundBoot
= FALSE
;
359 PULONG PartitionBuffer
;
361 /* Check if we only have one disk on the machine */
362 SingleDisk
= ArcDiskInfo
->DiskSignatureListHead
.Flink
->Flink
==
363 (&ArcDiskInfo
->DiskSignatureListHead
);
365 /* Create the global HAL partition name */
366 sprintf(ArcBuffer
, "\\ArcName\\%s", LoaderBlock
->ArcHalDeviceName
);
367 RtlInitAnsiString(&ArcString
, ArcBuffer
);
368 RtlAnsiStringToUnicodeString(&IoArcHalDeviceName
, &ArcString
, TRUE
);
370 /* Create the global system partition name */
371 sprintf(ArcBuffer
, "\\ArcName\\%s", LoaderBlock
->ArcBootDeviceName
);
372 RtlInitAnsiString(&ArcString
, ArcBuffer
);
373 RtlAnsiStringToUnicodeString(&IoArcBootDeviceName
, &ArcString
, TRUE
);
375 /* Allocate memory for the string */
376 Length
= strlen(LoaderBlock
->ArcBootDeviceName
) + sizeof(ANSI_NULL
);
377 IoLoaderArcBootDeviceName
= ExAllocatePoolWithTag(PagedPool
,
380 if (IoLoaderArcBootDeviceName
)
383 RtlMoveMemory(IoLoaderArcBootDeviceName
,
384 LoaderBlock
->ArcBootDeviceName
,
388 /* Check if we only found a disk, but we're booting from CD-ROM */
389 if ((SingleDisk
) && strstr(LoaderBlock
->ArcBootDeviceName
, "cdrom"))
391 /* Then disable single-disk mode, since there's a CD drive out there */
395 /* Build the boot strings */
396 RtlInitAnsiString(&ArcBootString
, LoaderBlock
->ArcBootDeviceName
);
397 RtlInitAnsiString(&ArcSystemString
, LoaderBlock
->ArcHalDeviceName
);
399 /* Loop every detected disk */
400 for (i
= 0; i
< ConfigInfo
->DiskCount
; i
++)
402 /* Get information about the disk */
403 if (!IopGetDiskInformation(i
,
414 for (NextEntry
= ArcDiskInfo
->DiskSignatureListHead
.Flink
;
415 NextEntry
!= &ArcDiskInfo
->DiskSignatureListHead
;
416 NextEntry
= NextEntry
->Flink
)
418 /* Get the current ARC disk signature entry */
419 ArcDiskEntry
= CONTAINING_RECORD(NextEntry
,
424 * Now check if the signature and checksum match, unless this is
425 * the only disk that was in the ARC list, and also in the device
426 * tree, in which case the check is bypassed and we accept the disk
428 if (((SingleDisk
) && (ConfigInfo
->DiskCount
== 1)) ||
429 ((Checksum
== ArcDiskEntry
->CheckSum
) &&
430 (Signature
== ArcDiskEntry
->Signature
)))
432 /* Build the NT Device Name */
433 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", i
);
435 /* Convert it to Unicode */
436 RtlInitAnsiString(&TempString
, Buffer
);
437 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
438 if (!NT_SUCCESS(Status
)) continue;
440 /* Build the ARC Device Name */
441 sprintf(ArcBuffer
, "\\ArcName\\%s", ArcDiskEntry
->ArcName
);
443 /* Convert it to Unicode */
444 RtlInitAnsiString(&ArcString
, ArcBuffer
);
445 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcString
, TRUE
);
446 if (!NT_SUCCESS(Status
)) continue;
448 /* Create the symbolic link and free the strings */
449 IoAssignArcName(&ArcName
, &DeviceName
);
450 RtlFreeUnicodeString(&ArcName
);
451 RtlFreeUnicodeString(&DeviceName
);
453 /* Loop all the partitions */
454 for (j
= 0; j
< PartitionCount
; j
++)
456 /* Build the partition device name */
457 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition%lu", i
, j
+ 1);
459 /* Convert it to Unicode */
460 RtlInitAnsiString(&TempString
, Buffer
);
461 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
462 if (!NT_SUCCESS(Status
)) continue;
464 /* Build the partial ARC name for this partition */
465 sprintf(ArcBuffer
, "%spartition(%lu)", ArcDiskEntry
->ArcName
, j
+ 1);
466 RtlInitAnsiString(&ArcString
, ArcBuffer
);
468 /* Check if this is the boot device */
469 if (RtlEqualString(&ArcString
, &ArcBootString
, TRUE
))
471 /* Remember that we found a Hard Disk Boot Device */
475 /* Check if it's the system boot partition */
476 if (RtlEqualString(&ArcString
, &ArcSystemString
, TRUE
))
478 /* It is, create a Unicode string for it */
479 RtlInitAnsiString(&BootString
, LoaderBlock
->NtHalPathName
);
480 Status
= RtlAnsiStringToUnicodeString(&BootPath
, &BootString
, TRUE
);
481 if (NT_SUCCESS(Status
))
483 /* FIXME: Save in registry */
485 /* Free the string now */
486 RtlFreeUnicodeString(&BootPath
);
490 /* Build the full ARC name */
491 sprintf(Buffer
, "\\ArcName\\%spartition(%lu)", ArcDiskEntry
->ArcName
, j
+ 1);
493 /* Convert it to Unicode */
494 RtlInitAnsiString(&ArcString
, Buffer
);
495 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcString
, TRUE
);
496 if (!NT_SUCCESS(Status
)) continue;
498 /* Create the symbolic link and free the strings */
499 IoAssignArcName(&ArcName
, &DeviceName
);
500 RtlFreeUnicodeString(&ArcName
);
501 RtlFreeUnicodeString(&DeviceName
);
507 /* Check if we didn't find the boot disk */
510 /* Allocate a buffer for the CD-ROM MBR */
511 PartitionBuffer
= ExAllocatePoolWithTag(NonPagedPool
, 2048, TAG_IO
);
512 if (!PartitionBuffer
) return STATUS_INSUFFICIENT_RESOURCES
;
514 /* Loop every CD-ROM */
515 for (i
= 0; i
< ConfigInfo
->CdRomCount
; i
++)
517 /* Give it an ARC name */
518 if (IopAssignArcNamesToCdrom(PartitionBuffer
, i
)) break;
521 /* Free the buffer */
522 ExFreePool(PartitionBuffer
);
526 return STATUS_SUCCESS
;
531 IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock
,
532 OUT PANSI_STRING NtBootPath
)
534 OBJECT_ATTRIBUTES ObjectAttributes
;
536 CHAR Buffer
[256], AnsiBuffer
[256];
537 WCHAR ArcNameBuffer
[64];
538 ANSI_STRING TargetString
, ArcString
, TempString
;
539 UNICODE_STRING LinkName
, TargetName
, ArcName
;
542 /* Create the Unicode name for the current ARC boot device */
543 sprintf(Buffer
, "\\ArcName\\%s", LoaderBlock
->ArcBootDeviceName
);
544 RtlInitAnsiString(&TargetString
, Buffer
);
545 Status
= RtlAnsiStringToUnicodeString(&TargetName
, &TargetString
, TRUE
);
546 if (!NT_SUCCESS(Status
)) return FALSE
;
548 /* Initialize the attributes and open the link */
549 InitializeObjectAttributes(&ObjectAttributes
,
551 OBJ_CASE_INSENSITIVE
,
554 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
555 SYMBOLIC_LINK_ALL_ACCESS
,
557 if (!NT_SUCCESS(Status
))
559 /* We failed, free the string */
560 RtlFreeUnicodeString(&TargetName
);
564 /* Query the current \\SystemRoot */
565 ArcName
.Buffer
= ArcNameBuffer
;
567 ArcName
.MaximumLength
= sizeof(ArcNameBuffer
);
568 Status
= NtQuerySymbolicLinkObject(LinkHandle
, &ArcName
, NULL
);
569 if (!NT_SUCCESS(Status
))
571 /* We failed, free the string */
572 RtlFreeUnicodeString(&TargetName
);
576 /* Convert it to Ansi */
577 ArcString
.Buffer
= AnsiBuffer
;
578 ArcString
.Length
= 0;
579 ArcString
.MaximumLength
= sizeof(AnsiBuffer
);
580 Status
= RtlUnicodeStringToAnsiString(&ArcString
, &ArcName
, FALSE
);
581 AnsiBuffer
[ArcString
.Length
] = ANSI_NULL
;
583 /* Close the link handle and free the name */
584 ObCloseHandle(LinkHandle
, KernelMode
);
585 RtlFreeUnicodeString(&TargetName
);
587 /* Setup the system root name again */
588 RtlInitAnsiString(&TempString
, "\\SystemRoot");
589 Status
= RtlAnsiStringToUnicodeString(&LinkName
, &TempString
, TRUE
);
590 if (!NT_SUCCESS(Status
)) return FALSE
;
592 /* Open the symbolic link for it */
593 InitializeObjectAttributes(&ObjectAttributes
,
595 OBJ_CASE_INSENSITIVE
,
598 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
599 SYMBOLIC_LINK_ALL_ACCESS
,
601 if (!NT_SUCCESS(Status
)) return FALSE
;
604 NtMakeTemporaryObject(LinkHandle
);
605 ObCloseHandle(LinkHandle
, KernelMode
);
607 /* Now create the new name for it */
608 sprintf(Buffer
, "%s%s", ArcString
.Buffer
, LoaderBlock
->NtBootPathName
);
610 /* Copy it into the passed parameter and null-terminate it */
611 RtlCopyString(NtBootPath
, &ArcString
);
612 Buffer
[strlen(Buffer
) - 1] = ANSI_NULL
;
614 /* Setup the Unicode-name for the new symbolic link value */
615 RtlInitAnsiString(&TargetString
, Buffer
);
616 InitializeObjectAttributes(&ObjectAttributes
,
618 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
621 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &TargetString
, TRUE
);
622 if (!NT_SUCCESS(Status
)) return FALSE
;
625 Status
= NtCreateSymbolicLinkObject(&LinkHandle
,
626 SYMBOLIC_LINK_ALL_ACCESS
,
630 /* Free all the strings and close the handle and return success */
631 RtlFreeUnicodeString(&ArcName
);
632 RtlFreeUnicodeString(&LinkName
);
633 ObCloseHandle(LinkHandle
, KernelMode
);