文章目录
chapter04 文件和目录(一)
函数stat,fstat,fstatat和lstat
通过stat
,fstat
,fstatat
和stat
四个函数可以返回文件的详细信息:
#include<sys/stat.h>
int stat(const char* restrict pathname, struct stat* restrict buf);
int fstat(int fd,struct stat* buf);
int lstat(const char* restrict pathname,struct stat* restrict buf);
int fstatst(int fd,const char* restrict pathname,struct stat* restrict buf);
lstat
函数类似于stat
,但是当命名的文件是一个符号链接时,lstat
返回给符号链接的有关信息,而不是由该符号链接引用的文件的信息.
fstat
函数传入文件描述符,在Unix中很多以f
开头的函数相比于不是f
开头的函数的区别就是传入的参数是不是文件描述符fd
,比如fchmod
,fchown
,ftruncate
等
上面四个函数通过填充由buf
指向的结构来返回文件信息,该结构的实际定义可能随具体实现有所不同,但其基本形式是:
(***)stat结构
struct stat{
mode_t st_mode; //记录文件类型+读写权限
ino_t st_ino; //记录i节点编号
dev_t st_dev; //记录设备号
dev_t st_rdev; //记录实际设备号,只有当文件是块设备或字符设备时才有
nlink_t st_nlink; //指向i节点的链接数
uid_t st_uid; //用户ID
gid_t std_gid; //组ID
off_t st_size; //size in bytes, for regular files
struct timespec st_atime; //文件最后访问时间
struct timespec st_mtime; //文件最后修改时间
struct timespce st_ctime; //文件最后状态更新时间(记录在i节点上)
blksize_t st_blksize; //best I/O block size
blkcnt_t st_blocks; //number of disk blocks allocated
};
其中timespec
结构类型按照秒和纳秒定义了时间,至少包括下面两个字段:
time_t tv_sec;
long tv_nsec;
文件类型
(***)UNIX中的文件类型包括以下几种:
- 普通文件(regular file)
- 目录文件(directory file)
- 块特殊文件(block specila file)
- 字符特殊文件(character special file)
- FIFO(也被称为命名通道)
- 套接字(socket)
- 符号链接
实例:通过定义的宏判断文件类型
#include<stdio.h>
#include<sys/stat.h>
#define oops(m,x) { perror(m); exit(x); }
int main(int ac,char* av[])
{
int i;
struct stat buf;
char *ptr;
for(i=1;i<ac;++i)
{
if(lstat(av[i], &buf)<0)
oops("lstat",1);
if(S_ISREG(buf.st_mode))
ptr="regular";
else if(S_ISDIR(buf.st_mode))
ptr="directory";
else if(S_ISCHR(buf.st_mode))
ptr="char";
else if(S_ISBLK(buf.st_mode))
ptr="block";
else if(S_ISFIFO(buf.st_mode))
ptr="FIFO";
else if(S_ISLNK(buf.st_mode))
ptr="link";
else if(S_ISSOCK(buf.st_mode))
ptr="socket";
else
ptr="unkown";
printf("%s\n",ptr);
}
return 0;
}
设置用户ID和设置组ID
与一个进程相关联的ID有6个或更多:
通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。当执行一个程序文件时,进程的有效用户ID通常就实际用户ID,有效组ID通常就是实际组ID.
(***) 但是可以在文件模式字(st_mode)中设置一个特殊标志,其含义是”执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)“。与此类似,在文件模式字中可以设置另一位,它将执行此文件的进程的有效组ID设置文件的组所有者ID(st_gid)。在文件模式字中的这两位被称为设置用户ID(set-user-ID)位
和设置组ID(set-group-ID)位
.
文件访问权限
所有文件类型(目录、字符特别文件等)都有访问权限(access permission)。很多人认为只要普通文件有访问权限,这是一种误解。
每个文件有9个访问权限位,可将它们分为3类:
(***)进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试,内核进行的测试具体如下:
- 若进程的有效用户是0(超级用户),则允许访问。
- 若进程的有效用户ID等于文件的所有者(也就是进程拥有此文件),那么如果所有者适当的访问权限位被设置,则允许访问(比如要对文件进行写操作,则该进程有效用户应该有写权限)
- 若进程的有效组ID或进程的附属组ID之一等于文件的组ID,那么如果组适当的访问权限位被设置,则允许访问;否则拒绝访问
注意,上述测试顺序执行,即如果进程拥有此文件(第2步),则按用户权限访问批准或拒绝该进程对文件的访问—不查看组访问权限。
新文件和目录的所有权
新文件的用户ID设置为进程的有效用户ID。关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID。
- 新文件的组ID可以是进程的有效组ID
- 新文件的组ID可以是它所在目录的组ID
函数access和faccessat
有时,进程也希望按其实际用户ID和实际组ID来测试其访问能力。例如,当一个进程使用设置用户ID或设置组ID功能作为另一个用户(或组)运行时,就可能会有这种需要。
通过access
和faccessat
可以判断实际用户是否有相应权限:
#include<unistd.h>
int access(const char* pathname,int mode);
int faccessat(int fd,const char* pathname,int mode,int flag);
其中,如果测试文件是否存在,mode
就为F_OK
;否则mode
是下表所列常量的按位或:
mode | 说明 |
---|---|
R_OK | 测试读权限 |
W_OK | 测试写权限 |
X_OK | 测试执行权限 |
实例:access函数实例
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#define oops(m,x) { perror(m); exit(x); }
int main(int ac,char* av[])
{
if(ac!=2){
fprintf(stderr,"usage: a.out <pathname>");
exit(1);
}
if(access(av[1],R_OK)<0){ //测试读权限
printf("read access ERROR\n");
}
else{
printf("read access OK\n");
}
if(open(av[1],O_RDONLY)<0){
oops("open",2);
}
else{
printf("open for reading OK\n");
}
exit(0);
}
函数umask
umask
函数为进程设置文件模式创建屏蔽字,并返回之前的值。
#include<sys/stat.h>
mode_t umask(mode_t cmask);
实例:umask函数实例
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
int main(void)
{
umask(0);
if(creat("foo",RWRWRW)<0)
printf("creat error\n");
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //umask值禁止所有组和其他用户的读写权限
if(creat("bar",RWRWRW)<0)
printf("creat error\n");
exit(0);
}
需要注意的是,更改进程的文件模式创建屏蔽字并不影响其父进程(常常是shell)的屏蔽字
函数chmod、fchmod和fchmodat
chmod
,fchmod
和fchmodat
这3个函数使我们可以更改现有文件的访问权限
#include<sys/stat.h>
int chmod(const char* pathname,mode_t mode);
int fchmod(int fd,mode_t mode);
int fchmodat(int fd,const char* pathname,mode_t mode,int flag);
其中参数mode
是前面的文件权限位。且为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者进程必须具有超级用户权限。
粘着位
S_ISVTX
位被称为粘着位
(sticky bit).后来的UNIX
版本称它为保存正文位
.
现在的系统扩展了粘着位的使用范围,Single UNIX Specification
允许对目录设置粘着位。如果对一个目录设置了粘着位,只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:
- 拥有此文件
- 拥有此目录
- 是超级用户
目录/tmp
和/var/tmp
是设置粘着位的典型候选者————任何用户都可在这两个目录中创建文件。任一用户(用户、组合其他)对这两个目录的权限通常都是读、写和执行。但是用户不应能删除或重命名属于其他人的文件,为此在这两个目录的文件模式中都设置了粘着位。