/*
* Copyright 2011 André Hentschel
* Copyright 2013 Mislav Blažević
- * Copyright 2015 Mark Jansen
+ * Copyright 2015,2016 Mark Jansen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "ntndk.h"
#include "strsafe.h"
#include "apphelp.h"
+#include "sdbstringtable.h"
#include "wine/unicode.h"
+static const GUID GUID_DATABASE_MSI = {0xd8ff6d16,0x6a3a,0x468a, {0x8b,0x44,0x01,0x71,0x4d,0xdc,0x49,0xea}};
+static const GUID GUID_DATABASE_SHIM = {0x11111111,0x1111,0x1111, {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}};
+static const GUID GUID_DATABASE_DRIVERS = {0xf9ab2228,0x3312,0x4a73, {0xb6,0xf9,0x93,0x6d,0x70,0xe1,0x12,0xef}};
+
static HANDLE SdbpHeap(void);
#if SDBAPI_DEBUG_ALLOC
return mem;
}
-LPVOID SdbpReAlloc(LPVOID mem, SIZE_T size
+LPVOID SdbpReAlloc(LPVOID mem, SIZE_T size, SIZE_T oldSize
#if SDBAPI_DEBUG_ALLOC
, int line, const char* file
#endif
HeapFree(SdbpHeap(), 0, mem);
}
+PDB WINAPI SdbpCreate(LPCWSTR path, PATH_TYPE type, BOOL write)
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK io;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING str;
+ PDB db;
+
+ if (type == DOS_PATH)
+ {
+ if (!RtlDosPathNameToNtPathName_U(path, &str, NULL, NULL))
+ return NULL;
+ }
+ else
+ RtlInitUnicodeString(&str, path);
+
+ /* SdbAlloc zeroes the memory. */
+ db = (PDB)SdbAlloc(sizeof(DB));
+ if (!db)
+ {
+ SHIM_ERR("Failed to allocate memory for shim database\n");
+ return NULL;
+ }
+
+ InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ Status = NtCreateFile(&db->file, (write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ )| SYNCHRONIZE,
+ &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+ write ? FILE_SUPERSEDE : FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
+
+ if (type == DOS_PATH)
+ RtlFreeUnicodeString(&str);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SdbCloseDatabase(db);
+ SHIM_ERR("Failed to create shim database file: %lx\n", Status);
+ return NULL;
+ }
+
+ return db;
+}
+
+void WINAPI SdbpFlush(PDB db)
+{
+ IO_STATUS_BLOCK io;
+ NTSTATUS Status = NtWriteFile(db->file, NULL, NULL, NULL, &io,
+ db->data, db->write_iter, NULL, NULL);
+ if( !NT_SUCCESS(Status))
+ SHIM_WARN("failed with 0x%lx\n", Status);
+}
+
DWORD SdbpStrlen(PCWSTR string)
{
- return (lstrlenW(string) + 1) * sizeof(WCHAR);
+ return lstrlenW(string);
+}
+
+DWORD SdbpStrsize(PCWSTR string)
+{
+ return (SdbpStrlen(string) + 1) * sizeof(WCHAR);
}
PWSTR SdbpStrDup(LPCWSTR string)
{
- PWSTR ret = SdbpAlloc(SdbpStrlen(string));
+ PWSTR ret = SdbpAlloc(SdbpStrsize(string));
lstrcpyW(ret, string);
return ret;
}
void WINAPI SdbpCloseMemMappedFile(PMEMMAPPED mapping)
{
- NtUnmapViewOfSection(NtCurrentProcess(), mapping->view);
+ /* Prevent a VAD warning */
+ if (mapping->view)
+ NtUnmapViewOfSection(NtCurrentProcess(), mapping->view);
NtClose(mapping->section);
NtClose(mapping->file);
RtlZeroMemory(mapping, sizeof(*mapping));
}
+BOOL WINAPI SdbpCheckTagType(TAG tag, WORD type)
+{
+ if ((tag & TAG_TYPE_MASK) != type)
+ return FALSE;
+ return TRUE;
+}
+
+BOOL WINAPI SdbpCheckTagIDType(PDB db, TAGID tagid, WORD type)
+{
+ TAG tag = SdbGetTagFromTagID(db, tagid);
+ if (tag == TAG_NULL)
+ return FALSE;
+ return SdbpCheckTagType(tag, type);
+}
+
+PDB SdbpOpenDatabase(LPCWSTR path, PATH_TYPE type, PDWORD major, PDWORD minor)
+{
+ IO_STATUS_BLOCK io;
+ PDB db;
+ NTSTATUS Status;
+ BYTE header[12];
+
+ db = SdbpCreate(path, type, FALSE);
+ if (!db)
+ return NULL;
+
+ db->size = GetFileSize(db->file, NULL);
+ db->data = SdbAlloc(db->size);
+ Status = NtReadFile(db->file, NULL, NULL, NULL, &io, db->data, db->size, NULL, NULL);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SdbCloseDatabase(db);
+ SHIM_ERR("Failed to open shim database file: 0x%lx\n", Status);
+ return NULL;
+ }
+
+ if (!SdbpReadData(db, &header, 0, 12))
+ {
+ SdbCloseDatabase(db);
+ SHIM_ERR("Failed to read shim database header\n");
+ return NULL;
+ }
+
+ if (memcmp(&header[8], "sdbf", 4) != 0)
+ {
+ SdbCloseDatabase(db);
+ SHIM_ERR("Shim database header is invalid\n");
+ return NULL;
+ }
+
+ *major = *(DWORD*)&header[0];
+ *minor = *(DWORD*)&header[4];
+
+ return db;
+}
+
+
+/**
+ * Opens specified shim database file.
+ *
+ * @param [in] path Path to the shim database.
+ * @param [in] type Type of path. Either DOS_PATH or NT_PATH.
+ *
+ * @return Success: Handle to the shim database, NULL otherwise.
+ */
+PDB WINAPI SdbOpenDatabase(LPCWSTR path, PATH_TYPE type)
+{
+ PDB db;
+ DWORD major, minor;
+
+ db = SdbpOpenDatabase(path, type, &major, &minor);
+ if (!db)
+ return NULL;
+
+ if (major != 2)
+ {
+ SdbCloseDatabase(db);
+ SHIM_ERR("Invalid shim database version\n");
+ return NULL;
+ }
+
+ db->stringtable = SdbFindFirstTag(db, TAGID_ROOT, TAG_STRINGTABLE);
+ if(!SdbGetDatabaseID(db, &db->database_id))
+ {
+ SHIM_INFO("Failed to get the database id\n");
+ }
+ return db;
+}
+
+/**
+ * Closes specified database and frees its memory.
+ *
+ * @param [in] db Handle to the shim database.
+ */
+void WINAPI SdbCloseDatabase(PDB db)
+{
+ if (!db)
+ return;
+
+ if (db->file)
+ NtClose(db->file);
+ if (db->string_buffer)
+ SdbCloseDatabase(db->string_buffer);
+ if (db->string_lookup)
+ SdbpTableDestroy(&db->string_lookup);
+ SdbFree(db->data);
+ SdbFree(db);
+}
+
+/**
+ * Retrieves AppPatch directory.
+ *
+ * @param [in] db Handle to the shim database.
+ * @param [out] path Pointer to memory in which path shall be written.
+ * @param [in] size Size of the buffer in characters.
+ */
+BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size)
+{
+ static WCHAR* default_dir = NULL;
+ static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0};
+
+ /* In case function fails, path holds empty string */
+ if (size > 0)
+ *path = 0;
+
+ if (!default_dir)
+ {
+ WCHAR* tmp = NULL;
+ UINT len = GetSystemWindowsDirectoryW(NULL, 0) + lstrlenW(szAppPatch);
+ tmp = SdbAlloc((len + 1)* sizeof(WCHAR));
+ if (tmp)
+ {
+ UINT r = GetSystemWindowsDirectoryW(tmp, len+1);
+ if (r && r < len)
+ {
+ if (SUCCEEDED(StringCchCatW(tmp, len+1, szAppPatch)))
+ {
+ if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL)
+ tmp = NULL;
+ }
+ }
+ if (tmp)
+ SdbFree(tmp);
+ }
+ if (!default_dir)
+ {
+ SHIM_ERR("Unable to obtain default AppPatch directory\n");
+ return FALSE;
+ }
+ }
+
+ if (!db)
+ {
+ return SUCCEEDED(StringCchCopyW(path, size, default_dir));
+ }
+ else
+ {
+ /* fixme */
+ return FALSE;
+ }
+}
+
+/**
+ * Parses a string to retrieve a GUID.
+ *
+ * @param [in] GuidString The string to parse.
+ * @param [out] Guid The resulting GUID.
+ *
+ * @return TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbGUIDFromString(PCWSTR GuidString, GUID *Guid)
+{
+ UNICODE_STRING GuidString_u;
+ RtlInitUnicodeString(&GuidString_u, GuidString);
+ return NT_SUCCESS(RtlGUIDFromString(&GuidString_u, Guid));
+}
+
+/**
+ * Converts a GUID to a string.
+ *
+ * @param [in] Guid The GUID to convert.
+ * @param [out] GuidString The resulting string representation of Guid.
+ * @param [in] Length The length of GuidString.
+ *
+ * @return TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbGUIDToString(CONST GUID *Guid, PWSTR GuidString, SIZE_T Length)
+{
+ UNICODE_STRING GuidString_u;
+ if(NT_SUCCESS(RtlStringFromGUID(Guid, &GuidString_u)))
+ {
+ HRESULT hr = StringCchCopyNW(GuidString, Length, GuidString_u.Buffer, GuidString_u.Length / 2);
+ RtlFreeUnicodeString(&GuidString_u);
+ return SUCCEEDED(hr);
+ }
+ return FALSE;
+}
+
+/**
+ * Checks if the specified GUID is a NULL GUID
+ *
+ * @param [in] Guid The GUID to check.
+ *
+ * @return TRUE if it is a NULL GUID.
+ */
+BOOL WINAPI SdbIsNullGUID(CONST GUID *Guid)
+{
+ static GUID NullGuid = { 0 };
+ return !Guid || IsEqualGUID(&NullGuid, Guid);
+}
+
+/**
+ * Get the GUID from one of the standard databases.
+ *
+ * @param [in] Flags The ID to retrieve the guid from. (See SDB_DATABASE_MAIN_[xxx])
+ * @param [out] Guid The resulting GUID.
+ *
+ * @return TRUE if a known database ID.
+ */
+BOOL WINAPI SdbGetStandardDatabaseGUID(DWORD Flags, GUID* Guid)
+{
+ const GUID* copy_from = NULL;
+ switch(Flags & HID_DATABASE_TYPE_MASK)
+ {
+ case SDB_DATABASE_MAIN_MSI:
+ copy_from = &GUID_DATABASE_MSI;
+ break;
+ case SDB_DATABASE_MAIN_SHIM:
+ copy_from = &GUID_DATABASE_SHIM;
+ break;
+ case SDB_DATABASE_MAIN_DRIVERS:
+ copy_from = &GUID_DATABASE_DRIVERS;
+ break;
+ default:
+ SHIM_ERR("Cannot obtain database guid for databases other than main\n");
+ return FALSE;
+ }
+ if(Guid)
+ {
+ memcpy(Guid, copy_from, sizeof(GUID));
+ }
+ return TRUE;
+}
+
+/**
+ * Read the database version from the specified database.
+ *
+ * @param [in] database The database.
+ * @param [out] VersionHi The first part of the version number.
+ * @param [out] VersionLo The second part of the version number.
+ *
+ * @return TRUE if it succeeds or fails, FALSE if ???
+ */
+BOOL WINAPI SdbGetDatabaseVersion(LPCWSTR database, PDWORD VersionHi, PDWORD VersionLo)
+{
+ PDB db;
+
+ db = SdbpOpenDatabase(database, DOS_PATH, VersionHi, VersionLo);
+ if (db)
+ SdbCloseDatabase(db);
+
+ return TRUE;
+}
+
+
+/**
+ * Converts the specified string to an index key.
+ *
+ * @param [in] str The string which will be converted.
+ *
+ * @return The resulting index key
+ *
+ * @todo: Fix this for unicode strings.
+ */
+LONGLONG WINAPI SdbMakeIndexKeyFromString(LPCWSTR str)
+{
+ LONGLONG result = 0;
+ int shift = 56;
+
+ while (*str && shift >= 0)
+ {
+ WCHAR c = toupper(*(str++));
+
+ if (c & 0xff)
+ {
+ result |= (((LONGLONG)(c & 0xff)) << shift);
+ shift -= 8;
+ }
+
+ if (shift < 0)
+ break;
+
+ c >>= 8;
+
+ if (c & 0xff)
+ {
+ result |= (((LONGLONG)(c & 0xff)) << shift);
+ shift -= 8;
+ }
+ }
+
+ return result;
+}
+
+
/**
* Converts specified tag into a string.
*
* @param [in] tag The tag which will be converted to a string.
*
* @return Success: Pointer to the string matching specified tag, or L"InvalidTag" on failure.
+ *
+ * @todo: Convert this into a lookup table, this is wasting alot of space.
*/
LPCWSTR WINAPI SdbTagToString(TAG tag)
{