Skip to content

os_update: address GPT partitions by PARTLABEL, use layout offsets only without one - #24

Open
mobileoverlord wants to merge 4 commits into
mainfrom
jschneck/partlabel-first
Open

os_update: address GPT partitions by PARTLABEL, use layout offsets only without one#24
mobileoverlord wants to merge 4 commits into
mainfrom
jschneck/partlabel-first

Conversation

@mobileoverlord

@mobileoverlord mobileoverlord commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

avocado deploy to an imx8mp-evk failed in os_update:

Writing rootfs_hash -> /dev/mmcblk1@618659840 (partition: rootfs-b-hash)
Failed to open device /dev/mmcblk1 for rootfs_hash: No such file or directory

What

When the bundle carries a layout (stone always emits one), artifacts were written by raw offset to the layout's device path even though /dev/disk/by-partlabel/<name> existed. The manifest's devpath is kernel-dependent (the eMMC is mmcblk1 in the manifest, mmcblk2 under 6.18), and computed offsets need not match where fwup placed the partitions.

How

locate_target resolves the PARTLABEL first. The layout offset is used only when the disk carries no labels at all (MBR layouts). A labeled disk that lacks this one label is a layout mismatch — a device provisioned before the partition existed — and is refused with a message saying to reprovision, rather than written at a computed offset onto whatever is there now. Both the staged and the streaming write paths use it.

Tests: unlabeled + layout → offset; unlabeled without layout → error.

https://claude.ai/code/session_01S75qGgqVA2cQUsZdW46qUz

@mobileoverlord
mobileoverlord force-pushed the jschneck/partlabel-first branch 2 times, most recently from 4a3c410 to 8983878 Compare August 27, 2026 18:59
…ly without one

A bundle's layout (devpath + computed offsets) was preferred over the
partition's PARTLABEL whenever the bundle carried one, so a GPT artifact was
written by raw offset to the manifest's devpath. On i.MX that device name is
kernel-dependent (/dev/mmcblk1 in the manifest, /dev/mmcblk2 under 6.18), and
the computed offsets need not match where the flasher placed the partitions:

  Writing rootfs_hash -> /dev/mmcblk1@618659840 (partition: rootfs-b-hash)
  Failed to open device /dev/mmcblk1 for rootfs_hash: No such file or directory

Resolve /dev/disk/by-partlabel/<name> first and fall back to the layout only
when udev has no label for the partition (MBR layouts). Both the staged and
the streaming write paths go through the same locate_target.
@mobileoverlord
mobileoverlord force-pushed the jschneck/partlabel-first branch from 8983878 to 6e15f73 Compare August 27, 2026 19:00
@mobileoverlord

Copy link
Copy Markdown
Contributor Author

Follow-up (stone has issues disabled, noting here): stone bundle always embeds layout, so an avocadoctl older than this PR still takes the offset path on GPT machines. Emitting layout only for label-less/MBR layouts would let old readers fall back to PARTLABEL on their own; manifests carry no GPT/MBR marker today, so that needs a manifest-level signal.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes os_update device targeting by preferring kernel-resolved GPT PARTLABEL paths (/dev/disk/by-partlabel/...) over bundle layout device paths/offsets, and only using layout offsets as a fallback when PARTLABELs aren’t available.

Changes:

  • Introduces locate_target/WriteTarget to route writes to either a resolved partition path (preferred) or a raw device + byte offset (fallback).
  • Applies the same targeting logic to both staged (apply_os_update) and streaming (apply_os_update_streaming) write paths.
  • Adds unit tests covering the “no PARTLABEL + layout -> offset” and “no PARTLABEL + no layout -> error” behaviors.
Suppressed comments (1)

src/os_update.rs:1576

  • Same flakiness concern as the test above: use a deliberately-unique PARTLABEL so the test result doesn’t depend on the host’s attached disks.
    #[test]
    fn an_unlabeled_partition_without_a_layout_is_an_error() {
        assert!(locate_target("avocadoctl-test-no-such-label", None).is_err());
    }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/os_update.rs Outdated
Comment thread src/os_update.rs
…ue test label

A label that exists but fails to resolve (dangling symlink, EACCES, I/O
error) is surfaced as the error it is rather than treated as absent, so a
transient failure can never turn into a computed-offset write. The tests
derive their nonexistent label from the pid so attached media cannot collide
with it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

src/os_update.rs:594

  • Path::exists() follows symlinks and returns false on metadata errors, so a dangling PARTLABEL or an EACCES/I/O failure still reaches the raw-offset fallback. This contradicts the fail-safe behavior described above and can write to the wrong location. Inspect the directory entry without following it, and only treat a definite NotFound as absence.
    if label.exists() {

Comment thread src/os_update.rs Outdated
Path::exists() answers false for a dangling symlink or a metadata error, so
both the label check in locate_target and the neighbour scan in
disk_has_any_label could misclassify a labeled disk as unlabeled and enable
raw offset writes. One helper, label_absent, now decides via symlink_metadata
and only a clean NotFound counts; every other outcome is surfaced by
resolve_partition or treated as evidence the disk is labeled.
test_mode_redirects_os_releases_to_tmpdir set TMPDIR=/scratch under
ENV_VAR_MUTEX, but tests in other modules call TempDir::new() without that
lock and read TMPDIR while it is set; whichever one raced into the window
failed on NotFound (seen as config::tests::test_load_invalid_toml in CI).
Use a real, kept directory so a racing TempDir still has somewhere to go.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants