stat(2) - Linux 手册页
名称
stat, fstat, lstat - 获取文件状态
概要
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
glibc 的功能测试宏要求(参见 feature_test_macros(7))
- lstat():
- _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED
|| /* 自 glibc 2.10 起: */ _POSIX_C_SOURCE >= 200112L
描述
这些函数返回有关文件的信息。文件本身不需要任何权限,但在使用 stat() 和 lstat() 时,需要对 path 中通往该文件的所有目录拥有执行(搜索)权限。
stat() 获取 path 指向的文件状态,并填充到 buf 中。
lstat() 与 stat() 相同,唯一的区别是如果 path 是一个符号链接,则获取的是链接本身的状态,而不是它所指向的文件。
fstat() 与 stat() 相同,唯一的区别是需要获取状态的文件是通过文件描述符 fd 指定的。
所有这些系统调用都会返回一个 stat 结构体,其中包含以下字段
-
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; - st_dev 字段描述了该文件驻留的设备。(major(3) 和 minor(3) 宏可用于解析该字段中的设备 ID。)
st_rdev 字段描述了该文件(inode)所表示的设备。
st_size 字段给出文件的大小(如果是普通文件或符号链接),单位为字节。符号链接的大小是其所包含路径名的长度,不包含结尾的空字节。
st_blocks 字段表示分配给文件的块数,单位为 512 字节。(当文件有空洞时,此值可能小于 st_size/512。)
st_blksize 字段给出了文件系统进行高效 I/O 操作的“首选”块大小。(以较小的块写入文件可能会导致低效的读取-修改-重写。)
并非所有的 Linux 文件系统都实现了所有的时间字段。某些文件系统类型允许以一种方式挂载,使得文件和/或目录的访问不会更新 st_atime 字段。(请参阅 mount(8) 中的 noatime、nodiratime 和 relatime,以及 mount(2) 中的相关信息。)此外,如果文件是以 O_NOATIME 打开的,则不会更新 st_atime;请参阅 open(2)。
st_atime 字段会在文件被访问时发生改变,例如通过 execve(2)、mknod(2)、pipe(2)、utime(2) 和 read(2)(读取超过零字节)。其他例程,如 mmap(2),可能会也可能不会更新 st_atime。
st_mtime 字段会在文件被修改时发生改变,例如通过 mknod(2)、truncate(2)、utime(2) 和 write(2)(写入超过零字节)。此外,目录的 st_mtime 会随着该目录下文件的创建或删除而改变。st_mtime 字段不会因所有者、组、硬链接数或模式的更改而改变。
st_ctime 字段会在写入数据或设置 inode 信息(即所有者、组、链接数、模式等)时发生改变。
定义了以下 POSIX 宏来通过 st_mode 字段检查文件类型
- S_ISREG(m)
是否是普通文件?
S_ISDIR(m)
目录?
S_ISCHR(m)
字符设备?
S_ISBLK(m)
块设备?
S_ISFIFO(m)
FIFO(命名管道)?
S_ISLNK(m)
符号链接?(不在 POSIX.1-1996 中。)
S_ISSOCK(m)
套接字?(不在 POSIX.1-1996 中。)
- 为 st_mode 字段定义了以下标志
- 设置组 ID 位 (S_ISGID) 有几种特殊用途。对于目录,它表示该目录应使用 BSD 语义:在此目录下创建的文件会从目录继承其组 ID,而不是从创建进程的有效组 ID 继承,并且在此目录下创建的目录也会设置 S_ISGID 位。对于未设置组执行位 (S_IXGRP) 的文件,设置组 ID 位表示强制文件/记录锁定。
目录上的粘滞位 (S_ISVTX) 表示该目录中的文件只能由文件所有者、目录所有者或特权进程重命名或删除。
返回值
成功时返回零。出错时返回 -1,并相应地设置 errno。
错误
- EACCES
path 的路径前缀中的某个目录拒绝搜索权限。(另请参阅 path_resolution(7)。)
EBADF
fd 无效。
EFAULT
地址错误(Bad address)。
ELOOP
在遍历路径时遇到过多的符号链接。
- ENAMETOOLONG
- path 太长。
- ENOENT
path 的某个组成部分不存在,或 path 是空字符串。
ENOMEM
内存不足(即内核内存)。
- ENOTDIR
- path 的路径前缀的某个组成部分不是目录。
- EOVERFLOW
- path 或 fd 指向的文件,其大小、inode 编号或块数无法分别由 off_t、ino_t 或 blkcnt_t 类型表示。当例如在未定义 -D_FILE_OFFSET_BITS=64 的 32 位平台上编译的应用程序调用 stat() 操作一个大小超过 (1<<31)-1 字节的文件时,可能会发生此错误。
符合
这些系统调用符合 SVr4、4.3BSD、POSIX.1-2001。
根据 POSIX.1-2001,对符号链接使用 lstat() 仅需在 stat 结构体中返回 st_size 字段和 st_mode 字段的文件类型部分中的有效信息。POSIX.1-2008 强化了该规范,要求 lstat() 在除 st_mode 中的权限位之外的所有字段中都返回有效信息。
使用 st_blocks 和 st_blksize 字段的可移植性可能较差。(它们是在 BSD 中引入的。在不同系统之间,甚至在同一系统涉及 NFS 挂载时,解释可能会有所不同。)如果需要从 <sys/stat.h> 获取 blkcnt_t 或 blksize_t 类型的定义,请定义 _XOPEN_SOURCE 且值为 500 或更高(在包含任何头文件之前)。
POSIX.1-1990 未描述 S_IFMT, S_IFSOCK, S_IFLNK, S_IFREG, S_IFBLK, S_IFDIR, S_IFCHR, S_IFIFO, S_ISVTX 常量,而是要求使用 S_ISDIR() 等宏。S_IF* 常量在 POSIX.1-2001 及更高版本中存在。
S_ISLNK() 和 S_ISSOCK() 宏不在 POSIX.1-1996 中,但在 POSIX.1-2001 中两者都存在;前者来自 SVID 4,后者来自 SUSv2。
UNIX V7(及后续系统)有 S_IREAD, S_IWRITE, S_IEXEC,而 POSIX 规定了其同义词 S_IRUSR, S_IWUSR, S_IXUSR。
其他系统
- 在各种系统中使用过(或正在使用)的值
- 粘滞位命令出现在 AT&T UNIX 第 32V 版中。
说明
从内核 2.5.48 开始,stat 结构体支持三个文件时间戳字段的纳秒级分辨率。如果定义了 _BSD_SOURCE 或 _SVID_SOURCE 特性测试宏,Glibc 会使用 st_atim.tv_nsec 格式的名称公开每个字段的纳秒分量。这些字段在 POSIX.1-2008 中有规定,且自 2.12 版本起,如果定义了 _POSIX_C_SOURCE 且值为 200809L 或更高,或者定义了 _XOPEN_SOURCE 且值为 700 或更高,glibc 也会公开这些字段名称。如果未定义上述任何宏,则纳秒值将以 st_atimensec 格式的名称公开。在不支持亚秒时间戳的文件系统上,纳秒字段返回的值为 0。
在 Linux 上,lstat() 通常不会触发自动挂载程序操作,而 stat() 会触发(但请参阅 fstatat(2))。
对于 /proc 目录下的大多数文件,stat() 不会返回 st_size 字段中的文件大小;相反,该字段返回的值为 0。
底层内核接口
- 随着时间的推移,stat 结构体大小的增加导致了三个连续版本的 stat():sys_stat() (插槽 __NR_oldstat), sys_newstat() (插槽 __NR_stat), 和 sys_stat64() (内核 2.4 中新增;插槽 __NR_stat64)。Glibc 的 stat() 包装函数对应用程序隐藏了这些细节,调用内核提供的最新版本的系统调用,并在需要时为旧二进制文件重新打包返回的信息。对于 fstat() 和 lstat() 也适用类似的说明。
示例
以下程序调用 stat() 并显示返回的 stat 结构体中选定的字段。
#include <sys/types.h> #include <sys/stat.h> #include <time.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { struct stat sb; if (argc != 2) { fprintf(stderr, "Usage: %s <pathname>\n", argv[0]); exit(EXIT_FAILURE); } if (stat(argv[1], &sb) == -1) { perror("stat"); exit(EXIT_FAILURE); } printf("File type: "); switch (sb.st_mode & S_IFMT) { case S_IFBLK: printf("block device\n"); break; case S_IFCHR: printf("character device\n"); break; case S_IFDIR: printf("directory\n"); break; case S_IFIFO: printf("FIFO/pipe\n"); break; case S_IFLNK: printf("symlink\n"); break; case S_IFREG: printf("regular file\n"); break; case S_IFSOCK: printf("socket\n"); break; default: printf("unknown?\n"); break; } printf("I-node number: %ld\n", (long) sb.st_ino); printf("Mode: %lo (octal)\n", (unsigned long) sb.st_mode); printf("Link count: %ld\n", (long) sb.st_nlink); printf("Ownership: UID=%ld GID=%ld\n", (long) sb.st_uid, (long) sb.st_gid); printf("Preferred I/O block size: %ld bytes\n", (long) sb.st_blksize); printf("File size: %lld bytes\n", (long long) sb.st_size); printf("Blocks allocated: %lld\n", (long long) sb.st_blocks); printf("Last status change: %s", ctime(&sb.st_ctime)); printf("Last file access: %s", ctime(&sb.st_atime)); printf("Last file modification: %s", ctime(&sb.st_mtime)); exit(EXIT_SUCCESS); }
参见
access(2), chmod(2), chown(2), fstatat(2), readlink(2), utime(2), capabilities(7), symlink(7)