3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
145 lines
5.2 KiB
Python
145 lines
5.2 KiB
Python
# SPDX-License-Identifier: MIT
|
|
"""Guest-disk SMB path translation (#616)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from winpodx.core.guest_disk import (
|
|
GUEST_SMB_PORT,
|
|
GUEST_SMB_SHARE,
|
|
SMB_HOST_PORT,
|
|
guest_win_path_to_host,
|
|
smb_uri,
|
|
)
|
|
|
|
|
|
def test_kio_fuse_dbus_service_present_finds_service_file(monkeypatch, tmp_path) -> None:
|
|
# #697: the authoritative signal is the D-Bus activation service file that
|
|
# _kio_fuse_mount calls -- on distros (Fedora Kinoite / KF6) whose binary
|
|
# lives in a path we don't enumerate, this is what makes the mount work.
|
|
# Real glob against a temp XDG_DATA_DIRS.
|
|
import winpodx.core.guest_disk as gd
|
|
|
|
svc = tmp_path / "dbus-1" / "services"
|
|
svc.mkdir(parents=True)
|
|
(svc / "org.kde.KIOFuse.service").write_text("[D-BUS Service]\nName=org.kde.KIOFuse\n")
|
|
monkeypatch.setenv("XDG_DATA_DIRS", f"/nonexistent-a:{tmp_path}")
|
|
monkeypatch.delenv("XDG_DATA_HOME", raising=False)
|
|
|
|
assert gd._kio_fuse_dbus_service_present() is True
|
|
|
|
|
|
def test_kio_fuse_available_via_dbus_when_binary_hidden(monkeypatch) -> None:
|
|
# No binary anywhere, but the D-Bus service is present -> available.
|
|
import winpodx.core.guest_disk as gd
|
|
|
|
monkeypatch.setattr("shutil.which", lambda _n: None)
|
|
monkeypatch.setattr("glob.glob", lambda _p: []) # no binary matches
|
|
monkeypatch.setattr(gd, "_kio_fuse_dbus_service_present", lambda: True)
|
|
assert gd.kio_fuse_available() is True
|
|
|
|
|
|
def test_kio_fuse_available_debian_multiarch_libexec(monkeypatch) -> None:
|
|
# #697: /usr/lib/<triplet>/libexec/kio-fuse is three levels deep -- missed
|
|
# by the old /usr/lib*/*/kio-fuse two-level glob; the new pattern catches it.
|
|
import winpodx.core.guest_disk as gd
|
|
|
|
monkeypatch.setattr("shutil.which", lambda _n: None)
|
|
monkeypatch.setattr(gd, "_kio_fuse_dbus_service_present", lambda: False)
|
|
hit = "/usr/lib/x86_64-linux-gnu/libexec/kio-fuse"
|
|
monkeypatch.setattr("glob.glob", lambda p: [hit] if p == "/usr/lib/*/libexec/kio-fuse" else [])
|
|
assert gd.kio_fuse_available() is True
|
|
|
|
|
|
def test_kio_fuse_unavailable_when_nothing_present(monkeypatch) -> None:
|
|
import winpodx.core.guest_disk as gd
|
|
|
|
monkeypatch.setattr("shutil.which", lambda _n: None)
|
|
monkeypatch.setattr("glob.glob", lambda _p: [])
|
|
monkeypatch.setattr(gd, "_kio_fuse_dbus_service_present", lambda: False)
|
|
assert gd.kio_fuse_available() is False
|
|
|
|
|
|
def test_host_port_is_unprivileged() -> None:
|
|
# Rootless podman/docker can't bind a privileged host port (#616).
|
|
assert SMB_HOST_PORT >= 1024
|
|
# The guest still listens on the standard SMB port.
|
|
assert GUEST_SMB_PORT == 445
|
|
|
|
|
|
def test_smb_uri_uses_user_and_host_port() -> None:
|
|
from winpodx.core.config import Config
|
|
|
|
cfg = Config()
|
|
cfg.rdp.user = "WPX-User"
|
|
uri = smb_uri(cfg)
|
|
assert uri == f"smb://WPX-User@127.0.0.1:{SMB_HOST_PORT}/{GUEST_SMB_SHARE}"
|
|
|
|
|
|
def test_smb_uri_with_password_is_url_encoded() -> None:
|
|
from winpodx.core.config import Config
|
|
|
|
cfg = Config()
|
|
cfg.rdp.user = "WPX-User"
|
|
cfg.rdp.password = "p@ss/wo rd"
|
|
uri = smb_uri(cfg, with_password=True)
|
|
# Special chars in the password are percent-encoded so the URL parses.
|
|
assert "p%40ss%2Fwo%20rd" in uri
|
|
assert uri.startswith("smb://WPX-User:")
|
|
assert uri.endswith(f"@127.0.0.1:{SMB_HOST_PORT}/{GUEST_SMB_SHARE}")
|
|
|
|
|
|
def test_kio_fuse_mount_parses_dbus_path(monkeypatch) -> None:
|
|
from winpodx.core import guest_disk
|
|
from winpodx.core.config import Config
|
|
|
|
fuse_path = "/run/user/1000/kio-fuse-AB/smb/WPX-User@127.0.0.1:4445/winpodx-c"
|
|
|
|
class _Proc:
|
|
returncode = 0
|
|
stdout = f'method return ...\n string "{fuse_path}"\n'
|
|
stderr = ""
|
|
|
|
def fake_run(argv, **kwargs):
|
|
# The credentialed mountUrl D-Bus call is what we issue.
|
|
assert argv[0] == "dbus-send"
|
|
assert "org.kde.KIOFuse.VFS.mountUrl" in argv
|
|
return _Proc()
|
|
|
|
monkeypatch.setattr(guest_disk.subprocess, "run", fake_run)
|
|
monkeypatch.setattr(guest_disk.Path, "is_dir", lambda self: True)
|
|
cfg = Config()
|
|
assert guest_disk._kio_fuse_mount(cfg) == Path(fuse_path)
|
|
|
|
|
|
def test_translate_maps_c_drive_under_mount() -> None:
|
|
mr = Path("/mnt/guest")
|
|
assert guest_win_path_to_host(r"C:\Users\me\Desktop\x.txt", mr) == (
|
|
mr / "Users" / "me" / "Desktop" / "x.txt"
|
|
)
|
|
|
|
|
|
def test_translate_normalises_forward_slashes() -> None:
|
|
mr = Path("/mnt/guest")
|
|
assert guest_win_path_to_host("C:/Users/me/y", mr) == mr / "Users" / "me" / "y"
|
|
|
|
|
|
def test_translate_rejects_non_c_drive() -> None:
|
|
assert guest_win_path_to_host(r"D:\data\x", Path("/mnt/guest")) is None
|
|
|
|
|
|
def test_translate_rejects_traversal() -> None:
|
|
assert guest_win_path_to_host(r"C:\..\..\etc\passwd", Path("/mnt/guest")) is None
|
|
assert guest_win_path_to_host(r"C:\Users\..\..\x", Path("/mnt/guest")) is None
|
|
|
|
|
|
def test_translate_rejects_non_drive_path() -> None:
|
|
assert guest_win_path_to_host(r"\\tsclient\home\x", Path("/mnt/guest")) is None
|
|
assert guest_win_path_to_host("relative/path", Path("/mnt/guest")) is None
|
|
|
|
|
|
def test_translate_bare_drive_root_is_mount_root() -> None:
|
|
mr = Path("/mnt/guest")
|
|
assert guest_win_path_to_host("C:\\", mr) == mr
|