From 21c96f8d75f9596e1cd523bae1f2f54b5d2cd445 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Thu, 3 Mar 2016 05:02:21 +0100
Subject: [PATCH] setupapi: Implement SetupAddToDiskSpaceList.

---
 dlls/setupapi/diskspace.c       | 137 +++++++++++++++++++++++++--
 dlls/setupapi/tests/diskspace.c | 159 +++++++++++++++++++++++++++++++-
 2 files changed, 285 insertions(+), 11 deletions(-)

diff --git a/dlls/setupapi/diskspace.c b/dlls/setupapi/diskspace.c
index 4f047dd2b54..dcee29443d7 100644
--- a/dlls/setupapi/diskspace.c
+++ b/dlls/setupapi/diskspace.c
@@ -48,7 +48,21 @@ struct space_list
     UINT flags;
 };
 
+static LONGLONG get_file_size(WCHAR *path)
+{
+    HANDLE file;
+    LARGE_INTEGER size;
+
+    file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file == INVALID_HANDLE_VALUE) return 0;
 
+    if (!GetFileSizeEx(file, &size))
+        size.QuadPart = 0;
+
+    CloseHandle(file);
+    return size.QuadPart;
+}
 
 /***********************************************************************
  *		SetupCreateDiskSpaceListW  (SETUPAPI.@)
@@ -280,25 +294,128 @@ BOOL WINAPI SetupDestroyDiskSpaceList(HDSKSPC diskspace)
 }
 
 /***********************************************************************
-*		SetupAddToDiskSpaceListA  (SETUPAPI.@)
+*		SetupAddToDiskSpaceListW  (SETUPAPI.@)
 */
-BOOL WINAPI SetupAddToDiskSpaceListA(HDSKSPC diskspace, PCSTR targetfile,
+BOOL WINAPI SetupAddToDiskSpaceListW(HDSKSPC diskspace, PCWSTR targetfile,
                                     LONGLONG filesize, UINT operation,
                                     PVOID reserved1, UINT reserved2)
 {
-    FIXME(": stub\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    struct space_list *list = diskspace;
+    struct file_entry *file;
+    WCHAR *fullpathW;
+    BOOL ret = FALSE;
+    DWORD size;
+
+    TRACE("(%p, %s, %s, %u, %p, %u)\n", diskspace, debugstr_w(targetfile),
+          wine_dbgstr_longlong(filesize), operation, reserved1, reserved2);
+
+    if (!targetfile)
+        return TRUE;
+
+    if (!diskspace)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (operation != FILEOP_COPY && operation != FILEOP_DELETE)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    size = GetFullPathNameW(targetfile, 0, NULL, NULL);
+    if (!size)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    size = (size+1) * sizeof(WCHAR);
+    fullpathW = HeapAlloc(GetProcessHeap(), 0, size);
+
+    if (!GetFullPathNameW(targetfile, size, fullpathW, NULL))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        goto done;
+    }
+
+    if (fullpathW[1] != ':' && fullpathW[2] != '\\')
+    {
+        FIXME("UNC paths not yet supported\n");
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+        goto done;
+    }
+
+    LIST_FOR_EACH_ENTRY(file, &list->files, struct file_entry, entry)
+    {
+        if (!lstrcmpiW(file->path, fullpathW))
+            break;
+    }
+
+    if (&file->entry == &list->files)
+    {
+        file = HeapAlloc(GetProcessHeap(), 0, sizeof(*file));
+        if (!file)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto done;
+        }
+
+        file->path = strdupW(fullpathW);
+        if (!file->path)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            HeapFree(GetProcessHeap(), 0, file);
+            goto done;
+        }
+
+        list_add_tail(&list->files, &file->entry);
+    }
+
+    file->operation = operation;
+    if (operation == FILEOP_COPY)
+        file->size = filesize;
+    else
+        file->size = 0;
+
+    if (!(list->flags & SPDSL_IGNORE_DISK))
+        file->size -= get_file_size(fullpathW);
+
+    ret = TRUE;
+
+done:
+    HeapFree(GetProcessHeap(), 0, fullpathW);
+    return ret;
 }
 
 /***********************************************************************
-*		SetupAddToDiskSpaceListW  (SETUPAPI.@)
+*       SetupAddToDiskSpaceListA  (SETUPAPI.@)
 */
