关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法

时间:2021-03-31 04:41:33


关于stat函数和根据st_mode 的值获取目标文件的文件类型和


                用户权限,以及对相关系统宏的一点想法

   

      谈论之前写说一下 stat 这个函数,这是个很有用的系统函数,


大家都知道调用stat函数可以获取到由参数(文件路径)指定的文


件的相关属性,包括文件类型,文件权限,硬链接数,用户,用


户组,修改时间等等信息,而这一系列状态信息都是保存在对应


的结构体里面的。与stat函数同出一胎的还有fstat,lstat这两个函


数,极其相似,区别这里不谈了。


      在linux 命令行下,输入命令“ man 2 stat ”可以查看 stat 函数


的原型:


     

       #include <sys/types.h>
       
       #include <sys/stat.h>

       #include <unistd.h>

       int stat(const char *path, struct stat *buf);



path参数对应目标文件的地址;


buf  参数是结构体指针类型。

     

在系统内,struct  stat 结构体是这样定义的:


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法


可以看到这个结构体内的各个域都用于保存文件的各种类型信


息,例如:


st_mode   记录文件类型+文件权限


st_nlink   记录文件的硬链接数,新建目录的硬链接数为2


结构体内右边的一列自然是变量,而左边的一列看起来凌而不乱


的字符串是变量的类型,其实也就是那些char型,unsigned,long


int类型等等的重定义而已。


      调用这个stat 函数之前应先定义一个用于接受文件相关信息的


结构体变量,这样:


      struct  stat   sb;


      stat(pathname,&sb);


就可以获取文件信息,stat函数获取状态信息成功返回0,有错误返


回-1。


大家可以尝试用printf将这些变量的数据一一打印出来,看看获取


到了文件的什么信息。


      接下里详细谈谈 struct stat 结构体里面 st_mode 这个东西。


st_mode 上面看到它定义为mode_t类型,它是用来获取文件的文


件类型+文件属性的。这就有意思了,这一个变量它是如何做到同


时保存文件类型和文件属性两个信息的。


      我们不妨写一个小程序测试一下,将他的数据打印到屏幕瞧


瞧:


#include<stdio.h>

#include<stdlib.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<unistd.h>


int main(int argc, char *argv[])

