
go 使用 os.mkdir() 创建目录时,实际权限 = 指定权限 &^ umask,因此即使传入 0777,也可能因系统 umask(如 0022)导致权限被屏蔽,最终得到 drwxr-xr-x 等“缩水”权限。
在 go 中,os.Mkdir(path, perm) 的第二个参数 perm 并非直接设置最终文件系统权限,而是与进程当前 umask 进行按位与非(bitwise AND NOT)运算后的结果。这是 unix/linux 系统级行为,Go 完全遵循 POSIX 语义。
例如,你传入 0777(即十进制 511),若当前进程 umask 为 0022(常见默认值),则实际创建权限为:
0777 &^ 0022 → 0755 → drwxr-xr-x
这正是你观察到 drwxr-xr-x 的根本原因——组和其他用户的写权限(w)被 umask 屏蔽了。
✅ 正确做法:使用 os.MkdirAll() + 显式 chmod(推荐)
若需确保目录拥有精确权限(如 0777),应分两步操作:
package main import ( "fmt" "os" ) func main() { dir := "/var/run/testdir" // 1. 创建目录(使用宽松权限占位,避免 umask 干扰逻辑) err := os.Mkdir(dir, 0755) if err != nil && !os.IsExist(err) { fmt.Printf("failed to create dir: %vn", err) return } // 2. 强制设置目标权限(绕过 umask 影响) err = os.Chmod(dir, 0777) if err != nil { fmt.Printf("failed to chmod dir: %vn", err) return } fmt.Println("Directory created with 0777 permissions") }
⚠️ 注意事项: os.Chmod() 仅修改权限位,不改变所有者/所属组;确保运行程序具有足够权限(如 root 对 /var/run/ 下目录)。 若需递归创建多级路径(如 /var/run/testdir/sub),请改用 os.MkdirAll(dir, 0755) 配合后续 os.Chmod(),否则 os.Mkdir() 仅创建最后一级。 在容器或 systemd 服务中,umask 可能被显式配置(如 UMask=0002),可通过 syscall.Umask(0) 临时修改——但不推荐:该操作是全局且非线程安全的,可能影响其他 goroutine 或第三方库。
? 验证当前 umask(调试用)
可在程序中打印当前 umask(需导入 syscall):
import "syscall" // ... mask := syscall.Umask(0) // 获取并重置为 0(注意:会改变当前 umask!) syscall.Umask(mask) // 立即恢复,仅用于读取 fmt.Printf("Current umask: %04on", mask)
但生产代码中应避免依赖或修改 umask,而采用 Chmod 显式赋权这一清晰、可预测、符合最小权限原则的方式。
总之,理解 umask 是掌握 Go 文件权限行为的关键;而 Mkdir + Chmod 组合,是实现确定性权限控制最可靠、最符合工程实践的标准方案。