0%

i-node

i 节点一般写作 i-node,中间有个短横线。i-node 表的编号开始于 1,因为 0 用来表示未使用的条目,然后 i-node 1 用来记录文件系统的坏块,i-node 2 用来记录根目录,即 /。由于可能有多个路径不同、名称不同的文件(通过硬链接)指向同一个 i-node,因此 i-node 中并不记录本文件的名称。

符号链接

符号链接的所有权和权限在大多数情况下会被忽略。仅在符号链接所在目录具有粘着位,又要删除或者重命名符号链接时,才会考虑符号链接本身的所有权。

符号链接解引用的次数是有限的,SUSv3 规定对路径中的每个符号链接部件至少 _POSIX_SYMLOOP_MAX 即 8 次。内核 2.6.18 之前,Linux 最多支持 5 次,2.6.18 时支持了 8 次。Linux 还规定一个完整路径中符号链接引用解除次数最多 40 次。

符号链接使用 symlink 创建,使用 readlink 读取。

linkunlink(不会解除符号链接引用)

link 创建(硬)链接,unlink 解除链接(也就是删除文件)。SUSv3 规定 link 要解符号链接引用,但是 Linux 并没有这样实现。现在 SUSv4 规定 link 调用是否解除链接由实现定义。

ACL 介绍

ACL 在 Linux 内核 2.6 被支持,是用文件的 扩展属性 实现的,扩展属性名为 system.posix_acl_access

要想在 ext2、ext3、ext4 或 reiserfs 文件系统上创建 ACL,装配相应的文件系统时需要带 mount –o acl 选项。

ACL 的功能是传统访问权限的超集,优先于传统访问权限生效。每个 ACL 项目(Access Control Entry,即 ACE)指定一个 rwx(3 个二进制位)的权限。其中:

  1. ACL_USER_OBJACL_GROUP_OBJACL_OTHER(在没有指定其他项目时)相当于传统权限,这个时候 ACL 被称为最小 ACL
  2. 支持对特定用户(ACL_USER)或者特定组(ACL_GRUOP)指定权限。
  3. 支持掩码。ACL_MASK 记录了可由 ACL_USERACL_GROUP_OBJ 以及 ACL_GROUP 型 ACE 所能授予的最高权限。但如果 ACL 含有标记类型为 ACL_USERACL_GROUP 的记录,那么就必须包含一条 ACL_MASK 型的 ACE。如果包含了掩码项目,ACL 就成为扩展 ACL
  4. ACL_USER_OBJACL_GROUP_OBJACL_OTHERACL_MASK 都只能出现一次。

ACL 权限匹配算法

ACL 的权限检查同样是具有短路特性的。

什么是扩展属性

扩展属性(Extended Attributes)可以将任何字符串键和任何数据值的信息和文件 inode 关联起来,值可以不是零结尾的字符串。其键的命名方式是 namespace.name,其中 namespace 只能是以下四种之一:

  1. user:权限管理方式和读写文件的内容是一样的。
  2. trusted:和 user 类似,但是需要管理员权限,即 CAP_SYS_ADMIN
  3. system:键的第二分量只能是内核认可的字符串。
  4. security:用来支持操作系统的安全模块。

在 ext2、ext3、ext4 或 Reiserfs 文件系统上,如欲将 user EA 与一文件关联,在装配底层文件系统时需带有 user_xattr 选项。 比如:

mount -o user_xattr <device> <directory>

Note

书上没有说扩展属性在 Linux 中是怎么实现的。

stat

可以用 stat 族系统调用来获取文件的属性。

utime 秒级时间戳更新

utime 族系统调用来设置文件的时间信息。修改时需要有相应的权限,以 utime 为例,其签名为:

int utime(const char *filename,
         const struct utimbuf *_Nullable times);

如果 times 参数为 NULL,则会将文件的 atime 和 mtime 修改为当前的时间。这需要进程的文件系统用户 ID 和文件匹配,且有写权限,或者是带有 CAP_FOWNER 或者 CAP_DAC_OVERRIDE 的特权级程序。

如果 times 参数非空,那么会用给定结构体的信息去更新 atime 和 mtime(ctime 信息是不能用系统调用任意修改的)。这需要进程的文件系统用户 ID 和文件匹配并有写权限,或者是带有 CAP_FOWNER 的特权级程序。

新建文件对应用户 ID 是进程(文件系统)有效用户 ID。

新建文件对应组 ID 在父目录 set-group-ID 未设置的情况下,是进程(文件系统)有效组 ID,否则是父目录的组 ID。除了显式设置父目录 set-group-ID 之外,使用 –o grpid 或者 –o bsdgroups 挂载也会将父目录的这一位置位。(挂载的默认行为是 –o nogrpid / –o sysvgroups,也就是 System V 行为。)

st_mode 的组成

st_mode 在 inode(7) 手册中的说明是 file type and mode,其组成是:文件类型 4 位 + 特殊 3 位 + 权限 9 位。

高 4 位是用来表示文件类型的(以下内容出自 man 7 inode),目前是有 7 种类型:

// 8 进制
S_IFMT     0170000   bit mask for the file type bit field

S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   FIFO

stat 结构中 st_mode 字段的低 12 位定义了文件权限。其中的前 3 位为专用位,分别是 set-user-ID 位、set-group-ID 位和 sticky 位(粘着位)。后面的 9 位分别是用户、组、其他的 rwx 权限。

Set-user-ID 位

如果可执行文件具有此位,则在执行时将进程的有效用户 ID 设置为可执行文件对应的用户 ID。

访问文件时,需要拥有对路径名所列所有目录的执行权限。例如,想读取文件 /home/mtk/x,则需拥有对目录 /、/home 以及 /home/mtk 的执行权限(还要有对文件 x 自身的读权限)。若当前的工作目录为 /home/mtk/sub1 ,访问相对路径名 ../sub2/x 时,需握有 /home/mtk 和 /home/mtk/sub2 这两个目录的可执行权限(不必有对 / 或 /home 的执行权限)。

拥有对目录的读权限,用户只是能查看目录中的文件列表。要想访问目录内文件的内容或是这些文件的 i 节点信息,还需握有对目录的执行权限。

umask

用来限制当前进程(和它将来的子进程)创建文件的访问权限。大多数 shell 的初始化文件会将 umask 默认置为八进制值 022 (—-w–w-),也就是说即便给了组和其他的写权限,实际创建出来的文件也是没有这两项权限的。

access

检查实际用户 / 组是否有文件的访问权限,但是不做实际访问。也能仅仅检查文件是否存在。

变种 faccessat 有更多功能,比如有选项支持对有效用户 / 组进行权限检查。

但是,因为检查文件和实际访问文件之间有滞后性(不是原子的),所以尽量避免“先使用 access 检查文件再访问”。

chmod

当非特权级(不具备 CAP_FSETID 能力的)进程调用 chmod 时,若文件的组 ID 不等于进程的有效组 ID 或是任一辅助组 ID,内核则总是清除文件的 set-group-ID 权限,这也是出于安全考虑。书中的例子:

简要介绍

这是一种 Linux 相对于 UNIX 上扩展的非标准功能,只在部分文件系统上支持。不过现代 BSD 上也有 chflags 命令和系统调用,功能有类似之处。

在程序中可利用 ioctl() 系统调用来获取并修改 i 节点标志;在终端则可以使用 lsattrchattr 命令操作文件属性。

ioctl 更新标志的例子:

inode 标记的功能

设备

设备分为两种:字符设备和块设备。每个设备都有主 ID 和辅 ID。主 ID 表示设备的一般等级,辅 ID 在主 ID 相同的设备中标识自己。每个设备驱动程序都是按照自己和主设备号的关联关系向内核注册的

在 Linux 的早期版本中,/dev 包含了系统中所有可能设备的条目,即使某些设备实际并未与系统连接。这意味着 /dev 会包含数以千计的未用设备项,从而导致了两个缺点:其一,对于需要扫描该目录内容的应用而言,降低了程序的执行速度;其二,根据该目录下的内容无法发现系统中实际存在哪些设备。Linux 2.6 运用 udev 程序解决了上述问题。该程序所依赖的 sysfs 文件系统,是装载于 /sys 下的伪文件系统,将设备和其他内核对象的相关信息导出至用户空间。

一些命令

创建文件系统组分的系统调用/命令基本上都是用 mk 开头。比如:

  1. mknod 可以创建 inode 结点,具体来说包括 socket、fifo、普通文件、字符设备、块设备。除了创建设备之外,一般都使用专门的系统调用不是直接用 mknod。有些 UNIX 实现用 mknod 来创建目录,但是 Linux 不这样做。
  2. mkdir 表示创建目录。
  3. mkfifo
  4. mkswap。这是用户空间命令,而不是一个系统调用。

文件系统

文件系统分配空间的基本单位是逻辑块,这个块大小可能和物理块不同。文件系统由引导块(第 1 块,不管这个磁盘是不是用来引导操作系统的)、超级块(第 2 块,包含和文件系统相关的诸多参数,比如 i 节点表的容量、文件系统逻辑块大小和文件系统大小)、i 节点表、数据块组成。