天天看點

13-stat 結構體 st_mode 字段

上一篇我們使用了 stat 函數取得了 test.txt 檔案的相關屬性,這些屬性都儲存在一個叫 struct stat 的結構體中:

struct stat {
    dev_t     st_dev;         /* 包含這個檔案的裝置 ID */
    ino_t     st_ino;         /* inode 編号 */
    mode_t    st_mode;        /* 通路權限 */
    nlink_t   st_nlink;       /* 硬連結數量 */
    uid_t     st_uid;         /* 使用者ID */
    gid_t     st_gid;         /* 組ID */
    dev_t     st_rdev;        /* 裝置ID */
    off_t     st_size;        /* 檔案占用的位元組數 */
    blksize_t st_blksize;     /* 檔案系統塊大小 */
    blkcnt_t  st_blocks;      /* 檔案占用了幾個512位元組 */
    time_t    st_atime;       /* 最後通路時間 */
    time_t    st_mtime;       /* 最後更改時間 */
    time_t    st_ctime;       /* 最後狀态更改時間 */      

本篇我們隻介紹 st_mode 字段。在上一篇中,我們得到的 st_mode 字段的10進制值是 33204. 記住這個值,待會我們要分析。

1 一堆 ID

  • 實際使用者 ID、實際組 ID
  • 有效使用者 ID、有效組 ID
  • 設定使用者 ID、設定組 ID

1、實際使用者ID(uid)和實際使用者組ID(gid):辨別我是誰。也就是登入使用者的uid和gid,比如我的Linux以allen登入,在Linux運作的所有的指令的實際使用者ID都是allen的uid,實際使用者組ID都是allen的gid(可以用id指令檢視)。

2、有效使用者ID(euid)和有效使用者組ID(egid):程序用來決定我們對資源的通路權限。一般情況下,有效使用者ID等于實際使用者ID,有效使用者組ID等于實際使用者組ID。當設定-使用者-ID(SUID)位設定,則有效使用者ID等于檔案的所有者的uid,而不是實際使用者ID;同樣,如果設定了設定-使用者組-ID(SGID)位,則有效使用者組ID等于檔案所有者的gid,而不是實際使用者組ID。

例:

  1. 你在登入 linux 時輸入的帳号,就是實際使用者。比方說我登入的帳号是 allen,那麼實際使用者就是 allen.
  2. 當你以 sudo 執行指令時,比如 sudo rm text.txt,這時候 ,在執行這條指令的時候 ,實際使用者是 allen,有效使用者是 root。
  3. 假設目前實際使用者是 allen,你執行的檔案 a.out 的所有者是 david,這時候你在執行這條指令的時候 ,實際使用者是 allen,有效使用者也是 allen,如果 a.out 這個檔案設定使用者 ID 标志打開,那麼執行 a.out 的時候,實際使用者是 allen, 有效使用者是 david.

2 st_mode 的結構

st_mode 主要包含了 3 部分資訊:

  • 15-12 位儲存檔案類型
  • 11-9 位儲存執行檔案時設定的資訊
  • 8-0 位儲存檔案通路權限

圖1 展示了 st_mode 各個位的結構。

13-stat 結構體 st_mode 字段

圖1 st_mode 結構(圖中花括号裡的數字都是 2 進制)

2.1 黏着位(sticky)

要删除一個檔案,你不一定要有這個檔案的寫權限,但你一定要有這個檔案的上級目錄的寫權限。也就是說,你即使沒有一個檔案的寫權限,但你有這個檔案的上級目錄的寫權限,你也可以把這個檔案給删除,而如果沒有一個目錄的寫權限,也就不能在這個目錄下建立檔案。

如何才能使一個目錄既可以讓任何使用者寫入檔案,又不讓使用者删除這個目錄下他人的檔案,sticky就是能起到這個作用。sticky一般隻用在目錄上,用在檔案上起不到什麼作用。

在一個目錄上設了sticky位後,(如/home,權限為1777)所有的使用者都可以在這個目錄下建立檔案,但隻能删除自己建立的檔案(root除外),這就對所有使用者能寫的目錄下的使用者檔案啟到了保護的作用。

如果使用者對目錄有寫權限,則可以删除其中的檔案和子目錄,即使該使用者不是這些檔案的所有者,而且也沒有讀或寫許可。黏着位出現執行許可的位置上,用t表示,設定了該位後,其它使用者就不可以删除不屬于他的檔案和目錄。但是該目錄下的目錄不繼承該權限,要再設定才可使用。

普通檔案的sticky位會被linux核心忽略。

目錄的sticky位表示這個目錄裡的檔案隻能被owner和root删除 。

/tmp常被我們用來存放臨時檔案,是所有使用者。但是我們不希望别的使用者随随便便的就删除了自己的檔案,于是便有了黏着位,它的作用便是讓使用者隻能删除屬于自己的檔案。

$ ls -dl /tmp  
drwxrwxrwt 15 root root  .........  // 注意 other 使用者的權限位,x 變成了 t      

那麼原來的執行标志x到哪裡去了呢? 系統是這樣規定的, 假如本來在該位上有x, 則這些特别标志 (suid, sgid, sticky) 顯示為小寫字母 (s, s, t). 否則, 顯示為大寫字母 (S, S, T) 。

另外:

chmod 777 abc  
chmod      

等價于

chmod 1777      

3 執行個體分析

3.1 普通檔案執行個體

上一篇我們得到的 st_mode 10進制值是 33204,轉換成 8 進制後為 100664.

把它填到圖1中後,是這樣:

13-stat 結構體 st_mode 字段

圖2 33204 填到 st_mode 中

根據圖2 我們可以得到如下資訊:

