1. 程式人生 > >第四章:文件和目錄

第四章:文件和目錄

以及 標識符 pos 函數返回 鏈接 文件訪問 用戶 dir ask


本章在第三章的基礎上描述文件的屬性,如大小、創建時間等。

本章最後介紹對目錄進行操作的各個函數。


一、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()關閉目錄,這步也可以省略

第四章:文件和目錄