paycheck: allow to pass an explicit path to bspatch

The bspatch binary is used when applying update payloads. By default, we
were using whatever bspatch that was found via path expansion in
os.execvp, however there are cases where we want to be specific about
where the bspatch binary is that we need to be using (such as during
paygen runs).

BUG=chromium:277072
TEST=Non-default bspatch binary used

Change-Id: I85ffd28aeb26938cbf5ea428fa97d29af0353a7d
Reviewed-on: https://gerrit.chromium.org/gerrit/66736
Tested-by: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Commit-Queue: Gilad Arnold <garnold@chromium.org>
diff --git a/scripts/paycheck.py b/scripts/paycheck.py
index e199a7c..380809e 100755
--- a/scripts/paycheck.py
+++ b/scripts/paycheck.py
@@ -97,6 +97,8 @@
                         default=False,
                         help=('use temp input/output files with BSDIFF '
                               'operations (not in-place)'))
+  trace_opts.add_option('--bspatch-path', metavar='FILE',
+                        help=('use the specified bspatch binary'))
   parser.add_option_group(trace_opts)
 
   trace_opts = optparse.OptionGroup(parser, 'Block tracing')
@@ -152,6 +154,8 @@
       opts.check = True
     if opts.extract_bsdiff:
       parser.error('--extract-bsdiff can only be used when applying payloads')
+    if opts.bspatch_path:
+      parser.error('--bspatch-path can only be used when applying payloads')
   else:
     parser.error('unexpected number of arguments')
 
@@ -218,6 +222,8 @@
       # Apply payload.
       if extra_args:
         dargs = {'bsdiff_in_place': not options.extract_bsdiff}
+        if options.bspatch_path:
+          dargs['bspatch_path'] = options.bspatch_path
         if options.assert_type == _TYPE_DELTA:
           dargs['old_kernel_part'] = extra_args[2]
           dargs['old_rootfs_part'] = extra_args[3]
diff --git a/scripts/update_payload/applier.py b/scripts/update_payload/applier.py
index 85f8b0e..9448ef7 100644
--- a/scripts/update_payload/applier.py
+++ b/scripts/update_payload/applier.py
@@ -192,13 +192,14 @@
 
   """
 
-  def __init__(self, payload, bsdiff_in_place=True,
+  def __init__(self, payload, bsdiff_in_place=True, bspatch_path=None,
                truncate_to_expected_size=True):
     """Initialize the applier.
 
     Args:
       payload: the payload object to check
       bsdiff_in_place: whether to perform BSDIFF operation in-place (optional)
+      bspatch_path: path to the bspatch binary (optional)
       truncate_to_expected_size: whether to truncate the resulting partitions
                                  to their expected sizes, as specified in the
                                  payload (optional)
@@ -208,6 +209,7 @@
     self.payload = payload
     self.block_size = payload.manifest.block_size
     self.bsdiff_in_place = bsdiff_in_place
+    self.bspatch_path = bspatch_path or 'bspatch'
     self.truncate_to_expected_size = truncate_to_expected_size
 
   def _ApplyReplaceOperation(self, op, op_name, out_data, part_file, part_size):
@@ -325,7 +327,7 @@
 
       # Invoke bspatch on partition file with extents args.
       file_name = '/dev/fd/%d' % part_file.fileno()
-      bspatch_cmd = ['bspatch', file_name, file_name, patch_file_name,
+      bspatch_cmd = [self.bspatch_path, file_name, file_name, patch_file_name,
                      in_extents_arg, out_extents_arg]
       subprocess.check_call(bspatch_cmd)
 
@@ -346,7 +348,8 @@
         out_file_name = out_file.name
 
       # Invoke bspatch.
-      bspatch_cmd = ['bspatch', in_file_name, out_file_name, patch_file_name]
+      bspatch_cmd = [self.bspatch_path, in_file_name, out_file_name,
+                     patch_file_name]
       subprocess.check_call(bspatch_cmd)
 
       # Read output.
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index 0bac091..b13aa11 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -232,7 +232,7 @@
                report_out_file=report_out_file)
 
   def Apply(self, new_kernel_part, new_rootfs_part, old_kernel_part=None,
-            old_rootfs_part=None, bsdiff_in_place=True,
+            old_rootfs_part=None, bsdiff_in_place=True, bspatch_path=None,
             truncate_to_expected_size=True):
     """Applies the update payload.
 
@@ -242,6 +242,7 @@
       old_kernel_part: name of source kernel partition file (optional)
       old_rootfs_part: name of source rootfs partition file (optional)
       bsdiff_in_place: whether to perform BSDIFF operations in-place (optional)
+      bspatch_path: path to the bspatch binary (optional)
       truncate_to_expected_size: whether to truncate the resulting partitions
                                  to their expected sizes, as specified in the
                                  payload (optional)
@@ -253,7 +254,7 @@
 
     # Create a short-lived payload applier object and run it.
     helper = applier.PayloadApplier(
-        self, bsdiff_in_place=bsdiff_in_place,
+        self, bsdiff_in_place=bsdiff_in_place, bspatch_path=bspatch_path,
         truncate_to_expected_size=truncate_to_expected_size)
     helper.Run(new_kernel_part, new_rootfs_part,
                old_kernel_part=old_kernel_part,