Added ntdll_reg_flush patchset

This commit is contained in:
Alistair Leslie-Hughes 2023-06-24 10:47:31 +10:00
parent 95679a2f40
commit 22ba0ef2a3
4 changed files with 1045 additions and 0 deletions

View File

@ -0,0 +1,410 @@
From 36b85f294e94ff58ba190d7b6606e34d64af1a31 Mon Sep 17 00:00:00 2001
From: Paul Gofman <pgofman@codeweavers.com>
Date: Tue, 20 Jun 2023 11:54:06 -0600
Subject: [PATCH 2/3] ntdll: Implement NtFlushKey().
---
dlls/ntdll/unix/registry.c | 155 +++++++++++++++++++++++++++++++++++--
server/protocol.def | 12 +++
server/registry.c | 94 ++++++++++++++++++++--
3 files changed, 247 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c
index b63c7a66925..14b3ade3e6d 100644
--- a/dlls/ntdll/unix/registry.c
+++ b/dlls/ntdll/unix/registry.c
@@ -29,6 +29,8 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -282,8 +284,9 @@ static char *save_subkeys( char *data, struct saved_key *parent, struct saved_ke
}
/* save a registry branch to a file */
-static void save_all_subkeys( char *data, FILE *f )
+static char *save_all_subkeys( char *data, FILE *f )
{
+ /* Output registry format should match server/registry.c:save_all_subkeys(). */
enum prefix_type prefix_type;
int parent_count;
@@ -309,7 +312,7 @@ static void save_all_subkeys( char *data, FILE *f )
default:
break;
}
- save_subkeys( data, NULL, NULL, f );
+ return save_subkeys( data, NULL, NULL, f );
}
@@ -901,22 +904,162 @@ NTSTATUS WINAPI NtNotifyChangeKey( HANDLE key, HANDLE event, PIO_APC_ROUTINE apc
io, filter, subtree, buffer, length, async );
}
+/* acquire mutex for registry flush operation */
+static HANDLE get_key_flush_mutex(void)
+{
+ WCHAR bufferW[256];
+ UNICODE_STRING name = {.Buffer = bufferW};
+ OBJECT_ATTRIBUTES attr;
+ char buffer[256];
+ HANDLE mutex;
+
+ snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\__wine_regkey_flush",
+ (int)NtCurrentTeb()->Peb->SessionId );
+ name.Length = name.MaximumLength = (strlen(buffer) + 1) * sizeof(WCHAR);
+ ascii_to_unicode( bufferW, buffer, name.Length / sizeof(WCHAR) );
+
+ InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
+ if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return NULL;
+ NtWaitForSingleObject( mutex, FALSE, NULL );
+ return mutex;
+}
+
+/* release registry flush mutex */
+static void release_key_flush_mutex( HANDLE mutex )
+{
+ NtReleaseMutant( mutex, NULL );
+ NtClose( mutex );
+}
+
+/* save registry branch to Wine regsitry storage file */
+static NTSTATUS save_registry_branch( char **data )
+{
+ static const char temp_fn[] = "savereg.tmp";
+ char *file_name, *path = NULL, *tmp = NULL;
+ int file_name_len, path_len, fd;
+ struct stat st;
+ NTSTATUS ret;
+ FILE *f;
+
+ file_name_len = *(int *)*data;
+ *data += sizeof(int);
+ file_name = *data;
+ *data += file_name_len;
+
+ path_len = strlen( config_dir ) + 1 + file_name_len + 1;
+ if (!(path = malloc( path_len ))) return STATUS_NO_MEMORY;
+ sprintf( path, "%s/%s", config_dir, file_name );
+
+ if ((fd = open( path, O_WRONLY )) != -1)
+ {
+ /* if file is not a regular file or has multiple links or is accessed
+ * via symbolic links, write directly into it; otherwise use a temp file */
+ if (!lstat( path, &st ) && (!S_ISREG(st.st_mode) || st.st_nlink > 1))
+ {
+ ftruncate( fd, 0 );
+ goto save;
+ }
+ close( fd );
+ }
+
+ /* create a temp file in the same directory */
+ if (!(tmp = malloc( strlen( config_dir ) + 1 + strlen( temp_fn ) + 1 )))
+ {
+ ret = STATUS_NO_MEMORY;
+ goto done;
+ }
+ sprintf( tmp, "%s/%s", config_dir, temp_fn );
+
+ if ((fd = open( tmp, O_CREAT | O_EXCL | O_WRONLY, 0666 )) == -1)
+ {
+ ret = errno_to_status( errno );
+ goto done;
+ }
+
+save:
+ if (!(f = fdopen( fd, "w" )))
+ {
+ ret = errno_to_status( errno );
+ if (tmp) unlink( tmp );
+ close( fd );
+ goto done;
+ }
+
+ *data = save_all_subkeys( *data, f );
+
+ ret = fclose( f ) ? errno_to_status( errno ) : STATUS_SUCCESS;
+ if (tmp)
+ {
+ if (!ret && rename( tmp, path )) ret = errno_to_status( errno );
+ if (ret) unlink( tmp );
+ }
+
+done:
+ free( tmp );
+ free( path );
+ return ret;
+}
/******************************************************************************
* NtFlushKey (NTDLL.@)
*/
NTSTATUS WINAPI NtFlushKey( HANDLE key )
{
+ abstime_t timestamp_counter;
+ data_size_t size = 0;
unsigned int ret;
+ char *data = NULL, *curr_data;
+ HANDLE mutex;
+ int i, branch_count, branch;
TRACE( "key=%p\n", key );
- SERVER_START_REQ( flush_key )
+ mutex = get_key_flush_mutex();
+
+ while (1)
{
- req->hkey = wine_server_obj_handle( key );
- ret = wine_server_call( req );
+ SERVER_START_REQ( flush_key )
+ {
+ req->hkey = wine_server_obj_handle( key );
+ if (size) wine_server_set_reply( req, data, size );
+ ret = wine_server_call( req );
+ size = reply->total;
+ branch_count = reply->branch_count;
+ timestamp_counter = reply->timestamp_counter;
+ }
+ SERVER_END_REQ;
+
+ if (ret != STATUS_BUFFER_TOO_SMALL) break;
+ free( data );
+ if (!(data = malloc( size )))
+ {
+ ERR( "No memory.\n" );
+ ret = STATUS_NO_MEMORY;
+ goto done;
+ }
}
- SERVER_END_REQ;
+ if (ret) goto done;
+
+ curr_data = data;
+ for (i = 0; i < branch_count; ++i)
+ {
+ branch = *(int *)curr_data;
+ curr_data += sizeof(int);
+ if ((ret = save_registry_branch( &curr_data ))) goto done;
+
+ SERVER_START_REQ( flush_key_done )
+ {
+ req->branch = branch;
+ req->timestamp_counter = timestamp_counter;
+ ret = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+ if (ret) break;
+ }
+
+done:
+ release_key_flush_mutex( mutex );
+ free( data );
return ret;
}
diff --git a/server/protocol.def b/server/protocol.def
index 12aa43a43e7..0e62f609459 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1818,6 +1818,18 @@ struct process_info
/* Flush a registry key */
@REQ(flush_key)
obj_handle_t hkey; /* handle to the key */
+@REPLY
+ abstime_t timestamp_counter; /* branch last change timestamp counter */
+ data_size_t total; /* total length needed for data */
+ int branch_count; /* number of registry branches to flush */
+ VARARG(data,bytes); /* registry data */
+@END
+
+
+/* Clear KEY_DIRTY after key flush */
+@REQ(flush_key_done)
+ abstime_t timestamp_counter; /* timestamp counter returned from flush_key */
+ int branch; /* saved registry branch id */
@END
diff --git a/server/registry.c b/server/registry.c
index 3365d4e9a4b..f6d57c579f4 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -90,6 +90,7 @@ struct key
unsigned int flags; /* flags */
timeout_t modif; /* last modification time */
struct list notify_list; /* list of notifications */
+ abstime_t timestamp_counter; /* timestamp counter at last change */
};
/* key flags */
@@ -118,6 +119,8 @@ struct key_value
#define MAX_NAME_LEN 256 /* max. length of a key name */
#define MAX_VALUE_LEN 16383 /* max. length of a value name */
+static abstime_t change_timestamp_counter;
+
/* the root of the registry tree */
static struct key *root_key;
@@ -709,6 +712,7 @@ static struct key *create_key_object( struct object *parent, const struct unicod
key->last_value = -1;
key->values = NULL;
key->modif = modif;
+ key->timestamp_counter = 0;
list_init( &key->notify_list );
if (options & REG_OPTION_CREATE_LINK) key->flags |= KEY_SYMLINK;
@@ -729,23 +733,25 @@ static struct key *create_key_object( struct object *parent, const struct unicod
/* mark a key and all its parents as dirty (modified) */
static void make_dirty( struct key *key )
{
+ ++change_timestamp_counter;
while (key)
{
if (key->flags & (KEY_DIRTY|KEY_VOLATILE)) return; /* nothing to do */
key->flags |= KEY_DIRTY;
+ key->timestamp_counter = change_timestamp_counter;
key = get_parent( key );
}
}
/* mark a key and all its subkeys as clean (not modified) */
-static void make_clean( struct key *key )
+static void make_clean( struct key *key, abstime_t timestamp_counter )
{
int i;
if (key->flags & KEY_VOLATILE) return;
if (!(key->flags & KEY_DIRTY)) return;
- key->flags &= ~KEY_DIRTY;
- for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i] );
+ if (key->timestamp_counter <= timestamp_counter) key->flags &= ~KEY_DIRTY;
+ for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i], timestamp_counter );
}
/* go through all the notifications and send them if necessary */
@@ -2004,6 +2010,7 @@ void init_registry(void)
/* save a registry branch to a file */
static void save_all_subkeys( struct key *key, FILE *f )
{
+ /* Registry format in ntdll/registry.c:save_all_subkeys() should match. */
fprintf( f, "WINE REGISTRY Version 2\n" );
fprintf( f, ";; All keys relative to " );
dump_path( key, NULL, f );
@@ -2192,7 +2199,7 @@ static int save_branch( struct key *key, const char *path )
done:
free( tmp );
- if (ret) make_clean( key );
+ if (ret) make_clean( key, key->timestamp_counter );
return ret;
}
@@ -2240,6 +2247,36 @@ static int is_wow64_thread( struct thread *thread )
return (is_machine_64bit( native_machine ) && !is_machine_64bit( thread->process->machine ));
}
+/* find all the branches inside the specified key or the branch containing the key */
+static void find_branches_for_key( struct key *key, int *branches, int *branch_count )
+{
+ struct key *k;
+ int i;
+
+ *branch_count = 0;
+ for (i = 0; i < save_branch_count; i++)
+ {
+ k = save_branch_info[i].key;
+ while ((k = get_parent(k)))
+ {
+ if (k != key) continue;
+ branches[(*branch_count)++] = i;
+ break;
+ }
+ }
+
+ if (*branch_count) return;
+
+ do
+ {
+ for (i = 0; i < save_branch_count; i++)
+ {
+ if(key != save_branch_info[i].key) continue;
+ branches[(*branch_count)++] = i;
+ return;
+ }
+ } while ((key = get_parent( key )));
+}
/* create a registry key */
DECL_HANDLER(create_key)
@@ -2304,15 +2341,56 @@ DECL_HANDLER(delete_key)
}
}
-/* flush a registry key */
+/* return registry branches snaphot data for flushing key */
DECL_HANDLER(flush_key)
{
struct key *key = get_hkey_obj( req->hkey, 0 );
- if (key)
+ int branches[3], branch_count = 0, i, path_len;
+ char *data;
+
+ if (!key) return;
+
+ reply->total = 0;
+ reply->branch_count = 0;
+ if ((key->flags & KEY_DIRTY) && !(key->flags & KEY_VOLATILE))
+ find_branches_for_key( key, branches, &branch_count );
+ release_object( key );
+
+ reply->timestamp_counter = change_timestamp_counter;
+ for (i = 0; i < branch_count; ++i)
{
- /* we don't need to do anything here with the current implementation */
- release_object( key );
+ if (!(save_branch_info[branches[i]].key->flags & KEY_DIRTY)) continue;
+ ++reply->branch_count;
+ path_len = strlen( save_branch_info[branches[i]].path ) + 1;
+ reply->total += sizeof(int) + sizeof(int) + path_len + save_registry( save_branch_info[branches[i]].key, NULL );
+ }
+ if (reply->total > get_reply_max_size())
+ {
+ set_error( STATUS_BUFFER_TOO_SMALL );
+ return;
}
+
+ if (!(data = set_reply_data_size( reply->total ))) return;
+
+ for (i = 0; i < branch_count; ++i)
+ {
+ if (!(save_branch_info[branches[i]].key->flags & KEY_DIRTY)) continue;
+ *(int *)data = branches[i];
+ data += sizeof(int);
+ path_len = strlen( save_branch_info[branches[i]].path ) + 1;
+ *(int *)data = path_len;
+ data += sizeof(int);
+ memcpy( data, save_branch_info[branches[i]].path, path_len );
+ data += path_len;
+ data += save_registry( save_branch_info[branches[i]].key, data );
+ }
+}
+
+/* clear dirty state after successful registry branch flush */
+DECL_HANDLER(flush_key_done)
+{
+ if (req->branch < save_branch_count) make_clean( save_branch_info[req->branch].key, req->timestamp_counter );
+ else set_error( STATUS_INVALID_PARAMETER );
}
/* enumerate registry subkeys */
--
2.40.1

