mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 11:01:07 +00:00
99 lines
4.1 KiB
Python
99 lines
4.1 KiB
Python
import os
|
|
from pathlib import Path
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-library-perms"
|
|
|
|
|
|
class LibraryPermsTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.restore)
|
|
self.root = Path(self.temp.name)
|
|
self.home = self.root / "home"
|
|
self.steam = self.home / ".local/share/Steam"
|
|
self.card = self.root / "card"
|
|
self.stores = self.root / "stores"
|
|
(self.steam / "steamapps").mkdir(parents=True)
|
|
(self.steam / "steamapps/libraryfolders.vdf").write_text(
|
|
'"libraryfolders"\n{\n\t"0"\n\t{\n\t\t"path"\t\t"%s"\n\t}\n\t"1"\n\t{\n\t\t"path"\t\t"%s"\n\t}\n}\n'
|
|
% (self.steam, self.card))
|
|
self.locked = []
|
|
|
|
def restore(self):
|
|
for path in self.locked:
|
|
if path.exists() and not path.is_symlink():
|
|
path.chmod(0o700 if path.is_dir() else 0o600)
|
|
for path in sorted(self.root.rglob("*"), key=lambda p: len(p.parts), reverse=True):
|
|
if path.is_dir() and not path.is_symlink():
|
|
path.chmod(0o700)
|
|
self.temp.cleanup()
|
|
|
|
def file(self, path, mode=0o400):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text("x")
|
|
path.chmod(mode)
|
|
self.locked.append(path)
|
|
return path
|
|
|
|
def folder(self, path, mode=0o500):
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
self.locked.append(path)
|
|
return path
|
|
|
|
def run_script(self):
|
|
env = dict(os.environ, HOME=str(self.home), DROIDDECK_STORES_ROOT=str(self.stores))
|
|
for path in reversed(self.locked):
|
|
if path.is_dir():
|
|
path.chmod(0o500)
|
|
return subprocess.run([sys.executable, str(SCRIPT)], env=env, check=True, capture_output=True, text=True).stdout
|
|
|
|
def mode(self, path):
|
|
return stat.S_IMODE(os.lstat(path).st_mode)
|
|
|
|
def test_read_only_game_files_and_folders_become_writable_in_every_library(self):
|
|
ini = self.file(self.steam / "steamapps/compatdata/3712566526/pfx/drive_c/users/steamuser/Documents/My Games/FalloutNV/FALLOUT.INI")
|
|
data = self.folder(self.steam / "steamapps/common/Game/Data")
|
|
game = self.file(data / "game.esm", 0o444)
|
|
card = self.file(self.card / "steamapps/common/Other/settings.ini")
|
|
gog = self.file(self.home / "Games/Stores/GOG/Fallout New Vegas/Fallout_default.ini")
|
|
store = self.file(self.stores / "abc/Game/config.cfg")
|
|
out = self.run_script()
|
|
self.assertEqual(self.mode(ini) & 0o600, 0o600)
|
|
self.assertEqual(self.mode(game), 0o644)
|
|
self.assertEqual(self.mode(data) & 0o700, 0o700)
|
|
for path in (card, gog, store):
|
|
self.assertEqual(self.mode(path) & 0o600, 0o600, path)
|
|
self.assertIn("made writable", out)
|
|
|
|
def test_proton_and_steam_runtimes_are_left_as_valve_ships_them(self):
|
|
proton = self.file(self.steam / "steamapps/common/Proton Experimental (ARM64)/files/lib/wine/a.so", 0o444)
|
|
runtime = self.file(self.card / "steamapps/common/SteamLinuxRuntime_sniper/run", 0o555)
|
|
nested = self.file(self.steam / "steamapps/common/Game/Proton notes.txt")
|
|
self.run_script()
|
|
self.assertEqual(self.mode(proton), 0o444)
|
|
self.assertEqual(self.mode(runtime), 0o555)
|
|
self.assertEqual(self.mode(nested) & 0o600, 0o600)
|
|
|
|
def test_symlinks_are_not_followed(self):
|
|
outside = self.file(self.root / "outside/keep.ini")
|
|
link = self.steam / "steamapps/compatdata/1/pfx/dosdevices/z:"
|
|
link.parent.mkdir(parents=True)
|
|
link.symlink_to(self.root / "outside")
|
|
self.run_script()
|
|
self.assertEqual(self.mode(outside), 0o400)
|
|
|
|
def test_writable_files_are_untouched_and_missing_libraries_are_fine(self):
|
|
fine = self.file(self.steam / "steamapps/common/Game/ok.ini", 0o640)
|
|
out = self.run_script()
|
|
self.assertEqual(self.mode(fine), 0o640)
|
|
self.assertIn("0 files and folders made writable", out)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|