2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/symlink.c
5 * PURPOSE: Implements symbolic links
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * David Welch (welch@mcmail.com)
10 /* INCLUDES *****************************************************************/
14 #include <internal/debug.h>
16 /* GLOBALS ******************************************************************/
18 POBJECT_TYPE ObSymbolicLinkType
= NULL
;
20 /* PRIVATE FUNCTIONS *********************************************************/
24 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
26 /* FIXME: Device maps not supported yet */
32 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
34 POBJECT_HEADER ObjectHeader
;
35 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
38 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
39 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
41 /* Check if we are not actually in a directory with a device map */
42 if (!(ObjectNameInfo
) ||
43 !(ObjectNameInfo
->Directory
) ||
44 !(ObjectNameInfo
->Directory
->DeviceMap
))
46 /* There's nothing to do, return */
50 /* FIXME: We don't support device maps yet */
51 DPRINT1("Unhandled path!\n");
56 * @name ObpDeleteSymbolicLink
58 * The ObpDeleteSymbolicLink routine <FILLMEIN>
70 ObpDeleteSymbolicLink(PVOID ObjectBody
)
72 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ObjectBody
;
74 /* Make sure that the symbolic link has a name */
75 if (SymlinkObject
->LinkTarget
.Buffer
)
78 ExFreePool(SymlinkObject
->LinkTarget
.Buffer
);
79 SymlinkObject
->LinkTarget
.Buffer
= NULL
;
84 * @name ObpParseSymbolicLink
86 * The ObpParseSymbolicLink routine <FILLMEIN>
97 * @param RemainingPath
103 * @return STATUS_SUCCESS or appropriate error value.
110 ObpParseSymbolicLink(IN PVOID ParsedObject
,
112 IN OUT PACCESS_STATE AccessState
,
113 IN KPROCESSOR_MODE AccessMode
,
115 IN OUT PUNICODE_STRING FullPath
,
116 IN OUT PUNICODE_STRING RemainingName
,
117 IN OUT PVOID Context OPTIONAL
,
118 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
119 OUT PVOID
*NextObject
)
121 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ParsedObject
;
122 PUNICODE_STRING TargetPath
;
124 ULONG LengthUsed
, MaximumLength
;
130 /* Check if we're out of name to parse */
131 if (!RemainingName
->Length
)
133 /* Check if we got an object type */
136 /* Reference the object only */
137 Status
= ObReferenceObjectByPointer(ParsedObject
,
141 if (NT_SUCCESS(Status
))
144 *NextObject
= ParsedObject
;
147 if ((NT_SUCCESS(Status
)) || (Status
!= STATUS_OBJECT_TYPE_MISMATCH
))
154 else if (RemainingName
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
)
156 /* Symbolic links must start with a backslash */
157 return STATUS_OBJECT_TYPE_MISMATCH
;
160 /* Set the target path and length */
161 TargetPath
= &SymlinkObject
->LinkTarget
;
162 LengthUsed
= TargetPath
->Length
+ RemainingName
->Length
;
164 /* Optimization: check if the new name is shorter */
165 if (FullPath
->MaximumLength
<= LengthUsed
)
167 /* It's not, allocate a new one */
168 MaximumLength
= LengthUsed
+ sizeof(WCHAR
);
169 NewTargetPath
= ExAllocatePoolWithTag(NonPagedPool
,
171 TAG_SYMLINK_TTARGET
);
175 /* It is! Reuse the name... */
176 MaximumLength
= FullPath
->MaximumLength
;
177 NewTargetPath
= FullPath
->Buffer
;
180 /* Make sure we have a length */
181 if (RemainingName
->Length
)
183 /* Copy the new path */
184 RtlMoveMemory((PVOID
)((ULONG_PTR
)NewTargetPath
+ TargetPath
->Length
),
185 RemainingName
->Buffer
,
186 RemainingName
->Length
);
189 /* Copy the target path and null-terminate it */
190 RtlCopyMemory(NewTargetPath
, TargetPath
->Buffer
, TargetPath
->Length
);
191 NewTargetPath
[LengthUsed
/ sizeof(WCHAR
)] = UNICODE_NULL
;
193 /* If the optimization didn't work, free the old buffer */
194 if (NewTargetPath
!= FullPath
->Buffer
) ExFreePool(FullPath
->Buffer
);
196 /* Update the path values */
197 FullPath
->Length
= (USHORT
)LengthUsed
;
198 FullPath
->MaximumLength
= (USHORT
)MaximumLength
;
199 FullPath
->Buffer
= NewTargetPath
;
201 /* Tell the parse routine to start reparsing */
202 return STATUS_REPARSE
;
205 /* PUBLIC FUNCTIONS **********************************************************/
208 * @name NtCreateSymbolicLinkObject
211 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
214 * Variable which receives the symlink handle.
216 * @param DesiredAccess
217 * Desired access to the symlink.
219 * @param ObjectAttributes
220 * Structure describing the symlink.
223 * Unicode string defining the symlink's target
225 * @return STATUS_SUCCESS or appropriate error value.
232 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle
,
233 IN ACCESS_MASK DesiredAccess
,
234 IN POBJECT_ATTRIBUTES ObjectAttributes
,
235 IN PUNICODE_STRING LinkTarget
)
238 POBJECT_SYMBOLIC_LINK SymbolicLink
;
239 UNICODE_STRING CapturedLinkTarget
;
240 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
241 NTSTATUS Status
= STATUS_SUCCESS
;
244 /* Check if we need to probe parameters */
245 if(PreviousMode
!= KernelMode
)
249 /* Probe the target */
250 CapturedLinkTarget
= ProbeForReadUnicodeString(LinkTarget
);
251 ProbeForRead(CapturedLinkTarget
.Buffer
,
252 CapturedLinkTarget
.MaximumLength
,
255 /* Probe the return handle */
256 ProbeForWriteHandle(LinkHandle
);
260 /* Exception, get the error code */
261 Status
= _SEH_GetExceptionCode();
265 /* Probing failed, return the error code */
266 if(!NT_SUCCESS(Status
)) return Status
;
270 /* No need to capture */
271 CapturedLinkTarget
= *LinkTarget
;
274 /* Check if the maximum length is odd */
275 if (CapturedLinkTarget
.MaximumLength
% sizeof(WCHAR
))
278 CapturedLinkTarget
.MaximumLength
=
279 (USHORT
)ALIGN_DOWN(CapturedLinkTarget
.MaximumLength
, WCHAR
);
282 /* Fail if the length is odd, or if the maximum is smaller or 0 */
283 if ((CapturedLinkTarget
.Length
% sizeof(WCHAR
)) ||
284 (CapturedLinkTarget
.MaximumLength
< CapturedLinkTarget
.Length
) ||
285 !(CapturedLinkTarget
.MaximumLength
))
287 /* This message is displayed on the debugger in Windows */
288 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
289 &CapturedLinkTarget
);
290 return STATUS_INVALID_PARAMETER
;
293 /* Create the object */
294 Status
= ObCreateObject(PreviousMode
,
299 sizeof(OBJECT_SYMBOLIC_LINK
),
302 (PVOID
*)&SymbolicLink
);
303 if (NT_SUCCESS(Status
))
305 /* Success! Fill in the creation time immediately */
306 KeQuerySystemTime(&SymbolicLink
->CreationTime
);
308 /* Setup the target name */
309 SymbolicLink
->LinkTarget
.Length
= CapturedLinkTarget
.Length
;
310 SymbolicLink
->LinkTarget
.MaximumLength
= CapturedLinkTarget
.Length
+
312 SymbolicLink
->LinkTarget
.Buffer
=
313 ExAllocatePoolWithTag(PagedPool
,
314 CapturedLinkTarget
.MaximumLength
,
316 if (!SymbolicLink
->LinkTarget
.Buffer
) return STATUS_NO_MEMORY
;
319 RtlCopyMemory(SymbolicLink
->LinkTarget
.Buffer
,
320 CapturedLinkTarget
.Buffer
,
321 CapturedLinkTarget
.MaximumLength
);
323 /* Initialize the remaining name, dos drive index and target object */
324 SymbolicLink
->LinkTargetObject
= NULL
;
325 SymbolicLink
->DosDeviceDriveIndex
= 0;
326 RtlInitUnicodeString(&SymbolicLink
->LinkTargetRemaining
, NULL
);
328 /* Insert it into the object tree */
329 Status
= ObInsertObject(SymbolicLink
,
335 if (NT_SUCCESS(Status
))
339 /* Return the handle to caller */
342 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
344 /* Get exception code */
345 Status
= _SEH_GetExceptionCode();
351 /* Return status to caller */
356 * @name NtOpenSymbolicLinkObject
359 * The NtOpenSymbolicLinkObject opens a symbolic link object.
362 * Variable which receives the symlink handle.
364 * @param DesiredAccess
365 * Desired access to the symlink.
367 * @param ObjectAttributes
368 * Structure describing the symlink.
370 * @return STATUS_SUCCESS or appropriate error value.
377 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle
,
378 IN ACCESS_MASK DesiredAccess
,
379 IN POBJECT_ATTRIBUTES ObjectAttributes
)
382 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
383 NTSTATUS Status
= STATUS_SUCCESS
;
386 /* Check if we need to probe parameters */
387 if(PreviousMode
!= KernelMode
)
391 /* Probe the return handle */
392 ProbeForWriteHandle(LinkHandle
);
396 /* Exception, get the error code */
397 Status
= _SEH_GetExceptionCode();
401 /* Probing failed, return the error code */
402 if(!NT_SUCCESS(Status
)) return Status
;
405 /* Open the object */
406 Status
= ObOpenObjectByName(ObjectAttributes
,
413 if (NT_SUCCESS(Status
))
417 /* Return the handle to caller */
420 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
422 /* Get exception code */
423 Status
= _SEH_GetExceptionCode();
428 /* Return status to caller */
433 * @name NtQuerySymbolicLinkObject
436 * The NtQuerySymbolicLinkObject queries a symbolic link object.
439 * Symlink handle to query
442 * Unicode string defining the symlink's target
444 * @param ResultLength
445 * Caller supplied storage for the number of bytes written (or NULL).
447 * @return STATUS_SUCCESS or appropriate error value.
454 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle
,
455 OUT PUNICODE_STRING LinkTarget
,
456 OUT PULONG ResultLength OPTIONAL
)
458 UNICODE_STRING SafeLinkTarget
= {0};
459 POBJECT_SYMBOLIC_LINK SymlinkObject
;
460 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
461 NTSTATUS Status
= STATUS_SUCCESS
;
465 if(PreviousMode
!= KernelMode
)
469 /* Probe the unicode string for read and write */
470 ProbeForWriteUnicodeString(LinkTarget
);
472 /* Probe the unicode string's buffer for write */
473 SafeLinkTarget
= *LinkTarget
;
474 ProbeForWrite(SafeLinkTarget
.Buffer
,
475 SafeLinkTarget
.MaximumLength
,
478 /* Probe the return length */
479 if(ResultLength
) ProbeForWriteUlong(ResultLength
);
483 /* Probe failure: get exception code */
484 Status
= _SEH_GetExceptionCode();
488 /* Probe failed, return status */
489 if(!NT_SUCCESS(Status
)) return Status
;
493 /* No need to probe */
494 SafeLinkTarget
= *LinkTarget
;
497 /* Reference the object */
498 Status
= ObReferenceObjectByHandle(LinkHandle
,
502 (PVOID
*)&SymlinkObject
,
504 if (NT_SUCCESS(Status
))
506 /* Lock the object */
507 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
510 * So here's the thing: If you specify a return length, then the
511 * implementation will use the maximum length. If you don't, then
512 * it will use the length.
514 LengthUsed
= ResultLength
? SymlinkObject
->LinkTarget
.MaximumLength
:
515 SymlinkObject
->LinkTarget
.Length
;
517 /* Enter SEH so we can safely copy */
520 /* Make sure our buffer will fit */
521 if (LengthUsed
<= SafeLinkTarget
.MaximumLength
)
523 /* Copy the buffer */
524 RtlCopyMemory(SafeLinkTarget
.Buffer
,
525 SymlinkObject
->LinkTarget
.Buffer
,
528 /* Copy the new length */
529 LinkTarget
->Length
= SymlinkObject
->LinkTarget
.Length
;
533 /* Otherwise set the failure status */
534 Status
= STATUS_BUFFER_TOO_SMALL
;
537 /* In both cases, check if the required length was requested */
541 *ResultLength
= SymlinkObject
->LinkTarget
.MaximumLength
;
544 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
546 /* Get the error code */
547 Status
= _SEH_GetExceptionCode();
551 /* Unlock and dereference the object */
552 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
553 ObDereferenceObject(SymlinkObject
);
556 /* Return query status */