linux文件属性、文件权限管理和stat函数

1. linux中各种文件类型

1.1 普通文件(-)regular file

文本文件:文件中的内容都是由文本构成的,文本指的是ASCII码,Unicode码,utf8码等字符。其实,这些字符码的本质是数字(无论什么编码格式,对应的都是二级制的0和1)。

常见的.c文件.h文件.txt文件等都是文本文件,其好处是可以被人轻松的编写和理解。

二进制文件:二进制文件中存储的也是二进制的0和1,只不过这些数字并不是符合一定编码规则的数字,而是纯粹的数字0和1。

gcc编译生成的a.out文件和arm-linux-gcc编译生成的.bin文件都是二进制文件;

如果区分文本文件和二进制文件呢?在linux系统中,是无法区分的;例如open,read,write等操作文本文件和二进制文件是没有任何区别的。只能通过文件的后缀来区别。

常见的文本编辑器:vi,vim,gedit,notepad++,sourceinsight等这些都属于文本编辑器;当我们使用这些编辑器打开文本文件的时候,编辑器会read出文本文件的二进制内容,然后按照编码格式去解码,将其还原成文本(字符)展现给我们。

但如果用文本文件编辑器打开二进制文件时,看到的结果是乱码!(或者使用不符合编码解码规则的文件时,打开也是乱码)。

反过来使用二进制阅读工具读取文本文件时,得到的就是文本文件中编码字符所对应的二进制数字。

1.2 目录文件(d)direct

目录就是文件夹,文件夹是一种页数的文件!

使用vi打开一个文件夹,就可以看到如下信息,里面包含文件路径和文件列表;但是文件夹比较特殊,不适合用普通的方式(read,write)来读写操作;linux中使用特殊的API来专门读写文件夹。

1.3 字符设备文件(c)character

1.4 块设备文件(b)block

设备文件对应的是“设备”,设备文件虽然存在与linux系统中,但是设备文件并不存在于硬盘上,而是linux文件系统虚拟制造出来的(叫做虚拟文件系统,如/dev  /sys  /proc)。

虚拟文件中的大多数不能直接读写,而是用一些特殊的API使用,详见linux驱动编写部分。

1.5 管道文件(p)pipe

使用管道作为通信方式时,会使用的到管道文件。

1.6 套接字文件(s)socket

linux网络的基础

1.7 符号连接文件(l)link

符号连接文件是一种快捷方式!!!

ln-s 软连接?软连接相当于是一个快捷方式,因此其inode与源文件不同;

ln硬连接?硬连接的inode与源文相同;

 

2. 常用文件属性获取

2.1 stat,fstat,lstat函数简介

0777就是rwx-rwx-rwx

size文件大小;Blocks占用了多少块;

IOBlocks该文件在被读写时,其内部的块的大小(linux系统操作缓冲区向硬盘写入数据的单位大小);

regular file:该文件属于普通文件;

Device:该文件所存放在的设备;(类似设备名,扇区名)

Inode:静态文件存储在硬盘里时的编号;一个文件分配一个Inode,linux系统中有多少文件就有多少个Inod;

Links:该文件的硬连接数;

Access:该文件的可访问性;可读可写可执行;

Uid:用户id;root组

Gid:文件分为哪一组;root组

Access:最后访问时间,打开,读,写;

Modify:文件内容最后被修改的时间;

Change:文件属性最后被修改的时间。

文件属性信息查看的API有三个,stat,fstat,lstat,三个作用一样,参数不同,细节略有不同;

man 1 普通文件; man 2 系统调用 ; man 3 库函数;

linux命令行下可以使用stat命令去查看文件的属性信息,实际上stat命令内部就是使用stat系统调用来实现的;

stat这个API的作用就是让内核将文件的属性信息结构体的值放入我们传递给stat函数的buf中,当stat这个API调用从内核返回的时候buf中就被填充了文件的正确的属性信息,然后我们通过查看buf这种结构体变量的元素的值就可以得知文件的属性。

向内核传递一个空白的结构体,该结构体从内核中带出来的信息,就是我们想要的信息!!!

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

       int stat(const char *pathname, struct stat *buf);//以文件名为入口
       int fstat(int fd, struct stat *buf);//以文件操作符为入口
       int lstat(const char *pathname, struct stat *buf);//以文件名为入口

fstat是从一个已经打开的文件的fd得到文件属性信息,fstat是从内存中读取动态文件的属性信息的,因此效率更高;

stat是从文件名获得文件信息,适用于未被打开的文件,stat是从磁盘获取文件属性信息的;

lstat与“fstat和stat”的区别:对于符号连接文件,lstat查阅的是符号连接文件本身的文件属性信息(快捷方式文件自身的文件属性信息),而stat和fstat查阅的是符号链接文件指向的源文件的文件属性信息!

