- Update to r53061
[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, 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 TAG_SYMLINK_TTARGET);
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) return STATUS_NO_MEMORY;
599
600 /* Copy it */
601 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
602 CapturedLinkTarget.Buffer,
603 CapturedLinkTarget.MaximumLength);
604
605 /* Initialize the remaining name, dos drive index and target object */
606 SymbolicLink->LinkTargetObject = NULL;
607 SymbolicLink->DosDeviceDriveIndex = 0;
608 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
609
610 /* Insert it into the object tree */
611 Status = ObInsertObject(SymbolicLink,
612 NULL,
613 DesiredAccess,
614 0,
615 NULL,
616 &hLink);
617 if (NT_SUCCESS(Status))
618 {
619 _SEH2_TRY
620 {
621 /* Return the handle to caller */
622 *LinkHandle = hLink;
623 }
624 _SEH2_EXCEPT(ExSystemExceptionFilter())
625 {
626 /* Get exception code */
627 Status = _SEH2_GetExceptionCode();
628 }
629 _SEH2_END;
630 }
631 }
632
633 /* Return status to caller */
634 return Status;
635 }
636
637 /*++
638 * @name NtOpenSymbolicLinkObject
639 * @implemented NT4
640 *
641 * The NtOpenSymbolicLinkObject opens a symbolic link object.
642 *
643 * @param LinkHandle
644 * Variable which receives the symlink handle.
645 *
646 * @param DesiredAccess
647 * Desired access to the symlink.
648 *
649 * @param ObjectAttributes
650 * Structure describing the symlink.
651 *
652 * @return STATUS_SUCCESS or appropriate error value.
653 *
654 * @remarks None.
655 *
656 *--*/
657 NTSTATUS
658 NTAPI
659 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
660 IN ACCESS_MASK DesiredAccess,
661 IN POBJECT_ATTRIBUTES ObjectAttributes)
662 {
663 HANDLE hLink;
664 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
665 NTSTATUS Status;
666 PAGED_CODE();
667
668 /* Check if we need to probe parameters */
669 if (PreviousMode != KernelMode)
670 {
671 _SEH2_TRY
672 {
673 /* Probe the return handle */
674 ProbeForWriteHandle(LinkHandle);
675 }
676 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
677 {
678 /* Return the exception code */
679 _SEH2_YIELD(return _SEH2_GetExceptionCode());
680 }
681 _SEH2_END;
682 }
683
684 /* Open the object */
685 Status = ObOpenObjectByName(ObjectAttributes,
686 ObSymbolicLinkType,
687 PreviousMode,
688 NULL,
689 DesiredAccess,
690 NULL,
691 &hLink);
692 if (NT_SUCCESS(Status))
693 {
694 _SEH2_TRY
695 {
696 /* Return the handle to caller */
697 *LinkHandle = hLink;
698 }
699 _SEH2_EXCEPT(ExSystemExceptionFilter())
700 {
701 /* Get exception code */
702 Status = _SEH2_GetExceptionCode();
703 }
704 _SEH2_END;
705 }
706
707 /* Return status to caller */
708 return Status;
709 }
710
711 /*++
712 * @name NtQuerySymbolicLinkObject
713 * @implemented NT4
714 *
715 * The NtQuerySymbolicLinkObject queries a symbolic link object.
716 *
717 * @param LinkHandle
718 * Symlink handle to query
719 *
720 * @param LinkTarget
721 * Unicode string defining the symlink's target
722 *
723 * @param ResultLength
724 * Caller supplied storage for the number of bytes written (or NULL).
725 *
726 * @return STATUS_SUCCESS or appropriate error value.
727 *
728 * @remarks None.
729 *
730 *--*/
731 NTSTATUS
732 NTAPI
733 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
734 OUT PUNICODE_STRING LinkTarget,
735 OUT PULONG ResultLength OPTIONAL)
736 {
737 UNICODE_STRING SafeLinkTarget = { 0, 0, NULL };
738 POBJECT_SYMBOLIC_LINK SymlinkObject;
739 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
740 NTSTATUS Status;
741 ULONG LengthUsed;
742 PAGED_CODE();
743
744 if (PreviousMode != KernelMode)
745 {
746 _SEH2_TRY
747 {
748 /* Probe the unicode string for read and write */
749 ProbeForWriteUnicodeString(LinkTarget);
750
751 /* Probe the unicode string's buffer for write */
752 SafeLinkTarget = *LinkTarget;
753 ProbeForWrite(SafeLinkTarget.Buffer,
754 SafeLinkTarget.MaximumLength,
755 sizeof(WCHAR));
756
757 /* Probe the return length */
758 if (ResultLength) ProbeForWriteUlong(ResultLength);
759 }
760 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
761 {
762 /* Return the exception code */
763 _SEH2_YIELD(return _SEH2_GetExceptionCode());
764 }
765 _SEH2_END;
766 }
767 else
768 {
769 /* No need to probe */
770 SafeLinkTarget = *LinkTarget;
771 }
772
773 /* Reference the object */
774 Status = ObReferenceObjectByHandle(LinkHandle,
775 SYMBOLIC_LINK_QUERY,
776 ObSymbolicLinkType,
777 PreviousMode,
778 (PVOID *)&SymlinkObject,
779 NULL);
780 if (NT_SUCCESS(Status))
781 {
782 /* Lock the object */
783 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
784
785 /*
786 * So here's the thing: If you specify a return length, then the
787 * implementation will use the maximum length. If you don't, then
788 * it will use the length.
789 */
790 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
791 SymlinkObject->LinkTarget.Length;
792
793 /* Enter SEH so we can safely copy */
794 _SEH2_TRY
795 {
796 /* Make sure our buffer will fit */
797 if (LengthUsed <= SafeLinkTarget.MaximumLength)
798 {
799 /* Copy the buffer */
800 RtlCopyMemory(SafeLinkTarget.Buffer,
801 SymlinkObject->LinkTarget.Buffer,
802 LengthUsed);
803
804 /* Copy the new length */
805 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
806 }
807 else
808 {
809 /* Otherwise set the failure status */
810 Status = STATUS_BUFFER_TOO_SMALL;
811 }
812
813 /* In both cases, check if the required length was requested */
814 if (ResultLength)
815 {
816 /* Then return it */
817 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
818 }
819 }
820 _SEH2_EXCEPT(ExSystemExceptionFilter())
821 {
822 /* Get the error code */
823 Status = _SEH2_GetExceptionCode();
824 }
825 _SEH2_END;
826
827 /* Unlock and dereference the object */
828 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
829 ObDereferenceObject(SymlinkObject);
830 }
831
832 /* Return query status */
833 return Status;
834 }
835
836 /* EOF */