[PATCH 2/4] dpinst: Copy driver source files to the driver store.

Zebediah Figura zfigura at codeweavers.com
Mon Feb 7 23:36:08 CST 2022


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 programs/dpinst/Makefile.in   |   1 +
 programs/dpinst/dpinst.c      | 352 +++++++++++++++++++++++++++++++++-
 programs/dpinst64/Makefile.in |   1 +
 3 files changed, 351 insertions(+), 3 deletions(-)

diff --git a/programs/dpinst/Makefile.in b/programs/dpinst/Makefile.in
index bd97b860ce7..64c9c015dad 100644
--- a/programs/dpinst/Makefile.in
+++ b/programs/dpinst/Makefile.in
@@ -1,4 +1,5 @@
 MODULE    = dpinst.exe
+IMPORTS   = setupapi
 EXTRADLLFLAGS = -mconsole -municode
 
 C_SRCS = dpinst.c
diff --git a/programs/dpinst/dpinst.c b/programs/dpinst/dpinst.c
index 52b4119760b..afcadffec53 100644
--- a/programs/dpinst/dpinst.c
+++ b/programs/dpinst/dpinst.c
@@ -18,18 +18,364 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <io.h>
 #include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "setupapi.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dpinst);
 
-int __cdecl wmain(int argc, WCHAR *argv[])
+#ifdef __i386__
+static const WCHAR platform_extension[] = L"NTx86";
+#elif defined(__x86_64__)
+static const WCHAR platform_extension[] = L"NTamd64";
+#elif defined(__arm__)
+static const WCHAR platform_extension[] = L"NTarm";
+#elif defined(__aarch64__)
+static const WCHAR platform_extension[] = L"NTarm64";
+#endif
+
+struct options
+{
+    const WCHAR *path;
+};
+
+static bool version_is_compatible(const WCHAR *version)
+{
+    size_t len = wcslen(version);
+    const WCHAR *p;
+
+    /* We are only concerned with architecture. */
+    if ((p = wcschr(version, '.')))
+        len = p - version;
+
+    if (!wcsnicmp(version, L"NT", len))
+        return true;
+
+    return !wcsnicmp(version, platform_extension, len);
+}
+
+static WCHAR *concat_path(const WCHAR *root, const WCHAR *path)
+{
+    size_t len = wcslen(root) + wcslen(path) + 2;
+    WCHAR *ret = malloc(len * sizeof(WCHAR));
+
+    swprintf(ret, len, L"%s\\%s", root, path);
+    return ret;
+}
+
+static WCHAR *get_string_field(INFCONTEXT *ctx, DWORD index)
+{
+    WCHAR *ret;
+    DWORD len;
+
+    if (!SetupGetStringFieldW(ctx, index, NULL, 0, &len) || len <= 1)
+        return NULL;
+
+    ret = malloc(len * sizeof(WCHAR));
+    SetupGetStringFieldW(ctx, index, ret, len, NULL);
+    return ret;
+}
+
+typedef struct
+{
+   ULONG Unknown[6];
+   ULONG State[5];
+   ULONG Count[2];
+   UCHAR Buffer[64];
+} SHA_CTX;
+
+void WINAPI A_SHAInit(SHA_CTX *ctx);
+void WINAPI A_SHAUpdate(SHA_CTX *ctx, const UCHAR *buffer, UINT size);
+void WINAPI A_SHAFinal(SHA_CTX *ctx, ULONG *result);
+
+static WCHAR *get_dst_root(const WCHAR *filename, const WCHAR *filename_abs)
+{
+    /* This is not compatible with Microsoft's naming scheme. We omit the
+     * architecture (which is redundant) and the locale (which is basically
+     * also redundant; in practice it is always "default"). We also don't use
+     * the same hash algorithm. */
+
+    unsigned char buffer[1024];
+    unsigned char hash[20];
+    unsigned int i;
+    SHA_CTX ctx;
+    HANDLE file;
+    WCHAR *name;
+    DWORD size;
+
+    file = CreateFileW(filename_abs, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+    if (file != INVALID_HANDLE_VALUE)
+    {
+        A_SHAInit(&ctx);
+
+        do
+        {
+            if (!ReadFile(file, buffer, sizeof(buffer), &size, NULL))
+            {
+                ERR("Failed to read from %s, error %lu.\n", debugstr_w(filename_abs), GetLastError());
+                break;
+            }
+            A_SHAUpdate(&ctx, buffer, size);
+        } while (size);
+
+        A_SHAFinal(&ctx, (ULONG *)hash);
+        CloseHandle(file);
+    }
+    else
+    {
+        ERR("Failed to open %s, error %lu.\n", debugstr_w(filename_abs), GetLastError());
+    }
+
+    name = malloc((wcslen(filename) + 1 + 40 + 1) * sizeof(WCHAR));
+    wcscpy(name, filename);
+    wcscat(name, L"_");
+    for (i = 0; i < ARRAY_SIZE(hash); ++i)
+        swprintf(name + wcslen(name), 3, L"%02x", hash[i]);
+
+    return concat_path(L"C:\\windows\\system32\\driverstore\\filerepository", name);
+}
+
+static void get_source_info(HINF hinf, const WCHAR *src_file, WCHAR **desc, WCHAR **tag, WCHAR **subdir)
+{
+    WCHAR *file_subdir, *disk_subdir;
+    INFCONTEXT file_ctx, disk_ctx;
+    int id, diskid;
+
+    /* find the SourceDisksFiles entry */
+    if (!SetupFindFirstLineW(hinf, L"SourceDisksFiles", src_file, &file_ctx)) return;
+    if (!SetupGetIntField(&file_ctx, 1, &diskid)) return;
+
+    /* now find the diskid in the SourceDisksNames section */
+    if (!SetupFindFirstLineW(hinf, L"SourceDisksNames", NULL, &disk_ctx)) return;
+    for (;;)
+    {
+        if (SetupGetIntField(&disk_ctx, 0, &id) && id == diskid) break;
+        if (!SetupFindNextLine(&disk_ctx, &disk_ctx)) return;
+    }
+
+    *desc = get_string_field(&disk_ctx, 1);
+    *tag = get_string_field(&disk_ctx, 2);
+    disk_subdir = get_string_field(&disk_ctx, 4);
+    file_subdir = get_string_field(&file_ctx, 2);
+
+    if (disk_subdir)
+    {
+        if (file_subdir)
+            *subdir = concat_path(disk_subdir, file_subdir);
+        else
+            *subdir = disk_subdir;
+    }
+    else
+    {
+        *subdir = file_subdir;
+    }
+}
+
+struct copy_context
+{
+    HINF hinf;
+    HSPFILEQ queue;
+    const WCHAR *src_root, *dst_root;
+};
+
+static void queue_copy_file(const struct copy_context *copy_ctx, const WCHAR *file)
+{
+    SP_FILE_COPY_PARAMS_W params =
+    {
+        .cbSize = sizeof(params),
+        .QueueHandle = copy_ctx->queue,
+        .SourceRootPath = copy_ctx->src_root,
+        .CopyStyle = SP_COPY_NODECOMP,
+        .SourceFilename = file,
+        .TargetFilename = file,
+    };
+
+    WCHAR *desc = NULL, *tag = NULL, *subdir = NULL;
+
+    get_source_info(copy_ctx->hinf, file, &desc, &tag, &subdir);
+    params.SourceDescription = desc;
+    params.SourceTagfile = tag;
+    params.SourcePath = subdir;
+
+    if (subdir)
+        params.TargetDirectory = concat_path(copy_ctx->dst_root, subdir);
+    else
+        params.TargetDirectory = copy_ctx->dst_root;
+
+    TRACE("Queueing copy from subdir %s, filename %s.\n", debugstr_w(subdir), debugstr_w(file));
+    if (!SetupQueueCopyIndirectW(&params))
+        ERR("Failed to queue copy, error %lu.\n", GetLastError());
+}
+
+static void queue_copy_section(const struct copy_context *copy_ctx, const WCHAR *section)
+{
+    if (section[0] == '@')
+    {
+        queue_copy_file(copy_ctx, section + 1);
+    }
+    else
+    {
+        INFCONTEXT context;
+
+        if (!SetupFindFirstLineW(copy_ctx->hinf, section, NULL, &context)) return;
+        do
+        {
+            WCHAR *file;
+
+            if (!(file = get_string_field(&context, 2)))
+                file = get_string_field(&context, 1);
+
+            queue_copy_file(copy_ctx, file);
+        } while (SetupFindNextLine(&context, &context));
+    }
+}
+
+static void install_driver(const struct copy_context *copy_ctx, const WCHAR *driver_section)
+{
+    INFCONTEXT ctx;
+    DWORD i, count;
+    BOOL found;
+
+    TRACE("Installing driver section %s.\n", debugstr_w(driver_section));
+
+    found = SetupFindFirstLineW(copy_ctx->hinf, driver_section, L"CopyFiles", &ctx);
+    while (found)
+    {
+        count = SetupGetFieldCount(&ctx);
+
+        for (i = 1; i <= count; ++i)
+            queue_copy_section(copy_ctx, get_string_field(&ctx, i));
+
+        found = SetupFindNextMatchLineW(&ctx, L"CopyFiles", &ctx);
+    }
+}
+
+static void install_inf(const WCHAR *root, const WCHAR *filename)
+{
+    WCHAR version[LINE_LEN], mfg_key[LINE_LEN], manufacturer[LINE_LEN];
+    struct copy_context copy_ctx;
+    WCHAR *filename_abs;
+    void *default_ctx;
+    INFCONTEXT ctx;
+    DWORD i, j;
+
+    filename_abs = concat_path(root, filename);
+
+    TRACE("Installing drivers from %s.\n", debugstr_w(filename_abs));
+
+    if ((copy_ctx.hinf = SetupOpenInfFileW(filename_abs, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE)
+    {
+        ERR("Failed to open %s, error %lu.\n", debugstr_w(filename_abs), GetLastError());
+        return;
+    }
+    copy_ctx.queue = SetupOpenFileQueue();
+    copy_ctx.src_root = root;
+    copy_ctx.dst_root = get_dst_root(filename, filename_abs);
+
+    for (i = 0; SetupGetLineByIndexW(copy_ctx.hinf, L"Manufacturer", i, &ctx); ++i)
+    {
+        SetupGetStringFieldW(&ctx, 0, manufacturer, ARRAY_SIZE(manufacturer), NULL);
+        if (!SetupGetStringFieldW(&ctx, 1, mfg_key, ARRAY_SIZE(mfg_key), NULL))
+            wcscpy(mfg_key, manufacturer);
+
+        if (SetupGetFieldCount(&ctx) >= 2)
+        {
+            BOOL compatible = FALSE;
+            for (j = 2; SetupGetStringFieldW(&ctx, j, version, ARRAY_SIZE(version), NULL); ++j)
+            {
+                if (version_is_compatible(version))
+                {
+                    compatible = TRUE;
+                    break;
+                }
+            }
+            if (!compatible)
+                continue;
+        }
+
+        if (!SetupDiGetActualSectionToInstallW(copy_ctx.hinf, mfg_key, mfg_key, ARRAY_SIZE(mfg_key), NULL, NULL))
+        {
+            WARN("Failed to find section for %s, skipping.\n", debugstr_w(mfg_key));
+            continue;
+        }
+
+        for (j = 0; SetupGetLineByIndexW(copy_ctx.hinf, mfg_key, j, &ctx); ++j)
+            install_driver(&copy_ctx, get_string_field(&ctx, 1));
+    }
+
+    default_ctx = SetupInitDefaultQueueCallback(NULL);
+    if (!SetupCommitFileQueueW(NULL, copy_ctx.queue, SetupDefaultQueueCallbackW, default_ctx))
+        ERR("Failed to commit queue, error %lu.\n", GetLastError());
+
+    SetupCloseFileQueue(copy_ctx.queue);
+    SetupCloseInfFile(copy_ctx.hinf);
+}
+
+static bool parse_options(struct options *options, int argc, WCHAR **argv)
 {
     int i;
 
+    for (i = 1; i < argc; i++)
+    {
+        if (!wcsicmp(argv[i], L"/path"))
+        {
+            if (i >= argc - 1)
+            {
+                ERR("No argument specified for /path.\n");
+                return false;
+            }
+            options->path = argv[++i];
+        }
+        else if (argv[i][0] == '/')
+        {
+            FIXME("Ignoring option %s.\n", debugstr_w(argv[i]));
+        }
+        else
+        {
+            ERR("Invalid argument %s.\n", debugstr_w(argv[i]));
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int __cdecl wmain(int argc, WCHAR *argv[])
+{
+    struct options options = {0};
+    struct _wfinddata_t data;
+    intptr_t handle;
+    int i;
+
     for (i = 0; i < argc; i++)
-        FIXME(" %s", debugstr_w(argv[i]));
-    FIXME("\n");
+        TRACE(" %s", debugstr_w(argv[i]));
+    TRACE("\n");
+
+    if (!parse_options(&options, argc, argv))
+        return 0;
+
+    if (!(handle = _wfindfirst(concat_path(options.path, L"*"), &data)))
+    {
+        ERR("Failed to enumerate the contents of %s.\n", debugstr_w(options.path));
+        return 0;
+    }
+
+    do
+    {
+        size_t len = wcslen(data.name);
+
+        TRACE("%s\n", debugstr_w(data.name));
+
+        if (len >= 4 && !wcsicmp(data.name + len - 4, L".inf"))
+            install_inf(options.path, data.name);
+    } while (!_wfindnext(handle, &data));
 
     return 0;
 }
diff --git a/programs/dpinst64/Makefile.in b/programs/dpinst64/Makefile.in
index f79ed3e0dba..29cc6c50046 100644
--- a/programs/dpinst64/Makefile.in
+++ b/programs/dpinst64/Makefile.in
@@ -1,4 +1,5 @@
 MODULE    = dpinst64.exe
+IMPORTS   = setupapi
 EXTRADLLFLAGS = -mconsole -municode
 PARENTSRC = ../dpinst
 
-- 
2.34.1




More information about the wine-devel mailing list