-BOOL WINAPI SetupAddToDiskSpaceListW(HDSKSPC diskspace, PCWSTR targetfile,
+BOOL WINAPI SetupAddToDiskSpaceListA(HDSKSPC diskspace, PCSTR targetfile,
                                     LONGLONG filesize, UINT operation,
                                     PVOID reserved1, UINT reserved2)
 {
-    FIXME(": stub\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    LPWSTR targetfileW = NULL;
+    DWORD len;
+    BOOL ret;
+
+    if (targetfile)
+    {
+        len = MultiByteToWideChar(CP_ACP, 0, targetfile, -1, NULL, 0);
+
+        targetfileW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        if (!targetfileW)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            return FALSE;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, targetfile, -1, targetfileW, len);
+    }
+
+    ret = SetupAddToDiskSpaceListW(diskspace, targetfileW, filesize,
+                                   operation, reserved1, reserved2);
+    if (targetfileW) HeapFree(GetProcessHeap(), 0, targetfileW);
+    return ret;
 }
diff --git a/dlls/setupapi/tests/diskspace.c b/dlls/setupapi/tests/diskspace.c
index dab6705eee3..2bdf8babce0 100644
--- a/dlls/setupapi/tests/diskspace.c
+++ b/dlls/setupapi/tests/diskspace.c
@@ -19,6 +19,7 @@
  */
 
 #include <stdarg.h>
+#include <stdio.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -29,6 +30,16 @@
 
 #include "wine/test.h"
 
+static inline const char* debugstr_longlong(ULONGLONG ll)
+{
+    static char string[17];
+    if (sizeof(ll) > sizeof(unsigned long) && ll >> 32)
+        sprintf(string, "%lx%08lx", (unsigned long)(ll >> 32), (unsigned long)ll);
+    else
+        sprintf(string, "%lx", (unsigned long)ll);
+    return string;
+}
+
 static void test_SetupCreateDiskSpaceListA(void)
 {
     HDSKSPC ret;
@@ -293,11 +304,31 @@ static void test_SetupDuplicateDiskSpaceListW(void)
     ok(SetupDestroyDiskSpaceList(handle), "Expected SetupDestroyDiskSpaceList to succeed\n");
 }
 
+static LONGLONG get_file_size(char *path)
+{
+    HANDLE file;
+    LARGE_INTEGER size;
+
+    file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file == INVALID_HANDLE_VALUE) return 0;
+
+    if (!GetFileSizeEx(file, &size))
+        size.QuadPart = 0;
+
+    CloseHandle(file);
+    return size.QuadPart;
+}
+
 static void test_SetupQuerySpaceRequiredOnDriveA(void)
 {
     BOOL ret;
     HDSKSPC handle;
     LONGLONG space;
+    char windir[MAX_PATH];
+    char drive[3];
+    char tmp[MAX_PATH];
+    LONGLONG size;
 
     SetLastError(0xdeadbeef);
     ret = SetupQuerySpaceRequiredOnDriveA(NULL, NULL, NULL, NULL, 0);
@@ -357,7 +388,7 @@ static void test_SetupQuerySpaceRequiredOnDriveA(void)
     ret = SetupQuerySpaceRequiredOnDriveA(handle, "", NULL, NULL, 0);
     ok(!ret, "Expected SetupQuerySpaceRequiredOnDriveA to return FALSE, got %d\n", ret);
     ok(GetLastError() == ERROR_INVALID_DRIVE,
-       "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
+       "Expected GetLastError() to return ERROR_INVALID_DRIVE, got %u\n",
        GetLastError());
 
     SetLastError(0xdeadbeef);
@@ -369,6 +400,97 @@ static void test_SetupQuerySpaceRequiredOnDriveA(void)
        "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
        GetLastError());
 
