用access()判断路径读取权限最直接:linux/macos下通过access(path, r_ok)检查有效uid/gid权限,返回0表示可读;windows不支持,需用createfile()尝试打开并结合getlasterror()判断error_access_denied,stat()因忽略acl、mount选项等不可靠。

用 access() 判断路径读取权限最直接
在 Linux/macOS 上,access() 是标准且轻量的方式,它不打开文件,只检查当前进程的有效用户/组 ID 是否有对应权限。Windows 不支持该函数,需换用其他方法。
关键点:它检查的是“真实 UID/GID”下的权限,不是有效 UID(即不考虑 setuid 程序的提权效果),这点和 stat() + 手动比对 mode 有本质区别。
-
access("/path/to/file", R_OK)返回 0 表示可读;返回 -1 且errno == EACCES表示拒绝;返回 -1 且errno == ENOENT表示路径不存在 - 注意:对目录,
R_OK检查的是能否列出内容(即是否可读取目录项),不是能否进入(那是X_OK) - 如果路径是符号链接,
access()默认跟随链接(POSIX 默认行为),不检查链接本身的权限
Windows 下必须用 GetFileAttributes() + 权限查询
Windows 没有 access(),也不能靠简单判断文件属性位(如 FILE_ATTRIBUTE_READONLY)来等价“可读”——只读属性 ≠ 不可读,它只是禁止写入;一个只读文件完全可能被当前用户正常读取。
真正可靠的做法是尝试打开文件并验证访问结果:
- 调用
CreateFile(),传入GENERIC_READ和FILE_SHARE_READ,再检查返回句柄是否为INVALID_HANDLE_VALUE - 若失败,用
GetLastError()判断:如果是ERROR_ACCESS_DENIED或ERROR_SHARING_VIOLATION,基本可判定无读取权限(注意区分“无权限”和“被占用”) - 别依赖
GetFileAttributes()的返回值做权限决策——它只反映基础属性,不体现 ACL 实际生效状态
跨平台封装要注意的三个坑
写个统一接口时,最容易栽在边界情况上:
- 路径末尾带斜杠(如
/tmp/):Linux 下access("/tmp/", R_OK)可行;Windows 下CreateFile("C:\tmp\", ...)会失败(目录不能用GENERIC_READ打开),得改用FindFirstFile()或GetFileAttributesEx()配合AccessCheck() - 空路径或相对路径(如
"./"):access()能处理,但 Windows API 对相对路径更敏感,建议先用std::filesystem::absolute()规范化 - 网络路径(SMB/NFS):Linux 的
access()可能因挂载选项(如noexec、nosuid)误报,而 Windows 的CreateFile()在断连时会卡住或超时,务必设FILE_FLAG_OPEN_NO_RECALL并控制超时
stat() + mode 解析不是推荐方案
有人试图用 stat() 获取 st_mode,再按 owner/group/other 位手动比对,这在绝大多数场景下都不可靠:
- 忽略 ACL:Linux 的 POSIX ACL、Windows 的 DACL 都不会体现在
st_mode中,导致误判 - 忽略 mount 选项:例如
mount -o ro后,文件st_mode仍是可写,但实际无法写入;同理,noexec不影响R_OK位,但会影响某些执行相关读取(如 mmap 执行段) - 忽略 capability:Linux capability(如
cap_dac_override)能让进程绕过常规权限检查,stat()完全无法感知
真正需要细粒度控制时,应直接走系统调用级检查(access() / CreateFile()),而不是模拟权限逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











