fix: distinguish supported network backup mounts from local filesystems

This commit is contained in:
Codex
2026-10-04 17:28:45 +08:00
parent 83e8981603
commit 631c411602
4 changed files with 55 additions and 7 deletions
+30 -3
View File
@@ -3,7 +3,7 @@ import datetime as dt
from contextlib import closing
import hashlib
import os
from pathlib import Path
from pathlib import Path, PurePosixPath
import re
import secrets
import shutil
@@ -27,6 +27,33 @@ class Backups:
last=self.a.setting('backup_last', None),
history=self.a.setting('backup_history', [])[-20:][::-1])
@staticmethod
def network_mount(folder):
if os.name == 'nt':
return folder.drive.startswith('\\\\')
try:
records = Path('/proc/self/mountinfo').read_text().splitlines()
except OSError:
return False
return Backups.linux_network_mount(PurePosixPath(str(folder)), records)
@staticmethod
def linux_network_mount(folder, records):
matches = []
for line in records:
left, separator, right = line.partition(' - ')
fields, detail = left.split(), right.split()
if not separator or len(fields) < 5 or not detail:
continue
mount = PurePosixPath(re.sub(r'\\([0-7]{3})', lambda m: chr(int(m[1], 8)), fields[4]))
if folder.is_relative_to(mount):
matches.append((len(mount.parts), detail[0]))
# The nearest mount wins: a tmpfs inside an NFS share is still local.
filesystem = max(matches, default=(0, ''), key=lambda x:x[0])[1]
return filesystem in {'cifs', 'smb3', 'nfs', 'nfs4', 'davfs',
'fuse.sshfs', 'fuse.rclone', 'fuse.davfs', 'fuse.davfs2',
'fuse.wdfs', 'fuse.s3fs', 'fuse.goofys'}
def network_folder(self, text):
if not text:
return None
@@ -39,8 +66,8 @@ class Backups:
if not folder.is_dir():
raise self.a.Problem('备份目录不存在,请先在服务器挂载网络存储并授予服务账号写入权限')
# A disconnected mount must not silently become a local-only cloud backup.
if not any(os.path.ismount(p) for p in (folder, *folder.parents) if p != Path(p.anchor)):
raise self.a.Problem('该目录尚未检测到独立挂载;请先连接网络盘,不能把本机目录当作云端成功')
if not self.network_mount(folder):
raise self.a.Problem('该目录未检测到支持的网络挂载,请先连接 NFS、SMB、WebDAV 或 rclone 网络盘')
if folder == (self.a.DATA/'managed-backups').resolve():
raise self.a.Problem('网络副本与本机备份目录不能相同')
return folder