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(可重入)。

 

 

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