feat: replace tar+DB+LND backup with ext4+rsync workflow
- Rewrite sovran-hub-backup.sh: rsync-based mirror to stable current/ path, ext4 validation, no tar/pg_dump/mariadb-dump/LND orchestration, exit-24 nonfatal for /home, INCOMPLETE/BACKUP_COMPLETE markers, flock - Update server.py: _is_supported_backup_fstype accepts only ext4 - Update support.js: require ext4, explain database limitations, update failure message from exFAT to ext4 - Update sovran-hub.nix: replace pkgs.gnutar with pkgs.rsync + pkgs.acl - Rewrite test_manual_backup_workflow.py: 40 new tests covering rsync options, ext4 acceptance, exit-24 handling, no-delete, stable current/ path, INCOMPLETE markers, behavioral rsync tests, and regressions
This commit is contained in:
@@ -1494,38 +1494,13 @@ def _is_internal_mount(mnt: str) -> bool:
|
||||
|
||||
|
||||
def _is_supported_backup_fstype(path: str, fstype: str) -> bool:
|
||||
"""Return whether the target filesystem type is supported for manual backup."""
|
||||
"""Return whether the target filesystem type is supported for manual backup.
|
||||
|
||||
Manual Backup requires ext4 for Linux metadata preservation (ACLs, xattrs,
|
||||
hard links). exFAT, FAT32, NTFS, and other filesystems are not supported.
|
||||
"""
|
||||
fstype = (fstype or "").lower()
|
||||
if fstype == "exfat":
|
||||
return True
|
||||
if fstype != "fuseblk":
|
||||
return False
|
||||
|
||||
src_dev = ""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["findmnt", "-n", "-o", "SOURCE", "-T", path],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
src_dev = result.stdout.strip()
|
||||
except Exception:
|
||||
src_dev = ""
|
||||
|
||||
if not src_dev:
|
||||
return False
|
||||
|
||||
for cmd in (
|
||||
["lsblk", "-no", "FSTYPE", src_dev],
|
||||
["blkid", "-o", "value", "-s", "TYPE", src_dev],
|
||||
):
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0 and result.stdout.strip().lower() in {"exfat", "fuseblk"}:
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
return False
|
||||
return fstype == "ext4"
|
||||
|
||||
|
||||
def _detect_external_drives() -> list[dict]:
|
||||
@@ -3799,7 +3774,7 @@ async def api_backup_run(target: str = ""):
|
||||
if selected_fstype and not _is_supported_backup_fstype(selected_target, selected_fstype):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Selected drive filesystem '{selected_fstype}' is not supported for manual backup.",
|
||||
detail=f"Selected drive filesystem '{selected_fstype}' is not supported for manual backup. Manual Backup requires an ext4-formatted drive.",
|
||||
)
|
||||
|
||||
# Clear stale log before starting
|
||||
|
||||
Reference in New Issue
Block a user