直接使用strace -e trace=fchmodat,fchownat,utimensat,utimes,ioctl命令可精准捕获chmod/chown/utime类系统调用,避免被openat、stat等无关调用干扰;需显式列举而非笼统用trace=file,因属性变更仅通过少数写操作调用实现,且fchmodat/fchownat/utimensat是现代程序主流实现。

怎么用 strace 抓到 chmod/chown/utime 触发的系统调用
直接对命令加 strace -e trace=chmod,fchmodat,chown,fchownat,utimensat,utimes 就能精准捕获属性变更类系统调用,不混入 read/write 等无关调用。
常见错误是只写 strace cmd 或笼统用 -e trace=file,结果被大量 openat、stat 噪声淹没。实际属性变更只走少数几个系统调用,必须显式列出。
-
chmod和chown命令底层几乎都转成fchmodat和fchownat(带 dirfd 的版本),而非旧式chmod/chown -
touch -m或touch -a会触发utimensat;老内核或某些 libc 可能回落到utimes - 如果目标文件在 symlink 后面,注意加
-L参数让strace跟符号链接,否则可能看到lchown而非fchownat
示例:strace -e trace=fchmodat,fchownat,utimensat -f touch /tmp/test.txt —— 输出里能看到具体哪个系统调用被调、传了什么参数、返回值是否为 0。
为什么 stat 和 ls -l 不算“属性变更”系统调用
stat、lstat、fstatat 是只读查询,它们不改变任何属性,只是读取当前值。用户常误以为“看到属性变了”就等于“有变更调用”,其实不是。
真正触发属性变更的只有写操作类系统调用:
-
fchmodat(含chmod命令) -
fchownat(含chown/chgrp) -
utimensat(含touch、cp --preserve=timestamps) -
ioctl配合特定 request(如 ext4 的FS_IOC_SETFLAGS,对应chattr +i)
注意:chattr +i 这类扩展属性变更走的是 ioctl,不是 fchmodat,必须单独加 -e trace=ioctl 并结合 strace -v 看 request code 才能确认。
如何区分是进程主动改属性,还是内核/其他进程间接导致
单靠 strace 只能告诉你“谁发出了系统调用”,但无法判断调用动机。比如:
- 一个
rsync进程调用fchmodat,大概率是同步权限;但如果是systemd调用,可能是服务重启时重置 socket 文件权限 -
utimensat被触发,可能是用户手动touch,也可能是编辑器保存时自动更新 mtime - 若看到
fchownat(..., AT_SYMLINK_NOFOLLOW),说明程序明确想改 symlink 本身属性(极少见),多数情况是 bug 或误配
要定位源头,得结合 strace -p PID -f attach 到可疑进程,或用 auditd 配合 -F arch=b64 -S fchmodat,fchownat,utimensat 记录完整上下文(包括 UID、comm、exe 路径)。
inotifywait 看不到系统调用,只看到结果事件
inotifywait -e attrib 能感知到文件属性变化(比如权限位、所有者、时间戳变),但它不告诉你是谁、用什么系统调用改的——它监听的是 VFS 层的事件通知,比系统调用高一层。
这意味着:
- 你看到
/path IN_ATTRIB,但不知道是chmod还是cp --preserve干的 - 如果变更来自 mmap 写 +
msync,inotify可能不触发IN_ATTRIB(取决于是否改了 mtime) -
inotify对chattr设置的扩展属性(如i)完全无感,因为那不经过 VFS 属性接口
所以,查“谁触发了变更”,必须用 strace 或 auditd;查“有没有变更发生”,inotifywait 更轻量实时。
真正难的不是抓调用,而是把一次 fchmodat 调用和业务逻辑对应起来——比如发现某个配置文件的 gid 总在凌晨 3 点被改成 0,得顺着 strace 输出里的 comm 和 exe 去翻 cron job 或 systemd timer。这种关联性没法自动化,得人盯日志细节。


















