《UNIX环境高级编程》笔记:第4章 文件和目录

“本章内容围绕stat函数,详细介绍了stat结构中的每一个成员。这使我们对UNIX文件和目录的各个属性都有所了解。我们讨论了文件和目录在文件系统中是如何设计的以及如何使用文件系统命名空间。对文件和目录的所有属性以及对文件和目录进行操作的所有函数的全面了解,对于UNIX编程是非常重要的。”


这章内容比较多,但并不难以理解。最难以理解的可能就是目录的执行权限,以及粘着位了。多花点时间看就行。

我看这章用了一个星期的业余时间,效率实在不怎么样。你只要比我快就OK啦。



4.2 函数stat、fstat、fstatat和lstat


4.3 文件类型

文件类型信息包含在stat结构的st_mode成员中。



*在命令行中,如果一条命令太长,可以在第一行输入反斜杠并按回车,然后在第二行继续输入。


4.4 设置用户ID和设置组ID

和这两个ID相关的有两个对象:操作者(进程),以及操作对象(文件)。

这里讲的是进程。

有两个文件的属性要拿到这里先讲,因为这两个属性会改变进程的属性。

当文件拥有“设置用户ID”属性时,执行此文件的进程的有效用户ID会变成此文件所有者的用户ID;

当文件拥有“设置组ID”属性时,执行此文件的进程的有效组ID会变成此文件的组所有者ID。


4.5 文件访问权限

这一节很短,但其内容非常重要,必须牢记。

文件所有者由stat结构中的st_uid指定,文件组所有者则由st_gid指定。

设置用户ID位及设置组ID位都包含在stat结构的st_mode值中。


我们用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行权限。

注意,对于目录的读权限和执行权限的意义是不同的。读权限允许我们读目录,获得在该目录中所有文件名的列表。当一个目录是我们要访问文件的路径名的一个组成部分时,对该目录的执行权限使我们可通过该目录寻找一个特定的文件名。

为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限。

为了删除一个现有文件,必须对包含该文件的目录具有写权限和执行权限。对该文件本身则不需要任何权限。


内核进行的文件操作权限测试具体步骤:

1) 若进程的有效用户ID是0(超级用户),则允许用户访问。这给予了超级用户对整个文件系统进行处理的最充分的自由。

2) 若进程的有效用户ID等于文件的所有者ID,那么如果所有者适当的访问权限被设置,则允许访问;否则拒绝访问。适当的访问权限是指,若进程为读而打开该文件,则用户读位应为1,以此类推。

3) 若进程的有效组ID或进程的附属组ID之一等于文件的组ID,那么如果适当的访问权限被设置,则允许访问;否则拒绝访问。

4) 若其他用户适当的访问权限被设置,则允许访问;否则拒绝访问。

注意,如果进程拥有此文件,则按用户访问权限批准或拒绝该进程对文件的访问——不查看组访问权限。以此类推。


4.6 新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID。

对于组ID,POSIX.1允许实现选择下列之一:

1) 新文件的组ID可以是进程的有效组ID;

2) 新文件的组ID可以是它所在目录的组ID。

对于Linux 3.2.0,默认情况下,新文件的组ID取决于它所在的目录的设置组ID位是否被设置。若被设置,则新文件的组ID为目录的组ID;否则新文件的组ID为进程的有效组ID。


4.7 函数access和faccessat

这两个函数默认按进程的实际用户ID和实际组ID对文件进行访问权限测试。


4.8 函数umask

此函数为进程设置文件模式创建屏蔽字,并返回之前的值。



有两点需要解释:

1) 进程创建新文件时,会指定新文件的访问权限。但并不是你指定了什么权限,文件最终就是什么权限。你指定的权限会先通过“文件模式创建屏蔽字”过滤,它没有屏蔽的部分才能留下来作为文件最终权限。如果你指定了用户读、写、执行,但屏蔽字里有用户写、执行,最终就只有用户读能留下来。

2) 此函数只对当前进程有效,如果你编写了一个程序,并在程序里改变了屏蔽字的值,不论在程序运行过程中还是运行完成后,控制台里的屏蔽字的值都不会发生变化。


由此,当我们编写创建新文件的程序时,为了确保指定的访问权限位已激活,就必须在程序运行时修改umask值。


4.9 函数chmod、fchmod和fchmodat

这三个函数更改文件的访问权限。

为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者该进程必须具有超级用户权限。


特殊规则:

1) Solaris等系统对用于普通文件的粘着位赋予了特殊含义,在这些系统上如果我们试图设置普通文件的粘着位,而且又没有超级用户权限,那么函数参数中的粘着位自动被清除。即只有超级用户才能设置普通文件的粘着位。(注意:Linux中粘着位对于普通文件并无意义。)

2) 如果新文件的组ID不等于进程的有效组ID或者进程附属组ID中的一个,而且进程没有超级用户权限,那么函数参数中的设置组ID位会被自动清除。这就防止了用户创建一个设置组ID文件,而该文件并非由该用户所属的组所拥有。

3) 如果没有超级用户权限的进程写一个文件,则文件的设置用户ID位和设置组ID位会被自动清除。(非POSIX.1规范,但被大多数系统采用。)

以上这些特殊规则都是为安全考虑而设的。


4.10 粘着位

S_ISVTX,粘着位(sticky bit)是以前的叫法,在现在的UNIX版中中,它被称为保存正文位(saved-text bit)。而这两种称呼都已失去了它原有的意义,现在粘着位对于普通文件无作用,只对目录有用。


如果对一个目录设置了粘着位,只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:1) 拥有此文件;2) 拥有此目录;3) 是超级用户。


因为对于一些临时目录,需要任何用户都可以在其中创建文件,但是用户不能删除或重命名其他人的文件。对这些临时目录设置粘着位正好能满足需求。


4.11 函数chown、fchown、fchownat和lchown

这些函数可用于更改文件的用户ID和组ID。

基于BSD的系统一直规定只有超级用户才能更改一个文件的所有者。这样做的原因是防止用户改变其文件的所有者从而摆脱磁盘空间限额对他们的限制。SystemV则允许任一用户更改他们所拥有的文件的所有者。Linux 3.2.0和Mac OS X 10.6.8则总对chown施加限制。

POSIX.1定义了常量_POSIX_CHOWN_RESTRICTED。当此常量有效时,不能更改其他用户文件的用户ID。你可以更改你所拥有的文件的组ID,但只能改到你所属的组。

如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置用户ID设设置组ID位都被清除。


4.12 文件长度

可以根据wc -c命令和du -s命令报告的文件长度和所用块数的不同,判断文件是否存在空洞。

可以认为read函数感知不到空洞。在空洞的位置,read函数读到的字节是0。


4.13 文件截断


4.14 文件系统





硬链接和符号链接的不同在于,硬链接这个概念被包含在文件系统底层,上层操作一般感觉不到;而符号链接位于文件系统上层,是文件类型的一种,上层能感觉得到。当然两者还有很多不同之处,这里就不细说了。

i节点包含了文件有关的所有信息:文件类型、文件访问权限位、文件长度和指向文件数据块的指针等。stat结构中的大多数信息都取自i节点。i节点编号的数据类型是ino_t。

因为目录项中的i节点编号指向同一文件系统中的相应i节点,一个目录项不能指向另一个文件系统的i节点。这就是ln命令不能跨越文件系统的原因。


* ln为创建链接命令。默认创建硬链接,带参数-s时创建符号链接。


4.15 函数link、linkat、unlink、unlinkat和remove

如果实现支持创建指向一个目录的硬链接,那么也仅限于超级用户才可以这么做。其理由是这样做可能在文件系统中形成循环,大多数处理文件系统的实用程序都不能处理这种情况。因此,很多文件系统实现不允许对目录的硬链接。


为了解除对文件的链接,必须对包含该目录项的目录具有写和执行权限。如果对该目录设置了粘着位,条件会更苛刻一些,具体参见粘着位那节(4.10)。


只有当链接计数达到0时,该文件的内容才可被删除。另一个条件也会阻止删除文件的内容——只要有进程打开了该文件,其内容也不能删除。关闭一个文件时,内核首先检查打开该文件的进程个数;如果这个计数达到0,内核再去检查其链接计数;如果计数也是0,那么就删除该文件的内容。

unlink的这种特性经常被程序用来确保即使是在程序崩溃时,它所创建的临时文件也不会遗留下来。即,创建一个文件后,立即调用unlink。


如果传入的是符号链接名,那么unlink删除该符号链接,而不是删除由该链接所引用的文件。给出符号链接名的情况下,没有一个函数能删除由该符号链接所引用的文件。


ISO C指定remove函数删除一个文件,这更改了UNIX历来使用的名字unlink,其原因是实现C标准的大多数非UNIX系统并不支持文件链接。


*在命令行中的&符号表示让程序在后台执行,比如"./a.out &"。输入命令之后不等待程序执行完毕,直接把控制权交给用户,用户可在后台执行该程序的同时执行其他命令。


4.16 函数rename和renameat


4.17 符号链接

引入符号链接的原因是为了避开硬链接的一些限制:

1) 硬链接通常要求链接和文件位于同一文件系统中;

2) 只有超级用户才能创建指向目录的硬链接(在底层文件系统支持的情况下)。


在图4-17中没有列出mkdir、mkinfo、mknod和rmdir这些函数,因为当路径名是符号链接时,它们都出错返回。

chown是否跟随符号链接取决于实现。在所有现代的系统中,chown函数都跟随符号链接。

若同时使用O_CREAT和O_EXCL调用open函数,且路径名是符号链接,open将出错返回,errno被设置为EEXIST。这种处理方式是为了封堵一个安全性漏洞,以防止具有特权的进程被诱骗写错误的文件。


4.18 创建和读取符号链接

symlink,symlinkat函数创建一个符号链接。

因为open函数跟随符号链接,所以需要有一种方法打开该链接本身,并读该链接中的名字。readlink和readlinkat函数提供了这种功能。


4.19 文件的时间

修改时间(st_mtim)是文件内容最后一次被修改的时间。

状态更改时间(st_ctim)是该文件的i节点最后一次被修改的时间。

系统并不维护对一个i节点的最后一次访问时间,所以acess和stat函数并不更改这3个时间中的任何一个。

系统也不记录文件的创建时间,这和Windows系统有所不同。


4.20 函数futimens、utimensat和utimes

更改文件的访问时间和修改时间。

futimens和utimensat是POSIX.1标准,最高精度为纳秒;utimes只是SUS的XSI扩展,且精度最高为微秒。

用的地方不是太多,典型的例子是tar程序解包文件时还原文件的访问和修改时间。


4.21 函数mkdir、mkdirat和rmdir

当目录的链接计数为0,且还有进程打开了该目录时,在此目录下不能再执行其他操作。这是为了保证该目录为空,从而让目录被成功删除。


4.22 读目录

一个目录的写权限位和执行权限位决定了在该目录中能否创建新文件及删除文件,它们并不表示能否写目录本身。


对于程序4-22,有几个疑问:

1. 两次realloc都只是把长度*2,是否够用没有判断;

2. 为什么在dopath()返回错误时停止遍历目录?应该继续遍历才对啊;

3. 当传入的路径为/时,程序输出的路径以//开头,即多了一个/。


4.23 函数chdir、fchdir和getcwd

改变当前工作目录的操作也是仅限当前进程,因此为了改变shell进程自己的工作目录,shell应当直接调用chdir函数,为此,cd命令内建在shell中。


4.24 设备特殊文件

系统中与每个文件名关联的st_dev值是文件系统的设备号,该文件系统包含了这一文件名以及与其对应的i节点。

只有字符特殊文件和块特殊文件才有st_rdev值。此值包含实际设备的设备号。


*shell正则表达式: "/dev/tty[01]" 被展开为 "/dev/tty0 /dev/tty1"。


4.25 文件访问权限位小结


习题

4.4

如果用open或create创建已经存在的文件,则该文件的访问权限位不变。


4.5

目录的长度从来不会是0,因为它总是包含.和..两项。符号链接的长度指其路径名包含的字符数,由于路径名中至少有一个字符,所以长度也不为0。


4.6

要复制文件时保留空洞,必须获取i节点中数据块指针才能做到。此题放到之后再做吧。


4.7

原始文件的访问权限和用cat复制后的文件的权限没有任何关系。


4.8

du是按文件名来计算所用文件系统空间的。当文件名被移除后,文件数据仍然存在,直到程序返回。在这过程中,du感知不到tempfile的文件系统空间占用。


4.10

当目录层数(目录树深度)太多时,会无法打开更下层目录。


4.11

代码就不贴了,没什么难度。

运行时间对比见下表(使用time -p命令测量时间),修改后确实会快些:



原程序(使用完整路径):



修改后程序(使用当前工作目录+文件名)


4.12

chroot非常有用,要讲清楚需要较多文字,我还是贴链接吧:理解 chroot


4.16

内核对目录树的深度没有内在的限制,但是如果路径名的长度超出了PATH_MAX,则有许多命令会失败。

具体解释及测试程序源代码见附录答案。


4.17

/dev目录关闭了一般用户的写访问权限,以防止普通用户删除目录中的文件名。这就意味着unlink会失败。


结语

最后发表一下个人观点,拿Windows下NTFS的权限系统和UNIX的权限系统相比:

NTFS的权限划分得更细一些,操作空间更大一些,整个权限结构也更为规范一些。缺点是权限设置比较复杂。

UNIX的权限系统应该在早期比较简单,功能不多,但结构规范。但随着时间推移,出现了很多新的需求,也发现了原结构的很多漏洞。于是一边开始补漏,一边开始在原结构上硬塞一些新功能上去。以致形成现在特殊规则很多,许多功能和权限结构不搭调的局面。整体来说权限设置依旧比较简单。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值