Skip to content

Refuse to remove Omarchy while its EFI partition is mounted - #43

Open
joshuaswarren wants to merge 1 commit into
omacom:mainfrom
joshuaswarren:fix/removal-refuses-mounted-efi
Open

joshuaswarren wants to merge 1 commit into
omacom:mainfrom
joshuaswarren:fix/removal-refuses-mounted-efi

Conversation

@joshuaswarren

Copy link
Copy Markdown
Contributor

Removal now refuses an EFI partition that is already mounted, before it deletes anything.

What happened

On a 14-inch M2 Max (apple,j414c, macOS 27.0, installer 2.0.10) the Omarchy EFI partition was still mounted through FSKit's msdos module from a backup taken earlier. Remove Omarchy read it in place. Then diskutil eraseVolume free none disk0s4 forced it off. The unified log shows the unmount request with option 0x00080000 (force), then from com.apple.fskit.msdos:

flush_meta returned -536870208
-[FATVolume unmountWithReplyHandler:]_block_invoke: Failed to meta flush, error Error Domain=NSPOSIXErrorDomain Code=5
-[FATVolume unmountWithReplyHandler:]_block_invoke: Failed to clean dirty bit, error Error Domain=NSPOSIXErrorDomain Code=5

The partitions were removed, but from then on mount, diskutil list and diskutil unmount hung. The app looked frozen. Disk commands answered again only after fskit_agent and its msdos extension were stopped by hand, about 16 minutes later.

main already reads an unmounted EFI partition from its raw device (a2f898b). The mounted case still reads in place and then erases a mounted partition.

Change

  • withEFIPartition refuses a mounted EFI partition. The message names the mount point and says to restart the Mac, because macOS doesn't mount the EFI partition at startup. An unmounted one is read from the raw device as before.
  • Execution reads the evidence again before it deletes anything, so a partition mounted after review is refused too.
  • The executor comment said diskutil refuses a busy volume. The log shows it force-unmounts, so the comment now says that.

Tests

testMountedEFIPartitionIsRefusedBeforeAnythingIsErased covers both review and execution. On main it fails: review accepts the mounted partition, and execution goes on to apfs deleteContainer. With this change it passes. testOlderInstallIsPlannedEndToEndFromDiskutilAndFilesReadInPlace is now ...WithTheStubReadInPlace and mounts only the stub, because a mounted EFI partition is no longer read in place.

Run on an M1 Ultra Mac Studio (Mac13,2), macOS 26.6.2, Xcode 27.0, Swift 6.4:

  • xcrun swift-format lint --strict --recursive Package.swift Sources Tests: clean
  • swift test: 147 + 3 + 520 + 8 tests, 0 failures (1 existing skip)
  • swift test -c release: 136 + 3 + 520 + 5 tests, 0 failures
  • ./test/all on Linux: passed

I didn't reproduce the hang on hardware again. The evidence is the log from the M2 Max.

@joshuaswarren
joshuaswarren marked this pull request as ready for review October 6, 2026 18:01
On the M2 Max (macOS 27.0, installer 2.0.10) the EFI partition was still
mounted through FSKit's msdos module from a backup taken earlier. Removal
read it in place, then `diskutil eraseVolume free none disk0s4` forced it
off (DiskArbitration unmount option 0x00080000). The msdos module lost its
device mid-flush (flush_meta and read_meta returned EIO), and from then on
`mount`, `diskutil list` and `diskutil unmount` hung until fskit_agent and
its msdos extension were killed by hand, about 16 minutes later.

Review now refuses a mounted EFI partition and says to restart the Mac,
which leaves it unmounted. Execution reads the evidence again before it
deletes anything, so a partition mounted after review is refused too. The
EFI partition is now always read from its raw device.

The executor's comment said diskutil refuses a busy volume. The unified
log shows it force-unmounts what it deletes or erases, so the comment now
says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@joshuaswarren
joshuaswarren force-pushed the fix/removal-refuses-mounted-efi branch from a400747 to f4efc5e Compare October 8, 2026 23:35
@joshuaswarren

Copy link
Copy Markdown
Contributor Author

Rebased onto the current main and the checks pass. Ready when you are.

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.

1 participant