Skip to content

Fix os.rename deleting the destination when the source does not exist - #364

Merged
akeit0 merged 1 commit into
nuskey8:mainfrom
billythekidz:fix/os-rename-missing-source
Oct 23, 2026
Merged

akeit0 merged 1 commit into
nuskey8:mainfrom
billythekidz:fix/os-rename-missing-source

Conversation

@billythekidz

Copy link
Copy Markdown
Contributor

os.rename(old, new) deleted new before checking that old exists. With a missing source it returned nil, error but the destination file was already gone.

local f = io.open("keep.txt", "w"); f:write("data"); f:close()
print(os.rename("missing.txt", "keep.txt"))  -- nil + error, and keep.txt was deleted

FileSystem.Rename now checks the source first, then deletes the destination, then moves. os.rename with a missing source returns nil, "...: No such file or directory" and leaves the destination alone, as in C Lua.

This is also why tests-lua/files.lua needed the PatchFilesLuaSource patch ("UTF-16 text-mode" block): the failing assertion at files.lua:129 was this bug, not an encoding issue. The patch is removed and files.lua runs unmodified.

Verification

  • New tests FileSystem_Rename_MissingSource_KeepsDestination and OsRename_MissingSource_ReturnsNilAndKeepsDestination fail on main and pass with the change.
  • dotnet test tests/Lua.Tests/Lua.Tests.csproj -c Release --filter "TestCategory!=ExpectedFailure": 612 passed, 0 failed (610 on main + 2 new).
  • dotnet csharpier check . is clean.
  • Run on macOS (arm64), .NET 8 runtime only.

FileSystem.Rename removed the destination file before checking that the
source exists, so a failed os.rename lost the destination's contents.
Check the source first, as rename(2) does. With the data no longer lost,
the part of files.lua that the test harness skipped now runs unmodified.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The targeted fix addresses the reported data loss, with regression coverage for both entry points and no unresolved findings.

No open issues 🎉

What changed in this PR

Fixes os.rename deleting an existing destination when the source is missing, preserving destination data on this failure path.

Changes:

  • Checks source existence before deleting the destination and removes the redundant post-move deletion.
  • Adds regression tests for destination preservation through both filesystem and Lua APIs.
  • Restores the previously skipped block of files.lua assertions.
File Description
tests/​Lua.Tests/​LuaTests.cs Removes the harness override that skipped the affected assertions.
tests/​Lua.Tests/​IOTests.cs Adds missing-source rename regression tests for both entry points.
src/​Lua/​IO/​FileSystem.cs Validates source existence before destination deletion and removes redundant cleanup.

Review effort: Balanced

akeit0 added a commit that referenced this pull request Oct 23, 2026
Integrate #364–#366 with rename safety, w+ seek and gsub allocation fixes
@akeit0
akeit0 merged commit de35b17 into nuskey8:main Oct 23, 2026
@akeit0

akeit0 commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Thank you for finding and fixing this data-loss bug, adding regression tests, and restoring the files.lua assertions! Your original commit has been merged through #368 with its authorship preserved.

During review we added follow-up hardening for other rename failures, including a locked source on Windows and directory-to-file attempts. The implementation now replaces the destination through filesystem operations instead of deleting it before the move, with a compatible path for .NET Standard 2.1 and coverage for directory renames. The combined changes passed local tests and Linux CI.

@akeit0 akeit0 mentioned this pull request Oct 25, 2026
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