[USERENV] Use a reference counter when loading and unloading profiles. Unload the...
[reactos.git] / dll / win32 / userenv / profile.c
index ac41f12..1c18a67 100644 (file)
@@ -1,24 +1,7 @@
-/*
- *  ReactOS kernel
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
- * FILE:            lib/userenv/profile.c
+ * FILE:            dll/win32/userenv/profile.c
  * PURPOSE:         User profile code
  * PROGRAMMERS:     Eric Kohl
  *                  HervĂ© Poussineau
 
 #include "precomp.h"
 
+#include <sddl.h>
+
 #define NDEBUG
 #include <debug.h>
 
-
 /* FUNCTIONS ***************************************************************/
 
 BOOL
@@ -43,7 +27,7 @@ AppendSystemPostfix(LPWSTR lpName,
     /* Build profile name postfix */
     if (!ExpandEnvironmentStringsW(L"%SystemRoot%",
                                    szSystemRoot,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szSystemRoot)))
     {
         DPRINT1("Error: %lu\n", GetLastError());
         return FALSE;
@@ -58,7 +42,7 @@ AppendSystemPostfix(LPWSTR lpName,
     while (*lpszPtr != (WCHAR)0)
     {
         if (*lpszPtr == L'\\')
-            *lpszPtr = '_';
+            *lpszPtr = L'_';
         lpszPtr++;
     }
 
@@ -122,24 +106,424 @@ AcquireRemoveRestorePrivilege(IN BOOL bAcquire)
 }
 
 
+static
+BOOL
+CheckForLoadedProfile(HANDLE hToken)
+{
+    UNICODE_STRING SidString;
+    HKEY hKey;
+
+    DPRINT("CheckForLoadedProfile() called\n");
+
+    /* Get the user SID string */
+    if (!GetUserSidStringFromToken(hToken, &SidString))
+    {
+        DPRINT1("GetUserSidStringFromToken() failed\n");
+        return FALSE;
+    }
+
+    if (RegOpenKeyExW(HKEY_USERS,
+                      SidString.Buffer,
+                      0,
+                      MAXIMUM_ALLOWED,
+                      &hKey))
+    {
+        DPRINT("Profile not loaded\n");
+        RtlFreeUnicodeString(&SidString);
+        return FALSE;
+    }
+
+    RegCloseKey(hKey);
+
+    RtlFreeUnicodeString(&SidString);
+
+    DPRINT("Profile already loaded\n");
+
+    return TRUE;
+}
+
+
+static
+HANDLE
+CreateProfileMutex(
+    _In_ PWSTR pszSidString)
+{
+    SECURITY_DESCRIPTOR SecurityDescriptor;
+    SECURITY_ATTRIBUTES SecurityAttributes;
+    PWSTR pszMutexName = NULL;
+    HANDLE hMutex = NULL;
+
+    pszMutexName = HeapAlloc(GetProcessHeap(),
+                             0,
+                             (wcslen(L"Global\\userenv:  User Profile Mutex for ") + wcslen(pszSidString) + 1) * sizeof(WCHAR));
+    if (pszMutexName == NULL)
+    {
+        DPRINT("Failed to allocate the mutex name buffer!\n");
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
+    }
+
+    /* Build the profile mutex name */
+    wcscpy(pszMutexName, L"Global\\userenv:  User Profile Mutex for ");
+    wcscat(pszMutexName, pszSidString);
+
+    /* Initialize the security descriptor */
+    InitializeSecurityDescriptor(&SecurityDescriptor,
+                                 SECURITY_DESCRIPTOR_REVISION);
+
+    /* Set a NULL-DACL (everyone has access) */
+    SetSecurityDescriptorDacl(&SecurityDescriptor,
+                              TRUE,
+                              NULL,
+                              FALSE);
+
+    /* Initialize the security attributes */
+    SecurityAttributes.nLength = sizeof(SecurityAttributes);
+    SecurityAttributes.lpSecurityDescriptor = &SecurityDescriptor;
+    SecurityAttributes.bInheritHandle = FALSE;
+
+    /* Create the profile mutex */
+    hMutex = CreateMutexW(&SecurityAttributes,
+                          FALSE,
+                          pszMutexName);
+    if (hMutex == NULL)
+    {
+        DPRINT1("Failed to create the profile mutex (Error %lu)\n", GetLastError());
+    }
+
+    HeapFree(GetProcessHeap(), 0, pszMutexName);
+
+    return hMutex;
+}
+
+
+static
+DWORD
+IncrementRefCount(
+    PWSTR pszSidString,
+    PDWORD pdwRefCount)
+{
+    HKEY hProfilesKey = NULL, hProfileKey = NULL;
+    DWORD dwRefCount = 0, dwLength, dwType;
+    DWORD dwError;
+
+    DPRINT1("IncrementRefCount(%S %p)\n",
+            pszSidString, pdwRefCount);
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
+                            0,
+                            KEY_QUERY_VALUE,
+                            &hProfilesKey);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    dwError = RegOpenKeyExW(hProfilesKey,
+                            pszSidString,
+                            0,
+                            KEY_QUERY_VALUE | KEY_SET_VALUE,
+                            &hProfileKey);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    /* Get the reference counter */
+    dwLength = sizeof(dwRefCount);
+    RegQueryValueExW(hProfileKey,
+                     L"RefCount",
+                     NULL,
+                     &dwType,
+                     (PBYTE)&dwRefCount,
+                     &dwLength);
+
+    dwRefCount++;
+
+    dwLength = sizeof(dwRefCount);
+    dwError = RegSetValueExW(hProfileKey,
+                             L"RefCount",
+                             0,
+                             REG_DWORD,
+                             (PBYTE)&dwRefCount,
+                             dwLength);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    if (pdwRefCount != NULL)
+        *pdwRefCount = dwRefCount;
+
+done:
+    if (hProfileKey != NULL)
+        RegCloseKey(hProfileKey);
+
+    if (hProfilesKey != NULL)
+        RegCloseKey(hProfilesKey);
+
+    return dwError;
+}
+
+
+static
+DWORD
+DecrementRefCount(
+    PWSTR pszSidString,
+    PDWORD pdwRefCount)
+{
+    HKEY hProfilesKey = NULL, hProfileKey = NULL;
+    DWORD dwRefCount = 0, dwLength, dwType;
+    DWORD dwError;
+
+    DPRINT1("DecrementRefCount(%S %p)\n",
+            pszSidString, pdwRefCount);
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
+                            0,
+                            KEY_QUERY_VALUE,
+                            &hProfilesKey);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    dwError = RegOpenKeyExW(hProfilesKey,
+                            pszSidString,
+                            0,
+                            KEY_QUERY_VALUE | KEY_SET_VALUE,
+                            &hProfileKey);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    /* Get the reference counter */
+    dwLength = sizeof(dwRefCount);
+    dwError = RegQueryValueExW(hProfileKey,
+                               L"RefCount",
+                               NULL,
+                               &dwType,
+                               (PBYTE)&dwRefCount,
+                               &dwLength);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    dwRefCount--;
+
+    dwLength = sizeof(dwRefCount);
+    dwError = RegSetValueExW(hProfileKey,
+                             L"RefCount",
+                             0,
+                             REG_DWORD,
+                             (PBYTE)&dwRefCount,
+                             dwLength);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", dwError);
+        goto done;
+    }
+
+    if (pdwRefCount != NULL)
+        *pdwRefCount = dwRefCount;
+
+done:
+    if (hProfileKey != NULL)
+        RegCloseKey(hProfileKey);
+
+    if (hProfilesKey != NULL)
+        RegCloseKey(hProfilesKey);
+
+    return dwError;
+}
+
+
+/* PUBLIC FUNCTIONS ********************************************************/
+
 BOOL
 WINAPI
