一、link、linkat、unlink、unlinkat、remove函數
創建一個指向現有文件的鏈接的方法是使用link函數或linkat函數。函數的原型為:
#include <unistd.h> int link(const char *existingpath,const char *newpath); int linkat(int efd,const char *existingpath,int nfd,const char *newpath,int flag);
這兩個函數創建一個新目錄項newpath,它引用現有文件existingpath。如果newpath已經存在,則返回錯誤。
只創建newpath中的最后一個分量,路徑中的其它部分應當已經存在。
對於linkat函數,現有文件是通過efd和existingpath參數指定的。默認情況下,如果兩個路徑名中的任意一
個是相對路徑,那么它需要通過相對於對應的文件描述符進行計算。如果兩個文件描述符中的任一個設置為AT_F
DCWD,那么相應的路徑名(如果它是相對路徑)就通過相對於當前目錄進行計算。如果任一路徑名是絕對路徑,
相應的文件描述符參數就會被忽略。當現有文件是符號鏈接時,由flag參數來控制linkat函數是創建指向現有符
號鏈接的鏈接還是創建指向現有符號鏈接所指向的文件的鏈接。如果在flag參數中設置了AT_SYMLINK_FOLLOW標志
,就是創建指向符號鏈接目標的鏈接。如果這個標志被清除了,則創建一個指向符號鏈接本身的鏈接。
為了刪除一個現有的目錄項,可以調用unlink函數。函數的原型為:
#include <unistd.h> int unlink(const char *pathname); int unlinkat(int fd,const char *pathname,int flag);
這兩個函數刪除目錄項,並將由pathname所引用文件的鏈接計數減1。如果對該文件還有其它鏈接,則仍可以
通過其它鏈接訪問該文件的數據。如果出錯,則不對該文件做任何更改。
如果pathname參數是相對路徑名,那么unlinkat函數計算相對於由fd文件描述符參數代表的目錄的路徑名。如
果fd參數設置為AT_FDCWD,那么通過相對於調用進程的當前工作目錄來計算路徑名。如果pathname參數是絕對路
徑名,那么fd參數被忽略。flag參數給出了一種方法使調用進程可以改變unlinkat函數的默認行為。當AT_REMOVE
DIR標志被設置時,unlinkat函數可以類似於rmdir一樣刪除目錄。如果這個標志被清除,unlinkat和unlink執行
同樣的操作。
也可以用remove函數解除對一個文件或目錄的鏈接。對於文件,remove的功能與unlink相同。對於目錄,remo
ve的功能與rmdir相同。函數的原型為:
#include <stdio.h> int remove(const char *pathname);
二、rename、renameat函數
文件或目錄可以用rename函數或者renameat函數進行重命名。函數的原型為:
#include <stdio.h> int rename(const char *oldname,const char *newname); int renameat(int oldfd,const char *oldname,int newfd,const char *newname);
(1)如果oldname指的是一個文件而不是目錄,那么為該文件或符號鏈接重命名。在這種情況下,如果newname已
存在,則它不能引用一個目錄。如果newname已存在,而且不是一個目錄,則現將該目錄項刪除然后將oldname重命名
為newname。對包含oldname的目錄以及包含newname的目錄,調用進程必須具有寫權限,因為將更改這兩個目錄。
(2)如果oldname指的是一個目錄,那么為該目錄重命名。如果newname已存在,則它必須引用一個目錄,而且該
目錄應當是空目錄(空目錄指的是該目錄中只有.和..項)。如果newname存在(而且是一個空目錄),則先將其刪除
,然后將oldname重命名為newname。另外,當為一個目錄重命名時,newname不能包含oldname作為其路徑前綴。
(3)如果oldname或newname引用符號鏈接,則處理的是符號鏈接本身,而不是它所引用的文件。
(4)不能對.和..重命名。更確切的說,.和..都不能出現在oldname和newname的最后部分。
(5)如果oldname和newname引用同一文件,則函數不做任何更改而成功返回。如果newname已經存在,則調用進程
對它需要有寫權限。另外,調用進程將刪除oldname目錄項,並可能要創建newname目錄項,所以它需要對包含oldnam
e及包含newname的目錄具有寫和執行權限。
除了當oldname或newname指向相對路徑名時,其它情況下renameat函數與rename函數功能相同。如果oldname參數
指定了相對路徑,就相對於oldfd參數引用的目錄來計算oldname。如果newname指定了相對路徑,就相對於newfd引用
的目錄來計算newname。oldfd或newfd參數都能設置為AT_FDCWD,此時相對於當前目錄來計算相應的路徑名。
三、symlink、symlinkat函數
可以用symlink或symlinkat函數創建一個符號鏈接。函數的原型為:
#include <unistd.h> int symlink(const char *actualpath,const char *sympath); int symlinkat(const char *actualpath,int fd,const char *sympath);
函數創建了一個指向actualpath的新目錄項sympath。在創建此符號鏈接時,並不要求actualpath已經存在。並且
,actualpath和sympath並不需要位於同一文件系統中。
symlinkat函數與symlink函數類似,但sympath參數根據相對於打開文件描述符引用的目錄(由fd參數指定)進行
計算。如果sympath參數指定的是絕對路徑或者fd參數設置了AT_FDCWD值,那么symlinkat就等同於symlink函數。
open函數跟隨符號鏈接,所以需要有一種方法打開該鏈接本身,並讀該鏈接中的名字。readlink和readlinkat函數
提供了這種功能。函數的原型為:
#include <unistd.h> ssize_t readlink(const char *restrict pathname,char *restrict buf,size_t bufsize); ssize_t readlinkat(int fd,const char *restrict pathname,char *restrict buf,size_t bufsize);
兩個函數組合了open、read和close的所有操作。如果函數執行成功,則返回讀入buf的字節數。在buf中返回的符號
鏈接的內容不以null字節終止。當pathname參數指定的是絕對路徑名或者fd參數的值為AT_FDCWD,readlinkat函數的行
為與readlink相同。但是,如果fd參數是一個打開目錄的有效文件描述符並且pathname參數是相對路徑名,則readlink
at計算相對於由fd代表的打開目錄的路徑名。
四、futimens、utimensat、utimes函數
一個文件的訪問和修改時間可以用以下幾個函數更改。futimens和utimensat函數可以指定納秒級精度的時間戳。用
到的數據結構是與stat函數族相同的timespec結構。函數的原型為:
#include <sys/stat.h> int futimens(int fd,const struct timespec times[2]); int utimensat(int fd,const char *path,const struct timespec times[2],int flag);
這兩個函數的times數組參數的第一個元素包含訪問時間,第二個元素包含修改時間。時間戳可以按以下4種方式之
一進行指定:
(1)如果times參數是一個空指針,則訪問時間和修改時間兩者都設置為當前時間。
(2)如果times參數指定兩個timespec結構的數組,任一數組元素的tv_nsec字段的值為UTIME_NOW,相應的時間戳就
設置為當前時間,忽略相應的tv_sec字段。
(3)如果times參數指向兩個timespec結構的數組,任一數組元素的tv_nsec字段的值為UTIME_OMIT,相應的時間戳
保持不變,忽略相應的tv_sec字段。
(4)如果times參數指向兩個timespec結構的數組,且tv_nsec字段的值為既不是UTIME_NOW也不是UTIME_OMIT,在這
種情況下,相應的時間戳設置為相應的tv_sec和tv_nsec字段的值。
執行這些函數所要求的優先權取決於times參數的值:
- 如果times是一個空指針,或者任一tv_nsec字段設置為UTIME_NOW,則進程的有效用戶ID必須等於該文件的所有者
ID;進程對該文件必須具有寫權限,或者進程是一個超級用戶進程。
- 如果times是非空指針,並且任一tv_nsec字段的值既不是UTIME_NOW也不是UTIME_OMIT,則進程的有效用戶ID必須
等於該文件的所有者ID,或者進程必須是一個超級用戶進程。對文件只具有寫權限是不夠的。
- 如果times是非空指針,並且兩個tv_nsec字段的值都為UTIME_OMIT,就不執行任何的權限檢查。
futimens函數需要打開文件來更改它的時間,utimensat函數提供了一種使用文件名更改文件時間的方法。pathname
參數是相對於fd參數進行計算的,fd要么是打開目錄的文件描述符,要么設置為特數值AT_FDCWD(強制通過相對於調用
進程的當前目錄計算pathname)。如果pathname指定了絕對路徑,那么fd參數被忽略。
utimensat的flag參數可用於進一步修改默認行為。如果設置了AT_SYMLINK_NOFOLLOW標志,則符號鏈接本身的時間
就會被修改(如果路徑名指向符號鏈接)。默認的行為是跟隨符號鏈接,並把文件的時間改成符號鏈接的時間。
#include <sys/time.h> int utimes(const char *pathname,const struct timeval times[2]);
utimes函數對路徑名進行操作。times參數是指向包含兩個時間戳(訪問時間和修改時間)元素的數組的指針,兩個
時間戳是用秒和微妙表示的。
五、mkdir、mkdirat、rmdir函數
用mkdir和mkdirat函數創建目錄,用rmdir函數刪除目錄。函數原型為:
#include <sys/stat.h> int mkdir(const char *pathname,mode_t mode); int mkdirat(int fd,const char *pathname,mode_t mode);
這兩個函數創建一個新的空目錄。其中,.和..目錄項是自動創建的。所指定的文件訪問權限mode由進程的文件模式
創建屏蔽字修改。
mkdirat函數與mkdir函數類似。當fd參數具有特殊值AT_FDCWD或者pathname參數指定了絕對路徑名時,mkdirat與mk
dir完全一樣。否則,fd參數是一個打開目錄,相對路徑名根據此打開目錄進行計算。
用rmdir函數可以刪除一個空目錄。空目錄只包含.和..這兩項的目錄。函數的原型為:
#include <unistd.h> int rmdir(const char *pathname);
如果調用此函數使目錄的鏈接計數成為0,並且也沒有其它進程打開此目錄,則釋放此前目錄占用的空間。如果在鏈
接計數達到0時,有一個或多個進程打開此目錄,則在此函數返回前刪除最后一個鏈接及.和..項。在此目錄中不能在創
建新文件。但是在最后一個進程關閉它之前並不釋放此目錄。
六、chdir、fchdir、getcwd函數
每個進程都有一個當前工作目錄,此目錄是搜索所有相對路徑名的起點。進程調用chdir或fchdir函數可以更改當前
工作目錄。函數的原型為:
#include <unistd.h> int chdir(const char *pathname); int fchdir(int fd);
在這兩個函數中,分別用pathname或打開文件描述符來指定新的當前工作目錄。
從當前工作目錄(.)開始,用..找到其上一級目錄,然后讀其目錄項,直到該目錄項中的i節點編號與工作目錄i節點
編號相同,這樣就找到了其對應的文件名。按照這種方法,逐層上移,直到遇到根,這樣就得到了當前工作目錄完整的
絕對路徑名。函數getcwd提供了這種功能,函數原型為:
#include <unistd.h> char* getcwd(char *buf,size_t size);
必須向此函數傳遞兩個參數,一個是緩沖區地址buf,另一個是緩沖區的長度size(以字節為單位)。該緩沖區必須
有足夠的長度以容納絕對路徑名再加上一個終止null字節,否則返回出錯。