mirror of https://github.com/borgbackup/borg.git
Project rename
This commit is contained in:
parent
a5a73ca926
commit
b718a443a8
|
@ -7,4 +7,4 @@ install:
|
|||
- "pip install --use-mirrors Cython"
|
||||
- "pip install -e ."
|
||||
# command to run tests
|
||||
script: fakeroot python -m darc.testsuite.run -vb
|
||||
script: fakeroot python -m attic.testsuite.run -vb
|
||||
|
|
32
README.rst
32
README.rst
|
@ -1,16 +1,16 @@
|
|||
What is darc?
|
||||
-------------
|
||||
Darc is a Deduplicating ARChiver written in Python. The main goal of darc is
|
||||
to provide an efficient and secure way to backup data. The data deduplication
|
||||
technique used makes darc suitable for daily backups since only actual changes
|
||||
What is Attic?
|
||||
--------------
|
||||
Attic is a deduplicating backup program. The main goal of attic is to provide
|
||||
an efficient and secure way to backup data. The data deduplication
|
||||
technique used makes Attic suitable for daily backups since only actual changes
|
||||
are stored.
|
||||
|
||||
Easy to use
|
||||
~~~~~~~~~~~
|
||||
Initialze backup repository and create a backup archive::
|
||||
|
||||
$ darc init /usbdrive/my-backup.darc
|
||||
$ darc create -v /usbdrive/my-backup.darc::documents ~/Documents
|
||||
$ attic init /usbdrive/my-backup.attic
|
||||
$ attic create -v /usbdrive/my-backup.attic::documents ~/Documents
|
||||
|
||||
Main features
|
||||
~~~~~~~~~~~~~
|
||||
|
@ -25,36 +25,36 @@ Optional data encryption
|
|||
and authenticity is verified using HMAC-SHA256.
|
||||
|
||||
Off-site backups
|
||||
darc can store data on any remote host accessible over SSH as long as
|
||||
darc is installed.
|
||||
attic can store data on any remote host accessible over SSH as long as
|
||||
attic is installed.
|
||||
|
||||
What do I need?
|
||||
---------------
|
||||
Darc requires Python 3.2 or above to work. Besides Python darc also requires
|
||||
Attic requires Python 3.2 or above to work. Besides Python attic also requires
|
||||
msgpack-python and sufficiently recent OpenSSL (>= 1.0.0).
|
||||
|
||||
How do I install it?
|
||||
--------------------
|
||||
::
|
||||
|
||||
$ pip install darc
|
||||
$ pip install Attic
|
||||
|
||||
Where are the docs?
|
||||
-------------------
|
||||
Go to https://pythonhosted.org/darc/ for a prebuilt version of the docs. You
|
||||
Go to https://pythonhosted.org/Attic/ for a prebuilt version of the docs. You
|
||||
can also build them yourself form the docs folder.
|
||||
|
||||
Where are the tests?
|
||||
--------------------
|
||||
The tests are in the darc/testsuite package. To run the test suite use the
|
||||
The tests are in the attic/testsuite package. To run the test suite use the
|
||||
following command::
|
||||
|
||||
$ python -m darc.testsuite.run
|
||||
$ python -m attic.testsuite.run
|
||||
|
||||
Contribute
|
||||
----------
|
||||
Found a bug? Have any ideas to improve darc? Add bug reports and feature
|
||||
requests to the `issue tracker <https://github.com/jborg/darc/issues>`_.
|
||||
Found a bug? Have any ideas to improve attic? Add bug reports and feature
|
||||
requests to the `issue tracker <https://github.com/jborg/attic/issues>`_.
|
||||
|
||||
You can also ask the author a question directly by
|
||||
`email <mailto:jonas@borgstrom.se>`_.
|
||||
|
|
|
@ -35,7 +35,7 @@ typedef struct {
|
|||
int upper_limit;
|
||||
} HashIndex;
|
||||
|
||||
#define MAGIC "DARCHASH"
|
||||
#define MAGIC "ATTICIDX"
|
||||
#define EMPTY ((int32_t)-1)
|
||||
#define DELETED ((int32_t)-2)
|
||||
#define MAX_BUCKET_SIZE 512
|
|
@ -32,7 +32,7 @@ class Archiver:
|
|||
def print_error(self, msg, *args):
|
||||
msg = args and msg % args or msg
|
||||
self.exit_code = 1
|
||||
print('darc: ' + msg, file=sys.stderr)
|
||||
print('attic: ' + msg, file=sys.stderr)
|
||||
|
||||
def print_verbose(self, msg, *args, **kw):
|
||||
if self.verbose:
|
||||
|
@ -76,7 +76,7 @@ class Archiver:
|
|||
archive = Archive(repository, key, manifest, args.archive.archive, cache=cache,
|
||||
create=True, checkpoint_interval=args.checkpoint_interval,
|
||||
numeric_owner=args.numeric_owner)
|
||||
# Add darc cache dir to inode_skip list
|
||||
# Add Attic cache dir to inode_skip list
|
||||
skip_inodes = set()
|
||||
try:
|
||||
st = os.stat(get_cache_dir())
|
||||
|
@ -314,7 +314,7 @@ class Archiver:
|
|||
default=False,
|
||||
help='verbose output')
|
||||
|
||||
parser = argparse.ArgumentParser(description='Darc - Deduplicating Archiver')
|
||||
parser = argparse.ArgumentParser(description='Attic - Deduplicated Backups')
|
||||
subparsers = parser.add_subparsers(title='Available subcommands')
|
||||
|
||||
subparser = subparsers.add_parser('serve', parents=[common_parser])
|
|
@ -35,7 +35,7 @@ class Cache(object):
|
|||
"""
|
||||
os.makedirs(self.path)
|
||||
with open(os.path.join(self.path, 'README'), 'w') as fd:
|
||||
fd.write('This is a DARC cache')
|
||||
fd.write('This is an Attic cache')
|
||||
config = RawConfigParser()
|
||||
config.add_section('cache')
|
||||
config.set('cache', 'version', '1')
|
||||
|
@ -49,14 +49,14 @@ class Cache(object):
|
|||
|
||||
def open(self):
|
||||
if not os.path.isdir(self.path):
|
||||
raise Exception('%s Does not look like a darc cache' % self.path)
|
||||
raise Exception('%s Does not look like an Attic cache' % self.path)
|
||||
self.lock_fd = open(os.path.join(self.path, 'README'), 'r+')
|
||||
fcntl.flock(self.lock_fd, fcntl.LOCK_EX)
|
||||
self.rollback()
|
||||
self.config = RawConfigParser()
|
||||
self.config.read(os.path.join(self.path, 'config'))
|
||||
if self.config.getint('cache', 'version') != 1:
|
||||
raise Exception('%s Does not look like a darc cache')
|
||||
raise Exception('%s Does not look like an Attic cache')
|
||||
self.id = self.config.get('cache', 'repository')
|
||||
self.manifest_id = unhexlify(self.config.get('cache', 'manifest'))
|
||||
self.chunks = ChunkIndex(os.path.join(self.path, 'chunks').encode('utf-8'))
|
|
@ -82,14 +82,14 @@ class Statistics:
|
|||
|
||||
def get_keys_dir():
|
||||
"""Determine where to repository keys and cache"""
|
||||
return os.environ.get('DARC_KEYS_DIR',
|
||||
os.path.join(os.path.expanduser('~'), '.darc', 'keys'))
|
||||
return os.environ.get('ATTIC_KEYS_DIR',
|
||||
os.path.join(os.path.expanduser('~'), '.attic', 'keys'))
|
||||
|
||||
|
||||
def get_cache_dir():
|
||||
"""Determine where to repository keys and cache"""
|
||||
return os.environ.get('DARC_CACHE_DIR',
|
||||
os.path.join(os.path.expanduser('~'), '.cache', 'darc'))
|
||||
return os.environ.get('ATTIC_CACHE_DIR',
|
||||
os.path.join(os.path.expanduser('~'), '.cache', 'attic'))
|
||||
|
||||
|
||||
def to_localtime(ts):
|
|
@ -137,7 +137,7 @@ class PassphraseKey(AESKeyBase):
|
|||
@classmethod
|
||||
def create(cls, repository, args):
|
||||
key = cls()
|
||||
passphrase = os.environ.get('DARC_PASSPHRASE')
|
||||
passphrase = os.environ.get('ATTIC_PASSPHRASE')
|
||||
if passphrase is not None:
|
||||
passphrase2 = passphrase
|
||||
else:
|
||||
|
@ -159,7 +159,7 @@ class PassphraseKey(AESKeyBase):
|
|||
def detect(cls, repository, manifest_data):
|
||||
prompt = 'Enter passphrase for %s: ' % repository._location.orig
|
||||
key = cls()
|
||||
passphrase = os.environ.get('DARC_PASSPHRASE')
|
||||
passphrase = os.environ.get('ATTIC_PASSPHRASE')
|
||||
if passphrase is None:
|
||||
passphrase = getpass(prompt)
|
||||
while True:
|
||||
|
@ -177,7 +177,7 @@ class PassphraseKey(AESKeyBase):
|
|||
|
||||
|
||||
class KeyfileKey(AESKeyBase):
|
||||
FILE_ID = 'DARC KEY'
|
||||
FILE_ID = 'ATTIC KEY'
|
||||
TYPE = 0x00
|
||||
|
||||
@classmethod
|
||||
|
@ -185,7 +185,7 @@ class KeyfileKey(AESKeyBase):
|
|||
key = cls()
|
||||
path = cls.find_key_file(repository)
|
||||
prompt = 'Enter passphrase for key file %s: ' % path
|
||||
passphrase = os.environ.get('DARC_PASSPHRASE', '')
|
||||
passphrase = os.environ.get('ATTIC_PASSPHRASE', '')
|
||||
while not key.load(path, passphrase):
|
||||
passphrase = getpass(prompt)
|
||||
key.init_ciphers(PREFIX + long_to_bytes(key.extract_iv(manifest_data) + 1000))
|
||||
|
@ -199,7 +199,7 @@ class KeyfileKey(AESKeyBase):
|
|||
filename = os.path.join(keys_dir, name)
|
||||
with open(filename, 'r') as fd:
|
||||
line = fd.readline().strip()
|
||||
if line and line.startswith(cls.FILE_ID) and line[9:] == id:
|
||||
if line and line.startswith(cls.FILE_ID) and line[10:] == id:
|
||||
return filename
|
||||
raise Exception('Key file for repository with ID %s not found' % id)
|
||||
|
||||
|
@ -278,7 +278,7 @@ class KeyfileKey(AESKeyBase):
|
|||
while os.path.exists(path):
|
||||
i += 1
|
||||
path = filename + '.%d' % i
|
||||
passphrase = os.environ.get('DARC_PASSPHRASE')
|
||||
passphrase = os.environ.get('ATTIC_PASSPHRASE')
|
||||
if passphrase is not None:
|
||||
passphrase2 = passphrase
|
||||
else:
|
|
@ -81,9 +81,9 @@ class RemoteRepository(object):
|
|||
self.msgid = 0
|
||||
self.received_msgid = 0
|
||||
if location.host == '__testsuite__':
|
||||
args = [sys.executable, '-m', 'darc.archiver', 'serve']
|
||||
args = [sys.executable, '-m', 'attic.archiver', 'serve']
|
||||
else:
|
||||
args = ['ssh', '-p', str(location.port), '%s@%s' % (location.user or getpass.getuser(), location.host), 'darc', 'serve']
|
||||
args = ['ssh', '-p', str(location.port), '%s@%s' % (location.user or getpass.getuser(), location.host), 'attic', 'serve']
|
||||
self.p = Popen(args, bufsize=0, stdin=PIPE, stdout=PIPE)
|
||||
self.stdin_fd = self.p.stdin.fileno()
|
||||
self.stdout_fd = self.p.stdout.fileno()
|
|
@ -5,8 +5,6 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
import struct
|
||||
import tempfile
|
||||
import unittest
|
||||
from zlib import crc32
|
||||
|
||||
from .hashindex import NSIndex
|
||||
|
@ -14,7 +12,7 @@ from .helpers import IntegrityError, read_msgpack, write_msgpack, unhexlify
|
|||
from .lrucache import LRUCache
|
||||
|
||||
MAX_OBJECT_SIZE = 20 * 1024 * 1024
|
||||
|
||||
MAGIC = b'ATTICSEG'
|
||||
TAG_PUT = 0
|
||||
TAG_DELETE = 1
|
||||
TAG_COMMIT = 2
|
||||
|
@ -57,7 +55,7 @@ class Repository(object):
|
|||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
with open(os.path.join(path, 'README'), 'w') as fd:
|
||||
fd.write('This is a DARC repository\n')
|
||||
fd.write('This is an Attic repository\n')
|
||||
os.mkdir(os.path.join(path, 'data'))
|
||||
config = RawConfigParser()
|
||||
config.add_section('repository')
|
||||
|
@ -78,7 +76,7 @@ class Repository(object):
|
|||
self.config = RawConfigParser()
|
||||
self.config.read(os.path.join(self.path, 'config'))
|
||||
if self.config.getint('repository', 'version') != 1:
|
||||
raise Exception('%s Does not look like a darc repository')
|
||||
raise Exception('%s Does not look like an Attic repository')
|
||||
self.max_segment_size = self.config.getint('repository', 'max_segment_size')
|
||||
self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
|
||||
self.id = unhexlify(self.config.get('repository', 'id').strip())
|
||||
|
@ -325,7 +323,7 @@ class LoggedIO(object):
|
|||
if not os.path.exists(dirname):
|
||||
os.mkdir(dirname)
|
||||
self._write_fd = open(self.segment_filename(self.segment), 'ab')
|
||||
self._write_fd.write(b'DSEGMENT')
|
||||
self._write_fd.write(MAGIC)
|
||||
self.offset = 8
|
||||
return self._write_fd
|
||||
|
||||
|
@ -346,7 +344,7 @@ class LoggedIO(object):
|
|||
def iter_objects(self, segment, lookup=None, include_data=False):
|
||||
fd = self.get_fd(segment)
|
||||
fd.seek(0)
|
||||
if fd.read(8) != b'DSEGMENT':
|
||||
if fd.read(8) != MAGIC:
|
||||
raise IntegrityError('Invalid segment header')
|
||||
offset = 8
|
||||
header = fd.read(self.header_fmt.size)
|
|
@ -1,7 +1,7 @@
|
|||
import unittest
|
||||
|
||||
|
||||
class DarcTestCase(unittest.TestCase):
|
||||
class AtticTestCase(unittest.TestCase):
|
||||
"""
|
||||
"""
|
||||
assert_equal = unittest.TestCase.assertEqual
|
||||
|
@ -25,7 +25,7 @@ class TestLoader(unittest.TestLoader):
|
|||
"""A customzied test loader that properly detects and filters our test cases
|
||||
"""
|
||||
def loadTestsFromName(self, pattern, module=None):
|
||||
suite = self.discover('darc.testsuite', '*.py')
|
||||
suite = self.discover('attic.testsuite', '*.py')
|
||||
tests = unittest.TestSuite()
|
||||
for test in get_tests(suite):
|
||||
if pattern.lower() in test.id().lower():
|
|
@ -5,10 +5,10 @@ import stat
|
|||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
from darc import xattr
|
||||
from darc.archiver import Archiver
|
||||
from darc.repository import Repository
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic import xattr
|
||||
from attic.archiver import Archiver
|
||||
from attic.repository import Repository
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
has_mtime_ns = sys.version >= '3.3'
|
||||
utime_supports_fd = os.utime in getattr(os, 'supports_fd', {})
|
||||
|
@ -27,7 +27,7 @@ class changedir:
|
|||
os.chdir(self.old)
|
||||
|
||||
|
||||
class ArchiverTestCase(DarcTestCase):
|
||||
class ArchiverTestCase(AtticTestCase):
|
||||
|
||||
prefix = ''
|
||||
|
||||
|
@ -40,8 +40,8 @@ class ArchiverTestCase(DarcTestCase):
|
|||
self.output_path = os.path.join(self.tmpdir, 'output')
|
||||
self.keys_path = os.path.join(self.tmpdir, 'keys')
|
||||
self.cache_path = os.path.join(self.tmpdir, 'cache')
|
||||
os.environ['DARC_KEYS_DIR'] = self.keys_path
|
||||
os.environ['DARC_CACHE_DIR'] = self.cache_path
|
||||
os.environ['ATTIC_KEYS_DIR'] = self.keys_path
|
||||
os.environ['ATTIC_CACHE_DIR'] = self.cache_path
|
||||
os.mkdir(self.input_path)
|
||||
os.mkdir(self.output_path)
|
||||
os.mkdir(self.keys_path)
|
||||
|
@ -53,7 +53,7 @@ class ArchiverTestCase(DarcTestCase):
|
|||
shutil.rmtree(self.tmpdir)
|
||||
os.chdir(self._old_wd)
|
||||
|
||||
def darc(self, *args, **kwargs):
|
||||
def attic(self, *args, **kwargs):
|
||||
exit_code = kwargs.get('exit_code', 0)
|
||||
args = list(args)
|
||||
try:
|
||||
|
@ -70,8 +70,8 @@ class ArchiverTestCase(DarcTestCase):
|
|||
sys.stdout, sys.stderr = stdout, stderr
|
||||
|
||||
def create_src_archive(self, name):
|
||||
self.darc('init', self.repository_location)
|
||||
self.darc('create', self.repository_location + '::' + name, src_dir)
|
||||
self.attic('init', self.repository_location)
|
||||
self.attic('create', self.repository_location + '::' + name, src_dir)
|
||||
|
||||
def create_regual_file(self, name, size=0):
|
||||
filename = os.path.join(self.input_path, name)
|
||||
|
@ -132,91 +132,91 @@ class ArchiverTestCase(DarcTestCase):
|
|||
os.symlink('somewhere', os.path.join(self.input_path, 'link1'))
|
||||
# FIFO node
|
||||
os.mkfifo(os.path.join(self.input_path, 'fifo1'))
|
||||
self.darc('init', self.repository_location)
|
||||
self.darc('create', self.repository_location + '::test', 'input')
|
||||
self.darc('create', self.repository_location + '::test.2', 'input')
|
||||
self.attic('init', self.repository_location)
|
||||
self.attic('create', self.repository_location + '::test', 'input')
|
||||
self.attic('create', self.repository_location + '::test.2', 'input')
|
||||
with changedir('output'):
|
||||
self.darc('extract', self.repository_location + '::test')
|
||||
self.assert_equal(len(self.darc('list', self.repository_location).splitlines()), 2)
|
||||
self.assert_equal(len(self.darc('list', self.repository_location + '::test').splitlines()), 9)
|
||||
self.attic('extract', self.repository_location + '::test')
|
||||
self.assert_equal(len(self.attic('list', self.repository_location).splitlines()), 2)
|
||||
self.assert_equal(len(self.attic('list', self.repository_location + '::test').splitlines()), 9)
|
||||
self.diff_dirs('input', 'output/input')
|
||||
info_output = self.darc('info', self.repository_location + '::test')
|
||||
info_output = self.attic('info', self.repository_location + '::test')
|
||||
shutil.rmtree(self.cache_path)
|
||||
info_output2 = self.darc('info', self.repository_location + '::test')
|
||||
info_output2 = self.attic('info', self.repository_location + '::test')
|
||||
# info_output2 starts with some "initializing cache" text but should
|
||||
# end the same way as info_output
|
||||
assert info_output2.endswith(info_output)
|
||||
|
||||
def test_extract_include_exclude(self):
|
||||
self.darc('init', self.repository_location)
|
||||
self.attic('init', self.repository_location)
|
||||
self.create_regual_file('file1', size=1024 * 80)
|
||||
self.create_regual_file('file2', size=1024 * 80)
|
||||
self.create_regual_file('file3', size=1024 * 80)
|
||||
self.create_regual_file('file4', size=1024 * 80)
|
||||
self.darc('create', '--exclude=input/file4', self.repository_location + '::test', 'input')
|
||||
self.attic('create', '--exclude=input/file4', self.repository_location + '::test', 'input')
|
||||
with changedir('output'):
|
||||
self.darc('extract', self.repository_location + '::test', 'input/file1', )
|
||||
self.attic('extract', self.repository_location + '::test', 'input/file1', )
|
||||
self.assert_equal(sorted(os.listdir('output/input')), ['file1'])
|
||||
with changedir('output'):
|
||||
self.darc('extract', '--exclude=input/file2', self.repository_location + '::test')
|
||||
self.attic('extract', '--exclude=input/file2', self.repository_location + '::test')
|
||||
self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'file3'])
|
||||
|
||||
def test_overwrite(self):
|
||||
self.create_regual_file('file1', size=1024 * 80)
|
||||
self.create_regual_file('dir2/file2', size=1024 * 80)
|
||||
self.darc('init', self.repository_location)
|
||||
self.darc('create', self.repository_location + '::test', 'input')
|
||||
self.attic('init', self.repository_location)
|
||||
self.attic('create', self.repository_location + '::test', 'input')
|
||||
# Overwriting regular files and directories should be supported
|
||||
os.mkdir('output/input')
|
||||
os.mkdir('output/input/file1')
|
||||
os.mkdir('output/input/dir2')
|
||||
with changedir('output'):
|
||||
self.darc('extract', self.repository_location + '::test')
|
||||
self.attic('extract', self.repository_location + '::test')
|
||||
self.diff_dirs('input', 'output/input')
|
||||
# But non-empty dirs should fail
|
||||
os.unlink('output/input/file1')
|
||||
os.mkdir('output/input/file1')
|
||||
os.mkdir('output/input/file1/dir')
|
||||
with changedir('output'):
|
||||
self.darc('extract', self.repository_location + '::test', exit_code=1)
|
||||
self.attic('extract', self.repository_location + '::test', exit_code=1)
|
||||
|
||||
def test_delete(self):
|
||||
self.create_regual_file('file1', size=1024 * 80)
|
||||
self.create_regual_file('dir2/file2', size=1024 * 80)
|
||||
self.darc('init', self.repository_location)
|
||||
self.darc('create', self.repository_location + '::test', 'input')
|
||||
self.darc('create', self.repository_location + '::test.2', 'input')
|
||||
self.darc('verify', self.repository_location + '::test')
|
||||
self.darc('verify', self.repository_location + '::test.2')
|
||||
self.darc('delete', self.repository_location + '::test')
|
||||
self.darc('verify', self.repository_location + '::test.2')
|
||||
self.darc('delete', self.repository_location + '::test.2')
|
||||
self.attic('init', self.repository_location)
|
||||
self.attic('create', self.repository_location + '::test', 'input')
|
||||
self.attic('create', self.repository_location + '::test.2', 'input')
|
||||
self.attic('verify', self.repository_location + '::test')
|
||||
self.attic('verify', self.repository_location + '::test.2')
|
||||
self.attic('delete', self.repository_location + '::test')
|
||||
self.attic('verify', self.repository_location + '::test.2')
|
||||
self.attic('delete', self.repository_location + '::test.2')
|
||||
# Make sure all data except the manifest has been deleted
|
||||
repository = Repository(self.repository_path)
|
||||
self.assert_equal(repository._len(), 1)
|
||||
|
||||
def test_corrupted_repository(self):
|
||||
self.create_src_archive('test')
|
||||
self.darc('verify', self.repository_location + '::test')
|
||||
self.attic('verify', self.repository_location + '::test')
|
||||
name = sorted(os.listdir(os.path.join(self.tmpdir, 'repository', 'data', '0')), reverse=True)[0]
|
||||
fd = open(os.path.join(self.tmpdir, 'repository', 'data', '0', name), 'r+')
|
||||
fd.seek(100)
|
||||
fd.write('X')
|
||||
fd.close()
|
||||
self.darc('verify', self.repository_location + '::test', exit_code=1)
|
||||
self.attic('verify', self.repository_location + '::test', exit_code=1)
|
||||
|
||||
def test_prune_repository(self):
|
||||
self.darc('init', self.repository_location)
|
||||
self.darc('create', self.repository_location + '::test1', src_dir)
|
||||
self.darc('create', self.repository_location + '::test2', src_dir)
|
||||
self.darc('prune', self.repository_location, '--daily=2')
|
||||
output = self.darc('list', self.repository_location)
|
||||
self.attic('init', self.repository_location)
|
||||
self.attic('create', self.repository_location + '::test1', src_dir)
|
||||
self.attic('create', self.repository_location + '::test2', src_dir)
|
||||
self.attic('prune', self.repository_location, '--daily=2')
|
||||
output = self.attic('list', self.repository_location)
|
||||
assert 'test1' not in output
|
||||
assert 'test2' in output
|
||||
|
||||
def test_usage(self):
|
||||
self.assert_raises(SystemExit, lambda: self.darc())
|
||||
self.assert_raises(SystemExit, lambda: self.darc('-h'))
|
||||
self.assert_raises(SystemExit, lambda: self.attic())
|
||||
self.assert_raises(SystemExit, lambda: self.attic('-h'))
|
||||
|
||||
|
||||
class RemoteArchiverTestCase(ArchiverTestCase):
|
|
@ -1,9 +1,9 @@
|
|||
from darc.chunker import chunkify, buzhash, buzhash_update
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic.chunker import chunkify, buzhash, buzhash_update
|
||||
from attic.testsuite import AtticTestCase
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class ChunkerTestCase(DarcTestCase):
|
||||
class ChunkerTestCase(AtticTestCase):
|
||||
|
||||
def test_chunkify(self):
|
||||
data = b'0' * 1024 * 1024 * 15 + b'Y'
|
|
@ -1,9 +1,9 @@
|
|||
from binascii import hexlify
|
||||
from darc.testsuite import DarcTestCase
|
||||
from darc.crypto import AES, bytes_to_long, bytes_to_int, long_to_bytes, pbkdf2_sha256, get_random_bytes
|
||||
from attic.testsuite import AtticTestCase
|
||||
from attic.crypto import AES, bytes_to_long, bytes_to_int, long_to_bytes, pbkdf2_sha256, get_random_bytes
|
||||
|
||||
|
||||
class CryptoTestCase(DarcTestCase):
|
||||
class CryptoTestCase(AtticTestCase):
|
||||
|
||||
def test_bytes_to_int(self):
|
||||
self.assert_equal(bytes_to_int(b'\0\0\0\1'), 1)
|
|
@ -1,10 +1,10 @@
|
|||
import hashlib
|
||||
import tempfile
|
||||
from darc.hashindex import NSIndex, ChunkIndex
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic.hashindex import NSIndex, ChunkIndex
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
|
||||
class HashIndexTestCase(DarcTestCase):
|
||||
class HashIndexTestCase(AtticTestCase):
|
||||
|
||||
def _generic_test(self, cls, make_value, sha):
|
||||
idx_name = tempfile.NamedTemporaryFile()
|
||||
|
@ -41,8 +41,8 @@ class HashIndexTestCase(DarcTestCase):
|
|||
self.assert_equal(len(cls(idx_name.name)), 0)
|
||||
|
||||
def test_nsindex(self):
|
||||
self._generic_test(NSIndex, lambda x: (x, x), '9a6f9cb3c03d83ed611265eeef1f9a9d69c2f0417a35ac14d56ce573d0c8b356')
|
||||
self._generic_test(NSIndex, lambda x: (x, x), '369a18ae6a52524eb2884a3c0fdc2824947edd017a2688c5d4d7b3510c245ab9')
|
||||
|
||||
def test_chunkindex(self):
|
||||
self._generic_test(ChunkIndex, lambda x: (x, x, x), '9c35f237e533b6d2533d2646da127052d615ab9b66de65a795cd922b337741ca')
|
||||
self._generic_test(ChunkIndex, lambda x: (x, x, x), 'ed22e8a883400453c0ee79a06c54df72c994a54eeefdc6c0989efdc5ee6d07b7')
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from datetime import datetime
|
||||
from darc.helpers import Location, format_timedelta, IncludePattern, ExcludePattern
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic.helpers import Location, format_timedelta, IncludePattern, ExcludePattern
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
|
||||
class LocationTestCase(DarcTestCase):
|
||||
class LocationTestCase(AtticTestCase):
|
||||
|
||||
def test(self):
|
||||
self.assert_equal(
|
||||
|
@ -19,8 +19,8 @@ class LocationTestCase(DarcTestCase):
|
|||
"Location(proto='ssh', user='user', host='host', port=22, path='/some/path', archive='archive')"
|
||||
)
|
||||
self.assert_equal(
|
||||
repr(Location('mybackup.darc::archive')),
|
||||
"Location(proto='file', user=None, host=None, port=None, path='mybackup.darc', archive='archive')"
|
||||
repr(Location('mybackup.attic::archive')),
|
||||
"Location(proto='file', user=None, host=None, port=None, path='mybackup.attic', archive='archive')"
|
||||
)
|
||||
self.assert_equal(
|
||||
repr(Location('/some/absolute/path::archive')),
|
||||
|
@ -32,7 +32,7 @@ class LocationTestCase(DarcTestCase):
|
|||
)
|
||||
|
||||
|
||||
class FormatTimedeltaTestCase(DarcTestCase):
|
||||
class FormatTimedeltaTestCase(AtticTestCase):
|
||||
|
||||
def test(self):
|
||||
t0 = datetime(2001, 1, 1, 10, 20, 3, 0)
|
||||
|
@ -43,7 +43,7 @@ class FormatTimedeltaTestCase(DarcTestCase):
|
|||
)
|
||||
|
||||
|
||||
class PatternTestCase(DarcTestCase):
|
||||
class PatternTestCase(AtticTestCase):
|
||||
|
||||
def test(self):
|
||||
self.assert_equal(IncludePattern('/usr').match('/usr'), True)
|
|
@ -3,19 +3,19 @@ import re
|
|||
import shutil
|
||||
import tempfile
|
||||
from binascii import hexlify
|
||||
from darc.crypto import bytes_to_long
|
||||
from darc.testsuite import DarcTestCase
|
||||
from darc.key import PlaintextKey, PassphraseKey, KeyfileKey
|
||||
from darc.helpers import Location, unhexlify
|
||||
from attic.crypto import bytes_to_long
|
||||
from attic.testsuite import AtticTestCase
|
||||
from attic.key import PlaintextKey, PassphraseKey, KeyfileKey
|
||||
from attic.helpers import Location, unhexlify
|
||||
|
||||
|
||||
class KeyTestCase(DarcTestCase):
|
||||
class KeyTestCase(AtticTestCase):
|
||||
|
||||
class MockArgs(object):
|
||||
repository = Location(tempfile.mkstemp()[1])
|
||||
|
||||
keyfile2_key_file = """
|
||||
DARC KEY 0000000000000000000000000000000000000000000000000000000000000000
|
||||
ATTIC KEY 0000000000000000000000000000000000000000000000000000000000000000
|
||||
hqppdGVyYXRpb25zzgABhqCkaGFzaNoAIMyonNI+7Cjv0qHi0AOBM6bLGxACJhfgzVD2oq
|
||||
bIS9SFqWFsZ29yaXRobaZzaGEyNTakc2FsdNoAINNK5qqJc1JWSUjACwFEWGTdM7Nd0a5l
|
||||
1uBGPEb+9XM9p3ZlcnNpb24BpGRhdGHaANAYDT5yfPpU099oBJwMomsxouKyx/OG4QIXK2
|
||||
|
@ -32,7 +32,7 @@ class KeyTestCase(DarcTestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.tmppath = tempfile.mkdtemp()
|
||||
os.environ['DARC_KEYS_DIR'] = self.tmppath
|
||||
os.environ['ATTIC_KEYS_DIR'] = self.tmppath
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmppath)
|
||||
|
@ -44,13 +44,6 @@ class KeyTestCase(DarcTestCase):
|
|||
_location = _Location()
|
||||
id = bytes(32)
|
||||
|
||||
def setUp(self):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
os.environ['DARC_KEYS_DIR'] = self.tmpdir
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
|
||||
def test_plaintext(self):
|
||||
key = PlaintextKey.create(None, None)
|
||||
data = b'foo'
|
||||
|
@ -58,7 +51,7 @@ class KeyTestCase(DarcTestCase):
|
|||
self.assert_equal(data, key.decrypt(key.id_hash(data), key.encrypt(data)))
|
||||
|
||||
def test_keyfile(self):
|
||||
os.environ['DARC_PASSPHRASE'] = 'test'
|
||||
os.environ['ATTIC_PASSPHRASE'] = 'test'
|
||||
key = KeyfileKey.create(self.MockRepository(), self.MockArgs())
|
||||
self.assert_equal(bytes_to_long(key.enc_cipher.iv, 8), 0)
|
||||
manifest = key.encrypt(b'')
|
||||
|
@ -72,14 +65,14 @@ class KeyTestCase(DarcTestCase):
|
|||
self.assert_equal(data, key2.decrypt(key.id_hash(data), key.encrypt(data)))
|
||||
|
||||
def test_keyfile2(self):
|
||||
with open(os.path.join(os.environ['DARC_KEYS_DIR'], 'keyfile'), 'w') as fd:
|
||||
with open(os.path.join(os.environ['ATTIC_KEYS_DIR'], 'keyfile'), 'w') as fd:
|
||||
fd.write(self.keyfile2_key_file)
|
||||
os.environ['DARC_PASSPHRASE'] = 'passphrase'
|
||||
os.environ['ATTIC_PASSPHRASE'] = 'passphrase'
|
||||
key = KeyfileKey.detect(self.MockRepository(), self.keyfile2_cdata)
|
||||
self.assert_equal(key.decrypt(self.keyfile2_id, self.keyfile2_cdata), b'payload')
|
||||
|
||||
def test_passphrase(self):
|
||||
os.environ['DARC_PASSPHRASE'] = 'test'
|
||||
os.environ['ATTIC_PASSPHRASE'] = 'test'
|
||||
key = PassphraseKey.create(self.MockRepository(), None)
|
||||
self.assert_equal(bytes_to_long(key.enc_cipher.iv, 8), 0)
|
||||
self.assert_equal(hexlify(key.id_key), b'793b0717f9d8fb01c751a487e9b827897ceea62409870600013fbc6b4d8d7ca6')
|
|
@ -1,8 +1,8 @@
|
|||
from darc.lrucache import LRUCache
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic.lrucache import LRUCache
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
|
||||
class LRUCacheTestCase(DarcTestCase):
|
||||
class LRUCacheTestCase(AtticTestCase):
|
||||
|
||||
def test(self):
|
||||
c = LRUCache(2)
|
|
@ -1,13 +1,13 @@
|
|||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from darc.helpers import Location
|
||||
from darc.remote import RemoteRepository
|
||||
from darc.repository import Repository
|
||||
from darc.testsuite import DarcTestCase
|
||||
from attic.helpers import Location
|
||||
from attic.remote import RemoteRepository
|
||||
from attic.repository import Repository
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
|
||||
class RepositoryTestCase(DarcTestCase):
|
||||
class RepositoryTestCase(AtticTestCase):
|
||||
|
||||
def open(self, create=False):
|
||||
return Repository(os.path.join(self.tmppath, 'repository'), create=create)
|
|
@ -1,5 +1,5 @@
|
|||
import unittest
|
||||
from darc.testsuite import TestLoader
|
||||
from attic.testsuite import TestLoader
|
||||
|
||||
|
||||
def main():
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from darc.testsuite import DarcTestCase
|
||||
from darc.xattr import lsetxattr, llistxattr, lgetxattr, get_all, set, flistxattr, fgetxattr, fsetxattr, is_enabled
|
||||
from attic.testsuite import AtticTestCase
|
||||
from attic.xattr import lsetxattr, llistxattr, lgetxattr, get_all, set, flistxattr, fgetxattr, fsetxattr, is_enabled
|
||||
|
||||
|
||||
@unittest.skipUnless(is_enabled(), 'xattr not enabled on filesystem')
|
||||
class XattrTestCase(DarcTestCase):
|
||||
class XattrTestCase(AtticTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.tmpfile = tempfile.NamedTemporaryFile()
|
|
@ -73,17 +73,17 @@ qthelp:
|
|||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Darc.qhcp"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/attic.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Darc.qhc"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/attic.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Darc"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Darc"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/attic"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/attic"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
|
|
16
docs/conf.py
16
docs/conf.py
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Darc documentation build configuration file, created by
|
||||
# Attic documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Sep 10 18:18:25 2011.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
|
@ -11,7 +11,7 @@
|
|||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os, darc
|
||||
import sys, os, attic
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
|
@ -40,7 +40,7 @@ source_suffix = '.rst'
|
|||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Darc - Deduplicating Archiver'
|
||||
project = 'Attic - Deduplicating Archiver'
|
||||
copyright = '2010-2013, Jonas Borgström'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -48,9 +48,9 @@ copyright = '2010-2013, Jonas Borgström'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = darc.__version__
|
||||
version = attic.__version__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = darc.__release__
|
||||
release = attic.__release__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -164,7 +164,7 @@ html_static_path = ['_static']
|
|||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Darcdoc'
|
||||
htmlhelp_basename = 'atticdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
@ -178,7 +178,7 @@ htmlhelp_basename = 'Darcdoc'
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Darc.tex', 'Darc Documentation',
|
||||
('index', 'Attic.tex', 'Attic Documentation',
|
||||
'Jonas Borgström', 'manual'),
|
||||
]
|
||||
|
||||
|
@ -211,6 +211,6 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'darc', 'Darc Documentation',
|
||||
('index', 'attic', 'Attic Documentation',
|
||||
['Jonas Borgström'], 1)
|
||||
]
|
||||
|
|
|
@ -12,8 +12,8 @@ Currently Linux and MacOS X are supported.
|
|||
Can I backup VM disk images?
|
||||
----------------------------
|
||||
|
||||
Yes, the :ref:`deduplication <deduplication_def>` technique used by darc will
|
||||
make sure only the modified parts of the file is stored.
|
||||
Yes, the :ref:`deduplication <deduplication_def>` technique used by |project_name|
|
||||
makes sure only the modified parts of the file are stored.
|
||||
|
||||
Which file attributes are preserved?
|
||||
------------------------------------
|
||||
|
|
|
@ -12,39 +12,39 @@ Initialize a local :ref:`repository <repository_def>` to store backup
|
|||
:ref:`archives <archive_def>` in (See :ref:`encrypted_repos` and
|
||||
:ref:`remote_repos` for more details)::
|
||||
|
||||
$ darc init /somewhere/my-backup.darc
|
||||
$ attic init /somewhere/my-backup.attic
|
||||
|
||||
Create an archive containing the ``~/src`` and ``~/Documents`` directories::
|
||||
|
||||
$ darc create -v /somwhere/my-backup.darc::first-backup ~/src ~/Documents
|
||||
$ attic create -v /somwhere/my-backup.attic::first-backup ~/src ~/Documents
|
||||
|
||||
Create another archive the next day. This backup will be a lot quicker since
|
||||
only new data is stored. The ``--stats`` option tells |project_name| to print
|
||||
statistics about the newly created archive such as the amount of unique data
|
||||
(not shared with other archives)::
|
||||
|
||||
$ darc create -v --stats /somwhere/my-backup.darc::second-backup ~/src ~/Documents
|
||||
$ attic create -v --stats /somwhere/my-backup.attic::second-backup ~/src ~/Documents
|
||||
|
||||
List all archives in the repository::
|
||||
|
||||
$ darc list /somewhere/my-backup.darc
|
||||
$ attic list /somewhere/my-backup.attic
|
||||
|
||||
List the files in the *first-backup* archive::
|
||||
|
||||
$ darc list /somewhere/my-backup.darc::first-backup
|
||||
$ attic list /somewhere/my-backup.attic::first-backup
|
||||
|
||||
Restore the *first-backup* archive::
|
||||
|
||||
$ darc extract -v /somwhere/my-backup.darc::first-backup
|
||||
$ attic extract -v /somwhere/my-backup.attic::first-backup
|
||||
|
||||
Recover disk space by manually deleting the *first-backup* archive::
|
||||
|
||||
$ darc delete /somwhere/my-backup.darc::first-backup
|
||||
$ attic delete /somwhere/my-backup.attic::first-backup
|
||||
|
||||
Use the ``prune`` subcommand to delete all archives except a given number of
|
||||
*daily*, *weekly*, *monthly* and *yearly* archives::
|
||||
|
||||
$ darc prune /somwhere/my-backup.darc --daily=7 --weekly=2 --monthly=6
|
||||
$ attic prune /somwhere/my-backup.attic --daily=7 --weekly=2 --monthly=6
|
||||
|
||||
|
||||
.. _encrypted_repos:
|
||||
|
@ -54,7 +54,7 @@ Repository encryption
|
|||
|
||||
Repository encryption is enabled at repository encryption time::
|
||||
|
||||
$ darc init --passphrase | --key-file
|
||||
$ attic init --passphrase | --key-file
|
||||
|
||||
When repository encryption is enabled all data is encrypted using 256-bit AES_
|
||||
encryption and the integrity and authenticity is verified using `HMAC-SHA256`_.
|
||||
|
@ -68,7 +68,7 @@ Passphrase based encryption
|
|||
|
||||
Key file based encryption
|
||||
This method generates random keys at repository initialization time that
|
||||
are stored in a password protected file in the ``~/.darc/keys/`` directory.
|
||||
are stored in a password protected file in the ``~/.attic/keys/`` directory.
|
||||
This method is secure and suitable for automated backups.
|
||||
|
||||
.. Note::
|
||||
|
@ -86,8 +86,8 @@ host is accessible using SSH and |project_name| is installed.
|
|||
|
||||
The following syntax is used to address remote repositories::
|
||||
|
||||
$ darc init user@hostname:repository.darc
|
||||
$ attic init user@hostname:repository.attic
|
||||
|
||||
or::
|
||||
|
||||
$ darc init ssh://user@hostname:port/repository.darc
|
||||
$ attic init ssh://user@hostname:port/repository.attic
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
.. |project_name| replace:: ``darc``
|
||||
.. |project_name| replace:: ``Attic``
|
||||
.. |project_version| replace:: 0.6
|
||||
.. |package_dirname| replace:: darc-|project_version|
|
||||
.. |package_dirname| replace:: Attic-|project_version|
|
||||
.. |package_filename| replace:: |package_dirname|.tar.gz
|
||||
.. |package_url| replace:: https://pypi.python.org/packages/source/d/darc/darc-0.6.tar.gz
|
||||
|
||||
.. |package_url| replace:: https://pypi.python.org/packages/source/A/Attic/Attic-0.6.tar.gz
|
||||
.. |git_url| replace:: https://github.com/jborg/attic.git
|
||||
.. _deduplication: https://en.wikipedia.org/wiki/Data_deduplication
|
||||
.. _AES: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||
.. _HMAC-SHA256: http://en.wikipedia.org/wiki/HMAC
|
||||
.. _PBKDF2: https://en.wikipedia.org/wiki/PBKDF2
|
||||
.. _ACL: https://en.wikipedia.org/wiki/Access_control_list
|
||||
.. _github: https://github.com/jborg/darc
|
||||
.. _github: https://github.com/jborg/attic
|
||||
.. _OpenSSL: https://www.openssl.org/
|
||||
.. _Python: http://www.python.org/
|
||||
.. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/
|
||||
.. _homebrew: http://mxcl.github.io/homebrew/
|
||||
.. _issue tracker: https://github.com/jborg/darc/issues
|
||||
.. _issue tracker: https://github.com/jborg/attic/issues
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
.. include:: global.rst.inc
|
||||
|
||||
Darc
|
||||
====
|
||||
|project_name| is a Deduplicating ARChiver written in Python.
|
||||
Attic
|
||||
=====
|
||||
|project_name| is a deduplicating backup program written in Python.
|
||||
The main goal of |project_name| is to provide an efficient and secure way
|
||||
to backup data. The data deduplication technique used makes |project_name|
|
||||
suitable for daily backups since only actual changes are stored.
|
||||
|
@ -28,8 +28,8 @@ Easy to use
|
|||
Initialize a new backup :ref:`repository <repository_def>` and create your
|
||||
first backup :ref:`archive <archive_def>` in two lines::
|
||||
|
||||
$ darc init /usbdrive/my-backup.darc
|
||||
$ darc create -v /usbdrive/my-backup.darc::documents ~/Documents
|
||||
$ attic init /usbdrive/my-backup.attic
|
||||
$ attic create -v /usbdrive/my-backup.attic::documents ~/Documents
|
||||
|
||||
See the :ref:`generalusage` section for more detailed examples.
|
||||
|
||||
|
@ -37,7 +37,7 @@ Easy installation
|
|||
-----------------
|
||||
You can use pip to install |project_name| quickly and easily::
|
||||
|
||||
$ pip install darc
|
||||
$ pip install attic
|
||||
|
||||
Need more help with installing? See :ref:`installation`
|
||||
|
||||
|
|
|
@ -20,21 +20,21 @@ Installing from PyPI using pip
|
|||
------------------------------
|
||||
::
|
||||
|
||||
$ pip install darc
|
||||
$ pip install Attic
|
||||
|
||||
Installing from source tarballs
|
||||
-------------------------------
|
||||
.. parsed-literal::
|
||||
|
||||
$ curl -O |package_url|
|
||||
$ tar -xvzf darc-|package_filename|
|
||||
$ tar -xvzf |package_filename|
|
||||
$ cd |package_dirname|
|
||||
$ python setup.py install
|
||||
|
||||
Installing from git
|
||||
-------------------
|
||||
::
|
||||
.. parsed-literal::
|
||||
|
||||
$ git clone https://github.com/jborg/darc.git
|
||||
$ cd darc
|
||||
$ git clone |git_url|
|
||||
$ cd attic
|
||||
$ python setup.py install
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/bash
|
||||
echo -n > usage.rst
|
||||
for cmd in init create extract delete prune verify change-passphrase; do
|
||||
LINE=`echo -n darc $cmd | tr 'a-z ' '~'`
|
||||
echo -e ".. _usage_darc_$cmd:\n\ndarc $cmd\n$LINE\n::\n" >> usage.rst
|
||||
darc $cmd -h >> usage.rst
|
||||
LINE=`echo -n attic $cmd | tr 'a-z ' '~'`
|
||||
echo -e ".. _usage_attic_$cmd:\n\nattic $cmd\n$LINE\n::\n" >> usage.rst
|
||||
attic $cmd -h >> usage.rst
|
||||
done
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
.. _usage_darc_init:
|
||||
.. _usage_attic_init:
|
||||
|
||||
darc init
|
||||
~~~~~~~~~
|
||||
attic init
|
||||
~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc init [-h] [-v] [--key-file] [--passphrase] repository
|
||||
usage: attic init [-h] [-v] [--key-file] [--passphrase] repository
|
||||
|
||||
Initialize a new repository
|
||||
|
||||
|
@ -16,15 +16,15 @@ optional arguments:
|
|||
-v, --verbose verbose output
|
||||
--key-file enable key file based encryption
|
||||
--passphrase enable passphrase based encryption
|
||||
.. _usage_darc_create:
|
||||
.. _usage_attic_create:
|
||||
|
||||
darc create
|
||||
~~~~~~~~~~~
|
||||
attic create
|
||||
~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc create [-h] [-v] [-s] [-e PATTERN] [-c SECONDS]
|
||||
[--do-not-cross-mountpoints] [--numeric-owner]
|
||||
ARCHIVE PATH [PATH ...]
|
||||
usage: attic create [-h] [-v] [-s] [-e PATTERN] [-c SECONDS]
|
||||
[--do-not-cross-mountpoints] [--numeric-owner]
|
||||
ARCHIVE PATH [PATH ...]
|
||||
|
||||
Create new archive
|
||||
|
||||
|
@ -43,14 +43,14 @@ optional arguments:
|
|||
--do-not-cross-mountpoints
|
||||
do not cross mount points
|
||||
--numeric-owner only store numeric user and group identifiers
|
||||
.. _usage_darc_extract:
|
||||
.. _usage_attic_extract:
|
||||
|
||||
darc extract
|
||||
~~~~~~~~~~~~
|
||||
attic extract
|
||||
~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc extract [-h] [-v] [-e PATTERN] [--numeric-owner]
|
||||
ARCHIVE [PATH [PATH ...]]
|
||||
usage: attic extract [-h] [-v] [-e PATTERN] [--numeric-owner]
|
||||
ARCHIVE [PATH [PATH ...]]
|
||||
|
||||
Extract archive contents
|
||||
|
||||
|
@ -64,13 +64,13 @@ optional arguments:
|
|||
-e PATTERN, --exclude PATTERN
|
||||
exclude paths matching PATTERN
|
||||
--numeric-owner only obey numeric user and group identifiers
|
||||
.. _usage_darc_delete:
|
||||
.. _usage_attic_delete:
|
||||
|
||||
darc delete
|
||||
~~~~~~~~~~~
|
||||
attic delete
|
||||
~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc delete [-h] [-v] ARCHIVE
|
||||
usage: attic delete [-h] [-v] ARCHIVE
|
||||
|
||||
Delete archive
|
||||
|
||||
|
@ -80,15 +80,15 @@ positional arguments:
|
|||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-v, --verbose verbose output
|
||||
.. _usage_darc_prune:
|
||||
.. _usage_attic_prune:
|
||||
|
||||
darc prune
|
||||
~~~~~~~~~~
|
||||
attic prune
|
||||
~~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc prune [-h] [-v] [-H HOURLY] [-d DAILY] [-w WEEKLY] [-m MONTHLY]
|
||||
[-y YEARLY] [-p PREFIX]
|
||||
REPOSITORY
|
||||
usage: attic prune [-h] [-v] [-H HOURLY] [-d DAILY] [-w WEEKLY] [-m MONTHLY]
|
||||
[-y YEARLY] [-p PREFIX]
|
||||
REPOSITORY
|
||||
|
||||
Prune repository archives according to specified rules
|
||||
|
||||
|
@ -110,13 +110,13 @@ optional arguments:
|
|||
number of yearly archives to keep
|
||||
-p PREFIX, --prefix PREFIX
|
||||
only consider archive names starting with this prefix
|
||||
.. _usage_darc_verify:
|
||||
.. _usage_attic_verify:
|
||||
|
||||
darc verify
|
||||
~~~~~~~~~~~
|
||||
attic verify
|
||||
~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc verify [-h] [-v] [-e PATTERN] ARCHIVE [PATH [PATH ...]]
|
||||
usage: attic verify [-h] [-v] [-e PATTERN] ARCHIVE [PATH [PATH ...]]
|
||||
|
||||
Verify archive consistency
|
||||
|
||||
|
@ -129,13 +129,13 @@ optional arguments:
|
|||
-v, --verbose verbose output
|
||||
-e PATTERN, --exclude PATTERN
|
||||
exclude paths matching PATTERN
|
||||
.. _usage_darc_change-passphrase:
|
||||
.. _usage_attic_change-passphrase:
|
||||
|
||||
darc change-passphrase
|
||||
~~~~~~~~~~~-~~~~~~~~~~
|
||||
attic change-passphrase
|
||||
~~~~~~~~~~~~-~~~~~~~~~~
|
||||
::
|
||||
|
||||
usage: darc change-passphrase [-h] [-v] repository
|
||||
usage: attic change-passphrase [-h] [-v] repository
|
||||
|
||||
Change passphrase on repository key file
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
from attic.archiver import main
|
||||
main()
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from darc.archiver import main
|
||||
main()
|
||||
|
32
setup.py
32
setup.py
|
@ -2,11 +2,11 @@
|
|||
import os
|
||||
import sys
|
||||
from glob import glob
|
||||
import darc
|
||||
import attic
|
||||
|
||||
min_python = (3, 2)
|
||||
if sys.version_info < min_python:
|
||||
print("Darc requires Python %d.%d or later" % min_python)
|
||||
print("Attic requires Python %d.%d or later" % min_python)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
|
@ -15,8 +15,8 @@ except ImportError:
|
|||
from distutils.core import setup, Extension
|
||||
from distutils.command.sdist import sdist
|
||||
|
||||
chunker_source = 'darc/chunker.pyx'
|
||||
hashindex_source = 'darc/hashindex.pyx'
|
||||
chunker_source = 'attic/chunker.pyx'
|
||||
hashindex_source = 'attic/hashindex.pyx'
|
||||
|
||||
try:
|
||||
from Cython.Distutils import build_ext
|
||||
|
@ -24,13 +24,13 @@ try:
|
|||
|
||||
class Sdist(sdist):
|
||||
def __init__(self, *args, **kwargs):
|
||||
for src in glob('darc/*.pyx'):
|
||||
cython_compiler.compile(glob('darc/*.pyx'),
|
||||
for src in glob('attic/*.pyx'):
|
||||
cython_compiler.compile(glob('attic/*.pyx'),
|
||||
cython_compiler.default_options)
|
||||
sdist.__init__(self, *args, **kwargs)
|
||||
|
||||
def make_distribution(self):
|
||||
self.filelist.extend(['darc/chunker.c', 'darc/_chunker.c', 'darc/hashindex.c', 'darc/_hashindex.c'])
|
||||
self.filelist.extend(['attic/chunker.c', 'attic/_chunker.c', 'attic/hashindex.c', 'attic/_hashindex.c'])
|
||||
super(Sdist, self).make_distribution()
|
||||
|
||||
except ImportError:
|
||||
|
@ -42,18 +42,18 @@ except ImportError:
|
|||
hashindex_source = hashindex_source.replace('.pyx', '.c')
|
||||
from distutils.command.build_ext import build_ext
|
||||
if not os.path.exists(chunker_source) or not os.path.exists(hashindex_source):
|
||||
raise ImportError('The GIT version of darc needs Cython. Install Cython or use a released version')
|
||||
raise ImportError('The GIT version of attic needs Cython. Install Cython or use a released version')
|
||||
|
||||
with open('README.rst', 'r') as fd:
|
||||
long_description = fd.read()
|
||||
|
||||
setup(
|
||||
name='darc',
|
||||
version=darc.__release__,
|
||||
name='Attic',
|
||||
version=attic.__release__,
|
||||
author='Jonas Borgström',
|
||||
author_email='jonas@borgstrom.se',
|
||||
url='http://github.com/jborg/darc/',
|
||||
description='Deduplicating ARChiver written in Python',
|
||||
url='http://github.com/jborg/attic/',
|
||||
description='Deduplicated backups',
|
||||
long_description=long_description,
|
||||
license='BSD',
|
||||
platforms=['Linux', 'MacOS X'],
|
||||
|
@ -68,12 +68,12 @@ setup(
|
|||
'Topic :: Security :: Cryptography',
|
||||
'Topic :: System :: Archiving :: Backup',
|
||||
],
|
||||
packages=['darc', 'darc.testsuite'],
|
||||
scripts=['scripts/darc'],
|
||||
packages=['attic', 'attic.testsuite'],
|
||||
scripts=['scripts/attic'],
|
||||
cmdclass={'build_ext': build_ext, 'sdist': Sdist},
|
||||
ext_modules=[
|
||||
Extension('darc.chunker', [chunker_source]),
|
||||
Extension('darc.hashindex', [hashindex_source])
|
||||
Extension('attic.chunker', [chunker_source]),
|
||||
Extension('attic.hashindex', [hashindex_source])
|
||||
],
|
||||
install_requires=['msgpack-python']
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue