Repository: borgbackup/borg

Issue:
FileNotFoundError: [Errno 2] No such file or directory: '/mnt/sshfs/borg-cache/REPOID/txn.active/files'
## Is this a BUG / ISSUE report or a QUESTION?

Bug

## System information. For client/server mode post info for both machines.

#### Your borg version (borg -V).

borg 1.1.17

#### Operating system (distribution) and version.

Debian 11


## Describe the problem you're observing.

I was doing a backup and the storage (mounted using sshfs and keeping both the repo and the borg cache) was unresponsive, so I had to restart the server.
After the reboot I restarted a borg prune but I am getting this error:
```
borgbackup version 1.1.17
Killed stale lock borg.server@345051709351.4131-0.
Removed stale exclusive roster lock for host borg.server@345051709351 pid 4131 thread 0.
Removed stale exclusive roster lock for host borg.server@345051709351 pid 4131 thread 0.
Local Exception
Traceback (most recent call last):
  File "borg/archiver.py", line 4703, in main
  File "borg/archiver.py", line 4635, in run
  File "borg/archiver.py", line 177, in wrapper
  File "borg/archiver.py", line 1652, in do_prune
  File "borg/cache.py", line 387, in __new__
  File "borg/cache.py", line 381, in local
  File "borg/cache.py", line 464, in __init__
  File "borg/cache.py", line 512, in open
  File "borg/cache.py", line 624, in rollback
  File "shutil.py", line 248, in copy
  File "shutil.py", line 120, in copyfile
FileNotFoundError: [Errno 2] No such file or directory: '/mnt/sshfs/borg-cache/a0a213e2d7c13a3d80933520b7298d9ddf27ac25c0fbbd9c55c62acce94f8811/txn.active/files'

Platform: Linux borg 5.10.0-11-amd64 #1 SMP Debian 5.10.92-1 (2022-01-18) x86_64
Linux: debian 11.2
Borg: 1.1.17  Python: CPython 3.7.11 msgpack: 0.5.6.+borg1
PID: 572  CWD: /root
sys.argv: ['borg', 'prune', '-v', '--stats', '--show-rc', '--show-version', '--list', '--keep-within=188d']
SSH_ORIGINAL_COMMAND: None

terminating with error status, rc 2
```

Since I had a files.STORAGE inside the txn.active directory I did a:
```
ln -s files.STORAGE files
```
and that was enough to make borg prune run again.

So borg is expecting the "files" file inside the txn.active directory even when using BORG_FILES_CACHE_SUFFIX.

A similar issue happens with borg create.

The problem is that borg should not expect to have the "files" file when not needed, i.e. https://github.com/borgbackup/borg/pull/5777 should probably also be applied to 1.1 branch.

Discussion:

Patch:
diff --git a/docs/usage/init.rst.inc b/docs/usage/init.rst.inc
index bfcfc40e..5bf1cb64 100644
--- a/docs/usage/init.rst.inc
+++ b/docs/usage/init.rst.inc
@@ -134,27 +134,27 @@ in the upper part of the table, in the lower part is the old and/or unsafe(r) st
 
 .. nanorst: inline-fill
 
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| Mode (K = keyfile or repokey)     | ID-Hash      | Encryption     | Authentication     | V >=    |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2-chacha20-poly1305        | BLAKE2b      | CHACHA20       | POLY1305           | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-chacha20-poly1305               | HMAC-SHA-256 | CHACHA20       | POLY1305           | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2-aes-ocb                  | BLAKE2b      | AES256-OCB     | AES256-OCB         | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-aes-ocb                         | HMAC-SHA-256 | AES256-OCB     | AES256-OCB         | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2                          | BLAKE2b      | AES256-CTR     | BLAKE2b            | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K                                 | HMAC-SHA-256 | AES256-CTR     | HMAC-SHA256        | any     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| authenticated-blake2              | BLAKE2b      | none           | BLAKE2b            | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| authenticated                     | HMAC-SHA-256 | none           | HMAC-SHA256        | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| none                              | SHA-256      | none           | none               | any     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
++---------------------------------+---------------+---------------+------------------+-------+
+|**mode (* = keyfile or repokey)**|**ID-Hash**    |**Encryption** |**Authentication**|**V>=**|
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2-chacha20-poly1305``  | BLAKE2b       | CHACHA20      | POLY1305         | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-chacha20-poly1305``         | HMAC-SHA-256  | CHACHA20      | POLY1305         | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2-aes-ocb``            | BLAKE2b       | AES256-OCB    | AES256-OCB       | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-aes-ocb``                   | HMAC-SHA-256  | AES256-OCB    | AES256-OCB       | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2``                    | BLAKE2b       | AES256-CTR    | BLAKE2b          | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*``                           | HMAC-SHA-256  | AES256-CTR    | HMAC-SHA256      | any   |
++---------------------------------+---------------+---------------+------------------+-------+
+| authenticated-blake2            | BLAKE2b       | none          | BLAKE2b          | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| authenticated                   | HMAC-SHA-256  | none          | HMAC-SHA256      | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| none                            | SHA-256       | none          | none             | any   |
++---------------------------------+---------------+---------------+------------------+-------+
 
 .. nanorst: inline-replace
 
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 83f7b759..bf7f9d70 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -632,9 +632,6 @@ def chunkit(chunker_name, *args, **kwargs):
             'lz4',
             'zstd,1',
             'zstd,3',
-            'zstd,5',
-            'zstd,10',
-            'zstd,16',
             'zstd,22',
             'zlib,0',
             'zlib,6',
@@ -4303,27 +4300,27 @@ def define_borg_mount(parser):
 
         .. nanorst: inline-fill
 
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | Mode (K = keyfile or repokey)     | ID-Hash      | Encryption     | Authentication     | V >=    |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2-chacha20-poly1305        | BLAKE2b      | CHACHA20       | POLY1305           | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-chacha20-poly1305               | HMAC-SHA-256 | CHACHA20       | POLY1305           | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2-aes-ocb                  | BLAKE2b      | AES256-OCB     | AES256-OCB         | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-aes-ocb                         | HMAC-SHA-256 | AES256-OCB     | AES256-OCB         | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2                          | BLAKE2b      | AES256-CTR     | BLAKE2b            | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K                                 | HMAC-SHA-256 | AES256-CTR     | HMAC-SHA256        | any     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | authenticated-blake2              | BLAKE2b      | none           | BLAKE2b            | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | authenticated                     | HMAC-SHA-256 | none           | HMAC-SHA256        | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | none                              | SHA-256      | none           | none               | any     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
+        +---------------------------------+---------------+---------------+------------------+-------+
+        |**mode (* = keyfile or repokey)**|**ID-Hash**    |**Encryption** |**Authentication**|**V>=**|
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2-chacha20-poly1305``  | BLAKE2b       | CHACHA20      | POLY1305         | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-chacha20-poly1305``         | HMAC-SHA-256  | CHACHA20      | POLY1305         | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2-aes-ocb``            | BLAKE2b       | AES256-OCB    | AES256-OCB       | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-aes-ocb``                   | HMAC-SHA-256  | AES256-OCB    | AES256-OCB       | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2``                    | BLAKE2b       | AES256-CTR    | BLAKE2b          | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*``                           | HMAC-SHA-256  | AES256-CTR    | HMAC-SHA256      | any   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | authenticated-blake2            | BLAKE2b       | none          | BLAKE2b          | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | authenticated                   | HMAC-SHA-256  | none          | HMAC-SHA256      | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | none                            | SHA-256       | none          | none             | any   |
+        +---------------------------------+---------------+---------------+------------------+-------+
 
         .. nanorst: inline-replace
 
diff --git a/src/borg/cache.py b/src/borg/cache.py
index 4e6de052..46848c8d 100644
--- a/src/borg/cache.py
+++ b/src/borg/cache.py
@@ -234,6 +234,10 @@ def files_cache_name():
     return 'files.' + suffix if suffix else 'files'
 
 
+def discover_files_cache_name(path):
+    return [fn for fn in os.listdir(path) if fn == 'files' or fn.startswith('files.')][0]
+
+
 class CacheConfig:
     def __init__(self, repository, path=None, lock_wait=None):
         self.repository = repository
@@ -650,7 +654,7 @@ def rollback(self):
         if os.path.exists(txn_dir):
             shutil.copy(os.path.join(txn_dir, 'config'), self.path)
             shutil.copy(os.path.join(txn_dir, 'chunks'), self.path)
