{"id":32137011,"url":"https://github.com/fwup-home/fwup","last_synced_at":"2025-12-11T22:50:32.875Z","repository":{"id":17614320,"uuid":"20418412","full_name":"fwup-home/fwup","owner":"fwup-home","description":"Configurable embedded Linux firmware update creator and runner","archived":false,"fork":false,"pushed_at":"2025-11-27T12:55:28.000Z","size":45391,"stargazers_count":412,"open_issues_count":17,"forks_count":56,"subscribers_count":20,"default_branch":"main","last_synced_at":"2025-11-30T05:44:32.829Z","etag":null,"topics":["embedded-systems","firmware-archive","firmware-updates"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fwup-home.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2014-06-02T20:25:38.000Z","updated_at":"2025-11-27T12:55:32.000Z","dependencies_parsed_at":"2024-04-20T14:42:24.260Z","dependency_job_id":"399e3eef-f44b-499e-bceb-70abb5513524","html_url":"https://github.com/fwup-home/fwup","commit_stats":{"total_commits":1001,"total_committers":32,"mean_commits":31.28125,"dds":0.06793206793206796,"last_synced_commit":"a713b55f9cd94f8fee74b275bc0dcc417c04946a"},"previous_names":["fhunleth/fwup"],"tags_count":81,"template":false,"template_full_name":null,"purl":"pkg:github/fwup-home/fwup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwup-home%2Ffwup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwup-home%2Ffwup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwup-home%2Ffwup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwup-home%2Ffwup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fwup-home","download_url":"https://codeload.github.com/fwup-home/fwup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwup-home%2Ffwup/sbom","scorecard":{"id":398723,"data":{"date":"2025-08-11","repo":{"name":"github.com/fwup-home/fwup","commit":"3ad2e8ff9a359c66eda7c16497898f68464c1f62"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":10,"reason":"12 commit(s) and 5 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 1/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.13.2 not signed: https://api.github.com/repos/fwup-home/fwup/releases/233773374","Warn: release artifact v1.13.1 not signed: https://api.github.com/repos/fwup-home/fwup/releases/233697429","Warn: release artifact v1.13.0 not signed: https://api.github.com/repos/fwup-home/fwup/releases/223650816","Warn: release artifact v1.12.0 not signed: https://api.github.com/repos/fwup-home/fwup/releases/192037167","Warn: release artifact v1.11.0 not signed: https://api.github.com/repos/fwup-home/fwup/releases/178102378","Warn: release artifact v1.13.2 does not have provenance: https://api.github.com/repos/fwup-home/fwup/releases/233773374","Warn: release artifact v1.13.1 does not have provenance: https://api.github.com/repos/fwup-home/fwup/releases/233697429","Warn: release artifact v1.13.0 does not have provenance: https://api.github.com/repos/fwup-home/fwup/releases/223650816","Warn: release artifact v1.12.0 does not have provenance: https://api.github.com/repos/fwup-home/fwup/releases/192037167","Warn: release artifact v1.11.0 does not have provenance: https://api.github.com/repos/fwup-home/fwup/releases/178102378"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: pipCommand not pinned by hash: scripts/ci_after_success.sh:17","Warn: nugetCommand not pinned by hash: scripts/ubuntu_install_chocolatey.sh:52: pin your dependecies by either enabling central package management (https://learn.microsoft.com/nuget/consume-packages/Central-Package-Management) or using a lockfile (https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies)","Info:   0 out of   1 pipCommand dependencies pinned","Info:   0 out of   1 nugetCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T19:38:26.177Z","repository_id":17614320,"created_at":"2025-08-18T19:38:26.177Z","updated_at":"2025-08-18T19:38:26.177Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27622832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-09T02:00:09.185Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded-systems","firmware-archive","firmware-updates"],"created_at":"2025-10-21T04:58:22.090Z","updated_at":"2025-12-11T22:50:32.867Z","avatar_url":"https://github.com/fwup-home.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿![The fwup pup](docs/fwup-pup.png)\n\n# Overview\n\n[![CircleCI](https://circleci.com/gh/fwup-home/fwup.svg?style=svg)](https://circleci.com/gh/fwup-home/fwup)\n[![Coverage Status](https://coveralls.io/repos/github/fwup-home/fwup/badge.svg)](https://coveralls.io/github/fhunleth/fwup)\n[![Coverity Scan Build Status](https://scan.coverity.com/projects/4094/badge.svg)](https://scan.coverity.com/projects/4094)\n\n`fwup` is a configurable image-based software update utility for embedded\nLinux-based systems. It primarily supports software upgrade strategies that\nupdate entire root filesystem images at once. This includes strategies like\nswapping back and forth between A and B partitions, recovery partitions, and\nvarious trial update/failback scenarios. All software update information is\ncombined into a ZIP archive that may optionally be cryptographically signed.\n`fwup` has minimal dependencies and runtime requirements. Scripts are\nintentionally limited to make failure scenarios easier to reason about.\nDistribution of software update archives is not a feature. Users can call out to\n`fwup` to run upgrades from external media, stream them from the network, or\nscript them using a tool like Ansible if so desired.\n\nHere's a list of features:\n\n1. Uses standard ZIP archives to make debugging and transmission simple.\n\n1. Simple, but flexible configuration language to enable firmware updates on\n   various platforms and firmware update policies.\n\n1. Streaming firmware update processing to simplify target storage requirements.\n\n1. Multiple firmware update task options per archive so that one archive can\n   upgrade varying target configurations\n\n1. Basic disk partitioning (GPT and MBR) and FAT filesystem manipulation\n\n1. Human and machine readable progress.\n\n1. Initialize or update SDCards on your development system whether you're\n   running Linux, OSX, BSD, or Windows. MMC and SDCards are automatically\n   detected and unmounted. No need to scan logs or manually unmount.\n\n1. Firmware archive digital signature creation and verification\n\n1. Delta update support using [VCDIFF](https://en.wikipedia.org/wiki/VCDIFF).\n   See delta update section for details (BETA FEATURE).\n\n1. Sparse file support to reduce number of bytes that need to be written when\n   initializing large file systems (see section on sparse files)\n\n1. Permissive license (Apache 2.0 License - see end of doc for details)\n\n1. Extensively regression tested! Tests also provide examples of\n    functionality.\n\nInternally, `fwup` has many optimizations to speed up low level disk writes over\nwhat can easily be achieved with `dd(1)`. It orders, Flash erase block aligns,\nand can skip writing large unused sections to minimize write time. It also\nverifies writes to catch corruption before the device reboots to the new\nfirmware. The goal is to make updates fast enough for code development and\nreliable enough for production use.\n\n# Installing\n\nThe simplest way to install `fwup` is via a package manager or installer.\n\nIf you're already using [asdf](https://asdf-vm.com/) or\n[mise-en-place](https://mise.jdx.dev/), install `fwup` via\n[asdf-fwup](https://github.com/fwup-home/asdf-fwup). This will allow you to\nmanage `fwup` versions in your software projects via `.tool-versions` files.\n\nFor system-wide installation on MacOS, `fwup` is in [homebrew](http://brew.sh/):\n\n```sh\nbrew install fwup\n```\n\nOn Linux, download and install the appropriate package for your platform:\n\n* [Debian/Ubuntu AMD64 .deb](https://github.com/fwup-home/fwup/releases/download/v1.14.0/fwup_1.14.0_amd64.deb)\n* [Raspbian armhf .deb](https://github.com/fwup-home/fwup/releases/download/v1.14.0/fwup_1.14.0_armhf.deb)\n* Alpine Linux - Install official [apk](https://pkgs.alpinelinux.org/packages?name=fwup\u0026branch=edge)\n* Arch Linux - See [fwup-git package](https://aur.archlinux.org/packages/fwup-git/) on AUR\n* Buildroot - Support is included upstream since the 2016.05 release\n* Yocto - See [meta-fwup](https://github.com/fwup-home/meta-fwup)\n\nOn Windows, `fwup` can be installed from [chocolatey](http://chocolatey.org)\n\n    choco install fwup\n\nAlternatively, download the [fwup executable](https://github.com/fwup-home/fwup/releases/download/v1.14.0/fwup.exe)\nand place it in your path.\n\nIf you're using another platform or prefer to build it yourself, download the\nlatest [source code release](https://github.com/fwup-home/fwup/releases/download/v1.14.0/fwup-1.14.0.tar.gz)\nor clone this repository. Then read one of the following files:\n\n* [Linux build instructions](docs/build_linux.md)\n* [OSX build instructions](docs/build_osx.md)\n* [Windows build instructions](docs/build_windows.md)\n* [Raspbian build instructions](docs/build_rpi.md)\n* [FreeBSD/NetBSD/OpenBSD build instructions](docs/build_bsd.md)\n\nWhen building from source, please verify that the regression test pass on your\nsystem (run `make check`) before using `fwup` in production. While the tests\nusually pass, they have found minor issues in third party libraries in the past\nthat really should be fixed.\n\nNOTE: For space-constrained target devices, use `./configure\n--enable-minimal-build` to trim functionality that's rarely used.\n\n# Invoking\n\nIf you were given a `.fw` file and just want to install its contents on an\nSDCard, here's an example run on Linux:\n\n```sh\n$ sudo fwup example.fw\nUse 14.92 GiB memory card found at /dev/sdc? [y/N] y\n100%\nElapsed time: 2.736s\n```\n\nIf you're on OSX or Windows, leave off the call to `sudo`.\n\nIMPORTANT: If you're using an older version of `fwup`, you'll have to specify\nmore arguments. Run: `fwup -a -i example.fw -t complete`\n\nHere's a list of other options:\n\n```plaintext\nUsage: fwup [OPTION]...\n\nOptions:\n  -a, --apply   Apply the firmware update\n  -c, --create  Create the firmware update\n  -d \u003cfile\u003e Device file for the memory card\n  -D, --detect List attached SDCards or MMC devices and their sizes\n  -E, --eject Eject removable media after successfully writing firmware.\n  --no-eject Do not eject media after writing firmware\n  --enable-trim Enable use of the hardware TRIM command\n  --exit-handshake Send a Ctrl+Z on exit and wait for stdin to close (Erlang)\n  -f \u003cfwup.conf\u003e Specify the firmware update configuration file\n  -F, --framing Apply framing on stdin/stdout\n  -g, --gen-keys Generate firmware signing keys (fwup-key.pub and fwup-key.priv, or specify with -o)\n  -i \u003cinput.fw\u003e Specify the input firmware update file (Use - for stdin)\n  -l, --list   List the available tasks in a firmware update\n  --max-size \u003cblocks\u003e Max size of the destination in 512-byte blocks (usually automatic)\n  -m, --metadata   Print metadata in the firmware update\n  --metadata-key \u003ckey\u003e Only output the specified key's value when printing metadata\n  --minimize-writes Skip write if contents match destination\n  --no-minimize-writes Don't try to minimize writes when applying firmware updates (default)\n  -n   Report numeric progress\n  -o \u003coutput.fw\u003e Specify the output file when creating an update (Use - for stdout)\n  -p, --public-key-file \u003ckeyfile\u003e A public key file for verifying firmware updates (can specify multiple times)\n  --private-key \u003ckey\u003e A private key for signing firmware updates\n  --progress-low \u003cnumber\u003e When displaying progress, this is the lowest number (normally 0 for 0%)\n  --progress-high \u003cnumber\u003e When displaying progress, this is the highest number (normally 100 for 100%)\n  --public-key \u003ckey\u003e A public key for verifying firmware updates (can specify multiple times)\n  -q, --quiet   Quiet\n  -s, --private-key-file \u003ckeyfile\u003e A private key file for signing firmware updates\n  -S, --sign Sign an existing firmware file (specify -i and -o)\n  --sparse-check \u003cpath\u003e Check if the OS and file system supports sparse files at path\n  --sparse-check-size \u003cbytes\u003e Hole size to check for --sparse-check\n  -t, --task \u003ctask\u003e Task to apply within the firmware update\n  -u, --unmount Unmount all partitions on device first\n  -U, --no-unmount Do not try to unmount partitions on device\n  --unsafe Allow unsafe commands (consider applying only signed archives)\n  -v, --verbose   Verbose\n  -V, --verify  Verify an existing firmware file (specify -i)\n  --verify-writes Verify writes when applying firmware updates to detect corruption (default for writing to device files)\n  --no-verify-writes Do not verify writes when applying firmware updates (default for regular files)\n  --version Print out the version\n  -y   Accept automatically found memory card when applying a firmware update\n  -z   Print the memory card that would be automatically detected and exit\n  -1   Fast compression (for create)\n  -9   Best compression (default)\n\nExamples:\n\nCreate a firmware update archive:\n\n  $ fwup -c -f fwup.conf -o myfirmware.fw\n\nApply the firmware to an attached SDCard. This would normally be run on the host\nwhere it would auto-detect an SDCard and initialize it using the 'complete' task:\n\n  $ fwup -a -i myfirmware.fw -t complete\n\nApply the firmware update to /dev/sdc and specify the 'upgrade' task:\n\n  $ fwup -a -d /dev/sdc -i myfirmware.fw -t upgrade\n\nCreate an image file from a .fw file for use with dd(1):\n\n  $ fwup -a -d myimage.img -i myfirmware.fw -t complete\n\nGenerate a public/private key pair:\n\n  $ fwup -g\n\nStore fwup-key.priv in a safe place and fwup-key.pub on the target. To sign\nan existing archive run:\n\n  $ fwup -S -s fwup-key.priv -i myfirmware.fw -o signedfirmware.fw\n```\n\n# Example usage\n\nThe regression tests contain short examples for usage of various script\nelements and are likely the most helpful to read due to their small size.\n\nOther examples can be found in the\n[bbb-buildroot-fwup](https://github.com/fhunleth/bbb-buildroot-fwup) for project\nfor the BeagleBone Black and Raspberry Pi. The [Nerves\nProject](https://github.com/nerves-project/nerves_system_br) has more examples\nand is better maintained.\n\nMy real world use of `fwup` involves writing the new firmware to a place on the\nFlash that's not in current use and then 'flipping' over to it at the very end.\nThe examples tend to reflect that. `fwup` can also be used to overwrite an\ninstallation in place assuming you're using an initramfs, but that doesn't give\nprotection against someone pulling power at a bad time. Also, `fwup`'s one pass\nover the archive feature means that firmware validation is mostly done on the\nfly, so you'll want to verify the archive first (see the `-V` option).\n\n# Helper scripts\n\nWhile not the original use of `fwup`, it can be convenient to convert other\nfiles to  `.fw` files. `fwup` comes with the following shell script helper:\n\n* `img2fwup` - convert a raw image file to a `.fw` file\n\nA use case for the `img2fwup` script is to convert a large SDCard image file\nto one that is compressed and checksummed by `fwup` for distribution.\n\n# Versioning\n\n`fwup` uses [semver](https://semver.org) for versioning. For example, if you are\nusing 1.0.0, that means no breaking changes until 2.0.0 or new features until\n1.1.0. I highly recommend a conservative approach to upgrading `fwup` once you\nhave devices in the field. For example, if you have 1.0.0 devices in the field,\nit's ok to update to 1.1.0, but be very careful about using 1.1.0 features in\nyour `fwup.conf` files. They will be ignored by 1.0.0 devices and that may or\nmay not be what you want.\n\n# Configuration file format\n\n`fwup` uses the Unix configuration library,\n[libconfuse](https://github.com/libconfuse/libconfuse), so its configuration has some\nsimilarities to other programs. The configuration file is used to create\nfirmware archives. During creation, `fwup` embeds a processed version of the\nconfiguration file into the archive that has been stripped of comments, has had\nall variables resolved, and has some additional useful metadata added.\nConfiguration files are organized into scoped blocks and options are set using\na `key = value` syntax. Additionally, configuration files may include\nconfiguration fragments and other files by calling `include(\"filename\")`.\n\n## Environment variables\n\nFor integration into build systems and other scripts, `fwup` performs\nvariable substitution inside of the configuration files. Keep in\nmind that environment variables are resolved on the host during firmware update\nfile creation. Generated firmware files do not contain\n\nEnvironment variables are referenced as follows:\n\n    key = ${ANY_ENVIRONMENT_VARIABLE}\n\nIt is possible to provide default values for environment variables using the\n`:-` symbol:\n\n    key = ${ANY_ENVIRONMENT_VARIABLE:-adefault}\n\nInside configuration files, it can be useful to define constants that are used\nthroughout the file. Constants are referenced identically to environment\nvariables. Here is an example:\n\n    define(MY_CONSTANT, 5)\n\nBy default, repeated definitions of the same constant do not change that\nconstant's value. In other words, the first definition wins. Note that the first\ndefinition could come from a similarly named environment variable. This makes\nit possible to override a constant in a build script.\n\nIn some cases, having the last definition win is preferable for constants that\nnever ever should be overridden by the environment or by earlier calls to\n`define`. For this behavior, use `define!`:\n\n    define!(MY_CONSTANT, \"Can't override this\")\n\nSimple math calculations may also be performed using `define-eval()` and\n`define-eval!()`. For example:\n\n    define-eval(AN_OFFSET, \"${PREVIOUS_OFFSET} + ${PREVIOUS_COUNT}\")\n\nThese two functions were added in release 0.10.0, but since\nthey are evaluated at firmware creation time, .fw files created using them are\ncompatible with older versions of `fwup`.\n\nFinally, `file-resource` will define a variable named `FWUP_SIZE_\u003cresource_name\u003e` with the size of the resource.  For example\nthe following will create a variable named `FWUP_SIZE_zImage`:\n\n```conf\nfile-resource zImage {\n        host-path = \"output/images/zImage\"\n}\n\nexecute(\"echo zImage size is ${FWUP_SIZE_zImage} bytes\")\n```\n\n## Global scope\n\nAt the global scope, the following options are available:\n\nKey                  | Description\n---------------------|------------\nrequire-fwup-version | Require a minimum version of fwup to apply this update\nmeta-product         | Product name\nmeta-description     | Description of product or firmware update\nmeta-version         | Firmware version\nmeta-author          | Author or company behind the update\nmeta-platform        | Platform that this update runs on (e.g., rpi or bbb)\nmeta-architecture    | Platform architectures (e.g., arm)\nmeta-vcs-identifier  | A version control identifier for use in reproducing this image\nmeta-misc            | Miscellaneous additional data. Format and contents are up to the user\nmeta-creation-date   | Timestamp when the update was created (derived from ZIP metadata). For reproducible builds, set the [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/specs/source-date-epoch/#idm55) environment variable.\nmeta-fwup-version    | Version of fwup used to create the update (deprecated - no longer added since fwup 1.2.0)\nmeta-uuid            | A UUID to represent this firmware. The UUID won't change even if the .fw file is digitally signed after creation (automatically generated)\n\nAfter setting the above options, it is necessary to create scopes for other options. The\ncurrently available scopes are:\n\nScope                | Description\n---------------------|------------\nfile-resource        | Defines a reference to a file that should be included in the archive\nmbr                  | Defines the master boot record contents on the destination\ngpt                  | Defines GPT partitions on the destination\ntask                 | Defines a firmware update task (referenced using -t from the command line)\n\n## file-resource\n\nA `file-resource` specifies a file on the host that should be included in the\narchive. Each `file-resource` should be given a unique name so that it can be\nreferred to by other parts of the update configuration. `fwup` will\nautomatically record the length and BLAKE2b-256 hash of the file in the\narchive. These fields are used internally to compute progress and verify the\ncontents of the archive. A typical `file-resource` section looks like this:\n\n```conf\nfile-resource zImage {\n        host-path = \"output/images/zImage\"\n}\n```\n\nResources are usually stored in the `data` directory of the firmware archive.\nThis is transparent for most users. If you need to make the `.fw` file\nwork with other software, it is sometimes useful to embed a file into\nthe archive at another location. This can be done by specifying an absolute\npath resource as follows:\n\n```conf\nfile-resource \"/my_custom_metadata\" {\n        host-path = \"path/to/my_custom_metadata_file\"\n}\n```\n\n### Resource concatenation\n\nSometimes you need to concatenate multiple files together to form one\n`file-resource`. While you can sometimes do this using multiple calls to\n`raw_write`, that won't work if you don't know the file offsets a priori or the\noffsets don't fall on block boundaries. Another alternative is to concatenate\nfiles as a prep step to fwup. If that's inconvenient, `fwup` allows multiple\npaths to be specified in `host-path` that are separated by semicolons. They\nwill be concatenated in the order they appear.\n\n```conf\nfile-resource kernel_and_rootfs {\n        # Concatenate uImage and the rootfs. OpenWRT mtd splitter will\n        # separate them back out at runtime.\n        host-path = \"output/images/uImage;output/images/rootfs.squashfs\"\n}\n```\n\n### File resource validation checks\n\nWhen creating archives, `fwup` can perform validation checking on file\nresources to catch simple errors. These checks can catch common errors like\nfile resources growing too large to fit on the destination or files truncated\ndue to cancelled builds.\n\nNote that these checks are not performed when applying updates, since the\nactual length (and a hash) is recorded in the archive metadata and used for\nverification.\n\nThe following checks are supported:\n\nCheck           | Description\n----------------|------------\nassert-size-lte | If the file size is not less than or equal the specified amount, report an error.\nassert-size-gte | If the file size is not greater than or equal the specified amount, report an error.\n\nSizes are given in 512 byte blocks (like everything else in `fwup`).\n\nHere's an example:\n\n```conf\nfile-resource rootfs.img {\n        host-path = \"output/images/rootfs.squashfs\"\n        assert-size-lte = ${ROOTFS_A_PART_COUNT}\n}\n```\n\n### Files from strings\n\nSometimes it's useful to create short files inside the `fwup` config file\nrather than referencing them. This can be accomplished using the `contents` key\nin a `file-resource`. Variable substitution works in the `contents` string just\nlike any other string in the `fwup` configuration file.\n\n```conf\nfile-resource short-file.txt {\n        contents = \"You're looking at a short\\n\\\nfile creating by fwup.\\n\\\nWhen it was made, FOO was ${FOO}.\\n\"\n}\n```\n\n## mbr\n\nA `mbr` section specifies the contents of the Master Boot Record on the\ndestination media. This section contains the partition table that's read by\nLinux and the bootloaders for finding the file systems that exist on the media.\nIn comparison to a tool like `fdisk`, `fwup` only supports simplistic partition\nsetup, but this is sufficient for many devices. Tools such as `fdisk` can be\nused to determine the block offsets and sizes of partitions for the\nconfiguration file. Offsets and sizes are given in 512 byte blocks. Here's an\nexample mbr definition:\n\n```conf\nmbr mbr-a {\n        bootstrap-code-host-path = \"path/to/bootstrap-data\" # should be 440 bytes\n        signature = 0x01020304\n\n        partition 0 {\n                block-offset = ${BOOT_PART_OFFSET}\n                block-count = ${BOOT_PART_COUNT}\n                type = 0x1 # FAT12\n                boot = true\n        }\n        partition 1 {\n                block-offset = ${ROOTFS_A_PART_OFFSET}\n                block-count = ${ROOTFS_A_PART_COUNT}\n                type = 0x83 # Linux\n        }\n        partition 2 {\n                block-offset = ${ROOTFS_B_PART_OFFSET}\n                block-count = ${ROOTFS_B_PART_COUNT}\n                type = 0x83 # Linux\n        }\n        partition 3 {\n                block-offset = ${APP_PART_OFFSET}\n                block-count = ${APP_PART_COUNT}\n                type = 0x83 # Linux\n        }\n}\n```\n\n### OSIP\n\nIf you're using an Intel Edison or similar platform, `fwup` supports generation\nof the OSIP header in the MBR. This header provides information for where to\nload the bootloader (e.g.., U-Boot) in memory. The `include-osip` option\ncontrols whether the header is generated. The OSIP and image record (OSII)\noption names map directly to the header fields with the exception that length,\nchecksum and image count fields are automatically calculated. The following is\nan example that shows all of the options:\n\n```conf\nmbr mbr-a {\n    include-osip = true\n    osip-major = 1\n    osip-minor = 0\n    osip-num-pointers = 1\n\n    osii 0 {\n        os-major = 0\n        os-minor = 0\n        start-block-offset = ${UBOOT_OFFSET}\n        ddr-load-address = 0x01100000\n        entry-point = 0x01101000\n        image-size-blocks = 0x0000c000\n        attribute = 0x0f\n    }\n\n    partition 0 {\n        block-offset = ${ROOTFS_A_PART_OFFSET}\n        block-count = ${ROOTFS_A_PART_COUNT}\n        type = 0x83 # Linux\n    }\n}\n```\n\n### Expanding the final partition\n\nSometimes it's useful to have the final partition fill the remainder of the\nstorage. This is needed if your target's storage size is unknown and you need\nto use as much of it as possible. The `expand` option requests that `fwup`\ngrow the `block-count` to be as large as possible. When using `expand`, the\n`block-count` is now the minimum partition size. Only the final partition can\nbe expandable. Here's an example:\n\n```conf\nmbr mbr-a {\n        partition 0 {\n                block-offset = ${BOOT_PART_OFFSET}\n                block-count = ${BOOT_PART_COUNT}\n                type = 0x1 # FAT12\n                boot = true\n        }\n        partition 1 {\n                block-offset = ${ROOTFS_A_PART_OFFSET}\n                block-count = ${ROOTFS_A_PART_COUNT}\n                type = 0x83 # Linux\n        }\n        partition 2 {\n                block-offset = ${ROOTFS_B_PART_OFFSET}\n                block-count = ${ROOTFS_B_PART_COUNT}\n                type = 0x83 # Linux\n        }\n        partition 3 {\n                block-offset = ${APP_PART_OFFSET}\n                block-count = ${APP_PART_COUNT}\n                type = 0x83 # Linux\n                expand = true\n        }\n}\n```\n\n### Extended partitions\n\nMBR supports more than 4 partitions by using a special extended partition type.\nNormally if you need more than 4 partitions, you'd want to switch to GPT, but\nyour setup may make this more trouble than it's worth. At a high level, to use\nthe extended partition feature, you need to mark partition 3 as the extended\npartition. Then you can specified partitions 4 and higher. Each of these\npartitions will have a 512-byte [Extended Boot\nRecord](https://en.wikipedia.org/wiki/Extended_boot_record) or EBR. Fwup stores\nall EBRs at the beginning of the extended partition, so if you have partitions\n4, 5, and 6, that will require 3 EBRs or 1536 bytes. Partition 4 would start at\na block after that. Note that there's flexibility in where EBRs are written so\nyou'll see other programs that make different decisions, but it all works so\nlong as the EBRs are inside of the extended partition and don't overlap with any\nother partition.\n\nHere's an example of an extended partition:\n\n```conf\nmbr mbr-a {\n    partition 0 {\n        block-offset = \\${AUTOBOOT_PART_OFFSET}\n        block-count = \\${AUTOBOOT_PART_COUNT}\n        type = 0x6 # FAT12\n        boot = true\n    }\n    partition 1 {\n        block-offset = \\${BOOT_A_PART_OFFSET}\n        block-count = \\${BOOT_A_PART_COUNT}\n        type = 0xc # FAT32\n    }\n    partition 2 {\n        block-offset = \\${BOOT_B_PART_OFFSET}\n        block-count = \\${BOOT_B_PART_COUNT}\n        type = 0xc # FAT32\n    }\n    partition 3 {\n        type = 0xf # Extended partition\n        block-offset = \\${EXTENDED_PART_OFFSET}\n    }\n    partition 4 {\n        block-offset = \\${ROOTFS_A_PART_OFFSET}\n        block-count = \\${ROOTFS_A_PART_COUNT}\n        type = 0x83 # Linux\n    }\n    partition 5 {\n        block-offset = \\${ROOTFS_B_PART_OFFSET}\n        block-count = \\${ROOTFS_B_PART_COUNT}\n        type = 0x83 # Linux\n    }\n    partition 6 {\n        block-offset = \\${APP_PART_OFFSET}\n        block-count = \\${APP_PART_COUNT}\n        type = 0x83 # Linux\n    }\n}\n```\n\nFwup calculates the extended partition's size based on the partitions after it,\nso you only need to specify the starting block offset.\n\n## gpt\n\nA `gpt` section specifies the contents of a [GUID Partition\nTable](https://en.wikipedia.org/wiki/GUID_Partition_Table). It is similar to the\n`mbr` section, but it supports more partitions and uses UUIDs. Here's an example\ngpt definition:\n\n```conf\ngpt my-gpt {\n    # UUID for the entire disk\n    guid = b443fbeb-2c93-481b-88b3-0ecb0aeba911\n\n    partition 0 {\n        block-offset = ${EFI_PART_OFFSET}\n        block-count = ${EFI_PART_COUNT}\n        type = c12a7328-f81f-11d2-ba4b-00a0c93ec93b # EFI type UUID\n        guid = 5278721d-0089-4768-85df-b8f1b97e6684 # ID for partition 0 (create with uuidgen)\n        name = \"efi-part.vfat\"\n    }\n    partition 1 {\n        block-offset = ${ROOTFS_PART_OFFSET}\n        block-count = ${ROOTFS_PART_COUNT}\n        type = 44479540-f297-41b2-9af7-d131d5f0458a # Rootfs type UUID\n        guid = fcc205c8-2f1c-4dcd-bef4-7b209aa15cca # Another uuidgen'd UUID\n        name = \"rootfs.ext2\"\n        flags = 0xc000000000000\n        boot = true\n    }\n}\n```\n\nSee the GPT partition entry header specification for what `partition` fields\nmean and how to use them. Of the fields, `name`, `flags`, and `boot` are\noptional and default to an empty string, zero, or false. The `flags` field holds\nthe integer value of the partition attribute field. It is a 64-bit number. Bit\n2, the legacy BIOS bootable flag, can also be set by specifying `boot = true`.\nBoth `boot` and `flags` can be specified in a `partition` block.\n\nCall `gpt_write` to tell `fwup` to write the protective MBR and primary and\nbackup GPT tables.\n\n## U-Boot environment\n\nFor systems using the U-Boot bootloader, some support is included for modifying\nU-Boot environment blocks. In order to take advantage of this, you must declare\na `uboot-environment` section at the top level that describes how the\nenvironment block:\n\n```conf\nuboot-environment my_uboot_env {\n    block-offset = 2048\n    block-count = 16\n}\n```\n\nTo use the redundant environment block style, add block-offset-redund with the\naddress where the redundant copy is located:\n\n```conf\nuboot-environment my_uboot_env {\n    block-offset = 2048\n    block-count = 16\n    block-offset-redund = 2064\n}\n```\n\nSee the functions in the task section for getting and setting U-Boot variables.\n\nNOTE: Currently, I've only implemented support for U-Boot environments that I\nuse. Notably, this doesn't support big endian targets, and writes to raw NAND\nparts. Please consider contributing back support for these if you use them.\n\nIf `fwup`'s U-Boot support does not meet your needs, it is always possible to\ncreate environment images using the `mkenvimage` utility and `raw_write` them to\nthe proper locations. This is probably more appropriate when setting lots of\nvariables.\n\n## task\n\nThe `task` section specifies a firmware update task. These sections are the\nmain part of the firmware update archive since they describe the conditions\nupon which an update is applied and the steps to apply the update. Each `task`\nsection must have a unique name, but when searching for a task, the firmware\nupdate tool only does a prefix match. This lets you define multiple tasks that\ncan be evaluated based on conditions on the target hardware. The first matching\ntask is the one that gets applied. This can be useful if the upgrade process is\ndifferent based on the version of firmware currently on the target, the target\narchitecture, etc. The following table lists the supported constraints:\n\nConstraint                                         | Min fwup version | Description\n---------------------------------------------------|------------------|------------\nrequire-fat-file-exists(block_offset, filename)    | 0.7.0 | Require that a file exists in the specified FAT filesystem\nrequire-fat-file-match(block_offset, filename, pattern) | 0.14.0 | Require that filename exists and that pattern matches bytes inside of the file\nrequire-partition-offset(partition, block_offset)  | 0.7.0 | Require that the block offset of a partition be the specified value\nrequire-path-on-device(path, device)               | 0.13.0 | Require that the specified path (e.g., \"/\") is on the specified partition device (e.g., \"/dev/mmcblk0p1\")\nrequire-path-at-offset(path, offset)               | 0.19.0 | Require that the specified path (e.g., \"/\") is at the specified block offset (e.g., 1024). Combine with require-path-on-device.\nrequire-uboot-variable(my_uboot_env, varname, value) | 0.10.0 | Require that a variable is set to the specified value in the U-Boot environment\n\nThe remainder of the `task` section is a list of event handlers. Event handlers\nare organized as scopes. An event handler matches during the application of a\nfirmware update when an event occurs. Events include initialization,\ncompletion, errors, and files being decompressed from the archive. Since\narchives are processed in a streaming manner, the order of events is\ndeterministic based on the order that files were added to the archive. If it is\nimportant that one event happen before another, make sure that `file-resource`\nsections are specified in the desired order. The following table lists\nsupported events:\n\nEvent                         | Description\n------------------------------|------------\non-init                       | First event sent when the task is applied\non-finish                     | Final event sent assuming no errors are detected during event processing\non-error                      | Sent if an error occurs so that intermediate files can be cleaned up\non-resource \u0026lt;resource name\u003e   | Sent as events occur. Currently, this is sent as `file-resources` are processed from the archive.\n\nThe event scopes contain a list of actions. Actions can format file systems, copy files to file systems or\nwrite to raw locations on the destination.\n\nAction                                  | Min fwup version | Description\n----------------------------------------|------------------|------------\nerror(message)                          | 0.12.0 | Immediately fail a firmware update with an error\nexecute(command)                        | 0.16.0 | Execute a command on the host. Requires the `--unsafe` flag\nfat_attrib(block_offset, filename, attrib) | 0.1.0 | Modify a file's attributes. attrib is a string like \"RHS\" where R=readonly, H=hidden, S=system\nfat_cp(block_offset, from, to)          | 0.3.0 | Copy a file on one partition\nfat_cp(from_offset, from, to_offset, to) | 1.12.0 | Copy a file between partitions\nfat_mkdir(block_offset, filename)       | 0.2.0 | Create a directory on a FAT file system. This also succeeds if the directory already exists.\nfat_mkfs(block_offset, block_count)     | 0.1.0 | Create a FAT file system at the specified block offset and count\nfat_mv(block_offset, oldname, newname)  | 0.1.0 | Rename the specified file on a FAT file system\nfat_mv!(block_offset, oldname, newname) | 0.14.0 | Rename the specified file even if newname already exists.\nfat_rm(block_offset, filename)          | 0.1.0 | Delete the specified file\nfat_setlabel(block_offset, label)       | 0.2.0 | Set the volume label on a FAT file system\nfat_touch(block_offset, filename)       | 0.7.0 | Create an empty file if the file doesn't exist (no timestamp update like on Linux)\nfat_write(block_offset, filename)       | 0.1.0 | Write the resource to the FAT file system at the specified block offset\nfat_write(block_offset)                 | 1.10.0 | Same as the two argument fat_write except the filename is the resource name. This is handled when creating the archive, so it's backwards compatible.\ngpt_write(gpt)                          | 1.4.0 | Write the specified GPT to the target\ninfo(message)                           | 0.13.0 | Print out an informational message\nmbr_write(mbr)                          | 0.1.0 | Write the specified mbr to the target\npath_write(destination_path)            | 0.16.0 | Write a resource to a path on the host. Requires the `--unsafe` flag. Passing `-d /dev/null` works if no destination image.\npipe_write(command)                     | 0.16.0 | Pipe a resource through a command on the host. Requires the `--unsafe` flag\nraw_memset(block_offset, block_count, value) | 0.10.0 | Write the specified byte value repeatedly for the specified blocks\nraw_write(block_offset, options)        | 0.1.0 | Write the resource to the specified block offset. Options include `cipher` and `secret`.\nreboot_param(args)                      | 1.12.0 | A string that will enqueued to the reboot command if supported\ntrim(block_offset, count)               | 0.15.0 | Discard any data previously written to the range. TRIM requests are issued to the device if --enable-trim is passed to fwup.\nuboot_clearenv(my_uboot_env)            | 0.10.0 | Initialize a clean, variable free U-boot environment\nuboot_recover(my_uboot_env)             | 0.15.0 | If the U-Boot environment is corrupt, reinitialize it. If not, then do nothing\nuboot_setenv(my_uboot_env, name, value) | 0.10.0 | Set the specified U-boot variable\nuboot_unsetenv(my_uboot_env, name)      | 0.10.0 | Unset the specified U-boot variable\n\n## Minimizing writes to the destination\n\nFwup tries to reduce the writes to the destination media using the following mechanisms:\n\n1. An internal block cache summarizes writes to individual blocks. This is\n   outside of OS caching that `fwup` tries to disable to ensure ordering of\n   writes. (i.e., switching the active filesystem needs to be after the\n   filesystem writes get made)\n2. The `--minimize-writes` option causes `fwup` to check if a block on the\n   destination has actually changed before writing it.\n\nThe motivation for these features is reduce the number of changes to the disk\nsince each change could be interrupted by a system reset or result in a write\nfailure or corruption. While the chance is remote, some users find peace of mind\nin the reduced write cycles on FLASH media.\n\nPerformance-wise, the internal block caching is almost improves update times.\nMinimizing writes incurs a block read operation before every write. As one would\nexpect, if the writes are actually redundant, it improves firmware update time.\nFor completely different content, it appears that the additional read does not\nnoticeably affect firmware update time on relatively fast embedded devices (600+\nMHz 4-core in testing). Slower single core devices have slightly longer firmware\nupdate times.\n\n## Delta firmware updates (BETA)\n\nThe purpose of delta firmware updates is to reduce firmware update file sizes\nand their associated costs by sending patches to devices. This requires that one\nknow what firmware is running on the device so that an appropriate patch can be\nmade. As with other features, `fwup` only addresses the firmware update file\nprocessing piece, but assists in this process by 1. uniquely identifying\nfirmware via UUIDs and 2. including\n[`xdelta3`](https://github.com/jmacd/xdelta/tree/release3_1_apl/xdelta3)\ndecompression support.\n\nThis feature is currently BETA, since it may change in backwards incompatible\nways based on trial deployments. If you're using this, please avoid deploying it\nto places that are hard to access just in case.\n\n`fwup` cannot produce delta `.fw` archives automatically. However, they are\neasy to produce manually or via a script. The deployments in progress use\nscripts to create patches for upgrading all possible firmware versions (keyed\noff UUID). Here's how:\n\n1. Decide which file resource is a good candidate for delta updates (you can\n   pick more than one). Call this `rootfs.img`.\n2. In the `fwup.conf`, in the `on-resource rootfs.img` handlers, specify where\n   the currently running rootfs.img contents exist. For example, if you're\n   doing an A/B upgrades, when you upgrade B, point to the A's rootfs for\n   source contents and vice versa for upgrading A. It will look something\n   like this:\n\n   ```txt\n   task upgrade.a {\n       on-resource rootfs.img {\n           delta-source-raw-offset=${ROOTFS_B_PART_OFFSET}\n           delta-source-raw-count=${ROOTFS_B_PART_COUNT}\n           # delta-source-raw-options=\"cipher=...,secret=...\" (if encrypted)\n           raw_write(${ROOTFS_A_PART_OFFSET})\n       }\n   }\n   task upgrade.b {\n       on-resource rootfs.img {\n           delta-source-raw-offset=${ROOTFS_A_PART_OFFSET}\n           delta-source-raw-count=${ROOTFS_A_PART_COUNT}\n           # delta-source-raw-options=\"cipher=...,secret=...\" (if encrypted)\n           raw_write(${ROOTFS_B_PART_OFFSET})\n       }\n   }\n   ```\n\n3. Obtain the firmware file for the software running on the device. Call this\n   `original.fw`.\n4. Create the firmware file for the new software. Call this `update.fw`. If\n   you normally sign your firmware files, you can sign it now or sign the\n   resulting patch file depending on what works best for your release process.\n5. Run `unzip` on both `original.fw` and `update.fw` in different directories.\n   The `rootfs.img` file can be found under the `data` directory when unzipped.\n6. `mkdir -p my_patch/data`\n7. `xdelta3 -A -S -f -s original/data/rootfs.img update/data/rootfs.img my_patch/data/rootfs.img`\n8. `cp update.fw my_patch/delta_update.fw`\n9. `cd my_patch \u0026\u0026 zip delta_update.fw data/rootfs.img`\n\nThe `delta_update.fw` will have the patch for the `rootfs.img` and should be\nquite a bit smaller. If not, check that your before and after `rootfs.img` files\ndon't have a lot of timestamp changes or are already so compressed that the\ndeltas propagate through the entire image.\n\nEven though you're manually modifying the `.fw` file, `fwup` stills provide\nguarantees on the final bits installed on the device. For one, `fwup` computes\nBlake 2B hashes on the final contents of the files, so what matters is what\ncomes out of the `xdelta3` decompressor and not what is stored in the archive.\nMost likely though, `xdelta3` will detect corruption since it checks Adler32\nchecksums as it decompresses.\n\n### Delta update on-resource source settings\n\nWhere to find the source (\"before\" version) is always specified in `on-resource`\nblocks. It can either be source from raw bytes or from files in a FAT-formatted\npartition. Only one source is supported in each `on-resource` block.\n\nKey                               | Description\n----------------------------------|------------\ndelta-source-raw-offset           | Set to the starting block offset\ndelta-source-raw-count            | Set to the number of blocks in the source region. No reads will be allowed outside of this area.\ndelta-source-raw-options          | Set source encryption options (\u003e= 1.13.0)\ndelta-source-fat-offset           | Set to the starting block offset of the source FAT partition\ndelta-source-fat-path             | Set to the path inside the FAT partition of the source file\n\n## Sparse files\n\nSparse files are files with gaps in them that are only represented on the\nfilesystem in metadata. Not all filesystems support sparse files, but in\ngeneral, Linux has good support. Creating a sparse file is easy: just seek to a\nlocation past the end of file and write some data. The gap is \"stored\" as a\nhole in the filesystem metadata. Data is read back from the hole as zeros. Data\nand holes are restricted to start and end on filesystem block boundaries, so\nsmall gaps may be filled in with zeros rather than being stored as a hole.\n\nWhy is this important? If you're using `fwup` to write a large EXT2 partition,\nyou'll find that it contains many gaps. It would be better to just write the\nEXT2 data and metadata without filling in all of the unused space. Sparse file\nsupport in `fwup` lets you do that. Since EXT2 filesystems legitimately contain\nlong runs of zeros that must be written to Flash, `fwup` queries the filesystem\ncontaining the EXT2 data to find the gaps. Other tools like `dd(1)` only look\nfor runs of zeros so their sparse file support cannot be used to emulate this.\nYou may see warnings about copying sparse files to Flash and it has to do with\ntools not writing long runs of zeros. The consequence of `fwup` querying the\nfilesystem for holes is that this feature only works when firmware update\narchives are created on operating systems and filesystems that support it. Of\ncourse, firmware updates can be applied on systems without support for querying\nholes in files. Those systems also benefit from not having to write as much to\nFlash devices. If you instead apply a firmware update to a normal file, though,\nthe OS will likely fill in the gaps with zeros and thus offer no improvement.\n\nThere is one VERY important caveat with the sparse file handling: Some zeros in\nfiles are important and some are not. If runs in zeros in a file are important\nand they are written to a file as a \"hole\", `fwup` will not write them back.\nThis is catastrophic if the zeros represent things like free blocks on a\nfilesystem. Luckily, the file system formatting utilities write the important\nzeros to the disk and the OS does not scan bytes to see which ones are runs on\nzeros and automatically create holes. Programs like `dd(1)` can do this, though,\nso it is crucial that you do not run files through `dd` to make then sparser\nbefore passing them to `fwup`.\n\nThis feature is off by default. To turn this feature on, set `skip-holes` on\nthe resource to `true`:\n\n```conf\nfile-resource rootfs.img {\n        host-path = \"output/images/rootfs.img\"\n        skip-holes = true\n}\n```\n\n## Disk encryption\n\nThe `raw_write` function has limited support for disk encryption that's\ncompatible with the Linux `dm-crypt` kernel driver. This makes it possible to\nwrite filesystem data in a way that's unreadable at rest. Caveats are in order:\n\n1. `fwup` does not address handling of secret keys and improper handling can\n   easily compromise the benefits of encryption\n2. The `.fw` archive is not encrypted. This mechanism assumes that the secrecy\n   of the archive is protected by other means. Of course, it is possible\n   pre-encrypt the data in the archive, but then you can't have device-specific\n   secret keys.\n3. Only the simplest `dm-crypt` cipher is currently supported (\"aes-cbc-plain\").\n   This has known deficiencies. PRs for other modes that can be incorporated\n   under `fwup`'s Apache License would be appreciated\n\nVarious tutorials exist on the Internet for creating encrypted filesystems and\nmounting filesystems using `dm-crypt`. `fwup` is much simpler. It takes a block\nof bytes to write, encrypts it, and writes it to the destination. For example,\nif you have a SquashFS-formatted filesystem that you want written encrypted to a\ndisk, you'd have this fragment:\n\n```conf\non-resource fs.squashfs {\n    raw_write(${PARTITION_START}, \"cipher=aes-cbc-plain\", \"secret=\\${SECRET_KEY}\")\n}\n```\n\nIn the above example, the `SECRET_KEY` is expected to come from an environment\nvariable being set on the device when applying the firmware update. You could,\nof course, hard-code the secret key in the configuration file to test things\nout. The key is hex-encoded.\n\nThen, on the device, mount the SquashFS partition but use `dm-crypt`. The\nprocess will look something like this:\n\n```sh\nlosetup /dev/loop0 /dev/mmcblk0p2\ncryptsetup open --type=plain --cipher=aes-cbc-plain --key-file=key.txt /dev/loop0 my-filesystem\nmount /dev/mapper/my-filesystem /mnt\n```\n\nYou will likely need to replace many of the arguments above with ones\nappropriate for your system.\n\nIf you're combining this with delta firmware updates, see\n`delta-source-raw-options` for reading encrypted source data.\n\n## Reproducible builds\n\nIt's possible for the system time to be saved in various places when using\n`fwup`. This means that an archive with the same contents, but built at\ndifferent times results in `.fw` files with different bytes. See\n[reproducible-builds.org](https://reproducible-builds.org/) for a discussion on\nthis topic.\n\n`fwup` obeys the\n[`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/)\n environment variable and will force all timestamps to the value of that\nvariable when needed. Set `$SOURCE_DATE_EPOCH` to the number of seconds since\nmidnight Jan 1, 1970 (run `date +%s`) to use this feature.\n\nA better way of comparing `.fw` archives, though, is to use the firmware UUID.\nThe firmware UUID is computed from the contents of the archive rather than the\nbit-for-bit representation of the `.fw` file, itself. The firmware UUID is\nunaffected by timestamps (with or without `SOURCE_DATE_EPOCH`) or other things\nlike compression algorithm improvements. This is not to say that\n`SOURCE_DATE_EPOCH` is not important, but that the UUID is an additional tool\nfor ensuring that firmware updates are reproducible.\n\n# Firmware authentication\n\nFirmware archives can be authenticated using a simple public/private key\nscheme. To get started, create a public/private key pair by invoking `fwup -g`.\nThe algorithm used is [Ed25519](http://ed25519.cr.yp.to/). This generates two\nfile: `fwup-key.pub` and `fwup-key.priv`. It is critical to keep the signing\nkey, `fwup-key.priv` secret.\n\nTo sign an archive, pass `-s fwup-key.priv` to fwup when creating the firmware.\nThe other option is to sign the firmware archive after creation with `--sign`\nor `-S`.\n\nTo verify that an archive has been signed, pass `-p fwup-key.pub` on the\ncommand line to any of the commands that read the archive. E.g., `-a`, `-l` or\n`-m`.\n\nIt is important to understand how verification works so that the security of\nthe archive isn't compromised. Firmware updates are applied in one pass to\navoid needing a lot of memory or disk space. The consequence of this is that\nverification is done on the fly. The main metadata for the archive is always\nverified before any operations occur. Cryptographic hashes (using the\n[BLAKE2b-256](https://blake2.net/) algorithm) of each file contained in the\narchive is stored in the metadata. The hash for each file is computed on the\nfly, so a compromised file may not be detected until it has been written to\nFlash. Since this is obviously bad, the strategy for creating firmware updates\nis to write them to an unused location first and then switch over at the last\npossible minute. This is desirable to do anyway, since this strategy also\nprovides some protection against the user disconnecting power midway through\nthe firmware update.\n\n# Reboot parameters\n\nThe Linux reboot syscall takes an argument when using the\n`LINUX_REBOOT_CMD_RESTART2` command.  This is not the shutdown timeout. It's a\nfree text string passed to the Linux kernel.  It's use is device and\ndriver-specific. The Raspberry Pi uses it for its TRYBOOT feature. Fwup allows\nconfigurations to set the reboot parameters via the `reboot_param` function:\n\n```conf\nreboot_param(\"0 tryboot\")\n```\n\nEven though `fwup` doesn't reboot the system, it can influence the parameter\npassed to the kernel due to the simplistic way this parameter is passed to the\n`init` process (PID 1).  Systemd, for example, reads the\n`/run/systemd/reboot-param` file when `init` initiates the reboot.  Erlinit with\nNerves uses the file `/run/reboot-param`. Fwup will automatically detect Systemd\nand write the right file. The `--reboot-param-path` commandline argument can be\nused to force a path.\n\n# Integration with applications\n\nIt is expected that many users will want to integrate `fwup` with their\napplications. Many operations can be accomplished by just invoking the `fwup`\nexecutable and parsing the text written to `stdout`. When applying firmware\nprogress updates are delivered based on commandline options:\n\n  1. Human readable - This is the default. Progress is updated from the text `0%` to `100%`.\n  1. Numeric (`-n`) - Progress is printed as `0\\n` to `100\\n`\n  1. Quiet (`-q`) - No progress is printed\n\nWhile the above works well for scripts and when errors can be seen by the\noperator, `fwup` supports a structured use of `stdin`/`stdout` as well. Specify\nthe `--framing` option to any of the commands to use this option.\n\nThe framing feature is influenced by the Erlang VM's port API and should be\nrelatively easy to integrate with non-Erlang VM languages. The framing works\naround deficiencies in the built-in interprocess communication. For example, by\nenabling framing, a program can stream a firmware update through `fwup's`\n`stdin` without needing to close its `stdout` to signal end of file. Another\nfeature aided by framing is knowing what text goes together and whether the\ntext is part of an error message or not. Exit status is still an indicator of\nsuccess or failure, but the controlling application doesn't need to wait for\nthe program to exit to know what happened.\n\nIn `--framing` mode, all communication with `fwup` is done in packets (rather\nthan byte streams). A packet starts with a 4 byte length field. The length is a\nbig endian (network byte order) unsigned integer. A zero-length packet (i.e., 4\nbytes of zeros) signals end of input.\n\nField          | Size         | Description\n---------------|--------------|-------------\nLength         | 4 bytes      | Packet length as a big endian integer\nData           | Length bytes | Payload\n\nInput and output packets have different formats. For sending input to `fwup`\n(like when streaming a `.fw` file using stdio), the input bytes should be\nframed into packets however is most convenient. For example, if bytes are\nreceived in 4K chunks, then they can be sent to `fwup` in 4K packets with a\nzero-length packet at the end. The packets need not be the same size.\n\nAll output packets from `fwup` have a 2 byte type field at the beginning of the\npacket:\n\nField          | Size           | Description\n---------------|----------------|-------------\nLength         | 4 bytes        | Packet length as a big endian integer\nType           | 2 bytes        | See below\nData           | Length-2 bytes | Payload\n\nThe following types are defined:\n\nType           | 2 byte value | Description\n---------------|--------------|------------\nSuccess        | \"OK\"         | The command was executed successfully. The payload is a 2 bytes result code (currently 0 for success) followed by an optional textual message.\nError          | \"ER\"         | A failure occurred. The payload is a 2 byte error code (future use) followed by a textual error message.\nInfo           | \"WN\"         | Output from info() calls and rare non-fatal fwup warnings. The payload is a 2 byte code (future use) followed by a textual message.\nProgress       | \"PR\"         | The next two bytes are the progress (0-100) as a big endian integer.\n\nA related option is `--exit-handshake`. This option was specifically implemented\nfor Erlang to support integration with its port process feature. It may be\nuseful for other integrations where it's more convenient to wait for a final\ncharacter coming from a subprocess rather than watching for an exit. The\nproblem with Erlang is that it's easy for the message that the process exited to\nbeat the final characters coming out stdout. When this option is enabled, `fwup`\nexpects the calling process to close `stdin` when it's ready for `fwup` to exit.\n\n# FAQ\n\n## How do I include a file in the archive that isn't used by fwup\n\nThe scenario is that you need to store some metadata or some other information\nthat is useful to another program. For example, you'd like to include some\ndocumentation or other notes inside the firmware update archive that the\ndestination device can pull out and present on a UI. To do this, just add\n`file-resource` blocks for each file. These blocks don't need to be referenced\nby an `on-resource` block.\n\n## How do I include the firmware version in the archive\n\nIf you are using git, you can invoke `fwup` as follows:\n\n```sh\nGITDESCRIBE=`git describe` fwup -c -f myupdate.conf -o out.fw\n```\n\nThen in `myupdate.conf` add the line:\n\n```conf\nmeta-version = \"${GITDESCRIBE}\"\n```\n\nOn the target device, you can retrieve the version by using `-m`. For example:\n\n```sh\n$ fwup -m -i out.fw\nmeta-product = \"Awesome product\"\nmeta-description = \"A description\"\nmeta-version = \"v0.0.1\"\nmeta-author = \"Me\"\nmeta-platform = \"bbb\"\nmeta-architecture = \"arm\"\nmeta-uuid=\"07a34e75-b7ea-5ed8-b5d9-80c10daf4939\"\n```\n\n## What's something cool that you can do with fwup\n\nOk, this isn't really a FAQ, but for some reason people think this is cool. Many\nsystems that I work on are network connected with ssh. Sometimes I update them\nby doing this:\n\n```sh\n$ cat mysoftware.fw | ssh root@192.168.1.20 \\\n    'fwup -a -U -d /dev/mmcblk2 -t upgrade \u0026\u0026 reboot'\n```\n\nThe ability to pipe software updates through `fwup` comes in handy. This has\nalso gotten me out of situations where, for whatever reason, I no longer had enough\nspace to store the update on the device.\n\n## How should I implement multiple signing keys\n\nThere are several use cases where it's necessary to have multiple signing keys\nin use at a time. For example, you could want to enforce that all firmware\nupdates are signed in your infrastructure, but not force everyone to go though\nthe official secure path for QA builds. You may also want to only use each key\nfor a limited amount of signings.\n\nCurrently, each firmware file can only have one signature. However, the\nverifier (device) can specify multiple public keys (repeated -p options). While\nit is possible to call `fwup` for each key, specifying multiples keys is\nrecommended to run the verification through fwup to support streamed updates\nand also to simplify this critical code path.\n\n## How do I debug\n\nI apply updates to regular files on my laptop (as opposed to eMMC or SDCards)\nand examine them with a hex editor. A few other routes might be useful too:\n\n1. Unzip the .fw file to inspect the contents. It's a regular ZIP file and\n   the `meta.conf` file inside it is the stripped down view of what your\n   configuration looks like after variable substitution, etc.\n1. Add the `error()` function to do printf-style debugging.\n1. Find an image that works and skip updating some sections. For example,\n   some processors are very picky on the MBR contents and it's easier to\n   get everything else working before tackling partition constraints.\n\n## How do I specify the root partition in Linux\n\nThere are a few options. Most people can specify `root=/dev/mmcblk0p1` or\n`root=/dev/sda1` or something similar on the kernel commandline and everything\nwill work out fine. On systems with multiple drives and an unpredictable boot\norder, you can specify `root=PARTUUID=01234567-01` where the `-01` part\ncorresponds to the 1-based partition index and `01234567` is any signature. In\nyour `fwup.conf` file's MBR block, specify `signature = 0x01234567`. A third\noption is to use an initramfs and not worry about any of this.\n\n## How do I get the best performance\n\nIn general, `fwup` writes to Flash memory in large blocks so that the update\ncan occur quickly. Obviously, reducing the amount that needs to get written\nalways helps. Beyond that, most optimizations are platform dependent. Linux\ncaches writes so aggressively that writes to Flash memory are nearly as fast as\npossible. OSX, on the other hand, does very little caching, so doing things\nlike only working with one FAT filesystem at a time can help. In this case,\n`fwup` only caches writes to one FAT filesystem at a time, so mixing them will\nflush caches. OSX is also slow to unmount disks, so keep in mind that\nperformance can only be so fast on some systems.\n\n## How do I update /dev/mmcblock0boot0\n\nThe special eMMC boot partitions are updatable the same way as the main\npartition. When I create .fw files for manufacturing, I create two targets, a\n`complete` target that updates the main eMMC and a `bootloader` target that\nupdates `mmcblock0boot0`. The manufacturing script runs `fwup` twice: once for\nthe `complete` target and then again for the `bootloader` target.\n\nAlso, the `/dev/mmcblock0boot0` device is forced read-only by the kernel. To\nunlock it, run:\n\n```sh\necho 0 \u003e /sys/block/mmcblk0boot0/force_ro\n```\n\n## What's the best way to identify firmware versions\n\nfwup supports several ways:\n\n1. Store the version in `meta-version`. This is usually the friendliest for\n   end users.\n2. Store the `git` hash in `meta-vcs-identifier`. This is good for developers.\n3. Use the `fwup`-computed UUID that's available in `meta-uuid' and\n   `${FWUP_META_UUID}`.\n\nOf these, the third one is always available since version fwup `v1.2.1`. The\nmotivation behind it was to unambiguously know whether installed firmware\nmatches the desired firmware. Since it is computed, `.fw` files generated with\nprevious versions of fwup have UUIDs.\n\nThe first two options require the versions to be added to the `fwup.conf` file.\nThey are usually added using environment variables so that the version numbers\nare not hardcoded.\n\n## How do I get the firmware metadata formatted as JSON\n\nUse `jq`!\n\n```sh\n$ fwup -m -i $FW_FILE | jq -n -R 'reduce inputs as $i ({}; . + ($i | (match(\"([^=]*)=\\\"(.*)\\\"\") | .captures | {(.[0].string) : .[1].string})))'\n{\n  \"meta-product\": \"My Awesome Product\",\n  \"meta-version\": \"0.1.0\",\n  \"meta-author\": \"All of us\",\n  \"meta-platform\": \"imx6\",\n  \"meta-architecture\": \"arm\",\n  \"meta-creation-date\": \"2018-11-07T14:46:38Z\",\n  \"meta-uuid\": \"7add3c6d-230c-5bf1-77ec-5f785e91be40\"\n}\n```\n\n## How do I use \"raw\" NAND Flash\n\nSome \"raw\" NAND Flash requires a wear leveling layer such as UBI.  See\nthe [UBI Example fwup.conf](docs/ubi_example/fwup.conf) for how to integrate\nfwup with the [UBI toolchain](http://www.linux-mtd.infradead.org/doc/ubi.html).\n\n## How do you pronounce fwup\n\nI used to pronounce it \"eff-double-you-up\", but then coworkers and others\nstarted calling it \"fwup\" (one syllable) and \"fwup-dates\" when referring to the\n`.fw` files. I now use the one syllable version. This has caused issues in the\ndocumentation where \"an\" is used rather than \"a\". Feel free to send PRs.\n\n# Licenses\n\nThis utility contains source code with various licenses. The bulk of the code is\nlicensed with the Apache-2.0 license which can be found in the `LICENSE` file.\n\nAll statically-linked 3rd party source code can be found in the `src/3rdparty`\ndirectory. The following sections summarize the included code licenses.\n\n## base64.c\n\nPublic Domain or Creative Commons CC0. See file for explanatory text.\n\n## FatFS\n\nThe FAT filesystem code (FatFs) comes from [elm-chan.org](http://elm-chan.org/fsw/ff/00index_e.html)\nand has the following license:\n\n\u003e\u003e\u003e\nFatFs module is a generic FAT file system module for small embedded systems.\nThis is a free software that opened for education, research and commercial\ndevelopments under license policy of following terms.\n\n Copyright (C) 2015, ChaN, all right reserved.\n\n* The FatFs module is a free software and there is NO WARRANTY.\n* No restriction on use. You can use, modify and redistribute it for\n  personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.\n* Redistributions of source code must retain the above copyright notice.\n\u003e\u003e\u003e\n\n## Monocypher\n\nThis package has been dual licensed under the 2-clause BSD and CC-0. See\n[LICENCE.md](src/3rdparty/monocypher-3.0.0/LICENCE.md).\n\n## semver.c\n\n`fwup` uses [semver.c](https://github.com/h2non/semver.c) for checking versions.\n`semver.c` is Copyright (c) Tomás Aparicio and distributed under the MIT\nLicense. See [LICENSE](src/3rdparty/semver.c/LICENSE).\n\n## strptime.c\n\nOn systems without the function strptime(), a version from Google is\nincluded that is distributed under the Apache 2.0 license.\n\n## Tiny AES\n\nThis code was released into the public domain. See\n[unlicense.txt](src/3rdparty/tiny-AES-c/unlicense.txt) for details.\n\n## Xdelta3\n\nOnly the xdelta3 decompressor is currently used by `fwup` so most of the code in\nthe `xdelta3` directory is ignored or disabled. Importantly, `fwup`'s use of\nxdelta3 does not bring in any xdelta3 dependencies. Xdelta3 is covered by the\nApache-2.0 license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffwup-home%2Ffwup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffwup-home%2Ffwup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffwup-home%2Ffwup/lists"}