Skip to content

fix: preserve POSIX permissions during atomic file writes - #17

Merged
irmia2026 merged 1 commit into
irmia2026:mainfrom
LoveS0ph1e:fix/preserve-posix-file-permissions
Aug 17, 2026
Merged

fix: preserve POSIX permissions during atomic file writes#17
irmia2026 merged 1 commit into
irmia2026:mainfrom
LoveS0ph1e:fix/preserve-posix-file-permissions

Conversation

@LoveS0ph1e

Copy link
Copy Markdown
Contributor

Summary

  • Preserve existing POSIX permission bits when atomically replacing a file.
  • Use an explicit 0644 mode for newly created files.
  • Apply the same behavior to multi_edit, which has its own atomic commit path.
  • Keep non-POSIX behavior unchanged.

Problem

The file tools write through a temporary file created by tempfile.mkstemp() and then commit it with os.replace().

On POSIX systems, mkstemp() creates the temporary file with mode 0600. Because os.replace() moves that inode into the destination path, the final user file also becomes 0600, regardless of the process umask.

This can make generated files unreadable to downstream processes running under another UID. It also silently removes existing modes such as 0640 or executable bits.

safe_write, safe_edit, and file_patch share atomic_write_text(). multi_edit implements a separate mkstemp() / os.replace() commit path and therefore requires the same handling independently.

Changes

  • Add _atomic_target_mode():
    • return None on non-POSIX platforms;
    • preserve the existing file's nine rwx permission bits;
    • return 0644 when creating a new file.
  • Apply the selected mode to the temporary file before os.replace().
  • Reuse the same helper in multi_edit.
  • Add regression tests for new files and for preserving 0600, 0640, and 0755 modes.

Applying the mode before replacement is intentional: if chmod() fails, the original target remains untouched and the existing rollback behavior is preserved.

Verification

  • Permission-related tests: 62 passed
  • Affected file-tool tests: 143 passed, 2 skipped
  • Full CI-equivalent unit suite: 657 passed, 2 skipped, 1 deselected
  • Ruff on changed files, excluding two pre-existing unused imports: passed

An isolated Linux probe also confirmed that new files are 0644 under umask 000, 022, and 077, while existing private files retain their original mode.

Scope

This change preserves traditional POSIX rwx permission bits. It deliberately does not copy ownership, ACLs, extended attributes, or setuid/setgid/sticky bits. Windows behavior is unchanged.

@LoveS0ph1e
LoveS0ph1e requested a review from irmia2026 as a code owner August 14, 2026 17:20
@irmia2026
irmia2026 merged commit 934d306 into irmia2026:main Aug 17, 2026
1 check passed
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.

2 participants