Project rename

This commit is contained in:
Jonas Borgström 2013-07-08 23:38:27 +02:00
parent a5a73ca926
commit b718a443a8
41 changed files with 227 additions and 236 deletions

View File

@ -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

View File

@ -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>`_.

View File

@ -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

View File

@ -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])

View File

@ -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'))

View File

@ -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):

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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():

View File

@ -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):

View File

@ -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'

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -1,5 +1,5 @@
import unittest
from darc.testsuite import TestLoader
from attic.testsuite import TestLoader
def main():

View File

@ -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()

View File

@ -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:

View File

@ -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)
]

View File

@ -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?
------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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

View File

@ -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

4
scripts/attic Normal file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env python
from attic.archiver import main
main()

View File

@ -1,4 +0,0 @@
#!/usr/bin/env python
from darc.archiver import main
main()

View File

@ -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']
)

View File

@ -4,4 +4,4 @@ envlist = py32, py33
[testenv]
# Change dir to avoid import problem
changedir = docs
commands = {envpython} -m darc.testsuite.run -bv []
commands = {envpython} -m attic.testsuite.run -bv []