Переглянути джерело

fix: sudo_exists — remplacer os.path.exists sur backup_dir (archives root-owned)

Tous les os.path.exists() sur des fichiers dans /home/yunohost.backup/archives/
retournaient False (PermissionError silencieux) car le répertoire est 750 root.
Remplacement par sudo_exists() via sudo stat comme fallback.

- transfer.py: détection des fichiers à transférer
- ynh_backup.py, db_dump.py, custom_dir.py: _abort_if_exists et restore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cédric Hansen 1 день тому
батько
коміт
b11ec6eff8

+ 4 - 2
sources/jobs/custom_dir.py

@@ -29,7 +29,8 @@ def backup_custom_dir(job, instance, backup_dir):
     archive_name = f"{instance}_{label}_{datetime.utcnow().strftime('%Y%m%d')}"
     archive_path = os.path.join(backup_dir, archive_name + ".tar")
 
-    if os.path.exists(archive_path):
+    from jobs.utils import sudo_exists
+    if sudo_exists(archive_path):
         raise RuntimeError(
             f"L'archive {archive_name}.tar existe déjà. "
             "Supprimez-la ou attendez le prochain cycle."
@@ -119,7 +120,8 @@ def backup_custom_dir(job, instance, backup_dir):
 def restore_custom_dir(archive_name, backup_dir):
     """Restauration complète d'un custom_dir : fichiers + user + service + permissions."""
     archive_path = os.path.join(backup_dir, archive_name + ".tar")
-    if not os.path.exists(archive_path):
+    from jobs.utils import sudo_exists
+    if not sudo_exists(archive_path):
         raise FileNotFoundError(f"Archive introuvable : {archive_path}")
 
     info = _read_backup_info(archive_path)

+ 4 - 2
sources/jobs/db_dump.py

@@ -104,8 +104,9 @@ def _archive_name(instance, db_type, dbname):
 
 
 def _abort_if_exists(archive_name, backup_dir):
+    from jobs.utils import sudo_exists
     path = os.path.join(backup_dir, archive_name + ".tar")
-    if os.path.exists(path):
+    if sudo_exists(path):
         raise RuntimeError(
             f"L'archive {archive_name}.tar existe déjà. "
             "Supprimez-la ou attendez le prochain cycle."
@@ -119,7 +120,8 @@ def _abort_if_exists(archive_name, backup_dir):
 def restore_db_dump(archive_name, backup_dir):
     """Restauration d'une base MySQL ou PostgreSQL depuis une archive BackupManager."""
     archive_path = os.path.join(backup_dir, archive_name + ".tar")
-    if not os.path.exists(archive_path):
+    from jobs.utils import sudo_exists
+    if not sudo_exists(archive_path):
         raise FileNotFoundError(f"Archive introuvable : {archive_path}")
 
     info = _read_backup_info(archive_path)

+ 2 - 1
sources/jobs/transfer.py

@@ -42,10 +42,11 @@ def transfer_archive(archive_name, destination, backup_dir, data_dir):
             "Accédez à la page de la destination pour afficher la clé publique."
         )
 
+    from jobs.utils import sudo_exists
     files = []
     for ext in (".tar", ".info.json"):
         path = os.path.join(backup_dir, archive_name + ext)
-        if os.path.exists(path):
+        if sudo_exists(path):
             files.append(path)
 
     if not files:

+ 11 - 0
sources/jobs/utils.py

@@ -36,6 +36,17 @@ def sudo_getmtime(path):
         return 0.0
 
 
+def sudo_exists(path):
+    """Vérifie l'existence d'un fichier via sudo si non accessible directement."""
+    if os.path.exists(path):
+        return True
+    result = subprocess.run(
+        ["sudo", "stat", path],
+        capture_output=True,
+    )
+    return result.returncode == 0
+
+
 def sudo_listdir(directory):
     """Liste les fichiers d'un répertoire via sudo find si non accessible directement."""
     try:

+ 2 - 1
sources/jobs/ynh_backup.py

@@ -121,8 +121,9 @@ def _run_ynh_system(job, instance, backup_dir):
 
 
 def _abort_if_exists(archive_name, backup_dir):
+    from jobs.utils import sudo_exists
     path = os.path.join(backup_dir, archive_name + ".tar")
-    if os.path.exists(path):
+    if sudo_exists(path):
         raise RuntimeError(
             f"L'archive {archive_name}.tar existe déjà. "
             "Supprimez-la manuellement ou attendez le prochain cycle."