$ ll wget
total 544
drwxr-xr-x 3 orisun orisun 4096 2011-12-15 09:48 ./
drwxr-xr-x 65 orisun orisun 4096 2011-12-20 19:45 ../
drwxr-xr-x 11 orisun orisun 4096 2011-08-09 21:55 wget-1.13/
-rw-r--r-- 1 orisun orisun 540756 2011-12-15 09:47 wget.pdf
目录总是不为空,它至少包含两项:.代表当前路径,..代表父路径。
ll命令用于列出一个目录下的文件的详细信息:
1、文件模式
这是个16位的2进制。4位表示文件类型,接下来3位依次是suid、sgid、sticky,后面的9位依次是user、group、other的读写执行标志。
文件类型:
1000 - 普通文件
0100 d 目录
0110 b 块设备
0010 c 字符设备
0001 p 命名管道
1010 l 符号链接
1100 s 套接字
文件可读意味着可以用cat命令查看,可写可以用vi打开。文件夹可执行则可以使用cd命令。
suid代表set-user-id,设置用户ID,即执行该文件的用户就像本文件的所有者一样。比如任何用户都可以通过password命令来修改自己的密码,但是存储密码的文件/etc/passwd的拥有者是root,其他用户是没有写权限的。而可执行文件/usr/bin/passwd的拥有者也是root,同时它的suid位上是1。则普通用户在运行/usr/bin/passwd的时候就成了root身份,既然是root身份自然对/etc/passwd拥有了写权限。那么passwd命令能不能修改其他用户的密码呢?不能!因为passwd命令会通过系统调用getuid来获得当前用户的ID,它只能修改这个ID用户的密码。如果文件拥有者本来具备写权限,同时suid位上是1,则相就位上的x会变成s;如果文件本来不是可执行文件,则相应位上会变成S。
sgid与suid类同。
sticky对文件和文件夹有不同的功用。sticky文件告诉系统要把文件放在swap区域。从交换空间加载程序比从普通磁盘区域加载要快,因为程序在硬盘上可能被分为好几块存放在许多不同的地方,而在交换空间上文件是不分块的。所以常用的程序如编辑器、编译器会放在交换空间。不过现在交换技术已经没那么重要了,取而代之的是虚拟内存,因为虚拟内存可以以更小的单位(如页page)进行交换。sticky使得一个目录里的文件只能被创建者删除,在/tmp目录里,谁都可以进行创建/删除文件。其他用户的x被替换这t,代表sticky被设置。
2、第二列显示链接数(硬链接),即指向该文件的引用次数。
3、第三列是文件所有者。/etc/passwd文件中包含用户列表,但它并没有包含所有的用户,因为在网络系统中,一台机器可以被很多用户登录,此时所有的用户信息都存储在一台叫作NIS的服务器上,所有的主机通过NIS进行身份验证。当然本地的/etc/passwd也存储了部分用户列表(这是NIS上用户列表的一个子集),以备离线操作。每个用户都有一个ID,该用户创建的文件有一个属性值就是此ID,这里就有一个问题,当用户被删除后,他创建的文件还在,如果后来又到了一个新用户,他刚好又被赋予了被删除的那个用户的ID,那么原先用户创建的文件归新用户所有。
4、第四列是文件拥有者所在的主组。不错,一个用户可以属于多个组。/etc/group里存储组列表。
5、 第五列是文件大小。令我们好奇的是为什么上面三个文件夹的大小都是4096?这是因为目录所占的空间是以块为分配单位的,每块为512B。对于一般文件,则列出实际字节数。
6、第六列是文件的修改时间。
7、第七列是文件名。
下面讨论如何来编程实现ll命令。
文件的很多基本信息都可以通过系统调用lstat来获取,它返回一个结构体:
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 */ };
一个struct stat里面似乎包含了我们编写ll命令所需的所有文件信息,但有些表现形式上还不对。
文件模式我们希望以“-rw-rw-r--"的形式显示,但st_mode是个十进制数。这里可以使用掩码来获取相应的位标识。
用户和组提供的是ID,而不是名称。通过UID来获取用户名可以用getpwuid(uid)->p_name,根据GID来获取组名可以用getgrgid(gid)->gr_name。getpwuid()先从本地/etc/passwd上查找用户,如果没有,再从NIS中获取。
st_mtime是time_t类型的,我们可以通过系统调用localtime把它转换成struct tm类型:
struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};
需要注意的是,这里的tm_year是减去了1990的,tm_mon是从0开始计算的。
下面给出完整代码。
#include<stdio.h>
#include<time.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<string.h>
void do_ls(char[]);
void dostat(char *);
void show_file_info(char *,struct stat *);
void mode_to_letters(int,char[]);
char * uid_to_name(uid_t);
char * gid_to_name(gid_t);
void main(int argc,char *argv[]){
if(argc==1)
do_ls(".");
else
printf("输入命令./ls即可,不要带任何参数。\n");
}
void do_ls(char dirname[]){
DIR *dir_ptr; //路径变量
struct dirent *direntp;
//存储路径下一个子项信息的结构体
if((dir_ptr=opendir(dirname))==0)
fprintf(stderr,"ls:cannot open %s\n",dirname);
else{
while((direntp=readdir(dir_ptr))!=0)
dostat(direntp->d_name);
closedir(dir_ptr);
}
}
void dostat(char *filename){
struct stat info;
if(lstat(filename,&info)==-1)
perror("lstat");
else
show_file_info(filename,&info);
}
void show_file_info(char *filename,struct stat *info_p){
char modestr[11];
mode_to_letters(info_p->st_mode,modestr);
printf("%-12s",modestr);
printf("%-4d",(int)info_p->st_nlink);
printf("%-8s",uid_to_name(info_p->st_uid));
printf("%-8s",gid_to_name(info_p->st_gid));
printf("%-8ld",(long)info_p->st_size);
time_t timelong=info_p->st_mtime;
struct tm *htime=localtime(&timelong);
printf("%-4d-%02d-%02d %02d:%02d",htime->tm_year+1990,htime->tm_mon+1,htime->tm_mday,htime->tm_hour,htime->tm_min);
printf(" %s\n",filename);}/*这个函数写得不够全面,首先文件类型不全,其次没有考虑suid,sgid,sticky*/
void mode_to_letters(int mode,char str[]){
strcpy(str,"----------");
if(S_ISDIR(mode))
str[0]='d';
if(S_ISCHR(mode))
str[0]='c';
if(S_ISBLK(mode))
str[0]='b';
if(mode & S_IRUSR)
str[1]='r';
if(mode & S_IWUSR)
str[2]='w';
if(mode & S_IXUSR)
str[3]='x';
if(mode & S_IRGRP)
str[4]='r';
if(mode & S_IWGRP)
str[5]='w';
if(mode & S_IXGRP)
str[6]='x';
if(mode & S_IROTH) str[7]='r';
if(mode & S_IWOTH) str[8]='w';
if(mode & S_IXOTH)
str[9]='x';}#include<pwd.h>
//#include可以出现在代码中的任何位置char * uid_to_name(uid_t uid)
{
struct passwd *pw_str;
static char numstr[10];
if((pw_str=getpwuid(uid))==NULL)
{
sprintf(numstr,"%d",uid);
//如果没有获得用户名,则直接把uid当作用户名
return numstr;
}
else
return pw_str->pw_name;}#include<grp.h>char * gid_to_name(gid_t gid)
{
struct group *grp_ptr;
static char numstr[10];
if((grp_ptr=getgrgid(gid))==NULL)
{
sprintf(numstr,"%d",gid);
return numstr;
}
else
return grp_ptr->gr_name;
}