ソースを参照

Merge pull request #613 from ThomasWaldmann/xdg-base-dir-keys

use xdg base dir for keys
TW 9 年 前
コミット
5d93b6cda9
7 ファイル変更88 行追加16 行削除
  1. 7 2
      borg/archiver.py
  2. 2 2
      borg/helpers.py
  3. 20 2
      borg/testsuite/helpers.py
  4. 50 1
      borg/upgrader.py
  5. 1 1
      docs/internals.rst
  6. 1 1
      docs/quickstart.rst
  7. 7 7
      docs/usage.rst

+ 7 - 2
borg/archiver.py

@@ -23,7 +23,7 @@ from .helpers import Error, location_validator, format_time, format_file_size, \
 from .logger import create_logger, setup_logging
 logger = create_logger()
 from .compress import Compressor, COMPR_BUFFER
-from .upgrader import AtticRepositoryUpgrader
+from .upgrader import AtticRepositoryUpgrader, BorgRepositoryUpgrader
 from .repository import Repository
 from .cache import Cache
 from .key import key_creator, RepoKey, PassphraseKey
@@ -557,6 +557,11 @@ class Archiver:
 
         # XXX: should auto-detect if it is an attic repository here
         repo = AtticRepositoryUpgrader(args.location.path, create=False)
+        try:
+            repo.upgrade(args.dry_run, inplace=args.inplace, progress=args.progress)
+        except NotImplementedError as e:
+            print("warning: %s" % e)
+        repo = BorgRepositoryUpgrader(args.location.path, create=False)
         try:
             repo.upgrade(args.dry_run, inplace=args.inplace, progress=args.progress)
         except NotImplementedError as e:
@@ -1214,7 +1219,7 @@ class Archiver:
         It will change the magic strings in the repository's segments
         to match the new Borg magic strings. The keyfiles found in
         $ATTIC_KEYS_DIR or ~/.attic/keys/ will also be converted and
-        copied to $BORG_KEYS_DIR or ~/.borg/keys.
+        copied to $BORG_KEYS_DIR or ~/.config/borg/keys.
 
         The cache files are converted, from $ATTIC_CACHE_DIR or
         ~/.cache/attic to $BORG_CACHE_DIR or ~/.cache/borg, but the

+ 2 - 2
borg/helpers.py

@@ -209,8 +209,8 @@ class Statistics:
 
 def get_keys_dir():
     """Determine where to repository keys and cache"""
-    return os.environ.get('BORG_KEYS_DIR',
-                          os.path.join(os.path.expanduser('~'), '.borg', 'keys'))
+    xdg_config = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
+    return os.environ.get('BORG_KEYS_DIR', os.path.join(xdg_config, 'borg', 'keys'))
 
 
 def get_cache_dir():

+ 20 - 2
borg/testsuite/helpers.py

@@ -10,7 +10,8 @@ import msgpack
 import msgpack.fallback
 
 from ..helpers import Location, format_file_size, format_timedelta, make_path_safe, \
-    prune_within, prune_split, get_cache_dir, Statistics, is_slow_msgpack, yes, TRUISH, FALSISH, DEFAULTISH, \
+    prune_within, prune_split, get_cache_dir, get_keys_dir, Statistics, is_slow_msgpack, \
+    yes, TRUISH, FALSISH, DEFAULTISH, \
     StableDict, int_to_bigint, bigint_to_int, parse_timestamp, CompressionSpec, ChunkerParams, \
     ProgressIndicatorPercent, ProgressIndicatorEndless, load_excludes, parse_pattern, \
     PatternMatcher, RegexPattern, PathPrefixPattern, FnmatchPattern, ShellPattern
@@ -579,7 +580,7 @@ class TestParseTimestamp(BaseTestCase):
 
 
 def test_get_cache_dir():
-    """test that get_cache_dir respects environement"""
+    """test that get_cache_dir respects environment"""
     # reset BORG_CACHE_DIR in order to test default
     old_env = None
     if os.environ.get('BORG_CACHE_DIR'):
@@ -595,6 +596,23 @@ def test_get_cache_dir():
         os.environ['BORG_CACHE_DIR'] = old_env
 
 
+def test_get_keys_dir():
+    """test that get_keys_dir respects environment"""
+    # reset BORG_KEYS_DIR in order to test default
+    old_env = None
+    if os.environ.get('BORG_KEYS_DIR'):
+        old_env = os.environ['BORG_KEYS_DIR']
+        del(os.environ['BORG_KEYS_DIR'])
+    assert get_keys_dir() == os.path.join(os.path.expanduser('~'), '.config', 'borg', 'keys')
+    os.environ['XDG_CONFIG_HOME'] = '/var/tmp/.config'
+    assert get_keys_dir() == os.path.join('/var/tmp/.config', 'borg', 'keys')
+    os.environ['BORG_KEYS_DIR'] = '/var/tmp'
+    assert get_keys_dir() == '/var/tmp'
+    # reset old env
+    if old_env is not None:
+        os.environ['BORG_KEYS_DIR'] = old_env
+
+
 @pytest.fixture()
 def stats():
     stats = Statistics()

+ 50 - 1
borg/upgrader.py

@@ -136,7 +136,7 @@ class AtticRepositoryUpgrader(Repository):
         replacement pattern is `s/ATTIC KEY/BORG_KEY/` in
         `get_keys_dir()`, that is `$ATTIC_KEYS_DIR` or
         `$HOME/.attic/keys`, and moved to `$BORG_KEYS_DIR` or