  • 1000: 這是一個正常檔案
  • 000: 執行時設定資訊為空,黏着位為 0
  • 110-110-100: 使用者權限為​

    ​RW-​

    ​​,組員權限為​

    ​RW-​

    ​​,其他人權限為​

    ​R--​

3.2 帶設定-使用者-ID标志的檔案分析

我們要分析的檔案是這樣的:

-rwsr-xr-x  1 root  root  7612 11月 28 14:58 append
-rw-r--r--  1 root  root     6 11月 28 14:52 test.txt      

append 是一個可執行檔案,它的作用是以追加的方式打開 test.txt,代碼如下:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
        int fd = open("test.txt", O_WRONLY | O_APPEND);
        if (fd == -1) {
                perror("open");
                return -1;
        }

        printf("uid: %d\n", getuid());
        printf("euid: %d\n", geteuid());

        close(fd);
        return 0;
}      

append 檔案生成方式如下:

$ gcc append.c -o append
$ sudo chown root:root append
$ sudo chmod u+s append      

目前實際使用者并不是 root,而是 allen.

一般來說,直接執行 ./append 會出現通路被禁止,因為 test.txt 檔案的所有者并不是 allen. 然而,這裡的會正常執行,結果顯示如下:

uid:1000
euid:0      

即實際使用者 ID 是 allen, 有效使用者 id 是 root. 為什麼直接執行 ./append 後有效使用者 id 不是 allen,而變成 了 root? 這裡實際上是 append 檔案的 suid 位被置 1 了。

通過 stat 函數,我們得到的 append 的 st_mode 值為 35309. 轉換成 8 進制後為 104755. 将其填充到圖 1 的結構中後是這樣的:

13-stat 結構體 st_mode 字段

圖3 填充 st_mode

分析圖3,可以知道:

- 1000:這是一個普通檔案

- 100:suid 為 1

- 111-101-101:使用者權限為 ​​

​RWX​

​​,組員權限為​

​R-X​

​​,其他人權限為​

​R-X​

是以這裡的 append 檔案在執行的時候,發現 suid 标志被打開,它會把有效使用者id (euid) 設定成 append 檔案的所有者(root)。

4 一些常用的宏

前面我們通過手工分析 st_mode 字段,實際上是很不友善的。實際寫程式,你可以使用 st_mode & 掩碼來得到 st_mode 中特定的部分。比如:

  • st_mode & 0170000 : 得到檔案類型
  • st_mode & 0007000 : 得到執行檔案時設定資訊
  • st_mode & 0000777 : 得到權限位
  • st_mode & 00100: 判斷所有者是否可執行

    ……

如果在程式中真這麼寫,估計很少有人願意去看。實際上,可以使用 linux 預定義的一些宏來代替這些生硬的數字。這些宏定義在 sys/stat.h 頭檔案中。

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

#define
#define
#define
#define
#define
#define
#define

#define
#define
#define
#define

#define
#define
#define
#define

#define
#define
#define
#define      

總結

  • 實際使用者 ID,實際使用者組 ID
  • 有效使用者 ID,有效使用者組 ID
  • 設定使用者 ID,設定使用者組 ID
  • st_mode 字段結構(檔案類型-執行時檔案設定資訊-權限位)
  • 黏着位

繼續閱讀