[BASESRV] Misc fixes: use BaseSrvHeap and silent a DPRINT
[reactos.git] / subsystems / win / basesrv / dosdev.c
index 148c2ea..d13edb4 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
-typedef struct _BASE_DOS_DEVICE_HISTORY_ENTRY
-{
-    LIST_ENTRY     Entry;
-    UNICODE_STRING Device;
-    UNICODE_STRING Target;
-} BASE_DOS_DEVICE_HISTORY_ENTRY, *PBASE_DOS_DEVICE_HISTORY_ENTRY;
-
 static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec;
-static LIST_ENTRY DosDeviceHistory;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
 VOID BaseInitDefineDosDevice(VOID)
 {
     RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec);
-    InitializeListHead(&DosDeviceHistory);
 }
 
 VOID BaseCleanupDefineDosDevice(VOID)
 {
-    PLIST_ENTRY Entry, ListHead;
-    PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry;
-
     RtlDeleteCriticalSection(&BaseDefineDosDeviceCritSec);
+}
 
-    ListHead = &DosDeviceHistory;
-    Entry = ListHead->Flink;
-    while (Entry != ListHead)
+NTSTATUS
+GetCallerLuid(PLUID CallerLuid)
+{
+    NTSTATUS Status;
+    HANDLE TokenHandle;
+    ULONG ReturnLength;
+    TOKEN_STATISTICS TokenInformation;
+
+    /* We need an output buffer */
+    if (CallerLuid == NULL)
     {
-        HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
-            CONTAINING_RECORD(Entry,
-                              BASE_DOS_DEVICE_HISTORY_ENTRY,
-                              Entry);
-        Entry = Entry->Flink;
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Open thread token */
+    TokenHandle = 0;
+    ReturnLength = 0;
+    Status = NtOpenThreadToken(NtCurrentThread(),
+                               READ_CONTROL | TOKEN_QUERY,
+                               FALSE, &TokenHandle);
+    /* If we fail, open process token */
+    if (Status == STATUS_NO_TOKEN)
+    {
+        Status = NtOpenProcessToken(NtCurrentProcess(),
+                                    READ_CONTROL | TOKEN_QUERY,
+                                    &TokenHandle);
+    }
 
-        if (HistoryEntry)
+    /* In case of a success get caller LUID and copy it back */
+    if (NT_SUCCESS(Status))
+    {
+        Status = NtQueryInformationToken(TokenHandle,
+                                         TokenStatistics,
+                                         &TokenInformation,
+                                         sizeof(TokenInformation),
+                                         &ReturnLength);
+        if (NT_SUCCESS(Status))
         {
-            if (HistoryEntry->Target.Buffer)
+            RtlCopyLuid(CallerLuid, &TokenInformation.AuthenticationId);
+        }
+    }
+
+    /* Close token handle */
+    if (TokenHandle != 0)
+    {
+        NtClose(TokenHandle);
+    }
+
+    return Status;
+}
+
+NTSTATUS
+IsGlobalSymbolicLink(HANDLE LinkHandle,
+                     PBOOLEAN IsGlobal)
+{
+    NTSTATUS Status;
+    DWORD ReturnLength;
+    UNICODE_STRING GlobalString;
+    OBJECT_NAME_INFORMATION NameInfo, *PNameInfo;
+
+    /* We need both parameters */
+    if (LinkHandle == 0 || IsGlobal == NULL)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    PNameInfo = NULL;
+    _SEH2_TRY
+    {
+        /* Query handle information */
+        Status = NtQueryObject(LinkHandle,
+                               ObjectNameInformation,
+                               &NameInfo,
+                               0,
+                               &ReturnLength);
+        /* Only failure we tolerate is length mismatch */
+        if (NT_SUCCESS(Status) || Status == STATUS_INFO_LENGTH_MISMATCH)
+        {
+            /* Allocate big enough buffer */
+            PNameInfo = RtlAllocateHeap(BaseSrvHeap, 0, ReturnLength);
+            if (PNameInfo == NULL)
             {
-                RtlFreeHeap(BaseSrvHeap,
-                            0,
-                            HistoryEntry->Target.Buffer);
+                Status = STATUS_NO_MEMORY;
+                _SEH2_LEAVE;
             }
-            if (HistoryEntry->Device.Buffer)
+
+            /* Query again handle information */
+            Status = NtQueryObject(LinkHandle,
+                                   ObjectNameInformation,
+                                   PNameInfo,
+                                   ReturnLength,
+                                   &ReturnLength);
+
+            /*
+             * If it succeed, check we have Global??
+             * If so, return success
+             */
+            if (NT_SUCCESS(Status))
             {
-                RtlFreeHeap(BaseSrvHeap,
-                            0,
-                            HistoryEntry->Device.Buffer);
+                RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??");
+                *IsGlobal = RtlPrefixUnicodeString(&GlobalString, &PNameInfo->Name, FALSE);
+                Status = STATUS_SUCCESS;
             }
-            RtlFreeHeap(BaseSrvHeap,
-                        0,
-                        HistoryEntry);
         }
     }
+    _SEH2_FINALLY
+    {
+        if (PNameInfo != NULL)
+        {
+            RtlFreeHeap(BaseSrvHeap, 0, PNameInfo);
+        }
+    }
+    _SEH2_END;
+
+    return Status;
 }
 
 /* PUBLIC SERVER APIS *********************************************************/
@@ -78,478 +153,608 @@ CSR_API(BaseSrvDefineDosDevice)
     NTSTATUS Status;
     PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest;
     OBJECT_ATTRIBUTES ObjectAttributes;
-    HANDLE LinkHandle = NULL;
+    HANDLE LinkHandle;
     UNICODE_STRING DeviceName = {0};
-    UNICODE_STRING RequestDeviceName = {0};
     UNICODE_STRING LinkTarget = {0};
-    PUNICODE_STRING RequestLinkTarget;
     ULONG Length;
     SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
     SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
-    PSECURITY_DESCRIPTOR SecurityDescriptor;
-    PACL Dacl;
-    PSID AdminSid;
     PSID SystemSid;
     PSID WorldSid;
-    ULONG SidLength;
-    PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry;
-    PLIST_ENTRY Entry;
-    PLIST_ENTRY ListHead;
-    BOOLEAN Matched, AddHistory;
-    DWORD dwFlags;
     PWSTR lpBuffer;
+    WCHAR Letter;
+    SHORT AbsLetter;
+    BOOLEAN DriveLetter = FALSE;
+    BOOLEAN RemoveDefinition;
+    BOOLEAN HandleTarget;
+    BOOLEAN HandleSMB = FALSE;
+    BOOLEAN IsGlobal = FALSE;
+    ULONG CchLengthLeft;
+    ULONG CchLength;
+    ULONG TargetLength;
+    PWSTR TargetBuffer;
+    PWSTR CurrentBuffer;
+    /* We store them on the stack, they are known in advance */
+    union {
+        SECURITY_DESCRIPTOR SecurityDescriptor;
+        UCHAR Buffer[20];
+    } SecurityDescriptor;
+    union {
+        ACL Dacl;
+        UCHAR Buffer[256];
+    } Dacl;
+    ACCESS_MASK AccessMask;
+    LUID CallerLuid;
+    WCHAR * CurrentPtr;
+    WCHAR CurrentChar;
+    PWSTR OrigPtr;
+    PWSTR InterPtr;
+    BOOLEAN RemoveFound;
+
+#if 0
+    /* FIXME: Check why it fails.... */
+    if (!CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&DefineDosDeviceRequest->DeviceName,
+                                  DefineDosDeviceRequest->DeviceName.Length,
+                                  1) ||
+        (DefineDosDeviceRequest->DeviceName.Length & 1) != 0 ||
+        !CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&DefineDosDeviceRequest->TargetPath,
+                                  (DefineDosDeviceRequest->TargetPath.Length != 0 ? sizeof(UNICODE_NULL) : 0) + DefineDosDeviceRequest->TargetPath.Length,
+                                  1) ||
+        (DefineDosDeviceRequest->TargetPath.Length & 1) != 0)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+#endif
 
-    DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ, TargetName:%wZ\n",
-           DefineDosDeviceRequest->dwFlags,
+    DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ (%d), TargetPath:%wZ (%d)\n",
+           DefineDosDeviceRequest->Flags,
            &DefineDosDeviceRequest->DeviceName,
-           &DefineDosDeviceRequest->TargetName);
-
-    Matched = AddHistory = FALSE;
-    HistoryEntry = NULL;
-    AdminSid = SystemSid = WorldSid = NULL;
-    SecurityDescriptor = NULL;
-    ListHead = &DosDeviceHistory;
-    dwFlags = DefineDosDeviceRequest->dwFlags;
-
-    /* Validate the flags */
-    if ( (dwFlags & 0xFFFFFFF0) ||
-        ((dwFlags & DDD_EXACT_MATCH_ON_REMOVE) &&
-            !(dwFlags & DDD_REMOVE_DEFINITION)) )
+           DefineDosDeviceRequest->DeviceName.Length,
+           &DefineDosDeviceRequest->TargetPath,
+           DefineDosDeviceRequest->TargetPath.Length);
+
+    /*
+     * Allocate a buffer big enough to contain:
+     * - device name
+     * - targets
+     */
+    lpBuffer = RtlAllocateHeap(BaseSrvHeap, 0, 0x2000);
+    if (lpBuffer == NULL)
     {
-        return STATUS_INVALID_PARAMETER;
+        return STATUS_NO_MEMORY;
     }
 
+    /* Enter our critical section */
     Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n",
                 Status);
+        RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
         return Status;
     }
 
+    LinkHandle = 0;
+    /* Does the caller wants to remove definition? */
+    RemoveDefinition = !!(DefineDosDeviceRequest->Flags & DDD_REMOVE_DEFINITION);
     _SEH2_TRY
     {
-        Status =
-            RtlUpcaseUnicodeString(&RequestDeviceName,
-                                   &DefineDosDeviceRequest->DeviceName,
-                                   TRUE);
-        if (!NT_SUCCESS(Status))
-            _SEH2_LEAVE;
+        /* First of all, check if that's a drive letter device amongst LUID mappings */
+        if (BaseStaticServerData->LUIDDeviceMapsEnabled && !(DefineDosDeviceRequest->Flags & DDD_NO_BROADCAST_SYSTEM))
+        {
+            if (DefineDosDeviceRequest->DeviceName.Buffer != NULL &&
+                DefineDosDeviceRequest->DeviceName.Length == 2 * sizeof(WCHAR) &&
+                DefineDosDeviceRequest->DeviceName.Buffer[1] == L':')
+            {
+                Letter = DefineDosDeviceRequest->DeviceName.Buffer[0];
+
+                /* Handle both lower cases and upper cases */
+                AbsLetter = Letter - L'a';
+                if (AbsLetter < 26 && AbsLetter >= 0)
+                {
+                    Letter = RtlUpcaseUnicodeChar(Letter);
+                }
 
-        RequestLinkTarget = &DefineDosDeviceRequest->TargetName;
-        lpBuffer = (PWSTR)RtlAllocateHeap(BaseSrvHeap,
-                                          HEAP_ZERO_MEMORY,
-                                          RequestDeviceName.MaximumLength + 5 * sizeof(WCHAR));
-        if (!lpBuffer)
+                AbsLetter = Letter - L'A';
+                if (AbsLetter < 26)
+                {
+                    /* That's a letter! */
+                    DriveLetter = TRUE;
+                }
+            }
+        }
+
+        /* We can only broadcast drive letters in case of LUID mappings */
+        if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE &&
+            !DriveLetter)
         {
-            DPRINT1("Failed to allocate memory\n");
-            Status = STATUS_NO_MEMORY;
+            Status = STATUS_INVALID_PARAMETER;
             _SEH2_LEAVE;
         }
 
-        swprintf(lpBuffer,
-                 L"\\??\\%wZ",
-                 &RequestDeviceName);
-        RtlInitUnicodeString(&DeviceName,
-                             lpBuffer);
+        /* First usage of our buffer: create device name */
+        CchLength = _snwprintf(lpBuffer, 0x1000, L"\\??\\%wZ", &DefineDosDeviceRequest->DeviceName);
+        CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
+        CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
+        RtlInitUnicodeString(&DeviceName, lpBuffer);
+
+        /* And prepare to open it */
         InitializeObjectAttributes(&ObjectAttributes,
                                    &DeviceName,
                                    OBJ_CASE_INSENSITIVE,
                                    NULL,
                                    NULL);
+
+        /* Assume it's OK and has a target to deal with */
+        HandleTarget = TRUE;
+
+        /* Move to the client context if the mapping was local */
+        if (!CsrImpersonateClient(NULL))
+        {
+            Status = STATUS_BAD_IMPERSONATION_LEVEL;
+            _SEH2_LEAVE;
+        }
+
+        /* While impersonating the caller, also get its LUID */
+        if (DriveLetter)
+        {
+            Status = GetCallerLuid(&CallerLuid);
+            if (NT_SUCCESS(Status))
+            {
+                HandleSMB = TRUE;
+            }
+        }
+
+        /* Now, open the device */
         Status = NtOpenSymbolicLinkObject(&LinkHandle,
-                                          DELETE | 0x1,
+                                          DELETE | SYMBOLIC_LINK_QUERY,
                                           &ObjectAttributes);
-        if (NT_SUCCESS(Status))
+
+        /* And get back to our context */
+        CsrRevertToSelf();
+
+        /* In case of LUID broadcast, do nothing but return to trigger broadcast */
+        if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE)
         {
-            Status = NtQuerySymbolicLinkObject(LinkHandle,
-                                               &LinkTarget,
-                                               &Length);
-            if (!NT_SUCCESS(Status) &&
-                Status == STATUS_BUFFER_TOO_SMALL)
+            /* Zero handle in case of a failure */
+            if (!NT_SUCCESS(Status))
             {
-                LinkTarget.Length = 0;
-                LinkTarget.MaximumLength = Length;
-                LinkTarget.Buffer = (PWSTR)
-                    RtlAllocateHeap(BaseSrvHeap,
-                                    HEAP_ZERO_MEMORY,
-                                    Length);
-                if (!LinkTarget.Buffer)
+                LinkHandle = 0;
+            }
+
+            /* If removal was asked, and no object found: the remval was successful */
+            if (RemoveDefinition && Status == STATUS_OBJECT_NAME_NOT_FOUND)
+            {
+                Status = STATUS_SUCCESS;
+            }
+
+            /* We're done here, nothing more to do */
+            _SEH2_LEAVE;
+        }
+
+        /* If device was not found */
+        if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+        {
+            /* No handle */
+            LinkHandle = 0;
+
+            /* If we were asked to remove... */
+            if (RemoveDefinition)
+            {
+                /*
+                 * If caller asked to pop first entry, nothing specific,
+                 * then, we can consider this as a success
+                 */
+                if (DefineDosDeviceRequest->TargetPath.Length == 0)
                 {
-                    DPRINT1("Failed to allocate memory\n");
-                    Status = STATUS_NO_MEMORY;
-                    _SEH2_LEAVE;
+                    Status = STATUS_SUCCESS;
                 }
 
-                Status = NtQuerySymbolicLinkObject(LinkHandle,
-                                                   &LinkTarget,
-                                                   &Length);
+                /* We're done, nothing to change */
+                _SEH2_LEAVE;
             }
 
+            /* There's no target to handle */
+            HandleTarget = FALSE;
+
+            /*
+             * We'll consider, that's a success
+             * Failing to open the device doesn't prevent
+             * from creating it later on to create
+             * the linking.
+             */
+            Status = STATUS_SUCCESS;
+        }
+        else
+        {
+            /* Unexpected failure, forward to caller */
             if (!NT_SUCCESS(Status))
             {
-                DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n",
-                     &DeviceName, Status);
                 _SEH2_LEAVE;
             }
 
-            if ((dwFlags & DDD_REMOVE_DEFINITION))
+            /* If LUID mapping enabled */
+            if (BaseStaticServerData->LUIDDeviceMapsEnabled)
             {
-                /* If no target name specified we remove the current symlink target */
-                if (RequestLinkTarget->Length == 0)
-                    Matched = TRUE;
-                else
-                {
-                    if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE)
-                        Matched = !RtlCompareUnicodeString(RequestLinkTarget,
-                                                           &LinkTarget,
-                                                           TRUE);
-                    else
-                        Matched = RtlPrefixUnicodeString(RequestLinkTarget,
-                                                         &LinkTarget,
-                                                         TRUE);
-                }
-
-                if (Matched && IsListEmpty(ListHead))
+                /* Check if that's global link */
+                Status = IsGlobalSymbolicLink(LinkHandle, &IsGlobal);
+                if (!NT_SUCCESS(Status))
                 {
-                    /* Current symlink target macthed and there is nothing to revert to */
-                    RequestLinkTarget = NULL;
+                    _SEH2_LEAVE;
                 }
-                else if (Matched && !IsListEmpty(ListHead))
-                {
-                    /*
-                     * Fetch the first history entry we come across for the device name.
-                     * This will become the current symlink target for the device name.
-                     */
-                    Matched = FALSE;
-                    Entry = ListHead->Flink;
-                    while (Entry != ListHead)
-                    {
-                        HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
-                            CONTAINING_RECORD(Entry,
-                                              BASE_DOS_DEVICE_HISTORY_ENTRY,
-                                              Entry);
-                        Matched =
-                            !RtlCompareUnicodeString(&RequestDeviceName,
-                                                     &HistoryEntry->Device,
-                                                     FALSE);
-                        if (Matched)
-                        {
-                            RemoveEntryList(&HistoryEntry->Entry);
-                            RequestLinkTarget = &HistoryEntry->Target;
-                            break;
-                        }
-                        Entry = Entry->Flink;
-                        HistoryEntry = NULL;
-                    }
 
-                    /* Nothing to revert to so delete the symlink */
-                    if (!Matched)
-                        RequestLinkTarget = NULL;
-                }
-                else if (!Matched)
+                /* If so, change our device name namespace to GLOBAL?? for link creation */
+                if (IsGlobal)
                 {
-                    /*
-                     * Locate a previous symlink target as we did not get
-                     * a hit earlier. If we find one we need to remove it.
-                     */
-                    Entry = ListHead->Flink;
-                    while (Entry != ListHead)
-                    {
-                        HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
-                            CONTAINING_RECORD(Entry,
-                                              BASE_DOS_DEVICE_HISTORY_ENTRY,
-                                              Entry);
-                        Matched =
-                            !RtlCompareUnicodeString(&RequestDeviceName,
-                                                     &HistoryEntry->Device,
-                                                     FALSE);
-                        if (!Matched)
-                        {
-                            HistoryEntry = NULL;
-                            Entry = Entry->Flink;
-                            continue;
-                        }
-
-                        Matched = FALSE;
-                        if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE)
-                        {
-                            if (!RtlCompareUnicodeString(RequestLinkTarget,
-                                                         &HistoryEntry->Target,
-                                                         TRUE))
-                            {
-                                Matched = TRUE;
-                            }
-                        }
-                        else if (RtlPrefixUnicodeString(RequestLinkTarget,
-                                                        &HistoryEntry->Target,
-                                                        TRUE))
-                        {
-                            Matched = TRUE;
-                        }
-
-                        if (Matched)
-                        {
-                            RemoveEntryList(&HistoryEntry->Entry);
-                            break;
-                        }
-                        Entry = Entry->Flink;
-                        HistoryEntry = NULL;
-                    }
+                    CchLength = _snwprintf(lpBuffer, 0x1000, L"\\GLOBAL??\\%wZ", &DefineDosDeviceRequest->DeviceName);
+                    CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
+                    CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
 
