reactos/lib/rtl/rxact.c
Amine Khaldi 527f2f9057 [SHELL/EXPERIMENTS]
* Create a branch for some evul shell experiments.

svn path=/branches/shell-experiments/; revision=61927
2014-02-02 19:37:27 +00:00

697 lines
20 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: lib/rtl/rxact.c
* PURPOSE: Registry Transaction API
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#include <ndk/cmfuncs.h>
#define NDEBUG
#include <debug.h>
#define RXACT_DEFAULT_BUFFER_SIZE (4 * PAGE_SIZE)
typedef struct _RXACT_INFO
{
ULONG Revision;
ULONG Unknown1;
ULONG Unknown2;
} RXACT_INFO, *PRXACT_INFO;
typedef struct _RXACT_DATA
{
ULONG ActionCount;
ULONG BufferSize;
ULONG CurrentSize;
} RXACT_DATA, *PRXACT_DATA;
typedef struct _RXACT_CONTEXT
{
HANDLE RootDirectory;
HANDLE KeyHandle;
BOOLEAN CanUseHandles;
PRXACT_DATA Data;
} RXACT_CONTEXT, *PRXACT_CONTEXT;
typedef struct _RXACT_ACTION
{
ULONG Size;
ULONG Type;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
HANDLE KeyHandle;
ULONG ValueType;
ULONG ValueDataSize;
PVOID ValueData;
} RXACT_ACTION, *PRXACT_ACTION;
enum
{
RXactDeleteKey = 1,
RXactSetValueKey = 2,
};
#define ALIGN_UP_BY ROUND_UP
/* FUNCTIONS *****************************************************************/
static
VOID
NTAPI
RXactInitializeContext(
PRXACT_CONTEXT Context,
HANDLE RootDirectory,
HANDLE KeyHandle)
{
Context->Data = NULL;
Context->RootDirectory = RootDirectory;
Context->CanUseHandles = TRUE;
Context->KeyHandle = KeyHandle;
}
static
NTSTATUS
NTAPI
RXactpOpenTargetKey(
HANDLE RootDirectory,
ULONG ActionType,
PUNICODE_STRING KeyName,
PHANDLE KeyHandle)
{
NTSTATUS Status;
ULONG Disposition;
OBJECT_ATTRIBUTES ObjectAttributes;
/* Check what kind of action this is */
if (ActionType == RXactDeleteKey)
{
/* This is a delete, so open the key for delete */
InitializeObjectAttributes(&ObjectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE,
RootDirectory,
NULL);
Status = ZwOpenKey(KeyHandle, DELETE, &ObjectAttributes);
}
else if (ActionType == RXactSetValueKey)
{
/* This is a create, so open or create with write access */
InitializeObjectAttributes(&ObjectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
RootDirectory,
NULL);
Status = ZwCreateKey(KeyHandle,
KEY_WRITE,
&ObjectAttributes,
0,
NULL,
0,
&Disposition);
}
else
{
return STATUS_INVALID_PARAMETER;
}
return Status;
}
static
NTSTATUS
NTAPI
RXactpCommit(
PRXACT_CONTEXT Context)
{
PRXACT_DATA Data;
PRXACT_ACTION Action;
NTSTATUS Status, TmpStatus;
HANDLE KeyHandle;
ULONG i;
Data = Context->Data;
/* The first action record starts after the data header */
Action = (PRXACT_ACTION)(Data + 1);
/* Loop all recorded actions */
for (i = 0; i < Data->ActionCount; i++)
{
/* Translate relative offsets to actual pointers */
Action->KeyName.Buffer = (PWSTR)((PUCHAR)Data + (ULONG_PTR)Action->KeyName.Buffer);
Action->ValueName.Buffer = (PWSTR)((PUCHAR)Data + (ULONG_PTR)Action->ValueName.Buffer);
Action->ValueData = (PUCHAR)Data + (ULONG_PTR)Action->ValueData;
/* Check what kind of action this is */
if (Action->Type == RXactDeleteKey)
{
/* This is a delete action. Check if we can use a handle */
if ((Action->KeyHandle != INVALID_HANDLE_VALUE) && Context->CanUseHandles)
{
/* Delete the key by the given handle */
Status = ZwDeleteKey(Action->KeyHandle);
if (!NT_SUCCESS(Status))
{
return Status;
}
}
else
{
/* We cannot use a handle, open the key first by it's name */
Status = RXactpOpenTargetKey(Context->RootDirectory,
RXactDeleteKey,
&Action->KeyName,
&KeyHandle);
if (NT_SUCCESS(Status))
{
Status = ZwDeleteKey(KeyHandle);
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
if (!NT_SUCCESS(Status))
{
return Status;
}
}
else
{
/* Failed to open the key, it's ok, if it was not found */
if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
return Status;
}
}
}
else if (Action->Type == RXactSetValueKey)
{
/* This is a set action. Check if we can use a handle */
if ((Action->KeyHandle != INVALID_HANDLE_VALUE) && Context->CanUseHandles)
{
/* Set the key value using the given key handle */
Status = ZwSetValueKey(Action->KeyHandle,
&Action->ValueName,
0,
Action->ValueType,
Action->ValueData,
Action->ValueDataSize);
if (!NT_SUCCESS(Status))
{
return Status;
}
}
else
{
/* We cannot use a handle, open the key first by it's name */
Status = RXactpOpenTargetKey(Context->RootDirectory,
RXactSetValueKey,
&Action->KeyName,
&KeyHandle);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Set the key value */
Status = ZwSetValueKey(KeyHandle,
&Action->ValueName,
0,
Action->ValueType,
Action->ValueData,
Action->ValueDataSize);
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
if (!NT_SUCCESS(Status))
{
return Status;
}
}
}
else
{
ASSERT(FALSE);
return STATUS_INVALID_PARAMETER;
}
/* Go to the next action record */
Action = (PRXACT_ACTION)((PUCHAR)Action + Action->Size);
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlStartRXact(
PRXACT_CONTEXT Context)
{
PRXACT_DATA Buffer;
/* We must not have a buffer yet */
if (Context->Data != NULL)
{
return STATUS_RXACT_INVALID_STATE;
}
/* Allocate a buffer */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, RXACT_DEFAULT_BUFFER_SIZE);
if (Buffer == NULL)
{
return STATUS_NO_MEMORY;
}
/* Initialize the buffer */
Buffer->ActionCount = 0;
Buffer->BufferSize = RXACT_DEFAULT_BUFFER_SIZE;
Buffer->CurrentSize = sizeof(RXACT_DATA);
Context->Data = Buffer;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlAbortRXact(
PRXACT_CONTEXT Context)
{
/* We must have a data buffer */
if (Context->Data == NULL)
{
return STATUS_RXACT_INVALID_STATE;
}
/* Free the buffer */
RtlFreeHeap(RtlGetProcessHeap(), 0, Context->Data);
/* Reinitialize the context */
RXactInitializeContext(Context, Context->RootDirectory, Context->KeyHandle);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlInitializeRXact(
HANDLE RootDirectory,
BOOLEAN Commit,
PRXACT_CONTEXT *OutContext)
{
NTSTATUS Status, TmpStatus;
PRXACT_CONTEXT Context;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
KEY_VALUE_BASIC_INFORMATION KeyValueBasicInfo;
UNICODE_STRING ValueName;
UNICODE_STRING KeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
RXACT_INFO TransactionInfo;
ULONG Disposition;
ULONG ValueType;
ULONG ValueDataLength;
ULONG Length;
HANDLE KeyHandle;
/* Open or create the 'RXACT' key in the root directory */
RtlInitUnicodeString(&KeyName, L"RXACT");
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
RootDirectory,
NULL);
Status = ZwCreateKey(&KeyHandle,
KEY_READ | KEY_WRITE | DELETE,
&ObjectAttributes,
0,
NULL,
0,
&Disposition);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Allocate a new context */
Context = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Context));
*OutContext = Context;
if (Context == NULL)
{
TmpStatus = ZwDeleteKey(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
return STATUS_NO_MEMORY;
}
/* Initialize the context */
RXactInitializeContext(Context, RootDirectory, KeyHandle);
/* Check if we created a new key */
if (Disposition == REG_CREATED_NEW_KEY)
{
/* The key is new, set the default value */
TransactionInfo.Revision = 1;
RtlInitUnicodeString(&ValueName, NULL);
Status = ZwSetValueKey(KeyHandle,
&ValueName,
0,
REG_NONE,
&TransactionInfo,
sizeof(TransactionInfo));
if (!NT_SUCCESS(Status))
{
TmpStatus = ZwDeleteKey(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
RtlFreeHeap(RtlGetProcessHeap(), 0, *OutContext);
return Status;
}
return STATUS_RXACT_STATE_CREATED;
}
else
{
/* The key exited, get the default key value */
ValueDataLength = sizeof(TransactionInfo);
Status = RtlpNtQueryValueKey(KeyHandle,
&ValueType,
&TransactionInfo,
&ValueDataLength,
0);
if (!NT_SUCCESS(Status))
{
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
RtlFreeHeap(RtlGetProcessHeap(), 0, Context);
return Status;
}
/* Check if the value date is valid */
if ((ValueDataLength != sizeof(TransactionInfo)) ||
(TransactionInfo.Revision != 1))
{
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
RtlFreeHeap(RtlGetProcessHeap(), 0, Context);
return STATUS_UNKNOWN_REVISION;
}
/* Query the 'Log' key value */
RtlInitUnicodeString(&ValueName, L"Log");
Status = ZwQueryValueKey(KeyHandle,
&ValueName,
KeyValueBasicInformation,
&KeyValueBasicInfo,
sizeof(KeyValueBasicInfo),
&Length);
if (!NT_SUCCESS(Status))
{
/* There is no 'Log', so we are done */
return STATUS_SUCCESS;
}
/* Check if the caller asked to commit the current state */
if (!Commit)
{
/* We have a log, that must be committed first! */
return STATUS_RXACT_COMMIT_NECESSARY;
}
/* Query the size of the 'Log' key value */
Status = ZwQueryValueKey(KeyHandle,
&ValueName,
KeyValueFullInformation,
NULL,
0,
&Length);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
return Status;
}
/* Allocate a buffer for the key value information */
KeyValueInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
if (KeyValueInformation == NULL)
{
return STATUS_NO_MEMORY;
}
/* Query the 'Log' key value */
Status = ZwQueryValueKey(KeyHandle,
&ValueName,
KeyValueFullInformation,
KeyValueInformation,
Length,
&Length);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation);
RtlFreeHeap(RtlGetProcessHeap(), 0, Context);
return Status;
}
/* Set the Data pointer to the key value data */
Context->Data = (PRXACT_DATA)((PUCHAR)KeyValueInformation +
KeyValueInformation->DataOffset);
/* This is an old log, don't use handles when committing! */
Context->CanUseHandles = FALSE;
/* Commit the data */
Status = RXactpCommit(Context);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation);
RtlFreeHeap(RtlGetProcessHeap(), 0, Context);
return Status;
}
/* Delete the old key */
Status = NtDeleteValueKey(KeyHandle, &ValueName);
ASSERT(NT_SUCCESS(Status));
/* Set the data member to the allocated buffer, so it will get freed */
Context->Data = (PRXACT_DATA)KeyValueInformation;
/* Abort the old transaction */
Status = RtlAbortRXact(Context);
ASSERT(NT_SUCCESS(Status));
return Status;
}
}
NTSTATUS
NTAPI
RtlAddAttributeActionToRXact(
PRXACT_CONTEXT Context,
ULONG ActionType,
PUNICODE_STRING KeyName,
HANDLE KeyHandle,
PUNICODE_STRING ValueName,
ULONG ValueType,
PVOID ValueData,
ULONG ValueDataSize)
{
ULONG ActionSize;
ULONG RequiredSize;
ULONG BufferSize;
ULONG CurrentOffset;
PRXACT_DATA NewData;
PRXACT_ACTION Action;
/* Validate ActionType parameter */
if ((ActionType != RXactDeleteKey) && (ActionType != RXactSetValueKey))
{
return STATUS_INVALID_PARAMETER;
}
/* Calculate the size of the new action record */
ActionSize = ALIGN_UP_BY(ValueName->Length, sizeof(ULONG)) +
ALIGN_UP_BY(ValueDataSize, sizeof(ULONG)) +
ALIGN_UP_BY(KeyName->Length, sizeof(ULONG)) +
ALIGN_UP_BY(sizeof(RXACT_ACTION), sizeof(ULONG));
/* Calculate the new buffer size we need */
RequiredSize = ActionSize + Context->Data->CurrentSize;
/* Check for integer overflow */
if (RequiredSize < ActionSize)
{
return STATUS_NO_MEMORY;
}
/* Check if the buffer is large enough */
BufferSize = Context->Data->BufferSize;
if (RequiredSize > BufferSize)
{
/* Increase by a factor of 2, until it is large enough */
while (BufferSize < RequiredSize)
{
BufferSize *= 2;
}
/* Allocate a new buffer from the heap */
NewData = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
if (NewData == NULL)
{
return STATUS_NO_MEMORY;
}
/* Copy the old buffer to the new one */
RtlCopyMemory(NewData, Context->Data, Context->Data->CurrentSize);
/* Free the old buffer and use the new one */
RtlFreeHeap(RtlGetProcessHeap(), 0, Context->Data);
Context->Data = NewData;
NewData->BufferSize = BufferSize;
}
/* Get the next action record */
Action = (RXACT_ACTION *)((PUCHAR)Context->Data + Context->Data->CurrentSize);
/* Fill in the fields */
Action->Size = ActionSize;
Action->Type = ActionType;
Action->KeyName = *KeyName;
Action->ValueName = *ValueName;
Action->ValueType = ValueType;
Action->ValueDataSize = ValueDataSize;
Action->KeyHandle = KeyHandle;
/* Copy the key name (and convert the pointer to a buffer offset) */
CurrentOffset = Context->Data->CurrentSize + sizeof(RXACT_ACTION);
Action->KeyName.Buffer = UlongToPtr(CurrentOffset);
RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset,
KeyName->Buffer,
KeyName->Length);
/* Copy the value name (and convert the pointer to a buffer offset) */
CurrentOffset += ALIGN_UP_BY(KeyName->Length, sizeof(ULONG));
Action->ValueName.Buffer = UlongToPtr(CurrentOffset);
RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset,
ValueName->Buffer,
ValueName->Length);
/* Update the offset */
CurrentOffset += ALIGN_UP_BY(ValueName->Length, sizeof(ULONG));
/* Is this a set action? */
if (ActionType == RXactSetValueKey)
{
/* Copy the key value data as well */
Action->ValueData = UlongToPtr(CurrentOffset);
RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset,
ValueData,
ValueDataSize);
CurrentOffset += ALIGN_UP_BY(ValueDataSize, sizeof(ULONG));
}
/* Update data site and action count */
Context->Data->CurrentSize = CurrentOffset;
Context->Data->ActionCount++;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlAddActionToRXact(
PRXACT_CONTEXT Context,
ULONG ActionType,
PUNICODE_STRING KeyName,
ULONG ValueType,
PVOID ValueData,
ULONG ValueDataSize)
{
UNICODE_STRING ValueName;
/* Create a key and set the default key value or delete a key. */
RtlInitUnicodeString(&ValueName, NULL);
return RtlAddAttributeActionToRXact(Context,
ActionType,
KeyName,
INVALID_HANDLE_VALUE,
&ValueName,
ValueType,
ValueData,
ValueDataSize);
}
NTSTATUS
NTAPI
RtlApplyRXactNoFlush(
PRXACT_CONTEXT Context)
{
NTSTATUS Status;
/* Commit the transaction */
Status = RXactpCommit(Context);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Reset the transaction */
Status = RtlAbortRXact(Context);
ASSERT(NT_SUCCESS(Status));
return Status;
}
NTSTATUS
NTAPI
RtlApplyRXact(
PRXACT_CONTEXT Context)
{
UNICODE_STRING ValueName;
NTSTATUS Status;
/* Temporarily safe the current transaction in the 'Log' key value */
RtlInitUnicodeString(&ValueName, L"Log");
Status = ZwSetValueKey(Context->KeyHandle,
&ValueName,
0,
REG_BINARY,
Context->Data,
Context->Data->CurrentSize);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Flush the key */
Status = NtFlushKey(Context->KeyHandle);
if (!NT_SUCCESS(Status))
{
NtDeleteValueKey(Context->KeyHandle, &ValueName);
return Status;
}
/* Now commit the transaction */
Status = RXactpCommit(Context);
if (!NT_SUCCESS(Status))
{
NtDeleteValueKey(Context->KeyHandle, &ValueName);
return Status;
}
/* Delete the 'Log' key value */
Status = NtDeleteValueKey(Context->KeyHandle, &ValueName);
ASSERT(NT_SUCCESS(Status));
/* Reset the transaction */
Status = RtlAbortRXact(Context);
ASSERT(NT_SUCCESS(Status));
return STATUS_SUCCESS;
}