mirror of
https://github.com/Ayymoss/Plutainer
synced 2026-08-24 14:32:06 -04:00
fix(iw4x): mirror host dirs recursively so zone/patch stays writable
link_dir_contents only symlinked the top level of the source dir. Host
zone/ contains nothing but directories, so zone/patch ended up a symlink
to the read-only gamefiles mount and the launcher died extracting into it:
[E] exception caught in main: failed to extract file:
zone/patch/iw4_credits_load.ff
Found on a real deployment. It presented as a healthy server because the
host's zone/patch/ already held iw4x's .ff files from a previous
bare-metal install, so the game loaded them anyway. On clean stock MW2
gamefiles the patch zones would simply be absent. The crash also aborted
the launcher before sync_dlc and sync_helper, and left release.zip
unextracted with rawfiles never stamped, so every subsequent start failed
at the same point and update checks stayed wedged.
Recreate every directory level as a real dir and symlink only leaf
entries. Also never overwrite a real file already at the destination: the
launcher may have written a newer copy there and it must win over the
host's version, which also keeps re-runs idempotent.
This commit is contained in:
parent
e82b1e2565
commit
c84066dcd1
2 changed files with 21 additions and 8 deletions
|
|
@ -61,7 +61,7 @@ Everything runs as the `plutainer` user from `/home/plutainer/.plutainer`. All e
|
|||
- `resolve_config_layout`: sets `CONFIG_SOT_DIR` and `ALT_CONFIG_DIR` based on `PLUTAINER_USE_RAW_CONFIGS`. Default: SOT = `configs/`, ALT = engine dir. With raw mode on: swapped.
|
||||
- `resolve_config_path`: convenience wrapper that resolves the engine dir + layout in one call so healthcheck/rcon-cli only need this.
|
||||
- `link_files <src> <dest> <name1>...`: existence-guarded symlink helper; replaces unsafe `ln -sf src/{a,b,c} dest/` bash brace expansion.
|
||||
- `link_dir_contents <src_root> <dest_root> <name>`: creates `dest_root/name` as a **real** dir and symlinks the *contents* of `src_root/name` into it. Use instead of `link_files` for any dir that must be writable while also carrying read-only host game files — the engine config dir (`link_configs` fans cfg symlinks into it) or a dir an updater writes into. Symlinking the dir itself would make the path read-only, and `ln -sf src/name dest/` would nest the link as `dest/name/name`. Glob-guarded (no bogus `*` symlink), and replaces a directory symlink left by an older image.
|
||||
- `link_dir_contents <src_root> <dest_root> <name>`: mirrors `src_root/name` into `dest_root/name`, recreating **every** directory level as a real dir and symlinking only leaf files. Use instead of `link_files` for any dir that must be writable while also carrying read-only host game files — the engine config dir (`link_configs` fans cfg symlinks into it) or a dir an updater writes into. Symlinking a dir at *any* depth inherits the read-only mount: linking just the top level still left `zone/patch/` read-only and killed the iw4x-launcher with `failed to extract file: zone/patch/iw4_credits_load.ff`. Never overwrites a real file already at the destination, so an updater-written copy wins over the host's. Replaces a directory symlink left by an older image.
|
||||
- `seed_configs <game-key> <asset-root> <cfg-root-rel>`: walks bundled seed, lifts top-level `*.cfg` files inside `cfg-root-rel` into `CONFIG_SOT_DIR`, places everything else under `asset-root`. Idempotent.
|
||||
- `link_configs <engine-dir1> [engine-dir2 ...]`: variadic. Fans out symlinks from every `configs/*.cfg` into each engine dir using relative paths. Refuses to overwrite a real (non-symlink) file at engine path (warns instead). Reaps dangling cfg symlinks. No-op when `PLUTAINER_USE_RAW_CONFIGS=true`.
|
||||
- `ensure_config_present`: checks that `CONFIG_FILE` exists at `CONFIG_SOT_DIR`. If absent there but present as a real file at the ALT location, moves it (auto-lift). If absent everywhere, prints a refusal with a `find -iname` case-insensitive hint, returns non-zero.
|
||||
|
|
|
|||
|
|
@ -209,16 +209,29 @@ link_dir_contents() {
|
|||
|
||||
mkdir -p "$dest"
|
||||
|
||||
if [[ ! -d "$src_root/$name" ]]; then
|
||||
echo "[WARN] missing $src_root/$name — nothing to link into $dest" >&2
|
||||
local src="$src_root/$name"
|
||||
if [[ ! -d "$src" ]]; then
|
||||
echo "[WARN] missing $src — nothing to link into $dest" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Guard the glob: bash leaves an unmatched `*` literal, so an empty source
|
||||
# dir would otherwise create a bogus symlink named `*`.
|
||||
if compgen -G "$src_root/$name/*" > /dev/null; then
|
||||
ln -sfn "$src_root/$name"/* "$dest"/
|
||||
fi
|
||||
# Mirror the source's directory skeleton as REAL dirs, then symlink only leaf
|
||||
# entries. Symlinking a subdirectory would inherit the read-only mount and
|
||||
# block writes one level down — e.g. iw4x-launcher extracting into
|
||||
# zone/patch/ dies with "failed to extract file" when zone/patch is a link.
|
||||
local rel
|
||||
while IFS= read -r -d '' rel; do
|
||||
mkdir -p "$dest/$rel"
|
||||
done < <(cd "$src" && find . -mindepth 1 -type d -printf '%P\0')
|
||||
|
||||
# Never clobber a real file already at the destination: an updater may have
|
||||
# written a newer copy there, and that must win over the host's version.
|
||||
while IFS= read -r -d '' rel; do
|
||||
if [[ -e "$dest/$rel" && ! -L "$dest/$rel" ]]; then
|
||||
continue
|
||||
fi
|
||||
ln -sfn "$src/$rel" "$dest/$rel"
|
||||
done < <(cd "$src" && find . \( -type f -o -type l \) -printf '%P\0')
|
||||
}
|
||||
|
||||
# Copy bundled community seed configs into the volume on first run.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue