#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%