[BASESRV] Misc fixes: use BaseSrvHeap and silent a DPRINT
[reactos.git] / subsystems / win / basesrv / dosdev.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Base API Server DLL
4 * FILE: subsystems/win/basesrv/dosdev.c
5 * PURPOSE: DOS Devices Management
6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "basesrv.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec;
19
20 /* PRIVATE FUNCTIONS **********************************************************/
21
22 VOID BaseInitDefineDosDevice(VOID)
23 {
24 RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec);
25 }
26
27 VOID BaseCleanupDefineDosDevice(VOID)
28 {
29 RtlDeleteCriticalSection(&BaseDefineDosDeviceCritSec);
30 }
31
32 NTSTATUS
33 GetCallerLuid(PLUID CallerLuid)
34 {
35 NTSTATUS Status;
36 HANDLE TokenHandle;
37 ULONG ReturnLength;
38 TOKEN_STATISTICS TokenInformation;
39
40 /* We need an output buffer */
41 if (CallerLuid == NULL)
42 {
43 return STATUS_INVALID_PARAMETER;
44 }
45
46 /* Open thread token */
47 TokenHandle = 0;
48 ReturnLength = 0;
49 Status = NtOpenThreadToken(NtCurrentThread(),
50 READ_CONTROL | TOKEN_QUERY,
51 FALSE, &TokenHandle);
52 /* If we fail, open process token */
53 if (Status == STATUS_NO_TOKEN)
54 {
55 Status = NtOpenProcessToken(NtCurrentProcess(),
56 READ_CONTROL | TOKEN_QUERY,
57 &TokenHandle);
58 }
59
60 /* In case of a success get caller LUID and copy it back */
61 if (NT_SUCCESS(Status))
62 {
63 Status = NtQueryInformationToken(TokenHandle,
64 TokenStatistics,
65 &TokenInformation,
66 sizeof(TokenInformation),
67 &ReturnLength);
68 if (NT_SUCCESS(Status))
69 {
70 RtlCopyLuid(CallerLuid, &TokenInformation.AuthenticationId);
71 }
72 }
73
74 /* Close token handle */
75 if (TokenHandle != 0)
76 {
77 NtClose(TokenHandle);
78 }
79
80 return Status;
81 }
82
83 NTSTATUS
84 IsGlobalSymbolicLink(HANDLE LinkHandle,
85 PBOOLEAN IsGlobal)
86 {
87 NTSTATUS Status;
88 DWORD ReturnLength;
89 UNICODE_STRING GlobalString;
90 OBJECT_NAME_INFORMATION NameInfo, *PNameInfo;
91
92 /* We need both parameters */
93 if (LinkHandle == 0 || IsGlobal == NULL)
94 {
95 return STATUS_INVALID_PARAMETER;
96 }
97
98 PNameInfo = NULL;
99 _SEH2_TRY
100 {
101 /* Query handle information */
102 Status = NtQueryObject(LinkHandle,
103 ObjectNameInformation,
104 &NameInfo,
105 0,
106 &ReturnLength);
107 /* Only failure we tolerate is length mismatch */
108 if (NT_SUCCESS(Status) || Status == STATUS_INFO_LENGTH_MISMATCH)
109 {
110 /* Allocate big enough buffer */
111 PNameInfo = RtlAllocateHeap(BaseSrvHeap, 0, ReturnLength);
112 if (PNameInfo == NULL)
113 {
114 Status = STATUS_NO_MEMORY;
115 _SEH2_LEAVE;
116 }
117
118 /* Query again handle information */
119 Status = NtQueryObject(LinkHandle,
120 ObjectNameInformation,
121 PNameInfo,
122 ReturnLength,
123 &ReturnLength);
124
125 /*
126 * If it succeed, check we have Global??
127 * If so, return success
128 */
129 if (NT_SUCCESS(Status))
130 {
131 RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??");
132 *IsGlobal = RtlPrefixUnicodeString(&GlobalString, &PNameInfo->Name, FALSE);
133 Status = STATUS_SUCCESS;
134 }
135 }
136 }
137 _SEH2_FINALLY
138 {
139 if (PNameInfo != NULL)
140 {
141 RtlFreeHeap(BaseSrvHeap, 0, PNameInfo);
142 }
143 }
144 _SEH2_END;
145
146 return Status;
147 }
148
149 /* PUBLIC SERVER APIS *********************************************************/
150
151 CSR_API(BaseSrvDefineDosDevice)
152 {
153 NTSTATUS Status;
154 PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest;
155 OBJECT_ATTRIBUTES ObjectAttributes;
156 HANDLE LinkHandle;
157 UNICODE_STRING DeviceName = {0};
158 UNICODE_STRING LinkTarget = {0};
159 ULONG Length;
160 SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
161 SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
162 PSID SystemSid;
163 PSID WorldSid;
164 PWSTR lpBuffer;
165 WCHAR Letter;
166 SHORT AbsLetter;
167 BOOLEAN DriveLetter = FALSE;
168 BOOLEAN RemoveDefinition;
169 BOOLEAN HandleTarget;
170 BOOLEAN HandleSMB = FALSE;
171 BOOLEAN IsGlobal = FALSE;
172 ULONG CchLengthLeft;
173 ULONG CchLength;
174 ULONG TargetLength;
175 PWSTR TargetBuffer;
176 PWSTR CurrentBuffer;
177 /* We store them on the stack, they are known in advance */
178 union {
179 SECURITY_DESCRIPTOR SecurityDescriptor;
180 UCHAR Buffer[20];
181 } SecurityDescriptor;
182 union {
183 ACL Dacl;
184 UCHAR Buffer[256];
185 } Dacl;
186 ACCESS_MASK AccessMask;
187 LUID CallerLuid;
188 WCHAR * CurrentPtr;
189 WCHAR CurrentChar;
190 PWSTR OrigPtr;
191 PWSTR InterPtr;
192 BOOLEAN RemoveFound;
193
194 #if 0
195 /* FIXME: Check why it fails.... */
196 if (!CsrValidateMessageBuffer(ApiMessage,
197 (PVOID*)&DefineDosDeviceRequest->DeviceName,
198 DefineDosDeviceRequest->DeviceName.Length,
199 1) ||
200 (DefineDosDeviceRequest->DeviceName.Length & 1) != 0 ||
201 !CsrValidateMessageBuffer(ApiMessage,
202 (PVOID*)&DefineDosDeviceRequest->TargetPath,
203 (DefineDosDeviceRequest->TargetPath.Length != 0 ? sizeof(UNICODE_NULL) : 0) + DefineDosDeviceRequest->TargetPath.Length,
204 1) ||
205 (DefineDosDeviceRequest->TargetPath.Length & 1) != 0)
206 {
207 return STATUS_INVALID_PARAMETER;
208 }
209 #endif
210
211 DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ (%d), TargetPath:%wZ (%d)\n",
212 DefineDosDeviceRequest->Flags,
213 &DefineDosDeviceRequest->DeviceName,
214 DefineDosDeviceRequest->DeviceName.Length,
215 &DefineDosDeviceRequest->TargetPath,
216 DefineDosDeviceRequest->TargetPath.Length);
217
218 /*
219 * Allocate a buffer big enough to contain:
220 * - device name
221 * - targets
222 */
223 lpBuffer = RtlAllocateHeap(BaseSrvHeap, 0, 0x2000);
224 if (lpBuffer == NULL)
225 {
226 return STATUS_NO_MEMORY;
227 }
228
229 /* Enter our critical section */
230 Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec);
231 if (!NT_SUCCESS(Status))
232 {
233 DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n",
234 Status);
235 RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
236 return Status;
237 }
238
239 LinkHandle = 0;
240 /* Does the caller wants to remove definition? */
241 RemoveDefinition = !!(DefineDosDeviceRequest->Flags & DDD_REMOVE_DEFINITION);
242 _SEH2_TRY
243 {
244 /* First of all, check if that's a drive letter device amongst LUID mappings */
245 if (BaseStaticServerData->LUIDDeviceMapsEnabled && !(DefineDosDeviceRequest->Flags & DDD_NO_BROADCAST_SYSTEM))
246 {
247 if (DefineDosDeviceRequest->DeviceName.Buffer != NULL &&
248 DefineDosDeviceRequest->DeviceName.Length == 2 * sizeof(WCHAR) &&
249 DefineDosDeviceRequest->DeviceName.Buffer[1] == L':')
250 {
251 Letter = DefineDosDeviceRequest->DeviceName.Buffer[0];
252
253 /* Handle both lower cases and upper cases */
254 AbsLetter = Letter - L'a';
255 if (AbsLetter < 26 && AbsLetter >= 0)
256 {
257 Letter = RtlUpcaseUnicodeChar(Letter);
258 }
259
260 AbsLetter = Letter - L'A';
261 if (AbsLetter < 26)
262 {
263 /* That's a letter! */
264 DriveLetter = TRUE;
265 }
266 }
267 }
268
269 /* We can only broadcast drive letters in case of LUID mappings */
270 if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE &&
271 !DriveLetter)
272 {
273 Status = STATUS_INVALID_PARAMETER;
274 _SEH2_LEAVE;
275 }
276
277 /* First usage of our buffer: create device name */
278 CchLength = _snwprintf(lpBuffer, 0x1000, L"\\??\\%wZ", &DefineDosDeviceRequest->DeviceName);
279 CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
280 CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
281 RtlInitUnicodeString(&DeviceName, lpBuffer);
282
283 /* And prepare to open it */
284 InitializeObjectAttributes(&ObjectAttributes,
285 &DeviceName,
286 OBJ_CASE_INSENSITIVE,
287 NULL,
288 NULL);
289
290 /* Assume it's OK and has a target to deal with */
291 HandleTarget = TRUE;
292
293 /* Move to the client context if the mapping was local */
294 if (!CsrImpersonateClient(NULL))
295 {
296 Status = STATUS_BAD_IMPERSONATION_LEVEL;
297 _SEH2_LEAVE;
298 }
299
300 /* While impersonating the caller, also get its LUID */
301 if (DriveLetter)
302 {
303 Status = GetCallerLuid(&CallerLuid);
304 if (NT_SUCCESS(Status))
305 {
306 HandleSMB = TRUE;
307 }
308 }
309
310 /* Now, open the device */
311 Status = NtOpenSymbolicLinkObject(&LinkHandle,
312 DELETE | SYMBOLIC_LINK_QUERY,
313 &ObjectAttributes);
314
315 /* And get back to our context */
316 CsrRevertToSelf();
317
318 /* In case of LUID broadcast, do nothing but return to trigger broadcast */
319 if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE)
320 {
321 /* Zero handle in case of a failure */
322 if (!NT_SUCCESS(Status))
323 {
324 LinkHandle = 0;
325 }
326
327 /* If removal was asked, and no object found: the remval was successful */
328 if (RemoveDefinition && Status == STATUS_OBJECT_NAME_NOT_FOUND)
329 {
330 Status = STATUS_SUCCESS;
331 }
332
333 /* We're done here, nothing more to do */
334 _SEH2_LEAVE;
335 }
336
337 /* If device was not found */
338 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
339 {
340 /* No handle */
341 LinkHandle = 0;
342
343 /* If we were asked to remove... */
344 if (RemoveDefinition)
345 {
346 /*
347 * If caller asked to pop first entry, nothing specific,
348 * then, we can consider this as a success
349 */
350 if (DefineDosDeviceRequest->TargetPath.Length == 0)
351 {
352 Status = STATUS_SUCCESS;
353 }
354
355 /* We're done, nothing to change */
356 _SEH2_LEAVE;
357 }
358
359 /* There's no target to handle */
360 HandleTarget = FALSE;
361
362 /*
363 * We'll consider, that's a success
364 * Failing to open the device doesn't prevent
365 * from creating it later on to create
366 * the linking.
367 */
368 Status = STATUS_SUCCESS;
369 }
370 else
371 {
372 /* Unexpected failure, forward to caller */
373 if (!NT_SUCCESS(Status))
374 {
375 _SEH2_LEAVE;
376 }
377
378 /* If LUID mapping enabled */
379 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
380 {
381 /* Check if that's global link */
382 Status = IsGlobalSymbolicLink(LinkHandle, &IsGlobal);
383 if (!NT_SUCCESS(Status))
384 {
385 _SEH2_LEAVE;
386 }
387
388 /* If so, change our device name namespace to GLOBAL?? for link creation */
389 if (IsGlobal)
390 {
391 CchLength = _snwprintf(lpBuffer, 0x1000, L"\\GLOBAL??\\%wZ", &DefineDosDeviceRequest->DeviceName);
392 CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
393 CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
394
395 DeviceName.Length = CchLength * sizeof(WCHAR);
396 DeviceName.MaximumLength = CchLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
397 }
398 }
399 }
400
401 /* If caller provided a target */
402 if (DefineDosDeviceRequest->TargetPath.Length != 0)
403 {
404 /* Make sure it's null terminated */
405 DefineDosDeviceRequest->TargetPath.Buffer[DefineDosDeviceRequest->TargetPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
406
407 /* Compute its size */
408 TargetLength = wcslen(DefineDosDeviceRequest->TargetPath.Buffer);
409
410 /* And make sure it fits our buffer */
411 if (TargetLength + 1 >= CchLengthLeft)
412 {
413 Status = STATUS_INVALID_PARAMETER;
414 _SEH2_LEAVE;
415 }
416
417 /* Copy it to our internal buffer */
418 RtlMoveMemory(CurrentBuffer, DefineDosDeviceRequest->TargetPath.Buffer, TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
419 TargetBuffer = CurrentBuffer;
420
421 /* Update our buffer status */
422 CchLengthLeft -= (TargetLength + 1);
423 CurrentBuffer += (TargetLength + 1);
424 }
425 /* Otherwise, zero everything */
426 else
427 {
428 TargetBuffer = NULL;
429 TargetLength = 0;
430 }
431
432 /* If we opened the device, then, handle its current target */
433 if (HandleTarget)
434 {
435 /* Query it with our internal buffer */
436 LinkTarget.Length = 0;
437 LinkTarget.MaximumLength = CchLengthLeft * sizeof(WCHAR);
438 LinkTarget.Buffer = CurrentBuffer;
439
440 Status = NtQuerySymbolicLinkObject(LinkHandle,
441 &LinkTarget,
442 &Length);
443 /* If we overflow, give up */
444 if (Length == LinkTarget.MaximumLength)
445 {
446 Status = STATUS_BUFFER_OVERFLOW;
447 }
448 /* In case of a failure, bye bye */
449 if (!NT_SUCCESS(Status))
450 {
451 _SEH2_LEAVE;
452 }
453
454 /*
455 * Properly null it for MULTI_SZ if needed
456 * Always update max length with
457 * the need size
458 * This is needed to hand relatively "small"
459 * strings to Ob and avoid killing ourselves
460 * on the next query
461 */
462 CchLength = Length / sizeof(WCHAR);
463 if (CchLength < 2 ||
464 CurrentBuffer[CchLength - 2] != UNICODE_NULL ||
465 CurrentBuffer[CchLength - 1] != UNICODE_NULL)
466 {
467 CurrentBuffer[CchLength] = UNICODE_NULL;
468 LinkTarget.MaximumLength = Length + sizeof(UNICODE_NULL);
469 }
470 else
471 {
472 LinkTarget.MaximumLength = Length;
473 }
474 }
475 /* There's no target, and we're asked to remove, so null target */
476 else if (RemoveDefinition)
477 {
478 RtlInitUnicodeString(&LinkTarget, NULL);
479 }
480 /* There's a target provided - new device, update buffer */
481 else
482 {
483 RtlInitUnicodeString(&LinkTarget, &CurrentBuffer[-TargetLength - 1]);
484 }
485
486 /*
487 * We no longer need old symlink, just drop it, we'll recreate it now
488 * with updated target.
489 * The benefit of it is that if caller asked us to drop last target, then
490 * the device is removed and not dangling
491 */
492 if (LinkHandle != 0)
493 {
494 Status = NtMakeTemporaryObject(LinkHandle);
495 NtClose(LinkHandle);
496 LinkHandle = 0;
497 }
498
499 /* At this point, we must have no failure */
500 if (!NT_SUCCESS(Status))
501 {
502 _SEH2_LEAVE;
503 }
504
505 /*
506 * If we have to remove definition, let's start to browse our
507 * target to actually drop it.
508 */
509 if (RemoveDefinition)
510 {
511 /* We'll browse our multi sz string */
512 RemoveFound = FALSE;
513 CurrentPtr = LinkTarget.Buffer;
514 InterPtr = LinkTarget.Buffer;
515 while (*CurrentPtr != UNICODE_NULL)
516 {
517 CchLength = 0;
518 OrigPtr = CurrentPtr;
519 /* First, find next string */
520 while (TRUE)
521 {
522 CurrentChar = *CurrentPtr;
523 ++CurrentPtr;
524
525 if (CurrentChar == UNICODE_NULL)
526 {
527 break;
528 }
529
530 ++CchLength;
531 }
532
533 /* This check is a bit tricky, but dead useful:
534 * If on the previous loop, we found the caller provided target
535 * in our list, then, we'll move current entry over the found one
536 * So that, it gets deleted.
537 * Also, if we don't find caller entry in our entries, then move
538 * current entry in the string if a previous one got deleted
539 */
540 if (RemoveFound ||
541 ((!(DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
542 TargetLength != CchLength || _wcsicmp(OrigPtr, TargetBuffer) != 0) &&
543 ((DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
544 (TargetLength != 0 && _wcsnicmp(OrigPtr, TargetBuffer, TargetLength) != 0))))
545 {
546 if (InterPtr != OrigPtr)
547 {
548 RtlMoveMemory(InterPtr, OrigPtr, sizeof(WCHAR) * CchLength + sizeof(UNICODE_NULL));
549 }
550
551 InterPtr += (CchLength + 1);
552 }
553 else
554 {
555 /* Match case! Remember for next loop turn and to delete it */
556 RemoveFound = TRUE;
557 }
558 }
559
560 /*
561 * Drop last entry, as required (pop)
562 * If there was a match previously, everything
563 * is already moved, so we're just nulling
564 * the end of the string
565 * If there was no match, this is the pop
566 */
567 *InterPtr = UNICODE_NULL;
568 ++InterPtr;
569
570 /* Compute new target length */
571 TargetLength = wcslen(LinkTarget.Buffer) * sizeof(WCHAR);
572 /*
573 * If it's empty, quit
574 * Beware, here, we quit with STATUS_SUCCESS, and that's expected!
575 * In case we dropped last target entry, then, it's empty
576 * and there's no need to recreate the device we deleted previously
577 */
578 if (TargetLength == 0)
579 {
580 _SEH2_LEAVE;
581 }
582
583 /* Update our target string */
584 LinkTarget.Length = TargetLength;
585 LinkTarget.MaximumLength = (ULONG_PTR)InterPtr - (ULONG_PTR)LinkTarget.Buffer;
586 }
587 /* If that's not a removal, just update the target to include new target */
588 else if (HandleTarget)
589 {
590 LinkTarget.Buffer = LinkTarget.Buffer - TargetLength - 1;
591 LinkTarget.Length = TargetLength * sizeof(WCHAR);
592 LinkTarget.MaximumLength += (TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
593 TargetLength *= sizeof(WCHAR);
594 }
595 /* No changes */
596 else
597 {
598 TargetLength = LinkTarget.Length;
599 }
600
601 /* Make sure we don't create empty symlink */
602 if (TargetLength == 0)
603 {
604 _SEH2_LEAVE;
605 }
606
607 /* Initialize our SIDs for symlink ACLs */
608 Status = RtlAllocateAndInitializeSid(&WorldAuthority,
609 1,
610 SECURITY_NULL_RID,
611 SECURITY_NULL_RID,
612 SECURITY_NULL_RID,
613 SECURITY_NULL_RID,
614 SECURITY_NULL_RID,
615 SECURITY_NULL_RID,
616 SECURITY_NULL_RID,
617 SECURITY_NULL_RID,
618 &WorldSid);
619 if (!NT_SUCCESS(Status))
620 {
621 _SEH2_LEAVE;
622 }
623
624 Status = RtlAllocateAndInitializeSid(&SystemAuthority,
625 1,
626 SECURITY_RESTRICTED_CODE_RID,
627 SECURITY_NULL_RID,
628 SECURITY_NULL_RID,
629 SECURITY_NULL_RID,
630 SECURITY_NULL_RID,
631 SECURITY_NULL_RID,
632 SECURITY_NULL_RID,
633 SECURITY_NULL_RID,
634 &SystemSid);
635 if (!NT_SUCCESS(Status))
636 {
637 RtlFreeSid(WorldSid);
638 _SEH2_LEAVE;
639 }
640
641 /* Initialize our SD (on stack) */
642 RtlCreateSecurityDescriptor(&SecurityDescriptor,
643 SECURITY_DESCRIPTOR_REVISION);
644
645 /* And our ACL (still on stack) */
646 RtlCreateAcl(&Dacl.Dacl, sizeof(Dacl), ACL_REVISION);
647
648 /*
649 * For access mask, if we have no session ID, or if
650 * protection mode is disabled, make them wide open
651 */
652 if (SessionId == 0 ||
653 (ProtectionMode & 3) == 0)
654 {
655 AccessMask = DELETE | SYMBOLIC_LINK_QUERY;
656 }
657 else
658 {
659 AccessMask = SYMBOLIC_LINK_QUERY;
660 }
661
662 /* Setup the ACL */
663 RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, WorldSid);
664 RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, SystemSid);
665
666 /* Drop SIDs */
667 RtlFreeSid(WorldSid);
668 RtlFreeSid(SystemSid);
669
670 /* Link DACL to the SD */
671 RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, &Dacl.Dacl, TRUE);
672
673 /* And set it in the OA used for creation */
674 ObjectAttributes.SecurityDescriptor = &SecurityDescriptor;
675
676 /*
677 * If LUID and not global, we need to impersonate the caller
678 * to make it local.
679 */
680 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
681 {
682 if (!IsGlobal)
683 {
684 if (!CsrImpersonateClient(NULL))
685 {
686 Status = STATUS_BAD_IMPERSONATION_LEVEL;
687 _SEH2_LEAVE;
688 }
689 }
690 }
691 /* The object will be permanent */
692 else
693 {
694 ObjectAttributes.Attributes |= OBJ_PERMANENT;
695 }
696
697 /* (Re)Create the symbolic link/device */
698 Status = NtCreateSymbolicLinkObject(&LinkHandle,
699 SYMBOLIC_LINK_ALL_ACCESS,
700 &ObjectAttributes,
701 &LinkTarget);
702
703 /* Revert to self if required */
704 if (BaseStaticServerData->LUIDDeviceMapsEnabled && !IsGlobal)
705 {
706 CsrRevertToSelf();
707 }
708
709 /* In case of a success, make object permanent for LUID links */
710 if (NT_SUCCESS(Status))
711 {
712 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
713 {
714 Status = NtMakePermanentObject(LinkHandle);
715 }
716
717 /* Close the link */
718 NtClose(LinkHandle);
719
720 /*
721 * Specific failure case here:
722 * We were asked to remove something
723 * but we didn't find the something
724 * (we recreated the symlink hence the fail here!)
725 * so fail with appropriate status
726 */
727 if (RemoveDefinition && !RemoveFound)
728 {
729 Status = STATUS_OBJECT_NAME_NOT_FOUND;
730 }
731 }
732
733 /* We closed link, don't double close */
734 LinkHandle = 0;
735 }
736 _SEH2_FINALLY
737 {
738 /* If we need to close the link, do it now */
739 if (LinkHandle != 0)
740 {
741 NtClose(LinkHandle);
742 }
743
744 /* Free our internal buffer */
745 RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
746
747 /* Handle SMB */
748 if (DriveLetter && Status == STATUS_SUCCESS && HandleSMB)
749 {
750 UNIMPLEMENTED;
751 }
752
753 /* Done! */
754 RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
755 }
756 _SEH2_END;
757
758 return Status;
759 }
760
761 /* EOF */