stat、fstat和lstat函数
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char* pathname, struct stat* buf);
int fstat(int filedes, struct stat* buf);
int lstat(const char* pathname, struct stat* buf);
/*三个函数的返回:若成功则为0,若出错则为-1*/
给定一个pathname,stat函数返回一个与此命名文件有关的信息结构,fstat函数获得已在描述符filedes上打开的文件的有关信息。lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息。
第二个参数是个指针,它指向一个我们应提供的结构。这些函数填写由buf指向的结构。该结构的实际定义可能随实现而有所不同,但其基本形式是:
struct stat {
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* i-node number (serialnumber) */
dev_t st_dev; /* device number (filesystem) */
dev_t st_rdev; /* device number for special files */
blink_t st_nlink; /* number of links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes, for regular files */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last file status change */
long st_blksize; /* best I/O block size */
long st_blocks; /* number of 512-byte blocks allocated */
};
除最后两个以外,其他各成员都为原始系统数据类型。
文件类型
- 普通文件(regular file)。最常见的文件类型,这种文件包含了某种形式的数据,对普通文件内容的解释由处理该文件的应用程序进行。
- 目录文件(directory file)。这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。
- 字符特殊文件(character special file)。这种文件用于系统中某些类型的设备。
- 块特殊文件(block special file)。这种文件典型地用于磁盘设备。系统中的所有设备或者
是字符特殊文件,或者是块特殊文件。 - FIFO。这种文件用于进程间的通信,有时也将其称为命名管道。
- 套接口(socket)。这种文件用于进程间的网络通信。
- 符号连接(symbolic link)。这种文件指向另一个文件。
文件类型信息包含在stat结构的st_mode成员中,在在sys/stat.h头文件定义类型宏。
| 宏 | 文件类型 |
|---|---|
| S_ISREG() | 普通文件 |
| S_ISDIR() | 目录文件 |
| S_ISCHR() | 字符特殊文件 |
| S_ISBLK() | 块特殊文件 |
| S_ISFIFO() | 管道或FIFO |
| S_ISLNK() | 符号连接 |
| S_ISSOCK() | 套接字 |
设置用户ID和设置组ID
与每个进程相关联的用户ID和组ID有:实际用户ID,实际组ID,有效用户ID,有效组ID,添加组ID,保存设置-用户-ID,保存设置-组-ID。实际用户ID和实际组ID标识我们究竟是谁。这两个字段在登录时取自口令文件中的登录项。通常,在一个登录会话期间这些值并不改变,但是超级用户进程有方法改变它们。有效用户ID,有效组ID以及添加组ID决定了我们的文件访问权。保存的设置-用户-ID和设置-组-ID在执行一个程序时包含了有效用户ID和有效组ID的副本。
通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。每个文件有一个所有者和组所有者,所有者由stat结构中的st_uid表示,组所有者则由st_gid成员表示。当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID,有效组ID通常是实际组ID。但是可以在文件方式字(st_mode)中设置一个特殊标志,其定义是“当执行此文件时,将进程的有效用户ID设置为文件的所有者(st_uid)”。与此相类似,在文件方式字中可以设置另一位,它使得执行此文件的进程的有效组ID设置为文件的组所有者(st_gid)。在文件方式字中的这两位被称之为设置-用户-ID(set-user-ID)位和设置-组-ID(set-group-ID)位,他们都包含在st_mode值中,可用常数S_ISUID和S_ISGID测试。
文件存取许可权
st_mode值也包含了对文件的存取许可权位。当提及文件时,指的是前面所提到的任何类型的文件。所有文件类型(目录,字符特别文件等)都有存取许可权。每个文件有9个存取许可权位,在sys/stat.h头文件中定义。
| st_mode | 含义 |
|---|---|
| S_IRUSR | 用户-读 |
| S_IWUSR | 用户-写 |
| S_IXUSR | 用户-执行 |
| S_IRGRP | 组-读 |
| S_IWGRP | 组-写 |
| S_IXGRP | 组-执行 |
| S_IROTH | 其他-读 |
| S_IWOTH | 其他-写 |
| S_IXOTH | 其他-执行 |
用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行许可权。
- 对于一个文件的读许可权决定了我们是否能够打开该文件进行读操作。这对应于open函数的O_RDONLY和O_RDWR标志。
- 对于一个文件的写许可权决定了我们是否能够打开该文件进行写操作。这对应于open函数的O_WRONLY和O_RDWR标志。
- 为了在open函数中对一个文件指定O_TRUNC标志,必须对该文件具有写许可权。
- 为了在一个目录中创建一个新文件,必须对该目录具有写许可权和执行许可权。
- 为了删除一个文件,必须对包含该文件的目录具有写许可权和执行许可权。对该文件本身则不需要有读、写许可权。
- 如果用6个exec函数中的任何一个执行某个文件,都必须对该文件具有执行许可权。
进程每次打开、创建或删除一个文件时,内核就进行文件存取许可权测试:
(1)若进程的有效用户ID是0(超级用户),则允许存取。这给予了超级用户对文件系统进行
处理的最充分的自由。
(2)若进程的有效用户ID等于文件的所有者ID(也就是该进程拥有此文件):
(a)若适当的所有者存取许可权位被设置,则允许存取。
(b)否则拒绝存取。
适当的存取许可权位指的是,若进程为读而打开该文件,则用户-读位应为1;若进程为写而打开该文件,则用户-写位应为1;若进程将执行该文件,则用户-执行位应为1。
(3)若进程的有效组ID或进程的添加组ID之一等于文件的组ID:
(a)若适当的组存取许可权位被设置,则允许存取。
(b)否则拒绝存取。
(4)若适当的其他用户存取许可权位被设置,则允许存取,否则拒绝存取。
新文件和目录的所有权
新文件的用户ID设置为进程的有效用户ID,新文件的组ID可以是进程的有效组ID或者是它所在目录的组ID。
access函数
access函数是按实际用户ID和实际组ID进行存取许可权测试的。
#include <unistd.h>
int access(const char* pathname, int mode);
/*返回:若成功则为0,若出错则为-1*/
mode参数在unistd.h中定义,是以下所列的逐位或运算
| mode | 说明 |
|---|---|
| R_OK | 测试读许可权 |
| W_OK | 测试写许可权 |
| X_OK | 测试执行许可权 |
| F_OK | 测试文件是否存在 |
umask函数
umask函数为进程设置文件方式创建屏蔽字,并返回以前的值。
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t cmask);
/*返回:以前的文件方式创建屏蔽字*/
参数cmask由9个权限常数逐位“或”构成。在进程创建一个新文件或新目录时,就一定会使用文件方式创建屏蔽字,在文件方式创建屏蔽字中为1的位,在文件mode中的相应位则一定被转成0。
chmod和fchmod函数
这两个函数可以更改现存文件的存取许可权。
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char* pathname, mode_t mode);
int fchmod(int filedes, mode_t mode);
/*两个函数返回:若成功则为0,若出错则为-1*/
chmod函数在指定的文件上进行操作,而fchmod函数则对已打开的文件进行操作。为了改变一个文件的许可权位,进程的有效用户ID必须等于文件的所有者,或者该进程必须具有超级用户许可权。参数mode在sys/stat.h文件中定义,是以下常数的某种逐位或运算。
| mode | 说明 |
|---|---|
| S_ISUID | 执行时设置-用户-ID |
| S_ISGID | 执行时设置-组-ID |
| S_ISVTX | 保存正文 |
| S_IRWXU | 用户(所有者)读、写和执行 |
| S_IRUSR | 用户(所有者)读 |
| S_IWUSR | 用户(所有者)写 |
| S_IXUSR | 用户(所有者)执行 |
| S_IRWXG | 组读、写和执行 |
| S_IRGRP | 组读 |
| S_IWGRP | 组写 |
| S_IXGRP | 组执行 |
| S_IRWXO | 其他读、写和执行 |
| S_IROTH | 其他读 |
| S_IWOTH | 其他写 |
| S_IXOTH | 其他执行 |
chmod函数在下列条件下自动清除两个许可权位。
- 如果我们试图设置普通文件的粘住位(S_ISVTX),而且又没有超级用户优先权,那么mode中的粘住位自动被关闭。这意味着只有超级用户才能设置普通文件的粘住位。
- 新创建文件的组ID可能不是调用进程所属的组。特别地,如果新文件的组ID不等于进程的有效组ID或者进程添加组ID中的一个,以及进程没有超级用户优先数,那么设置-组-ID位自动被关闭。
粘住位
如果对一个目录设置了粘住位,则只有对该目录具有写许可权的用户并且满足下列条件之一,才能删除或更名该目录下的文件:
- 拥有此文件。
- 拥有此目录。
- 是超级用户。
chown、fchown和lchown函数
chown函数可用于更改文件的用户ID和组ID。
#include<sys/types.h>
#include<unistd.h>
int chown(const char* pathname, uid_t owner,gid_t group);
int fchown(int filedes, uid_t owner, gid_t group);
int lchown(const char* pathname, uid_t owner, gid_t group);
/*三个函数返回:若成功则为0,若出错则为-1*/
在符号连接情况下,lchown更改符号连接本身的所有者,而不是该符号连接所指向的文件。
若_POSIX_CHOWN_RESTRICTED对指定的文件起作用,则
(1)只有超级用户进程能更改该文件的用户ID。
(2)若满足下列条件,一个非超级用户进程可以更改该文件的组ID:
(a)进程拥有此文件(其有效用户ID等于该文件的用户ID)。
(b)参数owner等于文件的用户ID,参数group等于进程的有效组ID或进程的添加组ID之一。
这意味着,当_POSIX_CHOWN_RESTRICTED有效时,不能更改其他用户的文件的用户ID。你可以更改你所拥用的文件的组ID,但只能改到你所属于的组。如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置-用户-ID位和设置-组-ID位都被清除。
文件长度
stat结构的成员st_size包含了以字节为单位的该文件的长度。此字段只对普通文件、目录文件和符号连接有意义。对于普通文件,其文件长度可以是0,在读这种文件时,将得到文件结束指示。对于目录,文件长度通常是一个数的整倍数。对于符号连接,文件长度是在文件名中的实际字节数。
文件截短
有时我们需要在文件尾端处截去一些数据以缩短文件。将一个文件的长度截短为0是一个特例,用O_TRUNC标志可以做到这一点。为了截短文件可以调用函数truncate和ftruncate。
#include <sys/types.h>
#include <unistd.h>
int truncate(const char* pathname, off_t length);
int ftruncate(int filedes, off_t length);
/*两个函数返回;若成功则为0,若出错则为-1*/
这两个函数将由路径名pathname或打开文件描述符filedes指定的一个现存文件的长度截短为length。如果该文件以前的长度大于length,则超过length以外的数据就不再能存取。如果以前的长度短于length,则其后果与系统有关。如果某个实现的处理是扩展该文件,则在以前的文件尾端和新的文件尾端之间的数据将读作0。
文件系统
把一个磁盘分成一个或多个分区,每个分区可以包含一个文件系统。磁盘、分区和文件系统的关系如下:
i节点是固定长度的记录项,它包含有关文件的信息。较为详细的文件系统的表示如下:
- 在图中有两个目录项指向同一i节点。每个i节点中都有一个连接计数,其值是指向该i节点的目录项数。只有当连接计数减少为0时,才可删除该文件(也就是可以释放该文件占用的数据块)。在stat结构中,连接计数包含在st_nlink成员中,其基本系统数据类型是nlink_t。
- 另外一种连接类型称之为符号连接(symbolic link)。对于这种连接,该文件的实际内容(在数据块中)包含了该符号连接所指向的文件的名字。
- i节点包含了所有与文件有关的信息:文件类型、文件存取许可权位、文件长度和指向该文件所占用的数据块的指针等等。stat结构中的大多数信息都取自i节点。只有两项数据存放在目录项中:文件名和i节点编号数。i节点编号数的数据类型是ino_t。
- 因为目录项中的i节点编号数指向同一文件系统中的i节点,所以不能使一个目录项指向另一个文件系统的i节点。
- 当在不更改文件系统的情况下为一个文件更名时,该文件的实际内容并未移动,只需构造一个指向现存i节点的新目录项,并删除老的目录项。
本文介绍了如何使用stat、fstat和lstat函数获取文件信息,包括文件类型、权限和其他属性。同时,还详细讲解了如何利用chmod、chown等函数修改文件的权限和所有权。
2081

被折叠的 条评论
为什么被折叠?



