1883 lines
51 KiB
C
1883 lines
51 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
rxact.c
|
||
|
||
Abstract:
|
||
|
||
This Module implements a simple transaction mechanism for registry
|
||
database operations which helps eliminate the possibility of partial
|
||
updates being made. The cases covered are specifically partial updates
|
||
caused by system crashes or program aborts during multiple-write updates.
|
||
|
||
|
||
WARNING: This package does not yet deal with full-disk problems
|
||
automatically. If a full disk is encountered during
|
||
transaction commit, then manual interaction may be required
|
||
to free enough space to complete the commit. There is
|
||
no means provided for backing out the commit.
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 15-May-1991
|
||
Robert Reichel (RobertRe) 15-July-1992
|
||
|
||
Environment:
|
||
|
||
Pure Runtime Library Routine
|
||
|
||
Revision History:
|
||
|
||
|
||
|
||
--*/
|
||
|
||
|
||
/*
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
High Level Description:
|
||
|
||
The simple transaction mechanism expects the following to be true:
|
||
|
||
(1) A single server is responsible for operations on an entire
|
||
sub-tree of the registry database. For example, the security
|
||
account manager server (SAM) is responsible for everything
|
||
below \REGISTRY\LOCAL_MACHINE\SECURITY\SAM.
|
||
|
||
(2) Transactions on the sub-tree are serialized by the server
|
||
responsible for the sub-tree. That is, the server will not
|
||
start a second user request until all previous user requests
|
||
have been completed.
|
||
|
||
The simple transaction mechanism helps eliminate the problem of partial
|
||
updates caused by system crash or program abort during multiple-write
|
||
updates to the registry database. This is achieved by:
|
||
|
||
(1) Keeping all actions in-memory until instructed to commit. The
|
||
presence of in-memory data structures implicitly indicates
|
||
that a transaction is in progress.
|
||
|
||
The initial state is no transaction in progress.
|
||
|
||
(2) Providing a service which allows a server to initiate a transaction.
|
||
This allocates in-memory data structures, thereby changing the
|
||
state to transaction in progress.
|
||
|
||
(3) Keeping a log of all keys in the sub-tree that are to be
|
||
updated in a single transaction. Each record in this log
|
||
contains the following information:
|
||
|
||
(a) The name of the sub-key effected
|
||
|
||
(b) The operation to be performed on the sub-key
|
||
either DELETE or SET_VALUE. Note that these
|
||
operations are idempotent and may be applied
|
||
again in the event that the server aborts during
|
||
an initial commit.
|
||
|
||
(c) The new value of the sub-key (if applicable)
|
||
|
||
(d) (optionally) The attribute name of the subkey
|
||
to be operated on.
|
||
|
||
(note that SET_VALUE is used to create new sub-keys
|
||
as well as updated existing ones).
|
||
|
||
The entire list of sub-keys to be modified must be entered
|
||
into this log before ANY of the sub-keys is actually modified.
|
||
|
||
(4) Providing a commit service that applies all changes indicated
|
||
in the change log. This is done by first writing the contents
|
||
of the in-memory structures to a single key value ("Log") in
|
||
the registry and flushing the data to disk. The presence of
|
||
the "Log" value and data imply that a commit is in progress.
|
||
|
||
All necessary changes are applied, the "Log" value and its
|
||
data are deleted, and in-memory data structres are freed,
|
||
thereby changing the state to no-transaction.
|
||
|
||
|
||
The package also includes a service which must be called upon server
|
||
startup. This service checks to make sure the state of the sub-tree
|
||
is NO_TRANSACTION. If it is not, then one of the actions below is
|
||
performed based upon the current state of the sub-tree:
|
||
|
||
COMMITTING - This means the server was previously aborted while
|
||
a transaction was being committed (applied to the registry).
|
||
In this case, the commit is performed again from the beginning
|
||
of the change log. After the commit is completed, the state
|
||
of the sub-tree is set to NO_TRANSACTION.
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
*/
|
||
|
||
|
||
|
||
/*
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
Detailed Description:
|
||
|
||
Registry State
|
||
--------------
|
||
|
||
The registry state of a subtree is kept in a sub-key of that tree
|
||
named:
|
||
|
||
"RXACT"
|
||
|
||
The value field of that registry key includes a revision field.
|
||
|
||
|
||
RXact Context
|
||
-------------
|
||
|
||
A call to RtlInitializeRXact will return a pointer to an
|
||
RTL_RXACT_CONTEXT structure. This structure contains:
|
||
|
||
(1) the passed RootRegistryKey (eg, key to "Sam"),
|
||
|
||
(2) a handle to the top of the RXact subtree (eg, key to
|
||
"Sam\RXACT"),
|
||
|
||
(3) a flag indicating if handles stored in the log are
|
||
valid,
|
||
|
||
(4) a pointer to the current RXactLog.
|
||
|
||
The subsystem calling RtlInitializeRXact must keep this returned
|
||
pointer and pass it back to RXact in all subsequent calls.
|
||
|
||
|
||
Operation Log
|
||
-------------
|
||
|
||
The operation log of a registry sub-tree transaction is kept as sequence
|
||
of "operation log entries".
|
||
|
||
An in-memory log is a block of heap memory allocted by RtlStartRXact.
|
||
It has a header which contains:
|
||
|
||
(1) The count of operations in the log.
|
||
|
||
(2) The maximum size of the log.
|
||
|
||
(3) The amount of the log currently in use.
|
||
|
||
The log data itself follows the header directly.
|
||
|
||
|
||
Operation Log Entries
|
||
---------------------
|
||
|
||
An operation log entry is described by the following structure:
|
||
|
||
typedef struct _RXACT_LOG_ENTRY {
|
||
ULONG LogEntrySize;
|
||
RTL_RXACT_OPERATION Operation;
|
||
UNICODE_STRING SubKeyName; // Self-relativized (Buffer is really offset)
|
||
UNICODE_STRING AttributeName; // Self-relativized (Buffer is really offset)
|
||
HANDLE KeyHandle; // optional, not valid if read from disk.
|
||
ULONG NewKeyValueType;
|
||
ULONG NewKeyValueLength;
|
||
PVOID NewKeyValue; // Contains offset to data from start of log
|
||
} RXACT_LOG_ENTRY, *PRXACT_LOG_ENTRY;
|
||
|
||
The log entry contains all of the information passed in during a call
|
||
to RtlAddActionToRXact or RtlAddAttributeActionToRXact.
|
||
|
||
The UNICODE_STRING structures contain an offset to the string data
|
||
rather than a pointer. These offsets are relative to the start of
|
||
the log data, and are adjusted in place as each log entry is commited.
|
||
|
||
The KeyHandle is valid if it is not equal to INVALID_HANDLE_VALUE and
|
||
if the HandlesValid flag in the RXactContext structure is TRUE. This
|
||
is so that we do not attempt to use the handles if the log has been
|
||
read from disk after a reboot.
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
*/
|
||
|
||
|
||
#include "ntrtlp.h"
|
||
|
||
//
|
||
// Cannot include <windows.h> from kernel code
|
||
//
|
||
#define INVALID_HANDLE_VALUE (HANDLE)-1
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Local Macros & Definitions //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
//
|
||
// Revision level of a registry transaction .
|
||
//
|
||
|
||
#define RTLP_RXACT_REVISION1 (1l)
|
||
#define RTLP_RXACT_CURRENT_REVISION RTLP_RXACT_REVISION1
|
||
|
||
|
||
#define RTLP_RXACT_KEY_NAME L"RXACT"
|
||
|
||
#define RTLP_RXACT_LOG_NAME L"Log"
|
||
|
||
#define RTLP_INITIAL_LOG_SIZE 0x4000
|
||
|
||
//
|
||
// Given a value return its longword aligned equivalent value
|
||
//
|
||
|
||
#define DwordAlign(Value) ( \
|
||
(ULONG)((((ULONG)(Value)) + 3) & 0xfffffffc) \
|
||
)
|
||
|
||
//
|
||
// The value field of the RXACT registry key is one of the following data
|
||
// structures.
|
||
//
|
||
|
||
//
|
||
// The state of a registry sub-tree is one of the following:
|
||
//
|
||
// RtlpRXactStateNoTransaction - There is not a transaction in progress.
|
||
//
|
||
// RtlpRXactStateCommitting - The actions of a transaction are being
|
||
// applied to the registry database.
|
||
//
|
||
|
||
typedef enum _RTLP_RXACT_STATE {
|
||
RtlpRXactStateNoTransaction = 2,
|
||
RtlpRXactStateCommitting
|
||
} RTLP_RXACT_STATE, *PRTLP_RXACT_STATE;
|
||
|
||
|
||
typedef struct _RTLP_RXACT {
|
||
ULONG Revision;
|
||
RTLP_RXACT_STATE State; // no longer used
|
||
ULONG OperationCount; // no longer used
|
||
} RTLP_RXACT, *PRTLP_RXACT;
|
||
|
||
|
||
typedef struct _RXACT_LOG_ENTRY {
|
||
ULONG LogEntrySize;
|
||
RTL_RXACT_OPERATION Operation;
|
||
UNICODE_STRING SubKeyName; // Self-relativized (Buffer is really offset)
|
||
UNICODE_STRING AttributeName; // Self-relativized (Buffer is really offset)
|
||
HANDLE KeyHandle; // optional, not valid if read from disk.
|
||
ULONG NewKeyValueType;
|
||
ULONG NewKeyValueLength;
|
||
PVOID NewKeyValue; // Contains offset to data from start of log
|
||
} RXACT_LOG_ENTRY, *PRXACT_LOG_ENTRY;
|
||
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Prototypes for local procedures //
|
||
// //
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RXactpCommit(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
);
|
||
|
||
NTSTATUS
|
||
RXactpOpenTargetKey(
|
||
IN HANDLE RootRegistryKey,
|
||
IN RTL_RXACT_OPERATION Operation,
|
||
IN PUNICODE_STRING SubKeyName,
|
||
OUT PHANDLE TargetKey
|
||
);
|
||
|
||
|
||
|
||
VOID
|
||
RXactInitializeContext(
|
||
IN PRTL_RXACT_CONTEXT RXactContext,
|
||
IN HANDLE RootRegistryKey,
|
||
IN HANDLE RXactKey
|
||
);
|
||
|
||
|
||
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
||
#pragma alloc_text(PAGE,RXactpCommit)
|
||
#pragma alloc_text(PAGE,RXactpOpenTargetKey)
|
||
#pragma alloc_text(PAGE,RXactInitializeContext)
|
||
#pragma alloc_text(PAGE,RtlInitializeRXact)
|
||
#pragma alloc_text(PAGE,RtlStartRXact)
|
||
#pragma alloc_text(PAGE,RtlAbortRXact)
|
||
#pragma alloc_text(PAGE,RtlAddAttributeActionToRXact)
|
||
#pragma alloc_text(PAGE,RtlAddActionToRXact)
|
||
#pragma alloc_text(PAGE,RtlApplyRXact)
|
||
#pragma alloc_text(PAGE,RtlApplyRXactNoFlush)
|
||
#endif
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Exported Procedures (defined in ntrtl.h) //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
NTSTATUS
|
||
RtlInitializeRXact(
|
||
IN HANDLE RootRegistryKey,
|
||
IN BOOLEAN CommitIfNecessary,
|
||
OUT PRTL_RXACT_CONTEXT *RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine should be called by a server exactly once when it starts.
|
||
This routine will check to see that the registry transaction information
|
||
exists for the specified registry sub-tree, and will create it if it
|
||
doesn't exist.
|
||
|
||
Arguments:
|
||
|
||
RootRegistryKey - A handle to the registry key within whose sub-tree
|
||
a transaction is to be initialized.
|
||
|
||
CommitIfNecessary - A BOOLEAN value indicating whether or not any
|
||
previously aborted commit discovered should be commited at this
|
||
time. A value of TRUE indicates the commit should be applied
|
||
if encountered. A value of FALSE indicates a previously
|
||
aborted COMMIT should not be committed at this time.
|
||
|
||
RXactContext - Returns a pointer to an RTL_RXACT_CONTEXT structure
|
||
allocated out of the local heap. The caller must keep this
|
||
pointer and pass it back in for all future RXact transactions
|
||
for the passed RootRegistryKey.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction state already exists for the
|
||
registry sub-tree and is already in the NO_TRANSACTION state.
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state already
|
||
exists for the specified sub-tree, but is a revision level that is
|
||
unknown by this service.
|
||
|
||
STATUS_RXACT_STATE_CREATED - This informational level status indicates
|
||
that a specified registry sub-tree transaction state did not yet
|
||
exist and had to be created.
|
||
|
||
STATUS_RXACT_COMMIT_NECESSARY - This warning level status indicates that the
|
||
transaction state already exists for the registry sub-tree, but that
|
||
a transaction commit was previously aborted. The commit has NOT been
|
||
completed. Another call to this service with a CommitIfNecessary value
|
||
of TRUE may be used to commit the transaction.
|
||
|
||
|
||
STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
|
||
of the registry sub-tree is incompatible with the requested operation.
|
||
For example, a request to start a new transaction while one is already
|
||
in progress, or a request to apply a transaction when one is not
|
||
currently in progress.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
HANDLE RXactKey;
|
||
LARGE_INTEGER LastWriteTime;
|
||
NTSTATUS Status, TmpStatus;
|
||
OBJECT_ATTRIBUTES RXactAttributes;
|
||
PKEY_VALUE_FULL_INFORMATION FullInformation;
|
||
RTLP_RXACT RXactKeyValue;
|
||
UCHAR BasicInformation[128]; // Should be more than long enough
|
||
ULONG Disposition;
|
||
ULONG KeyValueLength;
|
||
ULONG KeyValueType;
|
||
ULONG ResultLength;
|
||
UNICODE_STRING RXactKeyName;
|
||
UNICODE_STRING ValueName;
|
||
UNICODE_STRING NullName;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
//
|
||
// Initialize some stuff
|
||
//
|
||
|
||
KeyValueLength = (ULONG)sizeof( RTLP_RXACT );
|
||
KeyValueType = 0; // Not used by RXact
|
||
|
||
RtlInitUnicodeString( &NullName, NULL );
|
||
|
||
//
|
||
// Create or open the RXACT key.
|
||
//
|
||
|
||
RtlInitUnicodeString( &RXactKeyName, RTLP_RXACT_KEY_NAME);
|
||
|
||
InitializeObjectAttributes(
|
||
&RXactAttributes,
|
||
&RXactKeyName,
|
||
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
||
RootRegistryKey,
|
||
NULL);
|
||
|
||
// Status = RtlpNtCreateKey(
|
||
// &RXactKey,
|
||
// (KEY_READ | KEY_WRITE | DELETE),
|
||
// &RXactAttributes,
|
||
// 0,
|
||
// NULL,
|
||
// &Disposition
|
||
// );
|
||
|
||
Status = NtCreateKey( &RXactKey,
|
||
(KEY_READ | KEY_WRITE | DELETE),
|
||
&RXactAttributes,
|
||
0, //TitleIndex
|
||
NULL, //Class OPTIONAL,
|
||
REG_OPTION_NON_VOLATILE, //CreateOptions,
|
||
&Disposition
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Allocate the RXactContext block
|
||
//
|
||
|
||
*RXactContext = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( RTL_RXACT_CONTEXT ));
|
||
|
||
if ( *RXactContext == NULL ) {
|
||
|
||
//
|
||
// Something prevented value assignment...
|
||
// Get rid of the RXact key and return the error
|
||
//
|
||
|
||
TmpStatus = NtDeleteKey( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
TmpStatus = NtClose( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
|
||
return( STATUS_NO_MEMORY );
|
||
}
|
||
|
||
//
|
||
// Initialize the newly created RXactContext structure.
|
||
//
|
||
|
||
RXactInitializeContext( *RXactContext, RootRegistryKey, RXactKey );
|
||
|
||
//
|
||
// If we created (as opposed to opened an existing) rxact key,
|
||
// then we need to initialize it.
|
||
//
|
||
|
||
if ( Disposition == REG_CREATED_NEW_KEY ) {
|
||
|
||
RXactKeyValue.Revision = RTLP_RXACT_REVISION1;
|
||
|
||
Status = NtSetValueKey( RXactKey,
|
||
&NullName, // ValueName
|
||
0, // TitleIndex
|
||
KeyValueType,
|
||
&RXactKeyValue,
|
||
KeyValueLength
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
//
|
||
// Something prevented value assignment...
|
||
// Get rid of the RXact key and return the error
|
||
//
|
||
|
||
TmpStatus = NtDeleteKey( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
TmpStatus = NtClose( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
return( STATUS_RXACT_STATE_CREATED );
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// We have opened an existing RXACT key.
|
||
// See if it is a revision level we know about.
|
||
//
|
||
|
||
Status = RtlpNtQueryValueKey(
|
||
RXactKey, // KeyHandle
|
||
&KeyValueType, // KeyValueType
|
||
&RXactKeyValue, // KeyValue
|
||
&KeyValueLength, // KeyValueLength
|
||
&LastWriteTime // LastWriteTime
|
||
);
|
||
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
//
|
||
// Something prevented value query...
|
||
//
|
||
|
||
TmpStatus = NtClose( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
return( Status );
|
||
}
|
||
|
||
|
||
if ( KeyValueLength != (ULONG)sizeof(RTLP_RXACT) ) {
|
||
TmpStatus = NtClose( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
return( STATUS_UNKNOWN_REVISION );
|
||
}
|
||
|
||
if (RXactKeyValue.Revision != RTLP_RXACT_REVISION1) {
|
||
TmpStatus = NtClose( RXactKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
return( STATUS_UNKNOWN_REVISION );
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Right revision...
|
||
// See if there is a transaction or commit in progress. If not,
|
||
// return success
|
||
//
|
||
|
||
//
|
||
// If a log file exists, then we are committing.
|
||
//
|
||
|
||
RtlInitUnicodeString( &ValueName, RTLP_RXACT_LOG_NAME );
|
||
|
||
Status = NtQueryValueKey(
|
||
RXactKey,
|
||
&ValueName,
|
||
KeyValueBasicInformation,
|
||
&BasicInformation,
|
||
128,
|
||
&ResultLength
|
||
);
|
||
|
||
if ( NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// We found a value called 'Log'. This means that a commit
|
||
// was in progress.
|
||
//
|
||
|
||
if ( CommitIfNecessary ) {
|
||
|
||
//
|
||
// Query the full value of the log, then call a low level routine
|
||
// to actually perform the commit.
|
||
//
|
||
|
||
Status = NtQueryValueKey(
|
||
RXactKey,
|
||
&ValueName,
|
||
KeyValueFullInformation,
|
||
NULL,
|
||
0,
|
||
&ResultLength
|
||
);
|
||
|
||
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
|
||
return( Status );
|
||
}
|
||
|
||
FullInformation = RtlAllocateHeap( RtlProcessHeap(), 0, ResultLength );
|
||
|
||
if ( FullInformation == NULL ) {
|
||
return( STATUS_NO_MEMORY );
|
||
}
|
||
|
||
|
||
Status = NtQueryValueKey(
|
||
RXactKey,
|
||
&ValueName,
|
||
KeyValueFullInformation,
|
||
FullInformation,
|
||
ResultLength,
|
||
&ResultLength
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, FullInformation );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// The log information is buried in the returned FullInformation
|
||
// buffer. Dig it out and make the RXactLog in the RXactContext
|
||
// structure point to it. Then commit.
|
||
//
|
||
|
||
(*RXactContext)->RXactLog = (PRTL_RXACT_LOG)((PCHAR)FullInformation + FullInformation->DataOffset);
|
||
|
||
//
|
||
// Don't use any handles we may find in the log file
|
||
//
|
||
|
||
(*RXactContext)->HandlesValid = FALSE;
|
||
|
||
Status = RXactpCommit( *RXactContext );
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, FullInformation );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
|
||
return( Status );
|
||
}
|
||
|
||
|
||
//
|
||
// The commit was successful. Clean up.
|
||
// Delete the log file value and data
|
||
//
|
||
|
||
Status = NtDeleteValueKey( RXactKey, &ValueName );
|
||
|
||
//
|
||
// This should never fail
|
||
//
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
//
|
||
// Get rid of the in memory data structures. Abort
|
||
// will free the RXactLog, so put what we want
|
||
// freed in there and it will go away.
|
||
//
|
||
|
||
(*RXactContext)->RXactLog = (PRTL_RXACT_LOG)FullInformation;
|
||
|
||
Status = RtlAbortRXact( *RXactContext );
|
||
|
||
//
|
||
// This should never fail
|
||
//
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
return( Status );
|
||
} else {
|
||
|
||
return( STATUS_RXACT_COMMIT_NECESSARY );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// No log, so nothing to do here.
|
||
//
|
||
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
RXactInitializeContext(
|
||
IN PRTL_RXACT_CONTEXT RXactContext,
|
||
IN HANDLE RootRegistryKey,
|
||
IN HANDLE RXactKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes an in-memory RXactContext structure.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to an RXact Context created
|
||
by RtlInitializeRXact.
|
||
|
||
RootRegistryKey - Supplies the RootRegistryKey for this component.
|
||
|
||
RXactKey - Supplies the {RootRegistryKey}\RXactKey for this component
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Initialize the RXactContext for this client
|
||
//
|
||
|
||
RXactContext->RootRegistryKey = RootRegistryKey;
|
||
RXactContext->HandlesValid = TRUE;
|
||
RXactContext->RXactLog = NULL;
|
||
RXactContext->RXactKey = RXactKey;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RtlStartRXact(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to start a new transaction in a registry sub-tree.
|
||
Transactions must be serialized by the server so that only one transaction
|
||
is in progress at a time.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to an RTL_RXACT_CONTEXT structure
|
||
that is not currently in use.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction was started.
|
||
|
||
STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
|
||
of the registry sub-tree is incompatible with the requested operation.
|
||
For example, a request to start a new transaction while one is already
|
||
in progress, or a request to apply a transaction when one is not
|
||
currently in progress. This may also indicate that there is no
|
||
transaction state at all for the specified registry sub-tree.
|
||
|
||
--*/
|
||
{
|
||
PRTL_RXACT_LOG RXactLogHeader;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
//
|
||
// Allocate in-memory log file and initialize. This implicitly
|
||
// sets the state to 'transaction in progress'.
|
||
//
|
||
|
||
if ( RXactContext->RXactLog != NULL ) {
|
||
|
||
//
|
||
// There is already a transaction in progress for this
|
||
// context. Return an error.
|
||
//
|
||
|
||
return( STATUS_RXACT_INVALID_STATE );
|
||
}
|
||
|
||
RXactLogHeader = RtlAllocateHeap( RtlProcessHeap(), 0, RTLP_INITIAL_LOG_SIZE );
|
||
|
||
if ( RXactLogHeader == NULL ) {
|
||
return( STATUS_NO_MEMORY );
|
||
}
|
||
|
||
//
|
||
// Fill in the log header information at the top of the
|
||
// newly allocated buffer.
|
||
//
|
||
|
||
|
||
RXactLogHeader->OperationCount = 0;
|
||
RXactLogHeader->LogSize = RTLP_INITIAL_LOG_SIZE;
|
||
RXactLogHeader->LogSizeInUse = sizeof( RTL_RXACT_LOG );
|
||
|
||
RXactContext->RXactLog = RXactLogHeader;
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RtlAbortRXact(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to abort a transaction in a registry sub-tree.
|
||
|
||
Arguments:
|
||
|
||
RootRegistryKey - A handle to the registry key within whose sub-tree
|
||
the transaction is to be aborted.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction was aborted.
|
||
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state
|
||
exists for the specified sub-tree, but has a revision level that is
|
||
unknown by this service.
|
||
|
||
|
||
STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
|
||
of the registry sub-tree is incompatible with the requested operation.
|
||
For example, a request to start a new transaction while one is already
|
||
in progress, or a request to apply a transaction when one is not
|
||
currently in progress. This may also indicate that there is no
|
||
transaction state at all for the specified registry sub-tree.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_PAGED_CODE();
|
||
|
||
if ( RXactContext->RXactLog == NULL ) {
|
||
|
||
//
|
||
// There is no transaction in progress for this
|
||
// context. Return an error.
|
||
//
|
||
|
||
return( STATUS_RXACT_INVALID_STATE );
|
||
}
|
||
|
||
(VOID) RtlFreeHeap( RtlProcessHeap(), 0, RXactContext->RXactLog );
|
||
|
||
//
|
||
// Reinitialize the RXactContext structure with the same initial data.
|
||
//
|
||
|
||
RXactInitializeContext(
|
||
RXactContext,
|
||
RXactContext->RootRegistryKey,
|
||
RXactContext->RXactKey
|
||
);
|
||
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RtlAddAttributeActionToRXact(
|
||
IN PRTL_RXACT_CONTEXT RXactContext,
|
||
IN RTL_RXACT_OPERATION Operation,
|
||
IN PUNICODE_STRING SubKeyName,
|
||
IN HANDLE KeyHandle OPTIONAL,
|
||
IN PUNICODE_STRING AttributeName,
|
||
IN ULONG NewValueType,
|
||
IN PVOID NewValue,
|
||
IN ULONG NewValueLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to add a new action to the transaction operation log.
|
||
Upon commit, these operations are applied in the order they are added
|
||
to the log.
|
||
|
||
This routine differs from RtlAddActionToRXact in that it takes an Attribute
|
||
Name parameter, rather than using the default ("NULL") Attribute of the
|
||
specified key.
|
||
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to the RXactContext structure for this
|
||
subsystem's root registry key.
|
||
|
||
Operation - Indicates the type of operation to perform (e.g., delete
|
||
a sub-key or set the value of a sub-key). Sub-keys may be created
|
||
by setting a value of a previously non-existent sub-key. This will
|
||
cause all sub-keys between the root and the specified sub-key to
|
||
be created.
|
||
|
||
SubKeyName - Specifies the name of the target registry key. This name
|
||
is relative to the Root of the Registry transaction sub-tree
|
||
and must NOT start with a delimiter character ("\").
|
||
|
||
KeyHandle - Optionally supplies a handle to the target key. If
|
||
not specified, the name passed for SubKeyName will determine
|
||
the target key.
|
||
|
||
AttributeName - Supplies the name of the key attribute to be
|
||
modified.
|
||
|
||
NewKeyValueType - (Optional) Contains the KeyValueType to assign
|
||
to the target registry key. This parameter is ignored if the
|
||
Operation is not RtlRXactOperationSetValue.
|
||
|
||
NewKeyValue - (Optional) Points to a buffer containing the value
|
||
to assign to the specified target registry key. This parameter
|
||
is ignored if the Operation is not RtlRXactOperationSetValue.
|
||
|
||
NewKeyValueLength - Indicates the length (number of bytes) of the
|
||
NewKeyValue buffer. This parameter is ignored if the Operation
|
||
is not RtlRXactOperationSetValue.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the request completed successfully..
|
||
|
||
STATUS_INVALID_PARAMETER - Indicates that an unknown Operation
|
||
was requested.
|
||
|
||
STATUS_NO_MEMORY - Insufficient memeory was available to complete
|
||
this operation.
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state
|
||
exists for the specified sub-tree, but has a revision level that is
|
||
unknown by this service.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PRTL_RXACT_LOG NewLog;
|
||
PRXACT_LOG_ENTRY Base;
|
||
|
||
ULONG End;
|
||
ULONG LogEntrySize;
|
||
ULONG NewLogSize;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
//
|
||
// Make sure we were passed a legitimate operation.
|
||
//
|
||
|
||
if ( (Operation != RtlRXactOperationDelete) &&
|
||
(Operation != RtlRXactOperationSetValue) ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Compute the total size of the new data
|
||
//
|
||
|
||
LogEntrySize = sizeof( RXACT_LOG_ENTRY ) +
|
||
DwordAlign( SubKeyName->Length ) +
|
||
DwordAlign( AttributeName->Length ) +
|
||
DwordAlign( NewValueLength );
|
||
|
||
LogEntrySize = ALIGN_UP( LogEntrySize, PVOID );
|
||
|
||
//
|
||
// Make sure there is enough space in the current
|
||
// log file for this data. If not, we must create
|
||
// a larger log, copy all the old data, and then
|
||
// append this to the end.
|
||
//
|
||
|
||
if ( RXactContext->RXactLog->LogSizeInUse + LogEntrySize >
|
||
RXactContext->RXactLog->LogSize ) {
|
||
|
||
//
|
||
// We must allocate a bigger log file.
|
||
//
|
||
|
||
NewLogSize = RXactContext->RXactLog->LogSize;
|
||
|
||
do {
|
||
|
||
NewLogSize = NewLogSize * 2;
|
||
|
||
} while ( NewLogSize <
|
||
( RXactContext->RXactLog->LogSizeInUse + LogEntrySize ) );
|
||
|
||
NewLog = RtlAllocateHeap( RtlProcessHeap(), 0, NewLogSize );
|
||
|
||
if ( NewLog == NULL ) {
|
||
return( STATUS_NO_MEMORY );
|
||
}
|
||
|
||
//
|
||
// Copy over previous information
|
||
//
|
||
|
||
RtlCopyMemory( NewLog, RXactContext->RXactLog, RXactContext->RXactLog->LogSizeInUse );
|
||
|
||
//
|
||
// Free the old log file
|
||
//
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, RXactContext->RXactLog );
|
||
|
||
//
|
||
// Install the new log file and adjust its size in its header
|
||
//
|
||
|
||
RXactContext->RXactLog = NewLog;
|
||
RXactContext->RXactLog->LogSize = NewLogSize;
|
||
}
|
||
|
||
//
|
||
// The log file is big enough, append data to
|
||
// the end.
|
||
//
|
||
|
||
Base = (PRXACT_LOG_ENTRY)((PCHAR)(RXactContext->RXactLog) +
|
||
(RXactContext->RXactLog->LogSizeInUse));
|
||
|
||
|
||
//
|
||
// Append each parameter to the end of the log. Unicode string data
|
||
// will be appended to the end of the entry. The Buffer field in the
|
||
// Unicode string structure will contain the offset to the Buffer,
|
||
// relative to the beginning of the log file.
|
||
//
|
||
|
||
Base->LogEntrySize = LogEntrySize;
|
||
Base->Operation = Operation;
|
||
Base->SubKeyName = *SubKeyName;
|
||
Base->AttributeName = *AttributeName;
|
||
Base->NewKeyValueType = NewValueType;
|
||
Base->NewKeyValueLength = NewValueLength;
|
||
Base->KeyHandle = KeyHandle;
|
||
|
||
//
|
||
// Fill in the variable length data: SubKeyName, AttributeName,
|
||
// and NewKeyValue
|
||
//
|
||
|
||
//
|
||
// End is an offset relative to the beginning of the entire log
|
||
// structure. It is initialized to 'point' to the offset immediately
|
||
// following the structure we just filled in above.
|
||
//
|
||
|
||
End = (ULONG)((RXactContext->RXactLog->LogSizeInUse) +
|
||
sizeof( *Base ));
|
||
|
||
|
||
//
|
||
// Append SubKeyName information to the log file
|
||
//
|
||
|
||
RtlMoveMemory (
|
||
(PCHAR)(RXactContext->RXactLog) + End,
|
||
SubKeyName->Buffer,
|
||
SubKeyName->Length
|
||
);
|
||
|
||
Base->SubKeyName.Buffer = (PWSTR)ULongToPtr(End);
|
||
End += DwordAlign( SubKeyName->Length );
|
||
|
||
|
||
|
||
//
|
||
// Append AttributeName information to the log file
|
||
//
|
||
|
||
|
||
RtlMoveMemory(
|
||
(PCHAR)(RXactContext->RXactLog) + End,
|
||
AttributeName->Buffer,
|
||
AttributeName->Length
|
||
);
|
||
|
||
Base->AttributeName.Buffer = (PWSTR)ULongToPtr(End);
|
||
End += DwordAlign( AttributeName->Length );
|
||
|
||
|
||
|
||
//
|
||
// Append NewKeyValue information (if present) to the log file
|
||
//
|
||
|
||
if ( Operation == RtlRXactOperationSetValue ) {
|
||
|
||
RtlMoveMemory(
|
||
(PCHAR)(RXactContext->RXactLog) + End,
|
||
NewValue,
|
||
NewValueLength
|
||
);
|
||
|
||
Base->NewKeyValue = (PVOID)ULongToPtr(End);
|
||
End += DwordAlign( NewValueLength );
|
||
}
|
||
|
||
End = ALIGN_UP( End, PVOID );
|
||
|
||
RXactContext->RXactLog->LogSizeInUse = End;
|
||
RXactContext->RXactLog->OperationCount++;
|
||
|
||
//
|
||
// We're done
|
||
//
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RtlAddActionToRXact(
|
||
IN PRTL_RXACT_CONTEXT RXactContext,
|
||
IN RTL_RXACT_OPERATION Operation,
|
||
IN PUNICODE_STRING SubKeyName,
|
||
IN ULONG NewKeyValueType,
|
||
IN PVOID NewKeyValue OPTIONAL,
|
||
IN ULONG NewKeyValueLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to add a new action to the transaction operation log.
|
||
Upon commit, these operations are applied in the order they are added
|
||
to the log.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to the RXactContext structure for this
|
||
subsystem's root registry key.
|
||
|
||
Operation - Indicates the type of operation to perform (e.g., delete
|
||
a sub-key or set the value of a sub-key). Sub-keys may be created
|
||
by setting a value of a previously non-existent sub-key. This will
|
||
cause all sub-keys between the root and the specified sub-key to
|
||
be created.
|
||
|
||
SubKeyName - Specifies the name of the target registry key. This name
|
||
is relative to the Root of the Registry transaction sub-tree
|
||
and must NOT start with a delimiter character ("\").
|
||
|
||
NewKeyValueType - (Optional) Contains the KeyValueType to assign
|
||
to the target registry key. This parameter is ignored if the
|
||
Operation is not RtlRXactOperationSetValue.
|
||
|
||
NewKeyValue - (Optional) Points to a buffer containing the value
|
||
to assign to the specified target registry key. This parameter
|
||
is ignored if the Operation is not RtlRXactOperationSetValue.
|
||
|
||
NewKeyValueLength - Indicates the length (number of bytes) of the
|
||
NewKeyValue buffer. This parameter is ignored if the Operation
|
||
is not RtlRXactOperationSetValue.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the request completed successfully..
|
||
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state
|
||
exists for the specified sub-tree, but has a revision level that is
|
||
unknown by this service.
|
||
|
||
Others - Other status values that may be returned from registry key
|
||
services (such as STATUS_ACCESS_DENIED).
|
||
|
||
--*/
|
||
{
|
||
UNICODE_STRING AttributeName;
|
||
NTSTATUS Status;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
RtlInitUnicodeString( &AttributeName, NULL );
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
RXactContext,
|
||
Operation,
|
||
SubKeyName,
|
||
INVALID_HANDLE_VALUE,
|
||
&AttributeName,
|
||
NewKeyValueType,
|
||
NewKeyValue,
|
||
NewKeyValueLength
|
||
);
|
||
|
||
return( Status );
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RtlApplyRXact(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to apply the changes of a registry sub-tree
|
||
Transaction to that registry sub-tree. This routine is meant to be
|
||
called for the common case, where the hive is automatically
|
||
lazy-flushed. That means that this routine must write the change log
|
||
to disk, then flush the hive (to ensure that pieces of changes aren't
|
||
lazy-written to disk before this routine finishes an atomic operation),
|
||
the apply the changes, then delete the change log.
|
||
|
||
The actual changes will be lazy-written to disk, but the registry
|
||
guarantees that none or all will make it. If the machine goes down
|
||
while this routine is executing, the flushed change log guarantees
|
||
that the hive can be put into a consistent state.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to the RXactContext structure for this
|
||
subsystem's root registry key.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction was completed.
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state
|
||
exists for the specified sub-tree, but has a revision level that is
|
||
unknown by this service.
|
||
|
||
|
||
STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
|
||
of the registry sub-tree is incompatible with the requested operation.
|
||
For example, a request to start a new transaction while one is already
|
||
in progress, or a request to apply a transaction when one is not
|
||
currently in progress. This may also indicate that there is no
|
||
transaction state at all for the specified registry sub-tree.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING LogName;
|
||
HANDLE RXactKey;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
//
|
||
// Commit the contents of the current log to disk
|
||
//
|
||
|
||
RXactKey = RXactContext->RXactKey;
|
||
|
||
RtlInitUnicodeString( &LogName, RTLP_RXACT_LOG_NAME );
|
||
|
||
Status = NtSetValueKey( RXactKey,
|
||
&LogName, // ValueName
|
||
0, // TitleIndex
|
||
REG_BINARY,
|
||
RXactContext->RXactLog,
|
||
RXactContext->RXactLog->LogSizeInUse
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
return( Status );
|
||
}
|
||
|
||
Status = NtFlushKey( RXactKey );
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// If this fails, maintain the in-memory data,
|
||
// but get rid of what we just tried to write
|
||
// to disk.
|
||
//
|
||
// Ignore the error, since we're in a funky
|
||
// state right now.
|
||
//
|
||
|
||
(VOID) NtDeleteValueKey( RXactKey, &LogName );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// The log is safe, now execute what is in it
|
||
//
|
||
|
||
Status = RXactpCommit( RXactContext );
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// As above, try to get rid of what's on
|
||
// disk, leave the in-memory stuff alone,
|
||
// so that the caller may try again.
|
||
//
|
||
|
||
(VOID) NtDeleteValueKey( RXactKey, &LogName );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Delete the log file value and data
|
||
//
|
||
|
||
Status = NtDeleteValueKey( RXactKey, &LogName );
|
||
|
||
//
|
||
// This should never fail
|
||
//
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
//
|
||
// Get rid of the in memory data structures. Abort
|
||
// does exactly what we want to do.
|
||
//
|
||
|
||
Status = RtlAbortRXact( RXactContext );
|
||
|
||
//
|
||
// This should never fail
|
||
//
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RtlApplyRXactNoFlush(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to apply the changes of a registry sub-tree
|
||
Transaction to that registry sub-tree. This routine should only be
|
||
called for special hives that do not have automatic lazy-flushing.
|
||
The caller must decide when to flush the hive in order to guarantee
|
||
a consistent hive.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to the RXactContext structure for this
|
||
subsystem's root registry key.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction was completed.
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates that a transaction state
|
||
exists for the specified sub-tree, but has a revision level that is
|
||
unknown by this service.
|
||
|
||
|
||
STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
|
||
of the registry sub-tree is incompatible with the requested operation.
|
||
For example, a request to start a new transaction while one is already
|
||
in progress, or a request to apply a transaction when one is not
|
||
currently in progress. This may also indicate that there is no
|
||
transaction state at all for the specified registry sub-tree.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
RTL_PAGED_CODE();
|
||
|
||
//
|
||
// Execute the contents of the RXACT log.
|
||
//
|
||
|
||
Status = RXactpCommit( RXactContext );
|
||
|
||
if ( NT_SUCCESS( Status ) ) {
|
||
|
||
//
|
||
// Get rid of the in memory data structures. Abort
|
||
// does exactly what we want to do.
|
||
//
|
||
|
||
Status = RtlAbortRXact( RXactContext );
|
||
|
||
//
|
||
// This should never fail
|
||
//
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
}
|
||
|
||
return( Status );
|
||
|
||
}
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Internal Procedures (defined in within this file) //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RXactpCommit(
|
||
IN PRTL_RXACT_CONTEXT RXactContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine commits the operations in the operation log.
|
||
|
||
When all changes have been applied, the transaction state
|
||
is changed to NO_TRANSACTION.
|
||
|
||
Arguments:
|
||
|
||
RXactContext - Supplies a pointer to the RXactContext structure for this
|
||
subsystem's root registry key.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the transaction was completed.
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN HandlesValid;
|
||
|
||
HANDLE TargetKey;
|
||
HANDLE RXactKey;
|
||
HANDLE RootRegistryKey;
|
||
|
||
PRTL_RXACT_LOG RXactLog;
|
||
PRXACT_LOG_ENTRY RXactLogEntry;
|
||
RTL_RXACT_OPERATION Operation;
|
||
|
||
ULONG OperationCount;
|
||
ULONG i;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
NTSTATUS TmpStatus = STATUS_SUCCESS;
|
||
BOOLEAN CloseTargetKey;
|
||
|
||
//
|
||
// Extract information from the RXactContext to simplify
|
||
// the code that follows
|
||
//
|
||
|
||
RootRegistryKey = RXactContext->RootRegistryKey;
|
||
RXactKey = RXactContext->RXactKey;
|
||
RXactLog = RXactContext->RXactLog;
|
||
|
||
OperationCount = RXactLog->OperationCount;
|
||
|
||
HandlesValid = RXactContext->HandlesValid;
|
||
|
||
|
||
//
|
||
// Keep a pointer to the beginning of the current log entry.
|
||
//
|
||
|
||
RXactLogEntry = (PRXACT_LOG_ENTRY)((PCHAR)RXactLog + sizeof( RTL_RXACT_LOG ));
|
||
|
||
|
||
//
|
||
// Go through and perform each operation log. Notice that some operation
|
||
// logs may already have been deleted by a previous commit attempt.
|
||
// So, don't get alarmed if we don't successfully open some operation
|
||
// log entry keys.
|
||
//
|
||
|
||
for ( i=0 ; i<OperationCount ; i++ ) {
|
||
|
||
//
|
||
// Turn the self-relative offsets in the structure
|
||
// back into real pointers.
|
||
//
|
||
|
||
RXactLogEntry->SubKeyName.Buffer = (PWSTR) ((PCHAR)RXactLogEntry->SubKeyName.Buffer +
|
||
(ULONG_PTR)RXactLog);
|
||
|
||
RXactLogEntry->AttributeName.Buffer = (PWSTR) ((PCHAR)RXactLogEntry->AttributeName.Buffer +
|
||
(ULONG_PTR)RXactLog);
|
||
|
||
RXactLogEntry->NewKeyValue = (PVOID)((PCHAR)RXactLogEntry->NewKeyValue + (ULONG_PTR)RXactLog);
|
||
|
||
Operation = RXactLogEntry->Operation;
|
||
|
||
//
|
||
// Perform this operation
|
||
//
|
||
|
||
switch (Operation) {
|
||
case RtlRXactOperationDelete:
|
||
|
||
//
|
||
// Open the target key and delete it.
|
||
// The name is relative to the RootRegistryKey.
|
||
//
|
||
|
||
if ( ((RXactLogEntry->KeyHandle == INVALID_HANDLE_VALUE) || !HandlesValid) ) {
|
||
|
||
Status = RXactpOpenTargetKey(
|
||
RootRegistryKey,
|
||
RtlRXactOperationDelete,
|
||
&RXactLogEntry->SubKeyName,
|
||
&TargetKey
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// We must allow the object not to be found,
|
||
// because we may be replaying this log after
|
||
// it had been partially executed.
|
||
//
|
||
|
||
if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
|
||
|
||
return( Status );
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
CloseTargetKey = TRUE;
|
||
|
||
} else {
|
||
|
||
TargetKey = RXactLogEntry->KeyHandle;
|
||
CloseTargetKey = FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// If this fails, then it is an error
|
||
// because the key should exist at
|
||
// this point.
|
||
//
|
||
|
||
Status = NtDeleteKey( TargetKey );
|
||
|
||
|
||
//
|
||
// Only close the target key if we opened it
|
||
//
|
||
|
||
if ( CloseTargetKey ) {
|
||
|
||
TmpStatus = NtClose( TargetKey );
|
||
|
||
//
|
||
// If we opened this handle, then we should
|
||
// be able to close it, whether it has been
|
||
// deleted or not.
|
||
//
|
||
|
||
ASSERT(NT_SUCCESS(TmpStatus)); // safe to ignore, but curious...
|
||
}
|
||
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(Status);
|
||
}
|
||
|
||
break;
|
||
|
||
case RtlRXactOperationSetValue:
|
||
|
||
//
|
||
// Open the target key.
|
||
// The name is relative to the RootRegistryKey.
|
||
//
|
||
|
||
if ( ((RXactLogEntry->KeyHandle == INVALID_HANDLE_VALUE) || !HandlesValid) ) {
|
||
|
||
Status = RXactpOpenTargetKey(
|
||
RootRegistryKey,
|
||
RtlRXactOperationSetValue,
|
||
&RXactLogEntry->SubKeyName,
|
||
&TargetKey
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return(Status);
|
||
}
|
||
|
||
CloseTargetKey = TRUE;
|
||
|
||
} else {
|
||
|
||
TargetKey = RXactLogEntry->KeyHandle;
|
||
CloseTargetKey = FALSE;
|
||
}
|
||
|
||
//
|
||
// Assign to the target key's new value
|
||
//
|
||
|
||
Status = NtSetValueKey( TargetKey,
|
||
&RXactLogEntry->AttributeName,
|
||
0, // TitleIndex
|
||
RXactLogEntry->NewKeyValueType,
|
||
RXactLogEntry->NewKeyValue,
|
||
RXactLogEntry->NewKeyValueLength
|
||
);
|
||
|
||
//
|
||
// Only close the target key if we opened it
|
||
//
|
||
|
||
if ( CloseTargetKey ) {
|
||
|
||
TmpStatus = NtClose( TargetKey );
|
||
ASSERT(NT_SUCCESS(TmpStatus)); // safe to ignore, but curious...
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return(Status);
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
|
||
default:
|
||
|
||
//
|
||
// Unknown operation type. This should never happen.
|
||
//
|
||
|
||
ASSERT( FALSE );
|
||
|
||
return(STATUS_INVALID_PARAMETER);
|
||
|
||
}
|
||
|
||
RXactLogEntry = (PRXACT_LOG_ENTRY)((PCHAR)RXactLogEntry + RXactLogEntry->LogEntrySize);
|
||
|
||
}
|
||
|
||
//
|
||
// Commit complete
|
||
//
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RXactpOpenTargetKey(
|
||
IN HANDLE RootRegistryKey,
|
||
IN RTL_RXACT_OPERATION Operation,
|
||
IN PUNICODE_STRING SubKeyName,
|
||
OUT PHANDLE TargetKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the target registry key of an operation.
|
||
|
||
Arguments:
|
||
|
||
RootRegistryKey - A handle to the registry key within whose sub-tree
|
||
a transaction is to be initialized.
|
||
|
||
Operation - Indicates what operation is to be performed on the target.
|
||
This will effect how the target is opened.
|
||
|
||
OperationNameKey - A handle to the operation log sub-key
|
||
containing the name of the target registry key.
|
||
|
||
TargetKey - Receives a handle to the target registry key.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the operation log entry was opened.
|
||
|
||
STATUS_NO_MEMORY - Ran out of heap.
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES TargetKeyAttributes;
|
||
ACCESS_MASK DesiredAccess;
|
||
ULONG Disposition;
|
||
|
||
|
||
if (Operation == RtlRXactOperationDelete) {
|
||
|
||
DesiredAccess = DELETE;
|
||
|
||
InitializeObjectAttributes(
|
||
&TargetKeyAttributes,
|
||
SubKeyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RootRegistryKey,
|
||
NULL);
|
||
|
||
// Status = RtlpNtOpenKey(
|
||
// TargetKey,
|
||
// DesiredAccess,
|
||
// &TargetKeyAttributes,
|
||
// 0);
|
||
|
||
Status = NtOpenKey( TargetKey,
|
||
DesiredAccess,
|
||
&TargetKeyAttributes
|
||
);
|
||
|
||
|
||
} else if (Operation == RtlRXactOperationSetValue) {
|
||
|
||
DesiredAccess = KEY_WRITE;
|
||
|
||
InitializeObjectAttributes(
|
||
&TargetKeyAttributes,
|
||
SubKeyName,
|
||
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
||
RootRegistryKey,
|
||
NULL);
|
||
|
||
Status = NtCreateKey(
|
||
TargetKey,
|
||
DesiredAccess,
|
||
&TargetKeyAttributes,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&Disposition
|
||
);
|
||
|
||
} else {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
|
||
return( Status );
|
||
|
||
}
|
||
|
||
|
||
|
||
//NTSTATUS
|
||
//RXactpAssignTargetValue(
|
||
// IN PVOID NewKeyValue,
|
||
// IN ULONG NewKeyValueLength,
|
||
// IN ULONG NewKeyValueType,
|
||
// IN HANDLE TargetKey,
|
||
// IN PUNICODE_STRING AttributeName
|
||
// );
|
||
|
||
|
||
//NTSTATUS
|
||
//RXactpAssignTargetValue(
|
||
// IN PVOID NewKeyValue,
|
||
// IN ULONG NewKeyValueLength,
|
||
// IN ULONG NewKeyValueType,
|
||
// IN HANDLE TargetKey,
|
||
// IN PUNICODE_STRING AttributeName
|
||
// )
|
||
//
|
||
///*++
|
||
//
|
||
//Routine Description:
|
||
//
|
||
// This routine copies the value of an operation log entry to its
|
||
// corresponding target key. The target key must already be open.
|
||
//
|
||
//Arguments:
|
||
//
|
||
// NewKeyValue - The new value for the key being modified.
|
||
//
|
||
// NewKeyValueLength - The size in bytes of the new value information.
|
||
//
|
||
// NewKeyValueType - The type of the data for the new key.
|
||
//
|
||
// TargetKey - A handle to the target registry key.
|
||
//
|
||
// AttributeName - Supplies the name of the key attribute being edited.
|
||
//
|
||
//Return Value:
|
||
//
|
||
// STATUS_SUCCESS - Indicates the value was successfully applied to
|
||
// the target registry key.
|
||
//
|
||
// STATUS_NO_MEMORY - ran out of heap.
|
||
//
|
||
//
|
||
//--*/
|
||
//{
|
||
// NTSTATUS Status;
|
||
//
|
||
// //
|
||
// // Now apply the value to the target key
|
||
// //
|
||
// // Even if there is no key value, we need to do the assign so that
|
||
// // the key value type is assigned.
|
||
// //
|
||
//
|
||
// Status = NtSetValueKey( TargetKey,
|
||
// AttributeName,
|
||
// 0, // TitleIndex
|
||
// NewKeyValueType,
|
||
// NewKeyValue,
|
||
// NewKeyValueLength
|
||
// );
|
||
//
|
||
//
|
||
// return( Status );
|
||
//}
|