
Linux sys_stat vfs_statx与cp_new_stat64填充stat(2) 系列系统调用在内核的统一入口是 vfs_statx。64 位架构上通过 ksys_stat 进入clong ksys_stat(const char __user *filename, struct kstat __user *statbuf){struct kstat stat;int error;error vfs_stat(filename, stat);if (error)return error;return cp_new_stat(stat, statbuf);}vfs_stat 是对 vfs_statx 的简单的封装cint vfs_stat(const char __user *name, struct kstat *stat){return vfs_statx(AT_FDCWD, name, 0, stat, STATX_BASIC_STATS);}vfs_statx 是 VFS stat 路径的核心函数cint vfs_statx(int dfd, const char __user *filename, int flags,struct kstat *stat, u32 request_mask){struct path path;unsigned int lookup_flags 0;int error;if (flags ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |AT_STATX_SYNC_TYPE))return -EINVAL;if (flags AT_SYMLINK_NOFOLLOW)lookup_flags | LOOKUP_FOLLOW;retry:error user_path_at(dfd, filename, lookup_flags, path);if (error)goto out;error vfs_getattr(path, stat, request_mask, flags);path_put(path);if (retry_estale(error, lookup_flags)) {lookup_flags | LOOKUP_REVAL;goto retry;}out:return error;}user_path_at 使用 path_init link_path_walk 的路径遍历机制将用户态路径解析为 struct path包含 dentry 和 vfsmount。如果文件名是/proc/self/exe或包含符号链接路径遍历会在适当位置跟踪符号链接。vfs_getattr 是获取文件属性的调度中心cint vfs_getattr(const struct path *path, struct kstat *stat,u32 request_mask, unsigned int query_flags){struct inode *inode d_backing_inode(path-dentry);memset(stat, 0, sizeof(*stat));stat-result_mask 0;if (inode-i_op-getattr)return inode-i_op-getattr(path, stat, request_mask, query_flags);generic_fillattr(inode, stat);return 0;}大多数文件系统都实现了自己的 getattr。ext4 的 ext4_getattr 在调用 generic_fillattr 之前额外检查 inode 是否具有 EXT4_EA_INODE_FL 标志并报告 i_projidproject ID给 statx。generic_fillattr 将 inode 中的原始信息填入 struct kstatcvoid generic_fillattr(struct inode *inode, struct kstat *stat){stat-dev inode-i_sb-s_dev;stat-ino inode-i_ino;stat-mode inode-i_mode;stat-nlink inode-i_nlink;stat-uid from_kuid_munged(init_user_ns, inode-i_uid);stat-gid from_kgid_munged(init_user_ns, inode-i_gid);stat-rdev inode-i_rdev;stat-size i_size_read(inode);stat-atime inode-i_atime;stat-mtime inode-i_mtime;stat-ctime inode-i_ctime;stat-blksize i_blocksize(inode);stat-blocks inode-i_blocks;}struct kstat 是内核内部的通用结构体包含 64 位的时间戳、块大小和块计数等信息不依赖架构。但用户态的 struct stat 在不同架构上布局不同。cp_new_stat 负责将 kstat 转换为用户态可见的格式cstatic int cp_new_stat(struct kstat *stat, struct stat __user *statbuf){struct stat tmp;tmp.st_dev new_encode_dev(stat-dev);tmp.st_ino stat-ino;tmp.st_mode stat-mode;tmp.st_nlink stat-nlink;tmp.st_uid from_kuid_munged(init_user_ns, stat-uid);tmp.st_gid from_kgid_munged(init_user_ns, stat-gid);tmp.st_rdev new_encode_dev(stat-rdev);tmp.st_size stat-size;tmp.st_atime stat-atime.tv_sec;tmp.st_mtime stat-mtime.tv_sec;tmp.st_ctime stat-ctime.tv_sec;#ifdef STAT_HAVE_NSECtmp.st_atime_nsec stat-atime.tv_nsec;tmp.st_mtime_nsec stat-mtime.tv_nsec;tmp.st_ctime_nsec stat-ctime.tv_nsec;#endiftmp.st_blocks stat-blocks;tmp.st_blksize stat-blksize;return copy_to_user(statbuf, tmp, sizeof(tmp));}在 x86_64 架构上struct stat 的大小为 144 字节包含 st_atime_nsec 等纳秒字段。new_encode_dev 将内核使用的 dev_t包含 major 和 minor 号编码为用户态可理解的形式。对于 32 位用户态在 64 位内核上运行CONFIG_COMPAT需要 compat_sys_stat64 或 compat_sys_newfstatat 等入口。compat 版本使用 struct compat_stat64其字段宽度与 32 位 glibc 对齐避免在 64 位内核和 32 位用户态之间传递错误布局的数据。statx(2) 系统调用提供了更细粒度的控制。它不经过传统的 vfs_stat 路径而是直接调用 vfs_statx 后返回 struct statx 给用户态。struct statx 包含 stx_mask 位图标记哪些字段有效允许调用方只请求自己关心的属性request_mask在网络文件系统等场景下减少 RPC 开销。