-CreateUserProfileA(PSID Sid,
-                   LPCSTR lpUserName)
+CopySystemProfile(
+    _In_ ULONG Unused)
 {
-    UNICODE_STRING UserName;
+    WCHAR szKeyName[MAX_PATH];
+    WCHAR szRawProfilePath[MAX_PATH];
+    WCHAR szProfilePath[MAX_PATH];
+    WCHAR szDefaultProfilePath[MAX_PATH];
+    UNICODE_STRING SidString = {0, 0, NULL};
+    HANDLE hToken = NULL;
+    PSID pUserSid = NULL;
+    HKEY hProfileKey = NULL;
+    DWORD dwDisposition;
+    BOOL bResult = FALSE;
+    DWORD cchSize;
+    DWORD dwError;
+
+    DPRINT1("CopySystemProfile()\n");
+
+    if (!OpenProcessToken(GetCurrentProcess(),
+                          TOKEN_QUERY | TOKEN_IMPERSONATE,
+                          &hToken))
+    {
+        DPRINT1("Failed to open the process token (Error %lu)\n", GetLastError());
+        return FALSE;
+    }
+
+    pUserSid = GetUserSid(hToken);
+    if (pUserSid == NULL)
+    {
+        DPRINT1("Failed to get the users SID (Error %lu)\n", GetLastError());
+        goto done;
+    }
+
+    /* Get the user SID string */
+    if (!GetUserSidStringFromToken(hToken, &SidString))
+    {
+        DPRINT1("GetUserSidStringFromToken() failed\n");
+        goto done;
+    }
+
+    StringCbCopyW(szKeyName, sizeof(szKeyName),
+                  L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
+    StringCbCatW(szKeyName, sizeof(szKeyName), SidString.Buffer);
+
+    RtlFreeUnicodeString(&SidString);
+
+    dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+                              szKeyName,
+                              0, NULL, 0,
+                              KEY_WRITE,
+                              NULL,
+                              &hProfileKey,
+                              &dwDisposition);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Failed to create the profile key for the %s profile (Error %lu)\n",
+                SidString.Buffer, dwError);
+        goto done;
+    }
+
+    dwError = RegSetValueExW(hProfileKey,
+                             L"Sid",
+                             0,
+                             REG_BINARY,
+                             (PBYTE)pUserSid,
+                             RtlLengthSid(pUserSid));
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Failed to set the SID value (Error %lu)\n", dwError);
+        goto done;
+    }
+
+    wcscpy(szRawProfilePath,
+           L"%systemroot%\\system32\\config\\systemprofile");
+
+    dwError = RegSetValueExW(hProfileKey,
+                             L"ProfileImagePath",
+                             0,
+                             REG_EXPAND_SZ,
+                             (PBYTE)szRawProfilePath,
+                             (wcslen(szRawProfilePath) + 1) * sizeof(WCHAR));
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("Failed to set the ProfileImagePath value (Error %lu)\n", dwError);
+        goto done;
+    }
+
+    /* Expand the raw profile path */
+    if (!ExpandEnvironmentStringsW(szRawProfilePath,
+                                   szProfilePath,
+                                   ARRAYSIZE(szProfilePath)))
+    {
+        DPRINT1("Failled to expand the raw profile path (Error %lu)\n", GetLastError());
+        goto done;
+    }
+
+    /* Create the profile directory if it does not exist yet */
+    // FIXME: Security!
+    if (!CreateDirectoryW(szProfilePath, NULL))
+    {
+        if (GetLastError() != ERROR_ALREADY_EXISTS)
+        {
+            DPRINT1("Failed to create the profile directory (Error %lu)\n", GetLastError());
+            goto done;
+        }
+    }
+
+    /* Get the path of the default profile */
+    cchSize = ARRAYSIZE(szDefaultProfilePath);
+    if (!GetDefaultUserProfileDirectoryW(szDefaultProfilePath, &cchSize))
+    {
+        DPRINT1("Failed to create the default profile path (Error %lu)\n", GetLastError());
+        goto done;
+    }
+
+    /* Copy the default profile into the new profile directory */
+    // FIXME: Security!
+    if (!CopyDirectory(szProfilePath, szDefaultProfilePath))
+    {
+        DPRINT1("Failed to copy the default profile directory (Error %lu)\n", GetLastError());
+        goto done;
+    }
+
+    bResult = TRUE;
+
+done:
+    if (hProfileKey != NULL)
+        RegCloseKey(hProfileKey);
+
+    RtlFreeUnicodeString(&SidString);
+
+    if (pUserSid != NULL)
+        LocalFree(pUserSid);
+
+    if (hToken != NULL)
+        CloseHandle(hToken);
+
+    return bResult;
+}
+
+
+BOOL
+WINAPI
+CreateUserProfileA(
+    _In_ PSID pSid,
+    _In_ LPCSTR lpUserName)
+{
+    LPWSTR pUserNameW = NULL;
+    INT nLength;
     BOOL bResult;
 
-    if (!RtlCreateUnicodeStringFromAsciiz(&UserName,
-                                          (LPSTR)lpUserName))
+    DPRINT("CreateUserProfileA(%p %s)\n", pSid, lpUserName);
+
+    /* Convert lpUserName to Unicode */
+    nLength = MultiByteToWideChar(CP_ACP, 0, lpUserName, -1, NULL, 0);
+    pUserNameW = HeapAlloc(GetProcessHeap(), 0, nLength * sizeof(WCHAR));
+    if (pUserNameW == NULL)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
+    MultiByteToWideChar(CP_ACP, 0, lpUserName, -1, pUserNameW, nLength);
 
-    bResult = CreateUserProfileW(Sid, UserName.Buffer);
+    /* Call the Ex function */
+    bResult = CreateUserProfileExW(pSid,
+                                   pUserNameW,
+                                   NULL,
+                                   NULL,
+                                   0,
+                                   FALSE);
 
-    RtlFreeUnicodeString(&UserName);
+    HeapFree(GetProcessHeap(), 0, pUserNameW);
 
     return bResult;
 }
@@ -147,8 +531,120 @@ CreateUserProfileA(PSID Sid,
 
 BOOL
 WINAPI
-CreateUserProfileW(PSID Sid,
-                   LPCWSTR lpUserName)
+CreateUserProfileW(
+    _In_ PSID pSid,
+    _In_ LPCWSTR lpUserName)
+{
+    DPRINT("CreateUserProfileW(%p %S)\n", pSid, lpUserName);
+
+    /* Call the Ex function */
+    return CreateUserProfileExW(pSid,
+                                lpUserName,
+                                NULL,
+                                NULL,
+                                0,
+                                FALSE);
+}
+
+
+BOOL
+WINAPI
+CreateUserProfileExA(
+    _In_ PSID pSid,
+    _In_ LPCSTR lpUserName,
+    _In_opt_ LPCSTR lpUserHive,
+    _Out_opt_ LPSTR lpProfileDir,
+    _In_ DWORD dwDirSize,
+    _In_ BOOL bWin9xUpg)
+{
+    LPWSTR pUserNameW = NULL;
+    LPWSTR pUserHiveW = NULL;
+    LPWSTR pProfileDirW = NULL;
+    INT nLength;
+    BOOL bResult = FALSE;
+
+    DPRINT("CreateUserProfileExA(%p %s %s %p %lu %d)\n",
+           pSid, lpUserName, lpUserHive, lpProfileDir, dwDirSize, bWin9xUpg);
+
+    /* Check the parameters */
+    if (lpProfileDir != NULL && dwDirSize == 0)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    /* Convert lpUserName to Unicode */
+    nLength = MultiByteToWideChar(CP_ACP, 0, lpUserName, -1, NULL, 0);
+    pUserNameW = HeapAlloc(GetProcessHeap(), 0, nLength * sizeof(WCHAR));
+    if (pUserNameW == NULL)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        goto done;
+    }
+    MultiByteToWideChar(CP_ACP, 0, lpUserName, -1, pUserNameW, nLength);
+
+    /* Convert lpUserHive to Unicode */
+    if (lpUserHive != NULL)
+    {
+        nLength = MultiByteToWideChar(CP_ACP, 0, lpUserHive, -1, NULL, 0);
+        pUserHiveW = HeapAlloc(GetProcessHeap(), 0, nLength * sizeof(WCHAR));
+        if (pUserHiveW == NULL)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto done;
+        }
+        MultiByteToWideChar(CP_ACP, 0, lpUserHive, -1, pUserHiveW, nLength);
+    }
+
+    /* Allocate a Unicode buffer for lpProfileDir */
+    if (lpProfileDir != NULL)
+    {
+        pProfileDirW = HeapAlloc(GetProcessHeap(), 0, dwDirSize * sizeof(WCHAR));
+        if (pProfileDirW == NULL)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto done;
+        }
+    }
+
+    /* Call the Unicode function */
+    bResult = CreateUserProfileExW(pSid,
+                                   (LPCWSTR)pUserNameW,
+                                   (LPCWSTR)pUserHiveW,
+                                   pProfileDirW,
+                                   dwDirSize,
+                                   bWin9xUpg);
+
+    /* Convert the profile path to ANSI */
+    if (bResult && lpProfileDir != NULL)
+    {
+        WideCharToMultiByte(CP_ACP, 0, pProfileDirW, -1, lpProfileDir, dwDirSize, NULL, NULL);
+    }
+
+done:
+    /* Free the buffers */
+    if (pProfileDirW != NULL)
+        HeapFree(GetProcessHeap(), 0, pProfileDirW);
+
+    if (pUserHiveW != NULL)
+        HeapFree(GetProcessHeap(), 0, pUserHiveW);
+
+    if (pUserNameW != NULL)
+        HeapFree(GetProcessHeap(), 0, pUserNameW);
+
+    return bResult;
+}
+
+
+BOOL
+WINAPI
+CreateUserProfileExW(
+    _In_ PSID pSid,
+    _In_ LPCWSTR lpUserName,
+    _In_opt_ LPCWSTR lpUserHive,
+    _Out_opt_ LPWSTR lpProfileDir,
+    _In_ DWORD dwDirSize,
+    _In_ BOOL bWin9xUpg)
 {
     WCHAR szRawProfilesPath[MAX_PATH];
     WCHAR szProfilesPath[MAX_PATH];
@@ -157,14 +653,28 @@ CreateUserProfileW(PSID Sid,
     WCHAR szUserProfileName[MAX_PATH];
     WCHAR szBuffer[MAX_PATH];
     LPWSTR SidString;
-    DWORD dwLength;
+    DWORD dwType, dwLength;
     DWORD dwDisposition;
     UINT i;
     HKEY hKey;
     BOOL bRet = TRUE;
     LONG Error;
 
-    DPRINT("CreateUserProfileW() called\n");
+    DPRINT("CreateUserProfileExW(%p %S %S %p %lu %d)\n",
+           pSid, lpUserName, lpUserHive, lpProfileDir, dwDirSize, bWin9xUpg);
+
+    /* Parameters validation */
+    if (!pSid || !lpUserName)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    /*
+     * TODO:
+     *  - Add support for lpUserHive.
+     *  - bWin9xUpg is obsolete. Don't waste your time implementing this.
+     */
 
     Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
@@ -179,14 +689,14 @@ CreateUserProfileW(PSID Sid,
     }
 
     /* Get profiles path */
-    dwLength = MAX_PATH * sizeof(WCHAR);
+    dwLength = sizeof(szRawProfilesPath);
     Error = RegQueryValueExW(hKey,
                              L"ProfilesDirectory",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szRawProfilesPath,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -197,14 +707,15 @@ CreateUserProfileW(PSID Sid,
     /* Expand it */
     if (!ExpandEnvironmentStringsW(szRawProfilesPath,
                                    szProfilesPath,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szProfilesPath)))
     {
         DPRINT1("Error: %lu\n", GetLastError());
         RegCloseKey(hKey);
         return FALSE;
     }
 
-    /* create the profiles directory if it does not yet exist */
+    /* Create the profiles directory if it does not exist yet */
+    // FIXME: Security!
     if (!CreateDirectoryW(szProfilesPath, NULL))
     {
         if (GetLastError() != ERROR_ALREADY_EXISTS)
@@ -215,14 +726,14 @@ CreateUserProfileW(PSID Sid,
     }
 
     /* Get default user path */
-    dwLength = MAX_PATH * sizeof(WCHAR);
+    dwLength = sizeof(szBuffer);
     Error = RegQueryValueExW(hKey,
                              L"DefaultUserProfile",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -230,19 +741,17 @@ CreateUserProfileW(PSID Sid,
         return FALSE;
     }
 
-    RegCloseKey (hKey);
+    RegCloseKey(hKey);
 
-    wcscpy(szUserProfileName, lpUserName);
+    StringCbCopyW(szUserProfileName, sizeof(szUserProfileName), lpUserName);
 
-    wcscpy(szUserProfilePath, szProfilesPath);
-    wcscat(szUserProfilePath, L"\\");
-    wcscat(szUserProfilePath, szUserProfileName);
+    /* Create user profile directory */
 
-    wcscpy(szDefaultUserPath, szProfilesPath);
-    wcscat(szDefaultUserPath, L"\\");
-    wcscat(szDefaultUserPath, szBuffer);
+    StringCbCopyW(szUserProfilePath, sizeof(szUserProfilePath), szProfilesPath);
+    StringCbCatW(szUserProfilePath, sizeof(szUserProfilePath), L"\\");
+    StringCbCatW(szUserProfilePath, sizeof(szUserProfilePath), szUserProfileName);
 
-    /* Create user profile directory */
+    // FIXME: Security!
     if (!CreateDirectoryW(szUserProfilePath, NULL))
     {
         if (GetLastError() != ERROR_ALREADY_EXISTS)
@@ -255,10 +764,11 @@ CreateUserProfileW(PSID Sid,
         {
             swprintf(szUserProfileName, L"%s.%03u", lpUserName, i);
 
-            wcscpy(szUserProfilePath, szProfilesPath);
-            wcscat(szUserProfilePath, L"\\");
-            wcscat(szUserProfilePath, szUserProfileName);
+            StringCbCopyW(szUserProfilePath, sizeof(szUserProfilePath), szProfilesPath);
+            StringCbCatW(szUserProfilePath, sizeof(szUserProfilePath), L"\\");
+            StringCbCatW(szUserProfilePath, sizeof(szUserProfilePath), szUserProfileName);
 
+            // FIXME: Security!
             if (CreateDirectoryW(szUserProfilePath, NULL))
                 break;
 
@@ -271,6 +781,12 @@ CreateUserProfileW(PSID Sid,
     }
 
     /* Copy default user directory */
+
+    StringCbCopyW(szDefaultUserPath, sizeof(szDefaultUserPath), szProfilesPath);
+    StringCbCatW(szDefaultUserPath, sizeof(szDefaultUserPath), L"\\");
+    StringCbCatW(szDefaultUserPath, sizeof(szDefaultUserPath), szBuffer);
+
+    // FIXME: Security!
     if (!CopyDirectory(szUserProfilePath, szDefaultUserPath))
     {
         DPRINT1("Error: %lu\n", GetLastError());
@@ -278,16 +794,16 @@ CreateUserProfileW(PSID Sid,
     }
 
     /* Add profile to profile list */
-    if (!ConvertSidToStringSidW(Sid,
+    if (!ConvertSidToStringSidW(pSid,
                                 &SidString))
     {
         DPRINT1("Error: %lu\n", GetLastError());
         return FALSE;
     }
 
-    wcscpy(szBuffer,
-           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
-    wcscat(szBuffer, SidString);
+    StringCbCopyW(szBuffer, sizeof(szBuffer),
+                  L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
+    StringCbCatW(szBuffer, sizeof(szBuffer), SidString);
 
     /* Create user profile key */
     Error = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
@@ -303,13 +819,13 @@ CreateUserProfileW(PSID Sid,
     {
         DPRINT1("Error: %lu\n", Error);
         bRet = FALSE;
-        goto Done;
+        goto done;
     }
 
     /* Create non-expanded user profile path */
-    wcscpy(szBuffer, szRawProfilesPath);
-    wcscat(szBuffer, L"\\");
-    wcscat(szBuffer, szUserProfileName);
+    StringCbCopyW(szBuffer, sizeof(szBuffer), szRawProfilesPath);
+    StringCbCatW(szBuffer, sizeof(szBuffer), L"\\");
+    StringCbCatW(szBuffer, sizeof(szBuffer), szUserProfileName);
 
     /* Set 'ProfileImagePath' value (non-expanded) */
     Error = RegSetValueExW(hKey,
@@ -317,13 +833,13 @@ CreateUserProfileW(PSID Sid,
                            0,
                            REG_EXPAND_SZ,
                            (LPBYTE)szBuffer,
-                           (wcslen (szBuffer) + 1) * sizeof(WCHAR));
+                           (wcslen(szBuffer) + 1) * sizeof(WCHAR));
     if (Error != ERROR_SUCCESS)
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
         bRet = FALSE;
-        goto Done;
+        goto done;
     }
 
     /* Set 'Sid' value */
@@ -331,21 +847,23 @@ CreateUserProfileW(PSID Sid,
                            L"Sid",
                            0,
                            REG_BINARY,
-                           Sid,
-                           GetLengthSid(Sid));
+                           pSid,
+                           GetLengthSid(pSid));
     if (Error != ERROR_SUCCESS)
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
         bRet = FALSE;
-        goto Done;
+        goto done;
     }
 
     RegCloseKey(hKey);
 
-    /* Create user hive name */
-    wcscpy(szBuffer, szUserProfilePath);
-    wcscat(szBuffer, L"\\ntuser.dat");
+    /* Create user hive file */
+
+    /* Use the default hive file name */
+    StringCbCopyW(szBuffer, sizeof(szBuffer), szUserProfilePath);
+    StringCbCatW(szBuffer, sizeof(szBuffer), L"\\ntuser.dat");
 
     /* Acquire restore privilege */
     if (!AcquireRemoveRestorePrivilege(TRUE))
@@ -353,10 +871,10 @@ CreateUserProfileW(PSID Sid,
         Error = GetLastError();
         DPRINT1("Error: %lu\n", Error);
         bRet = FALSE;
-        goto Done;
+        goto done;
     }
 
-    /* Create new user hive */
+    /* Load the user hive */
     Error = RegLoadKeyW(HKEY_USERS,
                         SidString,
                         szBuffer);
@@ -365,7 +883,7 @@ CreateUserProfileW(PSID Sid,
     {
         DPRINT1("Error: %lu\n", Error);
         bRet = FALSE;
-        goto Done;
+        goto done;
     }
 
     /* Initialize user hive */
@@ -381,11 +899,18 @@ CreateUserProfileW(PSID Sid,
     RegUnLoadKeyW(HKEY_USERS, SidString);
     AcquireRemoveRestorePrivilege(FALSE);
 
-Done:
+    /*
+     * If the caller wants to retrieve the user profile path,
+     * give it now. 'dwDirSize' is the number of characters.
+     */
+    if (lpProfileDir && dwDirSize)
+        StringCchCopyW(lpProfileDir, dwDirSize, szUserProfilePath);
+
+done:
     LocalFree((HLOCAL)SidString);
     SetLastError((DWORD)Error);
 
-    DPRINT("CreateUserProfileW() done\n");
+    DPRINT("CreateUserProfileExW() done\n");
 
     return bRet;
 }
@@ -393,40 +918,75 @@ Done:
 
 BOOL
 WINAPI
-CreateUserProfileExA(IN PSID pSid,
-                     IN LPCSTR lpUserName,
-                     IN LPCSTR lpUserHive OPTIONAL,
-                     OUT LPSTR lpProfileDir OPTIONAL,
-                     IN DWORD dwDirSize,
-                     IN BOOL bWin9xUpg)
+DeleteProfileA(
+    _In_ LPCSTR lpSidString,
+    _In_opt_ LPCSTR lpProfilePath,
+    _In_opt_ LPCSTR lpComputerName)
 {
-    DPRINT1("CreateUserProfileExA() not implemented!\n");
-    return FALSE;
+    BOOL bResult;
+    UNICODE_STRING SidString, ProfilePath, ComputerName;
+
+    DPRINT("DeleteProfileA() called\n");
+
+    /* Conversion to UNICODE */
+    if (lpSidString)
+        RtlCreateUnicodeStringFromAsciiz(&SidString,
+                                         (LPSTR)lpSidString);
+
+    if (lpProfilePath)
+        RtlCreateUnicodeStringFromAsciiz(&ProfilePath,
+                                         (LPSTR)lpProfilePath);
+
+    if (lpComputerName)
+        RtlCreateUnicodeStringFromAsciiz(&ComputerName,
+                                         (LPSTR)lpComputerName);
+
+    /* Call the UNICODE function */
+    bResult = DeleteProfileW(SidString.Buffer,
+                             ProfilePath.Buffer,
+                             ComputerName.Buffer);
+
+    /* Memory cleanup */
+    if (lpSidString)
+        RtlFreeUnicodeString(&SidString);
+
+    if (lpProfilePath)
+        RtlFreeUnicodeString(&ProfilePath);
+
+    if (lpComputerName)
+        RtlFreeUnicodeString(&ComputerName);
+
+    return bResult;
 }
 
 
 BOOL
 WINAPI
-CreateUserProfileExW(IN PSID pSid,
-                     IN LPCWSTR lpUserName,
-                     IN LPCWSTR lpUserHive OPTIONAL,
-                     OUT LPWSTR lpProfileDir OPTIONAL,
-                     IN DWORD dwDirSize,
-                     IN BOOL bWin9xUpg)
+DeleteProfileW(
+    _In_ LPCWSTR lpSidString,
+    _In_opt_ LPCWSTR lpProfilePath,
+    _In_opt_ LPCWSTR lpComputerName)
 {
-    DPRINT1("CreateUserProfileExW() not implemented!\n");
+    DPRINT1("DeleteProfileW() not implemented!\n");
     return FALSE;
 }
 
 
 BOOL
 WINAPI
-GetAllUsersProfileDirectoryA(LPSTR lpProfileDir,
-                             LPDWORD lpcchSize)
+GetAllUsersProfileDirectoryA(
+    _Out_opt_ LPSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     LPWSTR lpBuffer;
     BOOL bResult;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     lpBuffer = GlobalAlloc(GMEM_FIXED,
                            *lpcchSize * sizeof(WCHAR));
     if (lpBuffer == NULL)
@@ -434,16 +994,16 @@ GetAllUsersProfileDirectoryA(LPSTR lpProfileDir,
 
     bResult = GetAllUsersProfileDirectoryW(lpBuffer,
                                            lpcchSize);
-    if (bResult)
+    if (bResult && lpProfileDir)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpBuffer,
-                            -1,
-                            lpProfileDir,
-                            *lpcchSize,
-                            NULL,
-                            NULL);
+        bResult = WideCharToMultiByte(CP_ACP,
+                                      0,
+                                      lpBuffer,
+                                      -1,
+                                      lpProfileDir,
+                                      *lpcchSize,
+                                      NULL,
+                                      NULL);
     }
 
     GlobalFree(lpBuffer);
@@ -454,15 +1014,22 @@ GetAllUsersProfileDirectoryA(LPSTR lpProfileDir,
 
 BOOL
 WINAPI
-GetAllUsersProfileDirectoryW(LPWSTR lpProfileDir,
-                             LPDWORD lpcchSize)
+GetAllUsersProfileDirectoryW(
+    _Out_opt_ LPWSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     WCHAR szProfilePath[MAX_PATH];
     WCHAR szBuffer[MAX_PATH];
-    DWORD dwLength;
+    DWORD dwType, dwLength;
     HKEY hKey;
     LONG Error;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
                           0,
@@ -480,10 +1047,10 @@ GetAllUsersProfileDirectoryW(LPWSTR lpProfileDir,
     Error = RegQueryValueExW(hKey,
                              L"ProfilesDirectory",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -494,10 +1061,10 @@ GetAllUsersProfileDirectoryW(LPWSTR lpProfileDir,
     /* Expand it */
     if (!ExpandEnvironmentStringsW(szBuffer,
                                    szProfilePath,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szProfilePath)))
     {
         DPRINT1("Error: %lu\n", GetLastError());
-        RegCloseKey (hKey);
+        RegCloseKey(hKey);
         return FALSE;
     }
 
@@ -506,10 +1073,10 @@ GetAllUsersProfileDirectoryW(LPWSTR lpProfileDir,
     Error = RegQueryValueExW(hKey,
                              L"AllUsersProfile",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -517,38 +1084,42 @@ GetAllUsersProfileDirectoryW(LPWSTR lpProfileDir,
         return FALSE;
     }
 
-    RegCloseKey (hKey);
+    RegCloseKey(hKey);
 
-    wcscat(szProfilePath, L"\\");
-    wcscat(szProfilePath, szBuffer);
+    StringCbCatW(szProfilePath, sizeof(szProfilePath), L"\\");
+    StringCbCatW(szProfilePath, sizeof(szProfilePath), szBuffer);
 
     dwLength = wcslen(szProfilePath) + 1;
-    if (lpProfileDir != NULL)
+    if (lpProfileDir && (*lpcchSize >= dwLength))
     {
-        if (*lpcchSize < dwLength)
-        {
-            *lpcchSize = dwLength;
-            SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            return FALSE;
-        }
-
-        wcscpy(lpProfileDir, szProfilePath);
+        StringCchCopyW(lpProfileDir, *lpcchSize, szProfilePath);
+        *lpcchSize = dwLength;
+        return TRUE;
+    }
+    else // if (!lpProfileDir || (*lpcchSize < dwLength))
+    {
+        *lpcchSize = dwLength;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
     }
-
-    *lpcchSize = dwLength;
-
-    return TRUE;
 }
 
 
 BOOL
 WINAPI
-GetDefaultUserProfileDirectoryA(LPSTR lpProfileDir,
-                                LPDWORD lpcchSize)
+GetDefaultUserProfileDirectoryA(
+    _Out_opt_ LPSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     LPWSTR lpBuffer;
     BOOL bResult;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     lpBuffer = GlobalAlloc(GMEM_FIXED,
                            *lpcchSize * sizeof(WCHAR));
     if (lpBuffer == NULL)
@@ -556,16 +1127,16 @@ GetDefaultUserProfileDirectoryA(LPSTR lpProfileDir,
 
     bResult = GetDefaultUserProfileDirectoryW(lpBuffer,
                                               lpcchSize);
-    if (bResult)
+    if (bResult && lpProfileDir)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpBuffer,
-                            -1,
-                            lpProfileDir,
-                            *lpcchSize,
-                            NULL,
-                            NULL);
+        bResult = WideCharToMultiByte(CP_ACP,
+                                      0,
+                                      lpBuffer,
+                                      -1,
+                                      lpProfileDir,
+                                      *lpcchSize,
+                                      NULL,
+                                      NULL);
     }
 
     GlobalFree(lpBuffer);
@@ -576,15 +1147,22 @@ GetDefaultUserProfileDirectoryA(LPSTR lpProfileDir,
 
 BOOL
 WINAPI
-GetDefaultUserProfileDirectoryW(LPWSTR lpProfileDir,
-                                LPDWORD lpcchSize)
+GetDefaultUserProfileDirectoryW(
+    _Out_opt_ LPWSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     WCHAR szProfilePath[MAX_PATH];
     WCHAR szBuffer[MAX_PATH];
-    DWORD dwLength;
+    DWORD dwType, dwLength;
     HKEY hKey;
     LONG Error;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
                           0,
@@ -602,10 +1180,10 @@ GetDefaultUserProfileDirectoryW(LPWSTR lpProfileDir,
     Error = RegQueryValueExW(hKey,
                              L"ProfilesDirectory",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -616,7 +1194,7 @@ GetDefaultUserProfileDirectoryW(LPWSTR lpProfileDir,
     /* Expand it */
     if (!ExpandEnvironmentStringsW(szBuffer,
                                    szProfilePath,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szProfilePath)))
     {
         DPRINT1("Error: %lu\n", GetLastError());
         RegCloseKey(hKey);
@@ -628,10 +1206,10 @@ GetDefaultUserProfileDirectoryW(LPWSTR lpProfileDir,
     Error = RegQueryValueExW(hKey,
                              L"DefaultUserProfile",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -639,38 +1217,42 @@ GetDefaultUserProfileDirectoryW(LPWSTR lpProfileDir,
         return FALSE;
     }
 
-    RegCloseKey(hKey);
-
-    wcscat(szProfilePath, L"\\");
-    wcscat(szProfilePath, szBuffer);
-
-    dwLength = wcslen(szProfilePath) + 1;
-    if (lpProfileDir != NULL)
-    {
-        if (*lpcchSize < dwLength)
-        {
-            *lpcchSize = dwLength;
-            SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            return FALSE;
-        }
-
-        wcscpy(lpProfileDir, szProfilePath);
-    }
-
-    *lpcchSize = dwLength;
+    RegCloseKey(hKey);
 
-    return TRUE;
+    StringCbCatW(szProfilePath, sizeof(szProfilePath), L"\\");
+    StringCbCatW(szProfilePath, sizeof(szProfilePath), szBuffer);
+
+    dwLength = wcslen(szProfilePath) + 1;
+    if (lpProfileDir && (*lpcchSize >= dwLength))
+    {
+        StringCchCopyW(lpProfileDir, *lpcchSize, szProfilePath);
+        *lpcchSize = dwLength;
+        return TRUE;
+    }
+    else // if (!lpProfileDir || (*lpcchSize < dwLength))
+    {
+        *lpcchSize = dwLength;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
 }
 
 
 BOOL
 WINAPI
-GetProfilesDirectoryA(LPSTR lpProfileDir,
-                      LPDWORD lpcchSize)
+GetProfilesDirectoryA(
+    _Out_ LPSTR lpProfileDir, // _Out_opt_
+    _Inout_ LPDWORD lpcchSize)
 {
     LPWSTR lpBuffer;
     BOOL bResult;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     lpBuffer = GlobalAlloc(GMEM_FIXED,
                            *lpcchSize * sizeof(WCHAR));
     if (lpBuffer == NULL)
@@ -678,16 +1260,16 @@ GetProfilesDirectoryA(LPSTR lpProfileDir,
 
     bResult = GetProfilesDirectoryW(lpBuffer,
                                     lpcchSize);
-    if (bResult)
+    if (bResult && lpProfileDir)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpBuffer,
-                            -1,
-                            lpProfileDir,
-                            *lpcchSize,
-                            NULL,
-                            NULL);
+        bResult = WideCharToMultiByte(CP_ACP,
+                                      0,
+                                      lpBuffer,
+                                      -1,
+                                      lpProfileDir,
+                                      *lpcchSize,
+                                      NULL,
+                                      NULL);
     }
 
     GlobalFree(lpBuffer);
@@ -698,15 +1280,22 @@ GetProfilesDirectoryA(LPSTR lpProfileDir,
 
 BOOL
 WINAPI
-GetProfilesDirectoryW(LPWSTR lpProfilesDir,
-                      LPDWORD lpcchSize)
+GetProfilesDirectoryW(
+    _Out_ LPWSTR lpProfilesDir, // _Out_opt_
+    _Inout_ LPDWORD lpcchSize)
 {
     WCHAR szProfilesPath[MAX_PATH];
     WCHAR szBuffer[MAX_PATH];
-    DWORD dwLength;
+    DWORD dwType, dwLength;
     HKEY hKey;
     LONG Error;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
                           0,
@@ -724,10 +1313,10 @@ GetProfilesDirectoryW(LPWSTR lpProfilesDir,
     Error = RegQueryValueExW(hKey,
                              L"ProfilesDirectory",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szBuffer,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -740,40 +1329,54 @@ GetProfilesDirectoryW(LPWSTR lpProfilesDir,
     /* Expand it */
     if (!ExpandEnvironmentStringsW(szBuffer,
                                    szProfilesPath,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szProfilesPath)))
     {
         DPRINT1("Error: %lu\n", GetLastError());
         return FALSE;
     }
 
-    dwLength = wcslen (szProfilesPath) + 1;
-    if (lpProfilesDir != NULL)
+    dwLength = wcslen(szProfilesPath) + 1;
+    if (lpProfilesDir && (*lpcchSize >= dwLength))
     {
-        if (*lpcchSize < dwLength)
-        {
-            *lpcchSize = dwLength;
-            SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            return FALSE;
-        }
-
-        wcscpy(lpProfilesDir, szProfilesPath);
+        StringCchCopyW(lpProfilesDir, *lpcchSize, szProfilesPath);
+        *lpcchSize = dwLength;
+        return TRUE;
+    }
+    else // if (!lpProfilesDir || (*lpcchSize < dwLength))
+    {
+        *lpcchSize = dwLength;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
     }
+}
 
-    *lpcchSize = dwLength;
 
-    return TRUE;
+BOOL
+WINAPI
+GetProfileType(
+    _Out_ PDWORD pdwFlags)
+{
+    DPRINT1("GetProfileType() not implemented!\n");
+    return FALSE;
 }
 
 
 BOOL
 WINAPI
-GetUserProfileDirectoryA(HANDLE hToken,
-                         LPSTR lpProfileDir,
-                         LPDWORD lpcchSize)
+GetUserProfileDirectoryA(
+    _In_ HANDLE hToken,
+    _Out_opt_ LPSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     LPWSTR lpBuffer;
     BOOL bResult;
 
+    if (!lpcchSize)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     lpBuffer = GlobalAlloc(GMEM_FIXED,
                            *lpcchSize * sizeof(WCHAR));
     if (lpBuffer == NULL)
@@ -782,16 +1385,16 @@ GetUserProfileDirectoryA(HANDLE hToken,
     bResult = GetUserProfileDirectoryW(hToken,
                                        lpBuffer,
                                        lpcchSize);
-    if (bResult)
+    if (bResult && lpProfileDir)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpBuffer,
-                            -1,
-                            lpProfileDir,
-                            *lpcchSize,
-                            NULL,
-                            NULL);
+        bResult = WideCharToMultiByte(CP_ACP,
+                                      0,
+                                      lpBuffer,
+                                      -1,
+                                      lpProfileDir,
+                                      *lpcchSize,
+                                      NULL,
+                                      NULL);
     }
 
     GlobalFree(lpBuffer);
@@ -802,31 +1405,43 @@ GetUserProfileDirectoryA(HANDLE hToken,
 
 BOOL
 WINAPI
-GetUserProfileDirectoryW(HANDLE hToken,
-                         LPWSTR lpProfileDir,
-                         LPDWORD lpcchSize)
+GetUserProfileDirectoryW(
+    _In_ HANDLE hToken,
+    _Out_opt_ LPWSTR lpProfileDir,
+    _Inout_ LPDWORD lpcchSize)
 {
     UNICODE_STRING SidString;
     WCHAR szKeyName[MAX_PATH];
     WCHAR szRawImagePath[MAX_PATH];
     WCHAR szImagePath[MAX_PATH];
-    DWORD dwLength;
+    DWORD dwType, dwLength;
     HKEY hKey;
     LONG Error;
 
-    if (!GetUserSidFromToken(hToken,
-                             &SidString))
+    if (!hToken)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (!lpcchSize)
     {
-        DPRINT1("GetUserSidFromToken() failed\n");
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    /* Get the user SID string */
+    if (!GetUserSidStringFromToken(hToken, &SidString))
+    {
+        DPRINT1("GetUserSidStringFromToken() failed\n");
         return FALSE;
     }
 
     DPRINT("SidString: '%wZ'\n", &SidString);
 
-    wcscpy(szKeyName,
-           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
-    wcscat(szKeyName,
-           SidString.Buffer);
+    StringCbCopyW(szKeyName, sizeof(szKeyName),
+                  L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
+    StringCbCatW(szKeyName, sizeof(szKeyName), SidString.Buffer);
 
     RtlFreeUnicodeString(&SidString);
 
@@ -848,10 +1463,10 @@ GetUserProfileDirectoryW(HANDLE hToken,
     Error = RegQueryValueExW(hKey,
                              L"ProfileImagePath",
                              NULL,
-                             NULL,
+                             &dwType,
                              (LPBYTE)szRawImagePath,
                              &dwLength);
-    if (Error != ERROR_SUCCESS)
+    if ((Error != ERROR_SUCCESS) || (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
     {
         DPRINT1("Error: %lu\n", Error);
         RegCloseKey(hKey);
@@ -866,70 +1481,35 @@ GetUserProfileDirectoryW(HANDLE hToken,
     /* Expand it */
     if (!ExpandEnvironmentStringsW(szRawImagePath,
                                    szImagePath,
-                                   MAX_PATH))
+                                   ARRAYSIZE(szImagePath)))
     {
-        DPRINT1 ("Error: %lu\n", GetLastError());
+        DPRINT1("Error: %lu\n", GetLastError());
         return FALSE;
     }
 
     DPRINT("ImagePath: '%S'\n", szImagePath);
 
-    dwLength = wcslen (szImagePath) + 1;
-    if (*lpcchSize < dwLength)
+    dwLength = wcslen(szImagePath) + 1;
+    if (lpProfileDir && (*lpcchSize >= dwLength))
     {
+        StringCchCopyW(lpProfileDir, *lpcchSize, szImagePath);
         *lpcchSize = dwLength;
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return FALSE;
-    }
-
-    *lpcchSize = dwLength;
-    wcscpy(lpProfileDir, szImagePath);
-
-    return TRUE;
-}
-
-
-static
-BOOL
-CheckForLoadedProfile(HANDLE hToken)
-{
-    UNICODE_STRING SidString;
-    HKEY hKey;
-
-    DPRINT("CheckForLoadedProfile() called\n");
-
-    if (!GetUserSidFromToken(hToken,
-                             &SidString))
-    {
-        DPRINT1("GetUserSidFromToken() failed\n");
-        return FALSE;
+        return TRUE;
     }
-
-    if (RegOpenKeyExW(HKEY_USERS,
-                      SidString.Buffer,
-                      0,
-                      MAXIMUM_ALLOWED,
-                      &hKey))
+    else // if (!lpProfileDir || (*lpcchSize < dwLength))
     {
-        DPRINT("Profile not loaded\n");
-        RtlFreeUnicodeString(&SidString);
+        *lpcchSize = dwLength;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
         return FALSE;
     }
-
-    RegCloseKey(hKey);
-
-    RtlFreeUnicodeString(&SidString);
-
-    DPRINT("Profile already loaded\n");
-
-    return TRUE;
 }
 
 
 BOOL
 WINAPI
-LoadUserProfileA(IN HANDLE hToken,
-                 IN OUT LPPROFILEINFOA lpProfileInfo)
+LoadUserProfileA(
+    _In_ HANDLE hToken,
+    _Inout_ LPPROFILEINFOA lpProfileInfo)
 {
     BOOL bResult = FALSE;
     PROFILEINFOW ProfileInfoW = {0};
@@ -946,7 +1526,7 @@ LoadUserProfileA(IN HANDLE hToken,
     }
 
     /* Convert the structure to UNICODE... */
-    ProfileInfoW.dwSize = sizeof(PROFILEINFOW);
+    ProfileInfoW.dwSize = sizeof(ProfileInfoW);
     ProfileInfoW.dwFlags = lpProfileInfo->dwFlags;
 
     if (lpProfileInfo->lpUserName)
@@ -1038,20 +1618,19 @@ cleanup:
 
 BOOL
 WINAPI
-LoadUserProfileW(IN HANDLE hToken,
-                 IN OUT LPPROFILEINFOW lpProfileInfo)
+LoadUserProfileW(
+    _In_ HANDLE hToken,
+    _Inout_ LPPROFILEINFOW lpProfileInfo)
 {
     WCHAR szUserHivePath[MAX_PATH];
-    LPWSTR UserName = NULL, Domain = NULL;
-    DWORD UserNameLength = 0, DomainLength = 0;
     PTOKEN_USER UserSid = NULL;
-    SID_NAME_USE AccountType;
     UNICODE_STRING SidString = { 0, 0, NULL };
+    HANDLE hProfileMutex = NULL;
     LONG Error;
     BOOL ret = FALSE;
     DWORD dwLength = sizeof(szUserHivePath) / sizeof(szUserHivePath[0]);
 
-    DPRINT("LoadUserProfileW() called\n");
+    DPRINT("LoadUserProfileW(%p %p)\n", hToken, lpProfileInfo);
 
     /* Check profile info */
     if (!lpProfileInfo || (lpProfileInfo->dwSize != sizeof(PROFILEINFOW)) ||
@@ -1061,136 +1640,115 @@ LoadUserProfileW(IN HANDLE hToken,
         return FALSE;
     }
 
-    /* Don't load a profile twice */
-    if (CheckForLoadedProfile(hToken))
-    {
-        DPRINT ("Profile already loaded\n");
-        lpProfileInfo->hProfile = NULL;
-        return TRUE;
-    }
+    DPRINT("UserName: %S\n", lpProfileInfo->lpUserName);
 
-    if (lpProfileInfo->lpProfilePath)
+    /* Get the user SID string */
+    ret = GetUserSidStringFromToken(hToken, &SidString);
+    if (!ret)
     {
-        wcscpy(szUserHivePath, lpProfileInfo->lpProfilePath);
+        DPRINT1("GetUserSidStringFromToken() failed\n");
+        goto cleanup;
     }
-    else
+    ret = FALSE;
+
+    /* Create the profile mutex */
+    hProfileMutex = CreateProfileMutex(SidString.Buffer);
+    if (hProfileMutex == NULL)
     {
-        /* FIXME: check if MS Windows allows lpProfileInfo->lpProfilePath to be NULL */
-        if (!GetProfilesDirectoryW(szUserHivePath, &dwLength))
-        {
-            DPRINT1("GetProfilesDirectoryW() failed (error %ld)\n", GetLastError());
-            return FALSE;
-        }
+        DPRINT1("Failed to create the profile mutex\n");
+        goto cleanup;
     }
 
-    /* Create user hive name */
-    wcscat(szUserHivePath, L"\\");
-    wcscat(szUserHivePath, lpProfileInfo->lpUserName);
-    wcscat(szUserHivePath, L"\\ntuser.dat");
-    DPRINT("szUserHivePath: %S\n", szUserHivePath);
+    /* Wait for the profile mutex */
+    WaitForSingleObject(hProfileMutex, INFINITE);
 
-    /* Create user profile directory if needed */
-    if (GetFileAttributesW(szUserHivePath) == INVALID_FILE_ATTRIBUTES)
+    /* Don't load a profile twice */
+    if (CheckForLoadedProfile(hToken))
     {
-        /* Get user sid */
-        if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) ||
-            GetLastError() != ERROR_INSUFFICIENT_BUFFER)
-        {
-            DPRINT1 ("GetTokenInformation() failed\n");
-            return FALSE;
-        }
+        DPRINT1("Profile %S already loaded\n", SidString.Buffer);
+    }
+    else
+    {
+        DPRINT1("Loading profile %S\n", SidString.Buffer);
 
-        UserSid = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, dwLength);
-        if (!UserSid)
+        if (lpProfileInfo->lpProfilePath)
         {
-            DPRINT1("HeapAlloc() failed\n");
-            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-            goto cleanup;
+            /* Use the caller's specified roaming user profile path */
+            StringCbCopyW(szUserHivePath, sizeof(szUserHivePath), lpProfileInfo->lpProfilePath);
         }
-
-        if (!GetTokenInformation(hToken, TokenUser, UserSid, dwLength, &dwLength))
+        else
         {
-            DPRINT1("GetTokenInformation() failed\n");
-            goto cleanup;
+            /* FIXME: check if MS Windows allows lpProfileInfo->lpProfilePath to be NULL */
+            if (!GetProfilesDirectoryW(szUserHivePath, &dwLength))
+            {
+                DPRINT1("GetProfilesDirectoryW() failed (error %ld)\n", GetLastError());
+                goto cleanup;
+            }
         }
 
-        /* Get user name */
-        do
+        /* Create user hive name */
+        StringCbCatW(szUserHivePath, sizeof(szUserHivePath), L"\\");
+        StringCbCatW(szUserHivePath, sizeof(szUserHivePath), lpProfileInfo->lpUserName);
+        StringCbCatW(szUserHivePath, sizeof(szUserHivePath), L"\\ntuser.dat");
+        DPRINT("szUserHivePath: %S\n", szUserHivePath);
+
+        /* Create user profile directory if needed */
+        if (GetFileAttributesW(szUserHivePath) == INVALID_FILE_ATTRIBUTES)
         {
-            if (UserNameLength > 0)
+            /* Get user sid */
+            if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) ||
+                GetLastError() != ERROR_INSUFFICIENT_BUFFER)
             {
-                HeapFree(GetProcessHeap(), 0, UserName);
-                UserName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, UserNameLength * sizeof(WCHAR));
-                if (!UserName)
-                {
-                    DPRINT1("HeapAlloc() failed\n");
-                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                    goto cleanup;
-                }
+                DPRINT1 ("GetTokenInformation() failed\n");
+                goto cleanup;
             }
-            if (DomainLength > 0)
+
+            UserSid = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, dwLength);
+            if (!UserSid)
             {
-                HeapFree(GetProcessHeap(), 0, Domain);
-                Domain = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, DomainLength * sizeof(WCHAR));
-                if (!Domain)
-                {
-                    DPRINT1("HeapAlloc() failed\n");
-                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                    goto cleanup;
-                }
+                DPRINT1("HeapAlloc() failed\n");
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto cleanup;
+            }
+
+            if (!GetTokenInformation(hToken, TokenUser, UserSid, dwLength, &dwLength))
+            {
+                DPRINT1("GetTokenInformation() failed\n");
+                goto cleanup;
+            }
+
+            /* Create profile */
+            ret = CreateUserProfileW(UserSid->User.Sid, lpProfileInfo->lpUserName);
+            if (!ret)
+            {
+                DPRINT1("CreateUserProfileW() failed\n");
+                goto cleanup;
             }
-            ret = LookupAccountSidW(NULL,
-                                    UserSid->User.Sid,
-                                    UserName,
-                                    &UserNameLength,
-                                    Domain,
-                                    &DomainLength,
-                                    &AccountType);
-        } while (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
-
-        if (!ret)
-        {
-            DPRINT1("LookupAccountSidW() failed\n");
-            goto cleanup;
         }
 
-        /* Create profile */
-        /* FIXME: ignore Domain? */
-        DPRINT("UserName %S, Domain %S\n", UserName, Domain);
-        ret = CreateUserProfileW(UserSid->User.Sid, UserName);
-        if (!ret)
+        /* Acquire restore privilege */
+        if (!AcquireRemoveRestorePrivilege(TRUE))
         {
-            DPRINT1("CreateUserProfileW() failed\n");
+            DPRINT1("AcquireRemoveRestorePrivilege() failed (Error %ld)\n", GetLastError());
             goto cleanup;
         }
-    }
 
-    /* Get user SID string */
-    ret = GetUserSidFromToken(hToken, &SidString);
-    if (!ret)
-    {
-        DPRINT1("GetUserSidFromToken() failed\n");
-        goto cleanup;
-    }
-    ret = FALSE;
+        /* Load user registry hive */
+        Error = RegLoadKeyW(HKEY_USERS,
+                            SidString.Buffer,
+                            szUserHivePath);
+        AcquireRemoveRestorePrivilege(FALSE);
 
-    /* Acquire restore privilege */
-    if (!AcquireRemoveRestorePrivilege(TRUE))
-    {
-        DPRINT1("AcquireRemoveRestorePrivilege() failed (Error %ld)\n", GetLastError());
-        goto cleanup;
-    }
+        /* HACK: Do not fail if the profile has already been loaded! */
+        if (Error == ERROR_SHARING_VIOLATION)
+            Error = ERROR_SUCCESS;
 
-    /* Load user registry hive */
-    Error = RegLoadKeyW(HKEY_USERS,
-                        SidString.Buffer,
-                        szUserHivePath);
-    AcquireRemoveRestorePrivilege(FALSE);
-    if (Error != ERROR_SUCCESS)
-    {
-        DPRINT1("RegLoadKeyW() failed (Error %ld)\n", Error);
-        SetLastError((DWORD)Error);
-        goto cleanup;
+        if (Error != ERROR_SUCCESS)
+        {
+            DPRINT1("RegLoadKeyW() failed (Error %ld)\n", Error);
+            SetLastError((DWORD)Error);
+            goto cleanup;
+        }
     }
 
     /* Open future HKEY_CURRENT_USER */
@@ -1206,12 +1764,26 @@ LoadUserProfileW(IN HANDLE hToken,
         goto cleanup;
     }
 
+    Error = IncrementRefCount(SidString.Buffer, NULL);
+    if (Error != ERROR_SUCCESS)
+    {
+        DPRINT1("IncrementRefCount() failed (Error %ld)\n", Error);
+        SetLastError((DWORD)Error);
+        goto cleanup;
+    }
+
     ret = TRUE;
 
 cleanup:
-    HeapFree(GetProcessHeap(), 0, UserSid);
-    HeapFree(GetProcessHeap(), 0, UserName);
-    HeapFree(GetProcessHeap(), 0, Domain);
+    if (UserSid != NULL)
+        HeapFree(GetProcessHeap(), 0, UserSid);
+
+    if (hProfileMutex != NULL)
+    {
+        ReleaseMutex(hProfileMutex);
+        CloseHandle(hProfileMutex);
+    }
+
     RtlFreeUnicodeString(&SidString);
 
     DPRINT("LoadUserProfileW() done\n");
@@ -1221,11 +1793,15 @@ cleanup:
 
 BOOL
 WINAPI
-UnloadUserProfile(HANDLE hToken,
-                  HANDLE hProfile)
+UnloadUserProfile(
+    _In_ HANDLE hToken,
+    _In_ HANDLE hProfile)
 {
-    UNICODE_STRING SidString;
+    UNICODE_STRING SidString = {0, 0, NULL};
+    HANDLE hProfileMutex = NULL;
+    DWORD dwRefCount = 0;
     LONG Error;
+    BOOL bRet = FALSE;
 
     DPRINT("UnloadUserProfile() called\n");
 
@@ -1236,108 +1812,96 @@ UnloadUserProfile(HANDLE hToken,
         return FALSE;
     }
 
-    RegCloseKey(hProfile);
-
-    if (!GetUserSidFromToken(hToken,
-                             &SidString))
+    /* Get the user SID string */
+    if (!GetUserSidStringFromToken(hToken, &SidString))
     {
-        DPRINT1("GetUserSidFromToken() failed\n");
+        DPRINT1("GetUserSidStringFromToken() failed\n");
         return FALSE;
     }
 
     DPRINT("SidString: '%wZ'\n", &SidString);
 
-    /* Acquire restore privilege */
-    if (!AcquireRemoveRestorePrivilege(TRUE))
+    /* Create the profile mutex */
+    hProfileMutex = CreateProfileMutex(SidString.Buffer);
+    if (hProfileMutex == NULL)
     {
-        DPRINT1("AcquireRemoveRestorePrivilege() failed (Error %ld)\n", GetLastError());
-        RtlFreeUnicodeString(&SidString);
-        return FALSE;
+        DPRINT1("Failed to create the profile mutex\n");
+        goto cleanup;
     }
 
-    /* Unload the hive */
-    Error = RegUnLoadKeyW(HKEY_USERS,
-                          SidString.Buffer);
+    /* Wait for the profile mutex */
+    WaitForSingleObject(hProfileMutex, INFINITE);
 
-    /* Remove restore privilege */
-    AcquireRemoveRestorePrivilege(FALSE);
+    /* Close the profile handle */
+    RegCloseKey(hProfile);
 
+    Error = DecrementRefCount(SidString.Buffer, &dwRefCount);
     if (Error != ERROR_SUCCESS)
     {
-        DPRINT1("RegUnLoadKeyW() failed (Error %ld)\n", Error);
-        RtlFreeUnicodeString(&SidString);
+        DPRINT1("DecrementRefCount() failed (Error %ld)\n", Error);
         SetLastError((DWORD)Error);
-        return FALSE;
+        goto cleanup;
     }
 
-    RtlFreeUnicodeString(&SidString);
-
-    DPRINT("UnloadUserProfile() done\n");
-
-    return TRUE;
-}
-
-
-BOOL
-WINAPI
-DeleteProfileW(LPCWSTR lpSidString,
-               LPCWSTR lpProfilePath,
-               LPCWSTR lpComputerName)
-{
-    DPRINT1("DeleteProfileW() not implemented!\n");
-    return FALSE;
-}
-
-
-BOOL
-WINAPI
-DeleteProfileA(LPCSTR lpSidString,
-               LPCSTR lpProfilePath,
-               LPCSTR lpComputerName)
-{
-    BOOL bResult;
-    UNICODE_STRING SidString, ProfilePath, ComputerName;
+    if (dwRefCount == 0)
+    {
+        DPRINT1("RefCount is 0: Unload the Hive!\n");
 
-    DPRINT("DeleteProfileA() called\n");
+        /* Acquire restore privilege */
+        if (!AcquireRemoveRestorePrivilege(TRUE))
+        {
+            DPRINT1("AcquireRemoveRestorePrivilege() failed (Error %ld)\n", GetLastError());
+            goto cleanup;
+        }
 
-    /* Conversion to UNICODE */
-    if (lpSidString)
-        RtlCreateUnicodeStringFromAsciiz(&SidString,
-                                         (LPSTR)lpSidString);
+        /* HACK */
+        {
+            HKEY hUserKey;
+
+            Error = RegOpenKeyExW(HKEY_USERS,
+                                  SidString.Buffer,
+                                  0,
+                                  KEY_WRITE,
+                                  &hUserKey);
+            if (Error == ERROR_SUCCESS)
+            {
+                RegDeleteKeyW(hUserKey,
+                              L"Volatile Environment");
 
-    if (lpProfilePath)
-        RtlCreateUnicodeStringFromAsciiz(&ProfilePath,
-                                         (LPSTR)lpProfilePath);
+                RegCloseKey(hUserKey);
+            }
+        }
+        /* End of HACK */
 
-    if (lpComputerName)
-        RtlCreateUnicodeStringFromAsciiz(&ComputerName,
-                                         (LPSTR)lpComputerName);
+        /* Unload the hive */
+        Error = RegUnLoadKeyW(HKEY_USERS,
+                              SidString.Buffer);
 
-    /* Call the UNICODE function */
-    bResult = DeleteProfileW(SidString.Buffer,
-                             ProfilePath.Buffer,
-                             ComputerName.Buffer);
+        /* Remove restore privilege */
+        AcquireRemoveRestorePrivilege(FALSE);
 
-    /* Memory cleanup */
-    if (lpSidString)
-        RtlFreeUnicodeString(&SidString);
+        if (Error != ERROR_SUCCESS)
+        {
+            DPRINT1("RegUnLoadKeyW() failed (Error %ld)\n", Error);
+            SetLastError((DWORD)Error);
+            goto cleanup;
+        }
+    }
 
-    if (lpProfilePath)
-        RtlFreeUnicodeString(&ProfilePath);
+    bRet = TRUE;
 
-    if (lpComputerName)
-        RtlFreeUnicodeString(&ComputerName);
+cleanup:
+    if (hProfileMutex != NULL)
+    {
+        ReleaseMutex(hProfileMutex);
+        CloseHandle(hProfileMutex);
+    }
 
-    return bResult;
-}
+    RtlFreeUnicodeString(&SidString);
 
+    DPRINT("UnloadUserProfile() done\n");
 
-BOOL
-WINAPI
-GetProfileType(OUT PDWORD pdwFlags)
-{
-    DPRINT1("GetProfileType() not implemented!\n");
-    return FALSE;
+    return bRet;
 }
 
 /* EOF */