View File

@ -0,0 +1,111 @@
From 093f14e3ce1753ef399681ec2ccdeecb4443c7a1 Mon Sep 17 00:00:00 2001
From: Paul Gofman <pgofman@codeweavers.com>
Date: Tue, 20 Jun 2023 12:17:36 -0600
Subject: [PATCH 3/3] mountmgr.sys: Perform periodic registry flush instead of
server.
---
dlls/mountmgr.sys/mountmgr.c | 22 ++++++++++++++++++++++
server/registry.c | 26 --------------------------
2 files changed, 22 insertions(+), 26 deletions(-)
diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c
index 1850051a258..ee8d282594f 100644
--- a/dlls/mountmgr.sys/mountmgr.c
+++ b/dlls/mountmgr.sys/mountmgr.c
@@ -618,6 +618,27 @@ static DWORD WINAPI run_loop_thread( void *arg )
return MOUNTMGR_CALL( run_loop, &params );
}
+static DWORD WINAPI registry_flush_thread( void *arg )
+{
+ UNICODE_STRING name = RTL_CONSTANT_STRING( L"\\Registry" );
+ OBJECT_ATTRIBUTES attr;
+ HANDLE root;
+
+ InitializeObjectAttributes( &attr, &name, 0, 0, NULL );
+ if (NtOpenKeyEx( &root, MAXIMUM_ALLOWED, &attr, 0 ))
+ {
+ ERR( "Failed opening root registry key.\n" );
+ return 0;
+ }
+
+ for (;;)
+ {
+ Sleep( 30000 );
+ if (NtFlushKey( root )) ERR( "Failed flushing registry.\n" );
+ }
+
+ return 0;
+}
/* main entry point for the mount point manager driver */
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
@@ -661,6 +682,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
thread = CreateThread( NULL, 0, device_op_thread, NULL, 0, NULL );
CloseHandle( CreateThread( NULL, 0, run_loop_thread, thread, 0, NULL ));
+ CloseHandle( CreateThread( NULL, 0, registry_flush_thread, thread, 0, NULL ));
#ifdef _WIN64
/* create a symlink so that the Wine port overrides key can be edited with 32-bit reg or regedit */
diff --git a/server/registry.c b/server/registry.c
index f6d57c579f4..21566057fa1 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -125,15 +125,12 @@ static abstime_t change_timestamp_counter;
static struct key *root_key;
static const timeout_t ticks_1601_to_1970 = (timeout_t)86400 * (369 * 365 + 89) * TICKS_PER_SEC;
-static const timeout_t save_period = 30 * -TICKS_PER_SEC; /* delay between periodic saves */
-static struct timeout_user *save_timeout_user; /* saving timer */
static enum prefix_type prefix_type;
static const WCHAR wow6432node[] = {'W','o','w','6','4','3','2','N','o','d','e'};
static const WCHAR symlink_value[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e'};
static const struct unicode_str symlink_str = { symlink_value, sizeof(symlink_value) };
-static void set_periodic_save_timer(void);
static struct key_value *find_value( const struct key *key, const struct unicode_str *name, int *index );
/* information about where to save a registry branch */
@@ -1983,9 +1980,6 @@ void init_registry(void)
release_object( hklm );
release_object( hkcu );
- /* start the periodic save timer */
- set_periodic_save_timer();
-
/* create windows directories */
if (!mkdir( "drive_c/windows", 0777 ))
@@ -2203,26 +2197,6 @@ done:
return ret;
}
-/* periodic saving of the registry */
-static void periodic_save( void *arg )
-{
- int i;
-
- if (fchdir( config_dir_fd ) == -1) return;
- save_timeout_user = NULL;
- for (i = 0; i < save_branch_count; i++)
- save_branch( save_branch_info[i].key, save_branch_info[i].path );
- if (fchdir( server_dir_fd ) == -1) fatal_error( "chdir to server dir: %s\n", strerror( errno ));
- set_periodic_save_timer();
-}
-
-/* start the periodic save timer */
-static void set_periodic_save_timer(void)
-{
- if (save_timeout_user) remove_timeout_user( save_timeout_user );
- save_timeout_user = add_timeout_user( save_period, periodic_save, NULL );
-}
-
/* save the modified registry branches to disk */
void flush_registry(void)
{
--
2.40.1

View File

@ -0,0 +1,2 @@
# PR 3124
# This will hopefully show improvements across the board.