注:前言、目录见 /qq_44220418/article/details/108428971
文章目录
一、文件系统简介1、索引节点2、文件系统组成3、文件(1).类型(2).文件权限[1].文件权限[2].表示方法(3).访问文件二、文件基本I/O操作1、文件描述符2、常用系统调用(1).`open`(2).`close`(3).`write`(4).`read`(5).`lseek`(6).使用示例(7).探索与练习三、目录操作1、目录文件2、常用系统调用(1).`opendir`(2).`readdir`(3).`closedir`(4).`rewinddir`(5).`seekdir`(6).`telldir`(7).`mkdir`(8).`rmdir`(9).`getcwd`(10).`chdir`(11).`rename`(12).使用示例四、文件/目录的属性1、常用函数(1).`stat`(2).`lstat`(3).`fstat`2、常用宏(1).文件类型宏(2).文件权限宏3、使用示例一、文件系统简介
1、索引节点
Linux系统采用按名存取的方式访问文件
除了文件名以外,文件的主要属性信息都存放在inode节点中
目录文件中保存着文件名和索引节点的对应关系【可用命令ls -i
查看文件/目录的inode号】
Linux系统中文件包括两部分
索引节点inode
\qquad记录文件属性信息(除了文件名)【可用命令ls -l
查看】
\;
\qquad文件属性包括{文件类型访问权限文件主人组长度访问日期……\begin{cases} 文件类型 \\ 访问权限 \\ 文件主人 \\ 组 \\ 长度 \\ 访问日期 \\ …… \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧文件类型访问权限文件主人组长度访问日期……
\;
\qquad索引节点的结构如下图所示:
\qquad\qquad数据块
\qquad存放文件具体内容
注:硬链接文件的inode与源文件的inode相同
更多关于inode节点、软链接与硬链接的区别相关的知识可以参考博文 Linux文件索引节点inode
2、文件系统组成
一个简单的Unix文件系统组成
\qquad
\qquad引导块:用于引导该分区内操作系统的引导程序
\qquad超级块:UFS的重要参数,如文件系统的{块索引节点总数空闲块空闲索引节点数\begin{cases} 块 \\ 索引节点总数 \\ 空闲块 \\ 空闲索引节点数 \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧块索引节点总数空闲块空闲索引节点数
\qquad索引节点表(i节点):文件系统中索引节点的集合
\qquad数据块:存储文件数据
Linux文件系统——Ext2
\qquad
\qquad第一个是引导块,其他空间分成各个块组
\qquad每个块组中{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块\begin{cases} 超级块 \\ 块组描述符,数据块位图,索引节点位图,索引节点,数据块 \end{cases}{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块
3、文件
(1).类型
文件类型{普通文件(rugularfile)目录文件(directory)字符设备文件(characterdevice)块设备文件(blockdevice)FIFO文件(fifo)符号链接文件(symboliclink)socket套接字文件(socket)\begin{cases} 普通文件 & (\text{rugular file})\\ 目录文件 & (\text{directory})\\ 字符设备文件 & (\text{character device})\\ 块设备文件 & (\text{block device})\\ \text{FIFO}文件 & (\text{fifo})\\ 符号链接文件 & (\text{symbolic link})\\ \text{socket}套接字文件 & (\text{socket})\\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧普通文件目录文件字符设备文件块设备文件FIFO文件符号链接文件socket套接字文件(rugularfile)(directory)(characterdevice)(blockdevice)(fifo)(symboliclink)(socket)
(2).文件权限
[1].文件权限
Linux中的用户分为三类{user/owner文件拥有者group用户组other其他用户\begin{cases} \text{user/owner} & 文件拥有者 \\ \text{group} & 用户组 \\ \text{other} & 其他用户 \\ \end{cases}⎩⎪⎨⎪⎧user/ownergroupother文件拥有者用户组其他用户
Linux文件对三类用户都有三种访问权限{r可读w可写x可执行\begin{cases} \text{r} & 可读 \\ \text{w} & 可写 \\ \text{x} & 可执行 \\ \end{cases}⎩⎪⎨⎪⎧rwx可读可写可执行
另外,Linux文件还有三种特殊的权限{setuidsetgidsticky\begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases}⎩⎪⎨⎪⎧setuidsetgidsticky
这里就不对这三种特殊权限做具体介绍了,想知道更详细的可以去看博文 Linux文件特殊权限——SetUID、SetGID、Sticky BIT
Linux文件的索引节点里面有一个st_mode
字段,记录文件类型及权限
该字段共 161616 位{高4位文件类型低12位文件权限\begin{cases} 高4位 & 文件类型 \\ 低12位 & 文件权限 \\ \end{cases}{高4位低12位文件类型文件权限
12位文件权限{特殊权限{setuidsetgidsticky文件拥有者{r可读权限w可写权限x可执行权限用户组{r可读权限w可写权限x可执行权限其他用户{r可读权限w可写权限x可执行权限\begin{cases} 特殊权限 & \begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases} \\ \\ 文件拥有者 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 用户组 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 其他用户 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧特殊权限文件拥有者用户组其他用户⎩⎪⎨⎪⎧setuidsetgidsticky⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限⎩⎪⎨⎪⎧rwx可读权限可写权限可执行权限
如下图所示:
[2].表示方法
文件权限的表示方法
字母如:rwxr-xr-x
就表示了{拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行\begin{cases} 拥有者对文件可读、可写、可执行 \\ 用户组对文件可读、可执行 \\ 其他用户对文件可读、可执行 \\ \end{cases}⎩⎪⎨⎪⎧拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行数字(9位二进制 / 3位八进制)
和上面的字母类似,对应位的二进制{1有权限0无权限\begin{cases} 1 & 有权限 \\ 0 & 无权限 \\ \end{cases}{10有权限无权限
如:111 101 101
就相当于rwxr-xr-x
,755
就相当于rwxr-xr-x
宏(S_IPwww模式)
S_I
固定,P
可为R
或W
或X
,www
可为USR
或GRP
或OTH
,多种权限可用或运算连接
如:S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
就相当于rwxr-xr-x
注:编程中作为参数时,只能使用数字/宏
(3).访问文件
每打开一次文件
系统增加一个file
表项进程增加一个fd
表项(以文件描述符为下标),通过指针指向file
表项用户通过文件描述符和系统调用访问该文件
二、文件基本I/O操作
1、文件描述符
用户空间中任何打开的文件都被分配一个唯一的非负整数,标识该打开文件,即文件描述符
进程默认打开的三个文件描述符
标准输入0
标准输出1
标准错误输出2
会选用文件描述符中未使用的最小值
2、常用系统调用
常用的系统调用如下
(1).open
定义在<fcntl.h>
中
函数原型如下:
int open( const char *path, int flags);int open( const char *path, int flags, mode_t perms);
功能打开一个已经存在的文件时,参数perms
可以省略 创建并打开一个不存在的文件时,参数perms
必填参数返回值成功:返回最小可用的文件描述符(当前文件所用的文件描述符) 失败:返回-1
(置errno
)
(2).close
定义在<unistd.h>
中
函数原型如下:
int close(int fd );
功能关闭文件,释放文件描述符,使之可再利用参数返回值成功:返回0
失败:返回-1
(置errno
)
注:当一个进程终止时,内核会自动检查并回收该进程所有的文件描述符
(3).write
定义在<unistd.h>
中
函数原型如下:
ssize_t write(int fd, const void *buf, size_t nbytes);
功能将buf
所指缓冲区中的nbytes
个字节写入文件描述符fd
所指示的已打开文件中注意点写操作从当前文件偏移量处开始写数据,写完后,文件偏移量也将后移成功写入的字节数(因此不移动读写指针读不到刚刚写入的内容) 若调用open
打开文件时,读写指针初始指向文件开头
\qquad若flags
包含O_APPEND
,则每次写入前,读写指针都将移动到文件末尾 由于物理介质空间不足等原因将会使得write
的返回值小于计划要写入文件的字节个数参数返回值成功:返回已写入的字节数 失败:返回-1
(置errno
)
(4).read
定义在<unistd.h>
中
函数原型如下:
ssize_t read(int fd, void *buf, size_t nbytes);
功能从一文件描述符fd
对应的已打开文件中,读取nbytes
字节的数据放入buf
中注意点读操作从当前文件偏移量处开始读数据,读完后,当前文件偏移也将改变成功读出的字节个数 若调用open
打开文件时,读写指针初始指向文件开头 有时读到的数据可能比希望读的字节少,但系统不会置errno
,因为这不是错误(如文件已经到结尾),需要用户自己去推测问题所在参数返回值成功:返回已读取的字节数 失败:返回-1
(置errno
)
(5).lseek
定义在<unistd.h>
中
函数原型如下:
off_t lseek(int fd, off_t pos, int whence);
文件偏移量文件偏移量是一个非负整数,它用于标识下一次读或写文件的位置 除了O_APPEND
模式打开文件外,读/写操作都从当前文件偏移量处开始 每open
一次文件,就能得到一个新的打开文件表项,因此每次open
都可以得到一个独立的文件偏移量功能移动文件读写指针(文件偏移量)参数返回值成功:返回当前文件偏移量 失败:返回-1
(置errno
)
(6).使用示例
示例1:创建文件权限为rw-r--r--
的文件test.txt
(初始时不存在),在其末尾写入内容
#include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>int main(){// 创建并打开文件test.txtint fd = open("test.txt", O_RDWR | O_APPEND | O_CREAT, 0644);if (fd == -1){perror("open");exit(1);}// 写入文件char str1[] = "123456789012345678901234567890\n";if (write(fd, str1, strlen(str1)) != -1){printf("写入成功\n");}// 关闭文件close(fd);return 0;}
程序输出的内容如下
写入成功
生成的test.txt
文件内容如下
123456789012345678901234567890
示例2:从刚刚示例1生成的test.txt
中读取至多 999999 字节的数据
#include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>int main(){// 打开文件int fd = open("test.txt", O_RDWR);if (fd == -1){perror("open");exit(1);}// 读取文件char buf[100];read(fd, buf, sizeof(buf) - 1);printf("读取到的字符串是\n%s\n", buf);// 关闭文件close(fd);return 0;}
示例3:创建文件权限为rw-r--r--
的文件test3.txt
(初始时不存在),利用lseek
函数定位,写入、读取文件内容
#include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>int main(){// 创建int fd3 = open("test3.txt", O_RDWR | O_CREAT, 0644);char str2[] = "newnewnewnewnewnewnewnew\n";char str3[] = "test\n";char buf[100];// 向文件末尾写入数据lseek(fd3, 0, SEEK_END);write(fd3, str2, strlen(str2));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串3是\n%s\n", buf);// 向文件开头写入数据lseek(fd3, 0, SEEK_SET);write(fd3, str3, strlen(str3));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串4是\n%s\n", buf);close(fd3);return 0;}
程序输出的内容如下
读取到的字符串3是newnewnewnewnewnewnewnew读取到的字符串4是testwnewnewnewnewnewnew
生成的test3.txt
文件内容如下
testwnewnewnewnewnewnew
显然这个最终结果是将test3.txt
之前的前5个字符"newne"
修改成了"test\n"
(7).探索与练习
探索1:对同一文件的不同fd
的操作是否会造成相互影响?
已存在test2.txt
,内容如下:
1234321222222
编写了如下C源程序对其进行探究:
#include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>int main(){char str1[] = "123456789012345678901234567890\n";char buf[100];int fd1 = open("test2.txt", O_RDWR | O_APPEND);int fd2 = open("test2.txt", O_RDONLY);write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));read(fd2, buf, sizeof(buf) - 1);printf("读取到的字符串2是\n%s\n", buf);close(fd1);close(fd2);return 0;}
程序输出的内容如下
读取到的字符串2是12343212222221234567890123456789012345678901234567890123456789012345678901234567890123456789012
修改后的test2.txt
文件内容如下
1234321222222123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
显然,fd1
和fd2
都是已打开文件test2.txt
的文件描述符
fd1
的写操作改变了fd1
的文件偏移量,而没有改变fd2
的文件偏移量(从读操作正常可见)
这也就验证了每次open
都可以得到一个独立的文件偏移量
练习1:将键盘输入的一批字符串(以quit
结束)写入到文件test.txt
中,然后再读出并显示到屏幕上
#include <stdio.h>#include <string.h>#include <unistd.h>#include <fcntl.h>int main(){char str[999];char buf[100];// 打开文件int fd = open("test.txt", O_CREAT | O_RDWR, 0644);// 循环读入并写入scanf("%s", &str);while (strcmp(str, "quit")){// 写入write(fd, str, strlen(str));scanf("%s", &str);}// 将文件偏移量定位到文件开头lseek(fd, 0, SEEK_SET);// 循环读取文件内容printf("读取到的写入内容如下:\n");while (read(fd, buf, sizeof(buf) - 1)){printf("%s", buf);}printf("\n");// 关闭文件close(fd);return 0;}
程序输入的内容如下
1234abcquit
程序输出的内容如下
读取到的写入内容如下:1234abc
创建的test.txt
文件内容如下
1234abc
三、目录操作
1、目录文件
Linux系统中,目录也是以文件的方式存储,在目录文件中存储了该目录下的文件的信息,包括{文件名文件inode编号文件类型⋯\begin{cases} 文件名 \\ 文件inode编号 \\ 文件类型 \\ \cdots \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧文件名文件inode编号文件类型⋯,姑且把这些信息称为一个文件的索引信息
如果想要找到某个文件,首先需要找到这个文件所在目录的目录文件
2、常用系统调用
常用的系统调用如下
(1).opendir
定义在<dirent.h>
中
函数原型如下:
DIR * opendir(const char *dir_path);
功能打开一个目录,返回一个DIR
指针,从而创建一个到目录的连接参数返回值成功:返回指向参数目录的指针 失败:返回NULL
(置errno
)
(2).readdir
定义在<dirent.h>
中
函数原型如下:
struct dirent * readdir(DIR *dir);
功能从DIR
指针中读取一个目录项(文件/目录)的信息,并后移指针对应目录的读取位置参数返回值成功:返回相应目录项的结构体指针 失败:返回NULL
(目录项读取完成)
(3).closedir
定义在<dirent.h>
中
函数原型如下:
int closedir(DIR * dir);
功能关闭目录参数返回值成功:返回0
失败:返回-1
(置errno
)
(4).rewinddir
定义在<dirent.h>
中
函数原型如下:
void rewinddir(DIR * dir);
功能重置目录指针读取位置到起始位置参数
(5).seekdir
定义在<dirent.h>
中
函数原型如下:
void seekdir(DIR * dir, off_t offset);
功能设置目录指针读取位置到指定位置参数
(6).telldir
定义在<dirent.h>
中
函数原型如下:
off_t telldir(DIR * dir);
功能返回目录指针下次读取的位置(可以理解为一个从0
开始的索引号)参数返回值成功:返回当前目录指针下次读取的位置 失败:返回-1
(置errno
)
(7).mkdir
定义在<sys/stat.h>
中
函数原型如下:
int mkdir(char * pathname, mode_t mode);
功能创建目录,并指定其权限模式参数返回值成功:返回0
失败:返回-1
(置errno
)
(8).rmdir
定义在<unistd.h>
中
函数原型如下:
int rmdir(char * pathname);
功能删除目录参数返回值成功:返回0
失败:返回-1
(置errno
)
(9).getcwd
定义在<unistd.h>
中
函数原型如下:
char* getcwd(char *buf, size_t size);
功能获取进程当前所处的工作目录的绝对路径,存入参数字符数组中参数返回值成功:返回存储空间的首地址 失败:返回NULL
(置errno
)
(10).chdir
定义在<unistd.h>
中
函数原型如下:
int chdir(const char *pathname);
功能切换当前进程所处的工作目录注意点对其他进程、调用该进程的进程的当前工作目录均没有影响参数返回值成功:返回0
失败:返回-1
(置errno
)
(11).rename
定义在<unistd.h>
中
函数原型如下:
int rename(const char *from, const char *to);
功能移动并重命名文件注意点rename
并不真正复制文件中的数据,它只是将文件的链接从一个目录移动到另一个目录中,这个过程数据本身并没有移动参数返回值成功:返回0
失败:返回-1
(置errno
)
(12).使用示例
示例4:尝试使用上述若干系统调用读取、创建、删除、更改、查看目录
#include <stdio.h>#include <dirent.h>#include <sys/stat.h>#include <unistd.h>int main(){// 打开目录testdirDIR * dir_ptr = opendir("testdir");struct dirent * dir;// 获取下次读取位置if (dir_ptr != NULL)printf("下次读取的位置是:%ld\n\n", telldir(dir_ptr));// 获取目录下所有文件的名称printf("第一次打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");// 重定位,重新读取printf("第二次打印:\n");if (dir_ptr != NULL){rewinddir(dir_ptr);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 重定位,重新读取printf("第三次打印:\n");if (dir_ptr != NULL){seekdir(dir_ptr, 3);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 关闭目录closedir(dir_ptr);// 创建目录int res = mkdir("./testmk", 0777);if (!res)printf("创建目录成功\n\n");// 删除目录res = rmdir("./testmk");if (!res)printf("删除目录成功\n\n");// 获取当前工作目录char str[100];getcwd(str, sizeof(str) - 1);printf("当前工作目录:\n%s\n\n", str);// 改变并重新获取当前工作目录res = chdir("./testdir/");if (!res){getcwd(str, sizeof(str) - 1);printf("改变后的当前工作目录:\n%s\n\n", str);}// 移动并重命名目录/文件// (注:因为已经改变了当前工作目录,所以现在已经跳转到了testdir目录下,再用"./testdir"就不管用了)int res2 = rename("./subdir/", "./newname/");int res3 = rename("./a.txt", "./b.txt");if (!res2 && !res3){printf("重命名成功\n");dir_ptr = opendir("./");printf("重命名后的打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");}return 0;}
程序输出的内容如下
下次读取的位置是:0第一次打印:...12.txtsubdira.txt第二次打印:...12.txtsubdira.txt第三次打印:12.txtsubdira.txt创建目录成功删除目录成功当前工作目录:/home/excious/工程/目录操作改变后的当前工作目录:/home/excious/工程/目录操作/testdir重命名成功重命名后的打印:...12.txtnewnameb.txt
四、文件/目录的属性
文件与目录的属性存储与inode节点中,可通过系统调用stat
获取文件或者目录的属性,存入struct stat
结构中
结构体stat
至少包含了以下文件信息:
struct stat{dev_t st_dev,// 包含该文件的设备ID号ino_t st_ino,// 文件的inode号mode_t st_mode,// 文件类型及权限模式nlink_t st_nlink,// 文件的链接数uid_t st_uid,// 文件所有者的用户IDgid_t st_gid,// 文件的组IDdev_t st_rdev,// 如果文件为字符或块设备时的设备IDoff_t st_size,// 如果文件为普通文件时的文件字节数time_t st_atime,// 最近的访问时间time_t st_mtime,// 最近的数据修改时间time_t st_ctime,// 最近的文件状态改变时间blksize_t st_blksize,// 该对象文件系统相关的最佳I/O块大小blkcnt_t st_blocks// 系统为此文件所分配的数据块数}
1、常用函数
(1).stat
定义在<sys/stat.h>
中
函数原型如下:
int stat(char *pathname, struct stat *buf);
功能获取文件的详细信息注意点路径中的文件若是符号链接文件,则会获取其所链接到的文件的详细信息参数返回值成功:返回0
失败:返回-1
(置errno
)
(2).lstat
定义在<sys/stat.h>
中
函数原型如下:
int lstat(char *pathname, struct stat *buf);
功能获取文件的详细信息注意点路径中的文件若是符号链接文件,则会获取该文件本身的详细信息参数返回值成功:返回0
失败:返回-1
(置errno
)
(3).fstat
定义在<sys/stat.h>
中
函数原型如下:
int fstat(int fd, struct stat *buf);
功能获取已打开文件的详细信息参数返回值成功:返回0
失败:返回-1
(置errno
)
2、常用宏
(1).文件类型宏
定义在<usr/include/bits/stat.h>
、<sys/stat.h>
中
文件信息中的模式st_mode
是一个161616位的二进制数,由高位(第151515位)到低位(第000位)分别是444为文件类型、333位特殊权限、333位用户权限、333位组权限、333位其他用户权限
C语言中已经定义好的文件类型宏如下表所示
可以把st_mode
和相应的mask
进行按位与,与上述相应的宏进行比较,判断文件类型
关于掩码mask
:要判断文件类型,因为161616位文件模式中高444位是文件类型,因此应取mask
为1111000000000000
,即mask = 0170000
,该常量
C语言中也同样预定义好了用于判断文件类型的函数宏如下,直接将st_mode
传入即可判断:
#define__S_ISTYPE(mode, mask)(((mode) & __S_IFMT) == (mask))#defineS_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)#defineS_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR)#defineS_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK)#defineS_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)#ifdef __S_IFIFO# define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)#endif#ifdef __S_IFLNK# define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)#endif
(2).文件权限宏
定义在<usr/include/bits/stat.h>
、<sys/stat.h>
中
文件信息中的模式st_mode
低999位表示文件权限
C语言中已经定义好的文件权限宏如下
#define S_IRUSR 0000400// 文件所有者读权限#define S_IWUSR 0000200// 文件所有者写权限#define S_IXUSR 0000100// 文件所有者执行权限#define S_IRWXU (__S_IREAD | __S_IWRITE | __S_IEXEC)// 文件所有者读写执行权限#define S_IRGRP (S_IRUSR >> 3)// 组用户读权限#define S_IWGRP (S_IWUSR >> 3)// 组用户写权限#define S_IXGRP (S_IXUSR >> 3)// 组用户执行权限#define S_IRWXG (S_IRWXI >> 3)// 组用户读写执行权限#define S_IROTH (S_IRGRP >> 3)// 其他用户读权限#define S_IWOTH (S_IWGRP >> 3)// 其他用户写权限#define S_IXOTH (S_IXGRP >> 3)// 其他用户执行权限#define S_IRWXO (S_IRWXO >> 3)// 其他用户读写执行权限
3、使用示例
示例5:尝试使用上述若干系统调用获取文件信息、判断文件类型和文件权限
#include <stdio.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>int main(int argc, char *argv[]){struct stat buf;int res = stat("./test.txt", &buf);if (res != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf.st_blocks);}printf("==============================================================\n");int fd = open("test.txt", O_RDONLY);struct stat buf2;int res2 = fstat(fd, &buf2);if (res2 != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf2.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf2.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf2.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf2.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf2.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf2.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf2.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf2.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf2.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf2.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf2.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf2.st_blocks);}close(fd);printf("==============================================================\n");struct stat buf3;int res3 = stat("./test.txt", &buf3);if (res3 != -1){if ((buf3.st_mode & S_IFMT) == S_IFREG)printf("文件test.txt是一个普通文件\n");if (S_ISREG(buf3.st_mode))printf("文件test.txt是一个普通文件\n");}printf("==============================================================\n");struct stat buf4;int res4 = stat("./test.txt", &buf4);if (res4 != -1){int mode = buf4.st_mode;char str_res[] = "----------";// 判断文件类型if (S_ISDIR(mode)) str_res[0] = 'd';if (S_ISCHR(mode)) str_res[0] = 'c';if (S_ISBLK(mode)) str_res[0] = 'b';// 判断用户权限if (mode & S_IRUSR) str_res[1] = 'r';if (mode & S_IWUSR) str_res[2] = 'w';if (mode & S_IXUSR) str_res[3] = 'x';// 判断组权限if (mode & S_IRGRP) str_res[4] = 'r';if (mode & S_IWGRP) str_res[5] = 'w';if (mode & S_IXGRP) str_res[6] = 'x';// 判断其他用户权限if (mode & S_IROTH) str_res[7] = 'r';if (mode & S_IWOTH) str_res[8] = 'w';if (mode & S_IXOTH) str_res[9] = 'x';// 输出文件类型和权限printf("文件类型和权限为%s\n", str_res);}return 0;}
程序输出的内容如下
文件的【设备ID号】为【33】文件的【inode号】为【33875】文件的【类型及权限模式】为【33188】文件的【链接数】为【1】文件的【所有者用户ID】为【1000】文件的【组ID】为【1000】文件的【字节数】为【7】文件的【最近访问时间】为【1618807263】文件的【最近数据修改时间】为【1618807248】文件的【最近状态改变时间】为【1618807260】文件的【系统相关最佳I/O块大小】为【4096】文件的【被系统分配到的数据块数】为【8】==============================================================文件的【设备ID号】为【33】文件的【inode号】为【33875】文件的【类型及权限模式】为【33188】文件的【链接数】为【1】文件的【所有者用户ID】为【1000】文件的【组ID】为【1000】文件的【字节数】为【7】文件的【最近访问时间】为【1618807263】文件的【最近数据修改时间】为【1618807248】文件的【最近状态改变时间】为【1618807260】文件的【系统相关最佳I/O块大小】为【4096】文件的【被系统分配到的数据块数】为【8】==============================================================文件test.txt是一个普通文件文件test.txt是一个普通文件==============================================================文件类型和权限为-rw-r--r--