- Greatly simplify and optimize IoCreateArcNames by using ARC data from FreeLDR/NTLDR...
[reactos.git] / reactos / ntoskrnl / io / iomgr / arcname.c
1 /* $Id$
2 *
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
7 *
8 * PROGRAMMERS: Eric Kohl (ekohl@rz-online.de)
9 */
10
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* FUNCTIONS ****************************************************************/
19
20 BOOLEAN
21 INIT_FUNCTION
22 NTAPI
23 IopApplyRosCdromArcHack(IN ULONG i)
24 {
25 ULONG DeviceNumber = -1;
26 OBJECT_ATTRIBUTES ObjectAttributes;
27 UNICODE_STRING DeviceName;
28 WCHAR Buffer[MAX_PATH];
29 CHAR AnsiBuffer[MAX_PATH];
30 FILE_BASIC_INFORMATION FileInfo;
31 NTSTATUS Status;
32 PCHAR p, q;
33
34 /* Only ARC Name left - Build full ARC Name */
35 p = strstr(KeLoaderBlock->ArcBootDeviceName, "cdrom");
36 if (p)
37 {
38 /* Try to find the installer */
39 swprintf(Buffer, L"\\Device\\CdRom%lu\\reactos\\ntoskrnl.exe", i);
40 RtlInitUnicodeString(&DeviceName, Buffer);
41 InitializeObjectAttributes(&ObjectAttributes,
42 &DeviceName,
43 0,
44 NULL,
45 NULL);
46 Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo);
47 if (NT_SUCCESS(Status)) DeviceNumber = i;
48
49 /* Try to find live CD boot */
50 swprintf(Buffer,
51 L"\\Device\\CdRom%lu\\reactos\\system32\\ntoskrnl.exe",
52 i);
53 RtlInitUnicodeString(&DeviceName, Buffer);
54 InitializeObjectAttributes(&ObjectAttributes,
55 &DeviceName,
56 0,
57 NULL,
58 NULL);
59 Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo);
60 if (NT_SUCCESS(Status)) DeviceNumber = i;
61
62 /* Build the name */
63 sprintf(p, "cdrom(%lu)", DeviceNumber);
64
65 /* Adjust original command line */
66 q = strchr(p, ')');
67 if (q)
68 {
69 q++;
70 strcpy(AnsiBuffer, q);
71 sprintf(p, "cdrom(%lu)", DeviceNumber);
72 strcat(p, AnsiBuffer);
73 }
74 }
75
76 /* Return whether this is the CD or not */
77 if (DeviceNumber != 1) return TRUE;
78 return FALSE;
79 }
80
81 VOID
82 INIT_FUNCTION
83 NTAPI
84 IopEnumerateDisks(IN PLIST_ENTRY ListHead)
85 {
86 ULONG i, j;
87 ANSI_STRING TempString;
88 CHAR Buffer[256];
89 UNICODE_STRING DeviceName;
90 NTSTATUS Status;
91 PDEVICE_OBJECT DeviceObject;
92 PFILE_OBJECT FileObject;
93 DISK_GEOMETRY DiskGeometry;
94 PDRIVE_LAYOUT_INFORMATION DriveLayout;
95 KEVENT Event;
96 PIRP Irp;
97 IO_STATUS_BLOCK StatusBlock;
98 LARGE_INTEGER PartitionOffset;
99 PPARTITION_SECTOR PartitionBuffer;
100 PDISKENTRY DiskEntry;
101
102 /* Loop every detected disk */
103 for (i = 0; i < IoGetConfigurationInformation()->DiskCount; i++)
104 {
105 /* Build the name */
106 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", i);
107
108 /* Convert it to Unicode */
109 RtlInitAnsiString(&TempString, Buffer);
110 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
111 if (!NT_SUCCESS(Status)) continue;
112
113 /* Get the device pointer */
114 Status = IoGetDeviceObjectPointer(&DeviceName,
115 FILE_READ_DATA,
116 &FileObject,
117 &DeviceObject);
118
119 /* Free the string */
120 RtlFreeUnicodeString(&DeviceName);
121
122 /* Move on if we failed */
123 if (!NT_SUCCESS(Status)) continue;
124
125 /* Allocate the ROS disk Entry */
126 DiskEntry = ExAllocatePoolWithTag(PagedPool, sizeof(DISKENTRY), TAG_IO);
127 DiskEntry->DiskNumber = i;
128 DiskEntry->DeviceObject = DeviceObject;
129
130 /* Build an IRP to determine the sector size */
131 KeInitializeEvent(&Event, NotificationEvent, FALSE);
132 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
133 DeviceObject,
134 NULL,
135 0,
136 &DiskGeometry,
137 sizeof(DISK_GEOMETRY),
138 FALSE,
139 &Event,
140 &StatusBlock);
141 if (!Irp)
142 {
143 /* Try again */
144 ObDereferenceObject(FileObject);
145 continue;
146 }
147
148 /* Call the driver and check if we have to wait on it */
149 Status = IoCallDriver(DeviceObject, Irp);
150 if (Status == STATUS_PENDING)
151 {
152 /* Wait on the driver */
153 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
154 Status = StatusBlock.Status;
155 }
156
157 /* Check if we failed */
158 if (!NT_SUCCESS(Status))
159 {
160 /* Try again */
161 ObDereferenceObject(FileObject);
162 continue;
163 }
164
165 /* Read the partition table */
166 Status = IoReadPartitionTable(DeviceObject,
167 DiskGeometry.BytesPerSector,
168 TRUE,
169 &DriveLayout);
170
171 /* Dereference the file object */
172 ObDereferenceObject(FileObject);
173 if (!NT_SUCCESS(Status)) continue;
174
175 /* Set the offset to 0 */
176 PartitionOffset.QuadPart = 0;
177
178 /* Allocate a buffer for the partition */
179 PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool,
180 DiskGeometry.BytesPerSector,
181 TAG_IO);
182 if (!PartitionBuffer) continue;
183
184 /* Build an IRP to read the partition sector */
185 KeInitializeEvent(&Event, NotificationEvent, FALSE);
186 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
187 DeviceObject,
188 PartitionBuffer,
189 DiskGeometry.BytesPerSector,
190 &PartitionOffset,
191 &Event,
192 &StatusBlock);
193
194 /* Call the driver and check if we have to wait */
195 Status = IoCallDriver(DeviceObject, Irp);
196 if (Status == STATUS_PENDING)
197 {
198 /* Wait for completion */
199 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
200 Status = StatusBlock.Status;
201 }
202
203 /* Check if we failed */
204 if (!NT_SUCCESS(Status))
205 {
206 /* Try again */
207 ExFreePool(PartitionBuffer);
208 ExFreePool(DriveLayout);
209 continue;
210 }
211
212 /* Calculate the MBR checksum */
213 DiskEntry->Checksum = 0;
214 for (j = 0; j < 128; j++)
215 {
216 DiskEntry->Checksum += ((PULONG)PartitionBuffer)[j];
217 }
218
219 /* Save the signature and checksum */
220 DiskEntry->Checksum = ~DiskEntry->Checksum + 1;
221 DiskEntry->Signature = DriveLayout->Signature;
222 DiskEntry->PartitionCount = DriveLayout->PartitionCount;
223
224 /* Insert it into the list */
225 InsertTailList(ListHead, &DiskEntry->ListEntry);
226
227 /* Free the buffer */
228 ExFreePool(PartitionBuffer);
229 ExFreePool(DriveLayout);
230 }
231 }
232
233 NTSTATUS
234 INIT_FUNCTION
235 NTAPI
236 IopAssignArcNamesToDisk(IN PDEVICE_OBJECT DeviceObject,
237 IN PCHAR BootArcName,
238 IN ULONG DiskNumber,
239 IN ULONG PartitionCount,
240 IN PBOOLEAN FoundHdBoot)
241 {
242 CHAR Buffer[256];
243 CHAR ArcBuffer[256];
244 ANSI_STRING TempString, ArcNameString, BootString;
245 ANSI_STRING ArcBootString, ArcSystemString;
246 UNICODE_STRING DeviceName, ArcName, BootPath;
247 ULONG i;
248 NTSTATUS Status;
249
250 /* Set default */
251 *FoundHdBoot = FALSE;
252
253 /* Build the boot strings */
254 RtlInitAnsiString(&ArcBootString, KeLoaderBlock->ArcBootDeviceName);
255 RtlInitAnsiString(&ArcSystemString, KeLoaderBlock->ArcHalDeviceName);
256
257 /* Build the NT Device Name */
258 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
259
260 /* Convert it to unicode */
261 RtlInitAnsiString(&TempString, Buffer);
262 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
263 if (!NT_SUCCESS(Status)) return Status;
264
265 /* Build the ARC Device Name */
266 sprintf(ArcBuffer, "\\ArcName\\%s", BootArcName);
267
268 /* Convert it to Unicode */
269 RtlInitAnsiString(&ArcNameString, ArcBuffer);
270 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcNameString, TRUE);
271 if (!NT_SUCCESS(Status)) return Status;
272
273 /* Create the symbolic link and free the strings */
274 IoAssignArcName(&ArcName, &DeviceName);
275 RtlFreeUnicodeString(&ArcName);
276 RtlFreeUnicodeString(&DeviceName);
277
278 /* Loop all the partitions */
279 for (i = 0; i < PartitionCount; i++)
280 {
281 /* Build the partition device name */
282 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, i+1);
283
284 /* Convert it to Unicode */
285 RtlInitAnsiString(&TempString, Buffer);
286 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
287 if (!NT_SUCCESS(Status)) continue;
288
289 /* Build the partial ARC name for this partition */
290 sprintf(ArcBuffer, "%spartition(%lu)", BootArcName, i + 1);
291 RtlInitAnsiString(&ArcNameString, ArcBuffer);
292
293 /* Check if this is the boot device */
294 if (RtlEqualString(&ArcNameString, &ArcBootString, TRUE))
295 {
296 /* Remember that we found a Hard Disk Boot Device */
297 *FoundHdBoot = TRUE;
298 }
299
300 /* Check if it's the system boot partition */
301 if (RtlEqualString(&ArcNameString, &ArcSystemString, TRUE))
302 {
303 /* It is, create a Unicode string for it */
304 RtlInitAnsiString(&BootString, KeLoaderBlock->NtHalPathName);
305 Status = RtlAnsiStringToUnicodeString(&BootPath, &BootString, TRUE);
306 if (NT_SUCCESS(Status))
307 {
308 /* FIXME: Save in registry */
309
310 /* Free the string now */
311 RtlFreeUnicodeString(&BootPath);
312 }
313 }
314
315 /* Build the full ARC name */
316 sprintf(Buffer, "\\ArcName\\%spartition(%lu)", BootArcName, i + 1);
317
318 /* Convert it to Unicode */
319 RtlInitAnsiString(&ArcNameString, Buffer);
320 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcNameString, TRUE);
321 if (!NT_SUCCESS(Status)) continue;
322
323 /* Create the symbolic link and free the strings */
324 IoAssignArcName(&ArcName, &DeviceName);
325 RtlFreeUnicodeString(&ArcName);
326 RtlFreeUnicodeString(&DeviceName);
327 }
328
329 /* Return success */
330 return STATUS_SUCCESS;
331 }
332
333 BOOLEAN
334 INIT_FUNCTION
335 NTAPI
336 IopAssignArcNamesToCdrom(IN PULONG Buffer,
337 IN ULONG DiskNumber)
338 {
339 CHAR ArcBuffer[256];
340 ANSI_STRING TempString, ArcNameString;
341 UNICODE_STRING DeviceName, ArcName;
342 NTSTATUS Status;
343 LARGE_INTEGER PartitionOffset;
344 KEVENT Event;
345 IO_STATUS_BLOCK IoStatusBlock;
346 PIRP Irp;
347 ULONG i, CheckSum = 0;
348 PDEVICE_OBJECT DeviceObject;
349 PFILE_OBJECT FileObject;
350
351 /* Build the device name */
352 sprintf(ArcBuffer, "\\Device\\CdRom%lu", DiskNumber);
353
354 /* Convert it to Unicode */
355 RtlInitAnsiString(&TempString, ArcBuffer);
356 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
357 if (!NT_SUCCESS(Status)) return FALSE;
358
359 /* Get the device for it */
360 Status = IoGetDeviceObjectPointer(&DeviceName,
361 FILE_READ_ATTRIBUTES,
362 &FileObject,
363 &DeviceObject);
364 if (!NT_SUCCESS(Status))
365 {
366 /* Free the string and fail */
367 RtlFreeUnicodeString(&DeviceName);
368 return FALSE;
369 }
370
371 /* Setup the event */
372 KeInitializeEvent(&Event, NotificationEvent, FALSE);
373
374 /* Set the offset and build the read IRP */
375 PartitionOffset.QuadPart = 0x8000;
376 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
377 DeviceObject,
378 Buffer,
379 2048,
380 &PartitionOffset,
381 &Event,
382 &IoStatusBlock);
383 if (!Irp)
384 {
385 /* Free the string and fail */
386 RtlFreeUnicodeString(&DeviceName);
387 return FALSE;
388 }
389
390 /* Call the driver and check if we have to wait on it */
391 Status = IoCallDriver(DeviceObject, Irp);
392 if (Status == STATUS_PENDING)
393 {
394 /* Wait for completion */
395 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
396 Status = IoStatusBlock.Status;
397 }
398
399 /* Dereference the file object */
400 ObDereferenceObject(FileObject);
401 if (!NT_SUCCESS(Status)) return FALSE;
402
403 /* Now calculate the checksum */
404 for (i = 0; i < 2048 / sizeof(ULONG); i++) CheckSum += Buffer[i];
405
406 /*
407 * FIXME: In normal conditions, NTLDR/FreeLdr sends the *proper* CDROM
408 * ARC Path name, and what happens here is a comparision of both checksums
409 * in order to see if this is the actual boot CD.
410 *
411 * In ReactOS this doesn't currently happen, instead we have a hack on top
412 * of this file which scans the CD for the ntoskrnl.exe file, then modifies
413 * the LoaderBlock's ARC Path with the right CDROM path. Consequently, we
414 * get the same state as if NTLDR had properly booted us, except that we do
415 * not actually need to check the signature, since the hack already did the
416 * check for ntoskrnl.exe, which is just as good.
417 *
418 * The signature code stays however, because eventually FreeLDR will work
419 * like NTLDR, and, conversly, we do want to be able to be booted by NTLDR.
420 */
421 if (IopApplyRosCdromArcHack(DiskNumber))
422 {
423 /* This is the boot CD-ROM, build the ARC name */
424 sprintf(ArcBuffer, "\\ArcName\\%s", KeLoaderBlock->ArcBootDeviceName);
425
426 /* Convert it to Unicode */
427 RtlInitAnsiString(&ArcNameString, ArcBuffer);
428 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcNameString, TRUE);
429 if (!NT_SUCCESS(Status)) return FALSE;
430
431 /* Create the symbolic link and free the strings */
432 IoAssignArcName(&ArcName, &DeviceName);
433 RtlFreeUnicodeString(&ArcName);
434 RtlFreeUnicodeString(&DeviceName);
435
436 /* Let caller know that we've found the boot CD */
437 return TRUE;
438 }
439
440 /* No boot CD found */
441 return FALSE;
442 }
443
444 NTSTATUS INIT_FUNCTION
445 IoCreateArcNames(VOID)
446 {
447 PCONFIGURATION_INFORMATION ConfigInfo;
448 ULONG i;
449 NTSTATUS Status;
450 PLIST_ENTRY BiosDiskListHead, Entry;
451 LIST_ENTRY DiskListHead;
452 PARC_DISK_SIGNATURE ArcDiskEntry;
453 PDISKENTRY DiskEntry;
454 BOOLEAN FoundBoot = FALSE;
455 PULONG Buffer;
456
457 ConfigInfo = IoGetConfigurationInformation();
458
459 /* Get the boot ARC disk list */
460 BiosDiskListHead = &KeLoaderBlock->ArcDiskInformation->
461 DiskSignatureListHead;
462
463 /* Enumerate system disks */
464 InitializeListHead(&DiskListHead);
465 IopEnumerateDisks(&DiskListHead);
466
467 while (!IsListEmpty(BiosDiskListHead))
468 {
469 Entry = RemoveHeadList(BiosDiskListHead);
470 ArcDiskEntry = CONTAINING_RECORD(Entry, ARC_DISK_SIGNATURE, ListEntry);
471 Entry = DiskListHead.Flink;
472 while (Entry != &DiskListHead)
473 {
474 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
475 DPRINT1("Entry: %s\n", ArcDiskEntry->ArcName);
476 if (DiskEntry->Checksum == ArcDiskEntry->CheckSum &&
477 DiskEntry->Signature == ArcDiskEntry->Signature)
478 {
479 Status = IopAssignArcNamesToDisk(DiskEntry->DeviceObject,
480 ArcDiskEntry->ArcName,
481 DiskEntry->DiskNumber,
482 DiskEntry->PartitionCount,
483 &FoundBoot);
484
485 RemoveEntryList(&DiskEntry->ListEntry);
486 ExFreePool(DiskEntry);
487 break;
488 }
489 Entry = Entry->Flink;
490 }
491 }
492
493 while (!IsListEmpty(&DiskListHead))
494 {
495 Entry = RemoveHeadList(&DiskListHead);
496 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
497 ExFreePool(DiskEntry);
498 }
499
500 /* Check if we didn't find the boot disk */
501 if (!FoundBoot)
502 {
503 /* Allocate a buffer for the CD-ROM MBR */
504 Buffer = ExAllocatePoolWithTag(NonPagedPool, 2048, TAG_IO);
505 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
506
507 /* Loop every CD-ROM */
508 for (i = 0; i < ConfigInfo->CdRomCount; i++)
509 {
510 /* Give it an ARC name */
511 if (IopAssignArcNamesToCdrom(Buffer, i)) break;
512 }
513
514 /* Free the buffer */
515 ExFreePool(Buffer);
516 }
517
518 /* Return success */
519 return STATUS_SUCCESS;
520 }
521
522 NTSTATUS
523 NTAPI
524 IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
525 OUT PANSI_STRING NtBootPath)
526 {
527 OBJECT_ATTRIBUTES ObjectAttributes;
528 NTSTATUS Status;
529 CHAR Buffer[256], AnsiBuffer[256];
530 WCHAR ArcNameBuffer[64];
531 ANSI_STRING TargetString, ArcString, TempString;
532 UNICODE_STRING LinkName, TargetName, ArcName;
533 HANDLE LinkHandle;
534
535 /* Create the Unicode name for the current ARC boot device */
536 sprintf(Buffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
537 RtlInitAnsiString(&TargetString, Buffer);
538 Status = RtlAnsiStringToUnicodeString(&TargetName, &TargetString, TRUE);
539 if (!NT_SUCCESS(Status)) return FALSE;
540
541 /* Initialize the attributes and open the link */
542 InitializeObjectAttributes(&ObjectAttributes,
543 &TargetName,
544 OBJ_CASE_INSENSITIVE,
545 NULL,
546 NULL);
547 Status = NtOpenSymbolicLinkObject(&LinkHandle,
548 SYMBOLIC_LINK_ALL_ACCESS,
549 &ObjectAttributes);
550 if (!NT_SUCCESS(Status))
551 {
552 /* We failed, free the string */
553 RtlFreeUnicodeString(&TargetName);
554 return FALSE;
555 }
556
557 /* Query the current \\SystemRoot */
558 ArcName.Buffer = ArcNameBuffer;
559 ArcName.Length = 0;
560 ArcName.MaximumLength = sizeof(ArcNameBuffer);
561 Status = NtQuerySymbolicLinkObject(LinkHandle, &ArcName, NULL);
562 if (!NT_SUCCESS(Status))
563 {
564 /* We failed, free the string */
565 RtlFreeUnicodeString(&TargetName);
566 return FALSE;
567 }
568
569 /* Convert it to Ansi */
570 ArcString.Buffer = AnsiBuffer;
571 ArcString.Length = 0;
572 ArcString.MaximumLength = sizeof(AnsiBuffer);
573 Status = RtlUnicodeStringToAnsiString(&ArcString, &ArcName, FALSE);
574 AnsiBuffer[ArcString.Length] = ANSI_NULL;
575
576 /* Close the link handle and free the name */
577 ObCloseHandle(LinkHandle, KernelMode);
578 RtlFreeUnicodeString(&TargetName);
579
580 /* Setup the system root name again */
581 RtlInitAnsiString(&TempString, "\\SystemRoot");
582 Status = RtlAnsiStringToUnicodeString(&LinkName, &TempString, TRUE);
583 if (!NT_SUCCESS(Status)) return FALSE;
584
585 /* Open the symbolic link for it */
586 InitializeObjectAttributes(&ObjectAttributes,
587 &LinkName,
588 OBJ_CASE_INSENSITIVE,
589 NULL,
590 NULL);
591 Status = NtOpenSymbolicLinkObject(&LinkHandle,
592 SYMBOLIC_LINK_ALL_ACCESS,
593 &ObjectAttributes);
594 if (!NT_SUCCESS(Status)) return FALSE;
595
596 /* Destroy it */
597 NtMakeTemporaryObject(LinkHandle);
598 ObCloseHandle(LinkHandle, KernelMode);
599
600 /* Now create the new name for it */
601 sprintf(Buffer, "%s%s", ArcString.Buffer, LoaderBlock->NtBootPathName);
602
603 /* Copy it into the passed parameter and null-terminate it */
604 RtlCopyString(NtBootPath, &ArcString);
605 Buffer[strlen(Buffer) - 1] = ANSI_NULL;
606
607 /* Setup the Unicode-name for the new symbolic link value */
608 RtlInitAnsiString(&TargetString, Buffer);
609 InitializeObjectAttributes(&ObjectAttributes,
610 &LinkName,
611 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
612 NULL,
613 NULL);
614 Status = RtlAnsiStringToUnicodeString(&ArcName, &TargetString, TRUE);
615 if (!NT_SUCCESS(Status)) return FALSE;
616
617 /* Create it */
618 Status = NtCreateSymbolicLinkObject(&LinkHandle,
619 SYMBOLIC_LINK_ALL_ACCESS,
620 &ObjectAttributes,
621 &ArcName);
622
623 /* Free all the strings and close the handle and return success */
624 RtlFreeUnicodeString(&ArcName);
625 RtlFreeUnicodeString(&LinkName);
626 ObCloseHandle(LinkHandle, KernelMode);
627 return TRUE;
628 }
629
630 /* EOF */