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