Go语言无文件权限继承机制,子文件权限须显式指定,os.MkdirAll的perm参数逐层显式设置且受umask影响,Windows忽略执行位和组权限。

Go 语言没有文件权限继承机制,所谓“继承”是系统级行为(如 umask、父目录默认 ACL),Go 本身不提供、也不模拟该能力。 你不能通过 os.FileMode 或任何标准库函数让子文件自动获得父目录的权限设置——这是常见误解的源头。
os.MkdirAll 的 perm 参数不是继承,而是逐层显式设置
调用 os.MkdirAll("a/b/c", 0755) 时,Go 会依次创建 a、b、c,并对每一层都传入 0755。但实际生效权限取决于系统 umask:
- Linux/macOS 下:若 umask=0022,则
0755 &^ 0022 = 0755→ 目录权限为drwxr-xr-x - 若 umask=0002,则
0755 &^ 0002 = 0754→ 实际为drwxr-xr--,组写位被截断 - Windows 下:只保留只读/可写语义,
0755和0644都等价于“可写”,执行位完全忽略
关键点:os.MkdirAll 不读取父目录当前权限,也不基于它计算子目录权限;它只是对每个新建目录重复应用你给的 perm 值。
子文件权限不会从父目录“继承”,必须显式指定
创建文件时,权限完全由你传给 os.OpenFile 或 os.WriteFile 的 perm 参数决定,与所在目录权限无关:
立即学习“go语言免费学习笔记(深入)”;
-
os.WriteFile("a/b/c.txt", data, 0600)→ 文件权限就是-rw-------,哪怕a/b/是0777 -
os.OpenFile("a/b/d.log", os.O_CREATE|os.O_APPEND, 0644)→ 权限固定为-rw-r--r-- - 不存在 “用父目录权限做默认值” 的逻辑;Go 不访问父目录的
stat结果来推导子文件权限
注意:0644 必须写成八进制字面量(前导零),写成 644 会被当十进制解析,结果是 os.FileMode(644) → 二进制 1010000100,对应权限位完全错乱。
想模拟“继承”,得自己提取并组合权限位
如果你真需要子文件权限参考父目录(比如日志文件保持和日志目录同组可写),只能手动获取父目录权限再按需调整:
parentInfo, err := os.Stat("a/b")
if err != nil {
log.Fatal(err)
}
parentPerm := parentInfo.Mode().Perm() // 提取纯权限位(去掉 ModeDir 等标志)
// 比如:让文件拥有和父目录相同的组写位,但关闭执行位
filePerm := parentPerm &^ 0111 | 0600 // 清执行位,设所有者读写
os.WriteFile("a/b/log.txt", data, filePerm)
- 必须用
info.Mode().Perm()提取干净权限位,直接用info.Mode()会混入os.ModeDir等标志,导致位运算出错 - Windows 下此逻辑无意义——组权限、执行位均不生效,强行模拟只会增加跨平台 bug
- umask 仍会二次截断,最终权限 = (你算出的值) &^ umask
真正容易被忽略的是:Go 的权限 API 是薄封装,它把控制权完全交还给操作系统。你看到的“不继承”,其实是 Unix/Linux 本身就没有目录级权限继承(ACL 除外),而 Windows 根本不支持该语义——Go 只是诚实地暴露了这个事实。


















