第三章 目录与文件属性:编写ls

磁盘上有文件和目录,文件和目录都有目录和属性.文件的内容可以是任意的数据,
目录的内容只能是文件名或者子目录名的属性

目录中的文件名和子目录名指向文件和其他的目录,内核提供了系统调用来读取目录的
内容,读取和修改文件的属性

文件类型,文件的访问权限和特殊属性被编码存储在一个16位整数中,可以通过
掩码技术来读取这些信息

文件所有者和组信息是以ID的形式保存的,它们与用户名和组名的联系保存在
passwd和group数据库中

自己编写ls,需要掌握三点:
如何读取目录的内容
如何读取并显示文件的属性
给出一个名字,如何判断是目录还是文件

把多种信息编码到不同的字段是一种常用的技术,如电话号码,IP字段等

为了比较,把不需要的地方置为0,这种技术称为掩码

将二进制数的每三位分为一组来操作,这就是八进制

结构stat 中的st_mode 成员包含16位,其中四位用作文件类型,九位用作许可权限,
剩下的三位用作文件特殊属性
set-user-ID s 使用它来给某些程序提供额外的权限,比如系统中的打印队列
set-group-ID s
sticky 它告诉内核,即使没有人使用程序,也要把它放在交换空间中,因为加载速度
比从硬盘空间快
在许可权限部分,用户的x被替换成s,代表set-user-ID 被设置
组用户的x被替换成s,代表set-group-ID被设置
其他用户的x被替换成t,代表sticky被设置

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

/* ls2.c
* purpose list contents of directory or directories
* action if no args, use . else list files in args
* note uses stat and pwd.h and grp.h
* BUG: try ls2 /tmp
*/

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.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);

main(int ac, char * av[])
{
if (ac == 1)
do_ls(".");
else
while (--ac)
{
printf("%s:\n", * ++av);
do_ls(*av);
}
}

void do_ls(char dirname[])
/*
* list files in directory called dirname
*/
{
DIR *dir_ptr; // the directory
struct dirent * direntp;// each entry

if ((dir_ptr = opendir(dirname)) == NULL)
fprintf(stderr, "ls1: cannot open %s\n", dirname);
else
{
while ((direntp = readdir(dir_ptr)) != NULL)
dostat(direntp->d_name);
closedir(dir_ptr);
}
}

void dostat(char *filename)
{
struct stat info;
if (stat(filename, &info) == -1) // cannot stat
perror(filename); // say why
else
show_file_info(filename, &info);
}

void show_file_info(char *filename, struct stat * info_p)
/*
* display the info about filename.
* the info is stored in struct at * info_p
*/
{
char * uid_to_name(), *ctime(), *gid_to_name(), *filemode();
void mode_to_letters();
char modestr[11];

mode_to_letters(info_p->st_mode, modestr);

printf("%s", 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);
printf("%.12s ", 4+ctime(&info_p->st_mtime));
printf("%s\n", filename);
}

/*
* utility functions
*/

/*
* This function takes a mode value and a char array
* and puts into the char array the file type and the
* nine letters that correspnd to the bits in mode.
* NOTE: It does not code setuid, setgid, and sticky
* codes
*/
void mode_to_letters(int mode, char str[])
{
strcpy(str, "----------"); // default = no perms
if (S_ISDIR(mode)) str[0] = 'd'; // directory
if (S_ISCHR(mode)) str[0] = 'c'; // char devices
if (S_ISBLK(mode)) str[0] = 'l'; // block device

if (mode & S_IRUSR) str[1] = 'r'; // 3 bits for user
if (mode & S_IWUSR) str[2] = 'w';
if (mode & S_IXUSR) str[3] = 'x';

if (mode & S_IRGRP) str[4] = 'r'; // 3 bits for group
if (mode & S_IWGRP) str[5] = 'w';
if (mode & S_IXGRP) str[6] = 'x';

if (mode & S_IROTH) str[7] = 'r'; // 3 bits for other
if (mode & S_IWOTH) str[8] = 'w';
if (mode & S_IXOTH) str[9] = 'x';
}

#include <pwd.h>

char * uid_to_name(uid_t uid)
/*
* returns pointer to username associated with uid, uses getpw()
*/
{
struct passwd * getpwuid(), *pw_ptr;
static char numstr[10];

if ((pw_ptr = getpwuid(uid)) == NULL)
{
sprintf(numstr, "%d", uid);
return numstr;
}
else
return pw_ptr->pw_name;
}

#include <grp.h>

char * gid_to_name(gid_t gid)
/*
* returns pointer to group number gid, used getgrid(3)
*/
{
struct group * getgrpid(), *grp_ptr;
static char numstr[10];

if ((grp_ptr = getgrgid(gid)) == NULL)
{
sprintf(numstr, "%d", gid);
return numstr;
}
else
return grp_ptr->gr_name;
}