{
        struct stat sb;

        int i=0;

        while(++i

        {

                
                if(stat(argv[i],&sb)==-1)

                        exit (1);

                printf("%s的 st_mode is  %o\n",argv[i],sb.st_mode);

        }

         return 0;

}



先用 ls -l查看几个类型相同,权限不同的目录属性:


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法


再用程序测试下这几个目录的st_mode值


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法



可以看到他们后3位数字不同。


     事实上后3位数字代表的就是3种用户权限。而我们看到的


st_mode 的值是八进制数。


     先说说权限,权限有3类,可读取 r,可写入w,可执行x。他


们分别对应一个值,r=4,w=2,x=1.对应二进制r=100,w=010,x=001,


似乎有点规律,这3个数确实不是随便取的,后面会说到他的妙


处。


     用户类型有3种,所有者,用户组,其他用户。后面那3个数字


正好分别对应一种用户类型。


     拿/proc/目录的st_mode值为例,40555后3位是555,数字5显然


只能拆分为4+0+1,所以是r_x,555组合为r_xr_xr_x,这就得到了3种


用户的权限。


     而一个数字拿拆分成4,2,1的哪种组合,我们一眼就能看出来,


可以计算机看不出来,这就需要给计算机设计一种算法,比如怎


么让他知道5是由4+0+1组成,而且555这3个数字还是连在一起


的,我们当然可以40555的后3个数字分别提取出来,然后将4


,2,1的所有组合尝试一遍最后找到正确的权限解。但这只是为了获


取文件的权限而已,这个算法也太繁琐了。


     我们用man 手册查看stat函数时,会看到一堆的大写字母的宏


定义,后面还给出一些确定的0,1组成的二进制数字,但是这一堆


乱七八糟的宏定义有何用呢?我们先来看看下面的方法是如何解


出具体的权限的。


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法

关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法

关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法



发现就是用st_mode 和那些宏取“&”运算来判断用户的各种权限


的,只用了一条简单的语句就解决了我们的困扰,妙哉。我们来


分析一下这是如何做到的。


        拿用户所有者权限为例:


        先看看对应的宏和相关数据:


宏 (前3个)        八进制值                  二进制值                 权限


S_IRUSR          00400            000 000 100 000 000   r=4


S_IWUSR         00200            000 000 010 000 000   w=2


S_IXUSR          00100            000 000 001 000 000   x=1


st_mode             *****              ***  ***   ***   ***   ***


/proc/               40555             100 000 101 101 101   

           


为了便于观察将5位八进制数各位编号


 e  d c  b  a


 0  0 0  0  0


将15位二进制数各位编号


      文件类型            ID          所有者     用户组     其他用户

 

     15  14  13     12  11  10    9   8  7     6  5   4      3   2  1

 

       0   0   0        0   0   0      0  0   0     0   0  0      0   0  0


                                            r   w  x      r   w x       r  w   x


第10~12位是ID位,第10位称粘附位,11,12位用于获取uid,gid;


第13~15位用于获取文件类型,后面谈论。


先看看上面判断文件所有者可读取权限r的算法语句:


sb.st_mode  & S_IRUSR


对于/proc/目录,st_mode是一个八进制数


=40555=100000101101101


对应的S_IRUSR=000 000 100000 000


两者取 &运算:


st_mode          100 000 101 101 101


S_IRUSR        000 000 100 000 000


     =                000 000 100 000 000


     =                1


运算值为1,所以判定为可读取,这方法简单吧。


系统为什么用取 &运算就可以判定呢,而不用其他逻辑运算。


st_mode是一个八进制数,仅仅5位,却代表了多个类型的信息,


而这5位之间的联系剪不断,理还乱。但我们要判定所有者可读取


权限,仅仅需要看的是决定他的第9位数字,这就使得任何一种算


法只要是其他数位也参与了运算就会影响我们的判断,从而达不


到判断用户权限的作用。而“&”运算在这里完美的解决了这个问


题,&运算的法则是只有1&1=1,其他情况取&都是0。这就可看出


为什么要用到宏,宏S_IRUSR为什么要取值00400。


      

       观察st_mode&S_IRUSR的运算可以看到,S_IRUSR除了第9


位其他位都为0,0与0,1的&运算都是0,所以st_mode除了第9位,其


他位与0取&后都得0,因此0在这里就起到了屏蔽其他位数字影响的


效果,起判定作用的就只有第九位。很巧妙的利用st_mode与


S_IRUSR的&运算识别到了所有者用户的可读取权限。


      再看看如何判断所有者用是否具有可写入权限w,如果有可写


入权限,那么八进制数的c位置数字一定能拆分出2,如6=4+2。如


果能拆分出2,那么对应的二进制数字位(第8位)一定是1,如


6=110(中间位为1)。因此判断是否可写入就转换成了第8位是


否为1的问题。我们回过头看上面的判定语句


if(st_mode &S_IWUSR):


         st_mode=40555=100 000 101 101 101


             &  S_IWUSR  000 000 010  000 000


                                 =000 000 000  000 000

                              

                                 =0


第8位取&后为0,所以st_mode的第8位为0,无可写入权限。


      同理,我要判断所有者可执行权限x时,起决定作用的是第7


位,所有让st_mode与只有第7位为1的数,


即000000 001 000 000=00100进行&运算即可判定。


      看出来了吧,这些宏的取值都不是随意的,而是有意义的。


很显然,剩下的宏的八进制值为:


      用户组权限:


      S_IRGRP      000 000 000 100 000=00040


      S_IWGRP     000 000 000 010 000=00020


      S_IXGRP      000 000 000 001 000=00010


      其他用户权限


      S_IROTH     000 000 000 000 100=00004


      S_IWOTH    000 000 000 000 010=00002


      S_IXOTH     000 000 000 000 001=00001


了解了它运算方法,再看看权限r,w,x为什么就要取4,2,1这3个数


值呢,我们已知道st_mode是个八进制数,权限位的数字都能拆


分成3个数字,每个每个数字都代表一种权限,而这种拆分必须是


唯一确定的。r,w,x对应的不同权限组合共有8种,正好可以对应八


进制基数0,1,2,3,4,5,6,7,8,所以将st_mode的数据类型定为八进制


数最为合适,而也只有4,2,1,0(无权限)这3个取值才能正好唯一的


对应权限的8种组合:


0+0+0=0;0+0+1=1,0+2+0=2,0+2+1=3,


4+0+0=4, 4+0+1=5,4+2+0=6, 4+2+1=7,


想到了这些,系统的宏也就不那么神秘了。



     说完了文件权限,再谈谈文件类型的获取吧。


     文件类型主要有如下几种:

     

     d              directoryfile              目录文件


     c              characterdevice        字符设备文件


     b              blockdevice              块设备文件


     p              FIFO (namedpipe)    管道文件


     l               symboliclink              符号链接文件


     s              socket                      套接字文件


     _              regularfile                 一般文件



     先看一段获取文件类型的代码:


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法

 


     看到判断文件类型时都用到了类似于 S_ISDIR(m) 这样的语句,


     stat 的man 手册里,给出的判断文件类型的语句如下:

关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法

    
 

     似乎有点不好理解,怎么S_IS...(m)就可以判断出文件类型,


     再看看 man 手册里还有这样一段代码:


关于stat函数和根据st_mode 的值获取目标文件的文件类型和用户权限,及对相关系统宏的一点想法


    

     可以看出这一段代码也是获取文件类型的,与前面那一段代码


相比较就可以想到了