-            shutil.copy(os.path.join(txn_dir, files_cache_name()), self.path)
+            shutil.copy(os.path.join(txn_dir, discover_files_cache_name(txn_dir)), self.path)
             os.rename(txn_dir, os.path.join(self.path, 'txn.tmp'))
             if os.path.exists(os.path.join(self.path, 'txn.tmp')):
                 shutil.rmtree(os.path.join(self.path, 'txn.tmp'))
diff --git a/src/borg/constants.py b/src/borg/constants.py
index 0b2ef16a..208991ee 100644
--- a/src/borg/constants.py
+++ b/src/borg/constants.py
@@ -112,8 +112,8 @@
 KEY_ALGORITHMS = {
     # encrypt-and-MAC, kdf: PBKDF2(HMAC−SHA256), encryption: AES256-CTR, authentication: HMAC-SHA256
     'pbkdf2': 'sha256',
-    # encrypt-then-MAC, kdf: argon2, encryption: chacha20, authentication: poly1305
-    'argon2': 'argon2 chacha20-poly1305',
+    # encrypt-then-MAC, kdf: argon2, encryption: AES256-CTR, authentication: HMAC-SHA256
+    'argon2': 'argon2 aes256-ctr hmac-sha256',
 }
 
 
diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py
index ad4cab9d..879f85c8 100644
--- a/src/borg/crypto/key.py
+++ b/src/borg/crypto/key.py
@@ -445,7 +445,7 @@ def decrypt_key_file(self, data, passphrase):
             self._encrypted_key_algorithm = encrypted_key.algorithm
             if encrypted_key.algorithm == 'sha256':
                 return self.decrypt_key_file_pbkdf2(encrypted_key, passphrase)
-            elif encrypted_key.algorithm == 'argon2 chacha20-poly1305':
+            elif encrypted_key.algorithm == 'argon2 aes256-ctr hmac-sha256':
                 return self.decrypt_key_file_argon2(encrypted_key, passphrase)
             else:
                 raise UnsupportedKeyFormatError()
@@ -497,14 +497,19 @@ def decrypt_key_file_pbkdf2(self, encrypted_key, passphrase):
     def decrypt_key_file_argon2(self, encrypted_key, passphrase):
         key = self.argon2(
             passphrase,
-            output_len_in_bytes=32,
+            output_len_in_bytes=64,
             salt=encrypted_key.salt,
             time_cost=encrypted_key.argon2_time_cost,
             memory_cost=encrypted_key.argon2_memory_cost,
             parallelism=encrypted_key.argon2_parallelism,
             type=encrypted_key.argon2_type,
         )
-        ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+        enc_key, mac_key = key[:32], key[32:]
+        ae_cipher = AES256_CTR_HMAC_SHA256(
+            iv=0, header_len=0, aad_offset=0,
+            enc_key=enc_key,
+            mac_key=mac_key,
+        )
         try:
             return ae_cipher.decrypt(encrypted_key.data)
         except low_level.IntegrityError:
@@ -513,7 +518,7 @@ def decrypt_key_file_argon2(self, encrypted_key, passphrase):
     def encrypt_key_file(self, data, passphrase, algorithm):
         if algorithm == 'sha256':
             return self.encrypt_key_file_pbkdf2(data, passphrase)
-        elif algorithm == 'argon2 chacha20-poly1305':
+        elif algorithm == 'argon2 aes256-ctr hmac-sha256':
             return self.encrypt_key_file_argon2(data, passphrase)
         else:
             raise ValueError(f'Unexpected algorithm: {algorithm}')
@@ -538,14 +543,19 @@ def encrypt_key_file_argon2(self, data, passphrase):
         salt = os.urandom(ARGON2_SALT_BYTES)
         key = self.argon2(
             passphrase,
-            output_len_in_bytes=32,
+            output_len_in_bytes=64,
             salt=salt,
             **ARGON2_ARGS,
         )
-        ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+        enc_key, mac_key = key[:32], key[32:]
+        ae_cipher = AES256_CTR_HMAC_SHA256(
+            iv=0, header_len=0, aad_offset=0,
+            enc_key=enc_key,
+            mac_key=mac_key,
+        )
         encrypted_key = EncryptedKey(
             version=1,
-            algorithm='argon2 chacha20-poly1305',
+            algorithm='argon2 aes256-ctr hmac-sha256',
             salt=salt,
             data=ae_cipher.encrypt(data),
             **{'argon2_' + k: v for k, v in ARGON2_ARGS.items()},


Test patch:
diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py
index 445ebac2..76d615f6 100644
--- a/src/borg/testsuite/archiver.py
+++ b/src/borg/testsuite/archiver.py
@@ -3600,7 +3600,7 @@ def test_init_defaults_to_argon2(self):
         self.cmd('init', '--encryption=repokey', self.repository_location)
         with Repository(self.repository_path) as repository:
             key = msgpack.unpackb(a2b_base64(repository.load_key()))
-        assert key[b'algorithm'] == b'argon2 chacha20-poly1305'
+        assert key[b'algorithm'] == b'argon2 aes256-ctr hmac-sha256'
 
     def test_init_with_explicit_key_algorithm(self):
         """https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401"""
@@ -3620,7 +3620,7 @@ def verify_change_passphrase_does_not_change_algorithm(self, given_algorithm, ex
             assert key[b'algorithm'] == expected_algorithm
 
     def test_change_passphrase_does_not_change_algorithm_argon2(self):
-        self.verify_change_passphrase_does_not_change_algorithm('argon2', b'argon2 chacha20-poly1305')
+        self.verify_change_passphrase_does_not_change_algorithm('argon2', b'argon2 aes256-ctr hmac-sha256')
 
     def test_change_passphrase_does_not_change_algorithm_pbkdf2(self):
         self.verify_change_passphrase_does_not_change_algorithm('pbkdf2', b'sha256')
@@ -3635,7 +3635,7 @@ def verify_change_location_does_not_change_algorithm(self, given_algorithm, expe
             assert key[b'algorithm'] == expected_algorithm
 
     def test_change_location_does_not_change_algorithm_argon2(self):
-        self.verify_change_location_does_not_change_algorithm('argon2', b'argon2 chacha20-poly1305')
+        self.verify_change_location_does_not_change_algorithm('argon2', b'argon2 aes256-ctr hmac-sha256')
 
     def test_change_location_does_not_change_algorithm_pbkdf2(self):
         self.verify_change_location_does_not_change_algorithm('pbkdf2', b'sha256')
@@ -3647,7 +3647,7 @@ def test_key_change_algorithm(self):
 
         with Repository(self.repository_path) as repository:
             _, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK)
-        assert key._encrypted_key_algorithm == 'argon2 chacha20-poly1305'
+        assert key._encrypted_key_algorithm == 'argon2 aes256-ctr hmac-sha256'
         self.cmd('info', self.repository_location)
 
 
diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py
index dac3efeb..570afbc1 100644
--- a/src/borg/testsuite/crypto.py
+++ b/src/borg/testsuite/crypto.py
@@ -247,11 +247,15 @@ def test_hkdf_hmac_sha512_5(self):
         assert okm == bytes.fromhex('1407d46013d98bc6decefcfee55f0f90b0c7f63d68eb1a80eaf07e953cfc0a3a5240a155d6e4daa965bb')
 
 
-def test_decrypt_key_file_argon2_chacha20_poly1305():
+def test_decrypt_key_file_argon2_aes256_ctr_hmac_sha256():
     plain = b'hello'
-    # echo -n "hello, pass phrase" | argon2 saltsaltsaltsalt -id -t 1 -k 8 -p 1 -l 32 -r
-    key = bytes.fromhex('a1b0cba145c154fbd8960996c5ce3428e9920cfe53c84ef08b4102a70832bcec')
-    ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+    # echo -n "hello, pass phrase" | argon2 saltsaltsaltsalt -id -t 1 -k 8 -p 1 -l 64 -r
+    key = bytes.fromhex('d07cc7f9cfb483303e0b9fec176b2a9c559bb70c3a9fb0d5f9c0c23527cd09570212449f09f8cd28c1a41b73fa0098e889c3f2642e87c392e51f95d70d248d9d')
+    ae_cipher = AES256_CTR_HMAC_SHA256(
+        iv=0, header_len=0, aad_offset=0,
+        enc_key=key[:32],
+        mac_key=key[32:],
+    )
 
     envelope = ae_cipher.encrypt(plain)
 
@@ -262,7 +266,7 @@ def test_decrypt_key_file_argon2_chacha20_poly1305():
         'argon2_memory_cost': 8,
         'argon2_parallelism': 1,
         'argon2_type': b'id',
-        'algorithm': 'argon2 chacha20-poly1305',
+        'algorithm': 'argon2 aes256-ctr hmac-sha256',
         'data': envelope,
     })
     key = KeyfileKey(None)