mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-09-13 09:17:20 -07:00
Added ntdll_reg_flush patchset
This commit is contained in:
parent
95679a2f40
commit
22ba0ef2a3
@ -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
|
||||
|
410
patches/ntdll_reg_flush/0002-ntdll-Implement-NtFlushKey.patch
Normal file
410
patches/ntdll_reg_flush/0002-ntdll-Implement-NtFlushKey.patch
Normal 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
|
||||
|
@ -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, ¶ms );
|
||||
}
|
||||
|
||||
+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
|
||||
|
2
patches/ntdll_reg_flush/definition
Normal file
2
patches/ntdll_reg_flush/definition
Normal file
@ -0,0 +1,2 @@
|
||||
# PR 3124
|
||||
# This will hopefully show improvements across the board.
|
Loading…
Reference in New Issue
Block a user