access(path, x_ok) 返回0表示有执行权限,它基于真实uid/gid、acl及noexec等挂载选项判断“能否执行”,比stat()+st_mode位运算更准确可靠。

怎么用 access() 检查可执行权限(最常用且可靠)
直接调用 access() 是检查当前进程对文件是否具有可执行权限的首选方式,它考虑了真实用户/组身份、ACL 和挂载选项(如 noexec),比单纯读取 stat() 的 st_mode 更准确。
关键点:
-
access(path, X_OK)返回 0 表示有执行权限;返回 -1 且errno == EACCES表示无权;errno == ENOENT表示文件不存在 - 它检查的是「能否执行」,不是「文件是否是可执行文件」——比如一个普通文本文件,只要权限位允许 +x 且未挂载为
noexec,access(..., X_OK)也可能返回成功 - 注意:在 setuid/setgid 程序中,
access()使用**实际用户 ID(real UID)** 判断,而非有效 UID,这是它和手动位运算的关键安全差异
为什么不能只靠 stat() + st_mode 位运算判断
很多人会读取 struct stat 后用位运算检查 st_mode & S_IXUSR 等,但这只是检查文件权限位,忽略了很多运行时约束:
- 挂载选项:文件系统以
noexec挂载时,即使S_IXUSR被置位,也无法执行 - SELinux/AppArmor 等 MAC 策略可能拒绝执行,但
stat()完全不体现 - 目录的可执行权限(
X_OK)实际表示「可进入」,而S_IXUSR在目录上也对应此语义——但新手常误以为它只用于二进制文件 - 如果进程的有效 UID/GID 与文件所有者不匹配,需同时检查
S_IXGRP和S_IXOTH,逻辑易错;而access()自动按进程实际身份做完整判定
st_mode 中执行位对应的宏和常见误用
若你确实需要解析 st_mode(例如做权限快照、日志审计),必须用标准宏,而不是硬写数字或移位:
- 用户执行位:
S_IXUSR(不是0100或1 —— 虽然值相同,但可移植性差) - 组执行位:
S_IXGRP - 其他用户执行位:
S_IXOTH - 检查是否任意执行位开启:
(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 - 注意:
S_ISREG(st.st_mode)必须先确认是常规文件,否则对目录或设备文件谈「可执行」容易误导——目录的X是「可遍历」,不是「可运行」
Linux 下 access() 的实际行为细节
access() 在 Linux 上的行为比 POSIX 更精细,但仍有边界情况需留意:
- 对符号链接:默认检查**目标文件**的权限(不跟随链接的权限位),符合直觉
- 对目录:检查是否能
chdir()进入(即X_OK语义),不是检查能否「运行目录」 - 性能:一次系统调用,开销远小于 fork+exec 失败再捕获错误
- 线程安全:
access()是异步信号安全的,可在信号处理函数中谨慎使用(但一般没必要) - 替代方案?
open(..., O_PATH | O_EXEC)(Linux 3.19+)可更早失败,但兼容性差,且仍不如access()通用
真正容易被忽略的是:即使 access(path, X_OK) == 0,后续 execve() 仍可能失败——比如文件被删(unlink() 后句柄仍有效)、格式不支持(非 ELF/脚本无解释器)、或磁盘 I/O 错误。权限检查只是必要非充分条件。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











