一 文件的状态信息
每个文件都有自己的状态信息,如文件的大小、文件的所有者以及修改日期等,这些是与文件的内容分开存储的。
Linux下使用stat函数获得一个文件的状态
int stat(const char* file_name, struct stat* buf)int fstat(int fd, struct stat * buf) int lstat (const char * file_name, struct stat * buf);
函数说明:
stat函数的pathname指定文件的路径,该函数从内核中取得文件的状态并将其存储在stat结构中返回给用户,成功返回0,失败返回-1。fstat函数和stat函数类似,只是fstat函数使用文件描述符作为函数参数。lstat函数与stat函数作用一样,其差别在于,当文件本身为链接文件时,lstat会返回链接文件本身的状态信息。
stat的结构及说明
struct stat { dev_t st_dev; /*文件的设备编号*/ ino_t st_ino; /*文件的i-node*/ mode_t st_mode; /*文件的类型和存取的权限*/ nlink_t st_nlink; /*n 连到该文件的硬连接数目,刚建立的文件值为1 */ uid_t st_uid; /*文件所有者的用户识别码*/ gid_t st_gid; /*文件所有者的组识别码*/ dev_t st_rdev; /*若此文件为装置设备文件,则为其设备编号 */ off_t st_size; /*文件大小,以字节计算*/ unsigned long st_blksize; /*文件系统的I/O 缓冲区大小 */ unsigned long st_blocks; /*占用文件区块的个数,每一区块大小为512 个字节*/ time_t st_atime; /* 文件最近一次被存取或被执行的时间,一般只有在用mknod、utime、read、write与tructate时改变*/ time_t st_mtime; /* 文件最后一次被修改的时间,一般只有在用mknod、utime和write时才会改变 */ time_t st_ctime; /* 最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新先前所描述的st_mode*/ };
二 文件的类型
Linux环境下的文件共有以下七种:
- 普通文件(regular file):常见的二进制可执行文件和文本文件都是普通文件。
- 目录文件(directory file):目录文件中存放目录下文件的名字和指向它们信息的指针。目录虽然也是一种文件,但是必须使用特殊的目录系统调用去操作。
- 块特殊文件(block special file):Linux将硬件设备也抽象为文件,块特殊文件就是对带缓冲的设备的抽象。
- 字符特殊文件(character special file):与块设备对应的是字符设备,字符设备是不带缓冲的设备的抽象。Linux中的设备不是块设备就是字符设备。
- 命名管道(named pipe, FIFO):用于进程间通信的特殊文件,只存在与内核中。
- 符号链接(symbolic link):指向另外一个文件的文件,分为符号链接和硬链接
- 套接字(socket):用于网络进程之间通信的文件,也可用于同一主机内进程的通信。
在stat结构文件类型存储在st_mode中,它是一个位向量。由于Linux中一共有七种文件,所以只要用3位二进制就可以表示所有的文件类型了。Linux用宏来去的相应的二进制位,从而判断文件的类型。如下是定义在sys/stat.h中的宏定义。
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊发文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 命名管道 |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字 |
三 文件权限
Linux环境下所有的文件都有访问限制,成为文件的访问权限。每个文件的访问权限分为三类:
- 文件所有者(u)
- 文件所有者所在的组(g)
- 其他的所有用户(o)
每类访问权限又分为三个权限标志位:
- 可读(r)
- 可写(w)
- 可执行(x)
三三得九,所以Linux下文件共有9个权限标志位。Linux提供了测试宏来判断指定的权限标志位是否有权限,方法是使用stat中st_mode域与相应的宏进行与操作,进而判断该位是否有相应的权限。如果有则宏返回true,否则返回false。
S_IRUSR | 0400 | 用户-读 |
S_IWUSR | 0200 | 用户-写 |
S_IXUSR | 0100 | 用户-执行 |
S_IRGRP | 0040 | 组-读 |
S_IWGRP | 0020 | 组-写 |
S_IXGRP | 0010 | 组-执行 |
S_IROTH | 0004 | 其他用户-读 |
S_IWOTH | 0002 | 其他用户-写 |
S_IXOTH | 0001 | 其他用户-执行 |
四 设置用户ID和组ID 位
一个进程通常属于某个用户,一个文件也是如此,与文件权限相关的进程ID分为4中情况,如下所示:
进程用户ID和组ID | 表示的意义 |
ruid | 实际用户ID |
euid | 有效用户ID |
rgid | 实际组ID |
egid | 有效组ID |
- 实际用户ID:表示进程实际属于哪个用户,通常进程由哪个用户创建,则进程的实际用户ID就是哪个用户。该值一般不会改变,除非调用setuid()函数。
- 有效用户ID:表示进程目前实际具有的用户权限。即该进程得到了某个用户的授权,暂时成为该用户的进程,从而具有与该用户其他进程同样的权限。
- 实际组ID:类似与实际用户ID,应用与用户组方面。
- 有效组ID:类似与有效用户ID,应用与用户组方面。
文件的设置用户ID位和设置组ID位,可以使进程的有效用户ID和有效组ID等同与文件所有者的用户ID和组ID,使进程获得等同于文件所有者的权限。进程的用户ID和组ID是在调用exec函数时改变的,因此设置用户ID位和设置组ID位只对可执行文件起作用。Linux提供了宏测试来判断设置用户ID位和设置组ID位是否被设置。如下
st_mode测试宏 | 宏的值(八进制) | 意义 |
S_ISUID | 042000 | 设置用户ID |
S_ISGID | 02000 | 设置组ID |
进程在打开文件时所做的测试如下:
- 若进程的有效用户ID是0,即,该进程属于根用户或得到了跟用户的授权,则可以访问。
- 若进程的有效用户ID和文件所有者的用户ID一致,并且文件所有者适当的文件权限位已被设置,则可以访问。
- 若进程的有效用户ID和文件的组用户ID一致,并且文件的组用户文件权限位已经设置,则可以访问。
- 若进程的有效用户ID不满足以上三个条件,则不能访问。
五 文件权限操作
1 测试文件访问权限
Linux下使用access函数对文件的访问权限进行测试。
相关函数 | stat,open,chmod,chown,setuid,setgid |
表头文件 | #include<unistd.h> |
定义函数 | int access(const char * pathname,int mode); |
函数说明 | access()会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合,R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。F_OK则是用来判断该文件是否存在。由于access()只作 权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。 例如,你会发现DOS的文件都具有“可执行”权限,但用execve()执行时则会失败。 |
返回值 | 若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回- |
access函数使用当前进程的实际用户ID和实际组ID与文件的所有者ID和组ID进行比较。比较方法如下:
- 如果进程的实际用户ID和文件的所有者ID相等,使用参数mode指定的权限与文件所有者的权限进行比较。
- 如果上一条件不成立,且进程的实际组ID和文件的组ID相等,则使用参数mode指定的权限与文件所有者的权限进行比较。
- 如果上述条件都不成立,则使用参数mode与文件的其他用户权限进行比较。
测试下
1 #include2 #include 3 #include 4 #include 5 #include 6 7 int main(void) 8 { 9 int fd; //文件描述符10 struct stat statbuf; //存储文件状态信息11 pid_t pid; //进程ID12 uid_t ruid,euid; //进程的实际用户ID和有效用户ID 13 14 //取得文件状态15 if(stat("test.txt", &statbuf) == -1){ 16 perror("获取文件状态失败: \n");17 exit(1);18 }19 20 //得到进程的实际用户ID和有效用户ID21 if((ruid = getuid()) == -1 || (euid = geteuid()) == -1){22 perror("获得进程有效用户ID失败\n");23 exit(1);24 }25 26 //打印进程的实际用户ID和有效用户ID27 printf("当前进程的实际用户ID为:%u,有效用户ID为:%u\n", ruid, euid);28 29 //打印文件所有者的ID30 printf("文件所有者为:%u\n", statbuf.st_uid);31 32 //测试文件权限33 if(access("test.txt", R_OK) == -1){34 perror("测试失败\n");35 exit(1);36 }37 38 printf("获取 成功\n");39 40 41 return 0;42 43 }
2 文件权限屏蔽字umask
使用umask权限屏蔽字,可以屏蔽一些不希望用户干预设置的权限位。即使用户对这些权限位进行设置,系统在创建文件时也会忽略用户对该位指定的值,将其设置为0。
umask(设置建立新文件时的权限遮罩) | |
相关函数 | creat,open |
表头文件 | #include<sys/types.h> #include<sys/stat.h> |
定义函数 | mode_t umask(mode_t mask); |
函数说明 | umask()会将系统umask值设成参数mask&0777后的值,然后将先前的umask值返回。在使用open()建立新文件时,该参数 mode并非真正建立文件的权限,而是(mode&~umask)的权限值。例如,在建立文件时指定文件权限为0666,通常umask值默认为 022,则该文件的真正权限则为0666&~022=0644,也就是rw-r--r--返回值此调用不会有错误值返回。返回值为原先系统的 umask值。 |
下面在shell中使用文件权限屏蔽字进行测试:
- 打开shell]终端,使用umask检查当前权限屏蔽字
2. 创建一个新文件,检查其权限是否被设置
3. 再次使用umask命令改变权限屏蔽字,再察看创建后的文件权限情况
可以看到此时文件的其他用户权限的可读权限被屏蔽了。
下面在程序中使用文件权限屏蔽字进行测试,程序如下:
1 #include2 #include 3 #include 4 #include 5 6 /*屏蔽所有者,组用户和其他用户的可读权限*/ 7 #define MASK S_IRUSR | S_IRGRP | S_IROTH 8 9 int main()10 {11 int fd;12 mode_t mask;13 struct stat buf;14 15 //改变文件权限屏蔽字,并将原来的屏蔽字保存16 mask = umask(MASK);17 18 printf("原始权限屏蔽字为:%x\n", mask);19 20 //使用新文件的所有者,组用户和其他用户的权限全部被设置21 if( (fd = creat("tmp.txt",777)) == -1){22 perror("创建文件失败\n");23 exit(1);24 }25 26 //获取文件的状态信息27 if(stat("tmp.txt", &buf) == -1){28 perror("获取文件状态信息失败\n");29 exit(1);30 }31 32 //测试文件的读属性33 if( (buf.st_mode & S_IRUSR) != 0)34 printf("文件tmp的所有者具有读权限\n");35 else36 printf("文件tmp的所有者不具有读权限\n");37 38 if( (buf.st_mode & S_IRGRP) != 0)39 printf("文件tmp的所在组具有读权限\n");40 else41 printf("文件tmp的所在组不具有读权限\n");42 43 if( (buf.st_mode & S_IROTH) != 0)44 printf("文件tmp的其他用户具有读权限\n");45 else46 printf("文件tmp的其他用户不具有读权限\n");47 48 49 close(fd);50 }
运行结果如下:
可以看到文件的所有可都权限都被屏蔽了。
3 改变文件权限
只有文件的所有者和根用户可以改变文件的权限。
Linux函数使用chmod和fchmod函数来改变文件的权限。
chmod函数
相关函数 | fchmod,stat,open,chown |
表头文件 | #include<sys/types.h> #include<sys/stat.h> |
定义函数 | int chmod(const char * path,mode_t mode); |
函数说明 | chmod()会依参数mode 权限来更改参数path 指定文件的权限。 |
参数 | mode 有下列数种组合 S_ISUID 04000 文件的(set user-id on execution)位 S_ISGID 02000 文件的(set group-id on execution)位 S_ISVTX 01000 文件的sticky位 S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限 S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限 S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限 S_IRGRP 00040 用户组具可读取权限 S_IWGRP 00020 用户组具可写入权限 S_IXGRP 00010 用户组具可执行权限 S_IROTH 00004 其他用户具可读取权限 S_IWOTH 00002 其他用户具可写入权限 S_IXOTH 00001 其他用户具可执行权限 只有该文件的所有者或有效用户识别码为0,才可以修改该文件权限。基于系统安全,如果欲将数据写入一执行文件,而该执行文件具有S_ISUID 或S_ISGID 权限,则这两个位会被清除。如果一目录具有S_ISUID 位权限,表示在此目录下只有该文件的所有者或root可以删除该文件。 |
返回值 | 权限改变成功返回0,失败返回-1,错误原因存于errno。 |
fchmod函数
相关函数 | chmod,stat,open,chown |
表头文件 | #include<sys/types.h> #include<sys/stat.h> |
定义函数 | int fchmod(int fildes,mode_t mode); |
函数说明 | fchmod()会依参数mode权限来更改参数fildes所指文件的权限。参数fildes为已打开文件的文件描述词。参数mode请参考chmod()。 |
返回值 | 权限改变成功则返回0,失败返回-1,错误原因存于errno。 |
3.1 在shell中改变文件权限
改变文件权限主要有两种方法:
- 直接使用一个确定好的权限字改变权限。如使用权限字0777添加文件所有者的用户权限。
- 使用相对权限,就是在原有的文件权限基础上添加若干权限,如新权限字 = 旧权限字 | 0004。
在shell中调用chmod改变文件权限,有两种使用方法:文字设定法和数字设定法
文字设定法,如下图
数字设定法,如下图
3.2 在程序中改变文件权限
代码如下:
1 #include2 #include 3 #include 4 #include 5 #include 6 7 8 #define RD_MODE S_IRUSR | S_IRGRP | S_IROTH //读文件权限 9 #define WR_MODE S_IWUSR | S_IWGRP | S_IWOTH //写文件权限10 #define EX_MODE S_IXUSR | S_IXGRP | S_IXOTH //文件执行权限11 12 int main()13 {14 int fd;15 struct stat statbuf;16 mode_t mode;17 18 printf("程序开始\n");19 sleep(5);20 21 //使用绝对权限字改变test.txt的权限22 if(chmod("test.txt", RD_MODE) == -1){23 perror("改变文件权限失败\n");24 exit(1);25 }else26 printf("使用绝对权限字改变文件权限成功\n");27 28 //获取文件的状态信息29 if(stat("test.txt", &statbuf) == -1){30 perror("获取文件状态失败\n");31 exit(1);32 }33 34 sleep(10);35 36 //使用相对权限字改变文件权限37 mode = statbuf.st_mode; //取得文件权限字38 mode &= (~S_IRWXU & ~S_IRWXG & ~S_IRWXO); //关闭文件所有的权限39 mode |= (WR_MODE);40 41 if(chmod("test.txt", mode) == -1){42 perror("使用相对权限字改变文件权限失败\n");43 exit(1);44 }else45 printf("使用相对权限字改变文件权限成功\n");46 47 sleep(10);48 49 //使用fchmod改变文件权限50 fd = open("./test.txt", O_WRONLY);51 if(fchmod(fd, EX_MODE) == -1){52 perror("改变文件权限失败\n");53 exit(1);54 }else55 printf("使用fchmod函数改变文件权限成功\n");56 57 printf("完成\n");58 59 60 return 0;61 }
程序在执行之前test.txt的权限如下:
使用chmod改变文件权限为RD_MODE后的权限为:
使用chmod改变文件权限为WR_MODE后的权限为:
使用fchmod改变文件权限为EX_MODE后的权限为: