From 17308b941ed99b80b14a59f4970af08ea1e97030 Mon Sep 17 00:00:00 2001 From: Jean Parpaillon Date: Mon, 20 Mar 2023 10:47:25 +0100 Subject: [PATCH] firmware.patch handles FAT resources With the following patch https://github.com/fwup-home/fwup/pull/202 , fwup is able to apply delta updates on FAT resources. This patch update firmware.patch mix task to create firmware patches including FAT resources delta updates. --- lib/mix/tasks/firmware.patch.ex | 72 ++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/lib/mix/tasks/firmware.patch.ex b/lib/mix/tasks/firmware.patch.ex index 3d5ef720..095b3aa3 100644 --- a/lib/mix/tasks/firmware.patch.ex +++ b/lib/mix/tasks/firmware.patch.ex @@ -5,7 +5,7 @@ defmodule Mix.Tasks.Firmware.Patch do Generate a firmware patch from a source and target firmware and output a new firmware file with the patch contents. The source firmware file - This requires fwup >= 1.6.0 + This requires fwup >= 1.10.0 ## Command line options @@ -21,7 +21,7 @@ defmodule Mix.Tasks.Firmware.Patch do alias Mix.Nerves.Preflight alias Nerves.Utils.Shell - @fwup_semver "~> 1.6 or ~> 1.6.0-dev" + @fwup_semver ">= 1.10.0" @switches [source: :string, target: :string, output: :string] @@ -56,27 +56,7 @@ defmodule Mix.Tasks.Firmware.Patch do target_stats = File.stat!(target) - source_work_dir = Path.join(work_dir, "source") - target_work_dir = Path.join(work_dir, "target") - output_work_dir = Path.join(work_dir, "output") - - File.mkdir_p!(source_work_dir) - File.mkdir_p!(target_work_dir) - File.mkdir_p!(Path.join(output_work_dir, "data")) - - {_, 0} = shell("unzip", ["-qq", source, "-d", source_work_dir]) - {_, 0} = shell("unzip", ["-qq", target, "-d", target_work_dir]) - - source_rootfs = Path.join([source_work_dir, "data", "rootfs.img"]) - target_rootfs = Path.join([target_work_dir, "data", "rootfs.img"]) - out_rootfs = Path.join([output_work_dir, "data", "rootfs.img"]) - - {_, 0} = shell("xdelta3", ["-A", "-S", "-f", "-s", source_rootfs, target_rootfs, out_rootfs]) - - File.mkdir_p!(Path.dirname(output)) - File.cp!(target, output) - - {_, 0} = shell("zip", ["-qq", output, "data/rootfs.img"], cd: output_work_dir) + _ = create(source, target, output, work_dir) output_stats = File.stat!(output) @@ -117,4 +97,50 @@ defmodule Mix.Tasks.Firmware.Patch do Mix.Tasks.Firmware.run(["--output", out_fw]) out_fw end + + defp create(source_path, target_path, output_path, work_dir) do + source_work_dir = Path.join(work_dir, "source") + target_work_dir = Path.join(work_dir, "target") + output_work_dir = Path.join(work_dir, "output") + + _ = File.mkdir_p(source_work_dir) + _ = File.mkdir_p(target_work_dir) + _ = File.mkdir_p(output_work_dir) + + {_, 0} = shell("unzip", ["-qq", source_path, "-d", source_work_dir]) + {_, 0} = shell("unzip", ["-qq", target_path, "-d", target_work_dir]) + + for path <- Path.wildcard(target_work_dir <> "/**") do + path = Regex.replace(~r/^#{target_work_dir}\//, path, "") + + unless File.dir?(Path.join(target_work_dir, path)) do + :ok = handle_content(path, source_work_dir, target_work_dir, output_work_dir) + end + end + + {_, 0} = shell("zip", ["-r", "-qq", output_path, "."], cd: output_work_dir) + output_path + end + + defp handle_content("meta." <> _ = path, _source_dir, target_dir, out_dir) do + do_copy(Path.join(target_dir, path), Path.join(out_dir, path)) + end + + defp handle_content(path, source_dir, target_dir, out_dir) do + do_delta(Path.join(source_dir, path), Path.join(target_dir, path), Path.join(out_dir, path)) + end + + defp do_copy(source, target) do + target |> Path.dirname() |> File.mkdir_p!() + File.cp(source, target) + end + + defp do_delta(source, target, out) do + out |> Path.dirname() |> File.mkdir_p!() + + case shell("xdelta3", ["-A", "-S", "-f", "-s", source, target, out]) do + {_, 0} -> :ok + {_, code} -> {:error, code} + end + end end