函数access和faccessat
当说明用open或creat创建文件时,没有说明赋予新文件的用户ID和组ID的值是什么。关于新目录的所有权与本节将说明的新文件的所有权的规则相同。
新文件的用户ID设置为用户的有效用户ID。关于组ID,POSIX.1允许选择下列之一作为新文件的组ID。
(1)新文件的组ID可以是进程的有效组ID。
(2)新文件的组ID可以是它所在目录的组ID。
在SVR4中,新文件的组ID取决于它所在的目录的设置-组-ID位是否设置。如果该目录的这一位已经设置,则新文件的组ID设置为目录的组ID;否则新文件的组ID设置为进程的有效组ID。
access函数
正如前面所说,当用open函数打开一个文件是,内核以进程的有效用户ID和有效组ID为基础执行其存取许可权测试。又是,进程也希望按照其实际用户ID和实际组ID来测试其存取能力。例如当一个进程使用设置-用户-ID,或者设置-组-ID特征作为另一个用户(或组)运行时,这就可能需要。即使一个进程可能已经设置-用户-ID为根,它仍可能想验证实际用户能够存取一个给定的文件。access函数是按实际用户ID和实际组ID进程存取许可权测试的。
chmod函数在下列条件下自动清除两个许可权位。
- 如果我们试图设置普通文件的粘住位(S_ISVTX),而且又没有超级用户优先权,那么mode中的粘住位自动被关闭.这意味着只有超级用户才能设置普通文件的粘住位。这样做的理由可以防止不怀好意的用户设置粘住位,并视图以此方式填满交换区(如果系统支持保存-正文特征的话)。
- 新创建文件的组ID可能不是调用进程所属的组。新文件的组ID可能是父目录的组ID。特别的,如果新文件的组ID不等于进程的有效组ID或者进程添加组ID中的一个,以及进程没有超级用户优先数,那么设置-组-ID为自动被关闭。这就防止了用户创建一个设置-组-ID文件,而该用户是由并非该用户所属的组拥有的。
4.3+BSD和其他伯克利导出的系统增加了另外的安全性特征以视图放置保护位的错误使用。如果一个没有超级用户优先权的进程写一个文件,则设置-用户-ID位和设置-组-ID为自动被清除。如果一个不怀好意的用户找到一个他可以写的设置-组-ID和设置-用户-ID文件,即使它可以修改此文件,但失去了对该文件的特别优先权。
基于伯克利的系统一直规定只有超级用户才能更改一个文件的所有者。这样做的原因是防止用户改变其文件的所有者从而摆脱磁盘空间限额对他们的限制。系统V则允许任一用户更改他们所拥有的文件的拥有者。
该常数可选的定义在头文件<unist.h>中,而且总是可以用pathconf或fpathconf函数查询。此选择项还与所引用的文件有关——可在每个文件系统基础上,使该选择项起作用或不起作用。在下文中如提及“若_POSIX_CHOWN_RESTRICTED起作用”,则表示这适用于我们正在谈及的文件,而不管该实际常数是否在文件中定义(例如,4.3+BSD总有这种限制,而并不在头文件中定义此常数)
若_POSIX_CHWON_RESTRICTED对指定的文件起作用,则
(1)只有超级用户进程才能更改该文件的用户ID
(2)若满足下列条件,一个非超级用户进程可以更改该文件的组ID
(a)进程拥有此文件(其有效用户ID等于该文件的用户ID)
(b)参数owner等于文件的用户ID,参数group等于进程的有效组ID或进程的添加组ID之一
文件截短
有时,我们需要在文件尾端截去一些数据以缩短文件.将一个文件的长度截短为0是一个特例,用O_TRUNC标志可以做到这一点.为了截短文埃及你可以调用函数truncate和ftruncate.
这两个韩式将由路径pathname或打开文件描述符fields指定的一个现存文件的长度截短为length.如果该文件以前的长度大于length,则超过length以外的数据就不能存取.如果以前的长度短于length,则后果与系统有关.如果某个实现的处理是扩展该文件,则在以前的文件尾端和新文件的尾端之间的数据将读作0(也就是在文件中创建了一个空洞)
文件系统
目前,正在使用的NUIX文件系统有多种实现.
我们可以把一个磁盘分成一个或多个分区.每个分区可以包含一个文件系统.i节点是固定长度的记录项,它包含有关文件的大部分信息.
-
在图中有两个目录项指向同一个i节点.每个i节点中都有一个链接计数,其值是指向该i节点的目录项数.只有当链接计数减少至0时,才可删除该文件(也就是可以释放该文件占用的数据块).这就是为什么"解除一个文件的链接"操作并不总是意味着"释放该文件占用的磁盘块"的原因.这也是为什么删除一个目录项的删除被称之为unlink而不是delete的原因.在stat结构中,链接计数包含在st_nlink成员中,其基本系统数据类型是nlink_t.这种链接被称为硬链接.
-
另外一种链接类型称为符号链接(synbolic link).符号链接文件的实际内容(在数据块中)包含了该符号链接所指向的文件的名字.
-
i节点包含了文件有关的所有信息:文件类型,文件访问权限位,文件长度和指向文件数据块的指针等.stat结构中的大多数信息都取自i节点.只有两项重要数据存放在目录项中:文件名和i节点编号.i节点编号的数据类型是ino_t.
-
因为目录项中的i节点编号指向同一文件系统中的相应i节点,一个目录项不能指向另一个文件系统的i节点.这就是为什么ln(1)命令不能跨越文件系统的原因.
-
当在不更换文件系统的情况下为一个文件重命名时,该文件的实际内容并不移动,只需构造一个指向现有i节点的新目录项,并删除老的目录项.链接计数并不会改变.例如,为将文件/usr/lib/foo重命名为/usr/foo,如果目录/usr/lib和/usr在同一文件系统中文件foo的内容无需移动.这就是mv(1)命令的通常操作方式.
函数link,linkat,unlink,unlinkat和remove
任何一个文件可以有多个目录指向其i节点.创建一个指向现有文件的链接的方法是使用link函数或linkat函数.
#include<unistd.h>
int link(const char * existingpath, const char * newpath);
int linkat(const efd,const char * existingpath,const nfd,const char * newpath,int flag);
两个函数的返回值:若成功,返回0;若出错,返回-1;
这两个函数创建一个新目录newpath,它引用现有文件existingpath.如果newpath已经存在,则返回出错.只创建newpath中的最后一个分量,路径中的其他部分应该已经存在.
对于linkat函数,吸纳有文件是通过efd和existingpath参数指定的,新的路径名是通过nfd和newpath参数指定的.默认情况下,如果两个路径名中的任一个是相对路径,那么它需要通过相对于相应的文件描述符进行计算.如果两个文件描述符中的任一个设置为AT_FDCWD,那么相应的路径名(如果它是相对路径)就通过相对于当前目录进行计算.如果任一路径名是绝对路径,响应的文件描述符参数就会被忽略.
只有当链接计数达到0时,该文件的内容才可被删除.另一个条件也会阻止文件的内容-只要有进程打开了该文件,其内容也不能删除.关闭一个文件时,内核首先检查打开该文件的进程个数;如果这个技术达到0,内核再去检查其链接计数;如果计数也是0,那么就删除该文件的内容.
unlink的这种特性经常被程序用来确保即使是在程序崩溃时,它所创建的临时文件也不会被留下来.进程用open或creat创建一个文件,然后立即调用unlink,因为该文件依旧是打开的,所以不会将其内容删除.只有当进程关闭该文件或中止时(在这种情况下,内核关闭该进程所打开的全部文件),该文件的内容才被删除.
函数rename或renameat
文件或目录可以用rename函数或者renameat函数进行重命名.
#include<stdio.h>
int rename(const char*oldname,const char*newname);
int renameat(int oldfd,const char*oldname,int newfd,const char *newname);