[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[win-pv-devel] [PATCH] Add registry override to prevent interception of Trim requests



Add a REG_DWORD called 'InterceptTrim' to XENDISK's parameters key
(now created by xenvbd.inf). Set the value to zero to disable Trim
interception.

This patch also adds registry code to XENDISK, brings util.h up to date,
and fixes some cosmetic issues in thread.c.

Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx>
---
 src/xendisk/driver.c           |   90 ++-
 src/xendisk/driver.h           |    5 +
 src/xendisk/fdo.c              |    2 +-
 src/xendisk/pdo.c              |   28 +-
 src/xendisk/registry.c         | 1492 ++++++++++++++++++++++++++++++++++++++++
 src/xendisk/registry.h         |  203 ++++++
 src/xendisk/thread.c           |    8 +-
 src/xendisk/util.h             |  404 +++++++----
 src/xenvbd.inf                 |    4 +
 vs2012/xendisk/xendisk.vcxproj |    1 +
 vs2013/xendisk/xendisk.vcxproj |    1 +
 11 files changed, 2097 insertions(+), 141 deletions(-)
 create mode 100644 src/xendisk/registry.c
 create mode 100644 src/xendisk/registry.h

diff --git a/src/xendisk/driver.c b/src/xendisk/driver.c
index fb0a4b3..4b004c5 100644
--- a/src/xendisk/driver.c
+++ b/src/xendisk/driver.c
@@ -30,14 +30,18 @@
  */
 
 #include <ntddk.h>
+
+#include "registry.h"
 #include "driver.h"
 #include "util.h"
 #include "debug.h"
 #include "assert.h"
+
 #include <version.h>
 
 typedef struct _XENDISK_DRIVER {
-    PDRIVER_OBJECT              DriverObject;
+    PDRIVER_OBJECT  DriverObject;
+    HANDLE          ParametersKey;
 } XENDISK_DRIVER, *PXENDISK_DRIVER;
 
 static XENDISK_DRIVER   Driver;
@@ -66,6 +70,30 @@ DriverGetDriverObject(
     return __DriverGetDriverObject();
 }
 
+static FORCEINLINE VOID
+__DriverSetParametersKey(
+    IN  HANDLE  Key
+    )
+{
+    Driver.ParametersKey = Key;
+}
+
+static FORCEINLINE HANDLE
+__DriverGetParametersKey(
+    VOID
+    )
+{
+    return Driver.ParametersKey;
+}
+
+HANDLE
+DriverGetParametersKey(
+    VOID
+    )
+{
+    return __DriverGetParametersKey();
+}
+
 DRIVER_UNLOAD   DriverUnload;
 
 VOID
@@ -73,10 +101,28 @@ DriverUnload(
     IN  PDRIVER_OBJECT  DriverObject
     )
 {
+    HANDLE              ParametersKey;
+
     ASSERT3P(DriverObject, ==, __DriverGetDriverObject());
 
     Trace("====>\n");
 
+    ParametersKey = __DriverGetParametersKey();
+    __DriverSetParametersKey(NULL);
+
+    RegistryCloseKey(ParametersKey);
+
+    RegistryTeardown();
+
+    Verbose("XENDISK %d.%d.%d (%d) (%02d.%02d.%04d)\n",
+            MAJOR_VERSION,
+            MINOR_VERSION,
+            MICRO_VERSION,
+            BUILD_NUMBER,
+            DAY,
+            MONTH,
+            YEAR);
+
     __DriverSetDriverObject(NULL);
 
     ASSERT(IsZeroMemory(&Driver, sizeof (XENDISK_DRIVER)));
@@ -162,7 +208,10 @@ DriverEntry(
     IN  PUNICODE_STRING RegistryPath
     )
 {
+    HANDLE              ServiceKey;
+    HANDLE              ParametersKey;
     ULONG               Index;
+    NTSTATUS            status;
 
     ASSERT3P(__DriverGetDriverObject(), ==, NULL);
     UNREFERENCED_PARAMETER(RegistryPath);
@@ -184,6 +233,25 @@ DriverEntry(
             MONTH,
             YEAR);
 
+    status = RegistryInitialize(RegistryPath);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenServiceKey(KEY_ALL_ACCESS, &ServiceKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = RegistryOpenSubKey(ServiceKey,
+                                "Parameters",
+                                KEY_READ,
+                                &ParametersKey);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __DriverSetParametersKey(ParametersKey);
+
+    RegistryCloseKey(ServiceKey);
+
     DriverObject->DriverExtension->AddDevice = AddDevice;
 
     for (Index = 0; Index <= IRP_MJ_MAXIMUM_FUNCTION; Index++) {
@@ -193,5 +261,25 @@ DriverEntry(
     }
 
     Trace("<====\n");
+
     return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    RegistryCloseKey(ServiceKey);
+
+fail2:
+    Error("fail2\n");
+
+    RegistryTeardown();
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __DriverSetDriverObject(NULL);
+
+    ASSERT(IsZeroMemory(&Driver, sizeof (XENDISK_DRIVER)));
+
+    return status;
 }
diff --git a/src/xendisk/driver.h b/src/xendisk/driver.h
index 42662dd..f1dc821 100644
--- a/src/xendisk/driver.h
+++ b/src/xendisk/driver.h
@@ -40,6 +40,11 @@ DriverGetDriverObject(
     VOID
     );
 
+extern HANDLE
+DriverGetParametersKey(
+    VOID
+    );
+
 #pragma warning(push)
 #pragma warning(disable:4201) // nonstandard extension used : nameless 
struct/union
 
diff --git a/src/xendisk/fdo.c b/src/xendisk/fdo.c
index 8d66e30..f00d47e 100644
--- a/src/xendisk/fdo.c
+++ b/src/xendisk/fdo.c
@@ -67,7 +67,7 @@ __FdoAllocate(
     IN  ULONG   Length
     )
 {
-    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
FDO_TAG);
+    return __AllocatePoolWithTag(NonPagedPool, Length, FDO_TAG);
 }
 
 static FORCEINLINE VOID
diff --git a/src/xendisk/pdo.c b/src/xendisk/pdo.c
index 9ca96da..0f719d1 100644
--- a/src/xendisk/pdo.c
+++ b/src/xendisk/pdo.c
@@ -43,6 +43,7 @@
 #include "fdo.h"
 #include "pdo.h"
 #include "driver.h"
+#include "registry.h"
 #include "thread.h"
 #include "debug.h"
 #include "assert.h"
@@ -62,6 +63,7 @@ struct _XENDISK_PDO {
 
     PXENDISK_FDO                Fdo;
 
+    BOOLEAN                     InterceptTrim;
     ULONG                       SectorSize;
 };
 
@@ -70,7 +72,7 @@ __PdoAllocate(
     IN  ULONG   Length
     )
 {
-    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
PDO_TAG);
+    return __AllocatePoolWithTag(NonPagedPool, Length, PDO_TAG);
 }
 
 static FORCEINLINE VOID
@@ -616,6 +618,11 @@ PdoQueryProperty(
 
     switch (Query->PropertyId) {
     case StorageDeviceTrimProperty:
+        if (!Pdo->InterceptTrim) {
+            status = PdoForwardIrpAndForget(Pdo, Irp);
+            break;
+        }
+
         if (Query->QueryType == PropertyStandardQuery) {
             PDEVICE_TRIM_DESCRIPTOR Trim;
 
@@ -662,6 +669,11 @@ PdoManageDataSetAttributes(
 
     switch (Attributes->Action) {
     case DeviceDsmAction_Trim:
+        if (!Pdo->InterceptTrim) {
+            status = PdoForwardIrpAndForget(Pdo, Irp);
+            break;
+        }
+
         Ranges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)Attributes + 
Attributes->DataSetRangesOffset);
         NumRanges = Attributes->DataSetRangesLength / 
sizeof(DEVICE_DATA_SET_RANGE);
 
@@ -1992,6 +2004,8 @@ PdoCreate(
     PDEVICE_OBJECT                  FilterDeviceObject;
     PXENDISK_DX                     Dx;
     PXENDISK_PDO                    Pdo;
+    HANDLE                          ParametersKey;
+    ULONG                           InterceptTrim;
     NTSTATUS                        status;
 
     LowerDeviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
@@ -2045,6 +2059,16 @@ PdoCreate(
     if (!NT_SUCCESS(status))
         goto fail5;
 
+    ParametersKey = DriverGetParametersKey();
+
+    Pdo->InterceptTrim = TRUE;
+
+    status = RegistryQueryDwordValue(ParametersKey,
+                                     "InterceptTrim",
+                                     &InterceptTrim);
+    if (NT_SUCCESS(status))
+        Pdo->InterceptTrim = (InterceptTrim != 0) ? TRUE : FALSE;
+
     Verbose("%p\n", FilterDeviceObject);
 
     Dx->Pdo = Pdo;
@@ -2110,6 +2134,8 @@ PdoDestroy(
 
     Dx->Pdo = NULL;
 
+    Pdo->InterceptTrim = FALSE;
+
     ThreadAlert(Pdo->DevicePowerThread);
     ThreadJoin(Pdo->DevicePowerThread);
     Pdo->DevicePowerThread = NULL;
diff --git a/src/xendisk/registry.c b/src/xendisk/registry.c
new file mode 100644
index 0000000..56f3942
--- /dev/null
+++ b/src/xendisk/registry.c
@@ -0,0 +1,1492 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * *   Redistributions of source code must retain the above
+ *     copyright notice, this list of conditions and the
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the
+ *     following disclaimer in the documentation and/or other
+ *     materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+
+#include "registry.h"
+#include "assert.h"
+#include "util.h"
+
+#define REGISTRY_TAG 'GERX'
+
+static UNICODE_STRING   RegistryPath;
+
+static FORCEINLINE PVOID
+__RegistryAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, REGISTRY_TAG);
+}
+
+static FORCEINLINE VOID
+__RegistryFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, REGISTRY_TAG);
+}
+
+NTSTATUS
+RegistryInitialize(
+    IN PUNICODE_STRING  Path
+    )
+{
+    NTSTATUS            status;
+
+    ASSERT3P(RegistryPath.Buffer, ==, NULL);
+
+    status = RtlUpcaseUnicodeString(&RegistryPath, Path, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+RegistryTeardown(
+    VOID
+    )
+{
+    RtlFreeUnicodeString(&RegistryPath);
+    RegistryPath.Buffer = NULL;
+    RegistryPath.MaximumLength = RegistryPath.Length = 0;
+}
+
+NTSTATUS
+RegistryOpenKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    OBJECT_ATTRIBUTES   Attributes;
+    NTSTATUS            status;
+
+    InitializeObjectAttributes(&Attributes,
+                               Path,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               Parent,
+                               NULL);
+
+    status = ZwOpenKey(Key,
+                       DesiredAccess,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
+
+static NTSTATUS
+RegistryOpenRoot(
+    IN  PWCHAR          Path,
+    OUT PHANDLE         Parent,
+    OUT PWCHAR          *ChildPath
+    )
+{
+    const WCHAR         Prefix[] = L"\\Registry\\Machine\\";
+    ULONG               Length;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    Length = (ULONG)wcslen(Prefix);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (_wcsnicmp(Path, Prefix, Length) != 0)
+        goto fail1;
+
+    RtlInitUnicodeString(&Unicode, Prefix);
+
+    status = RegistryOpenKey(NULL, &Unicode, KEY_ALL_ACCESS, Parent);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    *ChildPath = Path + Length;
+
+    return STATUS_SUCCESS;
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryCreateKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ULONG           Options,
+    OUT PHANDLE         Key
+    )
+{
+    PWCHAR              Buffer;
+    HANDLE              Root;
+    PWCHAR              ChildPath;
+    PWCHAR              ChildName;
+    PWCHAR              Context;
+    HANDLE              Child;
+    NTSTATUS            status;
+
+    //
+    // UNICODE_STRINGs are not guaranteed to have NUL terminated
+    // buffers.
+    //
+
+    Buffer = __RegistryAllocate(Path->MaximumLength + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto fail1;
+
+    RtlCopyMemory(Buffer, Path->Buffer, Path->Length);
+
+    Root = Parent;
+
+    if (Parent != NULL) {
+        ChildPath = Buffer;
+    } else {
+        status = RegistryOpenRoot(Buffer, &Parent, &ChildPath);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    }
+
+    ChildName = __wcstok_r(ChildPath, L"\\", &Context);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (ChildName == NULL)
+        goto fail3;
+
+    Child = NULL;
+
+    while (ChildName != NULL) {
+        UNICODE_STRING      Unicode;
+        OBJECT_ATTRIBUTES   Attributes;
+
+        RtlInitUnicodeString(&Unicode, ChildName);
+
+        InitializeObjectAttributes(&Attributes,
+                                   &Unicode,
+                                   OBJ_CASE_INSENSITIVE |
+                                   OBJ_KERNEL_HANDLE |
+                                   OBJ_OPENIF,
+                                   Parent,
+                                   NULL);
+
+        status = ZwCreateKey(&Child,
+                             KEY_ALL_ACCESS,
+                             &Attributes,
+                             0,
+                             NULL,
+                             Options,
+                             NULL
+                             );
+        if (!NT_SUCCESS(status))
+            goto fail4;
+
+        ChildName = __wcstok_r(NULL, L"\\", &Context);
+
+        if (Parent != Root)
+            ZwClose(Parent);
+
+        Parent = Child;
+    }
+
+    ASSERT(Child != NULL);
+
+    *Key = Child;
+
+    __RegistryFree(Buffer);
+
+    return STATUS_SUCCESS;
+
+fail4:
+fail3:
+    if (Parent != Root)
+        ZwClose(Parent);
+
+fail2:
+    __RegistryFree(Buffer);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenServiceKey(
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    return RegistryOpenKey(NULL, &RegistryPath, DesiredAccess, Key);
+}
+
+NTSTATUS
+RegistryCreateServiceKey(
+    OUT PHANDLE         Key
+    )
+{
+    return RegistryCreateKey(NULL, &RegistryPath, REG_OPTION_NON_VOLATILE, 
Key);
+}
+
+NTSTATUS
+RegistryOpenSoftwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    NTSTATUS            status;
+
+    status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DRIVER,
+                                     DesiredAccess,
+                                     Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenHardwareKey(
+    IN  PDEVICE_OBJECT      DeviceObject,
+    IN  ACCESS_MASK         DesiredAccess,
+    OUT PHANDLE             Key
+    )
+{
+    HANDLE                  SubKey;
+    ULONG                   Length;
+    PKEY_NAME_INFORMATION   Info;
+    PWCHAR                  Cursor;
+    UNICODE_STRING          Unicode;
+    NTSTATUS                status;
+
+    status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DEVICE,
+                                     KEY_READ,
+                                     &SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Length = 0;
+    status = ZwQueryKey(SubKey,
+                        KeyNameInformation,
+                        NULL,
+                        0,
+                        &Length);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    Info = __RegistryAllocate(Length + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Info == NULL)
+        goto fail3;
+
+    status = ZwQueryKey(SubKey,
+                        KeyNameInformation,
+                        Info,
+                        Length,
+                        &Length);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    Info->Name[Info->NameLength / sizeof (WCHAR)] = '\0';
+
+    Cursor = wcsrchr(Info->Name, L'\\');
+    ASSERT(Cursor != NULL);
+
+    *Cursor = L'\0';
+
+    RtlInitUnicodeString(&Unicode, Info->Name);
+
+    status = RegistryOpenKey(NULL, &Unicode, DesiredAccess, Key);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    __RegistryFree(Info);
+
+    RegistryCloseKey(SubKey);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Info);
+
+fail3:
+fail2:
+    RegistryCloseKey(SubKey);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         SubKey
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenKey(Key, &Unicode, DesiredAccess, SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryCreateSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Options,
+    OUT PHANDLE         SubKey
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryCreateKey(Key, &Unicode, Options, SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryDeleteSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    HANDLE              SubKey;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenKey(Key, &Unicode, KEY_ALL_ACCESS, &SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = ZwDeleteKey(SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(SubKey);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    ZwClose(SubKey);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryEnumerateSubKeys(
+    IN  HANDLE              Key,
+    IN  NTSTATUS            (*Callback)(PVOID, HANDLE, PANSI_STRING),
+    IN  PVOID               Context
+    )
+{
+    ULONG                   Size;
+    NTSTATUS                status;
+    PKEY_FULL_INFORMATION   Full;
+    PKEY_BASIC_INFORMATION  Basic;
+    ULONG                   Index;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+#pragma prefast(suppress:6102)
+    Full = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Full == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        Full,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
+           Full->MaxNameLen;
+
+    Basic = __RegistryAllocate(Size);
+    status = STATUS_NO_MEMORY;
+    if (Basic == NULL)
+        goto fail4;
+
+    for (Index = 0; Index < Full->SubKeys; Index++) {
+        ULONG           Ignore;
+        UNICODE_STRING  Unicode;
+        ANSI_STRING     Ansi;
+
+        status = ZwEnumerateKey(Key,
+                                Index,
+                                KeyBasicInformation,
+                                Basic,
+                                Size,
+                                &Ignore);
+        if (!NT_SUCCESS(status))
+            goto fail5;
+
+        Unicode.MaximumLength = (USHORT)Basic->NameLength;
+        Unicode.Buffer = Basic->Name;
+        Unicode.Length = (USHORT)Basic->NameLength;
+
+        Ansi.MaximumLength = (USHORT)((Basic->NameLength / sizeof (WCHAR)) + 
sizeof (CHAR));
+        Ansi.Buffer = __RegistryAllocate(Ansi.MaximumLength);
+
+        status = STATUS_NO_MEMORY;
+        if (Ansi.Buffer == NULL)
+            goto fail6;
+
+        status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi.Length = (USHORT)(strlen(Ansi.Buffer) * sizeof (CHAR));
+
+        status = Callback(Context, Key, &Ansi);
+
+        __RegistryFree(Ansi.Buffer);
+        Ansi.Buffer = NULL;
+
+        if (!NT_SUCCESS(status))
+            goto fail7;
+    }
+
+    __RegistryFree(Basic);
+
+    __RegistryFree(Full);
+
+    return STATUS_SUCCESS;
+
+fail7:
+fail6:
+fail5:
+    __RegistryFree(Basic);
+
+fail4:
+fail3:
+    __RegistryFree(Full);
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryEnumerateValues(
+    IN  HANDLE                      Key,
+    IN  NTSTATUS                    (*Callback)(PVOID, HANDLE, PANSI_STRING, 
ULONG),
+    IN  PVOID                       Context
+    )
+{
+    ULONG                           Size;
+    NTSTATUS                        status;
+    PKEY_FULL_INFORMATION           Full;
+    PKEY_VALUE_BASIC_INFORMATION    Basic;
+    ULONG                           Index;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+#pragma prefast(suppress:6102)
+    Full = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Full == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        Full,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) +
+           Full->MaxValueNameLen;
+
+    Basic = __RegistryAllocate(Size);
+    status = STATUS_NO_MEMORY;
+    if (Basic == NULL)
+        goto fail4;
+
+    for (Index = 0; Index < Full->Values; Index++) {
+        ULONG           Ignore;
+        UNICODE_STRING  Unicode;
+        ANSI_STRING     Ansi;
+
+        status = ZwEnumerateValueKey(Key,
+                                     Index,
+                                     KeyValueBasicInformation,
+                                     Basic,
+                                     Size,
+                                     &Ignore);
+        if (!NT_SUCCESS(status))
+            goto fail5;
+
+        Unicode.MaximumLength = (USHORT)Basic->NameLength;
+        Unicode.Buffer = Basic->Name;
+        Unicode.Length = (USHORT)Basic->NameLength;
+
+        Ansi.MaximumLength = (USHORT)((Basic->NameLength / sizeof (WCHAR)) + 
sizeof (CHAR));
+        Ansi.Buffer = __RegistryAllocate(Ansi.MaximumLength);
+
+        status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi.Length = (USHORT)(strlen(Ansi.Buffer) * sizeof (CHAR));
+
+        status = Callback(Context, Key, &Ansi, Basic->Type);
+
+        __RegistryFree(Ansi.Buffer);
+
+        if (!NT_SUCCESS(status))
+            goto fail6;
+    }
+
+    __RegistryFree(Basic);
+
+    __RegistryFree(Full);
+
+    return STATUS_SUCCESS;
+
+fail6:
+fail5:
+    __RegistryFree(Basic);
+
+fail4:
+fail3:
+    __RegistryFree(Full);
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryDeleteValue(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ZwDeleteValueKey(Key, &Unicode);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    (VOID) ZwFlushKey(Key);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQueryDwordValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PULONG                      Value
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    ULONG                           Size;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    Partial = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail3;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             Partial,
+                             Size,
+                             &Size);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Partial->Type != REG_DWORD ||
+        Partial->DataLength != sizeof (ULONG))
+        goto fail5;
+
+    *Value = *(PULONG)Partial->Data;
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Partial);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryUpdateDwordValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  ULONG                       Value
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, 
Data) +
+                                 sizeof (ULONG));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail2;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_DWORD;
+    Partial->DataLength = sizeof (ULONG);
+    *(PULONG)Partial->Data = Value;
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+
+    return status;
+}
+
+static PANSI_STRING
+RegistrySzToAnsi(
+    IN  PWCHAR      Buffer
+    )
+{
+    PANSI_STRING    Ansi;
+    ULONG           Length;
+    UNICODE_STRING  Unicode;
+    NTSTATUS        status;
+
+    Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * 2);
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi == NULL)
+        goto fail1;
+
+    Length = (ULONG)wcslen(Buffer);
+    Ansi[0].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+    Ansi[0].Buffer = __RegistryAllocate(Ansi[0].MaximumLength);
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi[0].Buffer == NULL)
+        goto fail2;
+
+    RtlInitUnicodeString(&Unicode, Buffer);
+    status = RtlUnicodeStringToAnsiString(&Ansi[0], &Unicode, FALSE);
+    ASSERT(NT_SUCCESS(status));
+
+    Ansi[0].Length = (USHORT)Length * sizeof (CHAR);
+
+    return Ansi;
+
+fail2:
+    __RegistryFree(Ansi);
+
+fail1:
+    return NULL;
+}
+
+static PANSI_STRING
+RegistryMultiSzToAnsi(
+    IN  PWCHAR      Buffer
+    )
+{
+    PANSI_STRING    Ansi;
+    LONG            Index;
+    LONG            Count;
+    NTSTATUS        status;
+
+    Index = 0;
+    Count = 0;
+    for (;;) {
+        ULONG   Length;
+
+        Length = (ULONG)wcslen(&Buffer[Index]);
+        if (Length == 0)
+            break;
+
+        Index += Length + 1;
+        Count++;
+    }
+
+    Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * (Count + 1));
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi == NULL)
+        goto fail1;
+
+    for (Index = 0; Index < Count; Index++) {
+        ULONG           Length;
+        UNICODE_STRING  Unicode;
+
+        Length = (ULONG)wcslen(Buffer);
+        Ansi[Index].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+        Ansi[Index].Buffer = __RegistryAllocate(Ansi[Index].MaximumLength);
+
+        status = STATUS_NO_MEMORY;
+        if (Ansi[Index].Buffer == NULL)
+            goto fail2;
+
+        RtlInitUnicodeString(&Unicode, Buffer);
+
+        status = RtlUnicodeStringToAnsiString(&Ansi[Index], &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi[Index].Length = (USHORT)Length * sizeof (CHAR);
+        Buffer += Length + 1;
+    }
+
+    return Ansi;
+
+fail2:
+    while (--Index >= 0)
+        __RegistryFree(Ansi[Index].Buffer);
+
+    __RegistryFree(Ansi);
+
+fail1:
+    return NULL;
+}
+
+NTSTATUS
+RegistryQuerySzValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PULONG                      Type OPTIONAL,
+    OUT PANSI_STRING                *Array
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Value;
+    ULONG                           Size;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    Value = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Value == NULL)
+        goto fail3;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             Value,
+                             Size,
+                             &Size);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    switch (Value->Type) {
+    case REG_SZ:
+        status = STATUS_NO_MEMORY;
+        *Array = RegistrySzToAnsi((PWCHAR)Value->Data);
+        break;
+
+    case REG_MULTI_SZ:
+        status = STATUS_NO_MEMORY;
+        *Array = RegistryMultiSzToAnsi((PWCHAR)Value->Data);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        *Array = NULL;
+        break;
+    }
+
+    if (*Array == NULL)
+        goto fail5;
+
+    if (Type != NULL)
+        *Type = Value->Type;
+
+    __RegistryFree(Value);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Value);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQueryBinaryValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PVOID                       *Buffer,
+    OUT PULONG                      Length
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    ULONG                           Size;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    Partial = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail3;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             Partial,
+                             Size,
+                             &Size);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    switch (Partial->Type) {
+    case REG_BINARY:
+        *Buffer = __RegistryAllocate(Partial->DataLength);
+
+        status = STATUS_NO_MEMORY;
+        if (*Buffer == NULL)
+            break;
+
+        *Length = Partial->DataLength;
+        RtlCopyMemory(*Buffer, Partial->Data, Partial->DataLength);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        *Buffer = NULL;
+        break;
+    }
+
+    if (*Buffer == NULL)
+        goto fail5;
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Partial);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryUpdateBinaryValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  PVOID                       Buffer,
+    IN  ULONG                       Length
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, 
Data) +
+                                 Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail2;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_BINARY;
+    Partial->DataLength = Length;
+    RtlCopyMemory(Partial->Data, Buffer, Partial->DataLength);
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+
+    return status;
+}
+
+NTSTATUS
+RegistryQueryKeyName(
+    IN  HANDLE              Key,
+    OUT PANSI_STRING        *Array
+    )
+{
+    PKEY_NAME_INFORMATION   Value;
+    ULONG                   Size;
+    NTSTATUS                status;
+
+    status = ZwQueryKey(Key,
+                        KeyNameInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+    // Name information is not intrinsically NULL terminated
+#pragma prefast(suppress:6102)
+    Value = __RegistryAllocate(Size + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Value == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyNameInformation,
+                        Value,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Value->Name[Value->NameLength / sizeof (WCHAR)] = L'\0';
+    *Array = RegistrySzToAnsi((PWCHAR)Value->Name);
+
+    status = STATUS_NO_MEMORY;
+    if (*Array == NULL)
+        goto fail4;
+
+    __RegistryFree(Value);
+
+    return STATUS_SUCCESS;
+
+fail4:
+fail3:
+    __RegistryFree(Value);
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQuerySystemStartOption(
+    IN  PCHAR                       Prefix,
+    OUT PANSI_STRING                *Value
+    )
+{
+    UNICODE_STRING                  Unicode;
+    HANDLE                          Key;
+    PANSI_STRING                    Ansi;
+    ULONG                           Length;
+    PCHAR                           Option;
+    PCHAR                           Context;
+    NTSTATUS                        status;
+
+    RtlInitUnicodeString(&Unicode, 
L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control");
+
+    status = RegistryOpenKey(NULL, &Unicode, KEY_READ, &Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryQuerySzValue(Key, "SystemStartOptions", NULL, &Ansi);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    // SystemStartOptions is a space separated list of options.
+    // Scan it looking for the one we want.
+    Length = (ULONG)strlen(Prefix);
+
+    Option = __strtok_r(Ansi[0].Buffer, " ", &Context);
+    while (Option != NULL) {
+        if (strncmp(Prefix, Option, Length) == 0)
+            goto found;
+
+        Option = __strtok_r(NULL, " ", &Context);
+    }
+
+    status = STATUS_OBJECT_NAME_NOT_FOUND;
+    goto fail3;
+
+found:
+    *Value = __RegistryAllocate(sizeof (ANSI_STRING) * 2);
+
+    status = STATUS_NO_MEMORY;
+    if (*Value == NULL)
+        goto fail4;
+
+    Length = (ULONG)strlen(Option);
+    (*Value)[0].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+    (*Value)[0].Buffer = __RegistryAllocate((*Value)[0].MaximumLength);
+
+    status = STATUS_NO_MEMORY;
+    if ((*Value)[0].Buffer == NULL)
+        goto fail5;
+
+    RtlCopyMemory((*Value)[0].Buffer, Option, Length * sizeof (CHAR));
+
+    (*Value)[0].Length = (USHORT)Length * sizeof (CHAR);
+
+    RegistryFreeSzValue(Ansi);
+
+    ZwClose(Key);
+
+    return STATUS_SUCCESS;
+
+fail5:
+    __RegistryFree(*Value);
+
+fail4:
+fail3:
+    RegistryFreeSzValue(Ansi);
+
+fail2:
+    ZwClose(Key);
+
+fail1:
+    return status;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToSz(
+    PANSI_STRING                    Ansi
+    )
+{
+    ULONG                           Length;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    UNICODE_STRING                  Unicode;
+    NTSTATUS                        status;
+
+    Length = Ansi->Length + 1;
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, 
Data) +
+                                 Length * sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail1;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_SZ;
+    Partial->DataLength = Length * sizeof (WCHAR);
+
+    Unicode.MaximumLength = (UCHAR)Partial->DataLength;
+    Unicode.Buffer = (PWCHAR)Partial->Data;
+    Unicode.Length = 0;
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, Ansi, FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    return Partial;
+
+fail2:
+    __RegistryFree(Partial);
+
+fail1:
+    return NULL;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToMultiSz(
+    PANSI_STRING                    Ansi
+    )
+{
+    ULONG                           Length;
+    ULONG                           Index;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    UNICODE_STRING                  Unicode;
+    NTSTATUS                        status;
+
+    Length = 1;
+    for (Index = 0; Ansi[Index].Buffer != NULL; Index++)
+        Length += Ansi[Index].Length + 1;
+
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, 
Data) +
+                               Length * sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail1;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_MULTI_SZ;
+    Partial->DataLength = Length * sizeof (WCHAR);
+
+    Unicode.MaximumLength = (USHORT)Partial->DataLength;
+    Unicode.Buffer = (PWCHAR)Partial->Data;
+    Unicode.Length = 0;
+
+    for (Index = 0; Ansi[Index].Buffer != NULL; Index++) {
+        status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi[Index], FALSE);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+
+        Length = Unicode.Length / sizeof (WCHAR);
+
+        ASSERT3U(Unicode.MaximumLength, >=, (Length + 1) * sizeof (WCHAR));
+        Unicode.MaximumLength -= (USHORT)((Length + 1) * sizeof (WCHAR));
+        Unicode.Buffer += Length + 1;
+        Unicode.Length = 0;
+    }
+    *Unicode.Buffer = L'\0';
+
+    return Partial;
+
+fail2:
+    __RegistryFree(Partial);
+
+fail1:
+    return NULL;
+}
+
+NTSTATUS
+RegistryUpdateSzValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  ULONG                       Type,
+    IN  PANSI_STRING                Array
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    switch (Type) {
+    case REG_SZ:
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToSz(Array);
+        break;
+
+    case REG_MULTI_SZ:
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToMultiSz(Array);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        Partial = NULL;
+        break;
+    }
+
+    if (Partial == NULL)
+        goto fail2;
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+VOID
+RegistryFreeSzValue(
+    IN  PANSI_STRING    Array
+    )
+{
+    ULONG               Index;
+
+    if (Array == NULL)
+        return;
+
+    for (Index = 0; Array[Index].Buffer != NULL; Index++)
+        __RegistryFree(Array[Index].Buffer);
+
+    __RegistryFree(Array);
+}
+
+VOID
+RegistryFreeBinaryValue(
+    IN  PVOID   Buffer
+    )
+{
+    __RegistryFree(Buffer);
+}
+
+VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    )
+{
+    ZwClose(Key);
+}
diff --git a/src/xendisk/registry.h b/src/xendisk/registry.h
new file mode 100644
index 0000000..24c0bf3
--- /dev/null
+++ b/src/xendisk/registry.h
@@ -0,0 +1,203 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * *   Redistributions of source code must retain the above
+ *     copyright notice, this list of conditions and the
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the
+ *     following disclaimer in the documentation and/or other
+ *     materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENDISK_REGISTRY_H
+#define _XENDISK_REGISTRY_H
+
+#include <ntddk.h>
+
+extern NTSTATUS
+RegistryInitialize(
+    IN PUNICODE_STRING  Path
+    );
+
+extern VOID
+RegistryTeardown(
+    VOID
+    );
+
+extern NTSTATUS
+RegistryOpenKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryCreateKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ULONG           Options,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryOpenServiceKey(
+    IN  ACCESS_MASK DesiredAccess,
+    OUT PHANDLE     Key
+    );
+
+extern NTSTATUS
+RegistryCreateServiceKey(
+    OUT PHANDLE     Key
+    );
+
+extern NTSTATUS
+RegistryOpenSoftwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryOpenHardwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryOpenSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name,
+    IN  ACCESS_MASK DesiredAccess,
+    OUT PHANDLE     SubKey
+    );
+
+extern NTSTATUS
+RegistryCreateSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name,
+    IN  ULONG       Options,
+    OUT PHANDLE     SubKey
+    );
+
+extern NTSTATUS
+RegistryDeleteSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name
+    );
+
+extern NTSTATUS
+RegistryEnumerateSubKeys(
+    IN  HANDLE      Key,
+    IN  NTSTATUS    (*Callback)(PVOID, HANDLE, PANSI_STRING),
+    IN  PVOID       Context
+    );
+
+extern NTSTATUS
+RegistryEnumerateValues(
+    IN  HANDLE      Key,
+    IN  NTSTATUS    (*Callback)(PVOID, HANDLE, PANSI_STRING, ULONG),
+    IN  PVOID       Context
+    );
+
+extern NTSTATUS
+RegistryDeleteValue(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name
+    );
+
+extern NTSTATUS
+RegistryQueryDwordValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PULONG          Value
+    );
+
+extern NTSTATUS
+RegistryUpdateDwordValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Value
+    );
+
+extern NTSTATUS
+RegistryQuerySzValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PULONG          Type OPTIONAL,
+    OUT PANSI_STRING    *Array
+    );
+
+extern NTSTATUS
+RegistryQueryBinaryValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PVOID           *Buffer,
+    OUT PULONG          Length
+    );
+
+extern NTSTATUS
+RegistryUpdateBinaryValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length
+    );
+
+extern NTSTATUS
+RegistryQueryKeyName(
+    IN  HANDLE              Key,
+    OUT PANSI_STRING        *Array
+    );
+
+extern NTSTATUS
+RegistryQuerySystemStartOption(
+    IN  PCHAR           Name,
+    OUT PANSI_STRING    *Option
+    );
+
+extern VOID
+RegistryFreeSzValue(
+    IN  PANSI_STRING    Array
+    );
+
+extern VOID
+RegistryFreeBinaryValue(
+    IN  PVOID           Buffer
+    );
+
+extern NTSTATUS
+RegistryUpdateSzValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Type,
+    IN  PANSI_STRING    Array
+    );
+
+extern VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    );
+
+#endif  // _XENDISK_REGISTRY_H
diff --git a/src/xendisk/thread.c b/src/xendisk/thread.c
index b7f3b2d..335b55a 100644
--- a/src/xendisk/thread.c
+++ b/src/xendisk/thread.c
@@ -36,10 +36,10 @@
 #include "assert.h"
 #include "util.h"
 
-#define THREAD_POOL 'ERHT'
+#define THREAD_TAG 'ERHT'
 
 struct _XENDISK_THREAD {
-    XENDISK_THREAD_FUNCTION Function;
+    XENDISK_THREAD_FUNCTION  Function;
     PVOID                   Context;
     KEVENT                  Event;
     BOOLEAN                 Alerted;
@@ -52,7 +52,7 @@ __ThreadAllocate(
     IN  ULONG   Length
     )
 {
-    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
THREAD_POOL);
+    return __AllocatePoolWithTag(NonPagedPool, Length, THREAD_TAG);
 }
 
 static FORCEINLINE VOID
@@ -60,7 +60,7 @@ __ThreadFree(
     IN  PVOID   Buffer
     )
 {
-    __FreePoolWithTag(Buffer, THREAD_POOL);
+    __FreePoolWithTag(Buffer, THREAD_TAG);
 }
 
 static FORCEINLINE VOID
diff --git a/src/xendisk/util.h b/src/xendisk/util.h
index 99a5b5f..38f2aa7 100644
--- a/src/xendisk/util.h
+++ b/src/xendisk/util.h
@@ -29,194 +29,330 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _UTIL_H
-#define _UTIL_H
+#ifndef _XENDISK_UTIL_H
+#define _XENDISK_UTIL_H
 
 #include <ntddk.h>
 
 #include "assert.h"
 
