Create a branch for cmake bringup.
[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 /* 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 ASSERT(FALSE);
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 PAGED_CODE();
127
128 /* Assume failure */
129 *NextObject = NULL;
130
131 /* Check if we're out of name to parse */
132 if (!RemainingName->Length)
133 {
134 /* Check if we got an object type */
135 if (ObjectType)
136 {
137 /* Reference the object only */
138 Status = ObReferenceObjectByPointer(ParsedObject,
139 0,
140 ObjectType,
141 AccessMode);
142 if (NT_SUCCESS(Status))
143 {
144 /* Return it */
145 *NextObject = ParsedObject;
146 }
147
148 if ((NT_SUCCESS(Status)) || (Status != STATUS_OBJECT_TYPE_MISMATCH))
149 {
150 /* Fail */
151 return Status;
152 }
153 }
154 }
155 else if (RemainingName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
156 {
157 /* Symbolic links must start with a backslash */
158 return STATUS_OBJECT_TYPE_MISMATCH;
159 }
160
161 /* Set the target path and length */
162 TargetPath = &SymlinkObject->LinkTarget;
163 LengthUsed = TargetPath->Length + RemainingName->Length;
164
165 /* Optimization: check if the new name is shorter */
166 if (FullPath->MaximumLength <= LengthUsed)
167 {
168 /* It's not, allocate a new one */
169 MaximumLength = LengthUsed + sizeof(WCHAR);
170 NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
171 MaximumLength,
172 TAG_SYMLINK_TTARGET);
173 if (!NewTargetPath) return STATUS_INSUFFICIENT_RESOURCES;
174 }
175 else
176 {
177 /* It is! Reuse the name... */
178 MaximumLength = FullPath->MaximumLength;
179 NewTargetPath = FullPath->Buffer;
180 }
181
182 /* Make sure we have a length */
183 if (RemainingName->Length)
184 {
185 /* Copy the new path */
186 RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TargetPath->Length),
187 RemainingName->Buffer,
188 RemainingName->Length);
189 }
190
191 /* Copy the target path and null-terminate it */
192 RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TargetPath->Length);
193 NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
194
195 /* If the optimization didn't work, free the old buffer */
196 if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
197
198 /* Update the path values */
199 FullPath->Length = (USHORT)LengthUsed;
200 FullPath->MaximumLength = (USHORT)MaximumLength;
201 FullPath->Buffer = NewTargetPath;
202
203 /* Tell the parse routine to start reparsing */
204 return STATUS_REPARSE;
205 }
206
207 /* PUBLIC FUNCTIONS **********************************************************/
208
209 /*++
210 * @name NtCreateSymbolicLinkObject
211 * @implemented NT4
212 *
213 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
214 *
215 * @param LinkHandle
216 * Variable which receives the symlink handle.
217 *
218 * @param DesiredAccess
219 * Desired access to the symlink.
220 *
221 * @param ObjectAttributes
222 * Structure describing the symlink.
223 *
224 * @param LinkTarget
225 * Unicode string defining the symlink's target
226 *
227 * @return STATUS_SUCCESS or appropriate error value.
228 *
229 * @remarks None.
230 *
231 *--*/
232 NTSTATUS
233 NTAPI
234 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
235 IN ACCESS_MASK DesiredAccess,
236 IN POBJECT_ATTRIBUTES ObjectAttributes,
237 IN PUNICODE_STRING LinkTarget)
238 {
239 HANDLE hLink;
240 POBJECT_SYMBOLIC_LINK SymbolicLink;
241 UNICODE_STRING CapturedLinkTarget;
242 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
243 NTSTATUS Status;
244 PAGED_CODE();
245
246 /* Check if we need to probe parameters */
247 if (PreviousMode != KernelMode)
248 {
249 _SEH2_TRY
250 {
251 /* Probe the target */
252 CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget);
253 ProbeForRead(CapturedLinkTarget.Buffer,
254 CapturedLinkTarget.MaximumLength,
255 sizeof(WCHAR));
256
257 /* Probe the return handle */
258 ProbeForWriteHandle(LinkHandle);
259 }
260 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
261 {
262 /* Return the exception code */
263 _SEH2_YIELD(return _SEH2_GetExceptionCode());
264 }
265 _SEH2_END;
266 }
267 else
268 {
269 /* No need to capture */
270 CapturedLinkTarget = *LinkTarget;
271 }
272
273 /* Check if the maximum length is odd */
274 if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
275 {
276 /* Round it down */
277 CapturedLinkTarget.MaximumLength =
278 (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
279 }
280
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))
285 {
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;
290 }
291
292 /* Create the object */
293 Status = ObCreateObject(PreviousMode,
294 ObSymbolicLinkType,
295 ObjectAttributes,
296 PreviousMode,
297 NULL,
298 sizeof(OBJECT_SYMBOLIC_LINK),
299 0,
300 0,
301 (PVOID*)&SymbolicLink);
302 if (NT_SUCCESS(Status))
303 {
304 /* Success! Fill in the creation time immediately */
305 KeQuerySystemTime(&SymbolicLink->CreationTime);
306
307 /* Setup the target name */
308 SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
309 SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.Length +
310 sizeof(WCHAR);
311 SymbolicLink->LinkTarget.Buffer =
312 ExAllocatePoolWithTag(PagedPool,
313 CapturedLinkTarget.MaximumLength,
314 TAG_SYMLINK_TARGET);
315 if (!SymbolicLink->LinkTarget.Buffer) return STATUS_NO_MEMORY;
316
317 /* Copy it */
318 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
319 CapturedLinkTarget.Buffer,
320 CapturedLinkTarget.MaximumLength);
321
322 /* Initialize the remaining name, dos drive index and target object */
323 SymbolicLink->LinkTargetObject = NULL;
324 SymbolicLink->DosDeviceDriveIndex = 0;
325 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
326
327 /* Insert it into the object tree */
328 Status = ObInsertObject(SymbolicLink,
329 NULL,
330 DesiredAccess,
331 0,
332 NULL,
333 &hLink);
334 if (NT_SUCCESS(Status))
335 {
336 _SEH2_TRY
337 {
338 /* Return the handle to caller */
339 *LinkHandle = hLink;
340 }
341 _SEH2_EXCEPT(ExSystemExceptionFilter())
342 {
343 /* Get exception code */
344 Status = _SEH2_GetExceptionCode();
345 }
346 _SEH2_END;
347 }
348 }
349
350 /* Return status to caller */
351 return Status;
352 }
353
354 /*++
355 * @name NtOpenSymbolicLinkObject
356 * @implemented NT4
357 *
358 * The NtOpenSymbolicLinkObject opens a symbolic link object.
359 *
360 * @param LinkHandle
361 * Variable which receives the symlink handle.
362 *
363 * @param DesiredAccess
364 * Desired access to the symlink.
365 *
366 * @param ObjectAttributes
367 * Structure describing the symlink.
368 *
369 * @return STATUS_SUCCESS or appropriate error value.
370 *
371 * @remarks None.
372 *
373 *--*/
374 NTSTATUS
375 NTAPI
376 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
377 IN ACCESS_MASK DesiredAccess,
378 IN POBJECT_ATTRIBUTES ObjectAttributes)
379 {
380 HANDLE hLink;
381 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
382 NTSTATUS Status;
383 PAGED_CODE();
384
385 /* Check if we need to probe parameters */
386 if (PreviousMode != KernelMode)
387 {
388 _SEH2_TRY
389 {
390 /* Probe the return handle */
391 ProbeForWriteHandle(LinkHandle);
392 }
393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
394 {
395 /* Return the exception code */
396 _SEH2_YIELD(return _SEH2_GetExceptionCode());
397 }
398 _SEH2_END;
399 }
400
401 /* Open the object */
402 Status = ObOpenObjectByName(ObjectAttributes,
403 ObSymbolicLinkType,
404 PreviousMode,
405 NULL,
406 DesiredAccess,
407 NULL,
408 &hLink);
409 if (NT_SUCCESS(Status))
410 {
411 _SEH2_TRY
412 {
413 /* Return the handle to caller */
414 *LinkHandle = hLink;
415 }
416 _SEH2_EXCEPT(ExSystemExceptionFilter())
417 {
418 /* Get exception code */
419 Status = _SEH2_GetExceptionCode();
420 }
421 _SEH2_END;
422 }
423
424 /* Return status to caller */
425 return Status;
426 }
427
428 /*++
429 * @name NtQuerySymbolicLinkObject
430 * @implemented NT4
431 *
432 * The NtQuerySymbolicLinkObject queries a symbolic link object.
433 *
434 * @param LinkHandle
435 * Symlink handle to query
436 *
437 * @param LinkTarget
438 * Unicode string defining the symlink's target
439 *
440 * @param ResultLength
441 * Caller supplied storage for the number of bytes written (or NULL).
442 *
443 * @return STATUS_SUCCESS or appropriate error value.
444 *
445 * @remarks None.
446 *
447 *--*/
448 NTSTATUS
449 NTAPI
450 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
451 OUT PUNICODE_STRING LinkTarget,
452 OUT PULONG ResultLength OPTIONAL)
453 {
454 UNICODE_STRING SafeLinkTarget = { 0, 0, NULL };
455 POBJECT_SYMBOLIC_LINK SymlinkObject;
456 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
457 NTSTATUS Status;
458 ULONG LengthUsed;
459 PAGED_CODE();
460
461 if (PreviousMode != KernelMode)
462 {
463 _SEH2_TRY
464 {
465 /* Probe the unicode string for read and write */
466 ProbeForWriteUnicodeString(LinkTarget);
467
468 /* Probe the unicode string's buffer for write */
469 SafeLinkTarget = *LinkTarget;
470 ProbeForWrite(SafeLinkTarget.Buffer,
471 SafeLinkTarget.MaximumLength,
472 sizeof(WCHAR));
473
474 /* Probe the return length */
475 if (ResultLength) ProbeForWriteUlong(ResultLength);
476 }
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
478 {
479 /* Return the exception code */
480 _SEH2_YIELD(return _SEH2_GetExceptionCode());
481 }
482 _SEH2_END;
483 }
484 else
485 {
486 /* No need to probe */
487 SafeLinkTarget = *LinkTarget;
488 }
489
490 /* Reference the object */
491 Status = ObReferenceObjectByHandle(LinkHandle,
492 SYMBOLIC_LINK_QUERY,
493 ObSymbolicLinkType,
494 PreviousMode,
495 (PVOID *)&SymlinkObject,
496 NULL);
497 if (NT_SUCCESS(Status))
498 {
499 /* Lock the object */
500 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
501
502 /*
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.
506 */
507 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
508 SymlinkObject->LinkTarget.Length;
509
510 /* Enter SEH so we can safely copy */
511 _SEH2_TRY
512 {
513 /* Make sure our buffer will fit */
514 if (LengthUsed <= SafeLinkTarget.MaximumLength)
515 {
516 /* Copy the buffer */
517 RtlCopyMemory(SafeLinkTarget.Buffer,
518 SymlinkObject->LinkTarget.Buffer,
519 LengthUsed);
520
521 /* Copy the new length */
522 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
523 }
524 else
525 {
526 /* Otherwise set the failure status */
527 Status = STATUS_BUFFER_TOO_SMALL;
528 }
529
530 /* In both cases, check if the required length was requested */
531 if (ResultLength)
532 {
533 /* Then return it */
534 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
535 }
536 }
537 _SEH2_EXCEPT(ExSystemExceptionFilter())
538 {
539 /* Get the error code */
540 Status = _SEH2_GetExceptionCode();
541 }
542 _SEH2_END;
543
544 /* Unlock and dereference the object */
545 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
546 ObDereferenceObject(SymlinkObject);
547 }
548
549 /* Return query status */
550 return Status;
551 }
552
553 /* EOF */