mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-22 22:22:27 +00:00
parent
a56652f5c1
commit
7bacfa2d6e
8 changed files with 46 additions and 42 deletions
1
CHANGES
1
CHANGES
|
@ -8,6 +8,7 @@ Version 0.9
|
||||||
|
|
||||||
(feature release, released on X)
|
(feature release, released on X)
|
||||||
|
|
||||||
|
- Improved error handling / reporting. (#12)
|
||||||
- Use fcntl() instead of flock() when locking repository/cache. (#15)
|
- Use fcntl() instead of flock() when locking repository/cache. (#15)
|
||||||
- Let ssh figure out port/user if not specified so we don't override .ssh/config (#9)
|
- Let ssh figure out port/user if not specified so we don't override .ssh/config (#9)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from attic import xattr
|
from attic import xattr
|
||||||
from attic.chunker import chunkify
|
from attic.chunker import chunkify
|
||||||
from attic.helpers import uid2user, user2uid, gid2group, group2gid, \
|
from attic.helpers import Error, uid2user, user2uid, gid2group, group2gid, \
|
||||||
Statistics, decode_dict, st_mtime_ns, make_path_safe
|
Statistics, decode_dict, st_mtime_ns, make_path_safe
|
||||||
|
|
||||||
ITEMS_BUFFER = 1024 * 1024
|
ITEMS_BUFFER = 1024 * 1024
|
||||||
|
@ -72,11 +72,11 @@ def peek(self):
|
||||||
|
|
||||||
class Archive(object):
|
class Archive(object):
|
||||||
|
|
||||||
class DoesNotExist(Exception):
|
class DoesNotExist(Error):
|
||||||
pass
|
"""Archive {} does not exist"""
|
||||||
|
|
||||||
class AlreadyExists(Exception):
|
class AlreadyExists(Error):
|
||||||
pass
|
"""Archive {} already exists"""
|
||||||
|
|
||||||
def __init__(self, repository, key, manifest, name, cache=None, create=False,
|
def __init__(self, repository, key, manifest, name, cache=None, create=False,
|
||||||
checkpoint_interval=300, numeric_owner=False):
|
checkpoint_interval=300, numeric_owner=False):
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
from attic.repository import Repository
|
from attic.repository import Repository
|
||||||
from attic.cache import Cache
|
from attic.cache import Cache
|
||||||
from attic.key import key_creator
|
from attic.key import key_creator
|
||||||
from attic.helpers import location_validator, format_time, \
|
from attic.helpers import Error, location_validator, format_time, \
|
||||||
format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \
|
format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \
|
||||||
get_cache_dir, get_keys_dir, format_timedelta, prune_split, Manifest, Location, remove_surrogates
|
get_cache_dir, get_keys_dir, format_timedelta, prune_split, Manifest, Location, remove_surrogates
|
||||||
from attic.remote import RepositoryServer, RemoteRepository, ConnectionClosed
|
from attic.remote import RepositoryServer, RemoteRepository, ConnectionClosed
|
||||||
|
@ -477,24 +477,9 @@ def main():
|
||||||
archiver = Archiver()
|
archiver = Archiver()
|
||||||
try:
|
try:
|
||||||
exit_code = archiver.run(sys.argv[1:])
|
exit_code = archiver.run(sys.argv[1:])
|
||||||
except Repository.DoesNotExist:
|
except Error as e:
|
||||||
archiver.print_error('Error: Repository not found')
|
archiver.print_error(e.get_message())
|
||||||
exit_code = 1
|
exit_code = e.exit_code
|
||||||
except Repository.AlreadyExists:
|
|
||||||
archiver.print_error('Error: Repository already exists')
|
|
||||||
exit_code = 1
|
|
||||||
except Archive.AlreadyExists as e:
|
|
||||||
archiver.print_error('Error: Archive "%s" already exists', e)
|
|
||||||
exit_code = 1
|
|
||||||
except Archive.DoesNotExist as e:
|
|
||||||
archiver.print_error('Error: Archive "%s" does not exist', e)
|
|
||||||
exit_code = 1
|
|
||||||
except Cache.RepositoryReplay:
|
|
||||||
archiver.print_error('Cache is newer than repository, refusing to continue')
|
|
||||||
exit_code = 1
|
|
||||||
except ConnectionClosed:
|
|
||||||
archiver.print_error('Connection closed by remote host')
|
|
||||||
exit_code = 1
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
archiver.print_error('Error: Keyboard interrupt')
|
archiver.print_error('Error: Keyboard interrupt')
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from .helpers import get_cache_dir, decode_dict, st_mtime_ns, unhexlify, UpgradableLock
|
from .helpers import Error, get_cache_dir, decode_dict, st_mtime_ns, unhexlify, UpgradableLock
|
||||||
from .hashindex import ChunkIndex
|
from .hashindex import ChunkIndex
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,9 +13,8 @@ class Cache(object):
|
||||||
"""Client Side cache
|
"""Client Side cache
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class RepositoryReplay(Exception):
|
class RepositoryReplay(Error):
|
||||||
"""
|
"""Cache is newer than repository, refusing to continue"""
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, repository, key, manifest):
|
def __init__(self, repository, key, manifest):
|
||||||
self.timestamp = None
|
self.timestamp = None
|
||||||
|
|
|
@ -14,9 +14,22 @@
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
"""Error base class"""
|
||||||
|
|
||||||
|
exit_code = 1
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
return 'Error: ' + type(self).__doc__.format(*self.args)
|
||||||
|
|
||||||
|
|
||||||
class UpgradableLock:
|
class UpgradableLock:
|
||||||
|
|
||||||
|
class LockUpgradeFailed(Error):
|
||||||
|
"""Failed to acquire write lock on {}"""
|
||||||
|
|
||||||
def __init__(self, path, exclusive=False):
|
def __init__(self, path, exclusive=False):
|
||||||
|
self.path = path
|
||||||
try:
|
try:
|
||||||
self.fd = open(path, 'r+')
|
self.fd = open(path, 'r+')
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -28,7 +41,10 @@ def __init__(self, path, exclusive=False):
|
||||||
self.is_exclusive = exclusive
|
self.is_exclusive = exclusive
|
||||||
|
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
fcntl.lockf(self.fd, fcntl.LOCK_EX)
|
try:
|
||||||
|
fcntl.lockf(self.fd, fcntl.LOCK_EX)
|
||||||
|
except OSError as e:
|
||||||
|
raise self.LockUpgradeFailed(self.path)
|
||||||
self.is_exclusive = True
|
self.is_exclusive = True
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
|
|
|
@ -4,17 +4,16 @@
|
||||||
import select
|
import select
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
import sys
|
import sys
|
||||||
import getpass
|
|
||||||
|
|
||||||
|
from .helpers import Error
|
||||||
from .repository import Repository
|
from .repository import Repository
|
||||||
from .lrucache import LRUCache
|
from .lrucache import LRUCache
|
||||||
|
|
||||||
BUFSIZE = 10 * 1024 * 1024
|
BUFSIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
|
|
||||||
class ConnectionClosed(Exception):
|
class ConnectionClosed(Error):
|
||||||
"""Connection closed by remote host
|
"""Connection closed by remote host"""
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class RepositoryServer(object):
|
class RepositoryServer(object):
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
from zlib import crc32
|
from zlib import crc32
|
||||||
|
|
||||||
from .hashindex import NSIndex
|
from .hashindex import NSIndex
|
||||||
from .helpers import IntegrityError, read_msgpack, write_msgpack, unhexlify, UpgradableLock
|
from .helpers import Error, IntegrityError, read_msgpack, write_msgpack, unhexlify, UpgradableLock
|
||||||
from .lrucache import LRUCache
|
from .lrucache import LRUCache
|
||||||
|
|
||||||
MAX_OBJECT_SIZE = 20 * 1024 * 1024
|
MAX_OBJECT_SIZE = 20 * 1024 * 1024
|
||||||
|
@ -30,11 +30,15 @@ class Repository(object):
|
||||||
DEFAULT_MAX_SEGMENT_SIZE = 5 * 1024 * 1024
|
DEFAULT_MAX_SEGMENT_SIZE = 5 * 1024 * 1024
|
||||||
DEFAULT_SEGMENTS_PER_DIR = 10000
|
DEFAULT_SEGMENTS_PER_DIR = 10000
|
||||||
|
|
||||||
class DoesNotExist(KeyError):
|
class DoesNotExist(Error):
|
||||||
"""Requested key does not exist"""
|
"""Repository {} does not exist"""
|
||||||
|
|
||||||
|
class AlreadyExists(Error):
|
||||||
|
"""Repository {} already exists"""
|
||||||
|
|
||||||
|
class InvalidRepository(Error):
|
||||||
|
"""{} is not a valid repository"""
|
||||||
|
|
||||||
class AlreadyExists(KeyError):
|
|
||||||
"""Requested key does not exist"""
|
|
||||||
|
|
||||||
def __init__(self, path, create=False):
|
def __init__(self, path, create=False):
|
||||||
self.io = None
|
self.io = None
|
||||||
|
@ -70,11 +74,11 @@ def open(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
raise self.DoesNotExist(path)
|
raise self.DoesNotExist(path)
|
||||||
self.lock = UpgradableLock(os.path.join(path, 'config'))
|
|
||||||
self.config = RawConfigParser()
|
self.config = RawConfigParser()
|
||||||
self.config.read(os.path.join(self.path, 'config'))
|
self.config.read(os.path.join(self.path, 'config'))
|
||||||
if self.config.getint('repository', 'version') != 1:
|
if not 'repository' in self.config.sections() or self.config.getint('repository', 'version') != 1:
|
||||||
raise Exception('%s Does not look like an Attic repository')
|
raise self.InvalidRepository(path)
|
||||||
|
self.lock = UpgradableLock(os.path.join(path, 'config'))
|
||||||
self.max_segment_size = self.config.getint('repository', 'max_segment_size')
|
self.max_segment_size = self.config.getint('repository', 'max_segment_size')
|
||||||
self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
|
self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
|
||||||
self.id = unhexlify(self.config.get('repository', 'id').strip())
|
self.id = unhexlify(self.config.get('repository', 'id').strip())
|
||||||
|
|
|
@ -84,5 +84,5 @@ def test_read_only_lock_file(self):
|
||||||
file = tempfile.NamedTemporaryFile()
|
file = tempfile.NamedTemporaryFile()
|
||||||
os.chmod(file.name, 0o444)
|
os.chmod(file.name, 0o444)
|
||||||
lock = UpgradableLock(file.name)
|
lock = UpgradableLock(file.name)
|
||||||
self.assert_raises(OSError, lock.upgrade)
|
self.assert_raises(UpgradableLock.LockUpgradeFailed, lock.upgrade)
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
Loading…
Reference in a new issue