Repository: borgbackup/borg

Issue:
borg 1.1.0 does not work - Repository encryption method changed
i`m trying to upgrade to borg 1.1.0 and while testing i`m getting this one:

old version - working good for months:
```
[root@backupvm2 borg-repos]# /backup/bin/borg-1.0.11 list --debug  /iscsi/lun1/borg-repos/adminstation
using builtin fallback logging configuration
TAM-verified manifest
archive-2017-02-28_1052              Tue, 2017-02-28 12:41:22
...
archive-2017-10-13_1524              Fri, 2017-10-13 15:23:40
```

1.10 not even listing repo contents:
```
[root@backupvm2 borg-repos]# /backup/bin/borg-1.1.0 list --debug  /iscsi/lun1/borg-repos/adminstation
using builtin fallback logging configuration
35 self tests completed in 0.14 seconds
TAM-verified manifest
Cache integrity: No integrity data found (files, chunks). Cache is from old version.
security: read previous location ''
security: using previous_location of cache: '/iscsi/lun1/borg-repos/adminstation'
Repository encryption method changed since last access, refusing to continue
Traceback (most recent call last):
  File "borg/archiver.py", line 4024, in main
  File "borg/archiver.py", line 3952, in run
  File "borg/archiver.py", line 141, in wrapper
  File "borg/cache.py", line 199, in assert_secure
  File "borg/cache.py", line 165, in assert_secure
  File "borg/cache.py", line 172, in _assert_secure
  File "borg/cache.py", line 153, in assert_key_type
borg.cache.Cache.EncryptionMethodMismatch: Repository encryption method changed since last access, refusing to continue

Platform: Linux backupvm2 3.10.0-514.26.2.el7.x86_64 #1 SMP Tue Jul 4 15:04:05 UTC 2017 x86_64 x86_64
Linux: CentOS Linux 7.3.1611 Core
Borg: 1.1.0  Python: CPython 3.5.4
PID: 29559  CWD: /iscsi/lun1/borg-repos
sys.argv: ['/backup/bin/borg-1.1.0', 'list', '--debug', '/iscsi/lun1/borg-repos/adminstation']
SSH_ORIGINAL_COMMAND: None
```

Same problem with "borg info"

Environment:
```
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
export BORG_CACHE_DIR=/backup/borg-cache
```


Discussion:
getting closer:
```
[root@backupvm2 security]# mv /root/.config/borg/security/402db26002abd9df0fa4def5e94e61645106dff354eeac48434934b84616866c /root/.config/borg/security/402db26002abd9df0fa4def5e94e61645106dff354eeac48434934b84616866c.old

[root@backupvm2 security]# ls -la /root/.config/borg/security/402db26002abd9df0fa4def5e94e61645106dff354eeac48434934b84616866c.old
insgesamt 16
drwx------   2 root root    61 13. Okt 15:01 .
drwx------ 109 root root 12288 13. Okt 16:22 ..
-rw-------   1 root root     0 13. Okt 15:01 key-type
-rw-------   1 root root     0 13. Okt 15:01 location
-rw-------   1 root root     0 13. Okt 15:01 manifest-timestamp


[root@backupvm2 security]# /backup/bin/borg-1.1.0 list /iscsi/lun1/borg-repos/adminstation
Warning: Attempting to access a previously unknown unencrypted repository!
Do you want to continue? [yN] y
archive-2017-02-28_1052              Tue, 2017-02-28 12:41:22 [7b858664cb8020f09cbe4166dad7bdf044158964d965b0be236f32d6ed4b31f4]
...
archive-2017-10-13_1524              Fri, 2017-10-13 15:23:40 [9537573ee39bf652d41cb17028399e8e704b4551496e9bb013974a4cbd808f87]
```
note: shortened the archive list in above posts, not important for the issue.

@devZer0 can you reproduce the issue with a fresh repo and post the commands / borg versions needed for that?
The line marked with XXX raised the exception:
```
    def assert_key_type(self, key, cache_config=None):
        # Make sure an encrypted repository has not been swapped for an unencrypted repository
        if cache_config and cache_config.key_type is not None and cache_config.key_type != str(key.TYPE):
            raise Cache.EncryptionMethodMismatch()
        if self.known() and not self.key_matches(key):
            raise Cache.EncryptionMethodMismatch()   # XXX
```

known() -> we have a key-type file

key_matches() -> the key type in the key-type file matches the currently used key type

So, the culprit is the `key-type` file with 0 length.

Would be interesting if you could reproduce that.
As all of these files are 0 bytes:
- did you run out of disk space?
- did your system crash while running borg?
- is your filesystem ok (fsck)?

TODO: use `platform.SaveFile` for these files.

Resolving patch:
diff --git a/src/borg/cache.py b/src/borg/cache.py
--- a/src/borg/cache.py
+++ b/src/borg/cache.py
@@ -85,11 +85,11 @@ def save(self, manifest, key):
         logger.debug('security: current location   %s', current_location)
         logger.debug('security: key type           %s', str(key.TYPE))
         logger.debug('security: manifest timestamp %s', manifest.timestamp)
-        with open(self.location_file, 'w') as fd:
+        with SaveFile(self.location_file) as fd:
             fd.write(current_location)
-        with open(self.key_type_file, 'w') as fd:
+        with SaveFile(self.key_type_file) as fd:
             fd.write(str(key.TYPE))
-        with open(self.manifest_ts_file, 'w') as fd:
+        with SaveFile(self.manifest_ts_file) as fd:
             fd.write(manifest.timestamp)
 
     def assert_location_matches(self, cache_config=None):
@@ -119,7 +119,7 @@ def assert_location_matches(self, cache_config=None):
                 raise Cache.RepositoryAccessAborted()
             # adapt on-disk config immediately if the new location was accepted
             logger.debug('security: updating location stored in cache and security dir')
-            with open(self.location_file, 'w') as fd:
+            with SaveFile(self.location_file) as fd:
                 fd.write(repository_location)
             if cache_config:
                 cache_config.save()
@@ -470,7 +470,7 @@ def create(self):
         self.cache_config.create()
         ChunkIndex().write(os.path.join(self.path, 'chunks'))
         os.makedirs(os.path.join(self.path, 'chunks.archive.d'))
-        with SaveFile(os.path.join(self.path, 'files'), binary=True) as fd:
+        with SaveFile(os.path.join(self.path, 'files'), binary=True):
             pass  # empty file
 
     def _do_open(self):
@@ -844,7 +844,7 @@ def wipe_cache(self):
             shutil.rmtree(os.path.join(self.path, 'chunks.archive.d'))
             os.makedirs(os.path.join(self.path, 'chunks.archive.d'))
         self.chunks = ChunkIndex()
-        with open(os.path.join(self.path, 'files'), 'wb'):
+        with SaveFile(os.path.join(self.path, 'files'), binary=True):
             pass  # empty file
         self.cache_config.manifest_id = ''
         self.cache_config._config.set('cache', 'manifest', '')


Test patch: