UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID
在前面的两篇,我们了解了IO操作的一些基本操作函数,包括open、read和write。
在本篇我们来学习一下文件系统的其他特性和一个文件的属性,涉及的函数功能包括:
- 查看文件的所有属性;
- 改变文件所有者;
- 改变文件权限;
- 操作文件夹。
我们还会了解一些文件系统相关的数据结构和符号链接(symbolic link)。
1 函数stat、fstat、fstatat、lsat函数
#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 fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
//All four return: 0 if OK, −1 on error
函数功能:
stat返回pathname指定文件的信息;
fstat获取在文件描述符fd上打开的文件信息;
lstat的功能和stat函数类似,只是有一种情况有区别,当pathname指定的文件是一个符号链接的时候,lstat函数获取的文件信息是该符号链接的信息,而不是符号链接所指向文件的信息,在后面我们会了解更多关于符号链接的相关内容;
fstatat:文件描述符fd表示一个父目录,pathame指定父目录下得一个子目录,fstatat函数返回该子目录下的文件统计数。flag用来设置是否查询符号链接所指向的文件,如果flag设置为AT_SYMLINK_NOFOLLOW,则fstatat函数只返回该符号链接的相关信息,如果不设置该标志位,则返回该符号链接指向的文件的相关信息。
是一个关键的输出参数,上面的参数将文件属性填充值该结构体内。结构体如下图所示:

结构体中得每个成员都代表文件的某一个属性,我们会逐个了解属性的具体含义。
2 文件类型(File Type)
目前我们了解了两种文件:常规文件(regular files)和文件夹(directory)。
常见的几种文件类型包括:
- 常规文件(Regular File):包含某种组织形式的数据的文件,Linux并不关心数据是文本或是二进制(有一个例外,就是二进制可执行文件,内核必须知道该类型的格式才能执行),对数据的解析由应用程序负责。
- 目录文件(Directory):包含其他文件的名字,和指向这些文件信息的指针。进程可以读取目录文件的内容,但是只有内核才又写该类型文件的权限,进程通过调用本篇介绍的一些函数才操作修改目录文件,本质上仍然是内核修改。
- 块文件(Block Special File):如硬盘驱动,提供了固定大小缓存用于IO的文件类型。
- 字符文件(Charactor Special File):无缓存的对可变大小单元进行操作的文件类型。操作系统所有的设备都是块设备文件或字符设备文件。
- 先入先出队列(FIFO):进程间通信使用的文件类型。
- 套接字(Socket):网络或单机中的两个进程之间的通信使用的文件类型。
- 符号链接(Symbolic File):指向另一个文件的文件类型。
stat结构体中得st_mode制定了文件的类型。
Example:
#include "apue.h" int
main(int argc, char *argv[])
{
int i;
struct stat buf;
char *ptr; for (i = ; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < ) {
err_ret("lstat error");
continue;
} 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 = "character special";
else if (S_ISBLK(buf.st_mode))
ptr = "block special";
else if (S_ISFIFO(buf.st_mode))
ptr = "fifo";
else if (S_ISLNK(buf.st_mode))
ptr = "symbolic link";
else if (S_ISSOCK(buf.st_mode))
ptr = "socket";
else
ptr = "** unknown mode **"; printf("%s\n", ptr);
}
exit();
}
运行截图:

3 设置用户Id(User ID)和组Id(Group ID)
每个进程6个甚至更多的ID和它关联。如下表所示:

简要介绍ID的区别:
- real user ID和real group ID:当前登陆用户的ID和该用户所属的组ID;
- effective user ID、effective group ID和supplementary group IDs:文件的访问权限。
- saved set-user-ID和saved set-group-ID:当一个程序运行时,会拷贝effective user ID和effective group ID到这两个变量中,这两个ID和函数setuid相关。
一般情况下,effective user ID = real user ID,effective group ID = real group ID.
每个文件都由一个所有者,和一个组所有者,分别对应stat数据结构中得字段:st_uid和st_gid。
如果执行程序时,希望effective user(group) ID != real user(group) ID,即希望改变进程的访问资源的权限为文件所有者的访问权限,而不是真实用户的访问权限,可以通过设置mode中两个bit来实现将effective user(group) ID设置为文件所有者(组),这两个bit叫做:set-user-ID位和set-group-ID位。这两个位包含在stat数据结构中得st_mode字段中,可以通过函数S_ISUID和S_ISGID来测试。
4 文件访问权限(File Access Permissions)
stat数据结构中的st_mode字段中同样包含文件访问权限位。
每个文件有9中权限位:

