From 37a1e2241efeb1d0654bbd6459b24ee6e92782d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Fri, 4 Mar 2016 04:54:37 +0100
Subject: [PATCH] setupapi: Implement SetupAddInstallSectionToDiskSpaceList.

---
 dlls/setupapi/diskspace.c       |  87 +++++++++++++++-
 dlls/setupapi/setupapi.spec     |   2 +-
 dlls/setupapi/tests/diskspace.c | 170 ++++++++++++++++++++++++++++++++
 include/setupapi.h              |   3 +
 4 files changed, 257 insertions(+), 5 deletions(-)

diff --git a/dlls/setupapi/diskspace.c b/dlls/setupapi/diskspace.c
index 3153a18856a..39673cd63b5 100644
--- a/dlls/setupapi/diskspace.c
+++ b/dlls/setupapi/diskspace.c
@@ -294,14 +294,93 @@ BOOL WINAPI SetupAddSectionToDiskSpaceListA(HDSKSPC diskspace, HINF hinf, HINF h
 /***********************************************************************
  *      SetupAddInstallSectionToDiskSpaceListW  (SETUPAPI.@)
  */
-BOOL WINAPI SetupAddInstallSectionToDiskSpaceListA(HDSKSPC DiskSpace, 
-                        HINF InfHandle, HINF LayoutInfHandle, 
-                        LPCSTR SectionName, PVOID Reserved1, UINT Reserved2)
+BOOL WINAPI SetupAddInstallSectionToDiskSpaceListW(HDSKSPC diskspace,
+                        HINF inf, HINF layoutinf, LPCWSTR section,
+                        PVOID reserved1, UINT reserved2)
 {
-    FIXME ("Stub\n");
+    static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
+    static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
+    WCHAR section_name[MAX_PATH];
+    INFCONTEXT context;
+    BOOL ret;
+    int i;
+
+    TRACE("(%p, %p, %p, %s, %p, %u)\n", diskspace, inf, layoutinf, debugstr_w(section),
+                                        reserved1, reserved2);
+
+    if (!diskspace)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (!section)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (!inf) return TRUE;
+    if (!layoutinf) layoutinf = inf;
+
+    ret = SetupFindFirstLineW(inf, section, CopyFiles, &context);
+    while (ret)
+    {
+        for (i = 1;; i++)
+        {
+            if (!SetupGetStringFieldW(&context, i, section_name, sizeof(section_name) / sizeof(WCHAR), NULL))
+                break;
+            SetupAddSectionToDiskSpaceListW(diskspace, layoutinf, inf, section_name, FILEOP_COPY, 0, 0);
+        }
+        ret = SetupFindNextLine(&context, &context);
+    }
+
+    ret = SetupFindFirstLineW(inf, section, DelFiles, &context);
+    while (ret)
+    {
+        for (i = 1;; i++)
+        {
+            if (!SetupGetStringFieldW(&context, i, section_name, sizeof(section_name) / sizeof(WCHAR), NULL))
+                break;
+            SetupAddSectionToDiskSpaceListW(diskspace, layoutinf, inf, section_name, FILEOP_DELETE, 0, 0);
+        }
+        ret = SetupFindNextLine(&context, &context);
+    }
+
     return TRUE;
 }
 
+/***********************************************************************
+ *		SetupAddInstallSectionToDiskSpaceListA  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupAddInstallSectionToDiskSpaceListA(HDSKSPC diskspace,
+                        HINF inf, HINF layoutinf, LPCSTR section,
+                        PVOID reserved1, UINT reserved2)
+{
+    LPWSTR sectionW = NULL;
+    DWORD len;
+    BOOL ret;
+
+    if (section)
+    {
+        len = MultiByteToWideChar(CP_ACP, 0, section, -1, NULL, 0);
+
+        sectionW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        if (!sectionW)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            return FALSE;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, section, -1, sectionW, len);
+    }
+
+    ret = SetupAddInstallSectionToDiskSpaceListW(diskspace, inf, layoutinf,
+                                                 sectionW, reserved1, reserved2);
+    if (sectionW) HeapFree(GetProcessHeap(), 0, sectionW);
+    return ret;
+}
+
 /***********************************************************************
 *		SetupQuerySpaceRequiredOnDriveW  (SETUPAPI.@)
 */
diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec
index ad8652ac93b..1d09b0e7e57 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -243,7 +243,7 @@
 @ stub SearchForInfFile
 @ stub SetArrayToMultiSzValue
 @ stdcall SetupAddInstallSectionToDiskSpaceListA(long long long str ptr long)
-@ stub SetupAddInstallSectionToDiskSpaceListW
+@ stdcall SetupAddInstallSectionToDiskSpaceListW(long long long wstr ptr long)
 @ stdcall SetupAddSectionToDiskSpaceListA(long long long str long ptr long)
 @ stdcall SetupAddSectionToDiskSpaceListW(long long long wstr long ptr long)
 @ stdcall SetupAddToDiskSpaceListA(long str int64 long ptr long)
diff --git a/dlls/setupapi/tests/diskspace.c b/dlls/setupapi/tests/diskspace.c
index 7b8bcf4a342..13e025a8cfd 100644
--- a/dlls/setupapi/tests/diskspace.c
+++ b/dlls/setupapi/tests/diskspace.c
@@ -935,6 +935,175 @@ static void test_SetupAddSectionToDiskSpaceListA(void)
     DeleteFileA(tmpfilename);
 }
 
+struct section_i
+{
+    const char *name;
+    BOOL result;
+    DWORD error_code;
+};
+
+static const struct
+{
+    const char *data;
+    struct section_i sections[2];
+    const char *devices;
+    int device_length;
+    struct device_usage usage[2];
+}
+section_test_i[] =
+{
+    /* 0 */
+    {STD_HEADER "[a.Install]\nCopyFiles=a.CopyFiles\n"
+                "[a.CopyFiles]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n",
+     {{"a.Install", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 4096}, {NULL, 0}}},
+    /* 1 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles\n"
+                "[a.CopyFiles]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 4096}, {NULL, 0}}},
+    /* 2 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles\nCopyFiles=a.CopyFiles2\n"
+                "[a.CopyFiles]\ntest,,,\n[a.CopyFiles2]\ntest2,,,\n"
+                "[SourceDisksFiles]\ntest=1,,4096\ntest2=1,,4096\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 8192}, {NULL, 0}}},
+    /* 3 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles,a.CopyFiles2\n"
+                "[a.CopyFiles]\ntest,,,\n[a.CopyFiles2]\ntest2,,,\n"
+                "[SourceDisksFiles]\ntest=1,,4096\ntest2=1,,4096\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 8192}, {NULL, 0}}},
+    /* 4 */
+    {STD_HEADER "[a]\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "", sizeof(""), {{NULL, 0}, {NULL, 0}}},
+    /* 5 */
+    {STD_HEADER "[a]\nDelFiles=a.DelFiles\n"
+                "[a.nDelFiles]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "", sizeof(""), {{NULL, 0}, {NULL, 0}}},
+    /* 6 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles\nDelFiles=a.DelFiles\n"
+                "[a.CopyFiles]\ntest,,,\n[a.DelFiles]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 4096}, {NULL, 0}}},
+    /* 7 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles\n[b]\nDelFiles=b.DelFiles\n"
+                "[a.CopyFiles]\ntest,,,\n[b.DelFiles]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n",
+     {{"a", TRUE, 0}, {"b", TRUE, 0}}, "c:\00", sizeof("c:\00"), {{"c:", 4096}, {NULL, 0}}},
+    /* 7 */
+    {STD_HEADER "[a]\nCopyFiles=\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "", sizeof(""), {{NULL, 0}, {NULL, 0}}},
+    /* 8 */
+    {STD_HEADER "[a]\nCopyFiles=something\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "", sizeof(""), {{NULL, 0}, {NULL, 0}}},
+    /* 9 */
+    {STD_HEADER "[a]\nCopyFiles=a.CopyFiles,b.CopyFiles\n[a.CopyFiles]\ntest,,,\n[b.CopyFiles]\ntest,,,\n"
+                "[SourceDisksFiles]\ntest=1,,4096\n[DestinationDirs]\nb.CopyFiles=-1,F:\\test\r\n",
+     {{"a", TRUE, 0}, {NULL, TRUE, 0}}, "c:\00f:\00", sizeof("c:\00f:\00"), {{"c:", 4096}, {"f:", 4096}}},
+};
+
+static void test_SetupAddInstallSectionToDiskSpaceListA(void)
+{
+    char tmp[MAX_PATH];
+    char tmpfilename[MAX_PATH];
+    char buffer[MAX_PATH];
+    HDSKSPC diskspace;
+    LONGLONG space;
+    UINT err_line;
+    BOOL ret;
+    int i, j;
+    HINF inf;
+
+    if (!GetTempPathA(MAX_PATH, tmp))
+    {
+        win_skip("GetTempPath failed with error %d\n", GetLastError());
+        return;
+    }
+
+    if (!GetTempFileNameA(tmp, "inftest", 0, tmpfilename))
+    {
+        win_skip("GetTempFileNameA failed with error %d\n", GetLastError());
+        return;
+    }
+
+    inf = inf_open_file_content(tmpfilename, STD_HEADER "[a]\nCopyFiles=b\n[b]\ntest,,,\n[SourceDisksFiles]\ntest=1,,4096\r\n", &err_line);
+    ok(!!inf, "Failed to open inf file (%d, line %d)\n", GetLastError(), err_line);
+
+    diskspace = SetupCreateDiskSpaceListA(NULL, 0, SPDSL_IGNORE_DISK);
+    ok(diskspace != NULL,"Expected SetupCreateDiskSpaceListA to return a valid handle\n");
+
+    ret = SetupAddInstallSectionToDiskSpaceListA(diskspace, NULL, NULL, "a", 0, 0);
+    ok(ret, "Expected SetupAddInstallSectionToDiskSpaceListA to succeed\n");
+
+    ret = SetupAddInstallSectionToDiskSpaceListA(NULL, inf, NULL, "a", 0, 0);
+    ok(!ret, "Expected SetupAddInstallSectionToDiskSpaceListA to fail\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE as error, got %u\n",
+       GetLastError());
+
+    ret = SetupAddInstallSectionToDiskSpaceListA(diskspace, inf, NULL, NULL, 0, 0);
+    ok(!ret || broken(ret), "Expected SetupAddSectionToDiskSpaceListA to fail\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(ret),
+       "Expected ERROR_INVALID_PARAMETER as error, got %u\n", GetLastError());
+
+    ret = SetupAddInstallSectionToDiskSpaceListA(diskspace, inf, NULL, "", 0, 0);
+    ok(ret, "Expected SetupAddInstallSectionToDiskSpaceListA to succeed (%u)\n", GetLastError());
+
+    ok(SetupDestroyDiskSpaceList(diskspace),
+       "Expected SetupDestroyDiskSpaceList to succeed\n");
+
+    for (i = 0; i < sizeof(section_test_i) / sizeof(section_test_i[0]); i++)
+    {
+        err_line = 0;
+
+        inf = inf_open_file_content(tmpfilename, section_test_i[i].data, &err_line);
+        ok(!!inf, "test %d: Failed to open inf file (%d, line %d)\n", i, GetLastError(), err_line);
+        if (!inf) continue;
+
+        diskspace = SetupCreateDiskSpaceListA(NULL, 0, SPDSL_IGNORE_DISK);
+        ok(diskspace != NULL,"Expected SetupCreateDiskSpaceListA to return a valid handle\n");
+
+        for (j = 0; j < 2; j++)
+        {
+            const struct section_i *section = &section_test_i[i].sections[j];
+            if (!section->name)
+                continue;
+
+            SetLastError(0xdeadbeef);
+            ret = SetupAddInstallSectionToDiskSpaceListA(diskspace, inf, NULL, section->name, 0, 0);
+            if (section->result)
+                ok(ret, "test %d: Expected adding section %d to succeed (%u)\n", i, j, GetLastError());
+            else
+            {
+                ok(!ret, "test %d: Expected adding section %d to fail\n", i, j);
+                ok(GetLastError() == section->error_code, "test %d: Expected %u as error, got %u\n",
+                   i, section->error_code, GetLastError());
+            }
+        }
+
+        memset(buffer, 0x0, sizeof(buffer));
+        ret = SetupQueryDrivesInDiskSpaceListA(diskspace, buffer, sizeof(buffer), NULL);
+        ok(ret, "test %d: Expected SetupQueryDrivesInDiskSpaceListA to succeed (%u)\n", i, GetLastError());
+        ok(!memcmp(section_test_i[i].devices, buffer, section_test_i[i].device_length),
+           "test %d: Device list (%s) does not match\n", i, buffer);
+
+        for (j = 0; j < 2; j++)
+        {
+            const struct device_usage *usage = &section_test_i[i].usage[j];
+            if (!usage->dev)
+                continue;
+
+            space = 0;
+            ret = SetupQuerySpaceRequiredOnDriveA(diskspace, usage->dev, &space, NULL, 0);
+            ok(ret, "test %d: Expected SetupQuerySpaceRequiredOnDriveA to succeed for device %s (%u)\n",
+               i, usage->dev, GetLastError());
+            ok(space == usage->usage, "test %d: Expected size %u for device %s, got %u\n",
+               i, (DWORD)usage->usage, usage->dev, (DWORD)space);
+        }
+
+        ok(SetupDestroyDiskSpaceList(diskspace),
+           "Expected SetupDestroyDiskSpaceList to succeed\n");
+
+        SetupCloseInfFile(inf);
+    }
+
+    DeleteFileA(tmpfilename);
+}
+
 START_TEST(diskspace)
 {
     test_SetupCreateDiskSpaceListA();
@@ -946,4 +1115,5 @@ START_TEST(diskspace)
     test_SetupAddToDiskSpaceListA();
     test_SetupQueryDrivesInDiskSpaceListA();
     test_SetupAddSectionToDiskSpaceListA();
+    test_SetupAddInstallSectionToDiskSpaceListA();
 }
diff --git a/include/setupapi.h b/include/setupapi.h
index 818cc12599a..e54c6c227f6 100644
--- a/include/setupapi.h
+++ b/include/setupapi.h
@@ -1416,6 +1416,9 @@ DWORD    WINAPI OpenAndMapForRead(PCWSTR, PDWORD, PHANDLE, PHANDLE, PVOID *);
 LONG     WINAPI QueryRegistryValue(HKEY, PCWSTR, PBYTE *, PDWORD, PDWORD);
 /* RetreiveFileSecurity is not a typo, as per Microsoft's dlls */
 DWORD    WINAPI RetreiveFileSecurity(PCWSTR, PSECURITY_DESCRIPTOR *);
+BOOL     WINAPI SetupAddInstallSectionToDiskSpaceListA(HDSKSPC, HINF, HINF, PCSTR, PVOID, UINT);
+BOOL     WINAPI SetupAddInstallSectionToDiskSpaceListW(HDSKSPC, HINF, HINF, PCWSTR, PVOID, UINT);
+#define         SetupAddInstallSectionToDiskSpaceList WINELIB_NAME_AW(SetupAddInstallSectionToDiskSpaceList)
 BOOL     WINAPI SetupAddSectionToDiskSpaceListA(HDSKSPC, HINF, HINF, PCSTR, UINT, PVOID, UINT);
 BOOL     WINAPI SetupAddSectionToDiskSpaceListW(HDSKSPC, HINF, HINF, PCWSTR, UINT, PVOID, UINT);
 #define         SetupAddSectionToDiskSpaceList WINELIB_NAME_AW(SetupAddSectionToDiskSpaceList)
-- 
2.23.0

