
os.FileMode 的底层其实是整数,不是位掩码组合
很多人以为 os.FileMode 像 C 里的 stat 权限一样,是纯位运算结果,可以随意用 | 或 & 组合。其实它是个带标志的整数类型,低 9 位存传统 Unix 权限(如 0644),但高几位是特殊标志(如 os.ModeDir、os.ModeSymlink),这些标志和权限位不在同一语义层。
常见错误现象:os.FileMode(0644) | os.ModeDir 看似合理,但结果既不是目录也不是普通文件——os.Stat 返回的 Mode() 不会这样构造;手动拼出来的 mode 可能被 os.OpenFile 忽略标志,或让 os.Mkdir 拒绝创建。
- 真正表示“可读可写、是目录”的正确写法是
os.ModeDir | 0755,不是0755 | os.ModeDir(虽然数值相同,但语义清晰更重要) - 判断是否为目录,用
fi.Mode()&os.ModeDir != 0,而不是fi.Mode() == os.ModeDir -
os.ModePerm是掩码(0777),只用于提取权限位:fi.Mode() & os.ModePerm
os.OpenFile 的 flag 和 perm 参数分工明确,别混用
os.OpenFile 的第三个参数叫 perm,但它**只在创建新文件时生效**,且仅当 flag 包含 os.O_CREATE 才会被读取。它和 open 的 flag(如 os.O_RDWR)完全无关,也不影响已存在文件的权限变更。
使用场景:你想创建一个带 0600 权限的临时配置文件,又怕被其他用户读到。
立即学习“go语言免费学习笔记(深入)”;
- 错误写法:
os.OpenFile("cfg.txt", os.O_WRONLY, 0600)—— 如果文件已存在,perm被完全忽略,权限不变 - 正确写法:
os.OpenFile("cfg.txt", os.O_CREATE|os.O_WRONLY, 0600)—— 仅首次创建时设权限 - 想改已有文件权限?必须额外调用
os.Chmod("cfg.txt", 0600) - Windows 下
perm仅部分生效(比如0200表示只读),0644和0755效果可能无差别
os.Mkdir 和 os.MkdirAll 的权限参数不继承父目录
os.Mkdir 创建单层目录,os.MkdirAll 创建多层(自动补全中间缺失路径)。它们都要求显式传入 perm,且这个 perm **只作用于最末一级目录**,不会向上影响父级,也不会向下影响未来子文件。
常见错误现象:执行 os.MkdirAll("a/b/c", 0755) 后发现 a 和 b 目录权限是 0755,但其实是默认 umask 截断后的结果——Go 不保证中间目录权限等于你传的值。
- Linux/macOS 下,中间目录权限 = 你传的
perm& ^umask(例如 umask=0022,则0755变成0755 &^ 0022 == 0755;但 umask=0002 就变成0755 &^ 0002 == 0753) - 如果要确保某级目录严格为
0700,得单独对它调用os.Chmod -
os.MkdirAll遇到已存在目录不会报错,但也不会重设权限 —— 即使你传了不同perm
os.Chmod 改权限失败?先确认文件系统和用户权限
os.Chmod 看似简单,但在容器、NFS、rootless Pod 或 FAT32 分区里经常静默失败或行为异常。它返回 error,但很多人没检查。
典型错误信息:operation not permitted(常因 CAP_SYS_ADMIN 缺失或挂载选项 noexec/nosuid)、invalid argument(FAT/exFAT 不支持 Unix 权限)、permission denied(非 owner 且无 CAP_FOWNER)。
- 不要假设
os.Chmod(path, 0600)一定成功,务必检查 error - Docker 容器中,若用
user: 1001启动,且挂载 host 目录,host 上该目录 owner 不是 1001 →Chmod失败 - macOS 上 APFS 卷支持 ACL,
os.Chmod只改基础权限位,ACL 条目不受影响 - 想跨平台稳妥设“私有”权限,建议:先
os.Chmod,失败则记录 warning,不 panic —— 很多场景下权限只是尽力而为
真正麻烦的从来不是怎么写那行 os.Chmod,而是你没法靠一次调用就确定目标文件系统到底认不认这套规则。










