Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ jobs:
- name: Install cargo-binutils
run: cargo install cargo-binutils --locked

# `ota/tests/apply.rs` builds its FAT32 volumes with `mkfs.vfat` and
# judges the result with `fsck.vfat`. Testing a filesystem writer
# against another implementation of the same filesystem is the whole
# point — a check written here could only agree with the code it is
# checking — so these tests fail rather than skip without the tools,
# and CI has to supply them.
- name: Install dosfstools
run: sudo apt-get update && sudo apt-get install -y dosfstools

- uses: Swatinem/rust-cache@v2
with:
workspaces: |
Expand Down
42 changes: 42 additions & 0 deletions ota/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,48 @@ wire protocol; this is a library, its consumers are firmware projects in
other repositories, and tying it to that version would bump their
dependency every time a command-line flag was renamed.

## [Unreleased]

### Added

- **`apply`**, behind the feature of the same name: the other half of an
update. It takes a validated bundle and a `resident-fat` volume and
writes every entry where its path says, in an order the crate imposes
rather than one the bundle chooses — ordinary files, then Raspberry Pi
firmware, then `config.txt`, then the kernel. The kernel is last
because while a board has one boot image that write *is* the commit, so
everything that could fail has to have failed already.

Nested destinations are created as needed, since a bundle can carry a
path and `write_file` resolves a parent rather than making one.

- **Entries the card already holds are read and not rewritten.** The same
function answers both halves of the question — before a write it
decides whether to write at all, and after one it *is* the
verification — so a skipped entry is checked exactly as strictly as a
written one. A bundle carrying the Raspberry Pi firmware carries about
3 MB of it, and that changes roughly once a year; on hardware, applying
an unchanged bundle now costs 1055 ms and **no card writes at all**,
against 3724 ms to write the same thing in full.

- **`Progress`**, which is how timing stays with the caller. Every method
defaults to doing nothing and `()` implements the whole trait, so a
caller names only what it wants. The boundaries separate the write from
the read-back deliberately: those are not the same operation and do not
have the same fix, so one figure covering both would hide which of them
an improvement had touched.

- **`Checksum`**, the streaming form of `checksum`, for checking a file
already on a card against a fixed scratch buffer rather than a second
copy of it in memory.

### Changed

- `tests/` is no longer published. The suite builds its FAT32 volumes
with `mkfs.vfat` and judges them with `fsck.vfat`, so it fails rather
than skips without `dosfstools` — a suite that cannot run from the
tarball it ships in says nothing about the crate.

## [0.1.0] - 2026-09-01

First release.
Expand Down
27 changes: 22 additions & 5 deletions ota/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@ categories = ["embedded", "no-std", "filesystem"]
# this that any configuration of this package could build on.
rust-version = "1.85"

# Repository-only. `rust-toolchain.toml` exists so `make clippy-ota`'s bare
# metal pass works from a fresh checkout; rustup reads that file from the
# directory a command runs in, never from a dependency's source, so a copy
# inside a downloaded crate does nothing but take up space.
exclude = ["rust-toolchain.toml"]
# Repository-only, and shipping either would be worse than leaving it out.
#
# `rust-toolchain.toml` exists so `make clippy-ota`'s bare metal pass works
# from a fresh checkout; rustup reads that file from the directory a command
# runs in, never from a dependency's source, so a copy inside a downloaded
# crate does nothing but take up space.
#
# `tests/` is the entry worth explaining, because shipping a test suite is
# usually a kindness. Here it would be a broken one: `tests/apply.rs` builds
# its FAT32 volumes with `mkfs.vfat` and judges them with `fsck.vfat`, so it
# fails rather than skips on a machine without dosfstools. A suite that
# cannot run from the tarball it ships in is worse than no suite, because a
# red result there says nothing about this crate. The place to run these is
# the repository, where CI installs the tools.
exclude = ["rust-toolchain.toml", "tests/"]

# Unlike the CLI beside it, this package is versioned on its own. It is a
# library whose consumers are firmware projects in other repositories, and
Expand Down Expand Up @@ -53,6 +63,13 @@ crc = { version = "3", default-features = false }
# opt-in there -- so nothing needs turning off.
resident-fat = { version = "0.1.0", optional = true }

[dev-dependencies]
# Named again, though the `apply` feature already pulls it in, because the
# integration tests implement `BlockDevice` and mount a volume themselves —
# they are a consumer of it in their own right, not merely a consumer of
# this crate. No `mbr`: the tests mount a whole device, not a partition.
resident-fat = "0.1.0"

[package.metadata.docs.rs]
# Document the apply half as well as the format; without this, docs.rs
# builds the default features and the `apply` module is simply absent from
Expand Down
Loading