-                    /* Leave existing symlink as is */
-                    if (!Matched)
-                        Status = STATUS_OBJECT_NAME_NOT_FOUND;
-                    else
-                        Status = STATUS_SUCCESS;
-                    _SEH2_LEAVE;
+                    DeviceName.Length = CchLength * sizeof(WCHAR);
+                    DeviceName.MaximumLength = CchLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
                 }
             }
-            else
+        }
+
+        /* If caller provided a target */
+        if (DefineDosDeviceRequest->TargetPath.Length != 0)
+        {
+            /* Make sure it's null terminated */
+            DefineDosDeviceRequest->TargetPath.Buffer[DefineDosDeviceRequest->TargetPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            /* Compute its size */
+            TargetLength = wcslen(DefineDosDeviceRequest->TargetPath.Buffer);
+
+            /* And make sure it fits our buffer */
+            if (TargetLength + 1 >= CchLengthLeft)
             {
-                AddHistory = TRUE;
+                Status = STATUS_INVALID_PARAMETER;
+                _SEH2_LEAVE;
             }
 
-            Status = NtMakeTemporaryObject(LinkHandle);
+            /* Copy it to our internal buffer */
+            RtlMoveMemory(CurrentBuffer, DefineDosDeviceRequest->TargetPath.Buffer, TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+            TargetBuffer = CurrentBuffer;
+
+            /* Update our buffer status */
+            CchLengthLeft -= (TargetLength + 1);
+            CurrentBuffer += (TargetLength + 1);
+        }
+        /* Otherwise, zero everything */
+        else
+        {
+            TargetBuffer = NULL;
+            TargetLength = 0;
+        }
+
+        /* If we opened the device, then, handle its current target */
+        if (HandleTarget)
+        {
+            /* Query it with our internal buffer */
+            LinkTarget.Length = 0;
+            LinkTarget.MaximumLength = CchLengthLeft * sizeof(WCHAR);
+            LinkTarget.Buffer = CurrentBuffer;
+
+            Status = NtQuerySymbolicLinkObject(LinkHandle,
+                                               &LinkTarget,
+                                               &Length);
+            /* If we overflow, give up */
+            if (Length == LinkTarget.MaximumLength)
+            {
+                Status = STATUS_BUFFER_OVERFLOW;
+            }
+            /* In case of a failure, bye bye */
             if (!NT_SUCCESS(Status))
             {
-                DPRINT1("NtMakeTemporaryObject(%wZ) failed (Status %lx)\n",
-                     &DeviceName, Status);
                 _SEH2_LEAVE;
             }
 
-            Status = NtClose(LinkHandle);
-            LinkHandle = NULL;
-            if (!NT_SUCCESS(Status))
+            /*
+             * Properly null it for MULTI_SZ if needed
+             * Always update max length with
+             * the need size
+             * This is needed to hand relatively "small"
+             * strings to Ob and avoid killing ourselves
+             * on the next query
+             */
+            CchLength = Length / sizeof(WCHAR);
+            if (CchLength < 2 ||
+                CurrentBuffer[CchLength - 2] != UNICODE_NULL ||
+                CurrentBuffer[CchLength - 1] != UNICODE_NULL)
             {
-                DPRINT1("NtClose(%wZ) failed (Status %lx)\n",
-                     &DeviceName, Status);
-                _SEH2_LEAVE;
+                CurrentBuffer[CchLength] = UNICODE_NULL;
+                LinkTarget.MaximumLength = Length + sizeof(UNICODE_NULL);
+            }
+            else
+            {
+                LinkTarget.MaximumLength = Length;
             }
         }
+        /* There's no target, and we're asked to remove, so null target */
+        else if (RemoveDefinition)
+        {
+            RtlInitUnicodeString(&LinkTarget, NULL);
+        }
+        /* There's a target provided - new device, update buffer */
+        else
+        {
+            RtlInitUnicodeString(&LinkTarget, &CurrentBuffer[-TargetLength - 1]);
+        }
+
+        /*
+         * We no longer need old symlink, just drop it, we'll recreate it now
+         * with updated target.
+         * The benefit of it is that if caller asked us to drop last target, then
+         * the device is removed and not dangling
+         */
+        if (LinkHandle != 0)
+        {
+            Status = NtMakeTemporaryObject(LinkHandle);
+            NtClose(LinkHandle);
+            LinkHandle = 0;
+        }
 
-        /* Don't create symlink if we don't have a target */
-        if (!RequestLinkTarget || RequestLinkTarget->Length == 0)
+        /* At this point, we must have no failure */
+        if (!NT_SUCCESS(Status))
+        {
             _SEH2_LEAVE;
+        }
 
-        if (AddHistory)
+        /*
+         * If we have to remove definition, let's start to browse our
+         * target to actually drop it.
+         */
+        if (RemoveDefinition)
         {
-            HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
-                RtlAllocateHeap(BaseSrvHeap,
-                                HEAP_ZERO_MEMORY,
-                                sizeof(BASE_DOS_DEVICE_HISTORY_ENTRY));
-            if (!HistoryEntry)
+            /* We'll browse our multi sz string */
+            RemoveFound = FALSE;
+            CurrentPtr = LinkTarget.Buffer;
+            InterPtr = LinkTarget.Buffer;
+            while (*CurrentPtr != UNICODE_NULL)
             {
-                DPRINT1("Failed to allocate memory\n");
-                Status = STATUS_NO_MEMORY;
-                _SEH2_LEAVE;
-            }
+                CchLength = 0;
+                OrigPtr = CurrentPtr;
+                /* First, find next string */
+                while (TRUE)
+                {
+                    CurrentChar = *CurrentPtr;
+                    ++CurrentPtr;
 
-            HistoryEntry->Target.Buffer =
-                RtlAllocateHeap(BaseSrvHeap,
-                                HEAP_ZERO_MEMORY,
-                                LinkTarget.Length);
-            if (!HistoryEntry->Target.Buffer)
-            {
-                DPRINT1("Failed to allocate memory\n");
-                Status = STATUS_NO_MEMORY;
-                _SEH2_LEAVE;
+                    if (CurrentChar == UNICODE_NULL)
+                    {
+                        break;
+                    }
+
+                    ++CchLength;
+                }
+
+                /* This check is a bit tricky, but dead useful:
+                 * If on the previous loop, we found the caller provided target
+                 * in our list, then, we'll move current entry over the found one
+                 * So that, it gets deleted.
+                 * Also, if we don't find caller entry in our entries, then move
+                 * current entry in the string if a previous one got deleted
+                 */
+                if (RemoveFound ||
+                    ((!(DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
+                      TargetLength != CchLength || _wcsicmp(OrigPtr, TargetBuffer) != 0) &&
+                     ((DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
+                      (TargetLength != 0 && _wcsnicmp(OrigPtr, TargetBuffer, TargetLength) != 0))))
+                {
+                    if (InterPtr != OrigPtr)
+                    {
+                        RtlMoveMemory(InterPtr, OrigPtr, sizeof(WCHAR) * CchLength + sizeof(UNICODE_NULL));
+                    }
+
+                    InterPtr += (CchLength + 1);
+                }
+                else
+                {
+                    /* Match case! Remember for next loop turn and to delete it */
+                    RemoveFound = TRUE;
+                }
             }
-            HistoryEntry->Target.Length =
-                HistoryEntry->Target.MaximumLength =
-                    LinkTarget.Length;
-            RtlCopyUnicodeString(&HistoryEntry->Target,
-                                 &LinkTarget);
-
-            HistoryEntry->Device.Buffer =
-                RtlAllocateHeap(BaseSrvHeap,
-                                HEAP_ZERO_MEMORY,
-                                RequestDeviceName.Length);
-            if (!HistoryEntry->Device.Buffer)
+
+            /*
+             * Drop last entry, as required (pop)
+             * If there was a match previously, everything
+             * is already moved, so we're just nulling
+             * the end of the string
+             * If there was no match, this is the pop
+             */
+            *InterPtr = UNICODE_NULL;
+            ++InterPtr;
+
+            /* Compute new target length */
+            TargetLength = wcslen(LinkTarget.Buffer) * sizeof(WCHAR);
+            /*
+             * If it's empty, quit
+             * Beware, here, we quit with STATUS_SUCCESS, and that's expected!
+             * In case we dropped last target entry, then, it's empty
+             * and there's no need to recreate the device we deleted previously
+             */
+            if (TargetLength == 0)
             {
-                DPRINT1("Failed to allocate memory\n");
-                Status = STATUS_NO_MEMORY;
                 _SEH2_LEAVE;
             }
-            HistoryEntry->Device.Length =
-                HistoryEntry->Device.MaximumLength =
-                    RequestDeviceName.Length;
-            RtlCopyUnicodeString(&HistoryEntry->Device,
-                                 &RequestDeviceName);
-
-            /* Remember previous symlink target for this device */
-            InsertHeadList(ListHead,
-                           &HistoryEntry->Entry);
-            HistoryEntry = NULL;
-        }
 
-        RtlAllocateAndInitializeSid(&WorldAuthority,
-                                    1,
-                                    SECURITY_WORLD_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    &WorldSid);
-
-        RtlAllocateAndInitializeSid(&SystemAuthority,
-                                    1,
-                                    SECURITY_LOCAL_SYSTEM_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    &SystemSid);
-
-        RtlAllocateAndInitializeSid(&SystemAuthority,
-                                    2,
-                                    SECURITY_BUILTIN_DOMAIN_RID,
-                                    DOMAIN_ALIAS_RID_ADMINS,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    SECURITY_NULL_RID,
-                                    &AdminSid);
-
-        SidLength = RtlLengthSid(SystemSid) +
-                    RtlLengthSid(AdminSid) +
-                    RtlLengthSid(WorldSid);
-        Length = sizeof(ACL) + SidLength + 3 * sizeof(ACCESS_ALLOWED_ACE);
-
-        SecurityDescriptor = RtlAllocateHeap(BaseSrvHeap,
-                                             0,
-                                             SECURITY_DESCRIPTOR_MIN_LENGTH + Length);
-        if (!SecurityDescriptor)
+            /* Update our target string */
+            LinkTarget.Length = TargetLength;
+            LinkTarget.MaximumLength = (ULONG_PTR)InterPtr - (ULONG_PTR)LinkTarget.Buffer;
+        }
+        /* If that's not a removal, just update the target to include new target */
+        else if (HandleTarget)
         {
-            DPRINT1("Failed to allocate memory\n");
-            Status = STATUS_NO_MEMORY;
-            _SEH2_LEAVE;
+            LinkTarget.Buffer = LinkTarget.Buffer - TargetLength - 1;
+            LinkTarget.Length = TargetLength * sizeof(WCHAR);
+            LinkTarget.MaximumLength += (TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+            TargetLength *= sizeof(WCHAR);
+        }
+        /* No changes */
+        else
+        {
+            TargetLength = LinkTarget.Length;
         }
 
-        Dacl = (PACL)((ULONG_PTR)SecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
-        Status = RtlCreateSecurityDescriptor(SecurityDescriptor,
-                                             SECURITY_DESCRIPTOR_REVISION);
-        if (!NT_SUCCESS(Status))
+        /* Make sure we don't create empty symlink */
+        if (TargetLength == 0)
         {
-            DPRINT1("RtlCreateSecurityDescriptor() failed (Status %lx)\n", Status);
             _SEH2_LEAVE;
         }
 
-        Status = RtlCreateAcl(Dacl,
-                              Length,
-                              ACL_REVISION);
+        /* Initialize our SIDs for symlink ACLs */
+        Status = RtlAllocateAndInitializeSid(&WorldAuthority,
+                                             1,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             &WorldSid);
         if (!NT_SUCCESS(Status))
         {
-            DPRINT1("RtlCreateAcl() failed (Status %lx)\n", Status);
             _SEH2_LEAVE;
         }
 
-        RtlAddAccessAllowedAce(Dacl,
-                               ACL_REVISION,
-                               GENERIC_ALL,
-                               SystemSid);
-        RtlAddAccessAllowedAce(Dacl,
-                               ACL_REVISION,
-                               GENERIC_ALL,
-                               AdminSid);
-        RtlAddAccessAllowedAce(Dacl,
-                               ACL_REVISION,
-                               STANDARD_RIGHTS_READ,
-                               WorldSid);
-
-        Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor,
-                                              TRUE,
-                                              Dacl,
-                                              FALSE);
+        Status = RtlAllocateAndInitializeSid(&SystemAuthority,
+                                             1,
+                                             SECURITY_RESTRICTED_CODE_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             SECURITY_NULL_RID,
+                                             &SystemSid);
         if (!NT_SUCCESS(Status))
         {
-            DPRINT1("RtlSetDaclSecurityDescriptor() failed (Status %lx)\n", Status);
+            RtlFreeSid(WorldSid);
             _SEH2_LEAVE;
         }
 
-        InitializeObjectAttributes(&ObjectAttributes,
-                                   &DeviceName,
-                                   OBJ_CASE_INSENSITIVE,
-                                   NULL,
-                                   SecurityDescriptor);
-        Status = NtCreateSymbolicLinkObject(&LinkHandle,
-                                            SYMBOLIC_LINK_ALL_ACCESS,
-                                            &ObjectAttributes,
-                                            RequestLinkTarget);
-        if (NT_SUCCESS(Status))
+        /* Initialize our SD (on stack) */
+        RtlCreateSecurityDescriptor(&SecurityDescriptor,
+                                    SECURITY_DESCRIPTOR_REVISION);
+
+        /* And our ACL (still on stack) */
+        RtlCreateAcl(&Dacl.Dacl, sizeof(Dacl), ACL_REVISION);
+
+        /*
+         * For access mask, if we have no session ID, or if
+         * protection mode is disabled, make them wide open
+         */
+        if (SessionId == 0 ||
+            (ProtectionMode & 3) == 0)
         {
-            Status = NtMakePermanentObject(LinkHandle);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("NtMakePermanentObject(%wZ) failed (Status %lx)\n",
-                        &DeviceName, Status);
-            }
+            AccessMask = DELETE | SYMBOLIC_LINK_QUERY;
         }
         else
         {
-            DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n",
-                 &DeviceName, Status);
-        }
-    }
-    _SEH2_FINALLY
-    {
-        RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
-        if (DeviceName.Buffer)
-        {
-            RtlFreeHeap(BaseSrvHeap,
-                        0,
-                        DeviceName.Buffer);
+            AccessMask = SYMBOLIC_LINK_QUERY;
         }
-        if (LinkTarget.Buffer)
+
+        /* Setup the ACL */
+        RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, WorldSid);
+        RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, SystemSid);
+
+        /* Drop SIDs */
+        RtlFreeSid(WorldSid);
+        RtlFreeSid(SystemSid);
+
+        /* Link DACL to the SD */
+        RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, &Dacl.Dacl, TRUE);
+
+        /* And set it in the OA used for creation */
+        ObjectAttributes.SecurityDescriptor = &SecurityDescriptor;
+
+        /*
+         * If LUID and not global, we need to impersonate the caller
+         * to make it local.
+         */
+        if (BaseStaticServerData->LUIDDeviceMapsEnabled)
         {
-            RtlFreeHeap(BaseSrvHeap,
-                        0,
-                        LinkTarget.Buffer);
+            if (!IsGlobal)
+            {
+                if (!CsrImpersonateClient(NULL))
+                {
+                    Status = STATUS_BAD_IMPERSONATION_LEVEL;
+                    _SEH2_LEAVE;
+                }
+            }
         }
-        if (SecurityDescriptor)
+        /* The object will be permanent */
+        else
         {
-            RtlFreeHeap(BaseSrvHeap,
-                        0,
-                        SecurityDescriptor);
+            ObjectAttributes.Attributes |= OBJ_PERMANENT;
         }
 
-        if (LinkHandle) NtClose(LinkHandle);
-        if (SystemSid)  RtlFreeSid(SystemSid);
-        if (AdminSid)   RtlFreeSid(AdminSid);
-        if (WorldSid)   RtlFreeSid(WorldSid);
+        /* (Re)Create the symbolic link/device */
+        Status = NtCreateSymbolicLinkObject(&LinkHandle,
+                                            SYMBOLIC_LINK_ALL_ACCESS,
+                                            &ObjectAttributes,
+                                            &LinkTarget);
 
-        RtlFreeUnicodeString(&RequestDeviceName);
+        /* Revert to self if required */
+        if (BaseStaticServerData->LUIDDeviceMapsEnabled && !IsGlobal)
+        {
+            CsrRevertToSelf();
+        }
 
-        if (HistoryEntry)
+        /* In case of a success, make object permanent for LUID links */
+        if (NT_SUCCESS(Status))
         {
-            if (HistoryEntry->Target.Buffer)
+            if (BaseStaticServerData->LUIDDeviceMapsEnabled)
             {
-                RtlFreeHeap(BaseSrvHeap,
-                            0,
-                            HistoryEntry->Target.Buffer);
+                Status = NtMakePermanentObject(LinkHandle);
             }
-            if (HistoryEntry->Device.Buffer)
+
+            /* Close the link */
+            NtClose(LinkHandle);
+
+            /*
+             * Specific failure case here:
+             * We were asked to remove something
+             * but we didn't find the something
+             * (we recreated the symlink hence the fail here!)
+             * so fail with appropriate status
+             */
+            if (RemoveDefinition && !RemoveFound)
             {
-                RtlFreeHeap(BaseSrvHeap,
-                            0,
-                            HistoryEntry->Device.Buffer);
+                Status = STATUS_OBJECT_NAME_NOT_FOUND;
             }
-            RtlFreeHeap(BaseSrvHeap,
-                        0,
-                        HistoryEntry);
         }
+
+        /* We closed link, don't double close */
+        LinkHandle = 0;
+    }
+    _SEH2_FINALLY
+    {
+        /* If we need to close the link, do it now */
+        if (LinkHandle != 0)
+        {
+             NtClose(LinkHandle);
+        }
+
+        /* Free our internal buffer */
+        RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
+
+        /* Handle SMB */
+        if (DriveLetter && Status == STATUS_SUCCESS && HandleSMB)
+        {
+            UNIMPLEMENTED;
+        }
+
+        /* Done! */
+        RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
     }
-    _SEH2_END
+    _SEH2_END;
 
-    DPRINT("BaseSrvDefineDosDevice exit, Status: 0x%x\n", Status);
     return Status;
 }