#include <sys/stat.h>node
int stat(const char *restrict pathname, struct stat *restrict buf);跨域
int fstat(int fd, struct stat *buf);緩存
int lstat(const char *restrict pathname, struct stat *restrict buf);網絡
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);數據結構
returns: 成功返回0, 失敗返回-1socket
stat函數用於處理文件名, fstat用於處理文件描述符, 而lstat用於處理連接文件.函數
stat的數據結構以下:測試
struct stat { mode_t st_mode; // 文件類型和模式 ino_t st_ino; // i-node數量 dev_t st_dev; // 設備數量 dev_t st_rdev; // 特殊文件的設備數量 nlink_t st_nlink; // 連接個數 uid_t st_uid; // 用戶id gid_t st_gid; // 分組id off_t st_size; // 文件大小 struct timespec st_atim; // 最後access時間 struct timespec st_mtim; // 最後修改時間 struct timespec st_ctim; // 最後文件狀態改變時間 blksize_t st_blksize; // I/O塊大小 blkcnt_t st_blocks; // 所分配的塊大小 }
一個實際的例子:ui
#include <stdio.h> #include <sys/stat.h> int main( void ) { struct stat buf; if (0 == stat("./test.js", &buf)) { // 33188 printf("%d\n", buf.st_mode); // 47154894 printf("%llu\n", buf.st_ino); // 16777220 printf("%d\n", buf.st_dev); // 0 printf("%d\n", buf.st_rdev); // 1 printf("%d\n", buf.st_nlink); // 501 printf("%d\n", buf.st_uid); // 20 printf("%d\n", buf.st_gid); // 69 printf("%lld\n", buf.st_size); // 4096 printf("%d\n", buf.st_blksize); // 8 printf("%lld\n", buf.st_blocks); } return 0; }
1. 普通文件, 對系統內核來講不區分文本仍是二進制文件.spa
2. 目錄文件: 包含其它文件名而且一個指針指向這些文件的一個文件.
3. 塊特殊文件: 帶緩存的訪問文件,每次訪問以固定長度爲單位進行.
4. 字符特殊文件: 不帶緩存的訪問文件, 訪問長度可變.
備註: 全部設備不是塊特殊文件就是字符特殊文件.
5. FIFO: 用於進程間通訊的文件.
6. socket: 進程間的網絡通訊.
7. 符號連接: 一個文件(相似指針)指向另外一個文件.
咱們能夠經過stat結構的st_mode來判斷文件的類型, 使用以下宏定義:
S_ISREG(): 普通文件
S_ISDIR(): 目錄文件
S_ISCHR(): 字符特殊文件
S_ISBLK(): 塊特殊文件
S_ISFIFO(): FIFO文件
S_ISLNK(): 符號連接文件
S_ISSOCK(): socket
一個實際的例子:
#include <stdio.h> #include <sys/stat.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) { printf("lstat error\n"); 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); } return 0; }
終端輸出:
leicj@leicj:~/test$ ./a.out /etc/passwd /etc /dev/log /dev/tty /var/lib/oprofile/opd_pipe /dev/sr0 /dev/cdrom /etc/passwd: regular /etc: directory /dev/log: symbolic link /dev/tty: character special /var/lib/oprofile/opd_pipe: lstat error /dev/sr0: block special /dev/cdrom: symbolic link
針對S_ISDIR的定義, 查看源代碼可知:
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
而S_IFDIR經過變換成二進制便可理解上述代碼(經過其它語言轉換成二進制顯示):
#include <stdio.h> #include <sys/stat.h> int main(int argc, char *argv[]) { // 110000000000000 printf("%d\n", S_IFBLK); // 10000000000000 printf("%d\n", S_IFCHR); // 100000000000000 printf("%d\n", S_IFDIR); // 1000000000000 printf("%d\n", S_IFIFO); // 1010000000000000 printf("%d\n", S_IFLNK); // 1000000000000000 printf("%d\n", S_IFREG); // 1100000000000000 printf("%d\n", S_IFSOCK); return 0; }
每一個進程都具備以下id:
real user ID real group ID |
who we really are |
effective user ID effective group ID supplementary group IDs |
used for file access permission checks |
saved set-user-ID saved set-group-ID |
saved by exec functions |
當咱們登陸時候, real user ID/real group ID都已經肯定下來了, 一般不可更改, 但超級用戶有權限修改.
而effective user ID/group ID(s)代表接觸文件的權限.
set-user-ID/set-group-ID在程序執行時, 複製effective user ID/group ID
普通文件具備access權限:
st_mode mask | 含義 |
S_IRUSR S_IWUSR S_IXUSR |
用戶只讀 用戶只寫 用戶可執行 |
S_IRGRP S_IWGRP S_IXGRP |
分組只讀 分組只寫 分組可執行 |
S_IROTH S_IWOTH S_IXOTH |
其它只讀 其它只寫 其它可執行 |
基於real user ID/group ID, 來測試文件是否可讀, 可寫, 可執行:
#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
returns: 成功返回0, 失敗返回-1
而mode具備如下四個值:
mode | 描述 |
F_OK | 文件是否存在 |
R_OK | 文件是否可讀 |
W_OK | 文件是否可寫 |
X_OK | 文件是否可執行 |
例子: 經過access函數來查看文件的訪問權限。
#include <stdio.h> #include <fcntl.h> int main(int argc, char *argv[]) { if (argc != 2) printf("usage:a.out <pathname>"); if (access(argv[ 1 ], R_OK) < 0) printf("access error for %s\n", argv[1]); else printf("read access OK\n"); if (open(argv[ 1 ], O_RDONLY ) < 0) printf("open error for %s\n", argv[1]); else printf("open for reading OK\n"); return 0; }
程序輸出:
leicj@leicj:~/test$ ls -l a.out -rwxrwxr-x 1 leicj leicj 8760 12月 24 10:30 a.out leicj@leicj:~/test$ ./a.out a.out read access OK open for reading OK leicj@leicj:~/test$ ls -l /etc/shadow -rw-r----- 1 root shadow 1343 12月 22 18:29 /etc/shadow leicj@leicj:~/test$ ./a.out /etc/shadow access error for /etc/shadow open error for /etc/shadow leicj@leicj:~/test$ sudo chown root a.out [sudo] password for leicj: leicj@leicj:~/test$ sudo chmod u+s a.out leicj@leicj:~/test$ ls -l a.out -rwsrwxr-x 1 root leicj 8760 12月 24 10:30 a.out leicj@leicj:~/test$ ./a.out /etc/shadow access error for /etc/shadow open for reading OK
#include <sys/stat.h>
mode_t umask(mode_t cmask);
returns: 返回文件以前的模式
umask用於屏蔽模式, 例如umask(444), 則後面建立的文件均不可讀:
mask bit | 含義 |
0400 | 用戶可讀 |
0200 | 用戶可寫 |
0100 | 用戶可執行 |
0040 | 分組可讀 |
0020 | 分組可寫 |
0010 | 分組可執行 |
0004 | 其它可讀 |
0002 | 其它可寫 |
0001 | 其它可執行 |
一個實際的例子:
#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) int main(void) { umask( 0 ); if (creat("foo", RWRWRW) < 0) printf("creat error for foo\n"); umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (creat("bar", RWRWRW ) < 0) printf("creat error for bar\n"); return 0; }
終端輸出:
leicj@leicj:~/test$ ./a.out leicj@leicj:~/test$ ls -l foo bar -rw------- 1 leicj leicj 0 12月 24 10:44 bar -rw-rw-rw- 1 leicj leicj 0 12月 24 10:44 foo
這三個函數用於改變文件的access權限:
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
returns: 成功返回0, 失敗返回-1
而mode取值以下:
mode | 含義 |
S_ISUID S_ISGID S_ISVTX |
set-user-ID on execution set-group-ID on execution saved-text(sticky bit) |
S_IRWXU S_IRUSR S_IWUSR S_IXUSR |
用戶可續,可寫, 可執行 用戶可讀 用戶可寫 用戶可執行 |
S_IRWXG S_IRGRP S_IWGRP S_IXGRP |
分組可讀,可寫,可執行 分組可讀 分組可寫 分組可執行 |
S_IRWXO S_IROTH S_IWOTH S_IXOTH |
其它可讀,可寫,可執行 其它可讀 其它可寫 其它可執行 |
例子: 修改文件的訪問權限
#include <stdio.h> #include <sys/stat.h> int main(void) { struct stat statbuf; if (stat("foo", &statbuf) < 0) printf("stat error for foo\n"); if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) printf("chmod error for foo\n"); if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) < 0) printf("chmod error for bar\n"); return 0; }
程序輸出:
leicj@leicj:~/test$ ./a.out leicj@leicj:~/test$ ls -l foo bar -rw-r--r-- 1 leicj leicj 0 12月 24 10:44 bar -rw-rwSrw- 1 leicj leicj 0 12月 24 10:44 foo
chown函數用於改變文件的用戶id和分組id, 若是owner或group參數有一個爲-1, 則修改失效.
#include <unistd.h>
int chown(conat char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group);
int lchown(const char *pathname, uid_t owner, gid_t group);
returns: 成功返回0, 失敗返回-1
#include <stdio.h> #include <unistd.h> #include <sys/stat.h> int main(void) { struct stat buf; if (stat("foo", &buf) < 0) { printf("stat error\n"); return 1; } printf("uid is:%d, gid is:%d\n", buf.st_uid, buf.st_gid); if (chown("foo", 0, 0) < 0) { printf("chown error\n"); return 1; } if (stat("foo", &buf) < 0) { printf("stat error\n"); return 1; } printf("now uid is:%d, gid is:%d\n", buf.st_uid, buf.st_gid); return 0; }
終端輸出:
leicj@leicj:~/test$ sudo ./a.out uid is:1000, gid is:1000 now uid is:0, gid is:0
stat結構中的st_size表明文件的大小, 文件大小隻對普通文件, 目錄和符號連接文件有效.
對於符號連接來講, 文件大小爲所連接文件名的長度.
leicj@leicj:~/test$ ls -l ttt test.js -rwxrwxrwx 1 leicj leicj 566 12月 19 14:21 test.js lrwxrwxrwx 1 leicj leicj 7 12月 24 12:58 ttt -> test.js
假設咱們使用lseek人爲的製造空洞文件:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> int main(void) { int fd; char buf[] = "123"; if ((fd = open("core", O_RDWR | O_CREAT)) < 0) printf("error"); write(fd, buf, 3); lseek(fd, 10, SEEK_SET); write(fd, buf, 3); close(fd); return 0; }
當咱們使用cat查看core文件時候, 終端輸出:
123123
執行命令查看:
leicj@leicj:~/test$ ls -l core -rwxrwxrwx 1 leicj leicj 13 12月 24 13:05 core
某種狀況下, 咱們但願截斷文件的數據, 則可使用truncate函數:
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
returns: 成功返回0, 失敗返回-1
1. 兩個目錄入口可能指向相同的i-node入口. 每一個i-node都存在一個統計多少個目錄指向它的變量. 只有此變量等於0, 才肯定文件被刪除.
這就是爲何unlink一個文件並不意味着刪除文件. 在stat結構中, 存在st_nlink屬性.
2. 符號連接僅僅只是一個指針, 指向具體的文件, 因此它的長度是具體文件名的長度.
leicj@leicj:~/test$ ll ttt lrwxrwxrwx 1 leicj leicj 7 12月 24 12:58 ttt -> test.js*
3. i-node節點包含一個文件的所有信息: 文件類型, 文件access權限, 文件大小, 指向數據的指針等等. 絕大部分的信息都在stat結構中. 僅僅只有兩項存在於目錄入口: 文件名和i-node數量.
4. 目錄中i-node number指向i-node, 因此對於ln命令來講, 沒法跨域文件系統執行.
5. 重命名一個文件並不改變文件系統, 僅僅只是增長一個目錄入口指向已存在的i-node, unlink舊的目錄入口.
當咱們執行: mkdir testdir時以下:
使用link建立多個目錄入口, 指向相同的i-node:
#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);
returns: 成功返回0, 失敗返回-1
int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
returns: 成功返回0, 失敗返回-1
考慮以下的代碼:
#include <stdio.h> #include <fcntl.h> int main(void) { if (open("tempfile", O_RDWR) < 0) printf("open error\n"); if (unlink( "tempfile") < 0) printf("unlink error\n"); printf("file unlinked\n"); sleep(30); printf("done\n"); return 0; }
正常狀況來講, 應該進程徹底退出後系統才統計空間大小. 但如今的結果(Ubuntu16)跟以前的不同(Ubuntu14):
leicj@leicj:~/test$ ls -l tempfile -rw-rw-r-- 1 leicj leicj 11 12月 24 14:45 tempfile leicj@leicj:~/test$ df /home Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda6 856811692 8678348 804586720 2% /home leicj@leicj:~/test$ ./a.out & [3] 6967 leicj@leicj:~/test$ file unlinked df /home Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda6 856811692 8678352 804586716 2% /home leicj@leicj:~/test$ done df /home [3] Done ./a.out Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda6 856811692 8678352 804586716 2% /home
也和書上說的不同(以前在Ubuntu14上測試跟書上同樣).
一個文件或目錄能夠經過rename進行重命名.
#include <stdio.h>
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
returns: 成功返回0, 失敗返回-1
基於連接是目錄, 文件仍是符號連接, 具備不一樣的結果:
1. 若是oldname是文件或符號連接. 若是newname已經存在, 則它不能指向目錄. newname存在且非目錄狀況下, 它將被刪除且oldname重命名爲newname, 而且newname的權限要跟oldname同樣.
2. 在oldname爲目錄狀況下, 若是newname已經存在則它必須爲目錄並且目錄必須爲空.
3. 若是oldname或者newname爲符號連接, 則執行連接便可.
4. 不能對"."和".."進行重命名.
5. 若是oldname和newname爲同一文件, 則沒作任何改變.
leicj@leicj:~/test$ ln -s /no/such/file myfile leicj@leicj:~/test$ ls -l myfile lrwxrwxrwx 1 leicj leicj 13 12月 24 15:40 myfile -> /no/such/file leicj@leicj:~/test$ cat myfile cat: myfile: No such file or directory
可使用symlink建立符號連接:
#include <unistd.h>
int symlink(const char *actualpath, const char *sympath);
int symlinkat(const char *actualpath, int fd, const char *sympath);
returns: 成功返回0, 失敗返回-1
使用mkdir建立目錄, 使用rmdir刪除目錄:
#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
returns: 成功返回0, 失敗返回-1
#include <unistd.h>
int rmdir(const char *pathname);
returns: 成功返回0, 失敗返回-1
例子: 遍歷一個目錄
#include <stdio.h> #include <unistd.h> #include <dirent.h> #include <sys/stat.h> #include <stdlib.h> #include <string.h> static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot; void myftw( char *, char * ); int main( int argc, char *argv[] ) { if ( argc != 2 ){ printf("input error\n"); return 1; } myftw( "", argv[ 1 ] ); ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock; if ( 0 == ntot ){ ntot = 1; } printf("regular files=%7ld, %5.2f%%\n", nreg, nreg * 100.0 / ntot ); printf("directory=%7ld, %5.2f%%\n", ndir, ndir * 100.0 / ntot ); printf("block special=%7ld, %5.2f%%\n", nblk, nblk * 100.0 / ntot ); printf("char special=%7ld, %5.2f%%\n", nchr, nchr * 100.0 / ntot ); printf("FIFOs=%7ld, %5.2f%%\n", nfifo, nfifo * 100.0 / ntot ); printf("symbolic links=%7ld, %5.2f%%\n", nslink, nslink * 100.0 / ntot ); printf("sockets=%7ld, %5.2f%%\n", nsock, nsock * 100.0 / ntot ); return 0; } void myftw( char *curPath, char *path ) { struct stat statbuf; DIR *dp; struct dirent *dirp; char fullPath[ 256 ]; if ( strcmp( curPath, "" ) != 0 ){ strncpy( fullPath, curPath, strlen( curPath ) ); fullPath[ strlen( curPath ) ] = '/'; strncpy( fullPath + strlen( curPath ) + 1, path, strlen( path ) ); fullPath[ strlen( curPath ) + strlen( path ) + 1 ] = '\0'; } else{ strncpy( fullPath, path, strlen( path ) ); fullPath[ strlen( path ) ] = '\0'; } if ( lstat( fullPath, &statbuf ) < 0 ){ printf("lstat error:%s\n", fullPath); return; } if ( S_ISREG( statbuf.st_mode ) ) nreg++; else if ( S_ISDIR( statbuf.st_mode ) ){ ndir++; if ( ( dp = opendir( fullPath ) ) == NULL ){ printf("can't open %s\n", fullPath ); return; } while ( ( dirp = readdir( dp ) ) != NULL ){ if ( strcmp( dirp->d_name, "." ) == 0 || strcmp( dirp->d_name, ".." ) == 0 ){ continue; } myftw( fullPath, dirp->d_name ); } } else if ( S_ISCHR( statbuf.st_mode ) ) nchr++; else if ( S_ISBLK( statbuf.st_mode ) ) nblk++; else if ( S_ISFIFO( statbuf.st_mode ) ) nfifo++; else if ( S_ISLNK( statbuf.st_mode ) ) nslink++; else if ( S_ISSOCK( statbuf.st_mode ) ) nsock++; else printf("file error\n"); }
程序輸出:
leicj@leicj:~/test$ ./a.out /dev regular files= 11, 1.88% directory= 33, 5.65% block special= 31, 5.31% char special= 234, 40.07% FIFOs= 0, 0.00% symbolic links= 275, 47.09% sockets= 0, 0.00%