-typedef struct _NON_PAGED_BUFFER_HEADER {
-    SIZE_T  Length;
-    ULONG   Tag;
-} NON_PAGED_BUFFER_HEADER, *PNON_PAGED_BUFFER_HEADER;
+#define        P2ROUNDUP(_x, _a)   \
+        (-(-(_x) & -(_a)))
 
-typedef struct _NON_PAGED_BUFFER_TRAILER {
-    ULONG   Tag;
-} NON_PAGED_BUFFER_TRAILER, *PNON_PAGED_BUFFER_TRAILER;
-
-static FORCEINLINE PVOID
-__AllocateNonPagedPoolWithTag(
-    IN  PCHAR                   Caller,
-    IN  ULONG                   Line,
-    IN  SIZE_T                  Length,
-    IN  ULONG                   Tag
+static FORCEINLINE LONG
+__ffs(
+    IN  unsigned long long  mask
     )
 {
-    PUCHAR                      Buffer;
-    PNON_PAGED_BUFFER_HEADER    Header;
-    PNON_PAGED_BUFFER_TRAILER   Trailer;
-
-    ASSERT3S(Length, !=, 0);
-
-    Buffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
-                                   sizeof (NON_PAGED_BUFFER_HEADER) +
-                                   Length +
-                                   sizeof (NON_PAGED_BUFFER_TRAILER),
-                                   Tag);
-    if (Buffer == NULL) {
-        Warning("%s:%u : AllocFailed %d bytes, %08x tag\n", Caller, Line, 
Length, Tag);
-        goto done;
-    }
+    unsigned char           *array = (unsigned char *)&mask;
+    unsigned int            byte;
+    unsigned int            bit;
+    unsigned char           val;
 
-    RtlZeroMemory(Buffer,
-                  sizeof (NON_PAGED_BUFFER_HEADER) +
-                  Length +
-                  sizeof (NON_PAGED_BUFFER_TRAILER));
+    val = 0;
 
-    Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
-    Header->Length = Length;
-    Header->Tag = Tag;
+    byte = 0;
+    while (byte < 8) {
+        val = array[byte];
 
-    Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+        if (val != 0)
+            break;
 
-    Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
-    Trailer->Tag = Tag;
+        byte++;
+    }
+    if (byte == 8)
+        return -1;
 
-done:
-    return Buffer;
+    bit = 0;
+    while (bit < 8) {
+        if (val & 0x01)
+            break;
+
+        val >>= 1;
+        bit++;
+    }
+
+    return (byte * 8) + bit;
 }
 
