fix(install): preserve custom bin/ files (e.g. hand-added geoip) across updates

Every reinstall/update wipes /usr/local/x-ui/ wholesale and re-extracts
the release tarball, which only ships known assets (xray/mtg binaries,
the bundled geoip*/geosite*.dat sets). A user-reported real incident:
a hand-placed custom geoip file referenced from a routing rule via
"ext:<file>:<code>" got silently deleted on update, and Xray refused
to start at all afterward ("failed to open <file>: no such file or
directory"), taking down every inbound until the file was manually
restored from the user's own backup.

install_x-ui now backs up the old bin/ before the wipe and restores,
after extraction, only the files the fresh release doesn't provide --
bundled assets still get the newer per-release copy, nothing custom
silently disappears. Verified in isolation: standard files (geoip.dat,
the xray binary) end up as the fresh release's copy; a custom file
absent from the release survives untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kuzz007
2026-07-27 13:46:19 +03:00
parent 196ec792d4
commit ee47aa2608
+37
View File
@@ -1688,6 +1688,7 @@ install_x-ui() {
fi
# Stop x-ui service and remove old resources
local custom_bin_backup=""
if [[ -e ${xui_folder}/ ]]; then
if [[ $release == "alpine" ]]; then
rc-service x-ui stop
@@ -1699,6 +1700,19 @@ install_x-ui() {
# an inbound port with an outdated secret, silently breaking new clients.
# The freshly installed panel respawns a clean mtg per inbound on start.
pkill -f 'mtg-linux-[^ ]* run ' > /dev/null 2>&1 || true
# bin/ is about to be wiped wholesale by the tar extraction below. The
# release only ships known assets (xray/mtg binaries, the bundled
# geoip*/geosite*.dat sets) -- anything else in bin/ was placed there
# by the admin (e.g. a hand-added custom geoip/geosite file referenced
# from a routing rule via "ext:<file>:<code>") and would otherwise be
# silently deleted on every update, breaking Xray at next start with
# "failed to open <file>: no such file or directory" for any routing
# rule that references it.
if [[ -d ${xui_folder}/bin ]]; then
custom_bin_backup=$(mktemp -d)
cp -a ${xui_folder}/bin/. "${custom_bin_backup}/" 2> /dev/null
fi
rm ${xui_folder}/ -rf
fi
@@ -1707,6 +1721,7 @@ install_x-ui() {
if [[ $? -ne 0 ]]; then
rm x-ui-linux-$(arch).tar.gz -f
rm -f "${xui_script_temp}"
[[ -n "${custom_bin_backup}" ]] && rm -rf "${custom_bin_backup}"
echo -e "${red}Failed to extract the x-ui release archive -- the previous installation has already been removed, so the panel will not start until this is fixed; try running the installer again${plain}"
exit 1
fi
@@ -1715,12 +1730,34 @@ install_x-ui() {
cd x-ui
if [[ $? -ne 0 || ! -s x-ui ]]; then
rm -f "${xui_script_temp}"
[[ -n "${custom_bin_backup}" ]] && rm -rf "${custom_bin_backup}"
echo -e "${red}Extracted x-ui archive is missing the x-ui binary -- the previous installation has already been removed, so the panel will not start until this is fixed; try running the installer again${plain}"
exit 1
fi
chmod +x x-ui
chmod +x x-ui.sh
# Restore anything from the old bin/ that the fresh release doesn't ship
# (custom geoip/geosite files, or anything else an admin hand-placed
# there) -- never overwrites a same-named file the new release provides,
# so bundled assets (geoip.dat, geoip_RU.dat, ...) still get the fresh
# per-release copy.
if [[ -n "${custom_bin_backup}" ]]; then
local restored_custom_bin=()
while IFS= read -r -d '' f; do
local rel="${f#"${custom_bin_backup}"/}"
if [[ ! -e "bin/${rel}" ]]; then
mkdir -p "bin/$(dirname "${rel}")"
cp -a "${f}" "bin/${rel}"
restored_custom_bin+=("${rel}")
fi
done < <(find "${custom_bin_backup}" -type f -print0)
rm -rf "${custom_bin_backup}"
if [[ ${#restored_custom_bin[@]} -gt 0 ]]; then
echo -e "${green}Restored custom file(s) in bin/ not shipped by this release: ${restored_custom_bin[*]}${plain}"
fi
fi
# Check the system's architecture and rename the file accordingly.
# The panel binary maps GOARCH=arm to "arm32" (internal/xray/process.go),
# so the Xray binary must be named xray-linux-arm32; mtg keeps plain "arm".