Repository: borgbackup/borg

Issue:
borg debug put-obj can not put a manifest
borg 1.2-maint branch (likely same in master branch).

One can only give the repo and the file it shall put, the chunkid gets computed automatically from the file contents.

In case of the repository manifest chunk, that does not work, because the chunkid must be all-zero (and not computed).


Discussion:

Resolving patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -9,7 +9,6 @@
     import configparser
     import faulthandler
     import functools
-    import hashlib
     import inspect
     import itertools
     import json
@@ -2193,28 +2192,36 @@ def do_debug_get_obj(self, args, repository):
         hex_id = args.id
         try:
             id = unhexlify(hex_id)
-        except ValueError:
-            print("object id %s is invalid." % hex_id)
-        else:
-            try:
-                data = repository.get(id)
-            except Repository.ObjectNotFound:
-                print("object %s not found." % hex_id)
-            else:
-                with open(args.path, "wb") as f:
-                    f.write(data)
-                print("object %s fetched." % hex_id)
+            if len(id) != 32:  # 256bit
+                raise ValueError("id must be 256bits or 64 hex digits")
+        except ValueError as err:
+            print("object id %s is invalid [%s]." % (hex_id, str(err)))
+            return EXIT_ERROR
+        try:
+            data = repository.get(id)
+        except Repository.ObjectNotFound:
+            print("object %s not found." % hex_id)
+            return EXIT_ERROR
+        with open(args.path, "wb") as f:
+            f.write(data)
+        print("object %s fetched." % hex_id)
         return EXIT_SUCCESS
 
     @with_repository(manifest=False, exclusive=True)
     def do_debug_put_obj(self, args, repository):
-        """put file(s) contents into the repository"""
-        for path in args.paths:
-            with open(path, "rb") as f:
-                data = f.read()
-            h = hashlib.sha256(data)  # XXX hardcoded
-            repository.put(h.digest(), data)
-            print("object %s put." % h.hexdigest())
+        """put file contents into the repository"""
+        with open(args.path, "rb") as f:
+            data = f.read()
+        hex_id = args.id
+        try:
+            id = unhexlify(hex_id)
+            if len(id) != 32:  # 256bit
+                raise ValueError("id must be 256bits or 64 hex digits")
+        except ValueError as err:
+            print("object id %s is invalid [%s]." % (hex_id, str(err)))
+            return EXIT_ERROR
+        repository.put(id, data)
+        print("object %s put." % hex_id)
         repository.commit(compact=False)
         return EXIT_SUCCESS
 
@@ -3744,7 +3751,7 @@ def define_borg_mount(parser):
                                help='file to write object data into')
 
         debug_put_obj_epilog = process_epilog("""
-        This command puts objects into the repository.
+        This command puts an object into the repository.
         """)
         subparser = debug_parsers.add_parser('put-obj', parents=[common_parser], add_help=False,
                                           description=self.do_debug_put_obj.__doc__,
@@ -3755,8 +3762,8 @@ def define_borg_mount(parser):
         subparser.add_argument('location', metavar='REPOSITORY',
                                type=location_validator(archive=False),
                                help='repository to use')
-        subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
-                               help='file(s) to read and create object(s) from')
+        subparser.add_argument("id", metavar="ID", type=str, help="hex object ID to put into the repo")
+        subparser.add_argument("path", metavar="PATH", type=str, help="file to read and create object from")
 
         debug_delete_obj_epilog = process_epilog("""
         This command deletes objects from the repository.
diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py
--- a/src/borg/testsuite/archiver.py
+++ b/src/borg/testsuite/archiver.py
@@ -2908,18 +2908,19 @@ def test_debug_dump_repo_objs(self):
     def test_debug_put_get_delete_obj(self):
         self.cmd('init', '--encryption=repokey', self.repository_location)
         data = b'some data'
-        hexkey = sha256(data).hexdigest()
         self.create_regular_file('file', contents=data)
-        output = self.cmd('debug', 'put-obj', self.repository_location, 'input/file')
-        assert hexkey in output
-        output = self.cmd('debug', 'get-obj', self.repository_location, hexkey, 'output/file')
-        assert hexkey in output
+        # TODO: to compute the correct hexkey, we need: borg debug id-hash file
+        id_hash = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"  # 256bit = 64 hex digits
+        output = self.cmd('debug', 'put-obj', self.repository_location, id_hash, 'input/file')
+        assert id_hash in output
+        output = self.cmd('debug', 'get-obj', self.repository_location, id_hash, 'output/file')
+        assert id_hash in output
         with open('output/file', 'rb') as f:
             data_read = f.read()
         assert data == data_read
-        output = self.cmd('debug', 'delete-obj', self.repository_location, hexkey)
+        output = self.cmd('debug', 'delete-obj', self.repository_location, id_hash)
         assert "deleted" in output
-        output = self.cmd('debug', 'delete-obj', self.repository_location, hexkey)
+        output = self.cmd('debug', 'delete-obj', self.repository_location, id_hash)
         assert "not found" in output
         output = self.cmd('debug', 'delete-obj', self.repository_location, 'invalid')
         assert "is invalid" in output


Test patch: