ntdll: Add support for growing the initial environment dynamically.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-03-03 09:56:30 +01:00
parent 16b0994412
commit e63b8ead43
1 changed files with 100 additions and 106 deletions

View File

@ -551,19 +551,6 @@ static BOOL is_dynamic_env_var( const char *var )
STARTS_WITH( var, "WINESERVERSOCKET=" )); STARTS_WITH( var, "WINESERVERSOCKET=" ));
} }
static BOOL is_dynamic_env_varW( const WCHAR *var )
{
size_t i;
char name[20];
for (i = 0; i < sizeof(name) && var[i]; i++)
{
name[i] = var[i] < 0x80 ? var[i] : '?';
if (name[i] == '=') return is_dynamic_env_var( name );
}
return FALSE;
}
static unsigned int decode_utf8_char( unsigned char ch, const char **str, const char *strend ) static unsigned int decode_utf8_char( unsigned char ch, const char **str, const char *strend )
{ {
/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */ /* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
@ -1111,22 +1098,18 @@ static const char overrides_help_message[] =
* *
* Return the initial environment. * Return the initial environment.
*/ */
static WCHAR *get_initial_environment( SIZE_T *ret_size ) static WCHAR *get_initial_environment( SIZE_T *pos, SIZE_T *size )
{ {
char **e; char **e;
SIZE_T size = 1;
WCHAR *env, *ptr, *end; WCHAR *env, *ptr, *end;
/* estimate needed size */ /* estimate needed size */
for (e = main_envp; *e; e++) *size = 1;
{ for (e = main_envp; *e; e++) *size += strlen(*e) + 1;
if (is_dynamic_env_var( *e ) || is_special_env_var( *e )) continue;
size += strlen(*e) + 1;
}
if (!(env = malloc( size * sizeof(WCHAR) ))) return NULL; if (!(env = malloc( *size * sizeof(WCHAR) ))) return NULL;
ptr = env; ptr = env;
end = env + size; end = env + *size - 1;
for (e = main_envp; *e && ptr < end; e++) for (e = main_envp; *e && ptr < end; e++)
{ {
char *str = *e; char *str = *e;
@ -1146,89 +1129,116 @@ static WCHAR *get_initial_environment( SIZE_T *ret_size )
if (is_dynamic_env_var( str )) continue; if (is_dynamic_env_var( str )) continue;
ptr += ntdll_umbstowcs( str, strlen(str) + 1, ptr, end - ptr ); ptr += ntdll_umbstowcs( str, strlen(str) + 1, ptr, end - ptr );
} }
*ret_size = (ptr - env) * sizeof(WCHAR); *pos = ptr - env;
return env; return env;
} }
/* append a variable to the environment */ static WCHAR *find_env_var( WCHAR *env, SIZE_T size, const WCHAR *name, SIZE_T namelen )
static void append_envA( WCHAR *env, SIZE_T *pos, const char *name, const char *value )
{ {
SIZE_T i = *pos; WCHAR *p = env;
while (*name) env[i++] = (unsigned char)*name++; while (p < env + size)
env[i++] = '='; {
i += ntdll_umbstowcs( value, strlen(value), env + i, strlen(value) ); if (!wcsnicmp( p, name, namelen ) && p[namelen] == '=') return p;
env[i++] = 0; p += wcslen(p) + 1;
*pos = i; }
return NULL;
} }
static void append_envW( WCHAR *env, SIZE_T *pos, const char *name, const WCHAR *value ) /* set an environment variable, replacing it if it exists */
static void set_env_var( WCHAR **env, SIZE_T *pos, SIZE_T *size,
const WCHAR *name, SIZE_T namelen, const WCHAR *value )
{ {
SIZE_T i = *pos; WCHAR *p;
SIZE_T len;
while (*name) env[i++] = (unsigned char)*name++; /* remove existing string */
env[i++] = '='; if ((p = find_env_var( *env, *pos, name, namelen )))
wcscpy( env + i, value ); {
*pos = i + wcslen( env + i ) + 1; len = wcslen(p) + 1;
memmove( p, p + len, (*pos - (p + len - *env)) * sizeof(WCHAR) );
*pos -= len;
}
if (!value) return;
len = wcslen( value );
if (*pos + namelen + len + 3 > *size)
{
*size = max( *size * 2, *pos + namelen + len + 3 );
*env = realloc( *env, *size * sizeof(WCHAR) );
}
memcpy( *env + *pos, name, namelen * sizeof(WCHAR) );
(*env)[*pos + namelen] = '=';
memcpy( *env + *pos + namelen + 1, value, (len + 1) * sizeof(WCHAR) );
*pos += namelen + len + 2;
}
static void append_envW( WCHAR **env, SIZE_T *pos, SIZE_T *size, const char *name, const WCHAR *value )
{
WCHAR nameW[32];
ascii_to_unicode( nameW, name, strlen(name) + 1 );
set_env_var( env, pos, size, nameW, wcslen(nameW), value );
}
static void append_envA( WCHAR **env, SIZE_T *pos, SIZE_T *size, const char *name, const char *value )
{
if (value)
{
SIZE_T len = strlen(value) + 1;
WCHAR *valueW = malloc( len * sizeof(WCHAR) );
ntdll_umbstowcs( value, len, valueW, len );
append_envW( env, pos, size, name, valueW );
free( valueW );
}
else append_envW( env, pos, size, name, NULL );
} }
/* set an environment variable for one of the wine path variables */ /* set an environment variable for one of the wine path variables */
static void add_path_var( WCHAR *env, SIZE_T *pos, const char *name, const char *path ) static void add_path_var( WCHAR **env, SIZE_T *pos, SIZE_T *size, const char *name, const char *path )
{ {
WCHAR *nt_name; WCHAR *nt_name = NULL;
if (unix_to_nt_file_name( path, &nt_name )) return; if (path && unix_to_nt_file_name( path, &nt_name )) return;
append_envW( env, pos, name, nt_name ); append_envW( env, pos, size, name, nt_name );
free( nt_name ); free( nt_name );
} }
/************************************************************************* /*************************************************************************
* get_dynamic_environment * add_dynamic_environment
* *
* Get the environment variables that can differ between processes. * Add the environment variables that can differ between processes.
*/ */
static WCHAR *get_dynamic_environment( SIZE_T *size ) static void add_dynamic_environment( WCHAR **env, SIZE_T *pos, SIZE_T *size )
{ {
const char *overrides = getenv( "WINEDLLOVERRIDES" ); const char *overrides = getenv( "WINEDLLOVERRIDES" );
SIZE_T alloc, pos = 0;
WCHAR *buffer;
DWORD i; DWORD i;
char str[22]; char str[22];
alloc = 20 * 10; /* 10 variable names */ add_path_var( env, pos, size, "WINEDATADIR", data_dir );
if (data_dir) alloc += strlen( data_dir ) + 9; add_path_var( env, pos, size, "WINEHOMEDIR", home_dir );
if (home_dir) alloc += strlen( home_dir ) + 9; add_path_var( env, pos, size, "WINEBUILDDIR", build_dir );
if (build_dir) alloc += strlen( build_dir ) + 9; add_path_var( env, pos, size, "WINECONFIGDIR", config_dir );
if (config_dir) alloc += strlen( config_dir ) + 9;
if (user_name) alloc += strlen( user_name );
if (overrides) alloc += strlen( overrides );
alloc += strlen(system_locale) + strlen(user_locale);
for (i = 0; dll_paths[i]; i++) alloc += 20 + strlen( dll_paths[i] ) + 9;
if (!(buffer = malloc( alloc * sizeof(WCHAR) ))) return NULL;
if (data_dir) add_path_var( buffer, &pos, "WINEDATADIR", data_dir );
if (home_dir) add_path_var( buffer, &pos, "WINEHOMEDIR", home_dir );
if (build_dir) add_path_var( buffer, &pos, "WINEBUILDDIR", build_dir );
if (config_dir) add_path_var( buffer, &pos, "WINECONFIGDIR", config_dir );
for (i = 0; dll_paths[i]; i++) for (i = 0; dll_paths[i]; i++)
{ {
sprintf( str, "WINEDLLDIR%u", i ); sprintf( str, "WINEDLLDIR%u", i );
add_path_var( buffer, &pos, str, dll_paths[i] ); add_path_var( env, pos, size, str, dll_paths[i] );
} }
if (user_name) append_envA( buffer, &pos, "WINEUSERNAME", user_name ); sprintf( str, "WINEDLLDIR%u", i );
if (overrides) append_envA( buffer, &pos, "WINEDLLOVERRIDES", overrides ); append_envW( env, pos, size, str, NULL );
append_envA( env, pos, size, "WINEUSERNAME", user_name );
append_envA( env, pos, size, "WINEDLLOVERRIDES", overrides );
if (unix_cp.data) if (unix_cp.data)
{ {
sprintf( str, "%u", unix_cp.data[1] ); sprintf( str, "%u", unix_cp.data[1] );
append_envA( buffer, &pos, "WINEUNIXCP", str ); append_envA( env, pos, size, "WINEUNIXCP", str );
} }
append_envA( buffer, &pos, "WINELOCALE", system_locale ); else append_envW( env, pos, size, "WINEUNIXCP", NULL );
if (strcmp( user_locale, system_locale )) append_envA( buffer, &pos, "WINEUSERLOCALE", user_locale ); append_envA( env, pos, size, "WINELOCALE", system_locale );
assert( pos <= alloc ); append_envA( env, pos, size, "WINEUSERLOCALE",
*size = pos * sizeof(WCHAR); strcmp( user_locale, system_locale ) ? user_locale : NULL );
return buffer;
} }
@ -1443,20 +1453,6 @@ static WCHAR *build_command_line( WCHAR **wargv )
} }
/* copy the environment, skipping dynamic strings */
static SIZE_T copy_environment( WCHAR *dst, const WCHAR *src )
{
WCHAR *p;
for (p = dst; *src; src += wcslen( src ) + 1)
{
if (is_dynamic_env_varW( src )) continue;
wcscpy( p, src );
p += wcslen(p) + 1;
}
return p - dst;
}
static inline void copy_unicode_string( WCHAR **src, WCHAR **dst, UNICODE_STRING *str, UINT len ) static inline void copy_unicode_string( WCHAR **src, WCHAR **dst, UNICODE_STRING *str, UINT len )
{ {
str->Buffer = *dst; str->Buffer = *dst;
@ -1482,18 +1478,20 @@ static inline void put_unicode_string( WCHAR *src, WCHAR **dst, UNICODE_STRING *
static RTL_USER_PROCESS_PARAMETERS *build_initial_params(void) static RTL_USER_PROCESS_PARAMETERS *build_initial_params(void)
{ {
RTL_USER_PROCESS_PARAMETERS *params = NULL; RTL_USER_PROCESS_PARAMETERS *params = NULL;
SIZE_T size, env_size = 0, dyn_size = 0; SIZE_T size, env_pos, env_size;
WCHAR *dst; WCHAR *dst;
WCHAR *cmdline = build_command_line( main_wargv + 1 ); WCHAR *cmdline = build_command_line( main_wargv + 1 );
WCHAR *env = get_initial_environment( &env_size ); WCHAR *env = get_initial_environment( &env_pos, &env_size );
WCHAR *dyn_env = get_dynamic_environment( &dyn_size );
NTSTATUS status; NTSTATUS status;
add_dynamic_environment( &env, &env_pos, &env_size );
env[env_pos++] = 0;
size = (sizeof(*params) size = (sizeof(*params)
+ MAX_PATH * sizeof(WCHAR) /* curdir */ + MAX_PATH * sizeof(WCHAR) /* curdir */
+ (wcslen( cmdline ) + 1) * sizeof(WCHAR) /* command line */ + (wcslen( cmdline ) + 1) * sizeof(WCHAR) /* command line */
+ (wcslen( main_wargv[0] ) + 1) * sizeof(WCHAR) /* image path */ + (wcslen( main_wargv[0] ) + 1) * sizeof(WCHAR) /* image path */
+ env_size + dyn_size + sizeof(WCHAR)); + env_pos * sizeof(WCHAR));
status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &size, status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &size,
MEM_COMMIT, PAGE_READWRITE ); MEM_COMMIT, PAGE_READWRITE );
@ -1513,14 +1511,9 @@ static RTL_USER_PROCESS_PARAMETERS *build_initial_params(void)
free( cmdline ); free( cmdline );
params->Environment = dst; params->Environment = dst;
params->EnvironmentSize = env_size + dyn_size + sizeof(WCHAR); params->EnvironmentSize = env_pos * sizeof(WCHAR);
memcpy( dst, env, env_size ); memcpy( dst, env, env_pos * sizeof(WCHAR) );
dst += env_size / sizeof(WCHAR);
memcpy( dst, dyn_env, dyn_size );
dst += dyn_size / sizeof(WCHAR);
*dst = 0;
free( env ); free( env );
free( dyn_env );
get_initial_console( params ); get_initial_console( params );
@ -1533,9 +1526,9 @@ static RTL_USER_PROCESS_PARAMETERS *build_initial_params(void)
*/ */
void init_startup_info(void) void init_startup_info(void)
{ {
WCHAR *src, *dst, *dyn_env; WCHAR *src, *dst, *env;
NTSTATUS status; NTSTATUS status;
SIZE_T size, info_size, env_size, dyn_size = 0; SIZE_T size, info_size, env_size, env_pos;
RTL_USER_PROCESS_PARAMETERS *params = NULL; RTL_USER_PROCESS_PARAMETERS *params = NULL;
startup_info_t *info; startup_info_t *info;
@ -1552,12 +1545,16 @@ void init_startup_info(void)
wine_server_set_reply( req, info, startup_info_size ); wine_server_set_reply( req, info, startup_info_size );
status = wine_server_call( req ); status = wine_server_call( req );
info_size = reply->info_size; info_size = reply->info_size;
env_size = wine_server_reply_size( reply ) - info_size; env_size = (wine_server_reply_size( reply ) - info_size) / sizeof(WCHAR);
} }
SERVER_END_REQ; SERVER_END_REQ;
assert( !status ); assert( !status );
dyn_env = get_dynamic_environment( &dyn_size ); env = malloc( env_size * sizeof(WCHAR) );
memcpy( env, (char *)info + info_size, env_size * sizeof(WCHAR) );
env_pos = env_size - 1;
add_dynamic_environment( &env, &env_pos, &env_size );
env[env_pos++] = 0;
size = (sizeof(*params) size = (sizeof(*params)
+ MAX_PATH * sizeof(WCHAR) /* curdir */ + MAX_PATH * sizeof(WCHAR) /* curdir */
@ -1568,7 +1565,7 @@ void init_startup_info(void)
+ info->desktop_len + sizeof(WCHAR) + info->desktop_len + sizeof(WCHAR)
+ info->shellinfo_len + sizeof(WCHAR) + info->shellinfo_len + sizeof(WCHAR)
+ info->runtime_len + sizeof(WCHAR) + info->runtime_len + sizeof(WCHAR)
+ env_size + dyn_size); + env_pos * sizeof(WCHAR));
status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &size, status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &size,
MEM_COMMIT, PAGE_READWRITE ); MEM_COMMIT, PAGE_READWRITE );
@ -1619,12 +1616,9 @@ void init_startup_info(void)
assert( (char *)src == (char *)info + info_size ); assert( (char *)src == (char *)info + info_size );
params->Environment = dst; params->Environment = dst;
dst += copy_environment( dst, src ); params->EnvironmentSize = env_pos * sizeof(WCHAR);
memcpy( dst, dyn_env, dyn_size ); memcpy( dst, env, env_pos * sizeof(WCHAR) );
dst += dyn_size / sizeof(WCHAR); free( env );
*dst++ = 0;
params->EnvironmentSize = (dst - params->Environment) * sizeof(WCHAR);
free( dyn_env );
free( info ); free( info );
NtCurrentTeb()->Peb->ProcessParameters = params; NtCurrentTeb()->Peb->ProcessParameters = params;
} }