mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-01-28 22:04:43 -08:00
Added ntdll_reg_flush patchset
This commit is contained in:
parent
95679a2f40
commit
22ba0ef2a3
File diff suppressed because it is too large
Load Diff
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…
x
Reference in New Issue
Block a user