access()是类unix系统判断路径读权限最直接方法,检查真实uid/gid与权限位,返回0表示可读、-1需结合errno区分原因;windows应使用getfileattributes()。

用 access() 判断读取权限最直接
在类 Unix 系统(Linux/macOS)上,access() 是标准且轻量的方式,它不打开文件,只检查进程对路径的权限位。注意它检查的是**真实用户 ID(real UID)** 的权限,不是有效 UID(比如 setuid 程序里可能有差异)。
实操建议:
- 包含头文件:
#include <unistd.h></unistd.h>(POSIX)或#include <>io.h>(Windows 兼容层,但行为不同) - 调用
access(path.c_str(), R_OK);返回 0 表示可读,-1 表示不可读(需结合errno判断原因) - 常见错误:传入空路径、路径含非法字符导致
errno == ENOENT,或目录无执行权限(ENOACCES)——后者常被误认为“文件不可读”,其实是父目录不可遍历 - 不要用
access()做安全检查(TOCTOU 问题),仅用于用户体验提示,如灰掉“打开”按钮
Windows 下用 GetFileAttributes() 更可靠
Windows 没有 access() 的语义等价物,GetFileAttributes() 是首选。它不依赖当前进程 token 的完整 ACL 解析,速度快,且能区分“不存在”和“拒绝访问”。
实操建议:
- 包含头文件:
#include <windows.h></windows.h> - 调用
DWORD attr = GetFileAttributesA(path.c_str());若返回INVALID_FILE_ATTRIBUTES,再查GetLastError() -
attr & FILE_ATTRIBUTE_DIRECTORY为真说明是目录,此时“可读”还需确保你有遍历权限(通常看是否能枚举子项,但GetFileAttributes()本身不报错) - 注意:该函数不检查 DACL 中的显式 deny 条目,只反映基础属性与默认允许逻辑;若需精确 ACL 判断,得用
AccessCheck(),但开销大、代码复杂,一般没必要
跨平台封装时避开 fopen() 和异常试探
有人习惯用 fopen(path, "r") 是否成功来判断可读性,这不可靠:文件可能被其他进程独占锁住(EBUSY),或磁盘已满(ENOSPC),甚至只是 fopen() 内部缓冲区失败——这些都不是权限问题。
实操建议:
- 不要依赖
std::ifstream构造函数或is_open()的返回值做权限判断 - 避免抛出异常后捕获
std::ios_base::failure来反推权限——异常类型不携带具体系统错误码,信息丢失严重 - 跨平台宏封装时,优先分支处理:
#ifdef _WIN32用GetFileAttributes(),否则用access();别试图用stat()+ 手动解析st_mode,因为 Windows 的stat()权限字段意义不同
符号链接和挂载点容易被忽略
路径指向符号链接时,access() 默认检查**目标文件**权限(遵循 symlink),而 GetFileAttributes() 在 Windows 上默认检查**链接自身**(除非用 GetFileAttributesEx() 配合 GET_FILEEX_INFO_LEVELS::GetFileExInfoStandard)。挂载点(如 NFS、FUSE)也可能返回临时错误(ESTALE, ETIMEDOUT),这时权限检查应视为“未知”而非“拒绝”。
实操建议:
- 若需检查链接自身权限(例如判断能否修改 symlink 目标),Linux 下用
access(path, R_OK | X_OK)不行,得改用stat()+lstat()对比st_mode - 遇到
errno == ELOOP(循环链接)或GetLastError() == ERROR_NOT_READY(未挂载卷),应记录日志并降级处理,别硬当成权限不足 - 容器或沙箱环境(如 Flatpak、Snap)中,宿主机路径即使可读,也可能因 bind mount 权限限制对进程不可见——此时
access()返回 -1 是正常现象
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