+    GetWindowsDirectoryA(windir, MAX_PATH);
+    drive[0] = windir[0]; drive[1] = windir[1]; drive[2] = 0;
+
+    snprintf(tmp, MAX_PATH, "%c:\\wine-test-should-not-exist.txt", drive[0]);
+    ret = SetupAddToDiskSpaceListA(handle, tmp, 0x100000, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    space = 0;
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0x100000, "Expected 0x100000 as required space, got %s\n", debugstr_longlong(space));
+
+    /* adding the same file again doesn't sum up the size */
+    ret = SetupAddToDiskSpaceListA(handle, tmp, 0x200000, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    space = 0;
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0x200000, "Expected 0x200000 as required space, got %s\n", debugstr_longlong(space));
+
+    /* the device doesn't need to exist */
+    snprintf(tmp, MAX_PATH, "F:\\wine-test-should-not-exist.txt");
+    ret = SetupAddToDiskSpaceListA(handle, tmp, 0x200000, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, "F:", &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0x200000, "Expected 0x100000 as required space, got %s\n", debugstr_longlong(space));
+
+    ok(SetupDestroyDiskSpaceList(handle),
+       "Expected SetupDestroyDiskSpaceList to succeed\n");
+
+    handle = SetupCreateDiskSpaceListA(NULL, 0, 0);
+    ok(handle != NULL,
+       "Expected SetupCreateDiskSpaceListA to return a valid handle, got NULL\n");
+
+    /* the real size is subtracted unless SPDSL_IGNORE_DISK is specified */
+    snprintf(tmp, MAX_PATH, "%s\\regedit.exe", windir);
+
+    size = get_file_size(tmp);
+    ret = SetupAddToDiskSpaceListA(handle, tmp, size, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+    space = 0;
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0 || broken(space == -0x5000) || broken(space == -0x7000),
+       "Expected 0x0 as required space, got %s\n", debugstr_longlong(space));
+
+    ret = SetupAddToDiskSpaceListA(handle, tmp, size + 0x100000, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0x100000 || broken(space == 0xf9000) || broken(space == 0xfb000),
+       "Expected 0x100000 as required space, got %s\n", debugstr_longlong(space));
+
+    ret = SetupAddToDiskSpaceListA(handle, tmp, size - 0x1000, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == -0x1000 || broken(space == -0x6000) || broken(space == -0x8000),
+       "Expected -0x1000 as required space, got %s\n", debugstr_longlong(space));
+
+    ok(SetupDestroyDiskSpaceList(handle),
+       "Expected SetupDestroyDiskSpaceList to succeed\n");
+
+    handle = SetupCreateDiskSpaceListA(NULL, 0, 0);
+    ok(handle != NULL,
+       "Expected SetupCreateDiskSpaceListA to return a valid handle, got NULL\n");
+
+    ret = SetupAddToDiskSpaceListA(handle, tmp, size, FILEOP_DELETE, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+    space = 0;
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space <= -size, "Expected space <= -size, got %s\n", debugstr_longlong(space));
+
+    ok(SetupDestroyDiskSpaceList(handle),
+       "Expected SetupDestroyDiskSpaceList to succeed\n");
+
+    handle = SetupCreateDiskSpaceListA(NULL, 0, SPDSL_IGNORE_DISK);
+    ok(handle != NULL,
+       "Expected SetupCreateDiskSpaceListA to return a valid handle, got NULL\n");
+
+    ret = SetupAddToDiskSpaceListA(handle, tmp, size, FILEOP_DELETE, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+    space = 0;
+    ret = SetupQuerySpaceRequiredOnDriveA(handle, drive, &space, NULL, 0);
+    ok(ret, "Expected SetupQuerySpaceRequiredOnDriveA to succeed\n");
+    ok(space == 0, "Expected size = 0, got %s\n", debugstr_longlong(space));
+
     ok(SetupDestroyDiskSpaceList(handle),
        "Expected SetupDestroyDiskSpaceList to succeed\n");
 }
@@ -460,6 +582,40 @@ static void test_SetupQuerySpaceRequiredOnDriveW(void)
        "Expected SetupDestroyDiskSpaceList to succeed\n");
 }
 
+static void test_SetupAddToDiskSpaceListA(void)
+{
+    HDSKSPC handle;
+    BOOL ret;
+
+    ret = SetupAddToDiskSpaceListA(NULL, "C:\\some-file.dat", 0, FILEOP_COPY, 0, 0);
+    ok(!ret, "Expected SetupAddToDiskSpaceListA to return FALSE, got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE,
+       "Expected GetLastError() to return ERROR_INVALID_HANDLE, got %u\n", GetLastError());
+
+    handle = SetupCreateDiskSpaceListA(NULL, 0, 0);
+    ok(handle != NULL,"Expected SetupCreateDiskSpaceListA to return a valid handle\n");
+
+    ret = SetupAddToDiskSpaceListA(handle, NULL, 0, FILEOP_COPY, 0, 0);
+    ok(ret || broken(!ret) /* >= Vista */, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    ret = SetupAddToDiskSpaceListA(handle, "C:\\some-file.dat", -20, FILEOP_COPY, 0, 0);
+    ok(ret, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    ret = SetupAddToDiskSpaceListA(handle, "C:\\some-file.dat", 0, FILEOP_RENAME, 0, 0);
+    ok(!ret, "Expected SetupAddToDiskSpaceListA to return FALSE\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
+
+    ret = SetupAddToDiskSpaceListA(handle, NULL, 0, FILEOP_RENAME, 0, 0);
+    ok(ret || broken(!ret) /* >= Vista */, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    ret = SetupAddToDiskSpaceListA(NULL, NULL, 0, FILEOP_RENAME, 0, 0);
+    ok(ret || broken(!ret) /* >= Vista */, "Expected SetupAddToDiskSpaceListA to succeed\n");
+
+    ok(SetupDestroyDiskSpaceList(handle),
+       "Expected SetupDestroyDiskSpaceList to succeed\n");
+}
+
 START_TEST(diskspace)
 {
     test_SetupCreateDiskSpaceListA();
@@ -468,4 +624,5 @@ START_TEST(diskspace)
     test_SetupDuplicateDiskSpaceListW();
     test_SetupQuerySpaceRequiredOnDriveA();
     test_SetupQuerySpaceRequiredOnDriveW();
+    test_SetupAddToDiskSpaceListA();
 }
-- 
2.23.0

