Skip to content

Commit 0830f48

Browse files
authored
fix: resolve path conflicts and improve self-healing during backup restore and plugin installation (#7737)
* fix(数据备份与恢复): 解决备份恢复和插件安装过程中的路径冲突及自愈问题 1. 修复备份导入时目录条目被误识别为 0 字节文件的问题。 2. 增加插件加载和数据目录创建时的路径冲突自动清理逻辑。 3. 增强插件解压安装过程对现有冲突文件的兼容性。 4. 优化 remove_dir 工具类使其支持同时处理文件和目录的删除。 * fix(core): 根据 CR 建议实现通用的路径冲突自愈机制并增强损坏符号链接的处理能力
1 parent 9165278 commit 0830f48

5 files changed

Lines changed: 39 additions & 8 deletions

File tree

astrbot/core/backup/importer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
get_astrbot_data_path,
2626
get_astrbot_knowledge_base_path,
2727
)
28+
from astrbot.core.utils.io import ensure_dir
2829
from astrbot.core.utils.version_comparator import VersionComparator
2930

3031
# 从共享常量模块导入
@@ -931,6 +932,11 @@ async def _import_directories(
931932
if not _validate_path_within(target_path, target_dir):
932933
result.add_warning(f"文件路径越界,已跳过: {name}")
933934
continue
935+
936+
if zf.getinfo(name).is_dir():
937+
ensure_dir(target_path)
938+
continue
939+
934940
target_path.parent.mkdir(parents=True, exist_ok=True)
935941

936942
with zf.open(name) as src, open(target_path, "wb") as dst:

astrbot/core/star/star_tools.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from astrbot.core.star.context import Context
3838
from astrbot.core.star.star import star_map
3939
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
40+
from astrbot.core.utils.io import ensure_dir
4041

4142

4243
class StarTools:
@@ -305,7 +306,7 @@ def get_data_dir(cls, plugin_name: str | None = None) -> Path:
305306
)
306307

307308
try:
308-
data_dir.mkdir(parents=True, exist_ok=True)
309+
ensure_dir(data_dir)
309310
except OSError as e:
310311
if isinstance(e, PermissionError):
311312
raise RuntimeError(f"无法创建目录 {data_dir}:权限不足") from e

astrbot/core/star/updator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from astrbot.core import logger
66
from astrbot.core.utils.astrbot_path import get_astrbot_plugin_path
7-
from astrbot.core.utils.io import on_error, remove_dir
7+
from astrbot.core.utils.io import ensure_dir, on_error, remove_dir
88

99
from ..star.star import StarMetadata
1010
from ..updator import RepoZipUpdator
@@ -71,7 +71,7 @@ async def update(
7171
return plugin_path
7272

7373
def unzip_file(self, zip_path: str, target_dir: str) -> None:
74-
os.makedirs(target_dir, exist_ok=True)
74+
ensure_dir(target_dir)
7575
update_dir = ""
7676
logger.info(f"Extracting archive: {zip_path}")
7777
with zipfile.ZipFile(zip_path, "r") as z:

astrbot/core/utils/io.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,36 @@ def on_error(func, path, exc_info) -> None:
3131

3232

3333
def remove_dir(file_path: str) -> bool:
34-
if not os.path.exists(file_path):
34+
if not os.path.lexists(file_path):
3535
return True
36-
shutil.rmtree(file_path, onerror=on_error)
36+
if os.path.isfile(file_path) or os.path.islink(file_path):
37+
os.remove(file_path)
38+
else:
39+
shutil.rmtree(file_path, onerror=on_error)
3740
return True
3841

3942

43+
def ensure_dir(dir_path: str | Path) -> None:
44+
"""确保目录存在。如果路径处存在非目录的文件或损坏的符号链接,则先将其删除。"""
45+
p = Path(dir_path)
46+
if (p.exists() or p.is_symlink()) and not p.is_dir():
47+
logger.warning(f"路径 {p} 已存在但不是目录,正在清理以创建目录。")
48+
try:
49+
if p.is_dir():
50+
shutil.rmtree(p, onerror=on_error)
51+
else:
52+
p.unlink()
53+
except Exception as e:
54+
logger.error(f"清理冲突路径 {p} 失败: {e!s}")
55+
raise RuntimeError(f"无法清理冲突路径 {p}{e!s}") from e
56+
57+
try:
58+
p.mkdir(parents=True, exist_ok=True)
59+
except Exception as e:
60+
logger.error(f"创建目录 {p} 失败: {e!s}")
61+
raise RuntimeError(f"无法创建目录 {p}{e!s}") from e
62+
63+
4064
def port_checker(port: int, host: str = "localhost") -> bool:
4165
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4266
sk.settimeout(1)

astrbot/core/zip_updator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import httpx
1010

1111
from astrbot.core import logger
12-
from astrbot.core.utils.io import on_error
12+
from astrbot.core.utils.io import ensure_dir, on_error
1313
from astrbot.core.utils.version_comparator import VersionComparator
1414

1515

@@ -56,7 +56,7 @@ async def _download_file(
5656
self, url: str, path: str, timeout: float = 1800.0
5757
) -> None:
5858
target_path = Path(path)
59-
target_path.parent.mkdir(parents=True, exist_ok=True)
59+
ensure_dir(target_path.parent)
6060

6161
try:
6262
async with self._create_httpx_client(timeout=timeout) as client:
@@ -233,7 +233,7 @@ def parse_github_url(self, url: str):
233233

234234
def unzip_file(self, zip_path: str, target_dir: str) -> None:
235235
"""解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir"""
236-
os.makedirs(target_dir, exist_ok=True)
236+
ensure_dir(target_dir)
237237
update_dir = ""
238238
with zipfile.ZipFile(zip_path, "r") as z:
239239
update_dir = z.namelist()[0]

0 commit comments

Comments
 (0)