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)
10 /* INCLUDES *****************************************************************/
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
;
131 /* Check if we're out of name to parse */
132 if (!RemainingName
->Length
)
134 /* Check if we got an object type */
137 /* Reference the object only */
138 Status
= ObReferenceObjectByPointer(ParsedObject
,
142 if (NT_SUCCESS(Status
))
145 *NextObject
= ParsedObject
;
148 if ((NT_SUCCESS(Status
)) || (Status
!= STATUS_OBJECT_TYPE_MISMATCH
))
155 else if (RemainingName
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
)
157 /* Symbolic links must start with a backslash */
158 return STATUS_OBJECT_TYPE_MISMATCH
;
161 /* Set the target path and length */
162 TargetPath
= &SymlinkObject
->LinkTarget
;
163 LengthUsed
= TargetPath
->Length
+ RemainingName
->Length
;
165 /* Optimization: check if the new name is shorter */
166 if (FullPath
->MaximumLength
<= LengthUsed
)
168 /* It's not, allocate a new one */
169 MaximumLength
= LengthUsed
+ sizeof(WCHAR
);
170 NewTargetPath
= ExAllocatePoolWithTag(NonPagedPool
,
172 TAG_SYMLINK_TTARGET
);
173 if (!NewTargetPath
) return STATUS_INSUFFICIENT_RESOURCES
;
177 /* It is! Reuse the name... */
178 MaximumLength
= FullPath
->MaximumLength
;
179 NewTargetPath
= FullPath
->Buffer
;
182 /* Make sure we have a length */
183 if (RemainingName
->Length
)
185 /* Copy the new path */
186 RtlMoveMemory((PVOID
)((ULONG_PTR
)NewTargetPath
+ TargetPath
->Length
),
187 RemainingName
->Buffer
,
188 RemainingName
->Length
);
191 /* Copy the target path and null-terminate it */
192 RtlCopyMemory(NewTargetPath
, TargetPath
->Buffer
, TargetPath
->Length
);
193 NewTargetPath
[LengthUsed
/ sizeof(WCHAR
)] = UNICODE_NULL
;
195 /* If the optimization didn't work, free the old buffer */
196 if (NewTargetPath
!= FullPath
->Buffer
) ExFreePool(FullPath
->Buffer
);
198 /* Update the path values */
199 FullPath
->Length
= (USHORT
)LengthUsed
;
200 FullPath
->MaximumLength
= (USHORT
)MaximumLength
;
201 FullPath
->Buffer
= NewTargetPath
;
203 /* Tell the parse routine to start reparsing */
204 return STATUS_REPARSE
;
207 /* PUBLIC FUNCTIONS **********************************************************/
210 * @name NtCreateSymbolicLinkObject
213 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
216 * Variable which receives the symlink handle.
218 * @param DesiredAccess
219 * Desired access to the symlink.
221 * @param ObjectAttributes
222 * Structure describing the symlink.
225 * Unicode string defining the symlink's target
227 * @return STATUS_SUCCESS or appropriate error value.
234 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle
,
235 IN ACCESS_MASK DesiredAccess
,
236 IN POBJECT_ATTRIBUTES ObjectAttributes
,
237 IN PUNICODE_STRING LinkTarget
)
240 POBJECT_SYMBOLIC_LINK SymbolicLink
;
241 UNICODE_STRING CapturedLinkTarget
;
242 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
246 /* Check if we need to probe parameters */
247 if (PreviousMode
!= KernelMode
)
251 /* Probe the target */
252 CapturedLinkTarget
= ProbeForReadUnicodeString(LinkTarget
);
253 ProbeForRead(CapturedLinkTarget
.Buffer
,
254 CapturedLinkTarget
.MaximumLength
,
257 /* Probe the return handle */
258 ProbeForWriteHandle(LinkHandle
);
260 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
262 /* Return the exception code */
263 _SEH2_YIELD(return _SEH2_GetExceptionCode());
269 /* No need to capture */
270 CapturedLinkTarget
= *LinkTarget
;
273 /* Check if the maximum length is odd */
274 if (CapturedLinkTarget
.MaximumLength
% sizeof(WCHAR
))
277 CapturedLinkTarget
.MaximumLength
=
278 (USHORT
)ALIGN_DOWN(CapturedLinkTarget
.MaximumLength
, WCHAR
);
281 /* Fail if the length is odd, or if the maximum is smaller or 0 */
282 if ((CapturedLinkTarget
.Length
% sizeof(WCHAR
)) ||
283 (CapturedLinkTarget
.MaximumLength
< CapturedLinkTarget
.Length
) ||
284 !(CapturedLinkTarget
.MaximumLength
))
286 /* This message is displayed on the debugger in Windows */
287 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
288 &CapturedLinkTarget
);
289 return STATUS_INVALID_PARAMETER
;
292 /* Create the object */
293 Status
= ObCreateObject(PreviousMode
,
298 sizeof(OBJECT_SYMBOLIC_LINK
),
301 (PVOID
*)&SymbolicLink
);
302 if (NT_SUCCESS(Status
))
304 /* Success! Fill in the creation time immediately */
305 KeQuerySystemTime(&SymbolicLink
->CreationTime
);
307 /* Setup the target name */
308 SymbolicLink
->LinkTarget
.Length
= CapturedLinkTarget
.Length
;
309 SymbolicLink
->LinkTarget
.MaximumLength
= CapturedLinkTarget
.Length
+
311 SymbolicLink
->LinkTarget
.Buffer
=
312 ExAllocatePoolWithTag(PagedPool
,
313 CapturedLinkTarget
.MaximumLength
,
315 if (!SymbolicLink
->LinkTarget
.Buffer
) return STATUS_NO_MEMORY
;
318 RtlCopyMemory(SymbolicLink
->LinkTarget
.Buffer
,
319 CapturedLinkTarget
.Buffer
,
320 CapturedLinkTarget
.MaximumLength
);
322 /* Initialize the remaining name, dos drive index and target object */
323 SymbolicLink
->LinkTargetObject
= NULL
;
324 SymbolicLink
->DosDeviceDriveIndex
= 0;
325 RtlInitUnicodeString(&SymbolicLink
->LinkTargetRemaining
, NULL
);
327 /* Insert it into the object tree */
328 Status
= ObInsertObject(SymbolicLink
,
334 if (NT_SUCCESS(Status
))
338 /* Return the handle to caller */
341 _SEH2_EXCEPT(ExSystemExceptionFilter())
343 /* Get exception code */
344 Status
= _SEH2_GetExceptionCode();
350 /* Return status to caller */
355 * @name NtOpenSymbolicLinkObject
358 * The NtOpenSymbolicLinkObject opens a symbolic link object.
361 * Variable which receives the symlink handle.
363 * @param DesiredAccess
364 * Desired access to the symlink.
366 * @param ObjectAttributes
367 * Structure describing the symlink.
369 * @return STATUS_SUCCESS or appropriate error value.
376 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle
,
377 IN ACCESS_MASK DesiredAccess
,
378 IN POBJECT_ATTRIBUTES ObjectAttributes
)
381 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
385 /* Check if we need to probe parameters */
386 if (PreviousMode
!= KernelMode
)
390 /* Probe the return handle */
391 ProbeForWriteHandle(LinkHandle
);
393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
395 /* Return the exception code */
396 _SEH2_YIELD(return _SEH2_GetExceptionCode());
401 /* Open the object */
402 Status
= ObOpenObjectByName(ObjectAttributes
,
409 if (NT_SUCCESS(Status
))
413 /* Return the handle to caller */
416 _SEH2_EXCEPT(ExSystemExceptionFilter())
418 /* Get exception code */
419 Status
= _SEH2_GetExceptionCode();
424 /* Return status to caller */
429 * @name NtQuerySymbolicLinkObject
432 * The NtQuerySymbolicLinkObject queries a symbolic link object.
435 * Symlink handle to query
438 * Unicode string defining the symlink's target
440 * @param ResultLength
441 * Caller supplied storage for the number of bytes written (or NULL).
443 * @return STATUS_SUCCESS or appropriate error value.
450 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle
,
451 OUT PUNICODE_STRING LinkTarget
,
452 OUT PULONG ResultLength OPTIONAL
)
454 UNICODE_STRING SafeLinkTarget
= { 0, 0, NULL
};
455 POBJECT_SYMBOLIC_LINK SymlinkObject
;
456 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
461 if (PreviousMode
!= KernelMode
)
465 /* Probe the unicode string for read and write */
466 ProbeForWriteUnicodeString(LinkTarget
);
468 /* Probe the unicode string's buffer for write */
469 SafeLinkTarget
= *LinkTarget
;
470 ProbeForWrite(SafeLinkTarget
.Buffer
,
471 SafeLinkTarget
.MaximumLength
,
474 /* Probe the return length */
475 if (ResultLength
) ProbeForWriteUlong(ResultLength
);
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
479 /* Return the exception code */
480 _SEH2_YIELD(return _SEH2_GetExceptionCode());
486 /* No need to probe */
487 SafeLinkTarget
= *LinkTarget
;
490 /* Reference the object */
491 Status
= ObReferenceObjectByHandle(LinkHandle
,
495 (PVOID
*)&SymlinkObject
,
497 if (NT_SUCCESS(Status
))
499 /* Lock the object */
500 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
503 * So here's the thing: If you specify a return length, then the
504 * implementation will use the maximum length. If you don't, then
505 * it will use the length.
507 LengthUsed
= ResultLength
? SymlinkObject
->LinkTarget
.MaximumLength
:
508 SymlinkObject
->LinkTarget
.Length
;
510 /* Enter SEH so we can safely copy */
513 /* Make sure our buffer will fit */
514 if (LengthUsed
<= SafeLinkTarget
.MaximumLength
)
516 /* Copy the buffer */
517 RtlCopyMemory(SafeLinkTarget
.Buffer
,
518 SymlinkObject
->LinkTarget
.Buffer
,
521 /* Copy the new length */
522 LinkTarget
->Length
= SymlinkObject
->LinkTarget
.Length
;
526 /* Otherwise set the failure status */
527 Status
= STATUS_BUFFER_TOO_SMALL
;
530 /* In both cases, check if the required length was requested */
534 *ResultLength
= SymlinkObject
->LinkTarget
.MaximumLength
;
537 _SEH2_EXCEPT(ExSystemExceptionFilter())
539 /* Get the error code */
540 Status
= _SEH2_GetExceptionCode();
544 /* Unlock and dereference the object */
545 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
546 ObDereferenceObject(SymlinkObject
);
549 /* Return query status */