6.1.普通文件
(1)普通文件(- regular file)包括文本文件+二进制文件。
(2)文本文件即文件中的内容是由文本构成的,文本即经过某种编码的字符(譬如ASCII码字符);所有文件的内容本质上都是数字,而文本文件中的数字本身应理解为该数字所对应的编码字符(譬如ASCII码对应的字符);常见的.c文件和.h文件和.txt文件等都是文本文件;文本文件的好处是可以被人轻松读懂和编辑,则文本文件天生就是为人类发明的。
(3)二进制文件即文件中存储的内容本质上也是数字,但这些数字并非字符对应的数字编码,而是真正的数字;常见的可执行程序文件(gcc编译生成的a.out;arm-linux-gcc编译生成的.bin)都是二进制文件。
(4)从本质上来看(刨除文件属性和内容的理解)文本文件和二进制文件并没有任何区别,都是1个文件里面存放了数字;区别是理解方式不同,若把这些数字就当作数字处理则就是二进制文件,若把这些数字按照某种编码格式去解码成文本字符,则就是文本文件。
(5)在linux系统层面是不区分文本文件和二进制文件的,我们无法从文件本身准确知道该文件属于哪种;我们只能事先就知道该文件的类型然后使用该种文件类型的用法去使用它;有时候也会用一些后缀名来人为的标记文件的类型。
(6)使用文本文件时,常规做法是使用文本文件编辑器(vim、gedit、notepad++、SourceInsight)去打开并编辑它,编辑器会read读出文件二进制数字内容,然后按照编码格式去解码将其还原成字符展现给我们;如果使用文本文件编辑器去打开某个二进制文件,则编辑器会以为该二进制文件还是文本文件然后试图去将其解码成文字,但解码过程很多数字并不对应有意义的字符所以成了乱码;使用二进制阅读工具去读取文本文件,则呈现的是文本文件中字符所对应的二进制的数字编码。
6.2.目录文件和设备文件
(1)目录文件(d directory)就是文件夹,文件夹在linux中是1种特殊文件;用vi打开某个文件夹,则里面存的内容包括该目录文件的路径+目录文件夹里面的文件列表;目录文件比较特殊,本身并不适合用普通的方式来读写,linux中使用特殊的一些API来专门读写目录文件。
(2)设备文件包括字符设备文件(c character)+ 块设备文件(b block);设备文件对应的是硬件设备,即该文件虽然在文件系统中存在,但并不是真正存在于硬盘上的某个文件,而是文件系统虚拟制造出来的(虚拟文件系统譬如如/dev /sys /proc等);虚拟文件系统中的文件大多数不能或者说不用直接读写的,而是用特殊的API产生或者使用的。
(3)管道文件(p pipe)+套接字文件(s socket)+符号链接文件(l link)后续用到的时候会详细分析。
6.3.stat和fstat及lstat函数
(1)每个文件中都附带了该文件的一些属性信息(属性信息是存在于文件本身中的,其只能被专用的API打开看到);文件属性信息查看API有stat、fstat、lstat,其作用一样,参数不同,细节略有不同;linux命令行下可以使用stat命令去查看文件属性信息,实际上stat命令内部就是使用了stat系统调用来实现的(见图1);struct stat是内核定义的1个结构体,在sys/stat.h中声明,该结构体中的所有元素加起来就是文件属性信息。
(2)stat该API的作用就是让内核将我们要查找的目的文件的属性信息结构体的值放入我们传递给stat函数的buf中,当stat该API调用从内核返回时buf中被填充了该文件的正确的属性信息,然后我们通过查看buf这种结构体变量的元素即可得知该文件的各种属性了。
(3)stat是从文件名出发得到文件属性信息结构体,而fstat是从1个已打开的文件fd出发得到文件的属性信息;则若文件没有打开需要读取文件信息即使用stat,若文件已经被打开需要读取文件信息即使用fstat效率会更高;stat是从磁盘去读取静态文件,而fstat是从内存读取动态文件;对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。
(4)通过stat判断文件具体类型(譬如-、d、l),文件属性中的文件类型标志在struct stat结构体的mode_t st_mode元素中,该元素中的每个bit位代表不同的含义,但这些位定义不容易记住,则linux系统事先定义多个宏来进行相应操作(譬如S_ISREG宏返回值是1表示该文件是普通文件,若该文件不是普通文件则返回0)。
(5)独立判断文件权限设置,st_mode记录了文件权限信息,linux并没有给文件权限测试提供宏操作,而只是提供了位掩码,则我们只能用位掩码来判断文件是否具有相应权限。
6.4.文件的权限管理
(1)st_mode本质上是1个32位的数(类型unsinged int),该数中的每个位表示1个含义;文件类型和文件权限都记录在st_mode中,我们使用专门的掩码去取出相应的位即可得知相应的信息。
(2)ls-l打印出的权限列表中共9位分为3组;第1组表示文件的属主(owner、user)对该文件的可读、可写、可执行权限;第2组表示文件的属主所在的组(group)对该文件的权限;第3组3个位表示其它用户(others)对该文件的权限;属主即该文件属于谁,一般来说是创建该文件的用户;但某个文件创建之后可用chown命令去修改该文件的属主,也可用chgrp命令去修改该文件所在的组(见图2)。
(3)文件操作时的权限检查规则即当某个程序a.out被执行,a.out试图去操作文件test.txt,判定a.out是否具有对test.txt的某种操作权限;首先test.txt具有9个权限位,规定了3种人(user、group、others)对该文件的操作权限;我们判定test.txt是否能被a.out来操作,关键先搞清楚a.out被谁执行,即当前程序(进程)是哪个用户的进程。
(4)文本权限管控其实蛮复杂,我们很难轻易的确定当前用户对某个文件是否具有某种权限;软件应在操作某个文件之前先判断当前用户是否有权限做相应操作,若有相应权限即可进行操作,若没有相应权限则提供错误信息给用户;access函数可以测试得到当前执行程序的那个用户在当前环境下对目标文件是否具有某种操作权限。
(5)chmod/fchmod与权限修改,chmod是1个linux命令,用来修改文件的各种权限属性,chmod命令只有root用户才有权利去执行修改,chmod命令其实内部是用linux的1个叫chmod的API实现的。
(6)chown/fchown/lchown与属主修改,linux中有个chown命令来修改文件属主,需以root身份运行(譬如chown root test.txt),chown命令是用chown API实现的;linux中有个chgrp命令来修改文件属主所在的组(譬如chgrp root test.txt)。
(7)umask与文件权限掩码,文件权限掩码是linux系统中维护的1个全局设置,umask的作用是用来设定我们系统中新创建的文件的默认权限的(譬如umask 0000即修改umask值;umask即查询umask值),umask命令就是用umask API实现的。
6.5.读取目录文件
(1)opendir打开某个目录后得到1个DIR类型的指针给readdir函数使用;readdir函数调用1次就会返回1个struct dirent类型的指针,该指针指向的结构体变量里面记录了1个目录项(即目录中的1个子文件);readdir函数调用1次只能读出1个目录项,要想读出目录中所有的目录项必须多次调用readdir函数;readdir函数内部会记住哪个目录项已经被读过了哪个还没读,则多次调用后不会重复返回已经返回过的目录项;当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。
(2)readdir函数和我们前面接触的某些函数是不同的,首先readdir函数直接返回了1个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址,多次调用readdir其实readir内部并不会重复申请内存而是使用第1次调用readdir时分配的那个内存,该设计方法是readdir不可重入的关键;readdir在多次调用时是有关联的,该关联也标明readdir函数是不可重入的。
(3)库函数中有一些函数当年刚开始提供时都是不可重入的,后来意识到这种方式不安全,所以重新封装了C库,提供了对应的可重入版本;可重入函数的命名格式一般为“不可重入版本函数名_r”(譬如readdir_r);关于可重入函数和不可重入函数的介绍请参考链接http://www.cnblogs.com/parrynee/archive/2010/01/29/1659071.html。
6.filepropertyget
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:通过stat简单读取某个文件属性信息并打印输出;
* 通过stat判断某个文件类型;独立判断某个文件的权限。
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PATHNAME "test.txt"
int main(int argc, char **argv)
{
int ret = -1;
struct stat buf;
memset(&buf, 0, sizeof(buf)); // 清零buf缓冲区
ret = stat(PATHNAME, &buf);
if (ret < 0)
{
perror("stat error");
exit(-1);
}
#if 0
// 成功获取了stat结构体,从中可以得到各种属性信息了
printf("inode = %d.\n", buf.st_ino); // 静态文件Inode编号st_ino
printf("size = %d bytes.\n", buf.st_size); // 文件大小st_size
printf("st_blksize = %d.\n", buf.st_blksize); // 操作系统文件读写IO缓冲区大小st_blksize
printf("st_blocks = %d.\n", buf.st_blocks); // 占用的块数st_blocks
#endif
#if 0
// 通过stat判断某个文件的类型
ret = S_ISREG(buf.st_mode);
printf("ret = %d.\n", ret); // ret = 1.
ret = S_ISDIR(buf.st_mode);
printf("ret = %d.\n", ret); // ret = 0.
#endif
#if 1
// 独立判断某个文件的权限
// 需在非共享目录下更改文件权限
// 增加文件属主的可执行权限(chmod u+x test.txt)
ret = ((S_IRUSR & buf.st_mode) ? 1 : 0); // 判断文件的属主是否有可读权限,若有则返回1,若无则返回0
printf("file owner = %d.\n", ret);
#endif
return 0;
}
6.filepermission
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:验证文件操作时的权限检查规则。
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int ret = -1;
if (argc != 2)
{
printf("usage: %s filename.\n", argv[0]);
exit(-1);
}
// 需在linux原生目录(譬如“/home/gec/.”)下运行该程序
// 需在普通用户下手动新建某个文件,然后手动创建新用户并来回切换用户进行测试
// 以“./a.out filename”格式运行程序
ret = open(argv[1], O_RDONLY);
if (ret > 0)
{
printf("can read.\n");
close(ret);
}
else
{
perror("open O_RDONLY error");
}
ret = open(argv[1], O_WRONLY);
if (ret > 0)
{
printf("can write.\n");
close(ret);
}
else
{
perror("open O_WRONLY error");
}
return 0;
}
6.access
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:使用access函数判断当前用户是否对该文件有相应的操作权限。
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define PATHNAME "test.txt"
int main(int argc, char **argv)
{
int ret = -1;
ret = access(PATHNAME, F_OK);
if (ret < 0)
{
printf("The file is not exit.\n");
exit(-1);
}
else
{
printf("The file is exit.\n");
}
ret = access(PATHNAME, R_OK);
if (ret < 0)
{
printf("The file can not read.\n");
}
else
{
printf("The file can read.\n");
}
return 0;
}
6.chmod
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:使用chmod函数修改文件权限。
*/
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define PATHNAME "test.txt"
int main(int argc, char **argv)
{
int ret = -1;
if (argc != 2)
{
printf("usage: %s filename.\n", argv[0]);
exit(-1);
}
// -rwx------若没特意指明的权限则默认被取消掉
ret = chmod(PATHNAME, S_IRUSR | S_IWUSR |S_IXUSR);
if (ret < 0)
{
perror("chmod error");
exit(-1);
}
return 0;
}
6.chown
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:chown函数修改文件属主和属主所在的组。
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define PATHNAME "test.txt"
int main(int argc, char **argv)
{
int ret = -1;
if (argc != 2)
{
printf("usage: %s filename.\n", argv[0]);
exit(-1);
}
// 将test.txt的文件属主和属主所在的组均改为root
ret = chown(PATHNAME, 0, 0);
if (ret < 0)
{
perror("chown error");
exit(-1);
}
return 0;
}
6.umask
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:使用umask函数设置新建文件的默认权限。
* Linux下设置新建文件时的默认权限掩码由umask函数设置
* mode_t umask(mode_t umask) 传入四位八进制数,返回系统原先的权限掩码
* 0666对应文件权限中的0000 0444对应文件权限中的0222
*
* 系统原来的权限掩码是:22.
* 系统新的权限掩码是:666.
* 创建了文件test1.txt.
* -r-xr-xr-x 1 root root 0 Jul 11 2016 test1.txt
* 系统原来的权限掩码是:666.
* 系统新的权限掩码是:444.
* 创建了文件test2.txt.
* -rwxrwxrwx 1 root root 0 Jul 11 2016 test2.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
mode_t new_umask, old_umask;
new_umask = 0666;
old_umask = umask(new_umask);
printf("系统原来的权限掩码是:%o.\n",old_umask);
printf("系统新的权限掩码是:%o.\n",new_umask);
system("touch test1.txt");
printf("创建了文件test1.txt.\n");
system("ls test1.txt -l");
new_umask = 0444;
old_umask = umask(new_umask);
printf("系统原来的权限掩码是:%o.\n",old_umask);
printf("系统新的权限掩码是:%o.\n",new_umask);
system("touch test2.txt");
printf("创建了文件test2.txt.\n");
system("ls test2.txt -l");
return 0;
}
6.opendir_readdir
/*
* 公司:XXXX
* 作者:Rston
* 项目:文件类型和文件权限管理
* 功能:读取目录文件并打印输出并判断文件类型和统计目录下的文件项数目。
*/
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
DIR *pDir = NULL;
struct dirent *pEnt = NULL;
unsigned int cnt = 0; // 统计目录文件下的文件项数目
if (argc != 2)
{
printf("usage: %s dirname.\n", argv[0]);
exit(-1);
}
pDir = opendir(argv[1]);
if (NULL == pDir)
{
perror("opendir error");
exit(-1);
}
while (1)
{
pEnt = readdir(pDir);
if (pEnt != NULL)
{
// 打印输出该目录下的所有的文件项名字
printf("name: [%s].\n", pEnt-> d_name);
// 判断某个文件项是否为普通文件
if (DT_REG == pEnt->d_type)
{
printf("%s is a regular file.\n", pEnt-> d_name);
}
else
{
printf("%s is not a regular file.\n", pEnt-> d_name);
}
// 统计该目录下所有文件项个数
cnt++;
}
else
{
break;
}
}
printf("The cnts of fileobject is %d.\n", cnt);
return 0;
}