sync with trunk head (34904)
[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/symlink.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 <internal/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 /* FIXME: Device maps not supported yet */
27
28 }
29
30 VOID
31 NTAPI
32 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
33 {
34 POBJECT_HEADER ObjectHeader;
35 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
36
37 /* Get header data */
38 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
39 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
40
41 /* Check if we are not actually in a directory with a device map */
42 if (!(ObjectNameInfo) ||
43 !(ObjectNameInfo->Directory) ||
44 !(ObjectNameInfo->Directory->DeviceMap))
45 {
46 /* There's nothing to do, return */
47 return;
48 }
49
50 /* FIXME: We don't support device maps yet */
51 DPRINT1("Unhandled path!\n");
52 KEBUGCHECK(0);
53 }
54
55 /*++
56 * @name ObpDeleteSymbolicLink
57 *
58 * The ObpDeleteSymbolicLink routine <FILLMEIN>
59 *
60 * @param ObjectBody
61 * <FILLMEIN>
62 *
63 * @return None.
64 *
65 * @remarks None.
66 *
67 *--*/
68 VOID
69 NTAPI
70 ObpDeleteSymbolicLink(PVOID ObjectBody)
71 {
72 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ObjectBody;
73
74 /* Make sure that the symbolic link has a name */
75 if (SymlinkObject->LinkTarget.Buffer)
76 {
77 /* Free the name */
78 ExFreePool(SymlinkObject->LinkTarget.Buffer);
79 SymlinkObject->LinkTarget.Buffer = NULL;
80 }
81 }
82
83 /*++
84 * @name ObpParseSymbolicLink
85 *
86 * The ObpParseSymbolicLink routine <FILLMEIN>
87 *
88 * @param Object
89 * <FILLMEIN>
90 *
91 * @param NextObject
92 * <FILLMEIN>
93 *
94 * @param FullPath
95 * <FILLMEIN>
96 *
97 * @param RemainingPath
98 * <FILLMEIN>
99 *
100 * @param Attributes
101 * <FILLMEIN>
102 *
103 * @return STATUS_SUCCESS or appropriate error value.
104 *
105 * @remarks None.
106 *
107 *--*/
108 NTSTATUS
109 NTAPI
110 ObpParseSymbolicLink(IN PVOID ParsedObject,
111 IN PVOID ObjectType,
112 IN OUT PACCESS_STATE AccessState,
113 IN KPROCESSOR_MODE AccessMode,
114 IN ULONG Attributes,
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)
120 {
121 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ParsedObject;
122 PUNICODE_STRING TargetPath;
123 PWSTR NewTargetPath;
124 ULONG LengthUsed, MaximumLength;
125 NTSTATUS Status;
126
127 /* Assume failure */
128 *NextObject = NULL;
129
130 /* Check if we're out of name to parse */
131 if (!RemainingName->Length)
132 {
133 /* Check if we got an object type */
134 if (ObjectType)
135 {
136 /* Reference the object only */
137 Status = ObReferenceObjectByPointer(ParsedObject,
138 0,
139 ObjectType,
140 AccessMode);
141 if (NT_SUCCESS(Status))
142 {
143 /* Return it */
144 *NextObject = ParsedObject;
145 }
146
147 if ((NT_SUCCESS(Status)) || (Status != STATUS_OBJECT_TYPE_MISMATCH))
148 {
149 /* Fail */
150 return Status;
151 }
152 }
153 }
154 else if (RemainingName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
155 {
156 /* Symbolic links must start with a backslash */
157 return STATUS_OBJECT_TYPE_MISMATCH;
158 }
159
160 /* Set the target path and length */
161 TargetPath = &SymlinkObject->LinkTarget;
162 LengthUsed = TargetPath->Length + RemainingName->Length;
163
164 /* Optimization: check if the new name is shorter */
165 if (FullPath->MaximumLength <= LengthUsed)
166 {
167 /* It's not, allocate a new one */
168 MaximumLength = LengthUsed + sizeof(WCHAR);
169 NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
170 MaximumLength,
171 TAG_SYMLINK_TTARGET);
172 }
173 else
174 {
175 /* It is! Reuse the name... */
176 MaximumLength = FullPath->MaximumLength;
177 NewTargetPath = FullPath->Buffer;
178 }
179
180 /* Make sure we have a length */
181 if (RemainingName->Length)
182 {
183 /* Copy the new path */
184 RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TargetPath->Length),
185 RemainingName->Buffer,
186 RemainingName->Length);
187 }
188
189 /* Copy the target path and null-terminate it */
190 RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TargetPath->Length);
191 NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
192
193 /* If the optimization didn't work, free the old buffer */
194 if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
195
196 /* Update the path values */
197 FullPath->Length = (USHORT)LengthUsed;
198 FullPath->MaximumLength = (USHORT)MaximumLength;
199 FullPath->Buffer = NewTargetPath;
200
201 /* Tell the parse routine to start reparsing */
202 return STATUS_REPARSE;
203 }
204
205 /* PUBLIC FUNCTIONS **********************************************************/
206
207 /*++
208 * @name NtCreateSymbolicLinkObject
209 * @implemented NT4
210 *
211 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
212 *
213 * @param LinkHandle
214 * Variable which receives the symlink handle.
215 *
216 * @param DesiredAccess
217 * Desired access to the symlink.
218 *
219 * @param ObjectAttributes
220 * Structure describing the symlink.
221 *
222 * @param LinkTarget
223 * Unicode string defining the symlink's target
224 *
225 * @return STATUS_SUCCESS or appropriate error value.
226 *
227 * @remarks None.
228 *
229 *--*/
230 NTSTATUS
231 NTAPI
232 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
233 IN ACCESS_MASK DesiredAccess,
234 IN POBJECT_ATTRIBUTES ObjectAttributes,
235 IN PUNICODE_STRING LinkTarget)
236 {
237 HANDLE hLink;
238 POBJECT_SYMBOLIC_LINK SymbolicLink;
239 UNICODE_STRING CapturedLinkTarget;
240 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
241 NTSTATUS Status = STATUS_SUCCESS;
242 PAGED_CODE();
243
244 /* Check if we need to probe parameters */
245 if(PreviousMode != KernelMode)
246 {
247 _SEH_TRY
248 {
249 /* Probe the target */
250 CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget);
251 ProbeForRead(CapturedLinkTarget.Buffer,
252 CapturedLinkTarget.MaximumLength,
253 sizeof(WCHAR));
254
255 /* Probe the return handle */
256 ProbeForWriteHandle(LinkHandle);
257 }
258 _SEH_HANDLE
259 {
260 /* Exception, get the error code */
261 Status = _SEH_GetExceptionCode();
262 }
263 _SEH_END;
264
265 /* Probing failed, return the error code */
266 if(!NT_SUCCESS(Status)) return Status;
267 }
268 else
269 {
270 /* No need to capture */
271 CapturedLinkTarget = *LinkTarget;
272 }
273
274 /* Check if the maximum length is odd */
275 if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
276 {
277 /* Round it down */
278 CapturedLinkTarget.MaximumLength =
279 (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
280 }
281
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))
286 {
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;
291 }
292
293 /* Create the object */
294 Status = ObCreateObject(PreviousMode,
295 ObSymbolicLinkType,
296 ObjectAttributes,
297 PreviousMode,
298 NULL,
299 sizeof(OBJECT_SYMBOLIC_LINK),
300 0,
301 0,
302 (PVOID*)&SymbolicLink);
303 if (NT_SUCCESS(Status))
304 {
305 /* Success! Fill in the creation time immediately */
306 KeQuerySystemTime(&SymbolicLink->CreationTime);
307
308 /* Setup the target name */
309 SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
310 SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.Length +
311 sizeof(WCHAR);
312 SymbolicLink->LinkTarget.Buffer =
313 ExAllocatePoolWithTag(PagedPool,
314 CapturedLinkTarget.MaximumLength,
315 TAG_SYMLINK_TARGET);
316 if (!SymbolicLink->LinkTarget.Buffer) return STATUS_NO_MEMORY;
317
318 /* Copy it */
319 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
320 CapturedLinkTarget.Buffer,
321 CapturedLinkTarget.MaximumLength);
322
323 /* Initialize the remaining name, dos drive index and target object */
324 SymbolicLink->LinkTargetObject = NULL;
325 SymbolicLink->DosDeviceDriveIndex = 0;
326 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
327
328 /* Insert it into the object tree */
329 Status = ObInsertObject(SymbolicLink,
330 NULL,
331 DesiredAccess,
332 0,
333 NULL,
334 &hLink);
335 if (NT_SUCCESS(Status))
336 {
337 _SEH_TRY
338 {
339 /* Return the handle to caller */
340 *LinkHandle = hLink;
341 }
342 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
343 {
344 /* Get exception code */
345 Status = _SEH_GetExceptionCode();
346 }
347 _SEH_END;
348 }
349 }
350
351 /* Return status to caller */
352 return Status;
353 }
354
355 /*++
356 * @name NtOpenSymbolicLinkObject
357 * @implemented NT4
358 *
359 * The NtOpenSymbolicLinkObject opens a symbolic link object.
360 *
361 * @param LinkHandle
362 * Variable which receives the symlink handle.
363 *
364 * @param DesiredAccess
365 * Desired access to the symlink.
366 *
367 * @param ObjectAttributes
368 * Structure describing the symlink.
369 *
370 * @return STATUS_SUCCESS or appropriate error value.
371 *
372 * @remarks None.
373 *
374 *--*/
375 NTSTATUS
376 NTAPI
377 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
378 IN ACCESS_MASK DesiredAccess,
379 IN POBJECT_ATTRIBUTES ObjectAttributes)
380 {
381 HANDLE hLink;
382 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
383 NTSTATUS Status = STATUS_SUCCESS;
384 PAGED_CODE();
385
386 /* Check if we need to probe parameters */
387 if(PreviousMode != KernelMode)
388 {
389 _SEH_TRY
390 {
391 /* Probe the return handle */
392 ProbeForWriteHandle(LinkHandle);
393 }
394 _SEH_HANDLE
395 {
396 /* Exception, get the error code */
397 Status = _SEH_GetExceptionCode();
398 }
399 _SEH_END;
400
401 /* Probing failed, return the error code */
402 if(!NT_SUCCESS(Status)) return Status;
403 }
404
405 /* Open the object */
406 Status = ObOpenObjectByName(ObjectAttributes,
407 ObSymbolicLinkType,
408 PreviousMode,
409 NULL,
410 DesiredAccess,
411 NULL,
412 &hLink);
413 if (NT_SUCCESS(Status))
414 {
415 _SEH_TRY
416 {
417 /* Return the handle to caller */
418 *LinkHandle = hLink;
419 }
420 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
421 {
422 /* Get exception code */
423 Status = _SEH_GetExceptionCode();
424 }
425 _SEH_END;
426 }
427
428 /* Return status to caller */
429 return Status;
430 }
431
432 /*++
433 * @name NtQuerySymbolicLinkObject
434 * @implemented NT4
435 *
436 * The NtQuerySymbolicLinkObject queries a symbolic link object.
437 *
438 * @param LinkHandle
439 * Symlink handle to query
440 *
441 * @param LinkTarget
442 * Unicode string defining the symlink's target
443 *
444 * @param ResultLength
445 * Caller supplied storage for the number of bytes written (or NULL).
446 *
447 * @return STATUS_SUCCESS or appropriate error value.
448 *
449 * @remarks None.
450 *
451 *--*/
452 NTSTATUS
453 NTAPI
454 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
455 OUT PUNICODE_STRING LinkTarget,
456 OUT PULONG ResultLength OPTIONAL)
457 {
458 UNICODE_STRING SafeLinkTarget = {0};
459 POBJECT_SYMBOLIC_LINK SymlinkObject;
460 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
461 NTSTATUS Status = STATUS_SUCCESS;
462 ULONG LengthUsed;
463 PAGED_CODE();
464
465 if(PreviousMode != KernelMode)
466 {
467 _SEH_TRY
468 {
469 /* Probe the unicode string for read and write */
470 ProbeForWriteUnicodeString(LinkTarget);
471
472 /* Probe the unicode string's buffer for write */
473 SafeLinkTarget = *LinkTarget;
474 ProbeForWrite(SafeLinkTarget.Buffer,
475 SafeLinkTarget.MaximumLength,
476 sizeof(WCHAR));
477
478 /* Probe the return length */
479 if(ResultLength) ProbeForWriteUlong(ResultLength);
480 }
481 _SEH_HANDLE
482 {
483 /* Probe failure: get exception code */
484 Status = _SEH_GetExceptionCode();
485 }
486 _SEH_END;
487
488 /* Probe failed, return status */
489 if(!NT_SUCCESS(Status)) return Status;
490 }
491 else
492 {
493 /* No need to probe */
494 SafeLinkTarget = *LinkTarget;
495 }
496
497 /* Reference the object */
498 Status = ObReferenceObjectByHandle(LinkHandle,
499 SYMBOLIC_LINK_QUERY,
500 ObSymbolicLinkType,
501 PreviousMode,
502 (PVOID *)&SymlinkObject,
503 NULL);
504 if (NT_SUCCESS(Status))
505 {
506 /* Lock the object */
507 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
508
509 /*
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.
513 */
514 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
515 SymlinkObject->LinkTarget.Length;
516
517 /* Enter SEH so we can safely copy */
518 _SEH_TRY
519 {
520 /* Make sure our buffer will fit */
521 if (LengthUsed <= SafeLinkTarget.MaximumLength)
522 {
523 /* Copy the buffer */
524 RtlCopyMemory(SafeLinkTarget.Buffer,
525 SymlinkObject->LinkTarget.Buffer,
526 LengthUsed);
527
528 /* Copy the new length */
529 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
530 }
531 else
532 {
533 /* Otherwise set the failure status */
534 Status = STATUS_BUFFER_TOO_SMALL;
535 }
536
537 /* In both cases, check if the required length was requested */
538 if (ResultLength)
539 {
540 /* Then return it */
541 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
542 }
543 }
544 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
545 {
546 /* Get the error code */
547 Status = _SEH_GetExceptionCode();
548 }
549 _SEH_END;
550
551 /* Unlock and dereference the object */
552 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
553 ObDereferenceObject(SymlinkObject);
554 }
555
556 /* Return query status */
557 return Status;
558 }
559
560 /* EOF */