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/debug_cmd.py b/src/borg/archiver/debug_cmd.py
--- a/src/borg/archiver/debug_cmd.py
+++ b/src/borg/archiver/debug_cmd.py
@@ -1,7 +1,6 @@
 import argparse
 from binascii import unhexlify, hexlify
 import functools
-import hashlib
 import json
 import textwrap
 
@@ -241,28 +240,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
 
@@ -503,7 +510,7 @@ def build_parser_debug(self, subparsers, common_parser, mid_common_parser):
 
         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(
@@ -516,9 +523,8 @@ def build_parser_debug(self, subparsers, common_parser, mid_common_parser):
             help="put object to repository (debug)",
         )
         subparser.set_defaults(func=self.do_debug_put_obj)
-        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(
             """
diff --git a/src/borg/testsuite/archiver/debug_cmds.py b/src/borg/testsuite/archiver/debug_cmds.py
--- a/src/borg/testsuite/archiver/debug_cmds.py
+++ b/src/borg/testsuite/archiver/debug_cmds.py
@@ -2,7 +2,6 @@
 import os
 import pstats
 import unittest
-from hashlib import sha256
 
 from ...constants import *  # NOQA
 from .. import changedir
@@ -47,18 +46,19 @@ def test_debug_dump_repo_objs(self):
     def test_debug_put_get_delete_obj(self):
         self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION)
         data = b"some data"
-        hexkey = sha256(data).hexdigest()
         self.create_regular_file("file", contents=data)
-        output = self.cmd(f"--repo={self.repository_location}", "debug", "put-obj", "input/file")
-        assert hexkey in output
-        output = self.cmd(f"--repo={self.repository_location}", "debug", "get-obj", 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(f"--repo={self.repository_location}", "debug", "put-obj", id_hash, "input/file")
+        assert id_hash in output
+        output = self.cmd(f"--repo={self.repository_location}", "debug", "get-obj", 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(f"--repo={self.repository_location}", "debug", "delete-obj", hexkey)
+        output = self.cmd(f"--repo={self.repository_location}", "debug", "delete-obj", id_hash)
         assert "deleted" in output
-        output = self.cmd(f"--repo={self.repository_location}", "debug", "delete-obj", hexkey)
+        output = self.cmd(f"--repo={self.repository_location}", "debug", "delete-obj", id_hash)
         assert "not found" in output
         output = self.cmd(f"--repo={self.repository_location}", "debug", "delete-obj", "invalid")
         assert "is invalid" in output


Test patch: