mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 11:01:07 +00:00
108 lines
4.1 KiB
Python
108 lines
4.1 KiB
Python
import json
|
|
import os
|
|
import runpy
|
|
import socket
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import threading
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
BWRAP = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-bwrap"
|
|
|
|
PROBE = """
|
|
import fcntl, os, sys
|
|
fd = os.open(sys.argv[1], os.O_RDWR)
|
|
try:
|
|
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
except OSError:
|
|
print("held")
|
|
else:
|
|
print("free")
|
|
"""
|
|
|
|
|
|
class BwrapLockFileTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.shim = runpy.run_path(str(BWRAP))
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.instance = Path(self.temp.name) / "instance"
|
|
self.instance.mkdir()
|
|
(self.instance / ".ref").write_text("")
|
|
|
|
def spec(self, *extra):
|
|
argv = ["--ro-bind", str(self.instance), "/run/user/1000/.flatpak/7", *extra, "--", "true"]
|
|
return self.shim["parse"](argv)
|
|
|
|
def probe(self):
|
|
return subprocess.run([sys.executable, "-c", PROBE, str(self.instance / ".ref")],
|
|
capture_output=True, text=True, timeout=30, check=True).stdout.strip()
|
|
|
|
def test_sandbox_paths_resolve_through_the_binds(self):
|
|
spec = self.spec()
|
|
self.assertEqual(str(self.instance / ".ref"), self.shim["host_path"](spec, "/run/user/1000/.flatpak/7/.ref"))
|
|
self.assertEqual("/etc/hosts", self.shim["host_path"](spec, "/etc/hosts"))
|
|
|
|
def test_the_instance_lock_is_held_while_the_sandbox_runs(self):
|
|
spec = self.spec("--lock-file", "/run/user/1000/.flatpak/7/.ref")
|
|
self.assertEqual("free", self.probe())
|
|
self.shim["hold_locks"](spec)
|
|
self.assertEqual("held", self.probe())
|
|
|
|
def test_without_a_lock_file_nothing_is_locked(self):
|
|
self.shim["hold_locks"](self.spec())
|
|
self.assertEqual("free", self.probe())
|
|
|
|
def test_the_lock_survives_exec(self):
|
|
script = (
|
|
"import runpy, sys; shim = runpy.run_path(sys.argv[1]); "
|
|
"spec = shim['parse'](['--ro-bind', sys.argv[2], '/run/user/1000/.flatpak/7', "
|
|
"'--lock-file', '/run/user/1000/.flatpak/7/.ref', '--', 'true']); "
|
|
"shim['hold_locks'](spec); "
|
|
"import os; os.execv(sys.executable, [sys.executable, '-c', "
|
|
"'import subprocess, sys; print(subprocess.run([sys.executable, \"-c\", sys.argv[1], sys.argv[2]], "
|
|
"capture_output=True, text=True).stdout.strip())', sys.argv[3], sys.argv[4]])"
|
|
)
|
|
out = subprocess.run([sys.executable, "-c", script, str(BWRAP), str(self.instance), PROBE,
|
|
str(self.instance / ".ref")], capture_output=True, text=True, timeout=30, check=True)
|
|
self.assertEqual("held", out.stdout.strip())
|
|
|
|
|
|
class BwrapReportedPidTest(unittest.TestCase):
|
|
def test_flatpak_is_given_the_wrapper_so_killing_it_ends_the_whole_sandbox(self):
|
|
name = "droiddeck-bwrap-test-%d" % os.getpid()
|
|
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
server.bind("\0" + name)
|
|
server.listen(1)
|
|
self.addCleanup(server.close)
|
|
|
|
def answer():
|
|
conn, _ = server.accept()
|
|
with conn:
|
|
head = conn.recv(5)
|
|
size = struct.unpack(">I", head[1:])[0]
|
|
while size > 0:
|
|
size -= len(conn.recv(size))
|
|
for kind, payload in ((b"P", b"424242"), (b"X", b"0")):
|
|
conn.sendall(kind + struct.pack(">I", len(payload)) + payload)
|
|
|
|
thread = threading.Thread(target=answer, daemon=True)
|
|
thread.start()
|
|
read_end, write_end = os.pipe()
|
|
env = dict(os.environ, DROIDDECK_BWRAP_SOCKET=name)
|
|
proc = subprocess.Popen([sys.executable, str(BWRAP), "--ro-bind", "/", "/elsewhere", "--info-fd",
|
|
str(write_end), "--", "true"], env=env, pass_fds=[write_end])
|
|
os.close(write_end)
|
|
info = os.read(read_end, 4096)
|
|
os.close(read_end)
|
|
self.assertEqual(0, proc.wait(30))
|
|
thread.join(5)
|
|
self.assertEqual({"child-pid": proc.pid}, json.loads(info))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|