4.1 函数stat
函数stat返回与此命名文件有关的信息结构。下面的代码实现了一个工具,显示树形目录结构,需要加两个参数,一个为目录名,一个为显示目录的深度。
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
/***************************************************************/
/*struct stat {*/
/* unsigned long st_dev; */ /* Device. */
/* unsigned long st_ino;*/ /* File serial number. */
/* unsigned int st_mode; */ /* File mode. */
/* unsigned int st_nlink; */ /* Link count. */
/* unsigned int st_uid; */ /* User ID of the file's owner. */
/* unsigned int st_gid; */ /* Group ID of the file's group. */
/* unsigned long st_rdev; *//* Device number, if device. */
/* unsigned long __pad1; */
/* long st_size; *//* Size of file, in bytes. */
/* int st_blksize; *//* Optimal block size for I/O. */
/* int __pad2; */
/* long st_blocks;*/ /* Number 512-byte blocks allocated. */
/* long st_atime; */ /* Time of last access. */
/* unsigned long st_atime_nsec; */
/* long st_mtime; */ /* Time of last modification. */
/* unsigned long st_mtime_nsec;*/
/* long st_ctime; */ /* Time of last status change. */
/* unsigned long st_ctime_nsec; */
/* unsigned int __unused4; */
/* unsigned int __unused5;*/
/* }; */
/* *************************************************************/
void printMode(unsigned int st_mode,int indent)
{
int num = 0;
for(;num<indent;num++)
{
printf(" ");
}
printf(S_ISDIR(st_mode)?"d":"-");
printf(st_mode&S_IRUSR?"r":"-");
printf(st_mode&S_IWUSR?"w":"-");
printf(st_mode&S_IXUSR?"x":"-");
printf(st_mode&S_IRGRP?"r":"-");
printf(st_mode&S_IWGRP?"w":"-");
printf(st_mode&S_IXGRP?"x":"-");
printf(st_mode&S_IROTH?"r":"-");
printf(st_mode&S_IWOTH?"w":"-");
printf(st_mode&S_IXOTH?"x":"-");
}
void printFileName(char *name)
{
printf(" %s\n",name);
}
void printUserName(unsigned int userId)
{
struct passwd *pwd = getpwuid(userId);
printf(" %s", pwd->pw_name);
}
void printGroupName(unsigned int grpId)
{
struct group *grp = getgrgid(grpId);
printf(" %s" ,grp->gr_name);
}
void printSize(long size)
{
printf(" %lu",size);
}
void printModifyTime(long mtime)
{
/*char buf[100]={0};
ctime_s(buf,26,mtime);
printf(" %s",buf);*/
printf(" %lu",mtime);
}
int ls(char *path,int depth,int indent)
{
DIR *dhandle;
struct dirent *file;
struct stat st;
if(!(dhandle=opendir(path)))
{
printf("error opendir %s\n",path);
return -1;
}
while((file = readdir(dhandle))!=NULL)
{
int fullPathLen = strlen(path)+strlen(file->d_name)+1;
if(strncmp(file->d_name,".",1)==0)
continue;
char *fullpath = (char*)malloc(fullPathLen+1);
memset(fullpath,0,fullPathLen+1);
strcpy(fullpath,path);
strcat(fullpath,"/");
strcat(fullpath,file->d_name);
int rv = stat(fullpath,&st);
if(rv<0)
{
return -1;
}
printMode(st.st_mode,indent);
printUserName(st.st_uid);
printGroupName(st.st_gid);
printSize(st.st_size);
printModifyTime(st.st_mtime);
printFileName(file->d_name);
if(S_ISDIR(st.st_mode)&& (depth-1>0))
{
ls(fullpath,depth-1,indent+1);
}
free(fullpath);
}
closedir(dhandle);
return 0;
}
int main(int argc,char *argv[])
{
int dep = atoi(argv[2]);
ls(argv[1],dep,0);
return 0;
}
运行如下命令
gcc stat.c
生成一个a.out可执行文件,运行如下命令:
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out /github/ 2
drwxrwxrwx harlan harlan 0 1494143291 3202C
drwxrwxrwx harlan harlan 0 1494143273 Doc
-rw-rw-rw- harlan harlan 828 1494143273 Readme.txt
drwxrwxrwx harlan harlan 0 1494143279 SRC
drwxrwxrwx harlan harlan 0 1494143281 inc
-rw-rw-rw- harlan harlan 9700 1494143282 m3327.mdf
-rw-rw-rw- harlan harlan 1182 1494143282 m3327boot.mdf
drwxrwxrwx harlan harlan 0 1494143290 prj
drwxrwxrwx harlan harlan 0 1494143292 sdk
drwxr-xr-x harlan harlan 0 1495673220 APUE
-rw-r--r-- harlan harlan 6 1493303590 README.md
-rwxrwxrwx root root 17478 1494424916 a.out
-rw-rw-rw- harlan harlan 4352 1494167949 apue.h
-rw-rw-rw- harlan harlan 2660400 1493735585 apue.h.gch
drwxrwxrwx harlan harlan 0 1494248815 chapter_1
drwxrwxrwx harlan harlan 0 1495117067 chapter_2
drwxrwxrwx harlan harlan 0 1494509690 chapter_3
drwxrwxrwx harlan harlan 0 1495113400 chapter_4
drwxrwxr-x harlan harlan 0 1494944116 chapter_5
-rw-rw-rw- harlan harlan 2220 1494167949 err.c
drwxrwxr-x harlan harlan 0 1494769702 foo
-rw-rw-r-- harlan harlan 399 1494424891 go.c
-rw------- harlan harlan 1675 1494512317 key
-rw-r--r-- harlan harlan 404 1494512317 key.pub
-rw-rw-rw- harlan harlan 1501 1494116048 print.c
-rwx------ harlan harlan 1457 1493733958 tags
drwxrwxr-x harlan harlan 0 1494769702 testdir
-rw-r--r-- harlan harlan 4790 1495671977 vimrc.txt
-rw------- harlan harlan 1679 1493304485 pub
-rw-r--r-- harlan harlan 402 1493304485 pub.pub
drwxrwxr-x harlan harlan 0 1494511444 test
4.2 文件类型
文件类型包括以下几种:
- 普通文件
- 目录文件
- 块特殊文件
- 字符特殊文件
- FIFO
- 套接字
- 符号链接
可以用图4-1中的宏确定文件类型,这些宏的参数是stat结构中的st_mode成员。
可以用图4-2中的宏来从stat结构中确定IPC对象的类型。,它们的参数是指向stat结构的指针。
4.3 设置用户ID和设置组ID
- 实际用户ID和实际组ID标识我们究竟是谁。
- 有效用户ID、有效组ID以及附属组ID决定了我们的文件访问权限。
- 保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本。
通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。
我们可以在文件模式字(st_mode)中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)”,组ID也一样。在文件模式字中的这两位被称为设置用户组ID(set-user=ID)位和设置组ID(set-group-ID)位。
例如,passwd允许任意用户改变其口令,该程序是一个设置用户ID程序。见下面的展示:
harlan@DESKTOP-KU8C3K5:~$ ll /etc/passwd
-rw-r--r-- 1 root root 1219 5月 8 21:34 /etc/passwd
harlan@DESKTOP-KU8C3K5:~$ ll /usr/bin/passwd
-rwsr-xr-x 1 root root 47032 1月 27 2016 /usr/bin/passwd
任意用户修改口令需要写入/etc/passwd文件中,而此文件只有root用户才有写权限,/usr/bin/passwd用来执行写操作,可以看到这个程序将root用户指定为设置用户组ID,因此任意用户当执行此程序进行写文件的时候将拥有root权限。
4.5 文件访问权限
所有文件类型(目录·字符特别文件等)都有访问权限(access permission)。每个文件有9个访问权限,将它们分为3类,见图4-6。
我们用chmod命令修改这9个权限位。它允许我们用u表示用户,用g表示组,用o表示其他。
图4-6中的3类访问权限(读、写和执行)以各种不同的方式由不同函数使用。
-
规则一,我们用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行权限。这就是为什么对于目录其执行权限位常被称为搜索位的原因。
读权限允许我们读目录,获得在该目录中所有文件名的列表,这是原书中的话,看下面的例子:
drwxr-xr-- root root 0 1496278552 test -rw-r--r-- root root 0 1496278548 1.txt -rw-r--r-- root root 0 1496278552 2.txt
test文件夹的路径为/,我们现在在普通用户(harlan)下执行ls,看是否可以将test下面的文件名列出来。可以看到test文件夹和下面的两个文件对于其他用户有读权限。
先执行一个cd.
harlan@DESKTOP-KU8C3K5:~$ cd /test
bash: cd: /test: 权限不够
可见cd是需要执行权限的。
harlan@DESKTOP-KU8C3K5:~$ ls /test
1.txt 2.txt
harlan@DESKTOP-KU8C3K5:~$ ls -l /test
ls: 无法访问/test/1.txt: 权限不够
ls: 无法访问/test/2.txt: 权限不够
总用量 0
-????????? ? ? ? ? ? 1.txt
-????????? ? ? ? ? ? 2.txt
可见普通的ls是可以的,以列表方式列出文件信息就只列出了文件名。
为文件夹加上执行权限:
drwxr-xr-x root root 0 1496278552 test
-rw-r--r-- root root 0 1496278548 1.txt
-rw-r--r-- root root 0 1496278552 2.txt
harlan@DESKTOP-KU8C3K5:/$ cd /test
harlan@DESKTOP-KU8C3K5:/test$
harlan@DESKTOP-KU8C3K5:/test$cd ..
harlan@DESKTOP-KU8C3K5:/$ ls -l /test
总用量 0
-rw-r--r-- 1 root root 0 6月 1 08:55 1.txt
-rw-r--r-- 1 root root 0 6月 1 08:55 2.txt
cd执行成功,ls -l 也执行成功。
- 对于一个文件的读权限决定了我们是否能够打开现有文件进行读操作。这与open函数的O_RDONLY和O_RDWR标志相关。
- 对于一个文件的写权限决定了我们是否能够打开现有文件进行写操作。这与open函数的O_WRONLY和O_RDWR标志相关。
- 为了在open函数中对一个文件指定O_TRUNC标志,必须对该文件具有写权限。
- 为了在一个目录中创建新文件,必须对该目录具有写权限和执行权限。
- 为了删除一个现有文件,必须对包含该文件的目录具有写和执行权限。对该文件不需要有读写权限。
看下面的例子:
drwxr-xrwx root root 0 1496278552 test
-rw-r----- root root 0 1496278548 1.txt
-rw-r--r-- root root 0 1496278552 2.txt
在普通用户下删除1.txt:
harlan@DESKTOP-KU8C3K5:/test$ ll
总用量 0
-rw-r----- 1 root root 0 6月 1 08:55 1.txt
-rw-r--r-- 1 root root 0 6月 1 08:55 2.txt
harlan@DESKTOP-KU8C3K5:/test$ rm 1.txt
rm:是否删除有写保护的普通空文件 "1.txt"? y
harlan@DESKTOP-KU8C3K5:/test$ ll
总用量 0
-rw-r--r-- 1 root root 0 6月 1 08:55 2.txt
- 如果用7个exec函数中的任何一个执行某个文件,都必须对该文件具有执行权限。该文件还必须是一个普通文件。
进程每打开、创建或者删除一个文件时,内核就进行文件访问权限测试,而这种测试可能涉及文件的所有者(st_uid和st_gid)、进程的有效ID(有效用户ID和有效组ID)以及进程的附属组ID(若支持的话)。两个所有者ID是文件的性质,而两个有效ID和附属组ID则是进程的性质。内核进行的具体测试如下。
注意: 如果进程拥有此文件,则按用户访问权限批准或拒绝该进程对文件的访问——不查看组访问权限。类似的,如果进程不拥有该文件,而是属于某个组,则按照组权限批准或拒绝访问文件——不看其他用户权限。
4.6 新文件和目录的所有权
关于新目录的所有权规则与本节将说明的新文件所有权规则相同。
新文件的用户ID设置为进程的有效用户ID。关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID。
- 新文件的组ID可以是进程的有效组ID。
- 新文件的组ID可以是它所在目录的组ID。
看下面的例子:
普通用户下写一个c程序 4-7.c
-rw-rw-r-- 1 harlan harlan 290 6月 1 22:13 4-7.c
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#define RWRWRW (S_IRUSR |S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int main(void)
{
int fd = creat("test4-7.txt",RWRWRW);
printf("uid = %d,gid=%d,euid=%d,egid=%d\n",getuid(),getgid(),geteuid(),getegid());
}
编译生成可执行文件a.out
gcc 4-7.c
-rwxrwxr-x 1 harlan harlan 8780 6月 1 22:13 a.out
运行程序
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
输出:
uid = 1000,gid=1000,euid=1000,egid=1000
查看创建的文件s所属用户(harlan用户ID为1000):
-rw-rw-r-- 1 harlan harlan 0 6月 1 22:13 test4-7.txt
修改a.out可执行程序的拥有着为root,并设置保存的设置用户ID:
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ su
密码:
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chown root a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwxrwxr-x 1 root harlan 8780 6月 1 22:13 a.out*
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chmod u+s a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwsrwxr-x 1 root harlan 8780 6月 1 22:13 a.out*
跳转到harlan用户执行a.out:
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=0,egid=1000
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 root harlan 0 6月 1 22:33 test4-7.txt
可以看到有效用户ID变为了0,因此创建的文件的用户变为了root。
在看一下新创建文件的组ID,将a.out的组设置为root,并且设置保存的设置组ID:
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ su
密码:
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chgrp root a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chmod g+s a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwxrwsr-x 1 root root 8780 6月 1 22:13 a.out*
回到harlan用户下执行a.out,可以看到生成文件所属组为root.
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# su harlan
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=1000,egid=0
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 harlan root 0 6月 1 22:41 test4-7.txt
可执行程序a.out所在目录myexamples的组为harlan
drwxrwxr-x 2 harlan harlan 0 6月 1 22:41 myexamples```
设置myexamples文件夹的设置组ID
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4$ chmod g+s myexamples
drwxrwsr-x 2 harlan harlan 0 6月 1 22:41 myexamples
执行a.out之后发现生成文件的组变为了harlan,也就验证了APUE中的说法。
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=1000,egid=0
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 harlan harlan 0 6月 2 08:14 test4-7.txt