+#define __ffu(_mask)  \
+        __ffs(~(_mask))
+
 static FORCEINLINE VOID
-__FreePoolWithTag(
-    IN  PVOID                   _Buffer,
-    IN  ULONG                   Tag
+__CpuId(
+    IN  ULONG   Leaf,
+    OUT PULONG  EAX OPTIONAL,
+    OUT PULONG  EBX OPTIONAL,
+    OUT PULONG  ECX OPTIONAL,
+    OUT PULONG  EDX OPTIONAL
     )
 {
-    PUCHAR                      Buffer = (PUCHAR)_Buffer;
-    SIZE_T                      Length;
-    PNON_PAGED_BUFFER_HEADER    Header;
-    PNON_PAGED_BUFFER_TRAILER   Trailer;
+    ULONG       Value[4] = {0};
 
-    ASSERT3P(Buffer, !=, NULL);
+    __cpuid(Value, Leaf);
 
-    Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+    if (EAX)
+        *EAX = Value[0];
 
-    Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
-    ASSERT3U(Tag, ==, Header->Tag);
-    Length = Header->Length;
+    if (EBX)
+        *EBX = Value[1];
 
-    Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+    if (ECX)
+        *ECX = Value[2];
 
-    Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
-    ASSERT3U(Tag, ==, Trailer->Tag);
+    if (EDX)
+        *EDX = Value[3];
+}
 
-    Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+static FORCEINLINE LONG
+__InterlockedAdd(
+    IN  LONG    *Value,
+    IN  LONG    Delta
+    )
+{
+    LONG        New;
+    LONG        Old;
 
-    RtlFillMemory(Buffer,
-                  sizeof (NON_PAGED_BUFFER_HEADER) +
-                  Length +
-                  sizeof (NON_PAGED_BUFFER_TRAILER),
-                  0xAA);
+    do {
+        Old = *Value;
+        New = Old + Delta;
+    } while (InterlockedCompareExchange(Value, New, Old) != Old);
 
-    ExFreePoolWithTag(Buffer, Tag);
+    return New;
 }
 
-static FORCEINLINE PMDL
-__AllocPagesForMdl(
-    IN  SIZE_T          Size
+static FORCEINLINE LONG
+__InterlockedSubtract(
+    IN  LONG    *Value,
+    IN  LONG    Delta
     )
 {
-    PMDL                Mdl;
-    PHYSICAL_ADDRESS    LowAddr;
-    PHYSICAL_ADDRESS    HighAddr;
-    PHYSICAL_ADDRESS    SkipBytes;
+    LONG        New;
+    LONG        Old;
 
-    SkipBytes.QuadPart = 0ull;
-    HighAddr.QuadPart = ~0ull;
-
-    // try > 4GB
-    LowAddr.QuadPart = 0x100000000ull;
-    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
-    if (Mdl) {
-        if (MmGetMdlByteCount(Mdl) == Size) {
-            goto done;
-        }
-        MmFreePagesFromMdl(Mdl);
-        ExFreePool(Mdl);
-        Mdl = NULL;
-    }
+    do {
+        Old = *Value;
+        New = Old - Delta;
+    } while (InterlockedCompareExchange(Value, New, Old) != Old);
 
-    // try > 2GB
-    LowAddr.QuadPart = 0x80000000ull;
-    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
-    if (Mdl) {
-        if (MmGetMdlByteCount(Mdl) == Size) {
-            goto done;
-        }
-        MmFreePagesFromMdl(Mdl);
-        ExFreePool(Mdl);
-        Mdl = NULL;
-    }
+    return New;
+}
 
-    // try anywhere
-    LowAddr.QuadPart = 0ull;
-    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
-    // Mdl byte count gets checked again after this returns
+static FORCEINLINE PVOID
+__AllocatePoolWithTag(
+    IN  POOL_TYPE   PoolType,
+    IN  SIZE_T      NumberOfBytes,
+    IN  ULONG       Tag
+    )
+{
+    PUCHAR          Buffer;
 
-done:
-    return Mdl;
+    __analysis_assume(PoolType == NonPagedPool ||
+                      PoolType == PagedPool);
+
+    Buffer = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
+    if (Buffer == NULL)
+        return NULL;
+
+    RtlZeroMemory(Buffer, NumberOfBytes);
+    return Buffer;
 }
-static FORCEINLINE PVOID
-___AllocPages(
-    IN  PCHAR           Caller,
-    IN  ULONG           Line,
-    IN  SIZE_T          Size,
-    OUT PMDL*           Mdl
+
+static FORCEINLINE VOID
+__FreePoolWithTag(
+    IN  PVOID   Buffer,
+    IN  ULONG   Tag
     )
 {
-    PVOID               Buffer;
+    ExFreePoolWithTag(Buffer, Tag);
+}
 
-    *Mdl = __AllocPagesForMdl(Size);
-    if (*Mdl == NULL) {
-        Warning("%s:%u : MmAllocatePagesForMdlEx Failed %d bytes\n", Caller, 
Line, Size);
+static FORCEINLINE PMDL
+__AllocatePages(
+    IN  ULONG           Count
+    )
+{
+    PHYSICAL_ADDRESS    LowAddress;
+    PHYSICAL_ADDRESS    HighAddress;
+    LARGE_INTEGER       SkipBytes;
+    SIZE_T              TotalBytes;
+    PMDL                Mdl;
+    PUCHAR              MdlMappedSystemVa;
+    NTSTATUS            status;
+
+    LowAddress.QuadPart = 0ull;
+    HighAddress.QuadPart = ~0ull;
+    SkipBytes.QuadPart = 0ull;
+    TotalBytes = (SIZE_T)PAGE_SIZE * Count;
+
+    Mdl = MmAllocatePagesForMdlEx(LowAddress,
+                                  HighAddress,
+                                  SkipBytes,
+                                  TotalBytes,
+                                  MmCached,
+                                  MM_DONT_ZERO_ALLOCATION);
+
+    status = STATUS_NO_MEMORY;
+    if (Mdl == NULL)
         goto fail1;
-    }
 
-    if (MmGetMdlByteCount(*Mdl) != Size) {
-        Warning("%s:%u : %d bytes != %d bytes requested\n", Caller, Line, 
MmGetMdlByteCount(*Mdl), Size);
+    if (Mdl->ByteCount < TotalBytes)
         goto fail2;
-    }
 
-    Buffer = MmMapLockedPagesSpecifyCache(*Mdl, KernelMode, MmCached, NULL, 
FALSE, NormalPagePriority);
-    if (Buffer == NULL) {
-        Warning("%s:%u : MmMapLockedPagesSpecifyCache Failed %d bytes\n", 
Caller, Line, Size);
+    ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
+                             MDL_PARTIAL_HAS_BEEN_MAPPED |
+                             MDL_PARTIAL |
+                             MDL_PARENT_MAPPED_SYSTEM_VA |
+                             MDL_SOURCE_IS_NONPAGED_POOL |
+                             MDL_IO_SPACE)) == 0);
+
+    MdlMappedSystemVa = MmMapLockedPagesSpecifyCache(Mdl,
+                                                     KernelMode,
+                                                                            
MmCached,
+                                                                            
NULL,
+                                                                            
FALSE,
+                                                                            
NormalPagePriority);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (MdlMappedSystemVa == NULL)
         goto fail3;
-    }
 
-    return Buffer;
+    ASSERT3P(MdlMappedSystemVa, ==, Mdl->MappedSystemVa);
+
+    RtlZeroMemory(MdlMappedSystemVa, Mdl->ByteCount);
+
+    return Mdl;
 
 fail3:
+    Error("fail3\n");
+
 fail2:
-    MmFreePagesFromMdl(*Mdl);
-    ExFreePool(*Mdl);
+    Error("fail2\n");
+
+    MmFreePagesFromMdl(Mdl);
+    ExFreePool(Mdl);
+
 fail1:
-    *Mdl = NULL;
+    Error("fail1 (%08x)\n", status);
+
     return NULL;
 }
