目录
正如文件描述符一章说过,在Unix/Linux中万事万物都是文件,目录也是文件。
“目录”本质上与一本书的目录没有差别。一个目录实为一个文件,其内容为一系列条目。每一个条目为文件名与“页码”的对应,“页码”即为文件存储的位置。
目录入口(dirent)结构
Linux中的目录同图书的目录十分相似,一个目录是一个特殊的文件,其内容为一系列条目。每个条目,即每个目录入口结构,中最为重要的是文件名(d_name)和其“页码”(d_ino)——在Linux中是一个称为i-node number(i节点号)的整数。完整的条目结构如下:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
编写ls命令
ls命令的目的,就是打开一个目录文件,并将其每一个目录入口结构中的文件名打印出来。由于不同的Unix/Linux的版本或不同的文件系统在目录文件结构上有所出入,不建议使用系统调用open/read/close
来处理目录文件。我们可以通过
man -k directory|grep open
来查找一下如何打开一个目录。
其中,opendir (3p)就是我们需要的,而且它是POSIX标准。当然opendir (3)也没问题。
man 3p opendir
基本的ls
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
main(int ac, char *av[]){
DIR *dp;
struct dirent *p;
if(1==ac){
dp=opendir(".");
}
else{
if((dp=opendir(av[1]))==NULL){
perror("cannot open");
exit(1);
}
}
while((p=readdir(dp))!=NULL){
if(p->d_name[0]!='.'){
printf("%s\n", p->d_name);
}
}
closedir(dp);
}
ls -l
运行该命令,结果如下
每一条目从左到右分别为:文件模式(mode)、链接数、用户(在Linux下user即所有者)、组用户、文件大小、最后修改时间、文件名。其中ls -l
展示的mode共包含10个字母,首位是文件类型,-
代表一般文件,d
代表目录,c
代表字符设备,b
代表块设备。剩下9位表示文件权限。共分为三组,每一组3位,第一组对应的是用户的权限,第二组对应的是同组用户的权限,第三组对一个的是其他用户的权限。每一组的三位对应的含义是:
-
r(Read,读权限)
:对文件而言,具有读取文件内容的权限;对目录来说,具有浏览目录的权限。 -
w(Write,写权限)
:对文件而言,具有新增、修改文件内容的权限;对目录来说,具有创建、删除、移动目录内文件的权限。 -
x(eXecute,可执行权限)
:对文件而言,具有执行文件的权限;对目录来说,该用户具有进入目录的权限。
想要编写ls -l,文件名我们已经通过读取目录获得了,我们还需获得其他信息。
文件状态(status)(文件属性)
通过man手册搜索如何获取文件状态(status)。
man -k file|grep status
我们选择使用系统调用stat (2),man 2 stat
可以看到stat
的手册页
文件mode
st_mode
是一个16 bit的整数,其中最高4位代表文件类型(理论上可以表示16种文件类型,但实际只用到了7种),后面12位每三位分为一组,共分为三组。第一组(最高的三位)对应的是setuid
,setgid
,和sticky
;第二组对应的是用户的权限,第三组对应的是同组用户的权限,第四组对一个的是其他用户的权限。
setuid
当setuid
位被设置为1之后, 当文件被执行(该程序被运行时)时, 操作系统会赋予文件用户(即文件所有者)的权限。
我们以修改密码为例,我们在“用户”一章已经学习过,每个用户都可以使用passwd
命令修改自己的密码。同样在“用户”一章也学习过,用户的真正密码,一般都存储于/etc/shadow
里。
shadow
条目中的第一项为用户名,第二项为加密后的密码。
这里面的关键问题是,很显然,/etc/shadow
没有写权限。当然root有至高无上的权利,他不会收到mode的限制而具有任何权限。但是为何每个用户都可以用passwd
命令修改/etc/shadow
——这个只有root有修改权限的文件呢?
原因在于passwd
的setuid
位被设置为1了。
第四个字符“s”的意思等于"x+setuid",/bin/passwd
的mode
对应的二进制数值为1000 100 111 101 101。1000代表的是一般的文件。
setgid
setgid
位与setuid
相似,当setgid
位被设置为1之后, 当文件被执行(该程序被运行时)时, 操作系统会赋予文件组用户的权限。
该mode对应的二进制数值为1000 010 111 101 101。
sticky
如果对文件设置sticky
位,则要求当该文件执行时,必须使其常驻内存而不要将其临时存储于硬盘上的交换空间上(swap space)。当然,由于现在都是采用基于分页的虚拟内存技术,Linux内核已经忽略普通文件的sticky
位了。
如果对目录设置sticky
位,则表示该目录中的文件,只能被其用户(所有者)和root删除。最有代表性的就是/tmp
目录。