Skip to content

Write config temp files next to the target so the move stays atomic - #6630

Open
dekrom wants to merge 1 commit into
MeteorDevelopment:masterfrom
dekrom:fix/atomic-config-save
Open

Write config temp files next to the target so the move stays atomic#6630
dekrom wants to merge 1 commit into
MeteorDevelopment:masterfrom
dekrom:fix/atomic-config-save

Conversation

@dekrom

@dekrom dekrom commented Aug 30, 2026

Copy link
Copy Markdown

Type of change

  • Bug fix
  • New feature

Description

System#save writes to a temp file and then moves it over the target, which is the right shape,
but the temp file is created with no directory argument:

File tempFile = File.createTempFile(MeteorClient.MOD_ID, file.getName());

so it lands in java.io.tmpdir while the target is under the game directory. When those are
different filesystems the atomic move cannot work, Files.move throws
AtomicMoveNotSupportedException, and the fallback is StreamUtils.copy, which opens
new FileOutputStream(to) and truncates the live config to zero before it writes a byte. It
also swallows the IOException into a log line, so a failure there leaves an empty file and
save() reports nothing.

/tmp being its own tmpfs mount is the systemd default, and on Windows %TEMP% is often on a
different drive to the instance, so for a lot of people this is not the rare fallback, it is
every single save. The temp file buys nothing there. Alt-F4 during the shutdown hook, or a full
disk, and modules.nbt comes back empty.

Resolving the destination before creating the temp file next to it puts the move back on one
filesystem so it stays atomic. The copy fallback stays for anything exotic. The delete moved
into a finally as well, since a throw from NbtIo.write or the move used to leave the temp
file behind.

Related issues

None that I found.

How Has This Been Tested?

Confirmed on my own machine that /tmp is a separate tmpfs mount from the game directory, which
is what sends every save down the truncating fallback. I have not gone as far as killing the
process mid-save to produce a zeroed config. Builds clean against current master.

Checklist:

  • My code follows the style guidelines of this project.
  • I have added comments to my code in more complex areas.
  • I have tested the code in both development and production environments.

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