Skip to content

Comments

fix: use & ~umask instead of ^ umask for permission masking#218

Open
Koan-Bot wants to merge 2 commits intocpanel:masterfrom
atoomic:koan.atoomic/fix-umask-xor
Open

fix: use & ~umask instead of ^ umask for permission masking#218
Koan-Bot wants to merge 2 commits intocpanel:masterfrom
atoomic:koan.atoomic/fix-umask-xor

Conversation

@Koan-Bot
Copy link
Contributor

Summary

  • Fix incorrect XOR-based umask application in 4 locations — use bitwise AND-NOT instead
  • Remove umask from chmod() method entirely (real chmod ignores umask)

Why

The XOR operator (^ umask) only produces correct results when all umask bits are set in the permission value. For non-default permissions where umask bits are already clear, XOR re-enables them:

0644 ^ 0022 = 0666  (wrong — should stay 0644)
0600 ^ 0022 = 0622  (wrong — should stay 0600)
0755 ^ 0022 = 0777  (wrong — chmod should give exactly 0755)

Default permissions (0666 for files, 0777 for dirs) with umask 0022 happened to work by coincidence, masking this bug.

What changed

Location Before After
file() constructor $perms ^ umask $perms & ~umask
dir() constructor $perms ^ umask $perms & ~umask
__mkdir() $perms ^ umask $perms & ~umask
chmod() method $mode ^ umask $mode (umask removed — matches real chmod)

Testing

Added 3 new subtests in t/chmod.t exercising non-default permissions that would fail with the old XOR logic:

  • File creation with explicit mode 0644 under umask 0022
  • $mock->chmod(0755) verifying umask is not applied
  • mkdir('/path', 0700) under umask 0022

The XOR operator was used to apply umask in 4 locations, which produces
correct results only when the permission bits being masked are all set
(e.g., 0666 ^ 0022 = 0644). For non-default permissions where umask
bits are already clear, XOR incorrectly re-enables them:
  0644 ^ 0022 = 0666 (wrong — should stay 0644)
  0600 ^ 0022 = 0622 (wrong — should stay 0600)

Fixed 4 locations:
- file() constructor (line 664): 0666 default, user-provided mode
- dir() constructor (line 844): 0777 default
- __mkdir() (line 2243): user-provided mode
- chmod() method (line 1525): removed umask entirely — real chmod(2)
  ignores umask, only open/mkdir/mkfifo apply it

Added tests for non-default permissions that would fail with XOR:
- File creation with mode 0644 under umask 0022
- chmod(0755) verifying umask is not applied
- mkdir with mode 0700 under umask 0022
@Koan-Bot Koan-Bot force-pushed the koan.atoomic/fix-umask-xor branch from 3841c39 to 456c3b4 Compare February 24, 2026 21:32
The permission recovery formula used XOR with umask, which only works
when umask was applied via XOR. Now that umask is correctly applied
with & ~umask, stat already returns the final mode — just mask with
07777 to strip file type bits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@atoomic atoomic marked this pull request as ready for review February 25, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants