Linux umask 权限计算:为什么文件是 640,目录却是 750
同一个终端里新建文本文件通常得到 644,新建目录却是 755,这并不矛盾。程序请求的初始权限不同,umask 再从中屏蔽指定权限位。把两者分开计算,才能解释为什么调整掩码后结果仍不符合预期。本文用 Linux 和 Python 3 做隔离实验,不改动现有业务文件或终端的掩码。
AI生成概念配图:三组权限方块经过掩码筛选,分别形成文件与目录的权限组合。仅作概念说明,不代表实际界面或实测结果。
先把八进制权限拆成位
普通权限分为所有者、所属组、其他人三组,每组的读、写、执行分别对应四、二、一。数字七代表三位全部打开,六代表读写,五代表读和执行,四代表只读。文件的执行位允许作为程序执行;目录对应的是搜索权限,用于沿路径查找其中的条目。
在父目录没有默认 ACL 的前提下,创建后的普通权限等于“请求权限按位与掩码取反”,即 mode & ~umask。掩码中的一表示清除对应位,它只会扣掉申请过的权限,不会补上程序没有申请的位。计算时使用八进制;Python 的八进制整数以 0o 开头。
常见普通文件请求 0666,目录请求 0777,但这是创建程序的选择。掩码 027 清除所属组的写权限和其他人的全部权限,因此文件得到 0640,目录得到 0750。掩码 000 也不能给申请 0666 的文件凭空增加执行权限。
用五组掩码直接验证
将代码保存为 umask_demo.py 并用 python3 运行。程序在独立临时目录中显式申请文件和目录权限,再用 stat 读取实际结果。若该目录继承了默认 ACL,实验会主动停止;请选择没有默认 ACL 的实验位置,再比较普通权限公式。
import os
from pathlib import Path
import stat
import tempfile
def mode(path):
return stat.S_IMODE(path.stat().st_mode) & 0o777
def main():
with tempfile.TemporaryDirectory(prefix="umask-demo-") as work:
root = Path(work)
if "system.posix_acl_default" in os.listxattr(root):
raise RuntimeError("Use a temporary directory without a default ACL")
previous = os.umask(0o022)
try:
print("umask file directory")
for mask in (0o000, 0o022, 0o027, 0o077, 0o111):
os.umask(mask)
file = root / f"file-{mask:03o}"
folder = root / f"dir-{mask:03o}"
fd = os.open(file, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o666)
os.close(fd)
folder.mkdir(mode=0o777)
actual = (mode(file), mode(folder))
assert actual == (0o666 & ~mask, 0o777 & ~mask)
print(f"{mask:03o} {actual[0]:03o} {actual[1]:03o}")
folder.chmod(0o700)
os.umask(0o077)
existing = root / "file-000"
assert mode(existing) == 0o666
existing.chmod(0o640)
assert mode(existing) == 0o640
os.umask(0)
fd = os.open(root / "private", os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
os.close(fd)
assert mode(root / "private") == 0o600
print("existing file unchanged; chmod applied; requested 600 stayed 600")
finally:
os.umask(previous)
if __name__ == "__main__":
main()本次运行得到:000 对应文件 666、目录 777;022 对应 644、755;027 对应 640、750;077 对应 600、700;111 对应 666、666。最后一组尤其适合检查理解:文件本来就没有执行位,屏蔽执行位以后仍然是 666,不能把掩码当作普通算术减数。
目录在 111 下会失去搜索位,因此代码读取模式后立即把实验子目录改为 700,便于后续清理。这个修正只作用于刚创建的实验对象。实验用 0777 只提取普通九位权限,继承的 setgid 等特殊位需要另查。测试没有用当前账户能否读取来代替权限位判断;特权账户可能绕过部分访问限制。
为什么修改掩码没有改变旧文件
umask 参与新对象的创建,不会追溯修改已存在文件。示例切换到 077 后,先前以 000 创建的文件仍是 666;随后显式调用 chmod,才变成 640。程序若只申请 0600,即使掩码是零,结果仍是 0600。排查时先确认对象是否新建,以及程序到底请求了什么模式。
umask 是进程属性,子进程会继承它。这个脚本只改变自己的进程状态,并在 finally 恢复原值,所以不会改变启动它的终端。服务进程的掩码也应检查它的启动上下文,不能只看你当前交互终端中的值。
排查默认 ACL 与并发影响
父目录存在默认 ACL 时,Linux 按继承的 ACL 和创建请求共同确定权限,普通的掩码计算不再直接适用。实际排查可先查看父目录的默认 ACL,再检查创建程序是否随后调用 chmod。不要只看到最终数字不同,就断定 umask 没有生效。
在多线程程序里,临时设置掩码再恢复会影响同一进程内其他线程的创建操作,因此本例应作为独立脚本运行。Linux 4.7 起可从 /proc/self/status 的 Umask 字段读取当前进程掩码,避免为了查询而先修改。需要严密控制文件权限时,优先在启动阶段确定策略,并显式指定创建模式。
参考资料
资料核验日期:2026年9月30日,UTC。