-#define __AllocPages(Size, Mdl) ___AllocPages(__FUNCTION__, __LINE__, Size, 
Mdl)
+
+#define __AllocatePage()    __AllocatePages(1)
 
 static FORCEINLINE VOID
 __FreePages(
-    IN  PVOID           Buffer,
-    IN  PMDL            Mdl
+    IN PMDL    Mdl
     )
 {
-    if (Buffer && Mdl) {
-        MmUnmapLockedPages(Buffer, Mdl);
-        MmFreePagesFromMdl(Mdl);
-        ExFreePool(Mdl);
-    }
+    PUCHAR     MdlMappedSystemVa;
+
+    ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+    MdlMappedSystemVa = Mdl->MappedSystemVa;
+
+    MmUnmapLockedPages(MdlMappedSystemVa, Mdl);
+
+    MmFreePagesFromMdl(Mdl);
+}
+
+#define __FreePage(_Mdl)    __FreePages(_Mdl)
+
+static FORCEINLINE PCHAR
+__strtok_r(
+    IN      PCHAR   Buffer,
+    IN      PCHAR   Delimiter,
+    IN OUT  PCHAR   *Context
+    )
+{
+    PCHAR           Token;
+    PCHAR           End;
+
+    if (Buffer != NULL)
+        *Context = Buffer;
+
+    Token = *Context;
+
+    if (Token == NULL)
+        return NULL;
+
+    while (*Token != '\0' &&
+           strchr(Delimiter, *Token) != NULL)
+        Token++;
+
+    if (*Token == '\0')
+        return NULL;
+
+    End = Token + 1;
+    while (*End != '\0' &&
+           strchr(Delimiter, *End) == NULL)
+        End++;
+
+    if (*End != '\0')
+        *End++ = '\0';
+
+    *Context = End;
+
+    return Token;
+}
+
+static FORCEINLINE PWCHAR
+__wcstok_r(
+    IN      PWCHAR  Buffer,
+    IN      PWCHAR  Delimiter,
+    IN OUT  PWCHAR  *Context
+    )
+{
+    PWCHAR          Token;
+    PWCHAR          End;
+
+    if (Buffer != NULL)
+        *Context = Buffer;
+
+    Token = *Context;
+
+    if (Token == NULL)
+        return NULL;
+
+    while (*Token != L'\0' &&
+           wcschr(Delimiter, *Token) != NULL)
+        Token++;
+
+    if (*Token == L'\0')
+        return NULL;
+
+    End = Token + 1;
+    while (*End != L'\0' &&
+           wcschr(Delimiter, *End) == NULL)
+        End++;
+
+    if (*End != L'\0')
+        *End++ = L'\0';
+
+    *Context = End;
+
+    return Token;
+}
+
+static FORCEINLINE CHAR
+__toupper(
+    IN  CHAR    Character
+    )
+{
+    if (Character < 'a' || Character > 'z')
+        return Character;
+
+    return 'A' + Character - 'a';
+}
+
+static FORCEINLINE CHAR
+__tolower(
+    IN  CHAR    Character
+    )
+{
+    if (Character < 'A' || Character > 'Z')
+        return Character;
+
+    return 'a' + Character - 'A';
 }
 
