UNIX環境高級編程(7):文件和目錄(1)

stat、fstat、lstat函數:

本章討論的中心是三個stat函數以及它們返回的信息:

#include <sys/stat.h>

int stat(const char *restrict pathname,struct stat *restrict buf);

int fstat(int filedes, struct stat *buf);

int lstat(const char *restrict pathname, struct stat *restrict buf);

三個函數的返回值:若成功返回0,若出錯則返回-1.

stat函數返回pathname所指定文件的有關的信息結構,fstat函數獲取已在描述符filedes上打開文件的有關信息,lstat函數類似於stat,但是當命名的文件是一個符號鏈接時,lstat返回該符號鏈接的有關信息,而不是由符號鏈接所指向文件的信息。

第二個參數buf是個指向struct stat結構體的指針,struct stat的實際定義可能隨實現有所不同,但其基本形式是:

struct stat {

mode_t st_mode;  /* file type & mode(permissions) */
info_t st_info;
dev_t st_dev;
dev_t st_rdev;
nlinK_t st_nlink;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
blksize_t st_blksize;
blkcnt_t st_blocks;

};

該結構的每一個成員都是基本系統數據類型,我們將說明此結構的每一個成員以瞭解文件的屬性。使用stat函數最多的可能是ls -l命令,用其可以獲得有關一個文件的所有信息。

文件類型:

UNIX系統的大多數文件是普通文件或目錄,但也有另外的一些文件類型,文件類型包括以下幾種:

(1)普通文件:這是最常用的文件類型,這種文件包含了某種形式的數據,這種數據是文本還是二進制數據對於UNIX內核而言並無區別。值得注意是,對於二進制可執行文件,爲了執行程序,內核必須理解其格式,所有二進制可執行文件都遵循一種格式,這種格式使內核能夠確定程序文本和數據加載的位置。

(2)目錄文件:這種文件包含了其他文件的名字以及指向與這些文件有關信息的指針。

(3)塊特殊文件:這種文件類型提供對設備(例如磁盤)帶緩衝的訪問,每次訪問以固定長度爲單位進行。

(4)字符特殊文件:這種文件類型提供對設備不帶緩衝的訪問,每次訪問長度可變。系統中的所有設備要麼是字符特殊文件,要麼是塊特殊文件。

(5)FIFO:這種文件類型用於進程間通信,有時也將其稱爲命名管道。

(6)套接字(socket):這種文件類型用於進程間的網絡通信。套接字也可用於在一臺宿主機上進程之間的非網絡通信。

(7)符號鏈接:這種文件類型指向另一個文件。

文件類型信息包含在stat結構體的st_mode成員中,可用下列宏確定文件類型,這些宏的參數都是stat結構中的st_mode成員:

  • S_ISREG() 普通文件;
  • S_ISDIR() 目錄文件;
  • S_ISBLK() 塊特殊文件;
  • S_ISCHR() 字符特殊文件;
  • S_ISFIFO() 管道或FIFO
  • S_ISSOCK() 套接字;
  • S_ISLNK() 符號鏈接;

POSIX.1允許實現將進程間通信(IPC)對象(例如,消息隊列和信號量)表示爲文件。可用如下宏來確定IPC對象的類型,這些宏的參數是指向stat結構的指針:

  • S_TYPEISMQ() 消息隊列;
  • S_TYPEISSEM() 信號量;
  • S_TYPEISSHM  共享存儲對象;
下列程序接收命令行參數,並針對每一個命令行參數打印其所對應文件的文件類型:
/*
 * Copyright (C) [email protected]
 */


#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>


int
main(int argc, char *argv[])
{
	int	i;
	struct stat	buf;
	char *pt;

	for (i = 1; i < argc; i++) {
		printf("%s: ", argv[i]);
		if (lstat(argv[i], &buf) < 0) {
			printf("lstat error\n");
			continue;
		}

		if (S_ISREG(buf.st_mode)) {
			pt = "regular";
		} else if (S_ISDIR(buf.st_mode)) {
			pt = "directory";
		} else if (S_ISBLK(buf.st_mode)) {
			pt = "block special";
		} else if (S_ISCHR(buf.st_mode)) {
			pt = "character special";
		} else if (S_ISFIFO(buf.st_mode)) {
			pt = "FIFO";
		} else if (S_ISSOCK(buf.st_mode)) {
			pt = "socket";
		} else if (S_ISLNK(buf.st_mode)) {
			pt = "symbol link";
		} else {
			pt = "unknown mode";
		}
		printf ("%s\n", pt);
	}

	exit(0);
}

以下是該程序的執行結果:


該程序特地使用了lstat函數而不是stat函數以便檢測符號鏈接,如果使用了stat函數,則不會觀察到符號鏈接。

設置用戶ID和設置組ID:

與一個進程相關聯的ID有6個或更多:

  • 實際用戶ID、實際組ID:標識我們實際是誰,這兩個字段是在登錄時取自口令文件中的登錄項;
  • 有效用戶ID、有效組ID、附加組ID:決定了我們的文件訪問權限;
  • 保存的設置用戶ID、保存的設置組ID:在執行一個程序時保存了有效用戶ID和有效組ID的副本。

通常,有效用戶ID等於實際用戶ID,有效組ID等於實際組ID。

每個文件都有一個所有者和組所有者,所有者由stat結構中的st_uid成員表示,組所有者由st_gid成員表示。當執行一個程序文件時,進程的有效用戶ID通常就是實際用戶ID,有效組ID通常是實際組ID。但是可以在文件模式字st_mode中設置一個特殊標誌,其含義是“當執行此文件時,將進程的有效用戶ID設置爲文件所有者的用戶ID(st_uid)”。與此類似,在文件模式字中可以設置另一位,它使得將執行此文件的進程的有效組ID設置爲文件的組所有者ID(st_gid)。在文件模式字中的這兩位被稱爲設置用戶ID位(set-user-ID)和設置組ID位(set-group-ID)。設置用戶ID位及設置組ID位都保存在st_mode值中,這兩位可用常量S_ISUID和S_ISGID測試。

UNIX程序passwd就是一個設置了設置用戶ID的可執行程序。

文件訪問權限:

st_mode值中也包含了針對文件的訪問權限位。所有文件都有訪問權限。每個文件有9個訪問權限位,chmod命令可用於修改這9個權限位。

  • S_IRUSR文件所有者-讀;
  • S_IWUSR:文件所有者-寫;
  • S_IXUSR:文件所有者-執行;
  • S_IRGRP:文件所屬用戶組-讀;
  • S_IWGRP:文件所屬用戶組-寫;  
  • S_IXGRP:文件所屬用戶組-執行;
  • S_IROTH:其他-讀;
  • S_IWOTH:其他-寫;
  • S_IXOTH:其他-執行;

關於文件的權限,有如下注意事項:

  • 我們用名字打開任意類型的文件時,對該名字中包含的每一個目錄,包括它可能隱含的當前工作目錄都應具有可執行權限;
  • 對於一個文件的讀權限決定了我們是否能夠打開該文件進行讀操作;
  • 對於一個文件的寫權限決定了我們是否能夠打開該文件進行寫操作;
  • 爲了在一個目錄中創建一個新文件,必須對該目錄具有寫權限和可執行權限;
  • 爲了刪除一個文件,必須對包含該文件的目錄具有寫權限和可執行權限;
  • 如果用6個exec函數中的任何一個執行某個文件,必須對該文件具有可執行權限,該文件還必須是個普通文件;

進程每次打開、創建、刪除文件時,內核就進行文件訪問權限測試,這種測試可能涉及文件的所有者(st_uid和st_gid),進程的有效ID(有效用戶ID,有效組ID)以及進程的附加組ID(如果支持的話)。兩個所有者ID是文件的性質,而兩個有效ID和和附加組ID則是進程的性質。

文件訪問權限測試的過程爲(按順序執行這四步)

  • 若進程的有效用戶ID是0(超級用戶),則允許訪問;
  • 若進程的有效用戶ID等於文件所有者ID(也就是該進程擁有此文件),那麼:若所有者適當的訪問權限位被設置,則允許訪問,否則拒絕訪問;
  • 若進程的有效組ID或進程的附加組ID之一等於文件的組ID,那麼,若組適當的訪問權限位被設置,則允許訪問,否則拒絕訪問;
  • 若其他用戶適當的訪問權限位被設置,則允許訪問,否則拒絕訪問;

新文件或目錄的所有權:

新文件的用戶ID設置爲進程的有效用戶ID,關於組ID,POSIX.1允許實現選擇下列之一作爲新文件的組ID:

  • 新文件的組ID可以是進程的有效組ID;
  • 新文件的組ID可以是它所在目錄的組ID;

對於Linux系統,新文件的組ID取決於它所在目錄的設置組ID位是否被設置,如果該目錄的這一位已經設置,則將新文件的組ID設置爲目錄的組ID,否則將新文件的組ID設置爲進程的有效組ID。


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