本章主要围绕四个函数
struct stat 是一个存放文件状态的结构体
stat是基本的形式 传入文件名,将文件的状态存至buf
fstat 是文件描述符形式
lstat 针对于符号链接文件,返回符号链接有关信息,而不返回链接引用的文件信息
fstatat 比较复杂
参数说明:
dirfd
:这是一个文件描述符,表示一个目录文件的引用。当dirfd
设置为AT_FDCWD
时,pathname
参数会被解释为相对于当前工作目录的路径。如果pathname
是一个绝对路径,那么dirfd
参数会被忽略。pathname
:这是指向要查询的文件或目录的路径名的指针。buf
:这是一个指向stat
结构体的指针,该结构体用于保存返回的文件状态信息。这包括文件的类型、权限、索引节点号、设备号、特殊文件的设备编号、链接数、用户ID、组ID以及文件大小等。flag
:这是一个标志,用于控制函数的行为。特别是,它决定是否应该跟随符号链接。如果flag
设置为AT_SYMLINK_NOFOLLOW
,那么fstatat
将返回符号链接本身的信息,而不是它指向的文件的信息。
fstatat
函数返回一个整数值,表示成功或失败。在成功的情况下,它返回0;在失败的情况下,它返回-1,并设置全局变量errno
以指示错误原因。
总的来说,fstatat
函数提供了一个灵活的方式来获取文件的状态信息,特别是当需要处理符号链接或相对于特定目录的路径时。
各种文件类型:
下面是一个用lstat函数判断文件类型的例子:
#include"apue.h"
int main(int argc,char *argv[])
{
int i;
struct stat buf;
char *str;
for (int i = 1; i < argc; i++)
{
if(lstat(argv[i],&buf)<0)
{
err_ret("lstat error");
continue;
}
if (S_ISREG(buf.st_mode))
{
str="regular";
}
else if(S_ISDIR(buf.st_mode))
{
str="directory";
}
else if (S_ISCHR(buf.st_mode))
{
str="character special";
}
else if(S_ISBLK(buf.st_mode))
{
str="block special";
}
else if(S_ISFIFO(buf.st_mode))
{
str="FIFO";
}
else if(S_ISSOCK(buf.st_mode))
{
str="socket";
}
else if (S_ISLNK(buf.st_mode))
{
str="symbolic link";
}
else
{
str="** unknown mode **";
}
printf("%s is %s\n",argv[i],str);
}
return 0;
}
PS:1.用了lstat而不用stat ,用stat观察不到符号链接
2.buf.st_mode文件模式字,其中有文件的类型信息。 用S_ISXXX来判断文件是否属于某种类型
对于实际用户和有效用户
进程执行时的有效用户和实际用户一般是相等的,由哪个用户执行进程,有效用户和实际用户就是哪个用户
但有的进程设置了SUID位:chmod u+s a.out
使得在执行进程时,有效用户变为了a.out的拥有者,而实际用户还是执行a.out的用户。
st_mode
使得“当执行此文件时,将进程的有效ID设置为文件所有者的用户ID”——S_ISUID
或者 “当执行此文件时,将进程的有效组ID设置为文件所有者的组ID”——S_ISGID
st_mode还包含了对文件的访问权限位:
PS:相当于shell chmod指令的 u g o
几条规则:
1.用名字打开任意类型的文件,对该名字中包含的每一个目录要有执行权限
eg:为了打开/usr/include/stdio.h 则要对/ usr include具有执行权限
然后对于文件stdio.h也要有适当权限(取决于你想怎么打开它)
2.在目录中创建新文件 需要对目录有写和执行权限
3.要删除一个现有文件,需要对文件所在目录有写、执行权限,要对文件本身不需要有读、写权限
新文件和目录的所有权
新文件的组ID有两种选择:
1.进程的有效用户ID
2.所在目录的组ID
函数access和faccessat
使用场景:
一个进程使用设置用户ID或者设置组ID功能作为另一个用户运行时,判断实际用户能否访问一个给定的文件.
access函数的使用方法实例:
#include"apue.h"
#include<fcntl.h>
int main(int argc,char * argv[])
{
if(argc!=2)
err_quit("usage: a.out <pathname>");
if(access(argv[1],R_OK)<0)
err_ret("access error for %s",argv[1]);
else
printf("read access OK\n");
if(open(argv[1],O_RDONLY)<0)
err_ret("open error for %s",argv[1]);
else
printf("open for reading OK\n");
return 0;
}
使用sar(非root用户)执行进程时,实际用户与有效用户均是sar
对于/etc/shadow 无论是access还是open都失败了
这里access是测试实际用户的权限,而open测试的是有效用户的权限
将a.out的所有者改成root 并执行chmod u+s后 为a.out设置了SUID位,这使得执行a.out时有效用户会变为root
切换为sar再次执行./a.out /etc/shadow
此时实际用户为sar,有效用户为root
所以access(实际用户)失败了 而open(有效用户)成功了
umask函数
为进程设置文件模式创建屏蔽字,并返回之前的值(没有出错返回)
进程创建文件后,文件的权限位是和umask设置的值和creat函数的mode值相与的结果
示例代码如下:
#include"apue.h"
#include<fcntl.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int main()
{
umask(0);
if(creat("foo",RWRWRW)<0)
err_sys("creat error for foo");
umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if(creat("bar",RWRWRW)<0)
err_sys("creat error for bar");
return 0;
}
注意:在创建新文件时,creat
函数使用的权限(代码中为RWRWRW)并不是真正的文件权限,而是根据mode & ~umask
计算出的权限值(先将umask取反后和mode按位与)。这意味着,即使你在creat
函数中指定了特定的权限,新文件的实际权限还会受到之前通过umask
设置的权限掩码的影响。
对于foo umask为0 RW为 110 110 110
umask相当于屏蔽掉哪些为 umask为0 表示全不屏蔽,于是 最终的文件foo权限为110 110 110
对于bar umask为000 110 110 RW为110 110 110
最后生成的文件bar权限为 110 000 000
另外:在子进程中改变umask通常不改变父进程(通常为shell)的umask位
函数chmod、fchmod和fchmodat
用于更改现有文件的访问权限
chmod函数示例代码如下:
#include"apue.h"
int main()
{
struct stat buf;
if(stat("foo",&buf)<0)
err_sys("stat error for foo");
if(chmod("foo",(buf.st_mode&~S_IXGRP)|S_ISGID)<0)
err_sys("chmod error for foo");
if(chmod("bar",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0)
err_sys("chmod error for bar");
return 0;
}
代码中去掉了foo的组执行权限并设置了SGUID位
为bar设置了用户写、用户读、其他人读的权限
粘着位或称保存正文位——S_ISVTX
从两个方面来解释粘着位的定义:
对于文件来说,特别是可执行文件,如果设置了S_ISVTX位,那么当程序第一次被执行并终止时,程序的正文部分(即text段或代码段)的一个副本会被保存在交换区(swap分区)。由于交换区中的文件是连续存放的,而非交换区中的文件内容可能分散在磁盘的几个块中,因此,当程序再次被执行时,可以更快地将其正文部分从交换区加载到内存中,从而提高程序的启动速度。
对于目录来说,如果设置了S_ISVTX位,那么只有对该目录具有写权限的用户,并且满足一定条件(如拥有该目录下的特定文件、拥有该目录本身或具有超级用户权限),才能删除或重命名该目录下的文件。这种特性有助于保护目录中的文件不被随意删除或重命名,特别是在多用户环境中。例如,在Linux系统中,/tmp和/var/tmp目录默认都设置了粘着位,这使得用户可以在/tmp目录下创建自己的文件,但无法删除或重命名其他用户的文件。
函数chown、fchown、fchownat、lchown
用于更改文件的用户ID和组ID
fchownat 根据选项来实现chown、lchwon的功能
文件长度
stat结构成员st_size表示以字节为单位的文件长度
只对普通文件、目录文件、符号链接有意义。
普通文件范围 大于0字节
目录文件 通常是一个数的整数倍
符号链接 长度通常是文件名中的实际字节数