mirror of
https://github.com/borgbackup/borg.git
synced 2024-10-26 12:41:29 +03:00
raise BackupOSError subclasses
This commit is contained in:
parent
9ac4672254
commit
97dd287584
@ -713,15 +713,17 @@ Warnings
|
||||
{}: file changed while we backed it up
|
||||
IncludePatternNeverMatchedWarning rc: 101
|
||||
Include pattern '{}' never matched.
|
||||
BackupWarning rc: 102
|
||||
BackupError rc: 102
|
||||
{}: {}
|
||||
BackupOSWarning rc: 104
|
||||
BackupRaceConditionError rc: 103
|
||||
Error: {}
|
||||
BackupOSError rc: 104
|
||||
{}: {}
|
||||
PermissionWarning rc: 105
|
||||
BackupPermissionError rc: 105
|
||||
{}: {}
|
||||
IOWarning rc: 106
|
||||
BackupIOError rc: 106
|
||||
{}: {}
|
||||
NotFoundWarning rc: 107
|
||||
BackupFileNotFoundError rc: 107
|
||||
{}: {}
|
||||
|
||||
Operations
|
||||
|
@ -1,4 +1,5 @@
|
||||
import base64
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
@ -26,7 +27,8 @@
|
||||
from .compress import CompressionSpec
|
||||
from .constants import * # NOQA
|
||||
from .crypto.low_level import IntegrityError as IntegrityErrorBase
|
||||
from .helpers import BackupError, BackupOSError, BackupRaceConditionError
|
||||
from .helpers import BackupError, BackupRaceConditionError
|
||||
from .helpers import BackupOSError, BackupPermissionError, BackupFileNotFoundError, BackupIOError
|
||||
from .hashindex import ChunkIndex, ChunkIndexEntry, CacheSynchronizer
|
||||
from .helpers import HardLinkManager
|
||||
from .helpers import ChunkIteratorFileWrapper, open_item
|
||||
@ -194,7 +196,14 @@ def __enter__(self):
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type and issubclass(exc_type, OSError):
|
||||
raise BackupOSError(self.op, exc_val) from exc_val
|
||||
E_MAP = {
|
||||
errno.EPERM: BackupPermissionError,
|
||||
errno.EACCES: BackupPermissionError,
|
||||
errno.ENOENT: BackupFileNotFoundError,
|
||||
errno.EIO: BackupIOError,
|
||||
}
|
||||
e_cls = E_MAP.get(exc_val.errno, BackupOSError)
|
||||
raise e_cls(self.op, exc_val) from exc_val
|
||||
|
||||
|
||||
backup_io = BackupIO()
|
||||
|
@ -26,7 +26,7 @@
|
||||
from ..constants import * # NOQA
|
||||
from ..helpers import EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR, EXIT_SIGNAL_BASE, classify_ec
|
||||
from ..helpers import Error, CommandError, get_ec, modern_ec
|
||||
from ..helpers import add_warning, BorgWarning
|
||||
from ..helpers import add_warning, BorgWarning, BackupWarning
|
||||
from ..helpers import format_file_size
|
||||
from ..helpers import remove_surrogates, text_to_json
|
||||
from ..helpers import DatetimeWrapper, replace_placeholders
|
||||
@ -144,10 +144,10 @@ def print_warning(self, msg, *args, **kw):
|
||||
|
||||
def print_warning_instance(self, warning):
|
||||
assert isinstance(warning, BorgWarning)
|
||||
msg = type(warning).__doc__
|
||||
msgid = type(warning).__qualname__
|
||||
args = warning.args
|
||||
self.print_warning(msg, *args, wc=warning.exit_code, wt="curly", msgid=msgid)
|
||||
# if it is a BackupWarning, use the wrapped BackupError exception instance:
|
||||
cls = type(warning.args[1]) if isinstance(warning, BackupWarning) else type(warning)
|
||||
msg, msgid, args, wc = cls.__doc__, cls.__qualname__, warning.args, warning.exit_code
|
||||
self.print_warning(msg, *args, wc=wc, wt="curly", msgid=msgid)
|
||||
|
||||
def print_file_status(self, status, path):
|
||||
# if we get called with status == None, the final file status was already printed
|
||||
|
@ -29,7 +29,7 @@
|
||||
from ..helpers import sig_int, ignore_sigint
|
||||
from ..helpers import iter_separated
|
||||
from ..helpers import MakePathSafeAction
|
||||
from ..helpers import Error, CommandError, BackupWarning, BackupOSWarning, FileChangedWarning
|
||||
from ..helpers import Error, CommandError, BackupWarning, FileChangedWarning
|
||||
from ..manifest import Manifest
|
||||
from ..patterns import PatternMatcher
|
||||
from ..platform import is_win32
|
||||
@ -87,7 +87,7 @@ def create_inner(archive, cache, fso):
|
||||
rc = proc.wait()
|
||||
if rc != 0:
|
||||
raise CommandError("Command %r exited with status %d", args.paths[0], rc)
|
||||
except BackupOSError as e:
|
||||
except BackupError as e:
|
||||
raise Error("%s: %s", path, e)
|
||||
else:
|
||||
status = "+" # included
|
||||
@ -121,9 +121,6 @@ def create_inner(archive, cache, fso):
|
||||
read_special=args.read_special,
|
||||
dry_run=dry_run,
|
||||
)
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(path, e))
|
||||
status = "E"
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(path, e))
|
||||
status = "E"
|
||||
@ -151,8 +148,8 @@ def create_inner(archive, cache, fso):
|
||||
status = fso.process_pipe(
|
||||
path=path, cache=cache, fd=sys.stdin.buffer, mode=mode, user=user, group=group
|
||||
)
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(path, e))
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(path, e))
|
||||
status = "E"
|
||||
else:
|
||||
status = "+" # included
|
||||
@ -183,9 +180,9 @@ def create_inner(archive, cache, fso):
|
||||
# if we get back here, we've finished recursing into <path>,
|
||||
# we do not ever want to get back in there (even if path is given twice as recursion root)
|
||||
skip_inodes.add((st.st_ino, st.st_dev))
|
||||
except BackupOSError as e:
|
||||
except BackupError as e:
|
||||
# this comes from os.stat, self._rec_walk has own exception handler
|
||||
self.print_warning_instance(BackupOSWarning(path, e))
|
||||
self.print_warning_instance(BackupWarning(path, e))
|
||||
continue
|
||||
if not dry_run:
|
||||
if args.progress:
|
||||
@ -368,7 +365,7 @@ def _process_any(self, *, path, parent_fd, name, st, fso, cache, read_special, d
|
||||
else:
|
||||
self.print_warning("Unknown file type: %s", path)
|
||||
return
|
||||
except (BackupError, BackupOSError) as err:
|
||||
except BackupError 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.
|
||||
@ -524,9 +521,6 @@ def _rec_walk(
|
||||
dry_run=dry_run,
|
||||
)
|
||||
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(path, e))
|
||||
status = "E"
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(path, e))
|
||||
status = "E"
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
from ._common import with_repository, with_archive
|
||||
from ._common import build_filter, build_matcher
|
||||
from ..archive import BackupError, BackupOSError
|
||||
from ..archive import BackupError
|
||||
from ..constants import * # NOQA
|
||||
from ..helpers import archivename_validator, PathSpec
|
||||
from ..helpers import remove_surrogates
|
||||
from ..helpers import HardLinkManager
|
||||
from ..helpers import ProgressIndicatorPercent
|
||||
from ..helpers import BackupWarning, BackupOSWarning, IncludePatternNeverMatchedWarning
|
||||
from ..helpers import BackupWarning, IncludePatternNeverMatchedWarning
|
||||
from ..manifest import Manifest
|
||||
|
||||
from ..logger import create_logger
|
||||
@ -65,8 +65,8 @@ def do_extract(self, args, repository, manifest, archive):
|
||||
dir_item = dirs.pop(-1)
|
||||
try:
|
||||
archive.extract_item(dir_item, stdout=stdout)
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(remove_surrogates(dir_item.path), e))
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))
|
||||
if output_list:
|
||||
logging.getLogger("borg.output.list").info(remove_surrogates(item.path))
|
||||
try:
|
||||
@ -80,8 +80,6 @@ def do_extract(self, args, repository, manifest, archive):
|
||||
archive.extract_item(
|
||||
item, stdout=stdout, sparse=sparse, hlm=hlm, pi=pi, continue_extraction=continue_extraction
|
||||
)
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(remove_surrogates(orig_path), e))
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(remove_surrogates(orig_path), e))
|
||||
if pi:
|
||||
@ -96,8 +94,8 @@ def do_extract(self, args, repository, manifest, archive):
|
||||
dir_item = dirs.pop(-1)
|
||||
try:
|
||||
archive.extract_item(dir_item, stdout=stdout)
|
||||
except BackupOSError as e:
|
||||
self.print_warning_instance(BackupOSWarning(remove_surrogates(dir_item.path), e))
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))
|
||||
for pattern in matcher.get_unmatched_include_patterns():
|
||||
self.print_warning_instance(IncludePatternNeverMatchedWarning(pattern))
|
||||
if pi:
|
||||
|
@ -13,8 +13,9 @@
|
||||
from .datastruct import StableDict, Buffer, EfficientCollectionQueue
|
||||
from .errors import Error, ErrorWithTraceback, IntegrityError, DecompressionError, CancelledByUser, CommandError
|
||||
from .errors import RTError, modern_ec
|
||||
from .errors import BorgWarning, FileChangedWarning, BackupWarning, BackupOSWarning, IncludePatternNeverMatchedWarning
|
||||
from .errors import BorgWarning, FileChangedWarning, BackupWarning, IncludePatternNeverMatchedWarning
|
||||
from .errors import BackupError, BackupOSError, BackupRaceConditionError
|
||||
from .errors import BackupPermissionError, BackupIOError, BackupFileNotFoundError
|
||||
from .fs import ensure_dir, join_base_dir, get_socket_filename
|
||||
from .fs import get_security_dir, get_keys_dir, get_base_dir, get_cache_dir, get_config_dir, get_runtime_dir
|
||||
from .fs import dir_is_tagged, dir_is_cachedir, remove_dotdot_prefixes, make_path_safe, scandir_inorder
|
||||
|
@ -1,4 +1,3 @@
|
||||
import errno
|
||||
import os
|
||||
|
||||
from ..constants import * # NOQA
|
||||
@ -112,70 +111,42 @@ class IncludePatternNeverMatchedWarning(BorgWarning):
|
||||
class BackupWarning(BorgWarning):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 102
|
||||
|
||||
|
||||
class BackupOSWarning(BorgWarning):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 104
|
||||
# this is to wrap a caught BackupError exception, so it can be given to print_warning_instance
|
||||
|
||||
@property
|
||||
def exit_code(self):
|
||||
if not modern_ec:
|
||||
return EXIT_WARNING
|
||||
exc = self.args[1]
|
||||
assert isinstance(exc, BackupOSError)
|
||||
if exc.errno in (errno.EPERM, errno.EACCES):
|
||||
return PermissionWarning.exit_mcode
|
||||
elif exc.errno in (errno.ENOENT,):
|
||||
return NotFoundWarning.exit_mcode
|
||||
elif exc.errno in (errno.EIO,):
|
||||
return IOWarning.exit_mcode
|
||||
else:
|
||||
return self.exit_mcode
|
||||
assert isinstance(exc, BackupError)
|
||||
return exc.exit_mcode
|
||||
|
||||
|
||||
class PermissionWarning(BorgWarning):
|
||||
"""{}: {}"""
|
||||
class BackupError(Error):
|
||||
"""{}: backup error"""
|
||||
|
||||
exit_mcode = 105
|
||||
|
||||
|
||||
class IOWarning(BorgWarning):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 106
|
||||
|
||||
|
||||
class NotFoundWarning(BorgWarning):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 107
|
||||
|
||||
|
||||
class BackupError(Exception):
|
||||
"""
|
||||
Exception raised for non-OSError-based exceptions while accessing backup files.
|
||||
"""
|
||||
# Exception raised for non-OSError-based exceptions while accessing backup files.
|
||||
exit_mcode = 102
|
||||
|
||||
|
||||
class BackupRaceConditionError(BackupError):
|
||||
"""
|
||||
Exception raised when encountering a critical race condition while trying to back up a file.
|
||||
"""
|
||||
"""{}: file type or inode changed while we backed it up (race condition, skipped file)"""
|
||||
|
||||
# Exception raised when encountering a critical race condition while trying to back up a file.
|
||||
exit_mcode = 103
|
||||
|
||||
|
||||
class BackupOSError(Exception):
|
||||
"""
|
||||
Wrapper for OSError raised while accessing backup files.
|
||||
class BackupOSError(BackupError):
|
||||
"""{}: {}"""
|
||||
|
||||
Borg does different kinds of IO, and IO failures have different consequences.
|
||||
This wrapper represents failures of input file or extraction IO.
|
||||
These are non-critical and are only reported (exit code = 1, warning).
|
||||
|
||||
Any unwrapped IO error is critical and aborts execution (for example repository IO failure).
|
||||
"""
|
||||
# Wrapper for OSError raised while accessing backup files.
|
||||
#
|
||||
# Borg does different kinds of IO, and IO failures have different consequences.
|
||||
# This wrapper represents failures of input file or extraction IO.
|
||||
# These are non-critical and are only reported (warnings).
|
||||
#
|
||||
# Any unwrapped IO error is critical and aborts execution (for example repository IO failure).
|
||||
exit_mcode = 104
|
||||
|
||||
def __init__(self, op, os_error):
|
||||
self.op = op
|
||||
@ -189,3 +160,21 @@ def __str__(self):
|
||||
return f"{self.op}: {self.os_error}"
|
||||
else:
|
||||
return str(self.os_error)
|
||||
|
||||
|
||||
class BackupPermissionError(BackupOSError):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 105
|
||||
|
||||
|
||||
class BackupIOError(BackupOSError):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 106
|
||||
|
||||
|
||||
class BackupFileNotFoundError(BackupOSError):
|
||||
"""{}: {}"""
|
||||
|
||||
exit_mcode = 107
|
||||
|
Loading…
Reference in New Issue
Block a user