Packages
nerves
1.13.1
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.0
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.3
1.9.2
retired
1.9.1
1.9.0
1.8.0
1.7.17
1.7.16
1.7.15
1.7.14
retired
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
retired
1.7.1
retired
1.7.0
retired
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.11.0
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-rc.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
Craft and deploy bulletproof embedded software
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/firmware.patch.ex
# SPDX-FileCopyrightText: 2020 Frank Hunleth
# SPDX-FileCopyrightText: 2020 Justin Schneck
# SPDX-FileCopyrightText: 2022 Jon Carstens
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Mix.Tasks.Firmware.Patch do
@shortdoc "Build a firmware patch"
@moduledoc """
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
## Command line options
* `--source` - (Optional) The path to the .fw file used as the source.
Defaults to the last firmware built.
* `--target` - (Optional) The path to the .fw file used as the target.
Defaults to generating a new firmware without overwriting the source.
* `--output` - (Optional) The path to the .fw file used to write the patch
firmware. Defaults to `Nerves.Env.firmware_path/1`
"""
use Mix.Task
import Mix.Nerves.Utils
alias Mix.Nerves.Preflight
alias Nerves.Utils.Shell
@fwup_semver "~> 1.6 or ~> 1.6.0-dev"
@switches [source: :string, target: :string, output: :string]
@impl Mix.Task
def run(args) do
work_dir = Path.join(Nerves.Env.images_path(), "patch")
_ = File.rm_rf!(work_dir)
File.mkdir_p!(work_dir)
config = Mix.Project.config()
{opts, _, _} = OptionParser.parse(args, switches: @switches)
Preflight.ensure_fwup_version!("fwup", @fwup_semver)
source = (opts[:source] || Nerves.Env.firmware_path(config)) |> Path.expand()
if !File.exists?(source) do
Mix.raise("""
Source firmware #{source} does not exist.
Please pass --source /path/source.fw or run `mix firmware`.
""")
end
source_stats = File.stat!(source)
target = (opts[:target] || mix_target_firmware(work_dir)) |> Path.expand()
output = (opts[:output] || Path.join(Nerves.Env.images_path(), "patch.fw")) |> Path.expand()
Shell.info("""
Generating patch firmware
""")
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)
output_stats = File.stat!(output)
{source_meta, 0} = System.cmd("fwup", ["-m", "-i", source])
{target_meta, 0} = System.cmd("fwup", ["-m", "-i", target])
[source_uuid | _] = String.split(source_meta, "meta-uuid=") |> Enum.reverse()
[target_uuid | _] = String.split(target_meta, "meta-uuid=") |> Enum.reverse()
source_uuid = String.trim(source_uuid, "\"")
target_uuid = String.trim(target_uuid, "\"")
_ = File.rm_rf!(work_dir)
Shell.success("""
Finished generating patch firmware
Source
#{source}
uuid: #{source_uuid}
size: #{source_stats.size} bytes
Target
#{target}
uuid: #{target_uuid}
size: #{target_stats.size} bytes
Patch
#{output}
size: #{output_stats.size} bytes
""")
end
defp mix_target_firmware(work_dir) do
Shell.info("Generating new target firmware")
out_fw = Path.join(work_dir, "target.fw")
Mix.Tasks.Firmware.run(["--output", out_fw])
out_fw
end
end