stat、fstat、lstat函数:
本章讨论的中心是三个stat函数以及它们返回的信息:
#include <sys/stat.h>
int stat(const char *restrict pathname,struct stat *restrict buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
三个函数的返回值:若成功返回0,若出错则返回-1.
stat函数返回pathname所指定文件的有关的信息结构,fstat函数获取已在描述符filedes上打开文件的有关信息,lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由符号链接所指向文件的信息。
第二个参数buf是个指向struct stat结构体的指针,struct stat的实际定义可能随实现有所不同,但其基本形式是:
struct stat {
mode_t st_mode; /* file type & mode(permissions) */};
该结构的每一个成员都是基本系统数据类型,我们将说明此结构的每一个成员以了解文件的属性。使用stat函数最多的可能是ls -l命令,用其可以获得有关一个文件的所有信息。
文件类型:
UNIX系统的大多数文件是普通文件或目录,但也有另外的一些文件类型,文件类型包括以下几种:
(1)普通文件:这是最常用的文件类型,这种文件包含了某种形式的数据,这种数据是文本还是二进制数据对于UNIX内核而言并无区别。值得注意是,对于二进制可执行文件,为了执行程序,内核必须理解其格式,所有二进制可执行文件都遵循一种格式,这种格式使内核能够确定程序文本和数据加载的位置。
(2)目录文件:这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。
(3)块特殊文件:这种文件类型提供对设备(例如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。
(4)字符特殊文件:这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
(5)FIFO:这种文件类型用于进程间通信,有时也将其称为命名管道。
(6)套接字(socket):这种文件类型用于进程间的网络通信。套接字也可用于在一台宿主机上进程之间的非网络通信。
(7)符号链接:这种文件类型指向另一个文件。
文件类型信息包含在stat结构体的st_mode成员中,可用下列宏确定文件类型,这些宏的参数都是stat结构中的st_mode成员:
POSIX.1允许实现将进程间通信(IPC)对象(例如,消息队列和信号量)表示为文件。可用如下宏来确定IPC对象的类型,这些宏的参数是指向stat结构的指针:
/* * Copyright (C) [email protected] */ #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i; struct stat buf; char *pt; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { printf("lstat error\n"); continue; } if (S_ISREG(buf.st_mode)) { pt = "regular"; } else if (S_ISDIR(buf.st_mode)) { pt = "directory"; } else if (S_ISBLK(buf.st_mode)) { pt = "block special"; } else if (S_ISCHR(buf.st_mode)) { pt = "character special"; } else if (S_ISFIFO(buf.st_mode)) { pt = "FIFO"; } else if (S_ISSOCK(buf.st_mode)) { pt = "socket"; } else if (S_ISLNK(buf.st_mode)) { pt = "symbol link"; } else { pt = "unknown mode"; } printf ("%s\n", pt); } exit(0); }
以下是该程序的执行结果:
该程序特地使用了lstat函数而不是stat函数以便检测符号链接,如果使用了stat函数,则不会观察到符号链接。
设置用户ID和设置组ID:
与一个进程相关联的ID有6个或更多:
通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。
每个文件都有一个所有者和组所有者,所有者由stat结构中的st_uid成员表示,组所有者由st_gid成员表示。当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID,有效组ID通常是实际组ID。但是可以在文件模式字st_mode中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)”。与此类似,在文件模式字中可以设置另一位,它使得将执行此文件的进程的有效组ID设置为文件的组所有者ID(st_gid)。在文件模式字中的这两位被称为设置用户ID位(set-user-ID)和设置组ID位(set-group-ID)。设置用户ID位及设置组ID位都保存在st_mode值中,这两位可用常量S_ISUID和S_ISGID测试。
UNIX程序passwd就是一个设置了设置用户ID的可执行程序。
文件访问权限:
st_mode值中也包含了针对文件的访问权限位。所有文件都有访问权限。每个文件有9个访问权限位,chmod命令可用于修改这9个权限位。
关于文件的权限,有如下注意事项:
进程每次打开、创建、删除文件时,内核就进行文件访问权限测试,这种测试可能涉及文件的所有者(st_uid和st_gid),进程的有效ID(有效用户ID,有效组ID)以及进程的附加组ID(如果支持的话)。两个所有者ID是文件的性质,而两个有效ID和和附加组ID则是进程的性质。
文件访问权限测试的过程为(按顺序执行这四步):
新文件的用户ID设置为进程的有效用户ID,关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID:
对于Linux系统,新文件的组ID取决于它所在目录的设置组ID位是否被设置,如果该目录的这一位已经设置,则将新文件的组ID设置为目录的组ID,否则将新文件的组ID设置为进程的有效组ID。