[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(¶ms))
+ 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(©_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