Signed-off-by: Zebediah Figura zfigura@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