这些标志位的使用需要注意的事项总结如下:
- 如果要打开某个文件,则该文件全路径上的所有文件夹都需要有执行权限(execute permission),该文件则需要对应的操作权限。需要可执行权限的原因是可以通过(pass through)它找到下一级目录或者文件;
- 读权限,允许打开某个已存在文件,标志位:O_RDONLY和O_RDWR;
- 写权限,允许向某个已存在文件写入数据,标志位:O_WRONLY和O_RDWR;
- 调用open函数时,如果要指定标志位O_TRUNC,需要写权限;
- 我们无法在某个文件夹下创建文件,除非我们对该文件夹有写权限(write permission)和执行权限(execute permission);
- 删除某个文件,需要该文件所在文件夹的写权限和执行权限,而不需要对该文件本身有写权限或读权限;
- 如果我们希望使用exec类函数执行某个文件,则必须有可执行权限,并且该文件必须为regular file。
文件访问权限检测流程:
- 如果进程的effective user ID是0,即超级用户,则允许访问;
- 如果进程的effective user ID是文件所有者的ID,即该进程是该文件的所有者(owner),并且相应的访问权限标志位被设置,则允许访问;
- 如果进程的effective group ID或者supplementary group IDs之一是文件所有组的ID,并且相应的访问权限标志位被设置,则允许访问;
- 如果相应的other访问权限标志位被设置,则允许访问。
简单来说,如果进程是该文件的拥有者,则访问是否允许取决于用户访问权限标志位,忽略组权限标志位;如果进程不是该文件的拥有者,但是该进程属于某个有访问权限的组,访问是否允许取决于组权限标志位的设置,忽略other访问权限标志位。
5 新文件和目录的所有权(ownership)
新文件的real user ID为创建该文件的进程的effective user ID。
新文件的real group ID的取值取决于:
- 新文件的real group ID可以是常见该文件的进程的effective group ID;
- 新文件的real group ID可以是该文件所在目录的real group ID。(必须保证该目录下面的所有文件的real group ID都是该目录的real group ID)。
6 函数access和faccessat
access和faccessat函数用于测试当前用户(real user,当前登录用户)对某一文件是否有某种操作权限。
函数声明:
#include <unistd.h>
int access(const char* pathname, int mode);
int faccessat(int fd, const char* pathname, int mode, int flag);
mode的可取值,当为F_OK,测试文件是否存在。

faccessat函数的相关细节:
- 当pathname为绝对路径,或者fd取值为AT_FDCWD并且pathname为相对路径时,功能和access相同。
- 否则,faccessat函数指定的工作目录为pathname指定的相对目录加上fd描述符指定的父目录。
- 当flag的值为AT_EACCESS时,测试时使用进程的effective user和group而不是当前real user ID和group。
Example:
#include "apue.h"
#include <fcntl.h> int
main(int argc, char *argv[])
{
if (argc != )
err_quit("usage: a.out <pathname>"); if (access(argv[], R_OK) < )
err_ret("access error for %s", argv[]);
else
printf("read access OK\n"); if (open(argv[], O_RDONLY) < )
err_ret("open error for %s", argv[]);
else
printf("open for reading OK\n"); exit();
}
测试:

