4.1 引言html
本章將描述文件的特徵和文件的性質node
4.2 stat、fstat和lstat函數shell
#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
一旦給出pathname,stat函數就返回與此命名文件有關的信息結構。數組
fstat函數獲取已在描述符filedes上打開文件的有關信息。安全
lstat相似於stat,可是當命名的文件是一個符號連接時,lstat返回該符號連接的有關信息,而不是由該符號連接引用文件的信息網絡
第二個參數buf是指針,它指向一個咱們必須提供的結構。這些函數填寫由buf指向的結構。該結構的實際定義可能隨實現有所不一樣,但其基本形式是:數據結構
struct stat{ mode_t st_mode; //file type&mode (permissions) ino_t st_ino; //i-node number dev_t st_dev; //device number(file system) dev_t st_rdev; //device number for special files nlink_t st_ulink; //number of links uid_t st_uid; //user ID of owner gid_t st_gid; //group ID of owner off_t st_size; //size in bytes, for regular files time_t st_atime //time of last access time_t st_mtime //time of last modification time_t st_ctime; //time of last file status change blksize_t st_blksize; //best I/O block size blkcnt_t st_blocks; //number of disk blocks allocated };//該結構中每個成員都是基本系統數據類型
使用stat函數最多的多是ls -l命令,用其能夠得到有關一個文件的全部信息app
4.3 文件類型socket
UNIX系統的大多數文件是普通文件或目錄,可是也有另一些文件類型。文件類型包括以下幾種:ide
(1)普通文件 S_ISREG()
(2)目錄文件 S_ISDIR()
(3)塊特殊文件。這種文件類型提供對設備(例如磁盤)帶緩衝的訪問,每次訪問以固定長度爲單位進行 S_ISCHR()
(4)字符特殊文件。這種文件類型提供對設備不帶緩衝的訪問,每次訪問長度可變。系統中的全部設備要麼是字符特殊文件,要麼是塊特殊文件 S_ISBLK()
(5)FIFO。這種類型文件用於進程間通訊,有時也稱爲命名管道。 S_ISFIFO()
(6)套接字。這種類型用於進程間的網絡通訊。套接字也可用於在一臺宿主機上進程之間的非網絡通訊 S_ISSOCK()
(7)符號連接。這種文件類型指向另外一個文件 S_ISLNK()
(文件類型信息包含在stat結構的st_mode成員中,如S_ISREG)
POSIX.1容許實現將進程間通訊(IPC)對象(例如,消息隊列和信號量等)表示爲文件。
<sys/stat.h>中的IPC類型宏:S_TYPEISMQ() 消息隊列
S_TYPEISSEM() 消息量
S_TYPEISSHM() 共享存儲對象
(這些宏的參數並不是st_mode,而是指向stat結構的指針)
實例:4_1 對每一個命令行參數打印文件類型
1 #include"apue.h" 2 int main(int argc,char *argv[]) 3 { 4 int i; 5 struct stat buf; 6 char *ptr; 7 8 for(i=1;i<argc;i++){ 9 printf("%s: ",argv[i]); 10 if(lstat(argv[i],&buf)<0){ //若改爲stat(),則沒法看到link類型 11 err_ret("lstat error"); 12 continue; 13 } 14 if(S_ISREG(buf.st_mode)) 15 ptr="regular"; 16 else if (S_ISDIR(buf.st_mode)) 17 ptr="directory"; 18 else if (S_ISCHR(buf.st_mode)) 19 ptr="character special"; 20 else if (S_ISBLK(buf.st_mode)) 21 ptr="block special"; 22 else if (S_ISFIFO(buf.st_mode)) 23 ptr="fifo"; 24 else if (S_ISLNK(buf.st_mode)) 25 ptr="symbolic link"; 26 else if (S_ISSOCK(buf.st_mode)) 27 ptr="socket"; 28 else 29 ptr="** unknown mode **"; 30 printf("%s\n",ptr); 31 } 32 exit(0); 33 }
4.4 設置用戶ID和設置組ID
與一個進程相關聯的ID有6個或更多:
-實際用戶ID和實際組ID標識咱們到底是誰。這兩個字段在登陸時取自口令文件中的登錄項。一般,在一個登錄會話間這些值並不改變,可是超級用戶進程有方法改變它們
-有效用戶ID,有效組ID以及附加組ID決定了咱們的文件訪問全鄉
-保存的設置用戶ID和保存的設置組ID在執行一個程序時包含了有效用戶ID和有效組ID的副本
一般,有效用戶ID等於實際用戶ID,有效組ID等於實際組ID。可是能夠在文件模式字(st_mode)中設置一個特殊標誌,其含義是」當執行此文件時,將進程的有效用戶ID設置爲文件全部者ID(st_uid)「(st_gid狀況也相似)
例如,若文件的全部者是超級用戶,並且設置了該文件的設置用戶ID位,而後當該程序由一個進程執行時,則該進程具備超級用戶特權。(無論執行此文件的進程的實際用戶ID是什麼,都進行這種處理)
4.5 文件訪問權限
st_mode值也包含了針對文件的訪問權限位,每一個文件有9個訪問權限位,可將它們分紅三類:owner group other
S_IRUSR 用戶-讀
S_IWUSR 用戶-寫
S_IXUSR 用戶-執行
S_IRGRP 組-讀
S_IWGRP 組-寫
S_IXGRP 組-執行
S_IROTH 其餘-讀
S_IWOTH 其餘-寫
S_IXOTH 其餘-執行
注意:對於目錄的讀權限和執行權限的意義是不一樣的。讀權限容許咱們讀目錄,得到在該目錄中全部文件名的列表。當一個目錄是咱們要訪問文件的路徑名的一個組成部分時,對該 目錄的執行權限使咱們能夠經過該目錄
訪問位規則:
-咱們用名字打開任意類型的文件時,對該名字中包含的每個目錄,包括它可能隱含的當前工做目錄都應具備執行權限
-對於一個文件的讀權限決定了咱們是否可以打開該文件進行讀操做
-對於一個文件的寫權限決定了咱們是夠可以打開該文件進行寫操做
-爲了在open函數中對一個文件指定O_TRUNC標誌,必須對該文件具備寫權限
-爲了在一個目錄中建立一個新文件,必須對該目錄具備寫權限和執行權限
-爲了刪除一個現有文件,必須對包含該文件的目錄具備寫權限和執行權限。對該文件自己則不須要有讀寫權限
-若是6個exec函數中的任何一個執行某個文件,都必須對該文件具備執行權限。該文件還必須是一個普通文件
進程每次打開、建立或刪除一個文件時,內核就進行文件訪問權限測試:
(1)若進程的有效用戶ID是0(超級用戶),則容許訪問。這給予了超級用戶對整個文件系統進行處理的最充分的自由
(2)若進程的有效用戶ID等於文件的全部者ID(也就是該進程擁有此文件),那麼:若全部者適當的訪問權限位被設置,則容許訪問,否者拒絕訪問。(適當的訪問權限位指的是 若進程爲讀而打開該文件,則用戶讀位應爲1;若進程爲寫而打開該文件,則用戶寫位應爲1;若進程將執行該文件,則用戶執行位應爲1)
(3)若進程的有效組ID或進程的附加組ID之一等於文件的組ID,那麼:若組適當的訪問權限位被設置,則容許訪問,否者拒絕訪問。
(4)若其餘用戶適當的訪問權限位被設置,則容許訪問,否者拒絕訪問
4.6 新文件和目錄的全部權
新文件的用戶ID設置爲進程的有效用戶ID。關於組ID,POSIX.1容許實現選擇下列之一爲新文件的組ID
(1)新文件的組ID能夠是進程的有效組ID
(2)新文件的組ID能夠是它所在目錄的組ID
4.7 access函數
access函數是按實際用戶ID和實際組ID進行文件訪問權限測試的
#include<unistd.h> int access(const char *pathname ,int mode); 返回值:若成功則返回0,若出錯則返回-1
access函數的mode常量:
R_OK 測試讀權限
W_OK 測試寫權限
X_OK 測試執行權限
F_OK 測試文件是否存在
實例:4_2 access函數實例
1 #include"apue.h" 2 #include<fcntl.h> 3 int main(int argc,char *argv[]) 4 { 5 if(argc!=2) 6 err_quit("usage: a.out <pathname>"); 7 if(access(argv[1],R_OK)<0) 8 err_ret("access error for %s",argv[1]); 9 else 10 printf("read access OK\n"); 11 if(open(argv[1],O_RDONLY)<0) 12 err_ret("open error for %s",argv[1]); 13 else 14 printf("open for reading OK\n"); 15 exit(0); 16 }
4.8 umask函數
umask函數爲進程設置文件模式建立屏蔽字,並返回之前的值
#include<sys/stat.h>
mode_t umask(mode_t cmask);
返回值:之前的文件模式建立屏蔽字
參數cmask是由st_mode中的9個常量(S_IRUSR,S_IWUSR等)中的若干個按位或構成的
在進程建立一個新文件或新目錄時,就必定會使用文件模式建立屏蔽字
實例:4_3 umask函數實例
#include"apue.h" #include<fcntl.h> #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) int main() { umask(0); if(creat("foo",RWRWRW)<0) err_sys("creat error for foo"); umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if(creat("bar",RWRWRW)<0) err_sys("creat error for bar"); exit(0); }
此程序建立了兩個文件,建立第一個時,umask值爲0,建立第二個時,umask值禁止全部組和其餘用戶的訪問權限。
UNIX系統的大多數用戶從不處理他們的umask值,但當咱們的進程運行時,有效的umask值可能關閉該權限位,這時應該在進程運行時修改umask值
4.9 chmod 和fchmod 函數
這兩個函數使咱們能夠更改現有文件的訪問權限
#include<sys/stat.h> int chmod(const char *pathname,mode_t mode); //在指定的文件上進行操做 int fchmod(int filedes,mode_t mode); //對已打開的文件進行操做 兩個函數返回值:若成功則放回0,若出錯則返回-1
參數mode常量有9個與st_mode同樣,不過多了6項,它們是兩個設置ID常量(S_ISUID,S_ISGID),保存正文常量(S_ISVTX),以及三個組合常量(S_IRWXU,S_IRWXG,S_IRWXO)。
附:保存-正文位(S_ISVTX)不是POSIX.1的一部分。在SUS中,它被定義在XSI擴展中
實例:4_4 chmod 函數實例
1 #include"apue.h" 2 int main() 3 { 4 struct stat statbuf; 5 6 /*turn on set-group-ID and turn off group-execute*/ 7 8 if(stat("foo",&statbuf)<0) 9 err_sys("stat error for foo"); 10 if(chmod("foo",(statbuf.st_mode&~S_IXGRP)|S_ISGID)<0) 11 err_sys("chmod error for foo"); 12 13 /*set absolute mode to "rw-r--r--"*/ 14 if(chmod("bar",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0) 15 err_sys("chmod error for bar"); 16 exit(0); 17 }
(在運行程序4_4中的程序後,ls命令列出的時間和日期並無改變。在4.18節中,咱們會了解到chmod函數更新的只是i節點最近一次被更改的時間。按系統默認方式,ls -l 列出的是最後修改文件內容的時間)
4.10 粘住位(S_ISVTX)
若是對一個目錄設置了粘住位,則只有對該目錄具備寫權限的用戶在知足下列條件之一的狀況下,才能刪除或改名該目錄下的文件:
-擁有此文件
-擁有此目錄
-是超級用戶
目錄/tmp和/var/spool/uucppublic是設置粘住位的典型候選者------任何用戶均可在這兩個目錄中建立文件。任意用戶(用戶、組和其餘)對這兩個目錄的權限一般都是讀、寫和執行。可是用戶不該能刪除或改名屬於其餘人的文件,爲此在這兩個目錄的文件模式中都設置了粘住位
4.11 chown,fchown和lchown函數
下面幾個chown函數可用於更改文件的用戶ID和組ID。
#include<unistd.h> int chown(const char *pathname ,uid_t owner,gid_t group); int fchown(int filedes,uid_t owner,gid_t group); int lchown(const char *pathname,uid_t owner,gid_t group); 三個函數的返回值:若成功則返回0,若出錯則返回-1
出了所引用的文件是符號連接外,這三個函數的操做類似。在符號連接的狀況下,lchown更改符號連接自己的全部者,而不是該符號連接所指向的文件
_POSIX_CHOWN_RESTRICTED指明使用chown是不是受限的
若_POSIX_CHOWN_RESTRICTED對指定的文件起做用,則
(1)只有超級用戶進程能更改該文件的用戶ID
(2)若知足下列條件,一個非超級用戶進程就能夠更改該文件的組ID:
a.進程擁有此文件(其有效用戶ID等於該文件的用戶ID)
b.參數owner等於-1或文件的用戶ID,而且參數group等於進程的有效ID或進程的附加組ID之一
這意味着,當_POSIX_CHOWEN_RESTRICTED起做用時,不能更改其餘用戶的用戶ID。你能夠更改你所擁有的文件的組ID,但只能改到你所屬的組
4.12 文件長度
stat結構成員st_size表示以字節爲單位的文件長度。此字段只對普通文件,目錄文件和符號連接有意義
-對於普通文件,其文件長度能夠是0,在讀這種文件時,將獲得文件結束(eof)指示
-對於目錄,文件長度是一個數(例如16或512)的倍數
-對於符號連接,文件的長度是文件名中的實際字節數
文件中的空洞
當存在空洞時,磁盤總量小於文件長度,對於沒有寫過的字節位置,read函數讀到的字節是0。
若是使用實用程序(例如cat)複製這種文件,那麼全部這些空洞都會被填滿,其中全部實際數據字節皆填寫爲0
$cat xxx > xxx.copy
新文件的實際字節數與文件長度接近,但並不相同。其緣由是文件系統使用了若干以存放指向實際數據塊的各個指針。
4.13 文件截短
在文件尾端處截取一些數據以縮短文件(將一個文件清空爲0是一個特例,在打開文件時使用O_TRUNC標誌能夠作到這一點
#include<unistd.h> int truncate(const char *pathname, off_t length); int ftruncate(int filedes, off_t length); 兩個函數的返回值:若成功則返回0,若出錯則返回-1
這兩個函數把現有的文件長度截短爲length字節。若是該文件之前的長度大於length,則超過部分不能再訪問。若是之前的長度短於length,則其效果與系統有關(遵循XSI的系統將增長該文件的長度。若UNIX系統實現擴展了該文件,則在之前的文件尾端和新的文件尾端之間的數據將讀做0(也就是可能在文件中建立了一個空洞))
4.14 文件系統
http://www.cnblogs.com/qianye/archive/2012/11/24/2786345.html
4.15 link,unlink,remove和rename函數
任何一個文件能夠有多個目錄項指向其i節點。建立一個指向現有文件的連接的方法是使用link函數
#include<unistd.h> int link(const char *existingpath, const char *newpath); 返回值:若成功則返回0,若出錯則返回-1
此函數建立一個新目錄項newpath,它引用現有的文件existingpath。如若newpath存在,則返回出錯。
爲了刪除一個現有的目錄項,能夠調用unlink函數
#include<unistd.h> int unlink(const char *pathname); 返回值:若成功則返回0,若出錯則返回-1
此函數刪除目錄項,並將由pathname所引用文件的連接計數減1。若是還有指向該文件的其餘連接,則仍可經過其餘連接訪問該文件的數據。若是出錯,則不對該文件作任何修改
(只有當連接計數達到0時,該文件內容纔可被刪除。另外一個條件也會阻止刪除文件的內容------只要有進程打開了該文件,其內容也不能刪除。關閉一個文件時,內核首先檢查打開該文件的進程數。若是該數達到0,而後內核檢查其連接數,若是這個數也是0,那麼就刪除該文件的內容)
實例:4_5 打開一個文件,而後unlink它
1 #include"apue.h" 2 #include<fcntl.h> 3 4 int main() 5 { 6 if(open("tempfile",O_RDWR)<0) 7 err_sys("open error"); 8 if(unlink("tempfile")<0) 9 err_sys("unlink error"); 10 printf("file unlinked\n"); 11 sleep(15); 12 printf("done\n"); 13 exit(0); 14 }
進程用open建立一個文件,而後當即調用unlink。由於該文件仍舊是打開的,因此不會將其內容刪除。只有當進程關閉該文件或終止時,該文件的內容纔會被刪除。
若是pathname是符號連接,那麼unlink刪除該符號連接,而不會刪除由該連接所引用的文件,給出符號連接名的狀況下,沒有一個函數能刪除由該連接所引用的文件
remove函數能夠解除對一個文件或目錄的連接。(對於文件,remove的功能與unlink相同。對於目錄,remove的功能與rmdir相同)
#include<stdio.h> int remove(const char *pathname); 返回值:若成功則返回0,若出錯則返回-1
文件或目錄用rename函數改名
#include<stdio.h> int rename(const char *oldname, const char *newname); 返回值:若成功則返回0,若出錯則返回-1
根據oldname是文件仍是目錄,分爲如下4種狀況:(還沒弄明白)
1,若是oldname指的是一個文件而不是目錄,那麼爲該文件或符號連接改名。在這種狀況下,若是newname已存在,則它不能引用一個目錄。若是newname已存在,並且不是一個目錄,則先將該目錄刪除而後將oldname改名爲newname。對於包含oldname的目錄以及包含newname的目錄,調用進程必須具備寫權限,由於將更改這兩個目錄;
2,若oldname指的是一個目錄,那麼爲該目錄改名。若是newname已存在,則它必須引用一個目錄,並且該目錄應當是空目錄(空目錄指的是該目錄中只有.和..項)。若是newname已存在並且是一個空目錄,則現將其刪除,而後將oldname改名爲newname。另外,當爲一個目錄改名時,newname不能包含oldname做爲其路徑前綴,由於舊名字是新名字的前綴而不能將其刪除;
3,若oldname或newname引用符號連接,則處理的是符號連接自己,而不是它所引用的文件;
4,做爲特例,若oldname和newname引用同一文件,則函數不作任何更改而成功返回。
4.16 符號連接
符號連接是指向一個文件的指針。引入符號連接的緣由是爲了避開硬連接的一些限制:
-硬連接一般要求連接和文件位於同一文件系統中
-只有超級用戶才能建立指向目錄的硬連接
對符號連接以及它指向何種對象並沒有任何文件系統限制,任何用戶均可建立指向目錄的符號連接。符號連接通常用於將一個文件或整個目錄結構移到系統中的另外一個位置
當使用以名字引用文件的函數時,應當瞭解該函數是否處理符號連接。也就是該函數是否跟隨符號連接到達它所連接的文件。如若該函數具備處理符號連接的功能,則其路徑名參數引用由符號連接指向的文件。不然,路徑名參數將引用連接自己,而不是該連接指向的文件。(mkdir,mkinfo,mkmod和rmdir這些函數,當路徑名是符號連接時,都返回出錯)
附各個函數對符號連接的處理:http://blog.chinaunix.net/uid-15084954-id-190319.html
注:同時用O_CREAT和O_EXCL二者調用open函數。在此狀況下,若路徑名引用符號連接,open將返回出錯,並將errno設置爲EEXIST。這種處理方式的意圖是堵塞一個安全性漏洞,使具備特權的進程不會被誘騙對不適當的文件進行寫操做
實例:使用符號連接可能在文件系統中引入循環
mkdir foo touch foo/a ln -s ../foo foo/testdir ls -l foo
用符號連接指定文件時,即便該文件不存在,也能創建連接,但若用open函數指定此連接,則返回出錯,由於不存在該文件。
4.17 symlink 和 readlink函數
symlink函數建立一個符號連接
#include<unistd.h> int symlink(const char *actualpath, const char *sympath); 返回值:若成功則返回0,若出錯則返回-1
該函數建立了一個指向actualpath的新目錄項sympath,在建立此符號連接時,並不要求actualpath已經存在。而且actualpath和sympath並不須要位於同一文件系統
由於open函數跟隨符號連接,因此須要有一種方法打開連接自己,並讀該連接中的名字。readlink函數提供了這種功能
#include<unistd.h> ssize_t readlink(const char* restrict pathname, char *restrict buf,size_t bufsize); 返回值:若成功則返回讀到的字節數,若出錯則返回-1
此函數組合了open,read和close的全部操做。若是此函數成功執行,則它返回讀入buf的字節數。在buf中返回的符號連接的內容不以null字符終止
4.18 文件的時間
st_atime,st_mtime,st_ctime
修改時間是文件內容最後一次被修改的時間。更改狀態時間是該文件的i節點最後一次被修改的時間。
影響i節點的操做:更改文件的訪問權限,用戶ID,連接數等,但它們並無更改文件的實際內容(系統並不保存對一個i節點的最後一次訪問時間,因此access和stat函數並不更改這 三個時間中的任何一個)
ls命令按這三個時間值中的一個排序進行顯示。按系統默認(用-l或-t選項時),它按文件的修改時間的前後排序顯示,-u選項使其用訪問時間排序,-c選項則使其用更改狀態時間排序
4.19 utime函數
一個文件的訪問和修改時間能夠用utime函數更改
#include<utime.h> int utime(const char *pathname, const struct utimbuf *times); 返回值:若成功則返回0,若出錯則返回-1
此函數所用的數據結構是
struct utimbuf{ time_t actime; time_t modtime; };
此結構中的兩個時間值是日曆時間。這是自1970年1月1日00:00:00以來國際標準時間所通過的秒數
此函數的操做以及執行它所要求的特權取決於times參數是不是null:
-若是times是一個空指針,則訪問時間和修改時間二者都設置爲當前時間。爲了執行此操做必須知足下列條件之一:進程的有效用戶ID必須等於該文件的全部者ID;或者進程對該文件必須具備寫權限
-若是times是非空指針,則訪問時間和修改時間被設置爲times所指向結構中的值。此時,進程的有效用戶ID必須等於該文件的全部者ID,或者進程必須是一個超級用戶進程。對文件只具備寫權限是不夠的
程序清單:4_6 utime函數實例
1 #include"apue.h" 2 #include<fcntl.h> 3 #include<utime.h> 4 5 int main(int argc,char *argv[]) 6 { 7 int i,fd; 8 struct stat statbuf; 9 struct utimbuf timebuf; 10 for(i=1;i<argc;i++){ 11 if(stat(argv[i],&statbuf)<0){ 12 err_ret("%s:stat error",argv[i]); 13 continue; 14 } 15 if((fd=open(argv[i],O_RDWR|O_TRUNC))<0){ 16 err_ret("%s:open error",argv[i]); 17 continue; 18 } 19 close(fd); 20 timebuf.actime=statbuf.st_atime; 21 timebuf.modtime=statbuf.st_mtime; 22 if(utime(argv[i],&timebuf)<0){ 23 err_ret("%s:utime error",argv[i]); 24 continue; 25 } 26 } 27 exit(0); 28 }
運行後,最後修改時間和最後訪問時間未變。可是,更改狀態時間則更改成程序運行時的時間
4.20 mkdir 和 rmdir函數
用mkdir函數建立目錄,用rmdir函數刪除目錄
#include<sys/stat.h> int mkdir(const char *pathname, mode_t mode); 返回值:若成功則返回0,若出錯則返回-1
用rmdir函數能夠刪除一個空目錄。空目錄是隻包含.和..這兩項的目錄
#include<unistd.h> int rmdir(const char *pathname); 返回值:若成功則返回0,若出錯則返回-1
若是調用此函數使目錄的連接計數成爲0,而且也沒有其餘進程打開此目錄,則釋放由此目錄佔用的空間。若是在連接計數達到0時,有一個或幾個進程打開了此目錄,則在此函數返回前刪除最後一個連接及.和..項。另外,在此目錄中不能在建立新文件。可是在最後一個進程關閉它前並不釋放此目錄。(即便另外一個進程打開該目錄,他們在此目錄下也不能執行其餘操做。這樣處理的緣由是,爲了使rmdir函數成功執行,該目錄必須是空的)
4.21 讀目錄
對某個目錄具備訪問權限的任一用戶均可讀該目錄,可是,爲了防止文件系統產生混亂,只有內核才能寫目錄。一個目錄的寫權限位和執行權限位決定了在該目錄中可否建立新文件以及刪除文件,他們並不表示可否寫目錄(不太明白)
#include<dirent.h> DIR *opendir(const char *pathname); //返回值:若成功則返回指針,出錯則返回null struct dirent *readdir(DIR *dp); //返回值:若成功則返回指針,若在目錄結尾或出錯則返回null void rewinddir(DIR *dp); int closedir(DIR *dp); //返回值:若成功則返回0,若出錯則返回-1 long telldir(DIR *dp); //返回值:與dp關聯的目錄中的當前位置 void seekdir(DIR *dp, long loc);
struct dirent{ ino_t d_ino; //i-node number char d_name[NAME_MAX+1]; //null-terminated filename }
簡單的文件遍歷程序
實例:4_7 遞歸降序遍歷目錄層次結構,並按文件類型計數
1 #include"apue.h" 2 #include<dirent.h> 3 #include<limits.h> 4 5 /*function type that is called for each filename*/ 6 typedef int Myfunc(const char *,const struct stat *,int); 7 static Myfunc myfunc; 8 static int myftw(char *,Myfunc *); 9 static int dopath(Myfunc *); 10 11 static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot; 12 13 int main(int argc,char *argv[]) 14 { 15 int ret; 16 if(argc!=2) 17 err_quit("usage: ftw <starting-pathname>"); 18 ret = myftw(argv[1],myfunc); //does it all 19 ntot=nreg+ndir+nblk+nchr+nfifo+nslink+nsock; 20 21 if(ntot==0) 22 ntot=1; //avoid divide by 0;print 0 for all counts 23 printf("regular files =%7ld,%5.2f %%\n",nreg,nreg*100.0/ntot); 24 printf("directories = %7ld,%5.2f %%\n",ndir,ndir*100.0/ntot); 25 printf("block special =%7ld,%5.2f %%\n",nblk,nblk*100.0/ntot); 26 printf("char special =%7ld,%5.2f %%\n",nchr,nchr*100.0/ntot); 27 printf("FIFOs =%7ld,%5.2f %%\n",nfifo,nfifo*100.0/ntot); 28 printf("symbloic links=%7ld,%5.2f %%\n",nslink,nslink*100.0/ntot); 29 printf("sockets =%7ld,%5.2f %%\n",nsock,nsock*100.0/ntot); 30 exit(ret); 31 } 32 /* 33 *Descend through the hierarchy,starting at "pathname". 34 *The caller's func() is called for every file 35 */ 36 #define FTW_F 1 //file other than directory 37 #define FTW_D 2 //directory 38 #define FTW_DNR 3 //directory that can't be read 39 #define FTW_NS 4 //file that we can't stat 40 41 static char *fullpath; //contains full pathname for every file 42 43 static int myftw(char *pathname,Myfunc *func) 44 { 45 int len; 46 fullpath=path_alloc(&len); //malloc's for PATH_MAX+1 bytes 47 strncpy(fullpath,pathname,len); //protect against 48 fullpath[len-1]=0; //buffer overrun 49 return(dopath(func)); 50 } 51 /* 52 *Descend through the hierarchy,starting at "fullpath" 53 *If "fullpath" is anything other than a directory,we lstat() it, 54 *call func(),and return . For a directory,we call ourself 55 *recursively for each name in the directory 56 */ 57 static int dopath(Myfunc* func) //we return whatever func() returns 58 { 59 struct stat statbuf; 60 struct dirent *dirp; 61 DIR *dp; 62 int ret; 63 char *ptr; 64 if(lstat(fullpath,&statbuf)<0) //stat error 65 return(func(fullpath,&statbuf,FTW_NS)); 66 if(S_ISDIR(statbuf.st_mode)==0) //not a directory 67 return(func(fullpath,&statbuf,FTW_F)); 68 /* 69 *It's a directory.First call func() for the directory 70 *then process each filename in the directory. 71 */ 72 if((ret=func(fullpath,&statbuf,FTW_D))!=0) 73 return(ret); 74 ptr=fullpath+strlen(fullpath);//point to end of fullpath 75 *ptr++='/'; 76 *ptr=0; 77 if((dp=opendir(fullpath))==NULL)//can't read directory 78 return(func(fullpath,&statbuf,FTW_DNR)); 79 while((dirp=readdir(dp))!=NULL){ 80 if(strcmp(dirp->d_name,".")==0||strcmp(dirp->d_name,"..")==0) 81 continue;//ignore dot and dot-dot 82 strcpy(ptr,dirp->d_name);//append name after slash 83 if((ret=dopath(func))!=0)//recursive 84 break; 85 } 86 ptr[-1]=0;//erase everthing for alash onwards 87 if(closedir(dp)<0) 88 err_ret("can't close directory %s",fullpath); 89 return(ret); 90 } 91 char* path_alloc(int*size) 92 { 93 char *p=NULL; 94 if(!size) 95 return NULL; 96 p=malloc(256); 97 if(p) 98 *size=256; 99 else 100 *size=0; 101 return p; 102 } 103 static int myfunc(const char *pathname,const struct stat *statptr,int type) 104 { 105 switch(type){ 106 case FTW_F: 107 switch(statptr->st_mode &S_IFMT){ 108 case S_IFREG: nreg++; break; 109 case S_IFBLK: nblk++; break; 110 case S_IFCHR: nchr++; break; 111 case S_IFIFO: nfifo++;break; 112 case S_IFLNK: nslink++;break; 113 case S_IFSOCK: nsock++;break; 114 case S_IFDIR: 115 err_dump("for S_IFDIR for %s",pathname); 116 //directories should have type = FTW_D 117 } 118 break; 119 case FTW_D: 120 ndir++; 121 break; 122 case FTW_DNR: 123 err_ret("can't read directory %s",pathname); 124 break; 125 case FTW_NS: 126 err_ret("stat error for %s",pathname); 127 break; 128 default: 129 err_dump("unknown type %d for pathname %s",type,pathname); 130 } 131 return(0); 132 }
按書上給的函數編譯後會告知path_alloc()未定義,解決辦法:http://www.cnblogs.com/xiaoliyu/archive/2009/03/08/1406448.html
4.22 chdir,fchdir和getcwd函數
進程經過調用chdir或fchdir函數能夠更改當前工做目錄
#include<unistd.h> int chdir(const char *pathname); //$ cd int fchdir(int fieldes); 兩個函數的返回值:若成功則返回0,若出錯則返回-1
在這兩個函數中,分別用pathname或打開文件描述符
實例:4_8 chdir函數實例
1 #include"apue.h" 2 int main() 3 { 4 if(chdir("/tmp")<0) 5 err_sys("chdir failed"); 6 printf("chdir to /tmp succeeded\n"); 7 exit(0); 8 }
執行完這段程序後能夠看出,當前工做目錄沒有改變,其緣由是shell建立了一個子進程,由該子進程具體執行mycd程序。因而可知,爲了改變shell進程本身的工做目錄,shell應當直接調用chdir函數,爲此cd命令的執行程序直接包含在shell程序中
getcwd獲取當前目錄路徑名
#include<unistd.h> char *getcwd(char *buf,size_t size); 返回值:若成功則返回buf,若出錯則返回NULL
4.23 設備特殊文件
st_dev和st_rdev這兩個字段常常引發混淆:
-每一個文件系統所在的存儲設備都由其主、次設備號表示。設備號所用的數據類型是基本系統數據類型dev_t。主設備號標識設備驅動程序,有時編碼爲其通訊的外設板;次設備號標識特定的子設備
-咱們一般可使用兩個宏即major和minor來訪問主、次設備號,大多數實現都定義了這兩個宏。這就意味着咱們無需關心這兩個數是如何存放在dev_t對象中的。
-系統中與每一個文件名關聯的st_dev值是文件系統的設備號,該文件系統包含了這一文件以及與其對應的i節點
-只有字符特殊文件和塊特殊文件纔有st_rdev值,此值包含實際設備的設備號
實例:4_10 打印st_dev和st_rdev值
1 #define _BSD_SOURCE 2 #include"apue.h" 3 //#ifdev SOLARIS 4 //#include<mkdev.h> 5 //#endif 6 7 int main(int argc,char *argv[]) 8 { 9 int i; 10 struct stat buf; 11 for(i=1;i<argc;i++){ 12 printf("%s: ",argv[i]); 13 if(stat(argv[i],&buf)<0){ 14 err_ret("stat error"); 15 continue; 16 } 17 printf("dev = %d/%d",major(buf.st_dev),minor(buf.st_dev)); 18 if(S_ISCHR(buf.st_mode)||S_ISBLK(buf.st_mode)){ 19 printf(" (%s) rdev=%d/%d",(S_ISCHR(buf.st_mode))?"character":"block", 20 major(buf.st_rdev),minor(buf.st_rdev)); 21 } 22 printf("\n"); 23 } 24 exit(0); 25 }
此程序按書上編譯時找不到<sys/mkdev.h>,後來直接在第一行加上一句就行
4.24 文件訪問權限位小結
常量 | 說明 | 對普通文件的影響 | 對目錄的影響 |
S_ISUID S_ISGID |
設置用戶ID 設置組ID |
執行時設置有效用戶ID 若組執行位設置,則執行時設置有效組ID,不然使強制性記錄鎖起做用(若支持) |
(不使用) 將在目錄中建立的新文件的組ID設置爲目錄的組ID |
S_ISVTX | 粘住位 | 在交換區保存程序正文(若支持) | 限制在目錄中刪除和改名文件 |
S_IRUSR S_IWUSR S_IXUSR |
用戶讀 用戶寫 用戶執行 |
許可用戶讀文件 許可用戶寫文件 許可用戶執行文件 |
許可用戶讀目錄項 許可用戶在目錄中刪除和建立文件 許可用戶在目錄中搜索給定路徑名 |
S_IRGRP S_IWGRP S_IXGRP |
組讀 組寫 組執行 |
許可組讀文件 許可組寫文件 許可組執行文件 |
許可組讀目錄項 許可組在目錄中刪除和建立文件 許可組在目錄中搜索給定路徑名 |
S_IROTH S_IWOTH S_IXOTH |
其餘讀 其餘寫 其餘執行 |
許可其餘讀文件 許可其餘寫文件 許可其餘執行文件 |
許可其餘讀目錄項 許可其餘在目錄中刪除和建立文件 許可其餘在目錄中搜索給定路徑名 |
最後9個常量分紅3組,由於:
S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR
S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH