正文
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 /testbash: cd: /test: 权限不够
可见cd是需要执行权限的。
harlan@DESKTOP-KU8C3K5:~$ ls /test1.txt 2.txt harlan@DESKTOP-KU8C3K5:~$ ls -l /testls: 无法访问/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则是进程的性质。内核进行的具体测试如下。
作者: HarlanC
博客地址: http://www.cnblogs.com/harlanc/
个人博客: http://www.harlancn.me/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 .
http://www.cnblogs.com/harlanc/p/6931620.html