You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1882 lines
53 KiB
1882 lines
53 KiB
/*++
|
|
|
|
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[0],
|
|
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 );
|
|
//}
|