Files
DroidDeck/tools/tests/test_storage_owner_preload.py
T
MaxsTechReview b9dbd006be fix(storage): Windows installs on a natively mounted card no longer ask for an administrator (#2100)
* fix(storage): Windows installs on a natively mounted card no longer ask for an administrator

Cards mounted directly as FAT, exFAT or NTFS were not treated as Android
storage, so wineserver reported an Anonymous owner and refused permission
changes. Its chmod on these volumes also no longer fails the request.

* fix(storage): keep NTFS owners and the chmod error
2026-11-05 00:47:38 -05:00

130 lines
4.4 KiB
Python

import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
PRELOAD = Path(__file__).resolve().parents[1] / "linuxfs/preload"
FAKE = r"""
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/syscall.h>
#include <unistd.h>
uid_t getuid(void) {
return (uid_t)syscall(SYS_getuid) + 1;
}
int fstatfs(int fd, struct statfs *out) {
char link[32], path[4096];
snprintf(link, sizeof(link), "/proc/self/fd/%d", fd);
long n = syscall(SYS_readlinkat, AT_FDCWD, link, path, sizeof(path) - 1);
path[n > 0 ? n : 0] = '\0';
int result = (int)syscall(SYS_fstatfs, fd, out);
if (result == 0 && strstr(path, "/fuse/")) out->f_type = 0x65735546;
if (result == 0 && strstr(path, "/sdcardfs/")) out->f_type = 0x5dca2df5;
if (result == 0 && strstr(path, "/ext4/")) out->f_type = 0xef53;
if (result == 0 && strstr(path, "/fat/")) out->f_type = 0x4d44;
if (result == 0 && strstr(path, "/exfat/")) out->f_type = 0x2011bab0;
if (result == 0 && strstr(path, "/ntfs/")) out->f_type = 0x5346544e;
if (strstr(path, "/broken/")) {
errno = EIO;
return -1;
}
return result;
}
int fchmod(int fd, mode_t mode) {
(void)fd;
(void)mode;
errno = EPERM;
return -1;
}
"""
PROGRAM = r"""
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
int main(int argc, char **argv) {
uid_t real = (uid_t)syscall(SYS_getuid);
for (int i = 1; i < argc; i++) {
int fd = open(argv[i], O_RDONLY);
struct stat st;
errno = 1234;
int result = fstat(fd, &st);
printf("%s %d\n", st.st_uid == real ? "real" : st.st_uid == real + 1 ? "user" : "other", result == 0 && errno == 1234);
errno = 0;
printf("%s\n", fchmod(fd, 0755) == 0 ? "chmod-ok" : errno == EPERM ? "chmod-denied" : "chmod-other");
close(fd);
}
return 0;
}
"""
@unittest.skipUnless(shutil.which("cc"), "needs a C compiler")
class StorageOwnerPreloadTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temp = tempfile.TemporaryDirectory()
cls.dir = Path(cls.temp.name)
build = cls.dir / "build"
build.mkdir()
(build / "fake.c").write_text(FAKE)
(build / "program.c").write_text(PROGRAM)
cls.preload = build / "libblsession.so"
cls.fake = build / "fake.so"
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-Wall", "-pthread", "-o", str(cls.preload),
*map(str, sorted(PRELOAD.glob("*.c"))), "-ldl"], check=True)
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-o", str(cls.fake), str(build / "fake.c")], check=True)
cls.wineserver = build / "wineserver"
subprocess.run(["cc", "-O2", "-o", str(cls.wineserver), str(build / "program.c")], check=True)
cls.other = build / "wine64-preloader"
shutil.copy(cls.wineserver, cls.other)
for kind in ("fuse", "sdcardfs", "ext4", "fat", "exfat", "ntfs", "broken"):
(cls.dir / kind).mkdir()
(cls.dir / kind / "file").write_text("x")
@classmethod
def tearDownClass(cls):
cls.temp.cleanup()
def owners(self, program, *kinds):
env = dict(os.environ, LD_PRELOAD="%s:%s" % (self.preload, self.fake))
env.pop("BL_STORAGE_DIAGNOSTICS", None)
paths = [str(self.dir / kind / "file") for kind in kinds]
result = subprocess.run([str(program)] + paths, env=env, capture_output=True, text=True, check=True)
return result.stdout.split()
def test_wineserver_owns_what_is_on_android_storage(self):
self.assertEqual(["user", "1", "chmod-ok"] * 2, self.owners(self.wineserver, "fuse", "sdcardfs"))
def test_wineserver_owns_what_is_on_a_native_card(self):
self.assertEqual(["user", "1", "chmod-ok"] * 2, self.owners(self.wineserver, "fat", "exfat"))
def test_other_filesystems_keep_their_owner(self):
self.assertEqual(["real", "1", "chmod-denied"] * 2, self.owners(self.wineserver, "ext4", "ntfs"))
def test_chmod_keeps_its_error_when_the_volume_is_unknown(self):
self.assertEqual(["real", "1", "chmod-denied"], self.owners(self.wineserver, "broken"))
def test_other_programs_see_the_real_owner(self):
self.assertEqual(["real", "1", "chmod-denied"] * 2, self.owners(self.other, "fuse", "sdcardfs"))
if __name__ == "__main__":
unittest.main()