除了對文件進行打開,讀寫等操做。文件系統還有其餘的特徵和性質等着咱們去研究哦。node
#include <sys/stat.h> int stat(const char *restrice pathname, struct stat *restrict buf); int fstat(int fieldes, struct stat *buf); int lstat(const char *restrice pathname, struct stat *restrict buf);
成功,返回0;不然,返回-1。shell
第一個參數,一旦給出pathname, stat函數就返回與此命名文件有關的信息結構。數據結構
第二個參數,buf是指針,指向一個結構。結構的基本形式以下:app
struct stat {socket
mode_t st_mode; /* file type & mode (permissions) */ide
ino_t st_ino; /* i-node number (serial number) */函數
dev_t st_dev; /* device number (file system) */測試
dev_t st_rdev; /* device number for special files */ui
nlink_t st_nlink; /* number of links */spa
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 t_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 */
};
程序:對每一個命令行參數打印文件類型
#include "apue.h" int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { err_ret("lstat error"); continue; } if (S_ISREG(buf.st_mode)) ptr = "regular"; else if (S_ISDIR(buf.st_mode)) ptr = "directory"; else if (S_ISCHR(buf.st_mode)) ptr = "character special"; else if (S_ISBLK(buf.st_mode)) ptr = "block special"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo"; else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; else ptr = "** unknown mode **"; printf("%s\n", ptr); } exit(0); }
9個訪問權限位,可看作三類,u表示用戶(全部者user),g表示組(group),o表示其餘(other)。每一個類中三個訪問權限(即讀(R),寫(W),執行(X))。
進程是根據有效用戶ID判斷當前用戶是否有無權限的。進程每次打開,建立,刪除一個文件時,內核就進行文件訪問權限測試。 超級用戶的有效ID是0。
若是進程擁有此文件,則按照用戶訪問權限批准或拒絕該進程對文件的訪問(不查看組訪問權限);若不擁有該文件,但進程屬於某個適當的組,則按照組訪問權限批准或拒絕該進程對文件的訪問。
#include <unistd.h> int access(const char* pathname, int mode);
成功,返回0;失敗,返回-1。
該函數按實際用戶ID和實際組ID進行訪問權限測試,也就是想驗證一個進程的實際用戶可否訪問一個給定的文件。其中mode是以下所列常量的按位或。
R_OK 測試讀權限
W_OK 測試寫權限
X_OK 測試執行權限
F_OK 測試文件是否存在
mode_t umask(mode_t cmask)
cmask是由9個訪問權限位(S_IRUSR, S_IWUSR,...)中的若干個按位「或」構成的。
該函數爲進程設置文件模式建立屏蔽字,也就是給一個文件設置權限,好比:禁止掉組和其餘用戶的讀寫權限等, 並返回之前的值。該函數無出錯返回。
#include "apue.h" #include <fcntl.h> #define RWRWRW (S_IRUSR) | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH) int main() { umask(0); //建立第一個文件,umask爲0 if(creat("foo", RWRWRW) < 0) err_sys("creat error for foo"); umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //建立第二個,umask值禁止全部組和其餘用戶的讀寫訪問權 if(creat("bar", RWRWRW) < 0) err_sys("creat error for bar"); exit(0); }
上面的程序建立兩個文件,建立第一個時,umask爲0,這是任何用戶均可以讀文件;建立第二個時,umask值禁止全部組和其餘用戶的讀寫訪問權限。
umask值是一個八進制數,每一位都表明一種要屏蔽的權限(u=rwx, g=rwx, o=rwx)。設置了相應位以後,它所對應的權限就會被拒絕。經常使用的是002(其餘用戶寫),022(同組成員和其餘用戶寫),027(同組成員寫和其餘用戶讀、寫或執行)。
Single UNIX Specification要求shell支持符號形式的umask命令。該格式與八進制格式恰好相反,即設置了的相應位表示非拒絕。
這兩個函數用於更改現有文件的訪問權限。
#include <sys/stat.h> int chmod(const char *pathname, mode_t mode); int fchmod(int fieldes, mode_t mode);
成功,返回0;失敗,返回-1。
chmod在指定的文件上操做,fchmod則是對已經打開的文件操做。
S_ISVTX(sticky bit) :保存文本(粘住位)。用於目錄,使多個用戶能夠共享一個目錄而不會彼此影響。只有超級用戶才能夠設置粘住位。如今不須要這種技術了。
硬連接:每一個i節點都有一個連接計數,其值是指向該i節點的目錄項數。只有當連接計數爲0時,才能夠刪除該文件,因此刪除文件用unlink, 不是delete。在stat結構中,鏈接計數包含在st_nlink中。這種連接叫硬連接。硬連接說白了是一個指針,指向文件i節點。
符號連接:是指向一個文件的間接指針。創建軟連接就是創建了一個新文件。 軟連接文件有點相似於Windows的快捷方式。它其實是特殊文件的一種。在符號鏈接中,文件其實是一個文本文件,其中包含的有另外一文件的位置信息,能夠是任意文件或目錄,能夠連接不一樣文件系統的文件。
引入符號連接是爲了避開硬連接的限制:
1.硬連接一般要求連接和文件位於同一個文件系統中。
2.只有超級用戶才能建立指向目錄的硬連接。由於這樣作可能在文件系統中造成循環。軟鏈接中也能造成循環,可是在軟連接中這種循環用unlink就很好消除。
如何造成循環的?以下:
$mkdir foo //建立目錄 $touch foo/a //建立0長文件 $ln -s ../foo foo/testdir //建立符號連接 $ls -l foo
這裏建立了一個目錄foo,它包含一個名字爲a的文件以及一個指向foo的符號連接。
下圖中,圓表示目錄,方表示文件。使用Solaris的標準函數ftw(3)以降序遍歷文件結構,則打印出的路徑名以下:
由於unlink不跟隨軟鏈接,因此能夠unlink文件foo/testdir,從而消除這個循環。
什麼叫不跟隨軟鏈接呢?
當使用以名字引用文件的函數時,咱們應當瞭解函數是否跟隨這個軟連接到達它所連接的文件,也能夠說是否處理符號連接,若是有這種功能,則其路徑名參數就引用這個軟鏈接指向的文件;不然就只是引用這個連接自己(就是個路徑名)。
#include <unistd.h> int link(const char *existingpath, const char *newpath); int unlink(const char *pathname)
link產生硬連接,即連接文件與目標文件是等價的,只是名字不一樣。目標文件必須存在。一般目標文件不容許是目錄,也不容許跨越文件系統。成功,返回0;失敗,返回-1.
#include <unistd.h> int symlink(const char *actualpath, const char *sympath); ssize_t readlink(const char* restrict pathname, char *restrict buf);
symlink產生軟連接,即連接文件是將目標文件名做爲字符序列存儲起來,所以對目標文件沒有任何限制。能夠用readlink讀取符號連接所存儲的目標文件名.成功,返回0;失敗,返回-1.
DIR *opendir(const char *pathname); // 返回值:NULL表示出錯,非空指針指向的對象爲打開的目錄數據結構(目錄流)。
struct dirent *readdir(DIR *dp); // 返回值: NULL表示讀到目錄尾部或出錯,非空指針指向的對象爲讀取的目錄項結構,目錄流的內部指針指向下一個目錄項。
一個dirent結構體至少包含下列兩個成員:
struct dirent { /*目錄項結構 */
ino_t d_ino; /* i-結點號 */
char d_name[NAME_MAX + 1]; /* null結尾的文件名 */
}
void rewinddir(DIR *dp); // 使readdir從目錄的開頭讀(即目錄流的內部指針指向第一個目錄項)。
int closedir(DIR *dp); // 返回值: 0 if OK, 1 on error.
DIR是一個內部結構,上述6個函數用這個內部結構保存當前正在被讀的目錄所在的有關信息。
opendir返回的指向DIR結構的指針由另外的5個函數使用。
只包含 .和.. 的目錄爲一個空目錄。
一個遞歸降序遍歷目錄層次結構,並按照文件類型計數的程序以下:
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 8 static Myfunc myfunc; 9 static int myftw(char *, Myfunc *); 10 static int dopath(Myfunc *); 11 12 static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot; 13 14 int 15 main(int argc, char *argv[]) 16 { 17 int ret; 18 19 if (argc != 2) 20 err_quit("usage: ftw <starting-pathname>"); 21 22 ret = myftw(argv[1], myfunc); /* does it all */ 23 24 ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock; 25 if (ntot == 0) 26 ntot = 1; /* avoid divide by 0; print 0 for all counts */ 27 printf("regular files = %7ld, %5.2f %%\n", nreg, 28 nreg*100.0/ntot); 29 printf("directories = %7ld, %5.2f %%\n", ndir, 30 ndir*100.0/ntot); 31 printf("block special = %7ld, %5.2f %%\n", nblk, 32 nblk*100.0/ntot); 33 printf("char special = %7ld, %5.2f %%\n", nchr, 34 nchr*100.0/ntot); 35 printf("FIFOs = %7ld, %5.2f %%\n", nfifo, 36 nfifo*100.0/ntot); 37 printf("symbolic links = %7ld, %5.2f %%\n", nslink, 38 nslink*100.0/ntot); 39 printf("sockets = %7ld, %5.2f %%\n", nsock, 40 nsock*100.0/ntot); 41 exit(ret); 42 } 43 44 /* 45 * Descend through the hierarchy, starting at "pathname". 46 * The caller's func() is called for every file. 47 */ 48 #define FTW_F 1 /* file other than directory */ 49 #define FTW_D 2 /* directory */ 50 #define FTW_DNR 3 /* directory that can't be read */ 51 #define FTW_NS 4 /* file that we can't stat */ 52 53 static char *fullpath; /* contains full pathname for every file */ 54 static size_t pathlen; 55 56 static int /* we return whatever func() returns */ 57 myftw(char *pathname, Myfunc *func) 58 { 59 fullpath = path_alloc(&pathlen); /* malloc PATH_MAX+1 bytes */ 60 /* ({Prog pathalloc}) */ 61 if (pathlen <= strlen(pathname)) { 62 pathlen = strlen(pathname) * 2; 63 if ((fullpath = realloc(fullpath, pathlen)) == NULL) 64 err_sys("realloc failed"); 65 } 66 strcpy(fullpath, pathname); 67 return(dopath(func)); 68 } 69 70 /* 71 * Descend through the hierarchy, starting at "fullpath". 72 * If "fullpath" is anything other than a directory, we lstat() it, 73 * call func(), and return. For a directory, we call ourself 74 * recursively for each name in the directory. 75 */ 76 static int /* we return whatever func() returns */ 77 dopath(Myfunc* func) 78 { 79 struct stat statbuf; 80 struct dirent *dirp; 81 DIR *dp; 82 int ret, n; 83 84 if (lstat(fullpath, &statbuf) < 0) /* stat error */ 85 return(func(fullpath, &statbuf, FTW_NS)); 86 if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */ 87 return(func(fullpath, &statbuf, FTW_F)); 88 89 /* 90 * It's a directory. First call func() for the directory, 91 * then process each filename in the directory. 92 */ 93 if ((ret = func(fullpath, &statbuf, FTW_D)) != 0) 94 return(ret); 95 96 n = strlen(fullpath); 97 if (n + NAME_MAX + 2 > pathlen) { /* expand path buffer */ 98 pathlen *= 2; 99 if ((fullpath = realloc(fullpath, pathlen)) == NULL) 100 err_sys("realloc failed"); 101 } 102 fullpath[n++] = '/'; 103 fullpath[n] = 0; 104 105 if ((dp = opendir(fullpath)) == NULL) /* can't read directory */ 106 return(func(fullpath, &statbuf, FTW_DNR)); 107 108 while ((dirp = readdir(dp)) != NULL) { 109 if (strcmp(dirp->d_name, ".") == 0 || 110 strcmp(dirp->d_name, "..") == 0) 111 continue; /* ignore dot and dot-dot */ 112 strcpy(&fullpath[n], dirp->d_name); /* append name after "/" */ 113 if ((ret = dopath(func)) != 0) /* recursive */ 114 break; /* time to leave */ 115 } 116 fullpath[n-1] = 0; /* erase everything from slash onward */ 117 118 if (closedir(dp) < 0) 119 err_ret("can't close directory %s", fullpath); 120 return(ret); 121 } 122 123 static int 124 myfunc(const char *pathname, const struct stat *statptr, int type) 125 { 126 switch (type) { 127 case FTW_F: 128 switch (statptr->st_mode & S_IFMT) { 129 case S_IFREG: nreg++; break; 130 case S_IFBLK: nblk++; break; 131 case S_IFCHR: nchr++; break; 132 case S_IFIFO: nfifo++; break; 133 case S_IFLNK: nslink++; break; 134 case S_IFSOCK: nsock++; break; 135 case S_IFDIR: /* directories should have type = FTW_D */ 136 err_dump("for S_IFDIR for %s", pathname); 137 } 138 break; 139 case FTW_D: 140 ndir++; 141 break; 142 case FTW_DNR: 143 err_ret("can't read directory %s", pathname); 144 break; 145 case FTW_NS: 146 err_ret("stat error for %s", pathname); 147 break; 148 default: 149 err_dump("unknown type %d for pathname %s", type, pathname); 150 } 151 return(0); 152 }
每一個進程都有一個當前工做目錄,此目錄是搜索全部相對路徑的起點(不以斜槓開始的路徑爲相對路徑)。
經過chdir和fchdir函數能夠更改當前工做路徑。
#include <unistd.h> int chdir(const char *pathname); int fchdir(int filedes);
成功,返回0;失敗,返回-1.
But,內核爲每一個進程只保存指向該目錄v節點的指針等目錄自己的信息,並不保存該目錄的完整路徑名。
Luckily,getcwd函數則提供了獲得當前工做目錄完整的絕對路徑名的功能。成功,返回buf; 失敗,返回NULL。
#include <unistd.h> char *getcwd(char *buf, size_t size);
第一個參數是緩衝地址buf, 第二是緩衝的長度size(單位:字節)。該緩衝必須有足夠長度以容納絕對路徑名再加上一個null終止字符,不然返回出錯。
1 #include "apue.h" 2 3 int 4 main(void) 5 { 6 char *ptr; 7 size_t size; 8 9 if (chdir("/usr/spool/uucppublic") < 0) 10 err_sys("chdir failed"); 11 12 ptr = path_alloc(&size); /* our own function */ 13 if (getcwd(ptr, size) == NULL) 14 err_sys("getcwd failed"); 15 16 printf("cwd = %s\n", ptr); 17 exit(0); 18 }
在更換目錄以前咱們能夠先調用getcwd函數得到路徑名,而後將這個路徑名做爲調用參數傳送給chdir.
fchdir函數更便捷。在更換到文件系統的不一樣位置以前,使用open函數打開當前工做目錄,而後保存文件描述符,當但願回到原工做目錄時,只要將這個文件描述符傳遞給fchdir便可。