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,522 @@
From 7ab1c4562cddd13b79dd6080f199e2687df98f70 Mon Sep 17 00:00:00 2001
From: Paul Gofman <pgofman@codeweavers.com>
Date: Mon, 19 Jun 2023 17:50:33 -0600
Subject: [PATCH 1/3] ntdll: Reimplement NtSaveKey() on the client side.
---
dlls/ntdll/unix/registry.c | 290 ++++++++++++++++++++++++++++++++++++-
server/protocol.def | 14 +-
server/registry.c | 124 +++++++++++++---
3 files changed, 399 insertions(+), 29 deletions(-)
diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c
index a24d6e7c267..b63c7a66925 100644
--- a/dlls/ntdll/unix/registry.c
+++ b/dlls/ntdll/unix/registry.c
@@ -27,6 +27,8 @@
#include <stdarg.h>
#include <string.h>
+#include <unistd.h>
+#include <errno.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -68,6 +70,248 @@ NTSTATUS open_hkcu_key( const char *path, HANDLE *key )
return NtCreateKey( key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL );
}
+/* dump a Unicode string with proper escaping */
+int dump_strW( const WCHAR *str, data_size_t len, FILE *f, const char escape[2] )
+{
+ static const char escapes[32] = ".......abtnvfr.............e....";
+ char buffer[256];
+ char *pos = buffer;
+ int count = 0;
+
+ for (len /= sizeof(WCHAR); len; str++, len--)
+ {
+ if (pos > buffer + sizeof(buffer) - 8)
+ {
+ fwrite( buffer, pos - buffer, 1, f );
+ count += pos - buffer;
+ pos = buffer;
+ }
+ if (*str > 127) /* hex escape */
+ {
+ if (len > 1 && str[1] < 128 && isxdigit( (char)str[1] ))
+ pos += sprintf( pos, "\\x%04x", *str );
+ else
+ pos += sprintf( pos, "\\x%x", *str );
+ continue;
+ }
+ if (*str < 32) /* octal or C escape */
+ {
+ if (!*str && len == 1) continue; /* do not output terminating NULL */
+ if (escapes[*str] != '.')
+ pos += sprintf( pos, "\\%c", escapes[*str] );
+ else if (len > 1 && str[1] >= '0' && str[1] <= '7')
+ pos += sprintf( pos, "\\%03o", *str );
+ else
+ pos += sprintf( pos, "\\%o", *str );
+ continue;
+ }
+ if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\';
+ *pos++ = *str;
+ }
+ fwrite( buffer, pos - buffer, 1, f );
+ count += pos - buffer;
+ return count;
+}
+
+struct saved_key
+{
+ data_size_t namelen;
+ WCHAR *name;
+ data_size_t classlen;
+ WCHAR *class;
+ int value_count;
+ int subkey_count;
+ unsigned int is_symlink;
+ timeout_t modif;
+ struct saved_key *parent;
+};
+
+/* read serialized key data */
+static char *fill_saved_key( struct saved_key *key, struct saved_key *parent, char *data )
+{
+ key->parent = parent;
+ key->namelen = *(data_size_t *)data;
+ data += sizeof(data_size_t);
+ key->name = (WCHAR *)data;
+ data += key->namelen;
+ key->classlen = *(data_size_t *)data;
+ data += sizeof(data_size_t);
+ key->class = (WCHAR *)data;
+ data += key->classlen;
+ key->value_count = *(int *)data;
+ data += sizeof(int);
+ key->subkey_count = *(int *)data;
+ data += sizeof(int);
+ key->is_symlink = *(unsigned int *)data;
+ data += sizeof(unsigned int);
+ key->modif = *(timeout_t *)data;
+ data += sizeof(timeout_t);
+
+ return data;
+}
+
+/* dump serialized key full path */
+static char *dump_parents( char *data, FILE *f, int count )
+{
+ data_size_t len;
+ WCHAR *name;
+
+ len = *(data_size_t *)data;
+ data += sizeof(data_size_t);
+ name = (WCHAR *)data;
+ data += len;
+
+ if (count > 1)
+ {
+ data = dump_parents( data, f, count - 1);
+ fprintf( f, "\\\\" );
+ }
+ dump_strW( name, len, f, "[]" );
+ return data;
+}
+
+/* dump the full path of a key */
+static void dump_path( const struct saved_key *key, const struct saved_key *base, FILE *f )
+{
+ if (key->parent && key->parent != base)
+ {
+ dump_path( key->parent, base, f );
+ fprintf( f, "\\\\" );
+ }
+ dump_strW( key->name, key->namelen, f, "[]" );
+}
+
+/* dump a value to a text file */
+static char *dump_value( char *data, FILE *f )
+{
+ unsigned int i, dw;
+ int count;
+ data_size_t namelen, valuelen;
+ char *valuedata;
+ WCHAR *name;
+ unsigned int type;
+
+ namelen = *(data_size_t *)data;
+ data += sizeof(data_size_t);
+ name = (WCHAR *)data;
+ data += namelen;
+ type = *(unsigned int *)data;
+ data += sizeof(unsigned int);
+ valuelen = *(data_size_t *)data;
+ data += sizeof(data_size_t);
+ valuedata = data;
+ data += valuelen;
+
+ if (namelen)
+ {
+ fputc( '\"', f );
+ count = 1 + dump_strW( name, namelen, f, "\"\"" );
+ count += fprintf( f, "\"=" );
+ }
+ else count = fprintf( f, "@=" );
+
+ switch(type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ case REG_MULTI_SZ:
+ /* only output properly terminated strings in string format */
+ if (valuelen < sizeof(WCHAR)) break;
+ if (valuelen % sizeof(WCHAR)) break;
+ if (((WCHAR *)valuedata)[valuelen / sizeof(WCHAR) - 1]) break;
+ if (type != REG_SZ) fprintf( f, "str(%x):", type );
+ fputc( '\"', f );
+ dump_strW( (WCHAR *)valuedata, valuelen, f, "\"\"" );
+ fprintf( f, "\"\n" );
+ return data;
+
+ case REG_DWORD:
+ if (valuelen != sizeof(dw)) break;
+ memcpy( &dw, valuedata, sizeof(dw) );
+ fprintf( f, "dword:%08x\n", dw );
+ return data;
+ }
+
+ if (type == REG_BINARY) count += fprintf( f, "hex:" );
+ else count += fprintf( f, "hex(%x):", type );
+ for (i = 0; i < valuelen; i++)
+ {
+ count += fprintf( f, "%02x", *((unsigned char *)valuedata + i) );
+ if (i < valuelen-1)
+ {
+ fputc( ',', f );
+ if (++count > 76)
+ {
+ fprintf( f, "\\\n " );
+ count = 2;
+ }
+ }
+ }
+ fputc( '\n', f );
+ return data;
+}
+
+/* save a registry key and all its subkeys to a text file */
+static char *save_subkeys( char *data, struct saved_key *parent, struct saved_key *base, FILE *f )
+{
+ struct saved_key key;
+ int i;
+
+ if (!base) base = &key;
+ data = fill_saved_key( &key, parent, data );
+
+ /* save key if it has either some values or no subkeys, or needs special options */
+ /* keys with no values but subkeys are saved implicitly by saving the subkeys */
+ if ((key.value_count > 0) || !key.subkey_count || key.classlen || key.is_symlink)
+ {
+ fprintf( f, "\n[" );
+ if (parent) dump_path( &key, base, f );
+ fprintf( f, "] %u\n", (unsigned int)((key.modif - SECS_1601_TO_1970 * TICKSPERSEC) / TICKSPERSEC) );
+ fprintf( f, "#time=%x%08x\n", (unsigned int)(key.modif >> 32), (unsigned int)key.modif );
+ if (key.classlen)
+ {
+ fprintf( f, "#class=\"" );
+ dump_strW( key.class, key.classlen, f, "\"\"" );
+ fprintf( f, "\"\n" );
+ }
+ if (key.is_symlink) fputs( "#link\n", f );
+ for (i = 0; i < key.value_count; i++) data = dump_value( data, f );
+ }
+ for (i = 0; i < key.subkey_count; i++) data = save_subkeys( data, &key, base, f );
+ return data;
+}
+
+/* save a registry branch to a file */
+static void save_all_subkeys( char *data, FILE *f )
+{
+ enum prefix_type prefix_type;
+ int parent_count;
+
+ prefix_type = *(int *)data;
+ data += sizeof(int);
+
+ parent_count = *(int *)data;
+ data += sizeof(int);
+
+ fprintf( f, "WINE REGISTRY Version 2\n" );
+ fprintf( f, ";; All keys relative to " );
+ data = dump_parents( data, f, parent_count );
+ fprintf( f, "\n" );
+
+ switch (prefix_type)
+ {
+ case PREFIX_32BIT:
+ fprintf( f, "\n#arch=win32\n" );
+ break;
+ case PREFIX_64BIT:
+ fprintf( f, "\n#arch=win64\n" );
+ break;
+ default:
+ break;
+ }
+ save_subkeys( data, NULL, NULL, f );
+}
+
/******************************************************************************
* NtCreateKey (NTDLL.@)
@@ -776,17 +1020,53 @@ NTSTATUS WINAPI NtUnloadKey( OBJECT_ATTRIBUTES *attr )
*/
NTSTATUS WINAPI NtSaveKey( HANDLE key, HANDLE file )
{
+ data_size_t size = 0;
unsigned int ret;
+ char *data = NULL;
+ int fd, fd2, needs_close = 0;
+ FILE *f;
TRACE( "(%p,%p)\n", key, file );
- SERVER_START_REQ( save_registry )
+ while (1)
{
- req->hkey = wine_server_obj_handle( key );
- req->file = wine_server_obj_handle( file );
- ret = wine_server_call( req );
+ SERVER_START_REQ( save_registry )
+ {
+ req->hkey = wine_server_obj_handle( key );
+ if (size) wine_server_set_reply( req, data, size );
+ ret = wine_server_call( req );
+ size = reply->total;
+ }
+ SERVER_END_REQ;
+
+ if (!ret) break;
+ free( data );
+ if (ret != STATUS_BUFFER_TOO_SMALL) return ret;
+ if (!(data = malloc( size )))
+ {
+ ERR( "No memory.\n" );
+ return STATUS_NO_MEMORY;
+ }
}
- SERVER_END_REQ;
+
+ if ((ret = server_get_unix_fd( file, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))) goto done;
+ if ((fd2 = dup( fd )) == -1)
+ {
+ ret = errno_to_status( errno );
+ goto done;
+ }
+ if (!(f = fdopen( fd2, "w" )))
+ {
+ close( fd2 );
+ ret = errno_to_status( errno );
+ goto done;
+ }
+ save_all_subkeys( data, f );
+ if (fclose(f)) ret = errno_to_status( errno );
+
+done:
+ if (needs_close) close( fd );
+ free( data );
return ret;
}
diff --git a/server/protocol.def b/server/protocol.def
index 766888894d8..12aa43a43e7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1898,11 +1898,19 @@ struct process_info
@END
-/* Save a registry branch to a file */
+/* Return full registry branch non-volatile data for saving */
@REQ(save_registry)
- obj_handle_t hkey; /* key to save */
- obj_handle_t file; /* file to save to */
+ obj_handle_t hkey; /* key to save */
+@REPLY
+ data_size_t total; /* total length needed for data */
+ VARARG(data,bytes); /* registry data */
@END
+enum prefix_type
+{
+ PREFIX_UNKNOWN,
+ PREFIX_32BIT,
+ PREFIX_64BIT,
+};
/* Add a registry key change notification */
diff --git a/server/registry.c b/server/registry.c
index 8f092965d92..3365d4e9a4b 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -124,7 +124,7 @@ 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_UNKNOWN, PREFIX_32BIT, PREFIX_64BIT } prefix_type;
+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'};
@@ -2022,29 +2022,104 @@ static void save_all_subkeys( struct key *key, FILE *f )
save_subkeys( key, key, f );
}
-/* save a registry branch to a file handle */
-static void save_registry( struct key *key, obj_handle_t handle )
+static data_size_t serialize_value( const struct key_value *value, char *buf )
{
- struct file *file;
- int fd;
+ data_size_t size;
- if (!(file = get_file_obj( current->process, handle, FILE_WRITE_DATA ))) return;
- fd = dup( get_file_unix_fd( file ) );
- release_object( file );
- if (fd != -1)
+ size = sizeof(data_size_t) + value->namelen + sizeof(unsigned int) + sizeof(data_size_t) + value->len;
+ if (!buf) return size;
+
+ *(data_size_t *)buf = value->namelen;
+ buf += sizeof(data_size_t);
+ memcpy( buf, value->name, value->namelen );
+ buf += value->namelen;
+
+ *(unsigned int *)buf = value->type;
+ buf += sizeof(unsigned int);
+
+ *(data_size_t *)buf = value->len;
+ buf += sizeof(data_size_t);
+ memcpy( buf, value->data, value->len );
+
+ return size;
+}
+
+/* save a registry key with subkeys to a buffer */
+static data_size_t serialize_key( const struct key *key, char *buf )
+{
+ data_size_t size;
+ int subkey_count, i;
+
+ if (key->flags & KEY_VOLATILE) return 0;
+
+ size = sizeof(data_size_t) + key->obj.name->len + sizeof(data_size_t) + key->classlen + sizeof(int) + sizeof(int)
+ + sizeof(unsigned int) + sizeof(timeout_t);
+ for (i = 0; i <= key->last_value; i++)
+ size += serialize_value( &key->values[i], buf ? buf + size : NULL );
+ subkey_count = 0;
+ for (i = 0; i <= key->last_subkey; i++)
{
- FILE *f = fdopen( fd, "w" );
- if (f)
- {
- save_all_subkeys( key, f );
- if (fclose( f )) file_set_error();
- }
- else
- {
- file_set_error();
- close( fd );
- }
+ if (key->subkeys[i]->flags & KEY_VOLATILE) continue;
+ size += serialize_key( key->subkeys[i], buf ? buf + size : NULL );
+ ++subkey_count;
+ }
+ if (!buf) return size;
+
+ *(data_size_t *)buf = key->obj.name->len;
+ buf += sizeof(data_size_t);
+ memcpy( buf, key->obj.name->name, key->obj.name->len );
+ buf += key->obj.name->len;
+
+ *(data_size_t *)buf = key->classlen;
+ buf += sizeof(data_size_t);
+ memcpy( buf, key->class, key->classlen );
+ buf += key->classlen;
+
+ *(int *)buf = key->last_value + 1;
+ buf += sizeof(int);
+
+ *(int *)buf = subkey_count;
+ buf += sizeof(int);
+
+ *(unsigned int *)buf = key->flags & KEY_SYMLINK;
+ buf += sizeof(unsigned int);
+
+ *(timeout_t *)buf = key->modif;
+
+ return size;
+}
+
+/* save registry branch to buffer */
+static data_size_t save_registry( const struct key *key, char *buf )
+{
+ int *parent_count = NULL;
+ const struct key *parent;
+ data_size_t size;
+
+ size = sizeof(int) + sizeof(int);
+ if (buf)
+ {
+ *(int *)buf = prefix_type;
+ buf += sizeof(int);
+ parent_count = (int *)buf;
+ buf += sizeof(int);
+ *parent_count = 0;
}
+
+ parent = key;
+ do
+ {
+ size += sizeof(data_size_t) + parent->obj.name->len;
+ if (!buf) continue;
+ ++*parent_count;
+ *(data_size_t *)buf = parent->obj.name->len;
+ buf += sizeof(data_size_t);
+ memcpy( buf, parent->obj.name->name, parent->obj.name->len );
+ buf += parent->obj.name->len;
+ } while ((parent = get_parent( parent )));
+
+ size += serialize_key( key, buf );
+ return size;
}
/* save a registry branch to a file */
@@ -2371,6 +2446,7 @@ DECL_HANDLER(unload_registry)
DECL_HANDLER(save_registry)
{
struct key *key;
+ char *data;
if (!thread_single_check_privilege( current, SeBackupPrivilege ))
{
@@ -2380,7 +2456,13 @@ DECL_HANDLER(save_registry)
if ((key = get_hkey_obj( req->hkey, 0 )))
{
- save_registry( key, req->file );
+ reply->total = save_registry( key, NULL );
+ if (reply->total <= get_reply_max_size())
+ {
+ if ((data = set_reply_data_size( reply->total )))
+ save_registry( key, data );
+ }
+ else set_error( STATUS_BUFFER_TOO_SMALL );
release_object( key );
}
}
--
2.40.1

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.