文件系统(file system)
Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其中。
从图中可以看出,一个硬盘可以划分为很多个分区,而每个分区则可以有一个文件系统。每个文件系统由逻辑块的序列组成,一个逻辑盘空间一般划分为几个用途各不相同的部分,即引导块、超级块、inode区以及数据区等。
引导块:在文件系统的开头,通常为一个扇区,其中存放引导程序,用于读入并启动操作系统;超级块:用于记录文件系统的管理信息。特定的文件系统定义了特定的超级块;inode区(索引节点):一个文件或目录占据一个索引节点。第一个索引节点是该文件系统的根节点。利用根节点,可以把一个文件系统挂在另一个文件系统的非叶节点上;数据区:用于存放文件数据或者管理数据。
大家可以看见,图中描述的内容还多了一级划分,那就是组(cylinder group)。一个组包含:超级块拷贝,组描述符表,块位图,inode位图,inode表,以及数据块。关于组(Cylinder group)的i-nodes和datablocks的更细节的地方展示如下:
从图中可以看见,在目录数据块里面每一个目录项存储的是文件名和i节点号,注意有两个不同目录的目录项的i节点号块都“指向”了同一个i节点,这说明,该文件同时被两个目录所包含,这就是
硬链接的概念。每一个i节点里面有一个计数器用于记录指向它的硬链接的个数,只有当该计数器减至0时,该文件才会被真正删除。
然后说明下
符号链接(symbolic link)的概念,上面说了,硬链接是由i节点直接指向对应的文件的数据块,而符号链接,它有自己的数据块,只不过,该数据库里面只存储有真正所指向文件的路径。在i节点里有标识可以说明这是一个符号链接。
Stat结构体
stat结构体是用于存储文件信息的载体,在不同的实现上它的细节可能会有所区别,其成员大致如下:
struct stat {
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* i-node number (serial number) */
dev_t st_dev; /* device number (file system) */
dev_t st_rdev; /* device number for special files */
nlink_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 */
blksize_t st_blksize; /* best I/O block size */
blkcnt_t st_blocks; /* number of disk blocks allocated */
};
对于各个成员的作用,在注释中已经有了说明。
比较迷惑的是第三个成员st_dev,结合前面关于文件系统的说明,我们可以了解到,一个一个硬盘可以划分为很多个分区,而每个分区则可以有一个文件系统。实际上st_dev就是关于文件系统类型的说明,这个成员更细节的包含两个部分:主设备号和次设备号。主设备号用来标识实际的物理设备(即硬盘),次设备号则用来标识子设备(即文件系统分区)。而其后的成员st_rdev也是设备号的说明。不同的是,它只有在文件为特殊文件(设备文件)时才有效,用来标识该设备的主设备号和次设备号。
获取stat结构体的函数有三:
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);
第三个函数和第一个函数的区别在于对符号链接的处理上,如果指定文件名是一个符号链接,第一个函数返回链接指向的文件的信息结构体,而第三个函数则返回该链接本身的信息结构体。
然后是重点关于第一个数据成员st_mode的说明,它反映了文件的类型以及访问权限等重要信息。
关于文件类型信息可以由以下宏来获得:
关于其访问权限信息则由以下常量来判断:
Set-User-ID 位与 Set-Group-ID 位
考虑这样一种情况,我们知道linux对于各个用户的访问权限都是有着严格的限制的。但是,即使是普通用户,他们也都可以修改自己的密码,我们知道,修改密码是需要访问到/etc/passwd文件以及 /etc/shadow文件的,而普通用户是没有权限修改这些文件的,那么这是怎么办到的呢?
要回答这个问题,首先得聊解每个进程都有6个或更多的与之相关联的ID,如下所示:
real user ID real group ID |
who we really are |
effective user ID effective group ID supplementary group IDs |
used for file access permission checks |
saved set-user-ID saved set-group-ID |
saved by exec functions |
表格中的信息表明了这些位的作用。
我们重点看第二行的数据,它们决定了文件的访问权限检差。在st_mode里面,关于访问权限的部分有两个特殊的位,它们是Set-User-ID 位与 Set-Group-ID 位。如果一个文件的这两个位被设置了,当该文件被执行时,进程的effective位就会被设置成该文件的实际拥有者而非进程的实际执行者。所以,我们可以想到,passwd文件的这两位是被设置了的,而它的实际拥有者是root用户,也就是说无论是谁,在执行passwd时都将拥有root用户的权限,也就解决了我们刚刚所提的问题了。