-        `$HOME/.borg/keys`.
+        `$HOME/.config/borg/keys`.
 
         no need to decrypt to convert. we need to rewrite the whole
         key file because magic string length changed, but that's not a
@@ -275,3 +275,52 @@ class AtticKeyfileKey(KeyfileKey):
                 if line and line.startswith(cls.FILE_ID) and line[10:] == id:
                     return filename
         raise KeyfileNotFoundError(repository.path, keys_dir)
+
+
+class BorgRepositoryUpgrader(Repository):
+    def upgrade(self, dryrun=True, inplace=False, progress=False):
+        """convert an old borg repository to a current borg repository
+        """
+        logger.info("converting borg 0.xx to borg current")
+        try:
+            keyfile = self.find_borg0xx_keyfile()
+        except KeyfileNotFoundError:
+            logger.warning("no key file found for repository")
+        else:
+            self.move_keyfiles(keyfile, dryrun)
+
+    def find_borg0xx_keyfile(self):
+        return Borg0xxKeyfileKey.find_key_file(self)
+
+    def move_keyfiles(self, keyfile, dryrun):
+        filename = os.path.basename(keyfile)
+        new_keyfile = os.path.join(get_keys_dir(), filename)
+        try:
+            os.rename(keyfile, new_keyfile)
+        except FileExistsError:
+            # likely the attic -> borg upgrader already put it in the final location
+            pass
+
+
+class Borg0xxKeyfileKey(KeyfileKey):
+    """backwards compatible borg 0.xx key file parser"""
+
+    @staticmethod
+    def get_keys_dir():
+        return os.environ.get('BORG_KEYS_DIR',
+                              os.path.join(os.path.expanduser('~'), '.borg', 'keys'))
+
+    @classmethod
+    def find_key_file(cls, repository):
+        get_keys_dir = cls.get_keys_dir
+        id = hexlify(repository.id).decode('ascii')
+        keys_dir = get_keys_dir()
+        if not os.path.exists(keys_dir):
+            raise KeyfileNotFoundError(repository.path, keys_dir)
+        for name in os.listdir(keys_dir):
+            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[len(cls.FILE_ID)+1:] == id:
+                    return filename
+        raise KeyfileNotFoundError(repository.path, keys_dir)

+ 1 - 1
docs/internals.rst

@@ -358,7 +358,7 @@ Key files
 ---------
 
 When initialized with the ``init -e keyfile`` command, |project_name|
-needs an associated file in ``$HOME/.borg/keys`` to read and write
+needs an associated file in ``$HOME/.config/borg/keys`` to read and write
 the repository. The format is based on msgpack_, base64 encoding and
 PBKDF2_ SHA256 hashing, which is then encoded again in a msgpack_.
 

+ 1 - 1
docs/quickstart.rst

@@ -167,7 +167,7 @@ is being made.
     protection. The repository server never sees the plaintext key.
 
 ``keyfile`` mode
-    The key is stored on your local disk (in ``~/.borg/keys/``).
+    The key is stored on your local disk (in ``~/.config/borg/keys/``).
     Use this mode if you want "passphrase and having-the-key" security.
 
 In both modes, the key is stored in encrypted form and can be only decrypted

+ 7 - 7
docs/usage.rst

@@ -85,7 +85,7 @@ Some automatic "answerers" (if set, they automatically answer confirmation quest
 
 Directories:
     BORG_KEYS_DIR
-        Default to '~/.borg/keys'. This directory contains keys for encrypted repositories.
+        Default to '~/.config/borg/keys'. This directory contains keys for encrypted repositories.
     BORG_CACHE_DIR
         Default to '~/.cache/borg'. This directory contains the local cache and might need a lot
         of space for dealing with big repositories).
@@ -203,9 +203,9 @@ be stored inside the repository (in its "config" file). In above mentioned
 attack scenario, the attacker will have the key (but not the passphrase).
 
 If you want "passphrase and having-the-key" security, use the ``keyfile`` mode.
-The key will be stored in your home directory (in ``.borg/keys``). In the attack
-scenario, the attacker who has just access to your repo won't have the key (and
-also not the passphrase).
+The key will be stored in your home directory (in ``.config/borg/keys``). In
+the attack scenario, the attacker who has just access to your repo won't have
+the key (and also not the passphrase).
 
 Make a backup copy of the key file (``keyfile`` mode) or repo config file
 (``repokey`` mode) and keep it at a safe place, so you still have the key in
@@ -411,15 +411,15 @@ Examples
     Initializing repository at "/mnt/backup"
     Enter passphrase (empty for no passphrase):
     Enter same passphrase again: 
-    Key file "/home/USER/.borg/keys/mnt_backup" created.
+    Key file "/home/USER/.config/borg/keys/mnt_backup" created.
     Keep this file safe. Your data will be inaccessible without it.
 
     # Change key file passphrase
     $ borg change-passphrase /mnt/backup
-    Enter passphrase for key file /home/USER/.borg/keys/mnt_backup:
+    Enter passphrase for key file /home/USER/.config/borg/keys/mnt_backup:
     New passphrase: 
     Enter same passphrase again: 
-    Key file "/home/USER/.borg/keys/mnt_backup" updated
+    Key file "/home/USER/.config/borg/keys/mnt_backup" updated
 
 
 .. include:: usage/serve.rst.inc