[CMAKE]
[reactos.git] / ntoskrnl / ob / oblink.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/oblink.c
5 * PURPOSE: Implements symbolic links
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * David Welch (welch@mcmail.com)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ******************************************************************/
17
18 POBJECT_TYPE ObSymbolicLinkType = NULL;
19
20 /* PRIVATE FUNCTIONS *********************************************************/
21
22 VOID
23 NTAPI
24 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
25 {
26 POBJECT_HEADER ObjectHeader;
27 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
28
29 /* FIXME: Need to support Device maps */
30
31 /* Get header data */
32 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
33 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
34
35 /* Check if we are not actually in a directory with a device map */
36 if (!(ObjectNameInfo) ||
37 !(ObjectNameInfo->Directory) /*||
38 !(ObjectNameInfo->Directory->DeviceMap)*/)
39 {
40 ObpDereferenceNameInfo(ObjectNameInfo);
41 return;
42 }
43
44 /* Check if it's a DOS drive letter, and remove the entry from drive map if needed */
45 if (SymbolicLink->DosDeviceDriveIndex != 0 &&
46 ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR) &&
47 ObjectNameInfo->Name.Buffer[1] == L':' &&
48 ( (ObjectNameInfo->Name.Buffer[0] >= L'A' &&
49 ObjectNameInfo->Name.Buffer[0] <= L'Z') ||
50 (ObjectNameInfo->Name.Buffer[0] >= L'a' &&
51 ObjectNameInfo->Name.Buffer[0] <= L'z') ))
52 {
53 /* Remove the drive entry */
54 KeAcquireGuardedMutex(&ObpDeviceMapLock);
55 ObSystemDeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex-1] =
56 DOSDEVICE_DRIVE_UNKNOWN;
57 ObSystemDeviceMap->DriveMap &=
58 ~(1 << (SymbolicLink->DosDeviceDriveIndex-1));
59 KeReleaseGuardedMutex(&ObpDeviceMapLock);
60
61 /* Reset the drive index, valid drive index starts from 1 */
62 SymbolicLink->DosDeviceDriveIndex = 0;
63 }
64
65 ObpDereferenceNameInfo(ObjectNameInfo);
66 }
67
68 NTSTATUS
69 NTAPI
70 ObpParseSymbolicLinkToIoDeviceObject(IN POBJECT_DIRECTORY SymbolicLinkDirectory,
71 IN OUT POBJECT_DIRECTORY *Directory,
72 IN OUT PUNICODE_STRING TargetPath,
73 IN OUT POBP_LOOKUP_CONTEXT Context,
74 OUT PVOID *Object)
75 {
76 UNICODE_STRING Name;
77 BOOLEAN ManualUnlock;
78
79 if (! TargetPath || ! Object || ! Context || ! Directory ||
80 ! SymbolicLinkDirectory)
81 {
82 return STATUS_INVALID_PARAMETER;
83 }
84
85 /* FIXME: Need to support Device maps */
86
87 /* Try walking the target path and open each part of the path */
88 while (TargetPath->Length)
89 {
90 /* Strip '\' if present at the beginning of the target path */
91 if (TargetPath->Length >= sizeof(OBJ_NAME_PATH_SEPARATOR)&&
92 TargetPath->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
93 {
94 TargetPath->Buffer++;
95 TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
96 }
97
98 /* Remember the current component of the target path */
99 Name = *TargetPath;
100
101 /* Move forward to the next component of the target path */
102 while (TargetPath->Length >= sizeof(OBJ_NAME_PATH_SEPARATOR))
103 {
104 if (TargetPath->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
105 {
106 TargetPath->Buffer++;
107 TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
108 }
109 else
110 break;
111 }
112
113 Name.Length -= TargetPath->Length;
114
115 /* Finished processing the entire path, stop */
116 if (! Name.Length)
117 break;
118
119 /*
120 * Make sure a deadlock does not occur as an exclusive lock on a pushlock
121 * would have already taken one in ObpLookupObjectName() on the parent
122 * directory where the symlink is being created [ObInsertObject()].
123 * Prevent recursive locking by faking lock state in the lookup context
124 * when the current directory is same as the parent directory where
125 * the symlink is being created. If the lock state is not faked,
126 * ObpLookupEntryDirectory() will try to get a recursive lock on the
127 * pushlock and hang. For e.g. this happens when a substed drive is pointed to
128 * another substed drive.
129 */
130 if (*Directory == SymbolicLinkDirectory && ! Context->DirectoryLocked)
131 {
132 /* Fake lock state so that ObpLookupEntryDirectory() doesn't attempt a lock */
133 ManualUnlock = TRUE;
134 Context->DirectoryLocked = TRUE;
135 }
136 else
137 ManualUnlock = FALSE;
138
139 *Object = ObpLookupEntryDirectory(*Directory,
140 &Name,
141 0,
142 FALSE,
143 Context);
144
145 /* Locking was faked, undo it now */
146 if (*Directory == SymbolicLinkDirectory && ManualUnlock)
147 Context->DirectoryLocked = FALSE;
148
149 /* Lookup failed, stop */
150 if (! *Object)
151 break;
152
153 if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObDirectoryType)
154 {
155 /* Make this current directory, and continue search */
156 *Directory = (POBJECT_DIRECTORY)*Object;
157 }
158 else if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObSymbolicLinkType &&
159 (((POBJECT_SYMBOLIC_LINK)*Object)->DosDeviceDriveIndex == 0))
160 {
161 /* Symlink points to another initialized symlink, ask caller to reparse */
162 *Directory = ObpRootDirectoryObject;
163 TargetPath = &((POBJECT_SYMBOLIC_LINK)*Object)->LinkTarget;
164 return STATUS_REPARSE_OBJECT;
165 }
166 else
167 {
168 /* Neither directory or symlink, stop */
169 break;
170 }
171 }
172
173 /* Return a valid object, only if object type is IoDeviceObject */
174 if (*Object &&
175 OBJECT_TO_OBJECT_HEADER(*Object)->Type != IoDeviceObjectType)
176 {
177 *Object = NULL;
178 }
179 return STATUS_SUCCESS;
180 }
181
182 VOID
183 NTAPI
184 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
185 {
186 POBJECT_HEADER ObjectHeader;
187 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
188 PVOID Object = NULL;
189 POBJECT_DIRECTORY Directory;
190 UNICODE_STRING TargetPath;
191 NTSTATUS Status;
192 ULONG DriveType = DOSDEVICE_DRIVE_CALCULATE;
193 ULONG ReparseCnt;
194 const ULONG MaxReparseAttempts = 20;
195 OBP_LOOKUP_CONTEXT Context;
196
197 /* FIXME: Need to support Device maps */
198
199 /* Get header data */
200 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
201 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
202
203 /* Check if we are not actually in a directory with a device map */
204 if (!(ObjectNameInfo) ||
205 !(ObjectNameInfo->Directory) /*||
206 !(ObjectNameInfo->Directory->DeviceMap)*/)
207 {
208 ObpDereferenceNameInfo(ObjectNameInfo);
209 return;
210 }
211
212 /* Check if it's a DOS drive letter, and set the drive index accordingly */
213 if (ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR) &&
214 ObjectNameInfo->Name.Buffer[1] == L':' &&
215 ( (ObjectNameInfo->Name.Buffer[0] >= L'A' &&
216 ObjectNameInfo->Name.Buffer[0] <= L'Z') ||
217 (ObjectNameInfo->Name.Buffer[0] >= L'a' &&
218 ObjectNameInfo->Name.Buffer[0] <= L'z') ))
219 {
220 SymbolicLink->DosDeviceDriveIndex =
221 RtlUpcaseUnicodeChar(ObjectNameInfo->Name.Buffer[0]) - L'A';
222 /* The Drive index start from 1 */
223 SymbolicLink->DosDeviceDriveIndex++;
224
225 /* Initialize lookup context */
226 ObpInitializeLookupContext(&Context);
227
228 /* Start the search from the root */
229 Directory = ObpRootDirectoryObject;
230 TargetPath = SymbolicLink->LinkTarget;
231
232 /*
233 * Locate the IoDeviceObject if any this symbolic link points to.
234 * To prevent endless reparsing, setting an upper limit on the
235 * number of reparses.
236 */
237 Status = STATUS_REPARSE_OBJECT;
238 ReparseCnt = 0;
239 while (Status == STATUS_REPARSE_OBJECT &&
240 ReparseCnt < MaxReparseAttempts)
241 {
242 Status =
243 ObpParseSymbolicLinkToIoDeviceObject(ObjectNameInfo->Directory,
244 &Directory,
245 &TargetPath,
246 &Context,
247 &Object);
248 if (Status == STATUS_REPARSE_OBJECT)
249 ReparseCnt++;
250 }
251
252 /* Cleanup lookup context */
253 ObpReleaseLookupContext(&Context);
254
255 /* Error, or max resparse attemtps exceeded */
256 if (! NT_SUCCESS(Status) || ReparseCnt >= MaxReparseAttempts)
257 {
258 /* Cleanup */
259 ObpDereferenceNameInfo(ObjectNameInfo);
260 return;
261 }
262
263 if (Object)
264 {
265 /* Calculate the drive type */
266 switch(((PDEVICE_OBJECT)Object)->DeviceType)
267 {
268 case FILE_DEVICE_VIRTUAL_DISK:
269 DriveType = DOSDEVICE_DRIVE_RAMDISK;
270 break;
271 case FILE_DEVICE_CD_ROM:
272 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
273 DriveType = DOSDEVICE_DRIVE_CDROM;
274 break;
275 case FILE_DEVICE_DISK:
276 case FILE_DEVICE_DISK_FILE_SYSTEM:
277 case FILE_DEVICE_FILE_SYSTEM:
278 if (((PDEVICE_OBJECT)Object)->Characteristics & FILE_REMOVABLE_MEDIA)
279 DriveType = DOSDEVICE_DRIVE_REMOVABLE;
280 else
281 DriveType = DOSDEVICE_DRIVE_FIXED;
282 break;
283 case FILE_DEVICE_NETWORK:
284 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
285 DriveType = DOSDEVICE_DRIVE_REMOTE;
286 break;
287 default:
288 DPRINT1("Device Type %ld for %wZ is not known or unhandled\n",
289 ((PDEVICE_OBJECT)Object)->DeviceType,
290 &SymbolicLink->LinkTarget);
291 DriveType = DOSDEVICE_DRIVE_UNKNOWN;
292 }
293 }
294
295 /* Add a new drive entry */
296 KeAcquireGuardedMutex(&ObpDeviceMapLock);
297 ObSystemDeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex-1] =
298 (UCHAR)DriveType;
299 ObSystemDeviceMap->DriveMap |=
300 1 << (SymbolicLink->DosDeviceDriveIndex-1);
301 KeReleaseGuardedMutex(&ObpDeviceMapLock);
302 }
303
304 /* Cleanup */
305 ObpDereferenceNameInfo(ObjectNameInfo);
306 }
307
308 /*++
309 * @name ObpDeleteSymbolicLink
310 *
311 * The ObpDeleteSymbolicLink routine <FILLMEIN>
312 *
313 * @param ObjectBody
314 * <FILLMEIN>
315 *
316 * @return None.
317 *
318 * @remarks None.
319 *
320 *--*/
321 VOID
322 NTAPI
323 ObpDeleteSymbolicLink(PVOID ObjectBody)
324 {
325 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ObjectBody;
326
327 /* Make sure that the symbolic link has a name */
328 if (SymlinkObject->LinkTarget.Buffer)
329 {
330 /* Free the name */
331 ExFreePool(SymlinkObject->LinkTarget.Buffer);
332 SymlinkObject->LinkTarget.Buffer = NULL;
333 }
334 }
335
336 /*++
337 * @name ObpParseSymbolicLink
338 *
339 * The ObpParseSymbolicLink routine <FILLMEIN>
340 *
341 * @param Object
342 * <FILLMEIN>
343 *
344 * @param NextObject
345 * <FILLMEIN>
346 *
347 * @param FullPath
348 * <FILLMEIN>
349 *
350 * @param RemainingPath
351 * <FILLMEIN>
352 *
353 * @param Attributes
354 * <FILLMEIN>
355 *
356 * @return STATUS_SUCCESS or appropriate error value.
357 *
358 * @remarks None.
359 *
360 *--*/
361 NTSTATUS
362 NTAPI
363 ObpParseSymbolicLink(IN PVOID ParsedObject,
364 IN PVOID ObjectType,
365 IN OUT PACCESS_STATE AccessState,
366 IN KPROCESSOR_MODE AccessMode,
367 IN ULONG Attributes,
368 IN OUT PUNICODE_STRING FullPath,
369 IN OUT PUNICODE_STRING RemainingName,
370 IN OUT PVOID Context OPTIONAL,
371 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
372 OUT PVOID *NextObject)
373 {
374 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ParsedObject;
375 PUNICODE_STRING TargetPath;
376 PWSTR NewTargetPath;
377 ULONG LengthUsed, MaximumLength;
378 NTSTATUS Status;
379 PAGED_CODE();
380
381 /* Assume failure */
382 *NextObject = NULL;
383
384 /* Check if we're out of name to parse */
385 if (!RemainingName->Length)
386 {
387 /* Check if we got an object type */
388 if (ObjectType)
389 {
390 /* Reference the object only */
391 Status = ObReferenceObjectByPointer(ParsedObject,
392 0,
393 ObjectType,
394 AccessMode);
395 if (NT_SUCCESS(Status))
396 {
397 /* Return it */
398 *NextObject = ParsedObject;
399 }
400
401 if ((NT_SUCCESS(Status)) || (Status != STATUS_OBJECT_TYPE_MISMATCH))
402 {
403 /* Fail */
404 return Status;
405 }
406 }
407 }
408 else if (RemainingName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
409 {
410 /* Symbolic links must start with a backslash */
411 return STATUS_OBJECT_TYPE_MISMATCH;
412 }
413
414 /* Set the target path and length */
415 TargetPath = &SymlinkObject->LinkTarget;
416 LengthUsed = TargetPath->Length + RemainingName->Length;
417
418 /* Optimization: check if the new name is shorter */
419 if (FullPath->MaximumLength <= LengthUsed)
420 {
421 /* It's not, allocate a new one */
422 MaximumLength = LengthUsed + sizeof(WCHAR);
423 NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
424 MaximumLength,
425 TAG_SYMLINK_TTARGET);
426 if (!NewTargetPath) return STATUS_INSUFFICIENT_RESOURCES;
427 }
428 else
429 {
430 /* It is! Reuse the name... */
431 MaximumLength = FullPath->MaximumLength;
432 NewTargetPath = FullPath->Buffer;
433 }
434
435 /* Make sure we have a length */
436 if (RemainingName->Length)
437 {
438 /* Copy the new path */
439 RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TargetPath->Length),
440 RemainingName->Buffer,
441 RemainingName->Length);
442 }
443
444 /* Copy the target path and null-terminate it */
445 RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TargetPath->Length);
446 NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
447
448 /* If the optimization didn't work, free the old buffer */
449 if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
450
451 /* Update the path values */
452 FullPath->Length = (USHORT)LengthUsed;
453 FullPath->MaximumLength = (USHORT)MaximumLength;
454 FullPath->Buffer = NewTargetPath;
455
456 /* Tell the parse routine to start reparsing */
457 return STATUS_REPARSE;
458 }
459
460 /* PUBLIC FUNCTIONS **********************************************************/
461
462 /*++
463 * @name NtCreateSymbolicLinkObject
464 * @implemented NT4
465 *
466 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
467 *
468 * @param LinkHandle
469 * Variable which receives the symlink handle.
470 *
471 * @param DesiredAccess
472 * Desired access to the symlink.
473 *
474 * @param ObjectAttributes
475 * Structure describing the symlink.
476 *
477 * @param LinkTarget
478 * Unicode string defining the symlink's target
479 *
480 * @return STATUS_SUCCESS or appropriate error value.
481 *
482 * @remarks None.
483 *
484 *--*/
485 NTSTATUS
486 NTAPI
487 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
488 IN ACCESS_MASK DesiredAccess,
489 IN POBJECT_ATTRIBUTES ObjectAttributes,
490 IN PUNICODE_STRING LinkTarget)
491 {
492 HANDLE hLink;
493 POBJECT_SYMBOLIC_LINK SymbolicLink;
494 UNICODE_STRING CapturedLinkTarget;
495 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
496 NTSTATUS Status;
497 PAGED_CODE();
498
499 /* Check if we need to probe parameters */
500 if (PreviousMode != KernelMode)
501 {
502 _SEH2_TRY
503 {
504 /* Probe the target */
505 CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget);
506 ProbeForRead(CapturedLinkTarget.Buffer,
507 CapturedLinkTarget.MaximumLength,
508 sizeof(WCHAR));
509
510 /* Probe the return handle */
511 ProbeForWriteHandle(LinkHandle);
512 }
513 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
514 {
515 /* Return the exception code */
516 _SEH2_YIELD(return _SEH2_GetExceptionCode());
517 }
518 _SEH2_END;
519 }
520 else
521 {
522 /* No need to capture */
523 CapturedLinkTarget = *LinkTarget;
524 }
525
526 /* Check if the maximum length is odd */
527 if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
528 {
529 /* Round it down */
530 CapturedLinkTarget.MaximumLength =
531 (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
532 }
533
534 /* Fail if the length is odd, or if the maximum is smaller or 0 */
535 if ((CapturedLinkTarget.Length % sizeof(WCHAR)) ||
536 (CapturedLinkTarget.MaximumLength < CapturedLinkTarget.Length) ||
537 !(CapturedLinkTarget.MaximumLength))
538 {
539 /* This message is displayed on the debugger in Windows */
540 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
541 &CapturedLinkTarget);
542 return STATUS_INVALID_PARAMETER;
543 }
544
545 /* Create the object */
546 Status = ObCreateObject(PreviousMode,
547 ObSymbolicLinkType,
548 ObjectAttributes,
549 PreviousMode,
550 NULL,
551 sizeof(OBJECT_SYMBOLIC_LINK),
552 0,
553 0,
554 (PVOID*)&SymbolicLink);
555 if (NT_SUCCESS(Status))
556 {
557 /* Success! Fill in the creation time immediately */
558 KeQuerySystemTime(&SymbolicLink->CreationTime);
559
560 /* Setup the target name */
561 SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
562 SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.Length +
563 sizeof(WCHAR);
564 SymbolicLink->LinkTarget.Buffer =
565 ExAllocatePoolWithTag(PagedPool,
566 CapturedLinkTarget.MaximumLength,
567 TAG_SYMLINK_TARGET);
568 if (!SymbolicLink->LinkTarget.Buffer) return STATUS_NO_MEMORY;
569
570 /* Copy it */
571 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
572 CapturedLinkTarget.Buffer,
573 CapturedLinkTarget.MaximumLength);
574
575 /* Initialize the remaining name, dos drive index and target object */
576 SymbolicLink->LinkTargetObject = NULL;
577 SymbolicLink->DosDeviceDriveIndex = 0;
578 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
579
580 /* Insert it into the object tree */
581 Status = ObInsertObject(SymbolicLink,
582 NULL,
583 DesiredAccess,
584 0,
585 NULL,
586 &hLink);
587 if (NT_SUCCESS(Status))
588 {
589 _SEH2_TRY
590 {
591 /* Return the handle to caller */
592 *LinkHandle = hLink;
593 }
594 _SEH2_EXCEPT(ExSystemExceptionFilter())
595 {
596 /* Get exception code */
597 Status = _SEH2_GetExceptionCode();
598 }
599 _SEH2_END;
600 }
601 }
602
603 /* Return status to caller */
604 return Status;
605 }
606
607 /*++
608 * @name NtOpenSymbolicLinkObject
609 * @implemented NT4
610 *
611 * The NtOpenSymbolicLinkObject opens a symbolic link object.
612 *
613 * @param LinkHandle
614 * Variable which receives the symlink handle.
615 *
616 * @param DesiredAccess
617 * Desired access to the symlink.
618 *
619 * @param ObjectAttributes
620 * Structure describing the symlink.
621 *
622 * @return STATUS_SUCCESS or appropriate error value.
623 *
624 * @remarks None.
625 *
626 *--*/
627 NTSTATUS
628 NTAPI
629 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
630 IN ACCESS_MASK DesiredAccess,
631 IN POBJECT_ATTRIBUTES ObjectAttributes)
632 {
633 HANDLE hLink;
634 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
635 NTSTATUS Status;
636 PAGED_CODE();
637
638 /* Check if we need to probe parameters */
639 if (PreviousMode != KernelMode)
640 {
641 _SEH2_TRY
642 {
643 /* Probe the return handle */
644 ProbeForWriteHandle(LinkHandle);
645 }
646 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
647 {
648 /* Return the exception code */
649 _SEH2_YIELD(return _SEH2_GetExceptionCode());
650 }
651 _SEH2_END;
652 }
653
654 /* Open the object */
655 Status = ObOpenObjectByName(ObjectAttributes,
656 ObSymbolicLinkType,
657 PreviousMode,
658 NULL,
659 DesiredAccess,
660 NULL,
661 &hLink);
662 if (NT_SUCCESS(Status))
663 {
664 _SEH2_TRY
665 {
666 /* Return the handle to caller */
667 *LinkHandle = hLink;
668 }
669 _SEH2_EXCEPT(ExSystemExceptionFilter())
670 {
671 /* Get exception code */
672 Status = _SEH2_GetExceptionCode();
673 }
674 _SEH2_END;
675 }
676
677 /* Return status to caller */
678 return Status;
679 }
680
681 /*++
682 * @name NtQuerySymbolicLinkObject
683 * @implemented NT4
684 *
685 * The NtQuerySymbolicLinkObject queries a symbolic link object.
686 *
687 * @param LinkHandle
688 * Symlink handle to query
689 *
690 * @param LinkTarget
691 * Unicode string defining the symlink's target
692 *
693 * @param ResultLength
694 * Caller supplied storage for the number of bytes written (or NULL).
695 *
696 * @return STATUS_SUCCESS or appropriate error value.
697 *
698 * @remarks None.
699 *
700 *--*/
701 NTSTATUS
702 NTAPI
703 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
704 OUT PUNICODE_STRING LinkTarget,
705 OUT PULONG ResultLength OPTIONAL)
706 {
707 UNICODE_STRING SafeLinkTarget = { 0, 0, NULL };
708 POBJECT_SYMBOLIC_LINK SymlinkObject;
709 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
710 NTSTATUS Status;
711 ULONG LengthUsed;
712 PAGED_CODE();
713
714 if (PreviousMode != KernelMode)
715 {
716 _SEH2_TRY
717 {
718 /* Probe the unicode string for read and write */
719 ProbeForWriteUnicodeString(LinkTarget);
720
721 /* Probe the unicode string's buffer for write */
722 SafeLinkTarget = *LinkTarget;
723 ProbeForWrite(SafeLinkTarget.Buffer,
724 SafeLinkTarget.MaximumLength,
725 sizeof(WCHAR));
726
727 /* Probe the return length */
728 if (ResultLength) ProbeForWriteUlong(ResultLength);
729 }
730 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
731 {
732 /* Return the exception code */
733 _SEH2_YIELD(return _SEH2_GetExceptionCode());
734 }
735 _SEH2_END;
736 }
737 else
738 {
739 /* No need to probe */
740 SafeLinkTarget = *LinkTarget;
741 }
742
743 /* Reference the object */
744 Status = ObReferenceObjectByHandle(LinkHandle,
745 SYMBOLIC_LINK_QUERY,
746 ObSymbolicLinkType,
747 PreviousMode,
748 (PVOID *)&SymlinkObject,
749 NULL);
750 if (NT_SUCCESS(Status))
751 {
752 /* Lock the object */
753 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
754
755 /*
756 * So here's the thing: If you specify a return length, then the
757 * implementation will use the maximum length. If you don't, then
758 * it will use the length.
759 */
760 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
761 SymlinkObject->LinkTarget.Length;
762
763 /* Enter SEH so we can safely copy */
764 _SEH2_TRY
765 {
766 /* Make sure our buffer will fit */
767 if (LengthUsed <= SafeLinkTarget.MaximumLength)
768 {
769 /* Copy the buffer */
770 RtlCopyMemory(SafeLinkTarget.Buffer,
771 SymlinkObject->LinkTarget.Buffer,
772 LengthUsed);
773
774 /* Copy the new length */
775 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
776 }
777 else
778 {
779 /* Otherwise set the failure status */
780 Status = STATUS_BUFFER_TOO_SMALL;
781 }
782
783 /* In both cases, check if the required length was requested */
784 if (ResultLength)
785 {
786 /* Then return it */
787 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
788 }
789 }
790 _SEH2_EXCEPT(ExSystemExceptionFilter())
791 {
792 /* Get the error code */
793 Status = _SEH2_GetExceptionCode();
794 }
795 _SEH2_END;
796
797 /* Unlock and dereference the object */
798 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
799 ObDereferenceObject(SymlinkObject);
800 }
801
802 /* Return query status */
803 return Status;
804 }
805
806 /* EOF */