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)
12 /* INCLUDES *****************************************************************/
16 #include <internal/debug.h>
18 /* MACROS *******************************************************************/
20 #define FS_VOLUME_BUFFER_SIZE (MAX_PATH + sizeof(FILE_FS_VOLUME_INFORMATION))
22 /* FUNCTIONS ****************************************************************/
28 DiskQueryRoutine(PWSTR ValueName
,
35 PLIST_ENTRY ListHead
= (PLIST_ENTRY
)Context
;
36 PULONG GlobalDiskCount
= (PULONG
)EntryContext
;
40 if (ValueType
== REG_SZ
&&
41 ValueLength
== 20 * sizeof(WCHAR
))
43 DiskEntry
= ExAllocatePool(PagedPool
, sizeof(DISKENTRY
));
44 if (DiskEntry
== NULL
)
46 return STATUS_NO_MEMORY
;
48 DiskEntry
->DiskNumber
= (*GlobalDiskCount
)++;
50 NameU
.Buffer
= (PWCHAR
)ValueData
;
51 NameU
.Length
= NameU
.MaximumLength
= 8 * sizeof(WCHAR
);
52 RtlUnicodeStringToInteger(&NameU
, 16, &DiskEntry
->Checksum
);
54 NameU
.Buffer
= (PWCHAR
)ValueData
+ 9;
55 RtlUnicodeStringToInteger(&NameU
, 16, &DiskEntry
->Signature
);
57 InsertTailList(ListHead
, &DiskEntry
->ListEntry
);
60 return STATUS_SUCCESS
;
63 #define ROOT_NAME L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter"
65 static VOID INIT_FUNCTION
66 IopEnumerateBiosDisks(PLIST_ENTRY ListHead
)
68 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
71 ULONG ControllerCount
;
74 ULONG GlobalDiskCount
=0;
77 memset(QueryTable
, 0, sizeof(QueryTable
));
78 QueryTable
[0].Name
= L
"Identifier";
79 QueryTable
[0].QueryRoutine
= DiskQueryRoutine
;
80 QueryTable
[0].EntryContext
= (PVOID
)&GlobalDiskCount
;
85 swprintf(Name
, L
"%s\\%lu", ROOT_NAME
, AdapterCount
);
86 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
91 if (!NT_SUCCESS(Status
))
96 swprintf(Name
, L
"%s\\%lu\\DiskController", ROOT_NAME
, AdapterCount
);
97 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
102 if (NT_SUCCESS(Status
))
107 swprintf(Name
, L
"%s\\%lu\\DiskController\\%lu", ROOT_NAME
, AdapterCount
, ControllerCount
);
108 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
113 if (!NT_SUCCESS(Status
))
118 swprintf(Name
, L
"%s\\%lu\\DiskController\\%lu\\DiskPeripheral", ROOT_NAME
, AdapterCount
, ControllerCount
);
119 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
124 if (NT_SUCCESS(Status
))
129 swprintf(Name
, L
"%s\\%lu\\DiskController\\%lu\\DiskPeripheral\\%lu", ROOT_NAME
, AdapterCount
, ControllerCount
, DiskCount
);
130 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
135 if (!NT_SUCCESS(Status
))
152 IopApplyRosCdromArcHack(IN ULONG i
)
154 ULONG DeviceNumber
= -1;
155 OBJECT_ATTRIBUTES ObjectAttributes
;
156 UNICODE_STRING DeviceName
;
157 WCHAR Buffer
[MAX_PATH
];
158 CHAR AnsiBuffer
[MAX_PATH
];
159 FILE_BASIC_INFORMATION FileInfo
;
163 /* Only ARC Name left - Build full ARC Name */
164 p
= strstr(KeLoaderBlock
->ArcBootDeviceName
, "cdrom");
167 /* Try to find the installer */
168 swprintf(Buffer
, L
"\\Device\\CdRom%lu\\reactos\\ntoskrnl.exe", i
);
169 RtlInitUnicodeString(&DeviceName
, Buffer
);
170 InitializeObjectAttributes(&ObjectAttributes
,
175 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &FileInfo
);
176 if (NT_SUCCESS(Status
)) DeviceNumber
= i
;
178 /* Try to find live CD boot */
180 L
"\\Device\\CdRom%lu\\reactos\\system32\\ntoskrnl.exe",
182 RtlInitUnicodeString(&DeviceName
, Buffer
);
183 InitializeObjectAttributes(&ObjectAttributes
,
188 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &FileInfo
);
189 if (NT_SUCCESS(Status
)) DeviceNumber
= i
;
192 sprintf(p
, "cdrom(%lu)", DeviceNumber
);
194 /* Adjust original command line */
199 strcpy(AnsiBuffer
, q
);
200 sprintf(p
, "cdrom(%lu)", DeviceNumber
);
201 strcat(p
, AnsiBuffer
);
205 /* Return whether this is the CD or not */
206 if (DeviceNumber
!= 1) return TRUE
;
213 IopEnumerateDisks(IN PLIST_ENTRY ListHead
)
216 ANSI_STRING TempString
;
218 UNICODE_STRING DeviceName
;
220 PDEVICE_OBJECT DeviceObject
;
221 PFILE_OBJECT FileObject
;
222 DISK_GEOMETRY DiskGeometry
;
223 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
226 IO_STATUS_BLOCK StatusBlock
;
227 LARGE_INTEGER PartitionOffset
;
228 PPARTITION_SECTOR PartitionBuffer
;
229 PDISKENTRY DiskEntry
;
231 /* Loop every detected disk */
232 for (i
= 0; i
< IoGetConfigurationInformation()->DiskCount
; i
++)
235 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", i
);
237 /* Convert it to Unicode */
238 RtlInitAnsiString(&TempString
, Buffer
);
239 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
240 if (!NT_SUCCESS(Status
)) continue;
242 /* Get the device pointer */
243 Status
= IoGetDeviceObjectPointer(&DeviceName
,
248 /* Free the string */
249 RtlFreeUnicodeString(&DeviceName
);
251 /* Move on if we failed */
252 if (!NT_SUCCESS(Status
)) continue;
254 /* Allocate the ROS disk Entry */
255 DiskEntry
= ExAllocatePoolWithTag(PagedPool
, sizeof(DISKENTRY
), TAG_IO
);
256 DiskEntry
->DiskNumber
= i
;
257 DiskEntry
->DeviceObject
= DeviceObject
;
259 /* Build an IRP to determine the sector size */
260 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
261 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY
,
266 sizeof(DISK_GEOMETRY
),
273 ObDereferenceObject(FileObject
);
277 /* Call the driver and check if we have to wait on it */
278 Status
= IoCallDriver(DeviceObject
, Irp
);
279 if (Status
== STATUS_PENDING
)
281 /* Wait on the driver */
282 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
283 Status
= StatusBlock
.Status
;
286 /* Check if we failed */
287 if (!NT_SUCCESS(Status
))
290 ObDereferenceObject(FileObject
);
294 /* Read the partition table */
295 Status
= IoReadPartitionTable(DeviceObject
,
296 DiskGeometry
.BytesPerSector
,
300 /* Dereference the file object */
301 ObDereferenceObject(FileObject
);
302 if (!NT_SUCCESS(Status
)) continue;
304 /* Set the offset to 0 */
305 PartitionOffset
.QuadPart
= 0;
307 /* Allocate a buffer for the partition */
308 PartitionBuffer
= ExAllocatePoolWithTag(NonPagedPool
,
309 DiskGeometry
.BytesPerSector
,
311 if (!PartitionBuffer
) continue;
313 /* Build an IRP to read the partition sector */
314 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
315 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
318 DiskGeometry
.BytesPerSector
,
323 /* Call the driver and check if we have to wait */
324 Status
= IoCallDriver(DeviceObject
, Irp
);
325 if (Status
== STATUS_PENDING
)
327 /* Wait for completion */
328 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
329 Status
= StatusBlock
.Status
;
332 /* Check if we failed */
333 if (!NT_SUCCESS(Status
))
336 ExFreePool(PartitionBuffer
);
337 ExFreePool(DriveLayout
);
341 /* Calculate the MBR checksum */
342 DiskEntry
->Checksum
= 0;
343 for (j
= 0; j
< 128; j
++)
345 DiskEntry
->Checksum
+= ((PULONG
)PartitionBuffer
)[j
];
348 /* Save the signature and checksum */
349 DiskEntry
->Checksum
= ~DiskEntry
->Checksum
+ 1;
350 DiskEntry
->Signature
= DriveLayout
->Signature
;
351 DiskEntry
->PartitionCount
= DriveLayout
->PartitionCount
;
353 /* Insert it into the list */
354 InsertTailList(ListHead
, &DiskEntry
->ListEntry
);
356 /* Free the buffer */
357 ExFreePool(PartitionBuffer
);
358 ExFreePool(DriveLayout
);
365 IopAssignArcNamesToDisk(IN PDEVICE_OBJECT DeviceObject
,
368 IN ULONG PartitionCount
,
369 IN PBOOLEAN FoundHdBoot
)
373 ANSI_STRING TempString
, ArcNameString
, BootString
;
374 ANSI_STRING ArcBootString
, ArcSystemString
;
375 UNICODE_STRING DeviceName
, ArcName
, BootPath
;
379 /* HACK: Build the ARC name that FreeLDR should've given us */
380 CHAR BootArcName
[256]; // should come from FREELDR
381 sprintf(BootArcName
, "multi(0)disk(0)rdisk(%lu)", RDisk
);
384 *FoundHdBoot
= FALSE
;
386 /* Build the boot strings */
387 RtlInitAnsiString(&ArcBootString
, KeLoaderBlock
->ArcBootDeviceName
);
388 RtlInitAnsiString(&ArcSystemString
, KeLoaderBlock
->ArcHalDeviceName
);
390 /* Build the NT Device Name */
391 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", DiskNumber
);
393 /* Convert it to unicode */
394 RtlInitAnsiString(&TempString
, Buffer
);
395 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
396 if (!NT_SUCCESS(Status
)) return Status
;
398 /* Build the ARC Device Name */
399 sprintf(ArcBuffer
, "\\ArcName\\%s", BootArcName
);
401 /* Convert it to Unicode */
402 RtlInitAnsiString(&ArcNameString
, ArcBuffer
);
403 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcNameString
, TRUE
);
404 if (!NT_SUCCESS(Status
)) return Status
;
406 /* Create the symbolic link and free the strings */
407 IoAssignArcName(&ArcName
, &DeviceName
);
408 RtlFreeUnicodeString(&ArcName
);
409 RtlFreeUnicodeString(&DeviceName
);
411 /* Loop all the partitions */
412 for (i
= 0; i
< PartitionCount
; i
++)
414 /* Build the partition device name */
415 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber
, i
+1);
417 /* Convert it to Unicode */
418 RtlInitAnsiString(&TempString
, Buffer
);
419 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
420 if (!NT_SUCCESS(Status
)) continue;
422 /* Build the partial ARC name for this partition */
423 sprintf(ArcBuffer
, "%spartition(%lu)", BootArcName
, i
+ 1);
424 RtlInitAnsiString(&ArcNameString
, ArcBuffer
);
426 /* Check if this is the boot device */
427 if (RtlEqualString(&ArcNameString
, &ArcBootString
, TRUE
))
429 /* Remember that we found a Hard Disk Boot Device */
433 /* Check if it's the system boot partition */
434 if (RtlEqualString(&ArcNameString
, &ArcSystemString
, TRUE
))
436 /* It is, create a Unicode string for it */
437 RtlInitAnsiString(&BootString
, KeLoaderBlock
->NtHalPathName
);
438 Status
= RtlAnsiStringToUnicodeString(&BootPath
, &BootString
, TRUE
);
439 if (NT_SUCCESS(Status
))
441 /* FIXME: Save in registry */
443 /* Free the string now */
444 RtlFreeUnicodeString(&BootPath
);
448 /* Build the full ARC name */
449 sprintf(Buffer
, "\\ArcName\\%spartition(%lu)", BootArcName
, i
+ 1);
451 /* Convert it to Unicode */
452 RtlInitAnsiString(&ArcNameString
, Buffer
);
453 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcNameString
, TRUE
);
454 if (!NT_SUCCESS(Status
)) continue;
456 /* Create the symbolic link and free the strings */
457 IoAssignArcName(&ArcName
, &DeviceName
);
458 RtlFreeUnicodeString(&ArcName
);
459 RtlFreeUnicodeString(&DeviceName
);
463 return STATUS_SUCCESS
;
469 IopAssignArcNamesToCdrom(IN PULONG Buffer
,
473 ANSI_STRING TempString
, ArcNameString
;
474 UNICODE_STRING DeviceName
, ArcName
;
476 LARGE_INTEGER PartitionOffset
;
478 IO_STATUS_BLOCK IoStatusBlock
;
480 ULONG i
, CheckSum
= 0;
481 PDEVICE_OBJECT DeviceObject
;
482 PFILE_OBJECT FileObject
;
484 /* Build the device name */
485 sprintf(ArcBuffer
, "\\Device\\CdRom%lu", DiskNumber
);
487 /* Convert it to Unicode */
488 RtlInitAnsiString(&TempString
, ArcBuffer
);
489 Status
= RtlAnsiStringToUnicodeString(&DeviceName
, &TempString
, TRUE
);
490 if (!NT_SUCCESS(Status
)) return FALSE
;
492 /* Get the device for it */
493 Status
= IoGetDeviceObjectPointer(&DeviceName
,
494 FILE_READ_ATTRIBUTES
,
497 if (!NT_SUCCESS(Status
))
499 /* Free the string and fail */
500 RtlFreeUnicodeString(&DeviceName
);
504 /* Setup the event */
505 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
507 /* Set the offset and build the read IRP */
508 PartitionOffset
.QuadPart
= 0x8000;
509 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
518 /* Free the string and fail */
519 RtlFreeUnicodeString(&DeviceName
);
523 /* Call the driver and check if we have to wait on it */
524 Status
= IoCallDriver(DeviceObject
, Irp
);
525 if (Status
== STATUS_PENDING
)
527 /* Wait for completion */
528 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
529 Status
= IoStatusBlock
.Status
;
532 /* Dereference the file object */
533 ObDereferenceObject(FileObject
);
534 if (!NT_SUCCESS(Status
)) return FALSE
;
536 /* Now calculate the checksum */
537 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++) CheckSum
+= Buffer
[i
];
540 * FIXME: In normal conditions, NTLDR/FreeLdr sends the *proper* CDROM
541 * ARC Path name, and what happens here is a comparision of both checksums
542 * in order to see if this is the actual boot CD.
544 * In ReactOS this doesn't currently happen, instead we have a hack on top
545 * of this file which scans the CD for the ntoskrnl.exe file, then modifies
546 * the LoaderBlock's ARC Path with the right CDROM path. Consequently, we
547 * get the same state as if NTLDR had properly booted us, except that we do
548 * not actually need to check the signature, since the hack already did the
549 * check for ntoskrnl.exe, which is just as good.
551 * The signature code stays however, because eventually FreeLDR will work
552 * like NTLDR, and, conversly, we do want to be able to be booted by NTLDR.
554 if (IopApplyRosCdromArcHack(DiskNumber
))
556 /* This is the boot CD-ROM, build the ARC name */
557 sprintf(ArcBuffer
, "\\ArcName\\%s", KeLoaderBlock
->ArcBootDeviceName
);
559 /* Convert it to Unicode */
560 RtlInitAnsiString(&ArcNameString
, ArcBuffer
);
561 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &ArcNameString
, TRUE
);
562 if (!NT_SUCCESS(Status
)) return FALSE
;
564 /* Create the symbolic link and free the strings */
565 IoAssignArcName(&ArcName
, &DeviceName
);
566 RtlFreeUnicodeString(&ArcName
);
567 RtlFreeUnicodeString(&DeviceName
);
569 /* Let caller know that we've found the boot CD */
573 /* No boot CD found */
577 NTSTATUS INIT_FUNCTION
578 IoCreateArcNames(VOID
)
580 PCONFIGURATION_INFORMATION ConfigInfo
;
581 ULONG i
, RDiskNumber
;
583 LIST_ENTRY BiosDiskListHead
;
584 LIST_ENTRY DiskListHead
;
586 PDISKENTRY BiosDiskEntry
;
587 PDISKENTRY DiskEntry
;
591 ConfigInfo
= IoGetConfigurationInformation();
593 /* create ARC names for hard disk drives */
594 InitializeListHead(&BiosDiskListHead
);
595 InitializeListHead(&DiskListHead
);
596 IopEnumerateBiosDisks(&BiosDiskListHead
);
597 IopEnumerateDisks(&DiskListHead
);
600 while (!IsListEmpty(&BiosDiskListHead
))
602 Entry
= RemoveHeadList(&BiosDiskListHead
);
603 BiosDiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
604 Entry
= DiskListHead
.Flink
;
605 while (Entry
!= &DiskListHead
)
607 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
608 if (DiskEntry
->Checksum
== BiosDiskEntry
->Checksum
&&
609 DiskEntry
->Signature
== BiosDiskEntry
->Signature
)
612 Status
= IopAssignArcNamesToDisk(DiskEntry
->DeviceObject
,
614 DiskEntry
->DiskNumber
,
615 DiskEntry
->PartitionCount
,
618 RemoveEntryList(&DiskEntry
->ListEntry
);
619 ExFreePool(DiskEntry
);
622 Entry
= Entry
->Flink
;
625 ExFreePool(BiosDiskEntry
);
628 while (!IsListEmpty(&DiskListHead
))
630 Entry
= RemoveHeadList(&DiskListHead
);
631 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
632 ExFreePool(DiskEntry
);
635 /* Check if we didn't find the boot disk */
638 /* Allocate a buffer for the CD-ROM MBR */
639 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, 2048, TAG_IO
);
640 if (!Buffer
) return STATUS_INSUFFICIENT_RESOURCES
;
642 /* Loop every CD-ROM */
643 for (i
= 0; i
< ConfigInfo
->CdRomCount
; i
++)
645 /* Give it an ARC name */
646 if (IopAssignArcNamesToCdrom(Buffer
, i
)) break;
649 /* Free the buffer */
654 return STATUS_SUCCESS
;
659 IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock
,
660 OUT PANSI_STRING NtBootPath
)
662 OBJECT_ATTRIBUTES ObjectAttributes
;
664 CHAR Buffer
[256], AnsiBuffer
[256];
665 WCHAR ArcNameBuffer
[64];
666 ANSI_STRING TargetString
, ArcString
, TempString
;
667 UNICODE_STRING LinkName
, TargetName
, ArcName
;
670 /* Create the Unicode name for the current ARC boot device */
671 sprintf(Buffer
, "\\ArcName\\%s", LoaderBlock
->ArcBootDeviceName
);
672 RtlInitAnsiString(&TargetString
, Buffer
);
673 Status
= RtlAnsiStringToUnicodeString(&TargetName
, &TargetString
, TRUE
);
674 if (!NT_SUCCESS(Status
)) return FALSE
;
676 /* Initialize the attributes and open the link */
677 InitializeObjectAttributes(&ObjectAttributes
,
679 OBJ_CASE_INSENSITIVE
,
682 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
683 SYMBOLIC_LINK_ALL_ACCESS
,
685 if (!NT_SUCCESS(Status
))
687 /* We failed, free the string */
688 RtlFreeUnicodeString(&TargetName
);
692 /* Query the current \\SystemRoot */
693 ArcName
.Buffer
= ArcNameBuffer
;
695 ArcName
.MaximumLength
= sizeof(ArcNameBuffer
);
696 Status
= NtQuerySymbolicLinkObject(LinkHandle
, &ArcName
, NULL
);
697 if (!NT_SUCCESS(Status
))
699 /* We failed, free the string */
700 RtlFreeUnicodeString(&TargetName
);
704 /* Convert it to Ansi */
705 ArcString
.Buffer
= AnsiBuffer
;
706 ArcString
.Length
= 0;
707 ArcString
.MaximumLength
= sizeof(AnsiBuffer
);
708 Status
= RtlUnicodeStringToAnsiString(&ArcString
, &ArcName
, FALSE
);
709 AnsiBuffer
[ArcString
.Length
] = ANSI_NULL
;
711 /* Close the link handle and free the name */
712 ObCloseHandle(LinkHandle
, KernelMode
);
713 RtlFreeUnicodeString(&TargetName
);
715 /* Setup the system root name again */
716 RtlInitAnsiString(&TempString
, "\\SystemRoot");
717 Status
= RtlAnsiStringToUnicodeString(&LinkName
, &TempString
, TRUE
);
718 if (!NT_SUCCESS(Status
)) return FALSE
;
720 /* Open the symbolic link for it */
721 InitializeObjectAttributes(&ObjectAttributes
,
723 OBJ_CASE_INSENSITIVE
,
726 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
727 SYMBOLIC_LINK_ALL_ACCESS
,
729 if (!NT_SUCCESS(Status
)) return FALSE
;
732 NtMakeTemporaryObject(LinkHandle
);
733 ObCloseHandle(LinkHandle
, KernelMode
);
735 /* Now create the new name for it */
736 sprintf(Buffer
, "%s%s", ArcString
.Buffer
, LoaderBlock
->NtBootPathName
);
738 /* Copy it into the passed parameter and null-terminate it */
739 RtlCopyString(NtBootPath
, &ArcString
);
740 Buffer
[strlen(Buffer
) - 1] = ANSI_NULL
;
742 /* Setup the Unicode-name for the new symbolic link value */
743 RtlInitAnsiString(&TargetString
, Buffer
);
744 InitializeObjectAttributes(&ObjectAttributes
,
746 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
749 Status
= RtlAnsiStringToUnicodeString(&ArcName
, &TargetString
, TRUE
);
750 if (!NT_SUCCESS(Status
)) return FALSE
;
753 Status
= NtCreateSymbolicLinkObject(&LinkHandle
,
754 SYMBOLIC_LINK_ALL_ACCESS
,
758 /* Free all the strings and close the handle and return success */
759 RtlFreeUnicodeString(&ArcName
);
760 RtlFreeUnicodeString(&LinkName
);
761 ObCloseHandle(LinkHandle
, KernelMode
);