Skip to content

systemd: lock disks while running filesystem checks - #18653

Draft
Pawel Winogrodzki (PawelWMS) wants to merge 1 commit into
microsoft:3.0-devfrom
PawelWMS:pawelwi/systemd-fsck-whole-disk-lock
Draft

systemd: lock disks while running filesystem checks#18653
Pawel Winogrodzki (PawelWMS) wants to merge 1 commit into
microsoft:3.0-devfrom
PawelWMS:pawelwi/systemd-fsck-whole-disk-lock

Conversation

@PawelWMS

@PawelWMS Pawel Winogrodzki (PawelWMS) commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Alternative implementation

Important

This change is a systemd-owned alternative to the proposed upstream e2fsck: take flock(LOCK_EX) on whole-disk device during filesystem check patch.

Do not ship both implementations. systemd-fsck retains its exclusive lock while waiting for child e2fsck; native e2fsck locking would block acquiring a second independently-opened lock on the same disk, deadlocking boot.

What

  • Add an Azure Linux patch that acquires the exclusive whole-disk BSD lock inside systemd-fsck.
  • Retain the lock in the parent process while child fsck checks the filesystem.
  • Release the lock after the child exits.
  • Keep systemd and systemd-boot-signed releases synchronized at 255-35.

Why

The root filesystem check can update ext4 metadata while systemd-udevd probes the same disk. If udev observes the primary superblock between its payload and checksum writes, libblkid can temporarily reject the filesystem, causing udev to remove its filesystem UUID link. The generated root mount then cannot resolve the device and boot stops in the initramfs.

systemd-udevd already takes a shared whole-disk lock while probing. Holding an exclusive lock across the filesystem check makes udev defer and retry its event after filesystem metadata is consistent.

How it works

All systemd-managed filesystem checks converge in systemd-fsck, after it has resolved the exact block device and verified an fsck implementation exists. Before forking child fsck, the patch:

  1. Resolves the partition's parent whole disk with path_get_whole_disk().
  2. Resolves and opens that disk node read-only.
  3. Revalidates that the opened node still has the expected block-device number.
  4. Takes a blocking LOCK_EX BSD lock.
  5. Forks child fsck while the parent retains the lock descriptor.
  6. Waits for child completion, then closes the descriptor and releases the lock.

The lock wait is intentionally unbounded, matching the existing TimeoutSec=infinity fsck units. Concurrent checks for partitions on one disk serialize rather than failing after an arbitrary timeout.

Why this layer

This is simpler than the generator option in #18640:

  • no generated-unit command rewriting;
  • no fixture updates for every generated root or /usr unit;
  • one execution boundary covers root, separate /usr, and instantiated checks;
  • systemd-fsck already ships in the initramfs;
  • exact device resolution remains inside systemd code.

It is also simpler than the drop-in option in #18639 because it needs no shell helper, BindsTo= reverse resolution, or custom dracut file inclusion.

Compared with e2fsprogs ownership, this implementation consistently protects every filesystem checker launched through systemd-fsck. The e2fsprogs implementation instead owns the exact lifetime of e2fsck metadata writes and also applies outside systemd-managed boot. They are mutually exclusive alternatives, not cumulative protections.

Scope

This affects every filesystem check launched through systemd-fsck, not only initrd root and /usr units. Checks for partitions on the same physical disk now serialize on one whole-disk lock.

Risk

  • A whole-disk resolution, open, stat, or lock failure fails the systemd-fsck unit instead of proceeding through the known unsafe unlocked path.
  • Multiple filesystem checks on one disk are serialized, including non-rotating storage where util-linux's private fsck -l lock may not previously have serialized them.
  • A process that retains the whole-disk lock indefinitely can block filesystem checks because the wait follows existing infinite unit timeout behavior.
  • This implementation must not be combined with native e2fsprogs whole-disk locking.

Verification

  • Generated the patch mechanically from pristine systemd-stable v255 source.
  • Applied the patch with GNU patch --dry-run -p1 without fuzz or offsets.
  • Verified referenced APIs, headers, and existing libshared linkage against v255.
  • Verified parent lock lifetime across safe_fork(), child execvp(), and wait_for_terminate_and_check().
  • Added block-device number revalidation matching udevadm lock behavior.
  • Confirmed package Release entanglement at 255-35.
  • Ran git diff --check and repository commit/push hooks.
  • Completed two focused implementation reviews.
  • Full package build and boot validation are pending draft CI and the parallel VM campaign.

@microsoft-github-policy-service microsoft-github-policy-service Bot added Packaging 3.0-dev PRs Destined for AzureLinux 3.0 labels Aug 28, 2026
Acquire the exclusive whole-disk lock in systemd-fsck before launching
its child fsck process, and retain it until that process exits.

This is a systemd-owned alternative to native e2fsprogs locking. The two
implementations must not be combined because nested locks would deadlock.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb0e6716-4886-4e95-9efe-71dcde8687d6
@PawelWMS
Pawel Winogrodzki (PawelWMS) force-pushed the pawelwi/systemd-fsck-whole-disk-lock branch from 8f32f47 to bc45e8f Compare August 29, 2026 00:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.0-dev PRs Destined for AzureLinux 3.0 Packaging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant