From: Konstantin Demin rockdrilla@gmail.com
try following locations for wineserver directory: - ${XDG_RUNTIME_DIR}/wine - /run/user/${uid}/wine - ${TMPDIR}/wine - only if ${TMPDIR} is owned by user - ${TMPDIR}/.wine-${uid} - /tmp/.wine-${uid}
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=39013
Signed-off-by: Konstantin Demin rockdrilla@gmail.com --- dlls/ntdll/unix/server.c | 16 ++- include/Makefile.in | 1 + include/wine/server_tmpdir.h | 223 +++++++++++++++++++++++++++++++++++ server/request.c | 12 +- 4 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 include/wine/server_tmpdir.h
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 258a959de72..85c6c39a7a1 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -81,6 +81,10 @@ #include "unix_private.h" #include "ddk/wdm.h"
+#ifndef __ANDROID__ +#include "wine/server_tmpdir.h" +#endif + WINE_DEFAULT_DEBUG_CHANNEL(server); WINE_DECLARE_DEBUG_CHANNEL(syscall);
@@ -1303,12 +1307,22 @@ int server_pipe( int fd[2] ) */ static const char *init_server_dir( dev_t dev, ino_t ino ) { +#ifndef __ANDROID__ + static const char * wineserver_tmpdir_errmsg = "error building temporary directory (client)\n"; + char *tmp_dir = NULL; +#endif char *dir = NULL;
#ifdef __ANDROID__ /* there's no /tmp dir on Android */ asprintf( &dir, "%s/.wineserver/server-%llx-%llx", config_dir, (unsigned long long)dev, (unsigned long long)ino ); #else - asprintf( &dir, "/tmp/.wine-%u/server-%llx-%llx", getuid(), (unsigned long long)dev, (unsigned long long)ino ); + tmp_dir = wineserver_tmpdir(); + if (!tmp_dir) fatal_error( wineserver_tmpdir_errmsg ); + + if (asprintf( &dir, "%s/server-%llx-%llx", tmp_dir, (unsigned long long) dev, (unsigned long long) ino ) < 0) + fatal_error( wineserver_tmpdir_errmsg ); + + free( tmp_dir ); #endif return dir; } diff --git a/include/Makefile.in b/include/Makefile.in index a43168baf1f..3b33dab02c3 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -983,6 +983,7 @@ SOURCES = \ wine/schrpc.idl \ wine/server.h \ wine/server_protocol.h \ + wine/server_tmpdir.h \ wine/strmbase.h \ wine/svcctl.idl \ wine/test.h \ diff --git a/include/wine/server_tmpdir.h b/include/wine/server_tmpdir.h new file mode 100644 index 00000000000..5d596e089cf --- /dev/null +++ b/include/wine/server_tmpdir.h @@ -0,0 +1,223 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * (c) 2022-2025, Konstantin Demin + */ + +#ifndef __WINE_WINE_SERVER_TMPDIR_H +#define __WINE_WINE_SERVER_TMPDIR_H 1 + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <dirent.h> +#include <pthread.h> +#include <sys/stat.h> +#include <sys/types.h> + + +static int wineserver_trydir(const char * name, struct stat * st, int per_user) +{ + if ((!name) || (!st)) return 1; + + (void) memset( st, 0, sizeof(struct stat) ); + if (lstat( name, st ) == -1) return 2; + + if (!S_ISDIR( st->st_mode )) + { + errno = ENOTDIR; + return 3; + } + + if (per_user) + { + if (st->st_uid != getuid()) return 4; + if (st->st_mode & 077) return 5; + if ((st->st_mode & 0700) != 0700) return 6; + } + + return 0; +} + +static char * wineserver_stralloc(size_t * len) +{ + size_t x = 0; + void * p = NULL; + + if (!len) return NULL; + if (!(x = (*len))) return NULL; + + /* reserve space for NUL */ + x++; + /* perform naive alignment */ + x -= x % sizeof( size_t ); + /* reserve (extra) space */ + x += sizeof( size_t ); + + /* in case of integer overflow */ + if (x < (*len)) return NULL; + + p = calloc(1, x); + if (!p) return NULL; + + *len = x; + return (char *) p; +} + +/* + * try following locations for wineserver directory: + * - ${XDG_RUNTIME_DIR}/wine + * - /run/user/${uid}/wine + * - ${TMPDIR}/wine - only if ${TMPDIR} is per-user + * - ${TMPDIR}/.wine-${uid} + * - /tmp/.wine-${uid} + */ + +static char * wineserver_tmpdir_impl(void) +{ + const int wineserver_max_uid_len = 11; /* maximum uid length */ + const char * const fmt_shared = "%s/.wine-%u"; + const char * const fmt_per_user = "%s/wine"; + + const char *rootdir = NULL; + char *workdir = NULL, *tmp1 = NULL; + size_t len1 = 0, len2 = 0; + struct stat st1; + + /* try "${XDG_RUNTIME_DIR}/wine" */ + do { + tmp1 = getenv( "XDG_RUNTIME_DIR" ); + if (!tmp1) break; + if (!tmp1[0]) break; + + rootdir = tmp1; + len1 = strlen( tmp1 ); + + if (wineserver_trydir( rootdir, &st1, 1 )) break; + + len2 = len1 + sizeof( "/wine" ); + workdir = wineserver_stralloc( &len2 ); + if ((!workdir) || (!len2)) + { + errno = ENOMEM; + return NULL; + } + + /* NB: safe enough - space is already allocated */ + (void) sprintf( workdir, fmt_per_user, rootdir ); + return workdir; + } while (0); + + /* try "/run/user/${uid}/wine" */ + do { + rootdir = "/run/user"; + if (wineserver_trydir( rootdir, &st1, 0 )) break; + + len1 = sizeof( "/run/user/" ) + wineserver_max_uid_len + sizeof( "/wine" ); + workdir = wineserver_stralloc( &len1 ); + if ((!workdir) || (!len1)) + { + errno = ENOMEM; + return NULL; + } + + /* NB: safe enough - space is already allocated */ + (void) sprintf( workdir, "%s/%u", rootdir, getuid() ); + if (wineserver_trydir( workdir, &st1, 1 )) + { + free( workdir ); workdir = NULL; + break; + } + + len2 = strlen( workdir ); + /* NB: safe enough - space is already allocated */ + (void) strcpy( workdir + len2, "/wine" ); + return workdir; + } while (0); + + /* try somewhere in ${TMPDIR}/ */ + do { + tmp1 = getenv( "TMPDIR" ); + if (!tmp1) break; + if (!tmp1[0]) break; + + rootdir = tmp1; + len1 = strlen( tmp1 ); + + if (wineserver_trydir( rootdir, &st1, 0 )) break; + + if (wineserver_trydir( rootdir, &st1, 1 )) + { + /* ${TMPDIR} is NOT per-user - try "${TMPDIR}/.wine-${uid}" */ + + len2 = len1 + sizeof( "/.wine-" ) + wineserver_max_uid_len; + workdir = wineserver_stralloc( &len2 ); + if ((!workdir) || (!len2)) + { + errno = ENOMEM; + return NULL; + } + + /* NB: safe enough - space is already allocated */ + (void) sprintf(workdir, fmt_shared, rootdir, getuid()); + } + else + { + /* ${TMPDIR} is per-user - try "${TMPDIR}/wine" */ + + len2 = len1 + sizeof( "/wine" ); + workdir = wineserver_stralloc( &len2 ); + if ((!workdir) || (!len2)) + { + errno = ENOMEM; + return NULL; + } + + /* NB: safe enough - space is already allocated */ + (void) sprintf(workdir, fmt_per_user, rootdir); + } + + return workdir; + } while (0); + + /* LAST RESORT: try "/tmp/.wine-${uid}" */ + do { + rootdir = "/tmp"; + if (wineserver_trydir( rootdir, &st1, 0 )) break; + + len1 = sizeof( "/tmp/.wine-" ) + wineserver_max_uid_len; + workdir = wineserver_stralloc( &len1 ); + if ((!workdir) || (!len1)) + { + errno = ENOMEM; + return NULL; + } + + /* NB: safe enough - space is already allocated */ + (void) sprintf( workdir, fmt_shared, rootdir, getuid() ); + return workdir; + } while (0); + + return NULL; +} + +static char * __wineserver_tmpdir = NULL; +static __attribute__((noinline)) void wineserver_tmpdir_once(void) +{ + /* in case of several calls */ + if (__wineserver_tmpdir) return; + + __wineserver_tmpdir = wineserver_tmpdir_impl(); +} + +static __attribute__((noinline)) char * wineserver_tmpdir(void) +{ + static pthread_once_t _once = PTHREAD_ONCE_INIT; + + (void) pthread_once( &_once, wineserver_tmpdir_once ); + return __wineserver_tmpdir; +} + +#endif /* __WINE_WINE_SERVER_TMPDIR_H */ diff --git a/server/request.c b/server/request.c index 432a5918892..af2647c0cd3 100644 --- a/server/request.c +++ b/server/request.c @@ -62,6 +62,10 @@ #include "handle.h" #include "request_handlers.h"
+#ifndef __ANDROID__ +#include "wine/server_tmpdir.h" +#endif + /* Some versions of glibc don't define this */ #ifndef SCM_RIGHTS #define SCM_RIGHTS 1 @@ -591,11 +595,15 @@ static void create_dir( const char *name, struct stat *st ) if (!S_ISDIR(st->st_mode)) fatal_error( "%s is not a directory\n", name ); if (st->st_uid != getuid()) fatal_error( "%s is not owned by you\n", name ); if (st->st_mode & 077) fatal_error( "%s must not be accessible by other users\n", name ); + if ((st->st_mode & 0700) != 0700) fatal_error( "%s must be writeable by you\n", name ); }
/* create the server directory and chdir to it */ static char *create_server_dir( int force ) { +#ifndef __ANDROID__ + static const char * wineserver_tmpdir_errmsg = "error building temporary directory (server)\n"; +#endif const char *prefix = getenv( "WINEPREFIX" ); char *p, *config_dir, *base_dir; struct stat st, st2; @@ -644,8 +652,8 @@ static char *create_server_dir( int force ) if (asprintf( &base_dir, "%s/.wineserver", config_dir ) == -1) fatal_error( "out of memory\n" ); #else - if (asprintf( &base_dir, "/tmp/.wine-%u", getuid() ) == -1) - fatal_error( "out of memory\n" ); + base_dir = wineserver_tmpdir(); + if (!base_dir) fatal_error( wineserver_tmpdir_errmsg ); #endif create_dir( base_dir, &st2 );