/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/io/iomgr/arcname.c * PURPOSE: ARC Path Initialization Functions * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Eric Kohl */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* GLOBALS *******************************************************************/ UNICODE_STRING IoArcHalDeviceName, IoArcBootDeviceName; PCHAR IoLoaderArcBootDeviceName; extern PROS_LOADER_PARAMETER_BLOCK KeRosLoaderBlock; /* FUNCTIONS *****************************************************************/ BOOLEAN INIT_FUNCTION NTAPI IopApplyRosCdromArcHack(IN ULONG i) { ULONG DeviceNumber = -1; OBJECT_ATTRIBUTES ObjectAttributes; ANSI_STRING InstallName; UNICODE_STRING DeviceName; CHAR Buffer[128], RosSysPath[16]; FILE_BASIC_INFORMATION FileInfo; NTSTATUS Status; PCHAR p, q; PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation(); extern BOOLEAN InitIsWinPEMode, ExpInTextModeSetup; /* Change this if you want ROS to boot properly from another directory */ sprintf(RosSysPath, "%s", "reactos"); /* Only ARC Name left - Build full ARC Name */ p = strstr(KeLoaderBlock->ArcBootDeviceName, "cdrom"); if (p) { /* Build installer name */ sprintf(Buffer, "\\Device\\CdRom%lu\\%s\\ntoskrnl.exe", i, RosSysPath); RtlInitAnsiString(&InstallName, Buffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &InstallName, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Try to find the installer */ InitializeObjectAttributes(&ObjectAttributes, &DeviceName, 0, NULL, NULL); Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo); /* Free the string */ RtlFreeUnicodeString(&DeviceName); /* Check if we found the file */ if (NT_SUCCESS(Status)) { /* We did, save the device number */ DeviceNumber = i; } else { /* Build live CD kernel name */ sprintf(Buffer, "\\Device\\CdRom%lu\\%s\\system32\\ntoskrnl.exe", i, RosSysPath); RtlInitAnsiString(&InstallName, Buffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &InstallName, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Try to find it */ InitializeObjectAttributes(&ObjectAttributes, &DeviceName, 0, NULL, NULL); Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo); if (NT_SUCCESS(Status)) DeviceNumber = i; /* Free the string */ RtlFreeUnicodeString(&DeviceName); } if (!InitIsWinPEMode) { /* Build the name */ sprintf(p, "cdrom(%lu)", DeviceNumber); /* Adjust original command line */ q = strchr(p, ')'); if (q) { q++; strcpy(Buffer, q); sprintf(p, "cdrom(%lu)", DeviceNumber); strcat(p, Buffer); } } } /* OK, how many disks are there? */ DeviceNumber += ConfigInfo->DiskCount; /* Return whether this is the CD or not */ if ((InitIsWinPEMode) || (ExpInTextModeSetup)) { return TRUE; } /* Failed */ return FALSE; } BOOLEAN INIT_FUNCTION NTAPI IopGetDiskInformation(IN ULONG i, OUT PULONG CheckSum, OUT PULONG Signature, OUT PULONG PartitionCount, OUT PDEVICE_OBJECT *DiskDeviceObject) { ULONG j, Checksum; ANSI_STRING TempString; CHAR Buffer[128]; UNICODE_STRING DeviceName; NTSTATUS Status; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; DISK_GEOMETRY DiskGeometry; PDRIVE_LAYOUT_INFORMATION DriveLayout; KEVENT Event; PIRP Irp; IO_STATUS_BLOCK StatusBlock; LARGE_INTEGER PartitionOffset; PULONG PartitionBuffer; /* Build the name */ sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", i); /* Convert it to Unicode */ RtlInitAnsiString(&TempString, Buffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Get the device pointer */ Status = IoGetDeviceObjectPointer(&DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); *DiskDeviceObject = DeviceObject; /* Free the string */ RtlFreeUnicodeString(&DeviceName); /* Move on if we failed */ if (!NT_SUCCESS(Status)) return FALSE; /* Build an IRP to determine the sector size */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY, DeviceObject, NULL, 0, &DiskGeometry, sizeof(DISK_GEOMETRY), FALSE, &Event, &StatusBlock); if (!Irp) { /* Try again */ ObDereferenceObject(FileObject); return FALSE; } /* Call the driver and check if we have to wait on it */ Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { /* Wait on the driver */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = StatusBlock.Status; } /* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Try again */ ObDereferenceObject(FileObject); return FALSE; } /* Read the partition table */ Status = IoReadPartitionTable(DeviceObject, DiskGeometry.BytesPerSector, TRUE, &DriveLayout); /* Dereference the file object */ ObDereferenceObject(FileObject); if (!NT_SUCCESS(Status)) return FALSE; /* Set the offset to 0 */ PartitionOffset.QuadPart = 0; /* Allocate a buffer for the partition */ PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_IO); if (!PartitionBuffer) { /* Try again */ ExFreePoolWithTag(DriveLayout, TAG_FILE_SYSTEM); return FALSE; } /* Build an IRP to read the partition sector */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, PartitionBuffer, DiskGeometry.BytesPerSector, &PartitionOffset, &Event, &StatusBlock); if (!Irp) { /* Try again */ ExFreePoolWithTag(PartitionBuffer, TAG_IO); ExFreePoolWithTag(DriveLayout, TAG_FILE_SYSTEM); return FALSE; } /* Call the driver and check if we have to wait */ Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = StatusBlock.Status; } /* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Try again */ ExFreePoolWithTag(PartitionBuffer, TAG_IO); ExFreePoolWithTag(DriveLayout, TAG_FILE_SYSTEM); return FALSE; } /* Calculate the MBR checksum */ Checksum = 0; for (j = 0; j < 128; j++) Checksum += PartitionBuffer[j]; /* Save the signature and checksum */ *CheckSum = ~Checksum + 1; *Signature = DriveLayout->Signature; *PartitionCount = DriveLayout->PartitionCount; /* Free the buffer */ ExFreePoolWithTag(PartitionBuffer, TAG_IO); ExFreePoolWithTag(DriveLayout, TAG_FILE_SYSTEM); return TRUE; } BOOLEAN INIT_FUNCTION NTAPI IopAssignArcNamesToCdrom(IN PLOADER_PARAMETER_BLOCK LoaderBlock, IN PULONG Buffer, IN ULONG DiskNumber) { CHAR ArcBuffer[128]; ANSI_STRING TempString; UNICODE_STRING DeviceName, ArcName; NTSTATUS Status; LARGE_INTEGER PartitionOffset; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; ULONG i, CheckSum = 0; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PARC_DISK_INFORMATION ArcDiskInfo = LoaderBlock->ArcDiskInformation; PLIST_ENTRY NextEntry; PARC_DISK_SIGNATURE ArcDiskEntry; BOOLEAN IsBootCdRom = FALSE; /* Build the device name */ sprintf(ArcBuffer, "\\Device\\CdRom%lu", DiskNumber); /* Convert it to Unicode */ RtlInitAnsiString(&TempString, ArcBuffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Get the device for it */ Status = IoGetDeviceObjectPointer(&DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { /* Free the string and fail */ RtlFreeUnicodeString(&DeviceName); return FALSE; } /* Setup the event */ KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Set the offset and build the read IRP */ PartitionOffset.QuadPart = 0x8000; Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, 2048, &PartitionOffset, &Event, &IoStatusBlock); if (!Irp) { /* Free the string and fail */ RtlFreeUnicodeString(&DeviceName); return FALSE; } /* Call the driver and check if we have to wait on it */ Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* Dereference the file object */ ObDereferenceObject(FileObject); if (!NT_SUCCESS(Status)) return FALSE; /* Now calculate the checksum */ for (i = 0; i < 2048 / sizeof(ULONG); i++) CheckSum += Buffer[i]; if (KeRosLoaderBlock) goto freeldrhack; /* Search if this device is the actual boot CD */ for (NextEntry = ArcDiskInfo->DiskSignatureListHead.Flink; NextEntry != &ArcDiskInfo->DiskSignatureListHead; NextEntry = NextEntry->Flink) { /* Get the current ARC disk signature entry */ ArcDiskEntry = CONTAINING_RECORD(NextEntry, ARC_DISK_SIGNATURE, ListEntry); /* And check if checksums and arc names match */ if (CheckSum == ArcDiskEntry->CheckSum && strcmp(KeLoaderBlock->ArcBootDeviceName, ArcDiskEntry->ArcName) == 0) { IsBootCdRom = TRUE; break; } } goto checkbootcd; freeldrhack: /* * FIXME: In normal conditions, NTLDR/FreeLdr sends the *proper* CDROM * ARC Path name, and what happens here is a comparision of both checksums * in order to see if this is the actual boot CD. * * In ReactOS this doesn't currently happen, instead we have a hack on top * of this file which scans the CD for the ntoskrnl.exe file, then modifies * the LoaderBlock's ARC Path with the right CDROM path. Consequently, we * get the same state as if NTLDR had properly booted us, except that we do * not actually need to check the signature, since the hack already did the * check for ntoskrnl.exe, which is just as good. * * The signature code stays however, because eventually FreeLDR will work * like NTLDR, and, conversly, we do want to be able to be booted by NTLDR. */ IsBootCdRom = IopApplyRosCdromArcHack(DiskNumber); checkbootcd: if (IsBootCdRom) { /* This is the boot CD-ROM, build the ARC name */ sprintf(ArcBuffer, "\\ArcName\\%s", KeLoaderBlock->ArcBootDeviceName); /* Convert it to Unicode */ RtlInitAnsiString(&TempString, ArcBuffer); Status = RtlAnsiStringToUnicodeString(&ArcName, &TempString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Create the symbolic link and free the strings */ IoAssignArcName(&ArcName, &DeviceName); RtlFreeUnicodeString(&ArcName); RtlFreeUnicodeString(&DeviceName); /* Let caller know that we've found the boot CD */ return TRUE; } /* No boot CD found */ return FALSE; } NTSTATUS INIT_FUNCTION NTAPI IopCreateArcNames(IN PLOADER_PARAMETER_BLOCK LoaderBlock) { PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation(); PARC_DISK_INFORMATION ArcDiskInfo = LoaderBlock->ArcDiskInformation; CHAR Buffer[128]; ANSI_STRING ArcBootString, ArcSystemString, ArcString; UNICODE_STRING ArcName, BootPath, DeviceName; BOOLEAN SingleDisk; ULONG i, j, Length; PDEVICE_OBJECT DeviceObject; ULONG Signature, Checksum, PartitionCount; PLIST_ENTRY NextEntry; PARC_DISK_SIGNATURE ArcDiskEntry; NTSTATUS Status; BOOLEAN FoundBoot = FALSE; PULONG PartitionBuffer; /* Check if we only have one disk on the machine */ SingleDisk = ArcDiskInfo->DiskSignatureListHead.Flink->Flink == (&ArcDiskInfo->DiskSignatureListHead); /* Create the global HAL partition name */ sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName); RtlInitAnsiString(&ArcString, Buffer); RtlAnsiStringToUnicodeString(&IoArcHalDeviceName, &ArcString, TRUE); /* Create the global system partition name */ sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName); RtlInitAnsiString(&ArcString, Buffer); RtlAnsiStringToUnicodeString(&IoArcBootDeviceName, &ArcString, TRUE); /* Allocate memory for the string */ Length = strlen(LoaderBlock->ArcBootDeviceName) + sizeof(ANSI_NULL); IoLoaderArcBootDeviceName = ExAllocatePoolWithTag(PagedPool, Length, TAG_IO); if (IoLoaderArcBootDeviceName) { /* Copy the name */ RtlCopyMemory(IoLoaderArcBootDeviceName, LoaderBlock->ArcBootDeviceName, Length); } /* Check if we only found a disk, but we're booting from CD-ROM */ if ((SingleDisk) && strstr(LoaderBlock->ArcBootDeviceName, "cdrom")) { /* Then disable single-disk mode, since there's a CD drive out there */ SingleDisk = FALSE; } /* Build the boot strings */ RtlInitAnsiString(&ArcBootString, LoaderBlock->ArcBootDeviceName); RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName); /* Loop every detected disk */ for (i = 0; i < ConfigInfo->DiskCount; i++) { /* Get information about the disk */ if (!IopGetDiskInformation(i, &Checksum, &Signature, &PartitionCount, &DeviceObject)) { /* Skip this disk */ continue; } /* Loop ARC disks */ for (NextEntry = ArcDiskInfo->DiskSignatureListHead.Flink; NextEntry != &ArcDiskInfo->DiskSignatureListHead; NextEntry = NextEntry->Flink) { /* Get the current ARC disk signature entry */ ArcDiskEntry = CONTAINING_RECORD(NextEntry, ARC_DISK_SIGNATURE, ListEntry); /* * Now check if the signature and checksum match, unless this is * the only disk that was in the ARC list, and also in the device * tree, in which case the check is bypassed and we accept the disk */ if (((SingleDisk) && (ConfigInfo->DiskCount == 1)) || ((Checksum == ArcDiskEntry->CheckSum) && (Signature == ArcDiskEntry->Signature))) { /* Build the NT Device Name */ sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", i); /* Convert it to Unicode */ RtlInitAnsiString(&ArcString, Buffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &ArcString, TRUE); if (!NT_SUCCESS(Status)) continue; /* Build the ARC Device Name */ sprintf(Buffer, "\\ArcName\\%s", ArcDiskEntry->ArcName); /* Convert it to Unicode */ RtlInitAnsiString(&ArcString, Buffer); Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcString, TRUE); if (!NT_SUCCESS(Status)) continue; /* Create the symbolic link and free the strings */ IoAssignArcName(&ArcName, &DeviceName); RtlFreeUnicodeString(&ArcName); RtlFreeUnicodeString(&DeviceName); /* Loop all the partitions */ for (j = 0; j < PartitionCount; j++) { /* Build the partition device name */ sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", i, j + 1); /* Convert it to Unicode */ RtlInitAnsiString(&ArcString, Buffer); Status = RtlAnsiStringToUnicodeString(&DeviceName, &ArcString, TRUE); if (!NT_SUCCESS(Status)) continue; /* Build the partial ARC name for this partition */ sprintf(Buffer, "%spartition(%lu)", ArcDiskEntry->ArcName, j + 1); RtlInitAnsiString(&ArcString, Buffer); /* Check if this is the boot device */ if (RtlEqualString(&ArcString, &ArcBootString, TRUE)) { /* Remember that we found a Hard Disk Boot Device */ FoundBoot = TRUE; } /* Check if it's the system boot partition */ if (RtlEqualString(&ArcString, &ArcSystemString, TRUE)) { /* It is, create a Unicode string for it */ RtlInitAnsiString(&ArcString, LoaderBlock->NtHalPathName); Status = RtlAnsiStringToUnicodeString(&BootPath, &ArcString, TRUE); if (NT_SUCCESS(Status)) { /* FIXME: Save in registry */ /* Free the string now */ RtlFreeUnicodeString(&BootPath); } } /* Build the full ARC name */ sprintf(Buffer, "\\ArcName\\%spartition(%lu)", ArcDiskEntry->ArcName, j + 1); /* Convert it to Unicode */ RtlInitAnsiString(&ArcString, Buffer); Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcString, TRUE); if (!NT_SUCCESS(Status)) continue; /* Create the symbolic link and free the strings */ IoAssignArcName(&ArcName, &DeviceName); RtlFreeUnicodeString(&ArcName); RtlFreeUnicodeString(&DeviceName); } } } } /* Check if we didn't find the boot disk */ if (!FoundBoot) { /* Allocate a buffer for the CD-ROM MBR */ PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool, 2048, TAG_IO); if (!PartitionBuffer) return STATUS_INSUFFICIENT_RESOURCES; /* Loop every CD-ROM */ for (i = 0; i < ConfigInfo->CdRomCount; i++) { /* Give it an ARC name */ if (IopAssignArcNamesToCdrom(LoaderBlock, PartitionBuffer, i)) break; } /* Free the buffer */ ExFreePoolWithTag(PartitionBuffer, TAG_IO); } /* Return success */ return STATUS_SUCCESS; } NTSTATUS NTAPI IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock, OUT PANSI_STRING NtBootPath) { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; CHAR Buffer[256], AnsiBuffer[256]; WCHAR ArcNameBuffer[64]; ANSI_STRING TargetString, ArcString, TempString; UNICODE_STRING LinkName, TargetName, ArcName; HANDLE LinkHandle; /* Create the Unicode name for the current ARC boot device */ sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName); RtlInitAnsiString(&TargetString, Buffer); Status = RtlAnsiStringToUnicodeString(&TargetName, &TargetString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Initialize the attributes and open the link */ InitializeObjectAttributes(&ObjectAttributes, &TargetName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS(Status)) { /* We failed, free the string */ RtlFreeUnicodeString(&TargetName); return FALSE; } /* Query the current \\SystemRoot */ ArcName.Buffer = ArcNameBuffer; ArcName.Length = 0; ArcName.MaximumLength = sizeof(ArcNameBuffer); Status = NtQuerySymbolicLinkObject(LinkHandle, &ArcName, NULL); if (!NT_SUCCESS(Status)) { /* We failed, free the string */ RtlFreeUnicodeString(&TargetName); return FALSE; } /* Convert it to Ansi */ ArcString.Buffer = AnsiBuffer; ArcString.Length = 0; ArcString.MaximumLength = sizeof(AnsiBuffer); Status = RtlUnicodeStringToAnsiString(&ArcString, &ArcName, FALSE); AnsiBuffer[ArcString.Length] = ANSI_NULL; /* Close the link handle and free the name */ ObCloseHandle(LinkHandle, KernelMode); RtlFreeUnicodeString(&TargetName); /* Setup the system root name again */ RtlInitAnsiString(&TempString, "\\SystemRoot"); Status = RtlAnsiStringToUnicodeString(&LinkName, &TempString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Open the symbolic link for it */ InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS(Status)) return FALSE; /* Destroy it */ NtMakeTemporaryObject(LinkHandle); ObCloseHandle(LinkHandle, KernelMode); /* Now create the new name for it */ sprintf(Buffer, "%s%s", ArcString.Buffer, LoaderBlock->NtBootPathName); /* Copy it into the passed parameter and null-terminate it */ RtlCopyString(NtBootPath, &ArcString); Buffer[strlen(Buffer) - 1] = ANSI_NULL; /* Setup the Unicode-name for the new symbolic link value */ RtlInitAnsiString(&TargetString, Buffer); InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL); Status = RtlAnsiStringToUnicodeString(&ArcName, &TargetString, TRUE); if (!NT_SUCCESS(Status)) return FALSE; /* Create it */ Status = NtCreateSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &ArcName); /* Free all the strings and close the handle and return success */ RtlFreeUnicodeString(&ArcName); RtlFreeUnicodeString(&LinkName); ObCloseHandle(LinkHandle, KernelMode); return TRUE; } /* EOF */