文件属性结构体详解
struct stat
{
dev_t st_dev; /* 块设备号 */
ino_t st_ino; /* inode结点号,文件属性信息所存inode节点的编号 */
mode_t st_mode; /* 文件类型和文件权限*/ ls
nlink_t st_nlink; /* 链接数 */ ls
uid_t st_uid; /* 文件所属用户ID*/ ls
gid_t st_gid; /* 文件所属组ID */ ls
dev_t st_rdev; /* 字符设备ID */
off_t st_size; /* 文件大小 */
blksize_t st_blksize; /* 系统每次按块Io操作时,块的大小(一般是512或1024) */
blkcnt_t st_blocks; /* 块的索引号 */
time_t st_atime; /* 最后一次访问时间,read*/ ls
time_t st_mtime; /* 最后一次修改时间,write */
time_t st_ctime; /* 最后一次属性修改的时间,如权限被修改,文件所有者(属主)被修改 */
};
7类文件都有的属性
ino_t st_ino; /* inode结点号 */
mode_t st_mode; /* 文件类型和文件权限*/
nlink_t st_nlink; /* 链接数 */
uid_t st_uid; /* 文件所属用户ID*/
gid_t st_gid; /* 文件所属组ID */
off_t st_size; /* 文件大小 */
time_t st_atime; /* 最后一次访问时间,read*/
time_t st_mtime; /* 最后一次修改时间,write */
time_t st_ctime; /* 最后一次属性修改的时间,如权限被修改,文件所有者(属主)被修改 */
以上是7类文件都有的属性。
dev_t st_dev; /* 块设备号 */
blksize_t st_blksize; /* 系统每次按块Io操作时,块的大小(一般是512或1024) */
blkcnt_t st_blocks; /* 块的索引号 */
以上专门给块设备文件用的。
dev_t st_rdev; /* 字符设备号(ID) */
上面是专门给字符设备用的
ls的详细信息
ls对应信息:
st_mode st_link st_uid st_gid st_size st_atime 文件名
-rw-rw-r-- 1 stu stu 0 Feb 4 20:35 new.c
st_uid
用户id(用户编号),编号是唯一的,例子中的编号1000代表的就是stu这个用户。
在Linux下,是以编号形式来管理用户的,这有点像人名和身份证的关系。
st_gid
多个用户可以在一起组成一个组,其中的某个用户会担任组长,该用户的用户id,就是整个组的组id。
这就好比张三、李四、王五组成了一个组,张三当组长,张三的身份证号也被作为了整个组的组id,张三的名字也被作为了整个组的组名。
对于普通用户而言,自己一个人就是一组,组员和组长都是自己,所以一般情况下,ls显示文件的所属组时,就是所属用户亲自担任组长的那个组,而且组员就自己一人。
st_mode
ls显示的st_mode(比如-rw-rw-r–)
– :文件类型
-rw-rw-r–:文件权限
每三个为一组(-rw-rw-r–)
每组第一个:如果是-,表示不能读,如果是r,表示可以读
每组第二个:如果是-,表示不能写,如果是w,表示可以写
每组第三个:如果是-,表示不可以被cpu执行,如果是x,表示可以被执行。
如果是编译得到的可执行文件(里面所放的内容是机器指令),这个文件是可以被cpu执行。
第一组:代表的是文件所属用户,对该文件的操作权限
第二组:代表的是文件所属组里面,其它的组员用户,对该文件的操作权限
第三组:除了所属用户、所属组以外的,其它用户对该文件的操作权限。
举例:在普通用户下修改root用户的文件。
三组权限的大小关系
正常情况下:
所属用户的操作权限 >= 组员用户的操作权限 >= 其它不相干用户的操作权限
这就好比:
你自己的私人物品:
所属用户:你,你拥有最高操作权限
所属组:你和你的家人就是一个组,你的家人作为组员,拥有仅次于你的操作权限
外人:你家人以外的人,显然拥有最低操作权限,低到甚至是没有操作权限
将数字形式的st_mode,打印为-rwxrwxr-x形式
st_mode的本质
st_mode的本质就是一个数字,我们看到的rwx的形式,只是形态转换的结果。
st_mode的组成
我们以二进制来分析st_mode,以二进制表示时,st_mode有16位,包含三部分信息:
我们之后对于设置位进行说明。
我们对于文件进行查看:
我们可以看到起始为十进制33204转化二进制形式为:
1000 000 110110100
文件类型:1000
设置位:000
文件权限:110 110 100
文件类型
12~15 bit用于表示文件类型
表示文件类型
八进制 二进制
- 0100000(1000 000000000000):代表普通文件
- 0040000(0100 000000000000):代表目录文件
- 0060000(0110 000000000000):代表块设备文件
- 0020000(0011 000000000000):代表字符设备文件
- 0010000(0001 000000000000):代表管道文件
- 0140000(1100 000000000000):代表套接字文件
- 0120000(1010 000000000000):代表符号链接文件
八进制第一个0是格式说明,表示八进制数。变换为二进制,前面4位表示文件类型。为了方便使用,在Linux系统提供的stat.h头文件中,给以上数字定义了宏名。
我们在linux平台中通过man 2 stat 查看:
如何取出12~15位的值,然后用于判断文件的类型:
使用c语言中的 & 操作即可实现
我们使用屏蔽字0170000(1111000000000000)&st_mode,将013清零(屏蔽),留下的1215就是我们要的文件类型。
关于C语言位操作,读者可以在C语言专栏进行阅读。
系统给这个屏蔽字定义了一个宏名,即#define S_IFMT 0170000
比如:我们前面中的st_mode为:
33204转化位二进制为
1000 000 110110100
&
1111 000 000 000 000
结果为:
1000 000 000 000 000
经过比对,我们给出的new.txt是属于普通文件。
判断文件类型
为了方便我们操作,系统在stat.h中定义了相应的带参宏,可以让我们快速的判断文件的类型。
#define S_ISLNK(st_mode) (((st_mode) & S_IFMT) == S_IFLNK)
#define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG)
#define S_ISDIR(st_mode) (((st_mode) & S_IFMT) == S_IFDIR)
#define S_ISCHR(st_mode) (((st_mode) & S_IFMT) == S_IFCHR)
#define S_ISBLK(st_mode) (((st_mode) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(st_mode) (((st_mode) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(st_mode) (((st_mode) & S_IFMT) == S_IFSOCK)
每一个带参宏,用于判断一种文件类型,判断时会把st_mode&S_IFMT,然后与对应的类型比对,如果比对结果为真,就表示是这种类型的文件,否则就不是。
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void print_error(char *str)
{
perror(str);
exit(-1);
}
int main(int argc,char **argv)
{
int ret = 0;
struct stat sta = {0};
//int stat(const char *pathname, struct stat *statbuf);
//获取文件属性
if(argc != 2)
{
printf("my_ls fileName\n\n");
exit(-1);
}
printf("argv[0] = %s,argc[1] = %s\n",argv[0],argv[1]);
ret = stat(argv[1],&sta);
if(-1 == ret) print_error("stat fail");
////打印文件类型
char file_type = '0';
if(S_ISLNK(sta.st_mode)) file_type = 'l';
else if( S_ISREG(sta.st_mode)) file_type = '-';
else if( S_ISDIR(sta.st_mode)) file_type = 'd';
else if( S_ISCHR(sta.st_mode)) file_type = 'c';
else if( S_ISBLK(sta.st_mode)) file_type = 'b';
else if( S_ISFIFO(sta.st_mode)) file_type = 'p';
else if( S_ISSOCK(sta.st_mode)) file_type = 's';
printf("%c ",file_type);
//打印文件属性
printf("%d %lu %d %d %ld %ld %s\n",sta.st_mode,sta.st_nlink,sta.st_uid,sta.st_gid,sta.st_size,sta.st_atime,argv[1]);
return 0;
}
执行结果为:
我们再创建一个目录文件进行验证:
我们就可以看到文件类型。
文件权限
0~8bit表示文件权限
对于普通文件,即使是给了可执行权限,能够被CPU执行也必须是机器指令才能被执行,如果是文字编码给可执行权限是没有意义的。
从st_mode中提取文件权限
提取用户对应的权限
在stat.h中,定义了对应的屏蔽字。定义了三个宏定义:
#define S_IRUSR 00400:对应的是0000 000 100 000 000,提取用户读权限
#define S_IWUSR 00200:对应的是0000 000 010 000 000,提取用户写权限
#define S_IXUSR 00100:对应的是0000 000 001 000 000,提取用户可执行权限
我们下里面举出来提取用户读权限的例子,写权限和可执行权限方法类似。
我们前面中的st_mode为:
33204转化位二进制为
1000 000 110 110 100
&
0000 000 100 000 000
结果为1
0000 000 100 000 000
表示用户可读 结果为0表示用户不可读。
提取组对应的权限
#define S_IRGRP 00040 (0000 000 000 100 000)组读权限
#define S_IWGRP 00020 (0000 000 000 010 000)组写权限
#define S_IXGRP 00010 (0000 000 000 001 000)组可执行权限
提取其他用户对应的权限
#define S_IROTH 00004 (0000 000 000 000 100)其他用户读权限
#define S_IWOTH 00002 (0000 000 000 000 010)其他用户写权限
#define S_IXOTH 00001 (0000 000 000 000 001)其他用户可执行权限
代码演示:
将数字表示的读写权限,表示为rwx所表示的权限。
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void print_error(char *str)
{
perror(str);
exit(-1);
}
int main(int argc,char **argv)
{
int ret = 0;
struct stat sta = {0};
//int stat(const char *pathname, struct stat *statbuf);
//获取文件属性
if(argc != 2)
{
printf("my_ls fileName\n\n");
exit(-1);
}
printf("argv[0] = %s,argc[1] = %s\n",argv[0],argv[1]);
ret = stat(argv[1],&sta);
if(-1 == ret) print_error("stat fail");
////打印文件类型
char file_type = '0';
if(S_ISLNK(sta.st_mode)) file_type = 'l';
else if( S_ISREG(sta.st_mode)) file_type = '-';
else if( S_ISDIR(sta.st_mode)) file_type = 'd';
else if( S_ISCHR(sta.st_mode)) file_type = 'c';
else if( S_ISBLK(sta.st_mode)) file_type = 'b';
else if( S_ISFIFO(sta.st_mode)) file_type = 'p';
else if( S_ISSOCK(sta.st_mode)) file_type = 's';
printf("%c ",file_type);
//打印文件权限
char buf[10] = {0};
char tmp_buf[] = "rwxrwxrwx";
int i;
for(i = 0;i<9;i++)
{
if(sta.st_mode&(1<<(8-i))) buf [i] = tmp_buf[i];
else buf[i] = '-';
}
printf("%s",buf);
//打印文件属性
printf("%d %lu %d %d %ld %ld %s\n",sta.st_mode,sta.st_nlink,sta.st_uid,sta.st_gid,sta.st_size,sta.st_atime,argv[1]);
return 0;
}
执行结果为:
那么我们已经实现了 st_mode 我们就可以把33204删除掉。
删除之后执行结果为:
我们可以看到文件权限的部分和ls实现的是一样,说明我们上面的实现没有问题。
使用chmod命令修改文件权限(rwx)
直接使用数字
①chmod 0777 file.txt
这个0777,在命令行时,代表八进制的0可以省略。
第一个7:user对应的权限,111
第二个7:group对应的权限,111
第三个7:other对应的权限,111
修改后,文件的权限变为了:rwxrwxrwx。
②:chmod 0664 file.txt
0664的二进制为
110 110 100
修改后,文件的权限变为了:rw-rw-r–。
我们前面提到的对于存放文字编码的普通文件来说,不需要任何的执行权限。
open创建文件时所指定的原始权限
fd = open("./new_file.txt", O_RDWR|O_CREAT, 0664);
Open函数里面0不能省略
使用open系统API创建新文件时,需要制定一个原始的权限,比如指定的是0664的话,就表示,创建出的文件的原始权限为rw-rw-r–。
直接使用rwx来设置
修改所有权限
chmod a=rw- file.txt
表示所有的权限全部指定为rw-
只修改某一组的权限
chmod u=rw- file.txt
只将文件所属用户对应的权限设置为rw-
chmod g=r-- file.txt
只将文件所属组对应的权限设置为r–
chmod o=r-- file.txt
只将其它用户对应的权限设置为r–
chmod u=rwx,g=rw-,o=r-- file.txt
一次性的对三个权限都做设置。
只修改组里面某一位的权限
chmod u+x file.txt 给所属用户加上可执行权限
chmod u-x file.txt 给所属用户去掉可执行权限
chmod u+x,u+r file.txt 给所属用户加上可执行权限和读权限
chmod g+w file.txt 给组加上写权限
chmod o+r file.txt 给其他用户加上读权限
chmod u+r,g+w,o+x file.txt 给所属用户机上读权限,给组加上写权限,给其他用户加上可执行权限。
设置位用的很少,因此这里就不再说明,我们只需要知道在st_mode里面有三位设置位即可。