Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695/* * 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,};
/* FUNCTIONS *****************************************************************/
staticVOIDNTAPIRXactInitializeContext( PRXACT_CONTEXT Context, HANDLE RootDirectory, HANDLE KeyHandle){ Context->Data = NULL; Context->RootDirectory = RootDirectory; Context->CanUseHandles = TRUE; Context->KeyHandle = KeyHandle;}
staticNTSTATUSNTAPIRXactpOpenTargetKey( 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;}
staticNTSTATUSNTAPIRXactpCommit( 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;}
NTSTATUSNTAPIRtlStartRXact( 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;}
NTSTATUSNTAPIRtlAbortRXact( 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;}
NTSTATUSNTAPIRtlInitializeRXact( 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; }}
NTSTATUSNTAPIRtlAddAttributeActionToRXact( 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;}
NTSTATUSNTAPIRtlAddActionToRXact( 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);}
NTSTATUSNTAPIRtlApplyRXactNoFlush( 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;}
NTSTATUSNTAPIRtlApplyRXact( 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;}