Files
DroidDeck/tools/linuxfs/preload/pathcache.c
T
The412Banner ae94d9db54 feat: optional x86-64 Steam client (Valve's Linux client under FEX) beside the arm64 one (#152)
An opt-in second Steam client (Steam settings, Steam client: x86-64): Valve's
x86-64 Linux client, unchanged, runs under FEX in its own home with its own
game library and the same sign-in. Linux builds run directly, so titles such
as Team Fortress 2 and Half-Life use their Linux versions; Windows games use
DroidDeck-Proton as on arm64. The arm64 client stays the default.

- droiddeck-steam-x64: FEX and the x86-64 rootfs from DroidDeck-Components
  (sha256-pinned), client install and sync from Valve's CDN, sign-in shared
  with the arm64 client, its web helper on the GPU (ANGLE GL over FEX's GL
  thunk, zink, Turnip), Proton wrappers for the arm64 client's tools,
  DroidDeck direct for Linux builds, SD-card library (x86-64/ beside the
  arm64 one), repair.
- FEX 2610 built by build-fex.yml with thunks and three patches: an openat2
  fallback without openat2 (proot), newer-clang thunkgen, MAP_32BIT
  exhaustion returning ENOMEM. libblsession resolves RESOLVE_IN_ROOT paths
  inside the root.
- Session: x86-64 gating, shortcuts and compatibility tools per client,
  logs from the client that ran.
- App: client setting with the FEX version, an x86-64 chip on the Games
  header, the active client's library, art, storage, downloads, repair,
  Offline Mode, Decky, desktop mode, optional VAC diagnostics; per-client
  state for art, the content log, repair and leftovers.
- Strings in every locale.
- Review fixes: the library cache is saved only by the newest scan; an
  abandoned session's Steam logs come from the client it recorded; distinct
  VAC console names; FEXInterpreter in CPU pinning; clearer VAC hint in
  pl, ru, uk, cs and bg.
2026-10-17 23:57:52 -04:00

180 lines
4.9 KiB
C

#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define PATH_CACHE_SLOTS 256
#define PATH_CACHE_KEY_MAX 512
#define PATH_CACHE_TTL_NS 2000000000LL
enum { KIND_REALPATH, KIND_ACCESS };
struct entry {
long long stamp;
unsigned hash;
int kind;
int mode;
int flags;
char key[PATH_CACHE_KEY_MAX];
char *value;
};
static struct entry table[PATH_CACHE_SLOTS];
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static long long now_ns(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC_COARSE, &t);
return (long long)t.tv_sec * 1000000000LL + t.tv_nsec;
}
static int enabled(void) {
static int cached = -1;
const char *off;
if (cached >= 0) return cached;
off = getenv("BL_NO_PATH_CACHE");
cached = strcmp(program_invocation_short_name, "steam") == 0 &&
(strstr(program_invocation_name, "steamrtarm64") != NULL ||
strstr(program_invocation_name, "steamrt64") != NULL) &&
!(off != NULL && off[0] == '1');
return cached;
}
static unsigned hash_of(const char *s, int kind, int mode, int flags) {
unsigned h = 2166136261u ^ (unsigned)(kind * 31 + mode * 7 + flags);
while (*s) h = (h ^ (unsigned char)*s++) * 16777619u;
return h;
}
static int cacheable(const char *path) {
return path != NULL && path[0] == '/' && strlen(path) < PATH_CACHE_KEY_MAX && enabled();
}
static struct entry *find(const char *path, int kind, int mode, int flags, unsigned h) {
struct entry *e = &table[h % PATH_CACHE_SLOTS];
if (e->stamp == 0 || e->hash != h || e->kind != kind || e->mode != mode || e->flags != flags) return NULL;
if (now_ns() - e->stamp > PATH_CACHE_TTL_NS || strcmp(e->key, path) != 0) return NULL;
return e;
}
static void store(const char *path, int kind, int mode, int flags, unsigned h, const char *value) {
struct entry *e = &table[h % PATH_CACHE_SLOTS];
char *copy = value != NULL ? strdup(value) : NULL;
if (value != NULL && copy == NULL) return;
free(e->value);
e->value = copy;
e->hash = h;
e->kind = kind;
e->mode = mode;
e->flags = flags;
memcpy(e->key, path, strlen(path) + 1);
e->stamp = now_ns();
}
__attribute__((visibility("hidden")))
char *bl_cached_realpath(const char *path, char *resolved, char *(*real)(const char *, char *)) {
char out[PATH_MAX];
unsigned h;
struct entry *e;
if (!cacheable(path)) return real(path, resolved);
h = hash_of(path, KIND_REALPATH, 0, 0);
pthread_mutex_lock(&lock);
e = find(path, KIND_REALPATH, 0, 0, h);
if (e != NULL && e->value != NULL) {
size_t n = strlen(e->value) + 1;
char *result = resolved != NULL ? resolved : malloc(n);
if (result != NULL) memcpy(result, e->value, n);
pthread_mutex_unlock(&lock);
return result;
}
pthread_mutex_unlock(&lock);
if (real(path, out) == NULL) return NULL;
pthread_mutex_lock(&lock);
store(path, KIND_REALPATH, 0, 0, h, out);
pthread_mutex_unlock(&lock);
if (resolved != NULL) {
memcpy(resolved, out, strlen(out) + 1);
return resolved;
}
return strdup(out);
}
static int cached_access(const char *path, int mode, int flags, int (*check)(const char *, int, int)) {
unsigned h;
int hit;
int result;
if (!cacheable(path)) return check(path, mode, flags);
h = hash_of(path, KIND_ACCESS, mode, flags);
pthread_mutex_lock(&lock);
hit = find(path, KIND_ACCESS, mode, flags, h) != NULL;
pthread_mutex_unlock(&lock);
if (hit) return 0;
result = check(path, mode, flags);
if (result == 0) {
pthread_mutex_lock(&lock);
store(path, KIND_ACCESS, mode, flags, h, NULL);
pthread_mutex_unlock(&lock);
}
return result;
}
static int (*next_faccessat(void))(int, const char *, int, int) {
static int (*real)(int, const char *, int, int);
if (real == NULL) real = (int (*)(int, const char *, int, int))dlsym(RTLD_NEXT, "faccessat");
return real;
}
__attribute__((visibility("hidden"))) int bl_binfmt_runnable(const char *path, int mode);
static int check_cwd(const char *path, int mode, int flags) {
int (*real)(int, const char *, int, int) = next_faccessat();
int result;
if (real == NULL) {
errno = ENOSYS;
return -1;
}
result = real(AT_FDCWD, path, mode, flags);
return result != 0 && errno == EACCES && bl_binfmt_runnable(path, mode) ? 0 : result;
}
int access(const char *path, int mode) {
return cached_access(path, mode, 0, check_cwd);
}
int euidaccess(const char *path, int mode) {
return cached_access(path, mode, AT_EACCESS, check_cwd);
}
int eaccess(const char *path, int mode) {
return cached_access(path, mode, AT_EACCESS, check_cwd);
}
int faccessat(int dirfd, const char *path, int mode, int flags) {
int (*real)(int, const char *, int, int);
if (dirfd == AT_FDCWD) {
return cached_access(path, mode, flags, check_cwd);
}
real = next_faccessat();
if (real == NULL) {
errno = ENOSYS;
return -1;
}
return real(dirfd, path, mode, flags);
}