fix(peer): confine download mutations to game root handles

Remote manifests were validated before mutation, but preparation, chunk writes,
sentinel transactions, and ownership recovery later reopened ambient paths. A
link or reparse-point swap between those steps could redirect a mutation outside
the validated game root.

Introduce a retained ConfinedGameRoot capability backed by cap-primitives. Carry
typed validated destinations into chunk plans, walk every component without
following links, and perform payload, sentinel, stale-file, abort, and recovery
mutations relative to the retained handle. File writes and verification use the
same opened handle, while final durability syncs payload files and unique parent
directories before committing version.ini.

Make ownership-record publication phase-aware as well. A directory-sync failure
after record rename now stops before payload mutation without performing an
unsafe old-sentinel rollback. Record the capability-root, bounded-handle,
hard-link, and unproven Windows durability tradeoffs in the decision log.

Test Plan:
- `just clippy` -- passed
- `just test` -- passed; 185 peer tests and the full workspace are green
- `just fmt` -- Rust, TOML, and Prettier completed; command remains nonzero on
  39 pre-existing rumdl issues outside this change
- `git diff --cached --check` -- passed
This commit is contained in:
2026-08-09 19:22:11 +02:00
parent 62cd9306bd
commit 691176e1d5
16 changed files with 1325 additions and 591 deletions
Generated
+96
View File
@@ -38,6 +38,12 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ambient-authority"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
[[package]]
name = "android_system_properties"
version = "0.1.6"
@@ -278,6 +284,35 @@ dependencies = [
"serde_core",
]
[[package]]
name = "cap-fs-ext"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78e5a3368ae89b7cb68186411452b4b9fac8b41be9c19bf3f47c2d2c8e36e6b"
dependencies = [
"cap-primitives",
"io-lifetimes 3.0.1",
"windows-sys 0.61.2",
]
[[package]]
name = "cap-primitives"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdadbd7c002d3a484b35243669abdae85a0ebaded5a61117169dc3400f9a7ff0"
dependencies = [
"ambient-authority",
"fs-set-times",
"io-extras",
"io-lifetimes 3.0.1",
"ipnet",
"maybe-owned",
"rustix",
"rustix-linux-procfs",
"windows-sys 0.61.2",
"winx",
]
[[package]]
name = "cargo-platform"
version = "0.1.9"
@@ -972,6 +1007,17 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs-set-times"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a"
dependencies = [
"io-lifetimes 2.0.4",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
@@ -1758,6 +1804,28 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4275b20e6057cd7733fd8df8a5a31701e4fe44497dad0f3fa0e1c4fb971506be"
[[package]]
name = "io-extras"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20fd6de4ccfcc187e38bc21cfa543cb5a302cb86a8b114eb7f0bf0dc9f8ac00f"
dependencies = [
"io-lifetimes 3.0.1",
"windows-sys 0.60.2",
]
[[package]]
name = "io-lifetimes"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983"
[[package]]
name = "io-lifetimes"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0fb0570afe1fed943c5c3d4102d5358592d8625fda6a0007fdbe65a92fba96"
[[package]]
name = "ipnet"
version = "2.12.1"
@@ -1965,6 +2033,8 @@ name = "lanspread-peer"
version = "0.1.0"
dependencies = [
"bytes",
"cap-fs-ext",
"cap-primitives",
"crc32fast",
"eyre",
"futures",
@@ -2170,6 +2240,12 @@ dependencies = [
"web_atoms",
]
[[package]]
name = "maybe-owned"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4"
[[package]]
name = "mdns-sd"
version = "0.20.3"
@@ -3088,6 +3164,16 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustix-linux-procfs"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056"
dependencies = [
"once_cell",
"rustix",
]
[[package]]
name = "rustls"
version = "0.23.43"
@@ -5634,6 +5720,16 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winx"
version = "0.36.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
dependencies = [
"bitflags 2.13.1",
"windows-sys 0.59.0",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"