[UNIX環境高階程式設計] 檔案和目錄
1 引言
上文圍繞了普通檔案I/O進行了討論——開啟檔案、讀檔案或寫檔案。本文將描述檔案系統的其他特徵和檔案的性質。將從stat函式開始,stat結構中的大多數成員都是基本系統資料型別,逐個分解stat結構的每一個成員以瞭解檔案的所有屬性。
使用stat函式最多的地方可能就是[ls -ls]命令,可以獲得一個檔案的全部資訊。
本文主要討論4個stat函式以及它們的返回資訊。
struct stat
{
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* file type and mode */
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
};
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
// return 0 or -1 if an error occurred.
int stat(const char* pathname, struct stat* buf);
int fstat(int fd, struct stat* buf);
int lstat(const char* pathname, struct stat* buf);
#include <fcntl.h>
#include <sys/stat.h>
// return 0 or -1 if an error occurred.
int fstatat(int dirfd, const char* pathname, struct stat* buf, int flags);
一旦給出pathname,stat函式將返回與此檔案有關的資訊結構。fstat函式獲得已在描述符fd上開啟檔案的有關資訊。lstat函式類似於stat,但是當命名的檔案是一個符號連結時,lstat返回該符號連結的有關資訊,而不是由該符號連結引用的檔案的資訊。
fstatat函式為一個相對於當前開啟目錄(由fd引數指定)的路徑名返回檔案統計資訊。flag引數控制著是否跟隨著一個符號連結。當AT_SYMLINK_NOFOLLOW標誌被設定時,fstatat不會跟隨符號連結,而是返回符號連結本身的資訊。如果fd引數的值是AT_FDCWD,並且pathname引數是一個相對路徑名,fstatat會計算相對於當前目錄的pathname引數;如果pathname引數是一個絕對路徑,fd引數就會被忽略。這兩種情況下,fstatat的作用就跟stat或lstat一樣。
2 檔案型別
檔案型別包含如下幾種:
[1] 普通檔案
[2] 目錄檔案
[3] 塊特殊檔案:提供對裝置帶緩衝的訪問,每次訪問以固定長度為單位進行
[4] 字元特殊檔案:提供對裝置不帶緩衝的訪問,每次訪問長度可變。系統中的所有裝置要麼是字元特殊檔案,要麼是塊特殊檔案
[5] FIFO:這種型別的檔案用於程序間通訊,有時也成為命名管道
[6] 套接字:這種型別的檔案用於程序間的網路通訊,也可用於在一臺宿主機上程序之間的非網路通訊
[7] 符號連結:這種型別的檔案指向另一個檔案
檔案型別資訊儲存在stat結構的st_mode成員中,下表中的巨集引數都是stat結構中st_mode成員的可取值。
巨集 | 檔案型別 |
---|---|
S_ISREG() | 普通檔案 |
S_ISDIR() | 目錄檔案 |
S_ISCHR() | 字元特殊檔案 |
S_ISBLK() | 塊特殊檔案 |
S_ISFIFO() | 管道或FIFO |
S_ISLNK() | 符號連結 |
S_ISSOCK() | 套接字 |
3 檔案ID和組ID
3.1 設定使用者ID和設定組ID
與一個程序相關聯的ID有6個或更多,包括:
實際使用者ID和實際組ID:標識我們究竟是誰;
有效使用者ID、有效組ID和附屬組ID:決定了我們的檔案訪問許可權;
儲存的設定使用者ID和儲存的設定組ID:在執行一個程式時包含了有效使用者ID和有效組ID的副本。
通常:有效使用者ID等於實際使用者ID,有效組ID等於實際組ID。
每個檔案有一個所有者和組所有者,所有者由stat結構中的st_uid指定,組所有者由st_gid指定。
設定使用者ID位以及設定組ID位都包含在檔案的st_mode值中,這兩位可分別用常量S_ISUID和S_ISGID測試。
3.2 更改檔案的使用者ID和組ID
下面幾個chown函式可用於更改檔案的使用者ID和組ID。
#include <unistd.h>
// return 0 or -1 if an error occurred.
int chown(const char* path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char* path, uid_t owner, gid_t group);
#include <fcntl.h>
#include <unistd.h>
// return 0 or -1 if an error occurred.
int fchownat(int dirfd, const char* pathname, uid_t owner, gid_t group, int flags);
除了所引用檔案為符號連結外,這4個函式的操作類似。在符號連結情況下,lchown和fchownat(設定了AT_SYMLINK_NOFOLLOW標誌)更改符號連結本身的所有者,而不是符號連結所指向的檔案的所有者。
fchown函式改變fd引數指向的開啟檔案的所有者,既然它在一個已開啟的檔案上操作,就不能用於改變符號連結的所有者。
4 檔案訪問許可權
st_mode值也包含了對檔案的訪問許可權位。當提及檔案時,指的是前面所提到的任何型別的檔案。所有檔案型別都有訪問許可權。
每個檔案有9個訪問許可權位,可分為三類。
st_mode遮蔽 | 含義 |
---|---|
S_IRUSR | 使用者讀 |
S_IWUSR | 使用者寫 |
S_IXUSR | 使用者執行 |
S_IRGRP | 組讀 |
S_IWGRP | 組寫 |
S_IXGRP | 組執行 |
S_IROTH | 其他讀 |
S_IWOTH | 其他寫 |
S_IXOTH | 其他執行 |
chmod命令可以修改這9個許可權位,用u表示使用者,用g表示組,用o表示其他。
4.1 訪問許可權測試
當用open函式開啟一個檔案時,核心以程序的有效使用者ID和有效組ID為基礎執行其訪問許可權測試。有時,程序也希望按其實際使用者ID和實際組ID來測試其訪問能力。
access和fassessat函式是按實際使用者ID和實際組ID進行訪問許可權測試的。
#include <unistd.h>
// return 0 or -1 if an error occurred.
int access(const char* pathname, int mode);
#include <fcntl.h>
#include <unistd.h>
// return 0 or -1 if an error occurred.
int faccessat(int dirfd, const char* pathname, int mode, int flags);
測試檔案是否存在,mode位F_OK;否則是下表所列常量的按位或
mode | 說明 |
---|---|
R_OK | 測試讀許可權 |
W_OK | 測試寫許可權 |
X_OK | 測試執行許可權 |
flag引數可以用於改變faccessat的行為,如果flag設定位AT_EACCESS,訪問檢查用的時呼叫程序的有效使用者ID和有效組ID,而不是實際使用者ID和實際組ID。
4.2 為程序設定檔案模式建立遮蔽字
#include <sys/types.h>
#include <sys/stat.h>
// Always succeeds and return previous value of the mask.
mode_t umask(mode_t mask);
4.3 更改現有檔案的訪問許可權
#include <sys/stat.h>
// return 0 or -1 if an error occurred.
int chmod(const char* path, mode_t mode);
int fchmod(int fd, mode_t mode);
#include <fcntl.h>
#include <sys/stat.h>
// return 0 or -1 if an error occurred.
int fchmodat(int dirfd, const char* pathname, mode_t mode, int flags);
chmod函式在指定的檔案上進行操作。
fchmod函式對已開啟的檔案進行操作。
4.4 檔案訪問許可權小結
常量 | 說明 | 對普通檔案的影響 | 對目錄的影響 |
---|---|---|---|
S_ISUID | 設定ID | 執行時設定有效使用者ID | 未使用 |
S_ISGID | 設定組ID | 若組執行位設定則執行設定有效組ID,否則使強制性鎖起作用(若支援) | 將在目錄中建立的新檔案的組ID設定為目錄的組ID |
S_ISVTX | 黏著位 | 在交換區快取程式正文(若支援) | 限止在目錄中刪除和重新命名檔案 |
S_IRUSR | 使用者讀 | 許可使用者讀檔案 | 許可使用者讀目錄項 |
S_IWUSR | 使用者寫 | 許可使用者讀檔案 | 許可使用者在目錄中刪除和建立檔案 |
S_IXUSR | 使用者執行 | 許可使用者讀檔案 | 許可使用者在目錄中搜索給定路徑名 |
S_IRGRP | 組讀 | 許可組讀檔案 | 許可組讀目錄項 |
S_IWGRP | 組寫 | 許可組讀檔案 | 許可組在目錄中刪除和建立檔案 |
S_IXGRP | 組執行 | 許可組讀檔案 | 許可組在目錄中搜索給定路徑名 |
S_IROTH | 其他讀 | 許可其他讀檔案 | 許可其他讀目錄項 |
S_IWOTH | 其他寫 | 許可其他讀檔案 | 許可其他在目錄中刪除和建立檔案 |
S_IXOTN | 其他執行 | 許可其他讀檔案 | 許可其他在目錄中搜索給定路徑名 |
S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR
S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH