From 045d428ecc2ee9ad4cb5ac8c4aead66e24858d9d Mon Sep 17 00:00:00 2001 From: snsmac Date: Sun, 19 Mar 2023 11:44:31 +0100 Subject: [PATCH 1/2] Do not retry on permission errors --- src/borg/archiver/create_cmd.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/borg/archiver/create_cmd.py b/src/borg/archiver/create_cmd.py index 9e4c3311b..773632be6 100644 --- a/src/borg/archiver/create_cmd.py +++ b/src/borg/archiver/create_cmd.py @@ -1,3 +1,4 @@ +import errno import sys import argparse import logging @@ -370,6 +371,10 @@ class CreateMixIn: self.print_warning("Unknown file type: %s", path) return except (BackupError, BackupOSError) as err: + if isinstance(err, BackupOSError): + if err.errno in (errno.EPERM, errno.EACCES): + # Do not try again, such errors can not be fixed by retrying. + raise # sleep a bit, so temporary problems might go away... sleep_s = 1000.0 / 1e6 * 10 ** (retry / 2) # retry 0: 1ms, retry 6: 1s, ... time.sleep(sleep_s) From dad99650af72b3a8018229786ab84b4083950612 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 20 Mar 2023 20:15:24 +0100 Subject: [PATCH 2/2] test whether borg skips permission denied files without retries --- src/borg/testsuite/archiver/create_cmd.py | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/borg/testsuite/archiver/create_cmd.py b/src/borg/testsuite/archiver/create_cmd.py index 12548bd4f..e7f1d4b0e 100644 --- a/src/borg/testsuite/archiver/create_cmd.py +++ b/src/borg/testsuite/archiver/create_cmd.py @@ -5,6 +5,7 @@ from random import randbytes import shutil import socket import stat +import subprocess import time import unittest @@ -208,6 +209,7 @@ class ArchiverTestCase(ArchiverTestCaseBase): input=flist.encode(), exit_code=0, ) + assert "retry: 3 of " in out assert "E input/file2" not in out # we managed to read it in the 3rd retry (after 3 failed reads) # repo looking good overall? checks for rc == 0. self.cmd(f"--repo={self.repository_location}", "check", "--debug") @@ -217,6 +219,37 @@ class ArchiverTestCase(ArchiverTestCaseBase): assert "input/file2" in out assert "input/file3" in out + def test_create_no_permission_file(self): + file_path = os.path.join(self.input_path, "file") + self.create_regular_file(file_path + "1", size=1000) + self.create_regular_file(file_path + "2", size=1000) + self.create_regular_file(file_path + "3", size=1000) + # revoke read permissions on file2 for everybody, including us: + if is_win32: + subprocess.run(["icacls.exe", file_path + "2", "/deny", "everyone:(R)"]) + else: + os.chmod(file_path + "2", 0o000) + self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION) + flist = "".join(f"input/file{n}\n" for n in range(1, 4)) + out = self.cmd( + f"--repo={self.repository_location}", + "create", + "--paths-from-stdin", + "--list", + "test", + input=flist.encode(), + exit_code=1, # WARNING status: could not back up file2. + ) + assert "retry: 1 of " not in out # retries were NOT attempted! + assert "E input/file2" in out # no permissions! + # repo looking good overall? checks for rc == 0. + self.cmd(f"--repo={self.repository_location}", "check", "--debug") + # check files in created archive + out = self.cmd(f"--repo={self.repository_location}", "list", "test") + assert "input/file1" in out + assert "input/file2" not in out # it skipped file2 + assert "input/file3" in out + def test_create_content_from_command(self): self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION) input_data = "some test content"