[KMTESTS:OB]
[reactos.git] / reactos / 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 %lu 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, TempLength;
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 /* Check if this symlink is bound to a specific object */
415 if (SymlinkObject->LinkTargetObject)
416 {
417 UNIMPLEMENTED;
418 }
419
420 /* Set the target path and length */
421 TargetPath = &SymlinkObject->LinkTarget;
422 TempLength = TargetPath->Length;
423
424 /*
425 * Strip off the extra trailing '\', if we don't do this we will end up
426 * adding a extra '\' between TargetPath and RemainingName
427 * causing caller's like ObpLookupObjectName() to fail.
428 */
429 if (TempLength && RemainingName->Length)
430 {
431 /* The target and remaining names aren't empty, so check for slashes */
432 if ((TargetPath->Buffer[TempLength / sizeof(WCHAR) - 1] ==
433 OBJ_NAME_PATH_SEPARATOR) &&
434 (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
435 {
436 /* Reduce the length by one to cut off the extra '\' */
437 TempLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
438 }
439 }
440
441 /* Calculate the new length */
442 LengthUsed = TempLength + RemainingName->Length;
443
444 /* Check if it's not too much */
445 if (LengthUsed > 0xFFF0)
446 return STATUS_NAME_TOO_LONG;
447
448 /* Optimization: check if the new name is shorter */
449 if (FullPath->MaximumLength <= LengthUsed)
450 {
451 /* It's not, allocate a new one */
452 MaximumLength = LengthUsed + sizeof(WCHAR);
453 NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
454 MaximumLength,
455 OB_NAME_TAG);
456 if (!NewTargetPath) return STATUS_INSUFFICIENT_RESOURCES;
457 }
458 else
459 {
460 /* It is! Reuse the name... */
461 MaximumLength = FullPath->MaximumLength;
462 NewTargetPath = FullPath->Buffer;
463 }
464
465 /* Make sure we have a length */
466 if (RemainingName->Length)
467 {
468 /* Copy the new path */
469 RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TempLength),
470 RemainingName->Buffer,
471 RemainingName->Length);
472 }
473
474 /* Copy the target path and null-terminate it */
475 RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TempLength);
476 NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
477
478 /* If the optimization didn't work, free the old buffer */
479 if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
480
481 /* Update the path values */
482 FullPath->Length = (USHORT)LengthUsed;
483 FullPath->MaximumLength = (USHORT)MaximumLength;
484 FullPath->Buffer = NewTargetPath;
485
486 /* Tell the parse routine to start reparsing */
487 return STATUS_REPARSE;
488 }
489
490 /* PUBLIC FUNCTIONS **********************************************************/
491
492 /*++
493 * @name NtCreateSymbolicLinkObject
494 * @implemented NT4
495 *
496 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
497 *
498 * @param LinkHandle
499 * Variable which receives the symlink handle.
500 *
501 * @param DesiredAccess
502 * Desired access to the symlink.
503 *
504 * @param ObjectAttributes
505 * Structure describing the symlink.
506 *
507 * @param LinkTarget
508 * Unicode string defining the symlink's target
509 *
510 * @return STATUS_SUCCESS or appropriate error value.
511 *
512 * @remarks None.
513 *
514 *--*/
515 NTSTATUS
516 NTAPI
517 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
518 IN ACCESS_MASK DesiredAccess,
519 IN POBJECT_ATTRIBUTES ObjectAttributes,
520 IN PUNICODE_STRING LinkTarget)
521 {
522 HANDLE hLink;
523 POBJECT_SYMBOLIC_LINK SymbolicLink;
524 UNICODE_STRING CapturedLinkTarget;
525 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
526 NTSTATUS Status;
527 PAGED_CODE();
528
529 /* Check if we need to probe parameters */
530 if (PreviousMode != KernelMode)
531 {
532 _SEH2_TRY
533 {
534 /* Probe the target */
535 CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget);
536 ProbeForRead(CapturedLinkTarget.Buffer,
537 CapturedLinkTarget.MaximumLength,
538 sizeof(WCHAR));
539
540 /* Probe the return handle */
541 ProbeForWriteHandle(LinkHandle);
542 }
543 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
544 {
545 /* Return the exception code */
546 _SEH2_YIELD(return _SEH2_GetExceptionCode());
547 }
548 _SEH2_END;
549 }
550 else
551 {
552 /* No need to capture */
553 CapturedLinkTarget = *LinkTarget;
554 }
555
556 /* Check if the maximum length is odd */
557 if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
558 {
559 /* Round it down */
560 CapturedLinkTarget.MaximumLength =
561 (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
562 }
563
564 /* Fail if the length is odd, or if the maximum is smaller or 0 */
565 if ((CapturedLinkTarget.Length % sizeof(WCHAR)) ||
566 (CapturedLinkTarget.MaximumLength < CapturedLinkTarget.Length) ||
567 !(CapturedLinkTarget.MaximumLength))
568 {
569 /* This message is displayed on the debugger in Windows */
570 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
571 &CapturedLinkTarget);
572 return STATUS_INVALID_PARAMETER;
573 }
574
575 /* Create the object */
576 Status = ObCreateObject(PreviousMode,
577 ObSymbolicLinkType,
578 ObjectAttributes,
579 PreviousMode,
580 NULL,
581 sizeof(OBJECT_SYMBOLIC_LINK),
582 0,
583 0,
584 (PVOID*)&SymbolicLink);
585 if (NT_SUCCESS(Status))
586 {
587 /* Success! Fill in the creation time immediately */
588 KeQuerySystemTime(&SymbolicLink->CreationTime);
589
590 /* Setup the target name */
591 SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
592 SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.Length +
593 sizeof(WCHAR);
594 SymbolicLink->LinkTarget.Buffer =
595 ExAllocatePoolWithTag(PagedPool,
596 CapturedLinkTarget.MaximumLength,
597 TAG_SYMLINK_TARGET);
598 if (!SymbolicLink->LinkTarget.Buffer)
599 {
600 /* Dereference the symbolic link object and fail */
601 ObDereferenceObject(SymbolicLink);
602 return STATUS_NO_MEMORY;
603 }
604
605 /* Copy it */
606 _SEH2_TRY
607 {
608 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
609 CapturedLinkTarget.Buffer,
610 CapturedLinkTarget.MaximumLength);
611 }
612 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
613 {
614 ObDereferenceObject(SymbolicLink);
615 _SEH2_YIELD(return _SEH2_GetExceptionCode());
616 }
617 _SEH2_END;
618
619 /* Initialize the remaining name, dos drive index and target object */
620 SymbolicLink->LinkTargetObject = NULL;
621 SymbolicLink->DosDeviceDriveIndex = 0;
622 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
623
624 /* Insert it into the object tree */
625 Status = ObInsertObject(SymbolicLink,
626 NULL,
627 DesiredAccess,
628 0,
629 NULL,
630 &hLink);
631 if (NT_SUCCESS(Status))
632 {
633 _SEH2_TRY
634 {
635 /* Return the handle to caller */
636 *LinkHandle = hLink;
637 }
638 _SEH2_EXCEPT(ExSystemExceptionFilter())
639 {
640 /* Get exception code */
641 Status = _SEH2_GetExceptionCode();
642 }
643 _SEH2_END;
644 }
645 }
646
647 /* Return status to caller */
648 return Status;
649 }
650
651 /*++
652 * @name NtOpenSymbolicLinkObject
653 * @implemented NT4
654 *
655 * The NtOpenSymbolicLinkObject opens a symbolic link object.
656 *
657 * @param LinkHandle
658 * Variable which receives the symlink handle.
659 *
660 * @param DesiredAccess
661 * Desired access to the symlink.
662 *
663 * @param ObjectAttributes
664 * Structure describing the symlink.
665 *
666 * @return STATUS_SUCCESS or appropriate error value.
667 *
668 * @remarks None.
669 *
670 *--*/
671 NTSTATUS
672 NTAPI
673 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
674 IN ACCESS_MASK DesiredAccess,
675 IN POBJECT_ATTRIBUTES ObjectAttributes)
676 {
677 HANDLE hLink;
678 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
679 NTSTATUS Status;
680 PAGED_CODE();
681
682 /* Check if we need to probe parameters */
683 if (PreviousMode != KernelMode)
684 {
685 _SEH2_TRY
686 {
687 /* Probe the return handle */
688 ProbeForWriteHandle(LinkHandle);
689 }
690 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
691 {
692 /* Return the exception code */
693 _SEH2_YIELD(return _SEH2_GetExceptionCode());
694 }
695 _SEH2_END;
696 }
697
698 /* Open the object */
699 Status = ObOpenObjectByName(ObjectAttributes,
700 ObSymbolicLinkType,
701 PreviousMode,
702 NULL,
703 DesiredAccess,
704 NULL,
705 &hLink);
706 if (NT_SUCCESS(Status))
707 {
708 _SEH2_TRY
709 {
710 /* Return the handle to caller */
711 *LinkHandle = hLink;
712 }
713 _SEH2_EXCEPT(ExSystemExceptionFilter())
714 {
715 /* Get exception code */
716 Status = _SEH2_GetExceptionCode();
717 }
718 _SEH2_END;
719 }
720
721 /* Return status to caller */
722 return Status;
723 }
724
725 /*++
726 * @name NtQuerySymbolicLinkObject
727 * @implemented NT4
728 *
729 * The NtQuerySymbolicLinkObject queries a symbolic link object.
730 *
731 * @param LinkHandle
732 * Symlink handle to query
733 *
734 * @param LinkTarget
735 * Unicode string defining the symlink's target
736 *
737 * @param ResultLength
738 * Caller supplied storage for the number of bytes written (or NULL).
739 *
740 * @return STATUS_SUCCESS or appropriate error value.
741 *
742 * @remarks None.
743 *
744 *--*/
745 NTSTATUS
746 NTAPI
747 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
748 OUT PUNICODE_STRING LinkTarget,
749 OUT PULONG ResultLength OPTIONAL)
750 {
751 UNICODE_STRING SafeLinkTarget = { 0, 0, NULL };
752 POBJECT_SYMBOLIC_LINK SymlinkObject;
753 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
754 NTSTATUS Status;
755 ULONG LengthUsed;
756 PAGED_CODE();
757
758 if (PreviousMode != KernelMode)
759 {
760 _SEH2_TRY
761 {
762 /* Probe the unicode string for read and write */
763 ProbeForWriteUnicodeString(LinkTarget);
764
765 /* Probe the unicode string's buffer for write */
766 SafeLinkTarget = *LinkTarget;
767 ProbeForWrite(SafeLinkTarget.Buffer,
768 SafeLinkTarget.MaximumLength,
769 sizeof(WCHAR));
770
771 /* Probe the return length */
772 if (ResultLength) ProbeForWriteUlong(ResultLength);
773 }
774 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
775 {
776 /* Return the exception code */
777 _SEH2_YIELD(return _SEH2_GetExceptionCode());
778 }
779 _SEH2_END;
780 }
781 else
782 {
783 /* No need to probe */
784 SafeLinkTarget = *LinkTarget;
785 }
786
787 /* Reference the object */
788 Status = ObReferenceObjectByHandle(LinkHandle,
789 SYMBOLIC_LINK_QUERY,
790 ObSymbolicLinkType,
791 PreviousMode,
792 (PVOID *)&SymlinkObject,
793 NULL);
794 if (NT_SUCCESS(Status))
795 {
796 /* Lock the object */
797 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
798
799 /*
800 * So here's the thing: If you specify a return length, then the
801 * implementation will use the maximum length. If you don't, then
802 * it will use the length.
803 */
804 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
805 SymlinkObject->LinkTarget.Length;
806
807 /* Enter SEH so we can safely copy */
808 _SEH2_TRY
809 {
810 /* Make sure our buffer will fit */
811 if (LengthUsed <= SafeLinkTarget.MaximumLength)
812 {
813 /* Copy the buffer */
814 RtlCopyMemory(SafeLinkTarget.Buffer,
815 SymlinkObject->LinkTarget.Buffer,
816 LengthUsed);
817
818 /* Copy the new length */
819 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
820 }
821 else
822 {
823 /* Otherwise set the failure status */
824 Status = STATUS_BUFFER_TOO_SMALL;
825 }
826
827 /* In both cases, check if the required length was requested */
828 if (ResultLength)
829 {
830 /* Then return it */
831 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
832 }
833 }
834 _SEH2_EXCEPT(ExSystemExceptionFilter())
835 {
836 /* Get the error code */
837 Status = _SEH2_GetExceptionCode();
838 }
839 _SEH2_END;
840
841 /* Unlock and dereference the object */
842 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
843 ObDereferenceObject(SymlinkObject);
844 }
845
846 /* Return query status */
847 return Status;
848 }
849
850 /* EOF */