- Part 2.5 of ARC Boot changes. Re-factor IoCreateArcNames not to use a list-entry...
[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 /* 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 BOOLEAN
25 INIT_FUNCTION
26 NTAPI
27 IopApplyRosCdromArcHack(IN ULONG i)
28 {
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;
35 NTSTATUS Status;
36 PCHAR p, q;
37
38 /* Only ARC Name left - Build full ARC Name */
39 p = strstr(KeLoaderBlock->ArcBootDeviceName, "cdrom");
40 if (p)
41 {
42 /* Try to find the installer */
43 swprintf(Buffer, L"\\Device\\CdRom%lu\\reactos\\ntoskrnl.exe", i);
44 RtlInitUnicodeString(&DeviceName, Buffer);
45 InitializeObjectAttributes(&ObjectAttributes,
46 &DeviceName,
47 0,
48 NULL,
49 NULL);
50 Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo);
51 if (NT_SUCCESS(Status)) DeviceNumber = i;
52
53 /* Try to find live CD boot */
54 swprintf(Buffer,
55 L"\\Device\\CdRom%lu\\reactos\\system32\\ntoskrnl.exe",
56 i);
57 RtlInitUnicodeString(&DeviceName, Buffer);
58 InitializeObjectAttributes(&ObjectAttributes,
59 &DeviceName,
60 0,
61 NULL,
62 NULL);
63 Status = ZwQueryAttributesFile(&ObjectAttributes, &FileInfo);
64 if (NT_SUCCESS(Status)) DeviceNumber = i;
65
66 /* Build the name */
67 sprintf(p, "cdrom(%lu)", DeviceNumber);
68
69 /* Adjust original command line */
70 q = strchr(p, ')');
71 if (q)
72 {
73 q++;
74 strcpy(AnsiBuffer, q);
75 sprintf(p, "cdrom(%lu)", DeviceNumber);
76 strcat(p, AnsiBuffer);
77 }
78 }
79
80 /* Return whether this is the CD or not */
81 if (DeviceNumber != 1) return TRUE;
82 return FALSE;
83 }
84
85 BOOLEAN
86 INIT_FUNCTION
87 NTAPI
88 IopGetDiskInformation(IN ULONG i,
89 OUT PULONG CheckSum,
90 OUT PULONG Signature,
91 OUT PULONG PartitionCount,
92 OUT PDEVICE_OBJECT *DiskDeviceObject)
93 {
94 ULONG j, Checksum;
95 ANSI_STRING TempString;
96 CHAR Buffer[256];
97 UNICODE_STRING DeviceName;
98 NTSTATUS Status;
99 PDEVICE_OBJECT DeviceObject;
100 PFILE_OBJECT FileObject;
101 DISK_GEOMETRY DiskGeometry;
102 PDRIVE_LAYOUT_INFORMATION DriveLayout;
103 KEVENT Event;
104 PIRP Irp;
105 IO_STATUS_BLOCK StatusBlock;
106 LARGE_INTEGER PartitionOffset;
107 PPARTITION_SECTOR PartitionBuffer;
108
109 /* Build the name */
110 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", i);
111
112 /* Convert it to Unicode */
113 RtlInitAnsiString(&TempString, Buffer);
114 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
115 if (!NT_SUCCESS(Status)) return FALSE;
116
117 /* Get the device pointer */
118 Status = IoGetDeviceObjectPointer(&DeviceName,
119 FILE_READ_DATA,
120 &FileObject,
121 &DeviceObject);
122 *DiskDeviceObject = DeviceObject;
123
124 /* Free the string */
125 RtlFreeUnicodeString(&DeviceName);
126
127 /* Move on if we failed */
128 if (!NT_SUCCESS(Status)) return FALSE;
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 return FALSE;
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 return FALSE;
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)) return FALSE;
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) return FALSE;
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 return FALSE;
210 }
211
212 /* Calculate the MBR checksum */
213 Checksum = 0;
214 for (j = 0; j < 128; j++)
215 {
216 Checksum += ((PULONG)PartitionBuffer)[j];
217 }
218
219 /* Save the signature and checksum */
220 *CheckSum = ~Checksum + 1;
221 *Signature = DriveLayout->Signature;
222 *PartitionCount = DriveLayout->PartitionCount;
223
224 /* Free the buffer */
225 ExFreePool(PartitionBuffer);
226 ExFreePool(DriveLayout);
227 return TRUE;
228 }
229
230 BOOLEAN
231 INIT_FUNCTION
232 NTAPI
233 IopAssignArcNamesToCdrom(IN PULONG Buffer,
234 IN ULONG DiskNumber)
235 {
236 CHAR ArcBuffer[256];
237 ANSI_STRING TempString, ArcNameString;
238 UNICODE_STRING DeviceName, ArcName;
239 NTSTATUS Status;
240 LARGE_INTEGER PartitionOffset;
241 KEVENT Event;
242 IO_STATUS_BLOCK IoStatusBlock;
243 PIRP Irp;
244 ULONG i, CheckSum = 0;
245 PDEVICE_OBJECT DeviceObject;
246 PFILE_OBJECT FileObject;
247
248 /* Build the device name */
249 sprintf(ArcBuffer, "\\Device\\CdRom%lu", DiskNumber);
250
251 /* Convert it to Unicode */
252 RtlInitAnsiString(&TempString, ArcBuffer);
253 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
254 if (!NT_SUCCESS(Status)) return FALSE;
255
256 /* Get the device for it */
257 Status = IoGetDeviceObjectPointer(&DeviceName,
258 FILE_READ_ATTRIBUTES,
259 &FileObject,
260 &DeviceObject);
261 if (!NT_SUCCESS(Status))
262 {
263 /* Free the string and fail */
264 RtlFreeUnicodeString(&DeviceName);
265 return FALSE;
266 }
267
268 /* Setup the event */
269 KeInitializeEvent(&Event, NotificationEvent, FALSE);
270
271 /* Set the offset and build the read IRP */
272 PartitionOffset.QuadPart = 0x8000;
273 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
274 DeviceObject,
275 Buffer,
276 2048,
277 &PartitionOffset,
278 &Event,
279 &IoStatusBlock);
280 if (!Irp)
281 {
282 /* Free the string and fail */
283 RtlFreeUnicodeString(&DeviceName);
284 return FALSE;
285 }
286
287 /* Call the driver and check if we have to wait on it */
288 Status = IoCallDriver(DeviceObject, Irp);
289 if (Status == STATUS_PENDING)
290 {
291 /* Wait for completion */
292 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
293 Status = IoStatusBlock.Status;
294 }
295
296 /* Dereference the file object */
297 ObDereferenceObject(FileObject);
298 if (!NT_SUCCESS(Status)) return FALSE;
299
300 /* Now calculate the checksum */
301 for (i = 0; i < 2048 / sizeof(ULONG); i++) CheckSum += Buffer[i];
302
303 /*
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.
307 *
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.
314 *
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.
317 */
318 if (IopApplyRosCdromArcHack(DiskNumber))
319 {
320 /* This is the boot CD-ROM, build the ARC name */
321 sprintf(ArcBuffer, "\\ArcName\\%s", KeLoaderBlock->ArcBootDeviceName);
322
323 /* Convert it to Unicode */
324 RtlInitAnsiString(&ArcNameString, ArcBuffer);
325 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcNameString, TRUE);
326 if (!NT_SUCCESS(Status)) return FALSE;
327
328 /* Create the symbolic link and free the strings */
329 IoAssignArcName(&ArcName, &DeviceName);
330 RtlFreeUnicodeString(&ArcName);
331 RtlFreeUnicodeString(&DeviceName);
332
333 /* Let caller know that we've found the boot CD */
334 return TRUE;
335 }
336
337 /* No boot CD found */
338 return FALSE;
339 }
340
341 NTSTATUS
342 INIT_FUNCTION
343 IoCreateArcNames(VOID)
344 {
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;
351 BOOLEAN SingleDisk;
352 ULONG i, j, Length;
353 PDEVICE_OBJECT DeviceObject;
354 ULONG Signature, Checksum, PartitionCount;
355 PLIST_ENTRY NextEntry;
356 PARC_DISK_SIGNATURE ArcDiskEntry;
357 NTSTATUS Status;
358 BOOLEAN FoundBoot = FALSE;
359 PULONG PartitionBuffer;
360
361 /* Check if we only have one disk on the machine */
362 SingleDisk = ArcDiskInfo->DiskSignatureListHead.Flink->Flink ==
363 (&ArcDiskInfo->DiskSignatureListHead);
364
365 /* Create the global HAL partition name */
366 sprintf(ArcBuffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName);
367 RtlInitAnsiString(&ArcString, ArcBuffer);
368 RtlAnsiStringToUnicodeString(&IoArcHalDeviceName, &ArcString, TRUE);
369
370 /* Create the global system partition name */
371 sprintf(ArcBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
372 RtlInitAnsiString(&ArcString, ArcBuffer);
373 RtlAnsiStringToUnicodeString(&IoArcBootDeviceName, &ArcString, TRUE);
374
375 /* Allocate memory for the string */
376 Length = strlen(LoaderBlock->ArcBootDeviceName) + sizeof(ANSI_NULL);
377 IoLoaderArcBootDeviceName = ExAllocatePoolWithTag(PagedPool,
378 Length,
379 TAG_IO);
380 if (IoLoaderArcBootDeviceName)
381 {
382 /* Copy the name */
383 RtlMoveMemory(IoLoaderArcBootDeviceName,
384 LoaderBlock->ArcBootDeviceName,
385 Length);
386 }
387
388 /* Check if we only found a disk, but we're booting from CD-ROM */
389 if ((SingleDisk) && strstr(LoaderBlock->ArcBootDeviceName, "cdrom"))
390 {
391 /* Then disable single-disk mode, since there's a CD drive out there */
392 SingleDisk = FALSE;
393 }
394
395 /* Build the boot strings */
396 RtlInitAnsiString(&ArcBootString, LoaderBlock->ArcBootDeviceName);
397 RtlInitAnsiString(&ArcSystemString, LoaderBlock->ArcHalDeviceName);
398
399 /* Loop every detected disk */
400 for (i = 0; i < ConfigInfo->DiskCount; i++)
401 {
402 /* Get information about the disk */
403 if (!IopGetDiskInformation(i,
404 &Checksum,
405 &Signature,
406 &PartitionCount,
407 &DeviceObject))
408 {
409 /* Skip this disk */
410 continue;
411 }
412
413 /* Loop ARC disks */
414 for (NextEntry = ArcDiskInfo->DiskSignatureListHead.Flink;
415 NextEntry != &ArcDiskInfo->DiskSignatureListHead;
416 NextEntry = NextEntry->Flink)
417 {
418 /* Get the current ARC disk signature entry */
419 ArcDiskEntry = CONTAINING_RECORD(NextEntry,
420 ARC_DISK_SIGNATURE,
421 ListEntry);
422
423 /*
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
427 */
428 if (((SingleDisk) && (ConfigInfo->DiskCount == 1)) ||
429 ((Checksum == ArcDiskEntry->CheckSum) &&
430 (Signature == ArcDiskEntry->Signature)))
431 {
432 /* Build the NT Device Name */
433 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", i);
434
435 /* Convert it to Unicode */
436 RtlInitAnsiString(&TempString, Buffer);
437 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
438 if (!NT_SUCCESS(Status)) continue;
439
440 /* Build the ARC Device Name */
441 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskEntry->ArcName);
442
443 /* Convert it to Unicode */
444 RtlInitAnsiString(&ArcString, ArcBuffer);
445 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcString, TRUE);
446 if (!NT_SUCCESS(Status)) continue;
447
448 /* Create the symbolic link and free the strings */
449 IoAssignArcName(&ArcName, &DeviceName);
450 RtlFreeUnicodeString(&ArcName);
451 RtlFreeUnicodeString(&DeviceName);
452
453 /* Loop all the partitions */
454 for (j = 0; j < PartitionCount; j++)
455 {
456 /* Build the partition device name */
457 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", i, j + 1);
458
459 /* Convert it to Unicode */
460 RtlInitAnsiString(&TempString, Buffer);
461 Status = RtlAnsiStringToUnicodeString(&DeviceName, &TempString, TRUE);
462 if (!NT_SUCCESS(Status)) continue;
463
464 /* Build the partial ARC name for this partition */
465 sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskEntry->ArcName, j + 1);
466 RtlInitAnsiString(&ArcString, ArcBuffer);
467
468 /* Check if this is the boot device */
469 if (RtlEqualString(&ArcString, &ArcBootString, TRUE))
470 {
471 /* Remember that we found a Hard Disk Boot Device */
472 FoundBoot = TRUE;
473 }
474
475 /* Check if it's the system boot partition */
476 if (RtlEqualString(&ArcString, &ArcSystemString, TRUE))
477 {
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))
482 {
483 /* FIXME: Save in registry */
484
485 /* Free the string now */
486 RtlFreeUnicodeString(&BootPath);
487 }
488 }
489
490 /* Build the full ARC name */
491 sprintf(Buffer, "\\ArcName\\%spartition(%lu)", ArcDiskEntry->ArcName, j + 1);
492
493 /* Convert it to Unicode */
494 RtlInitAnsiString(&ArcString, Buffer);
495 Status = RtlAnsiStringToUnicodeString(&ArcName, &ArcString, TRUE);
496 if (!NT_SUCCESS(Status)) continue;
497
498 /* Create the symbolic link and free the strings */
499 IoAssignArcName(&ArcName, &DeviceName);
500 RtlFreeUnicodeString(&ArcName);
501 RtlFreeUnicodeString(&DeviceName);
502 }
503 }
504 }
505 }
506
507 /* Check if we didn't find the boot disk */
508 if (!FoundBoot)
509 {
510 /* Allocate a buffer for the CD-ROM MBR */
511 PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool, 2048, TAG_IO);
512 if (!PartitionBuffer) return STATUS_INSUFFICIENT_RESOURCES;
513
514 /* Loop every CD-ROM */
515 for (i = 0; i < ConfigInfo->CdRomCount; i++)
516 {
517 /* Give it an ARC name */
518 if (IopAssignArcNamesToCdrom(PartitionBuffer, i)) break;
519 }
520
521 /* Free the buffer */
522 ExFreePool(PartitionBuffer);
523 }
524
525 /* Return success */
526 return STATUS_SUCCESS;
527 }
528
529 NTSTATUS
530 NTAPI
531 IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
532 OUT PANSI_STRING NtBootPath)
533 {
534 OBJECT_ATTRIBUTES ObjectAttributes;
535 NTSTATUS Status;
536 CHAR Buffer[256], AnsiBuffer[256];
537 WCHAR ArcNameBuffer[64];
538 ANSI_STRING TargetString, ArcString, TempString;
539 UNICODE_STRING LinkName, TargetName, ArcName;
540 HANDLE LinkHandle;
541
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;
547
548 /* Initialize the attributes and open the link */
549 InitializeObjectAttributes(&ObjectAttributes,
550 &TargetName,
551 OBJ_CASE_INSENSITIVE,
552 NULL,
553 NULL);
554 Status = NtOpenSymbolicLinkObject(&LinkHandle,
555 SYMBOLIC_LINK_ALL_ACCESS,
556 &ObjectAttributes);
557 if (!NT_SUCCESS(Status))
558 {
559 /* We failed, free the string */
560 RtlFreeUnicodeString(&TargetName);
561 return FALSE;
562 }
563
564 /* Query the current \\SystemRoot */
565 ArcName.Buffer = ArcNameBuffer;
566 ArcName.Length = 0;
567 ArcName.MaximumLength = sizeof(ArcNameBuffer);
568 Status = NtQuerySymbolicLinkObject(LinkHandle, &ArcName, NULL);
569 if (!NT_SUCCESS(Status))
570 {
571 /* We failed, free the string */
572 RtlFreeUnicodeString(&TargetName);
573 return FALSE;
574 }
575
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;
582
583 /* Close the link handle and free the name */
584 ObCloseHandle(LinkHandle, KernelMode);
585 RtlFreeUnicodeString(&TargetName);
586
587 /* Setup the system root name again */
588 RtlInitAnsiString(&TempString, "\\SystemRoot");
589 Status = RtlAnsiStringToUnicodeString(&LinkName, &TempString, TRUE);
590 if (!NT_SUCCESS(Status)) return FALSE;
591
592 /* Open the symbolic link for it */
593 InitializeObjectAttributes(&ObjectAttributes,
594 &LinkName,
595 OBJ_CASE_INSENSITIVE,
596 NULL,
597 NULL);
598 Status = NtOpenSymbolicLinkObject(&LinkHandle,
599 SYMBOLIC_LINK_ALL_ACCESS,
600 &ObjectAttributes);
601 if (!NT_SUCCESS(Status)) return FALSE;
602
603 /* Destroy it */
604 NtMakeTemporaryObject(LinkHandle);
605 ObCloseHandle(LinkHandle, KernelMode);
606
607 /* Now create the new name for it */
608 sprintf(Buffer, "%s%s", ArcString.Buffer, LoaderBlock->NtBootPathName);
609
610 /* Copy it into the passed parameter and null-terminate it */
611 RtlCopyString(NtBootPath, &ArcString);
612 Buffer[strlen(Buffer) - 1] = ANSI_NULL;
613
614 /* Setup the Unicode-name for the new symbolic link value */
615 RtlInitAnsiString(&TargetString, Buffer);
616 InitializeObjectAttributes(&ObjectAttributes,
617 &LinkName,
618 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
619 NULL,
620 NULL);
621 Status = RtlAnsiStringToUnicodeString(&ArcName, &TargetString, TRUE);
622 if (!NT_SUCCESS(Status)) return FALSE;
623
624 /* Create it */
625 Status = NtCreateSymbolicLinkObject(&LinkHandle,
626 SYMBOLIC_LINK_ALL_ACCESS,
627 &ObjectAttributes,
628 &ArcName);
629
630 /* Free all the strings and close the handle and return success */
631 RtlFreeUnicodeString(&ArcName);
632 RtlFreeUnicodeString(&LinkName);
633 ObCloseHandle(LinkHandle, KernelMode);
634 return TRUE;
635 }
636
637 /* EOF */