由于本地mac环境搞不定,所以直接截书上的例子吧。
该例中,当切换到root用户,修改了该文件的所有者为root用户,并且设置了set-user-ID,这样当切换到别的用户时,仍可以以RDONLY方式打开文件,但是由于access是测试当前real user的读权限,当切换到其他用户时,access测试不通过,显示Permission denied。
好吧,直接忽略上面这个例子吧,我没能在自己的机器上重现,只是直接随便翻译书上某一段话。如果有某位高手看懂了,请指点。
小结
由于这一章内容比较多,所以打算用三篇来写,这是第一篇,主要介绍了stat函数,access函数,文件类型和各种令人糊涂的ID。
参考资料:
《Advanced Programming in the UNIX Envinronment 3rd》
UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID的更多相关文章
- UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流
1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...
- UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作
标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题. 1 ...
- UNIX高级环境编程1
UNIX高级环境编程1 故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表 ...
- UNIX高级环境编程(14)文件IO - O_DIRECT和O_SYNC详解 < 海棠花溪 >
春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动. 上周末在元大都遗址公园海棠花溪拍的海棠花. 进入正题. O_DIRECT和O_SYNC是系统调用open的flag参数.通过指定o ...
- UNIX高级环境编程(4)Files And Directories - umask、chmod、文件系统组织结构和链接
本篇主要介绍文件和文件系统中常用的一些函数,文件系统的组织结构和硬链接.符号链接. 通过对这些知识的了解,可以对Linux文件系统有更为全面的了解. 1 umask函数 之前我们已经了解了每个文件 ...
- Unix高级环境编程
[07] Unix进程环境==================================1. 进程终止 atexit()函数注册终止处理程序. exit()或return语句: ...
- UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数
本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件 1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...
- UNIX高级环境编程(16)文件系统 < 雨后 >
来点绿色放松一下眼睛吧 :) 文件系统是对文件和目录的组织集合. 一 设备文件 设备文件和系统的某个设备相对应. 设备驱动程序 处理设备的所有IO请求. 提供了一致的API接口,对应于系统调用的ope ...
- UNIX高级环境编程(12)进程关联(Process Relationships)- 终端登录过程 ,进程组,Session
在前面的章节我们了解到,进程之间是有关联的: 每个进程都有一个父进程: 子进程退出时,父进程可以感知并且获取子进程的退出状态. 本章我们将了解: 进程组的更多细节: sessions的内容: logi ...
随机推荐
- 《深入理解Java虚拟机》目录
第一部分 走进Java 第1章 走进Java 第二部分 自动内存管理机制 第2章 Java内存区域与内存溢出异常 2.2 运行时数据区域 2.3 HotSpot虚拟机对象探秘 第3章 垃圾收集器与 ...
- Joda-Time 学习笔记
一 Jode-Time 介绍 任何企业应用程序都需要处理时间问题.应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个时间点之间的路径.使用 JDK 完成这项任务将非常痛苦和繁琐.既然 ...
- SQL Server中使用表值函数
函数有很多限制,不能使用动态语句,不能使用临时表等等...细看一下,直接写语句就行了,不用动态语句 insert into @re select id,parid,@I from videoclass ...
- ASP.NET 省市县三联动 (包含用户控件)
将压缩文件下载解压后,将用户控件拖到解决方案里,直接可以拖到需要用到的页面里 使用: 数据库是最新的(父子级关系表结构----Region2016.sql) 右键记事本打开,放在sqlServerl里 ...
- 使用jQuery和CSS3制作数字时钟(jQuery篇) 附源码下载
HTML 和上一篇文章:使用jQuery和CSS3制作数字时钟(CSS3篇)一样的HTML结构,只是多了个>date用来展示日期和星期的. <div id="clock" ...
- Servlet 思维导图
- Oracle用户权限及死锁
Oracle用户权限表 oracle数据库中涉及到用户权限的三个表,dba_users,all_users,user_users有什么区别 dba_开头的是查全库所有的,all_开头的是查当前用户可以 ...
- 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop
PS: 我先旁观下大师们的讨论,得多看书了~ 别人说的:“看了一下不觉得评注对到哪里去,只有吹毛求疵之感. 比如同步异步介绍,本来就无大错:比如node图里面的OS operation,推敲一下就 ...
- js中list 和 map还有string的部分操作
1.创建list或者数组 var list = []; list中添加元素:list.push("hello"); 如果没有先定义为数组类型不能使用 push方法 判断list ...
- 【java错误】System.out.println()出错
今天想测试java的System的类,没想到居然出错了.在同一个包下的java文件System全错,而其他包中的System没错.上网查了下资料,原来我是重定义了System类,覆盖了原来的Syste ...