Python’s os.makedirs() with mode 0o777 doesn’t grant write permissions to others
When you call os.makedirs(path, mode=0o777) in Python, the resulting directory often won’t have the permissions you expect. The mode argument gets masked by the process’s umask, which removes certain permission bits before applying them to the filesystem.
Your umask acts as a filter. If your umask is 0o022 (common on many systems), it removes write permissions for group and others:
0o777 & ~0o022 = 0o755
This means os.makedirs(path, mode=0o777) creates a directory with 0o755 permissions (rwxr-xr-x), not 0o777 (rwxrwxrwx).
Checking and Changing Your Umask
View your current umask:
umask
If you need to temporarily change it before calling os.makedirs():
import os
# Get current umask (this sets it to 0, then restores the original)
current_umask = os.umask(0)
os.umask(current_umask)
# Create directory with full permissions
os.makedirs('/tmp/test_dir', mode=0o777)
# Restore original umask
os.umask(current_umask)
However, changing the umask process-wide is rarely the right solution—it affects all subsequent file creation in your process.
Better Approaches
Use chmod() after creation (recommended):
import os
os.makedirs('/tmp/test_dir', mode=0o777, exist_ok=True)
os.chmod('/tmp/test_dir', 0o777)
This is explicit and doesn’t manipulate your umask. Any future calls to mkdir() or file creation still respect your original umask.
Create with restricted permissions, then expand:
import os
os.makedirs('/tmp/test_dir', mode=0o700, exist_ok=True)
os.chmod('/tmp/test_dir', 0o777)
This is safer—the directory starts private, then gets opened up only when needed.
Use context manager for temporary umask changes:
import os
from contextlib import contextmanager
@contextmanager
def temporary_umask(new_umask):
old_umask = os.umask(new_umask)
try:
yield
finally:
os.umask(old_umask)
with temporary_umask(0):
os.makedirs('/tmp/test_dir', mode=0o777, exist_ok=True)
This ensures your umask is restored even if an exception occurs.
Security Considerations
Before setting 0o777 on directories, consider:
- Do you actually need world-writable directories? This is a security risk in production. Anyone can modify files there.
- Is this a temporary directory? Use
/tmpwith proper naming conventions and verify ownership. - Are you running in a container? Docker and other containers can restrict capabilities regardless of permissions.
- What’s the principle of least privilege? Start restrictive (0o700) and relax only when necessary.
For shared development environments, prefer 0o770 (group-writable) with appropriate group membership, or 0o755 (world-readable but only owner-writable) for most cases.
Checking Actual Permissions
After creating a directory, verify permissions:
import os
import stat
os.makedirs('/tmp/test_dir', exist_ok=True)
os.chmod('/tmp/test_dir', 0o777)
mode = os.stat('/tmp/test_dir').st_mode
permissions = oct(stat.S_IMODE(mode))
print(f"Directory permissions: {permissions}")
This tells you exactly what permissions the filesystem actually assigned, accounting for umask interference.