第四章:文件和目錄
本章在第三章的基礎上描述文件的屬性,如大小、創建時間等。
本章最後介紹對目錄進行操作的各個函數。
一、stat()、fstat()、fstatat()和lstat()
stat系列函數用於返回文件的屬性信息,如文件類型、大小、所有者、訪問修改時間等。函數聲明如下:
1 /* 文件屬性查看函數 */ 2 #include <sys/stat.h> 3 4 int stat(const char *pathname, struct stat *buf); 5 int fstat(int fd, struct stat *buf);6 int lstat(const char *pathname, struct stat *buf); 7 int fstatat(int fd, const char *pathname, struct stat *buf, int flags); 8 9 /* 例子 */ 10 struct stat st; 11 fstat(fd, &st); 12 char *buf; 13 14 if (S_ISREG(st.st_mode)) 15 buf = "regular"; /* 普通文件 */ 16 else if (S_ISDIR(st.st_mode))17 buf = "directory"; /* 目錄 */ 18 else if (S_ISCHR(st.st_mode)) 19 buf = "character special"; /* 字符文件 */ 20 else if (S_ISBLK(st.st_mode)) 21 buf = "block special"; /* 塊文件 */ 22 else if (S_ISFIFO(st.st_mode)) 23 buf = "fifo"; /* 管道文件 */ 24 /* 25 else if (S_ISLINK(st.st_mode))26 buf = "link"; /* 鏈接文件,使用fstat()無法識別 */ 27 */ 28 else if (S_ISSOCK(st.st_mode)) 29 buf = "socket"; /* 網絡套接字 */ 30 else 31 buf = "unknow mode"; 32 33 printf("%s\n", buf);
函數參數以及返回值:
pathname:文件路徑名
buf:返回的stat結構體
fd:文件打開函數返回的文件描述符
flags:標識符
返回值:成功返回0;出錯返回-1。
上面函數返回的stat結構體各個實現可能存在差異,但它們都至少具備下列的信息:
1 struct stat { 2 mode_t st_mode; // 文件類型和訪問權限 3 ino_t st_ino; // 指向數據塊的節點的編號 4 dev_t st_dev; // 設備號 5 dev_t st_rdev; 6 nlink_t st_nlink; // 硬鏈接數 7 uid_t st_uid; // 用戶ID 8 gid_t st_gid; // 用戶組ID 9 off_t st_size; 10 struct timespec st_atim; // 數據訪問時間 11 struct timespec st_mtim; // 數據修改時間 12 struct timespec st_ctim; // 屬性修改時間 13 blksize_t st_blksize; // 最好的IO塊大小 14 blkcnt_t st_blocks; 15 };
二、文件類型
UNIX系統中的文件大多數是普通文件和目錄,但也存在其他類型的文件,其分類如下:
普通文件(regular file):包含數據的常規文件,數據可以是文本類型的,也可以是二進制的。
目錄文件(directory file):它是一個目錄,保存目錄相關的信息。
塊設備文件(block special file):這種文件提供對硬件(如磁盤)帶緩沖的訪問。
字符設備文件(regular file):這種文件提供對硬件(如磁盤)不帶緩沖的訪問。
FIFO:這種文件用於進程間通信。
套接字文件(regular file):這種文件用於網絡間通信。
符號連接(regular file):類似Windows系統的快捷方式,指向另外一個文件。
以上文件類型的信息存儲在前面說明的stat結構體中st_mode成員中的。正如我所給出的上面的例子,st_mode成員的讀取是利用系統提供的宏函數進行的。在此我總結以下上文例子中的宏函數:
S_ISREG() /* 普通文件 */ S_ISDIR() /* 目錄 */ S_ISCHR() /* 字符文件 */ S_ISBLK() /* 塊文件 */ S_ISFIFO() /* 管道文件 */ S_ISLINK() /* 鏈接文件 */ S_ISSOCK() /* 網絡套接字 */
三、文件訪問權限
stat結構體中st_mode成員還包含有文件的訪問權限,訪問權限曾在第三章第二節有簡單的演示。
為了打開任意類型的一個文件,則需要對該文件所在的父級以及父級的父級等目錄具有執行權限。刪除一個文件不需要對該文件有任何權限,只需要對被刪除文件的父級目錄具有寫和執行權限即可。
進程每次打開、創建或刪除一個文件時,內核就對該文件進行訪問權限測試,通常的步驟是:
1. 先判斷進程是否是超級用戶,即ID是否為0,是則允許訪問,否則執行第二步;
2. 再判斷進程的有效用戶ID是否等於文件的所有者ID,如果是並且被訪問文件設定了適當的讀寫權限,則允許訪問;否則執行第三步;
3. 然後判斷進程有效組ID或者附加組ID是否等於文件的組ID,如果是並且被訪問文件設定了適當的讀寫權限,則允許訪問;否則執行第四步;
4. 最後查看文件的其他用戶是否有適當權限訪問文件,有則允許,否則判斷結束、訪問失敗。
四、access()和faccessat()
access()和faccessat()函數可用於判斷當前用戶是否具有訪問某個文件的權限。函數聲明如下:
1 /* 權限檢測函數 */ 2 #include <unistd.h> 3 4 int access(const char *pathname, int mode); 5 int faccessat(int fd, const char *pathname, int mode, int flags); 6 7 /* 例子 */ 8 if (access("a.txt", R_OK) == 0) /* 是否有讀權限 */ 9 printf("a.txt read ok\n"); 10 if (access("a.txt", W_OK) == 0) /* 是否有寫權限 */ 11 printf("a.txt write ok\n"); 12 if (access("a.txt", X_OK) == 0) /* 是否有執行權限 */ 13 printf("a.txt execute ok\n");
函數參數以及返回值:
pathname:文件路徑名
mode:模式,包含有R_OK、W_OK、X_OK和F_OK
flags:標識符
返回值:有權限返回0
五、文件操作其他函數
權限屏蔽函數umask(),用於設置創建新文件時的權限屏蔽字,對於修改文件權限時權限屏蔽字沒有效果,函數聲明如下:
#include <sys/stat.h> mode_t umask(mode_t mask); /* 例子 */ mode_t old = umask(0222); /* 如果原來的權限是0777,那麽最終的結果是077 - 0222 = 0555,old返回原來的權限 */ umask(old); /* 恢復權限 */
chmod()、fchmod()和fchmodat()這三個函數用於更改文件的訪問權限。函數聲明如下:
1 #include <sys/stat.h> 2 3 int chmod(const char *file, mode_t mode); 4 int fchmod(int fd, mode_t mode); 5 int fchmodat(int fd, const char *file, mode_t mode, int flag); 6 7 /* 例子 */ 8 fchmod(fd, 0666); /* 更改權限為0666 */
權限改變成功則返回0,失敗返回-1。
六、目錄相關函數
1 #include <dirent.h> 2 3 /* 打開一個目錄 */ 4 DIR *opendir(const char *name); // 成功返回指針,失敗返回NULL 5 DIR *fdopendir(int fd); // 成功返回指針,失敗返回NULL 6 7 /* 讀取目錄中的內容,如文件、子目錄 */ 8 struct dirent *readdir(DIR *dirp); // 成功返回指針,失敗返回NULL 9 10 /* 讓目前的讀取位置還原到開頭的讀取位置 */ 11 void rewinddir(DIR *dirp); 12 13 /* 設置相對於開頭偏移值為pos的讀取位置 */ 14 void seekdir(DIR *dirp, long int pos); 15 16 /* 關閉目錄 */ 17 int closedir(DIR *dirp); // 成功時返回0,失敗返回-1 18 19 /* 例子 */ 20 DIR* dir = opendir("../"); 21 struct dirent* ent; 22 while(ent=readdir(dir)) // 1 讀, 2 =,3 判斷ent是否為0 23 { 24 printf("%d, %s\n", ent->d_type, ent->d_name); 25 } // d_tpye == 4 的是目錄 26 closedir(dir);
讀某個目錄內容(子項)的步驟:
1. opendir()返回目錄指針
2. 循環調用readdir(),逐一讀取每個子項
3. closedir()關閉目錄,這步也可以省略
第四章:文件和目錄