Files
DroidDeck/tools/linuxfs/preload/space.c
T
The412Banner b74aa97996 Bring the runtime up to date with Bannerlator, and end a session the way proot can survive
From Bannerlator's feat/linux-gamescope-runtime since this repo was cut:

- Session teardown (4509d788): proot was SIGKILLed, which never lets its
  --kill-on-exit cleanup run; its tracees lived on with no tracer, every
  seccomp-trapped syscall failing with ENOSYS, spinning at a full core for over
  an hour. Now SIGTERM, a grace for proot's own cleanup, SIGKILL only if it
  will not go, then a sweep of the tree it had — snapshotted first, each pid
  checked against its start time.
- The Steam client's controller interposer is its own source
  (fakeinput_steam.cpp, 28922e7c), separate from the one preloaded into Wine.
- The seeder runs on a beat so a prefix the client creates mid-session is
  covered before its install scripts run (6ee86bff).
- GE-Proton and proton-cachyos as compatibility tools: the registrar
  bannerlator-proton-extra and the compat script's adopt_extras (03867656).
- space.c in the session shim: a library's own free space, not the phone's.

The env-vs-args ordering bug (98d38931) does not apply here: nothing is added
after the script name.
2026-09-20 00:55:37 -04:00

80 lines
3.6 KiB
C

/*
* The free space a library reports.
*
* A library's games can live somewhere the folder that names the library does not. The card
* library is exactly that: its steamapps/common is bound to the card, but the directory above it
* belongs to the runtime image in the phone's own storage, and that directory is what the client
* measures. So it offers both install locations with the phone's free space against each, and the
* card's real figure - seventeen gigabytes more, on the device this was found on - never appears.
*
* The number is not only cosmetic: the client refuses an install it believes will not fit. Once
* the phone fills up it would turn down a card install with room to spare, and say nothing about
* why.
*
* Moving the whole library onto the card would fix the number and break something worse. The
* prefixes under steamapps/compatdata want symlinks and file locks, and the card is served over
* FUSE, which gives us neither - that is why they are kept in the runtime image in the first
* place. So the question is answered where the content actually is: a query about a library root
* named in BL_LIBRARY_SPACE is served from its steamapps/common, the bind that points at the
* card. Every other path is passed through untouched.
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statfs.h>
#include <sys/statvfs.h>
/* A library root, or its steamapps, resolved to the directory that holds the games. Returns the
* rewritten path in `buf`, or NULL when `path` is not one of ours and should be left alone. */
static const char *content_of(const char *path, char *buf, size_t len) {
const char *roots = getenv("BL_LIBRARY_SPACE");
if (!path || !roots || !*roots)
return NULL;
size_t path_len = strlen(path);
while (*roots) {
const char *end = strchr(roots, ':');
size_t root_len = end ? (size_t)(end - roots) : strlen(roots);
if (root_len > 0) {
/* The root itself, or the steamapps directly beneath it. Anything deeper is already inside
* a bind and answers for itself. */
int match = (path_len == root_len && !strncmp(path, roots, root_len)) ||
(path_len == root_len + 10 && !strncmp(path, roots, root_len) &&
!strcmp(path + root_len, "/steamapps"));
if (match) {
if (root_len + sizeof("/steamapps/common") > len)
return NULL;
memcpy(buf, roots, root_len);
strcpy(buf + root_len, "/steamapps/common");
return buf;
}
}
if (!end)
break;
roots = end + 1;
}
return NULL;
}
#define FORWARD(name, type) \
int name(const char *path, type *out) { \
static int (*real)(const char *, type *); \
char buf[4096]; \
const char *content; \
if (!real) \
real = dlsym(RTLD_NEXT, #name); \
if (!real) \
return -1; \
content = content_of(path, buf, sizeof(buf)); \
return real(content ? content : path, out); \
}
FORWARD(statfs, struct statfs)
FORWARD(statvfs, struct statvfs)
#ifdef __USE_LARGEFILE64
FORWARD(statfs64, struct statfs64)
FORWARD(statvfs64, struct statvfs64)
#endif