2.2 struct stat结构体简介

struct stat是llinux系统内核定义的一个结构体,在<sys/stat.h>中声明,这个结构体中的所有元素加起来就是我们的文件属性信息。

 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 filesystem I/O */
               blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */
               struct timespec st_mtim;  /* time of last modification */
               struct timespec st_ctim;  /* time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };

写个程序查看文件信息:

 

3. stat函数的应用案例

3.1 使用代码判断文件类型

文件类型就是“普通文件,目录文件,字符设备文件,块设备文件,管道文件,套接字文件,符号连接文件”;

文件属性中的文件类型标志在struct stat的mode_t  st_mode元素中,这个元素是一个按位来定义的一个位标志,由很多的标志位共同构成,记录了很多信息,查找是按位&操作就知道结果了,但是由于这些位定义不容易记住,因此linux事先定义好了很多宏来进行操作。

 Because tests of the above form are common, additional macros are defined by POSIX to allow the test of the file type in st_mode to be written more concisely:

           S_ISREG(m)  is it a regular file?//是否是普通文件

           S_ISDIR(m)  directory?//是否是目录文件

           S_ISCHR(m)  character device?//是否是字符设备文件

           S_ISBLK(m)  block device?//是否是块设备文件

           S_ISFIFO(m) FIFO (named pipe)?

           S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)

           S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

例如,S_ISREG宏返回值是1表示这个文件是一个普通文件,如果文件不是普通文件则返回值是0;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>



#define NAME "1.txt"

int main()
{


    int ret=-1;
    struct stat buff;
    memset(&buff,0,sizeof(buff));//memset后,buff中全部是0;
    ret=stat(NAME,&buff);//stat后,buff中有内容了;
    if(ret<0)
    {
        perror("stat");
        exit(-1);
    }
    //判断文件属性
    int result=S_ISREG(buff.st_mode);
    printf("result is %d\n",result);

    return 0;

}

 

3.2  利用代码判断文件权限设置

st_mode中除了记录文件类型以外,还记录了“文件权限”;

linux并没有提供对文件权限测试的宏操作,而只提供了掩码,所以我们只能使用位掩码来判断是否具有响应的权限;

The following mask values are defined for the file mode component of the st_mode field:

           S_ISUID     04000   set-user-ID bit
           S_ISGID     02000   set-group-ID bit (see below)
           S_ISVTX     01000   sticky bit (see below)
           
           //属主的权限user
           S_IRWXU     00700   owner has read, write, and execute permission
           
           S_IRUSR     00400   owner has read permission
           S_IWUSR     00200   owner has write permission
           S_IXUSR     00100   owner has execute permission
           
           //所在组的权限group
           S_IRWXG     00070   group has read, write, and execute permission
           
           S_IRGRP     00040   group has read permission
           S_IWGRP     00020   group has write permission
           S_IXGRP     00010   group has execute permission

          //other的权限      
           S_IRWXO     00007   others  (not  in group) have read, write, and exe‐
                               cute permission
           S_IROTH     00004   others have read permission
           S_IWOTH     00002   others have write permission
           S_IXOTH     00001   others have execute permission

 

4. 文件权限管理1

4.1 st_mode中记录的文件权限位

st_mode本质上是一个32位的数,类型是unsigned int,这个数里的每一位表示一个含义;

文件类型和文件权限都记录在st_mode中,我们使用的时候用专门的掩码取出响应的位即可得到响应的信息。

4.2 ls -l打印出的权限列表

123-456-789,一共9位,3个一组。

第一组三个表示文件的属主(owner或者user,简写u)对该文件的可读、可写、可执行权限;

第二组三个表示文件的属主所在的组(group,简写g)对该文件的可读、可写、可执行权限;

第三组三个表示其他用户(others,简写o)对该文件的可读、可写、可执行权限。

“属主”就是这个文件属于谁,一般来说文件创建时属主就是创建这个文件的那个用户;

在一个文件创建以后,还可以使用chown命令去修改这个文件的属主,也可以使用chgrp命令去修改文件所在的组。

4.3 文件操作时的权限检查规则

一个程序a.out被执行,a.out试图去操作一个文件1.txt,这时如何判定a.out是否具有对1.txt的某种操作权限呢?

判定方法是:首先1.txt具有9个权限位,依次规定了3种人(user,group,others)对该文件的操作权限。所以我们判定1.txt能否被a.out来操作,首先搞清楚执行a.out的人是属于哪个角色,即a.out被哪种角色的人执行了,也就是当前程序(进程)是哪个用户的进程。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>


#define NAME "1.txt"
int main(viod)
{
    int ret=open(NAME,O_RDONLY);
    if(ret>0)
    {
        printf("可读");
        close(ret);
    }
    else
    {
        perror("read");
    }

    ret=open(NAME,O_WRONLY);
    if(ret>0)
    {
        printf("可写");
        close(ret);
    }
    else
    {
        perror("write");
    }

    return 0;
}

 

 

5. 文件权限管理2

5.1 access函数检查权限设置

文件的权限管理并不简单,一般不容易确定对于以个文件是具有某种操作权限。设计优秀的软件,在操作某个文件之前应该自动判断是否具备对该文件的操作权限,如果有权限则执行,没有则应明确提示用户没有操作权限。

access函数可以测试当前执行程序的用户是否具有对目标文件的操作权限。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define NAME "1.txt"

int main(void)
{
    int ret = -1;
    ret = access(NAME,F_OK);
    if(ret<0)
    {
        printf("文件不存在!\n");
    }
    else
    {
        printf("文件存在OK\n");
    }

    ret = access(NAME,R_OK);
    if(ret<0)
    {
        printf("文件不可读!\n");
    }
    else
    {
        printf("文件可读OK\n");
    }

    ret = access(NAME,W_OK);
    if(ret<0)
    {
        printf("文件不可写!\n");
    }
    else
    {
        printf("文件可写OK\n");
    }

    return 0;
}

5.2 chmod/fchmod与权限修改

chmod是一个linux命令,用来修改文件的各种权限属性。chmod命令只有root用户才有权利去执行修改。

chmod命令其实内部是用linux的一个叫chmod的API实现的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define NAME "1.txt"


int main(int argc , char **argv)
{
    int ret = -1;
    if(agrc != 2)
    {
        printf("usage : %s filename\n",argv[0]);
        return -1;//不用包含头文件?
    }

    //chmod成功返回0,失败返回-1
    ret=chmod(argv[1],S_IRUSR|S_IWUSR|S_IXUSR);//argv[1]就是filename!!!
    if(ret<0)
    {
        perror("chmod");
        return -1;
    }

    return 0;

}

 

5.3 chown/fchown/lchown与属主修改

linux中使用chown命令来修改文件的属主

chown命令是用chown API来实现的。

5.4 umask与文件权限掩码

umask是linux系统中维护的一个全局设置,umask的作用是用来设定我们系统中新建文件的默认权限的。

 

 

6.  读取目录文件

6.1 opendir与readdir函数

opendir打开一个目录后得到一个DIR类型的指针给readdir使用;

readdir调用一次就会返回一个struct dirent类型的指针,该指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(目录中的一个子文件);

 struct dirent {
               ino_t          d_ino;       /* inode number */
               off_t          d_off;       /* not an offset; see NOTES */
               unsigned short d_reclen;    /* length of this record */
               unsigned char  d_type;      /* type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* filename */
           };

redir调用一次只能读取一个目录项,要想读出目录中所有的目录项必须多次调用readdir函数。readdir函数内部记住了哪个目录项已经被读过了哪个还没被读过,所以多次调用后不会重复返回已经返回过的目录项。当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。

DT_BLK      This is a block device.

       DT_CHR      This is a character device.

       DT_DIR      This is a directory.

       DT_FIFO     This is a named pipe (FIFO).

       DT_LNK      This is a symbolic link.

       DT_REG      This is a regular file.

       DT_SOCK     This is a UNIX domain socket.

       DT_UNKNOWN  The file type is unknown.

 

6.2 dirent结构体

 

6.3 读取目录实战演练

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>



int main(int argc,int **argv)
{
   DIR *pDir = NULL;
   struct dirent *pEnt= NULL;
   unsigned count = 0;
    if(argc != 2)
    {
       printf("usage: %s dirname\n",argv[0]);
       return -1;
    }

    pDir = opendir(argv[1]);
    if(NULL == pDir)
    {
        perror("opendir");
    }
    while(1)
    {
        pEnt=readdir(pDir);
        if(pEnt!=NULL)
        {
            //还有子文件,再次处理子文件
            printf("name : [%s]   \n",pEnt->d_name);
            count++;
            /*
            if(pEnt->D_type == DT_REG)
            {
                printf("是普通文件哦\n");
            }
            else
            {
                printf("不是普通文件\n");
            }
            */
        }
        else
        {
            break;
        }
    }
    printf("总文件书是:%d\n",count);

    return 0;

}

6.4 可重入函数介绍

有些函数是可重入的,有些是不可重入的;

readdir函数与之前的函数不同之处在于,readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readdir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法设计readdir不可重入的关键,

readdir在多次调用时是有关联的,这个关联也表明readdir是不可重入的。

库函数中有一些函数在设计之初是不可重入的,后来意识到这种方式是不安全的,所以重新封装了C库,提供了对应的可重入版本(一般是不可重入函数名_r):readdir(不可重入)  readdir_r(可重入)。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章