access() 是类 unix 系统判断读权限最直接方式,仅查元数据不打开文件;windows 上行为不同,检查 dacl 中“读取属性”而非 r-bit,且无法反映 selinux/apparmor 等策略拦截。

用 access() 判断读取权限最直接
在类 Unix 系统(Linux/macOS)上,access() 是标准且轻量的方式,它绕过 C++ 文件流、不打开文件,只查文件系统元数据。Windows 上也能用(通过 MSVC CRT 或 MinGW),但行为略有差异——它检查的是 DACL 中的“读取属性”权限,而非严格等价于 Unix 的 r-bit。
关键点:access() 不验证当前进程是否能实际打开该文件(比如被 SELinux/AppArmor 拦截),只反映基础 ACL 或 mode 位。
- 传入路径必须是
const char*,中文路径需确保编码与 locale 一致(Linux 下通常是 UTF-8,Windows 默认 ANSI) - 检查读权限用标志
R_OK;Windows 上access(path, R_OK)成功仅表示“有读取文件属性权”,不一定能fopen(..., "r") - 返回 0 表示有权限,-1 表示无权限或路径不存在;需配合
errno区分:若errno == ENOENT,说明路径根本不存在
#include <unistd.h> // Linux/macOS
#include <io.h> // Windows (MSVC)
#include <errno.h><p>bool can_read(const char* path) {
if (access(path, R_OK) == 0) return true;
if (errno == ENOENT) return false; // 路径不存在,不是权限问题
return false; // 权限不足或其他错误(如 EACCES)
}</p></errno.h></io.h></unistd.h>
Windows 上用 GetFileAttributes() 更可靠
当目标是 Windows 原生开发(尤其需要兼容 NTFS 加密/压缩/符号链接等特性),GetFileAttributes() 比 access() 更贴近系统语义。它返回文件属性掩码,但注意:它**不检查 DACL**,只判断是否为只读文件(FILE_ATTRIBUTE_READONLY)——这和“能否读取”不是一回事。
真正要模拟“用户是否有读权限”,必须调用 AccessCheck() 配合 GetNamedSecurityInfo(),但这太重。实践中,多数场景只需确认:路径存在 + 不是只读 + 当前用户属于所有者或组(靠 GetFileSecurity() 解析 ACL 太复杂,一般跳过)。
-
GetFileAttributes()对目录也有效,返回INVALID_FILE_ATTRIBUTES表示路径无效或无访问权 - 它无法区分“无权限”和“路径不存在”,两者都返回
INVALID_FILE_ATTRIBUTES,需额外用PathFileExists()或FindFirstFile()辅助判断 - 对符号链接默认解析目标;若需检查链接本身权限,得用
CreateFile()加FILE_FLAG_OPEN_REPARSE_POINT
跨平台封装时别忽略 stat() 的权限字段
stat() 在 Unix 和 Windows(MinGW/MSVC 2015+)都可用,它返回 struct stat,其中 st_mode 字段含完整权限位。这是唯一能同时拿到“是否为目录”“是否可读”“所有者/组/其他权限”的方式。
但要注意:st_mode & S_IRUSR 只表示 owner 有读权限,不代表当前进程就是 owner——还需比对 st_uid 和 getuid();同理,组权限要查 getgid() 和 getgroups()。生产环境极少这么细粒度校验。
- Unix 下:用
(st.st_mode & S_IRUSR) || (st.st_mode & S_IRGRP) || (st.st_mode & S_IROTH)粗略判断“至少一类用户可读” - Windows 下:
stat()的st_mode仅模拟 Unix 权限,对 NTFS ACL 完全无效;S_IRUSR永远为真(除非文件标记为只读) - 路径含软链接时,
stat()默认解析目标;要获取链接本身信息,用lstat()(Unix)或_stat64i32()(Windows 不支持)
真正想“读”,就直接 fopen() 或 std::ifstream
所有静态权限检查都有竞态风险:检查时有权限,打开瞬间被 chmod 或策略拦截。最可靠的判断方式,就是尝试打开——这也是 POSIX 推荐做法("do the operation, check errno")。
用 std::ifstream 时,.is_open() 为 false 不一定代表无读权限(可能是路径错、磁盘满、inode 耗尽);需结合 errno(Linux/macOS)或 GetLastError()(Windows)进一步定位。
- 避免先
access()再fopen():两次系统调用 + 竞态窗口 - 若需区分“无权限”和“文件不存在”,打开失败后检查
errno == EACCES(Unix)或GetLastError() == ERROR_ACCESS_DENIED(Windows) - 对普通应用,直接尝试打开并处理失败,比写一堆权限适配逻辑更健壮、更少出错
权限检查的边界很模糊:操作系统层、文件系统层、安全策略层(SELinux/AppArmor/Windows Defender Application Control)各管一段。你写的代码只能触达最外层的 DACL 或 mode bits,再往里就得依赖系统工具了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