-#endif  // _UTIL_H
+#endif  // _XENDISK_UTIL_H
diff --git a/src/xenvbd.inf b/src/xenvbd.inf
index 7037e45..68e7a15 100644
--- a/src/xenvbd.inf
+++ b/src/xenvbd.inf
@@ -83,6 +83,10 @@ StartType=%SERVICE_BOOT_START%
 ErrorControl=%SERVICE_ERROR_NORMAL%
 ServiceBinary=%12%\xendisk.sys
 LoadOrderGroup="Scsi Miniport"
+AddReg=XenDisk_Parameters
+
+[XenDisk_Parameters]
+HKR,"Parameters",,0x00000010
 
 [XenVbd_Service] 
 DisplayName=%XenVbdName%
diff --git a/vs2012/xendisk/xendisk.vcxproj b/vs2012/xendisk/xendisk.vcxproj
index c68e886..478b7f0 100644
--- a/vs2012/xendisk/xendisk.vcxproj
+++ b/vs2012/xendisk/xendisk.vcxproj
@@ -75,6 +75,7 @@
                <ClCompile Include="../../src/xendisk/driver.c" />
                <ClCompile Include="../../src/xendisk/fdo.c" />
                <ClCompile Include="../../src/xendisk/pdo.c" />
+               <ClCompile Include="../../src/xendisk/registry.c" />
                <ClCompile Include="../../src/xendisk/thread.c" />
        </ItemGroup>
        <ItemGroup>
diff --git a/vs2013/xendisk/xendisk.vcxproj b/vs2013/xendisk/xendisk.vcxproj
index f5c341e..73812d8 100644
--- a/vs2013/xendisk/xendisk.vcxproj
+++ b/vs2013/xendisk/xendisk.vcxproj
@@ -103,6 +103,7 @@
     <ClCompile Include="../../src/xendisk/driver.c" />
     <ClCompile Include="../../src/xendisk/fdo.c" />
     <ClCompile Include="../../src/xendisk/pdo.c" />
+    <ClCompile Include="../../src/xendisk/registry.c" />
     <ClCompile Include="../../src/xendisk/thread.c" />
   </ItemGroup>
   <ItemGroup>
-- 
2.5.3


_______________________________________________
win-pv-devel mailing list
win-pv-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/cgi-bin/mailman/listinfo/win-pv-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.