Python でファイルを作成するとき、パーミッションが意図どおりにならないことがある。これは umask の仕組みを理解していないと解決できない。
umask とは
umask はファイル作成時のパーミッションから「引かれる」ビットマスクだ。
ファイル作成時に指定するパーミッション(例: 0o666)
指定したパーミッションから umask を引いた値
# 現在の umask を確認 umask # 0022 # 意味: 新規ファイルから w(書き込み)を group と other から除去
umask の計算
実際のパーミッションは以下の式で計算される。
# 実際のパーミッション = 指定パーミッション & ~umask
# 例: mode=0o666, umask=0o022 の場合
mode = 0o666 # 110 110 110
umask = 0o022 # 000 010 010
result = mode & ~umask
print(oct(result)) # 0o644 (110 100 100)
umask が 0o022 なら、group と other から書き込み権限が除去される。
Python でのファイル作成と umask
open() でファイルを作成すると、umask が適用される。
import os
import stat
# 現在の umask を確認
current_umask = os.umask(0)
os.umask(current_umask) # 元に戻す
print(f"umask: {oct(current_umask)}") # 0o22
# ファイルを作成(デフォルトは 0o666 から umask を引いた値)
with open("test.txt", "w") as f:
f.write("hello")
# パーミッションを確認
mode = os.stat("test.txt").st_mode & 0o777
print(f"permission: {oct(mode)}") # 0o644
umask を一時的に変更する
特定のパーミッションでファイルを作成したい場合、umask を一時的に変更する。
import os
def create_with_mode(path, mode, content):
"""指定したパーミッションでファイルを作成"""
old_umask = os.umask(0) # umask を 0 にして影響を無効化
try:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode)
try:
os.write(fd, content.encode())
finally:
os.close(fd)
finally:
os.umask(old_umask) # umask を元に戻す
# 0o600(所有者のみ読み書き可能)でファイルを作成
create_with_mode("secret.txt", 0o600, "secret data")
os.open() と open() の違い
Python レベルの関数。モード指定は文字列("r", "w" など)。パーミッションは指定できない
システムコールの薄いラッパー。パーミッションを直接指定できる
import os
# os.open() でパーミッションを指定
fd = os.open("data.txt", os.O_WRONLY | os.O_CREAT, 0o600)
os.write(fd, b"hello")
os.close(fd)
# open() はパーミッションを指定できない(umask 依存)
with open("data2.txt", "w") as f:
f.write("hello")
# パーミッションは umask に従う
ディレクトリの umask
ディレクトリ作成時も umask が適用される。ディレクトリのデフォルトパーミッションは 0o777 だ。
import os
# umask=0o022 の場合
os.mkdir("newdir") # 0o777 & ~0o022 = 0o755
# 指定したパーミッションで作成(umask は適用される)
os.mkdir("newdir2", mode=0o700) # 実際は 0o700 & ~umask
# シェルでディレクトリを作成 mkdir testdir ls -ld testdir # drwxr-xr-x 2 user user 4096 Jan 15 10:00 testdir (0o755)
chmod で事後変更
umask を経由せずにパーミッションを設定したい場合は、作成後に chmod する方法もある。
import os
import stat
# ファイルを作成
with open("secret.txt", "w") as f:
f.write("secret")
# パーミッションを変更
os.chmod("secret.txt", 0o600)
# stat モジュールの定数を使う方法
os.chmod("secret.txt", stat.S_IRUSR | stat.S_IWUSR) # 0o600 と同じ
ただし、作成と chmod の間に一瞬のすきまがある。機密ファイルの場合は os.open() で最初から正しいパーミッションを設定するほうが安全だ。
umask のセキュリティ上の意味
group と other から書き込み権限を除去。ファイルは 0o644、ディレクトリは 0o755 になる
group と other からすべての権限を除去。ファイルは 0o600、ディレクトリは 0o700 になる
制限なし。すべてのユーザーが読み書き可能なファイルが作成される可能性がある
Python スクリプト全体で umask を設定
スクリプトの開始時に umask を設定しておくと、以降のすべてのファイル作成に適用される。
import os
# スクリプト開始時に厳格な umask を設定
os.umask(0o077)
# 以降、作成されるファイルはすべて 0o600
with open("file1.txt", "w") as f:
f.write("data1")
with open("file2.txt", "w") as f:
f.write("data2")
まとめ
| 関数 | パーミッション指定 | umask 適用 |
|---|---|---|
| open() | 不可 | される |
| os.open() | 可能 | される |
| os.mkdir() | 可能 | される |
| os.chmod() | 可能 | されない |
セキュリティが重要なファイル、他ユーザーとの共有ファイル、サービスで作成するファイル
機密ファイルは os.open() で明示的にパーミッションを指定し、作成後に chmod で再確認する