扩展属性¶
扩展属性 (xattrs) 通常存储在磁盘上的一个单独数据块中,并通过 inode.i_file_acl*
从 inode 引用。扩展属性的首次使用似乎是为了存储文件 ACL 和其他安全数据 (selinux)。通过 user_xattr
挂载选项,只要所有属性名称以“user”开头,用户就可以存储扩展属性;此限制似乎已在 Linux 3.0 中消失。
扩展属性可以在两个地方找到。第一个地方是每个 inode 条目的末尾和下一个 inode 条目的开头之间。例如,如果 inode.i_extra_isize = 28 且 sb.inode_size = 256,则有 256 - (128 + 28) = 100 字节可用于 inode 内部的扩展属性存储。扩展属性可以找到的第二个地方是由 inode.i_file_acl
指向的块中。截至 Linux 3.11,此块不可能包含指向第二个扩展属性块(甚至一个簇的其余块)的指针。理论上,每个属性的值都可以存储在单独的数据块中,但截至 Linux 3.11,代码不允许这样做。
键通常假定为 ASCIIZ 字符串,而值可以是字符串或二进制数据。
扩展属性在 inode 之后存储时,有一个 4 字节长的头 ext4_xattr_ibody_header
偏移量 |
类型 |
名称 |
描述 |
---|---|---|---|
0x0 |
__le32 |
h_magic |
用于标识的魔数,0xEA020000。此值由 Linux 驱动程序设置,但 e2fsprogs 似乎没有检查它(?) |
扩展属性块的开头是 struct ext4_xattr_header
,它长 32 字节
偏移量 |
类型 |
名称 |
描述 |
---|---|---|---|
0x0 |
__le32 |
h_magic |
用于标识的魔数,0xEA020000。 |
0x4 |
__le32 |
h_refcount |
引用计数。 |
0x8 |
__le32 |
h_blocks |
使用的磁盘块数量。 |
0xC |
__le32 |
h_hash |
所有属性的哈希值。 |
0x10 |
__le32 |
h_checksum |
扩展属性块的校验和。 |
0x14 |
__u32 |
h_reserved[3] |
零。 |
校验和是根据文件系统 UUID、扩展属性块的 64 位块号以及整个块(头 + 条目)计算的。
在 struct ext4_xattr_header
或 struct ext4_xattr_ibody_header
之后是一个 struct ext4_xattr_entry
数组;这些条目中的每一个至少长 16 字节。当存储在外部块中时,struct ext4_xattr_entry
条目必须按排序顺序存储。排序顺序是 e_name_index
,然后是 e_name_len
,最后是 e_name
。存储在 inode 内部的属性不需要按排序顺序存储。
偏移量 |
类型 |
名称 |
描述 |
---|---|---|---|
0x0 |
__u8 |
e_name_len |
名称长度。 |
0x1 |
__u8 |
e_name_index |
属性名称索引。下面有关于此的讨论。 |
0x2 |
__le16 |
e_value_offs |
此属性值在存储它的磁盘块上的位置。多个属性可以共享相同的值。对于 inode 属性,此值是相对于第一个条目开始的;对于块,此值是相对于块开始(即头部)的。 |
0x4 |
__le32 |
e_value_inum |
存储值的 inode。零表示值与此条目在同一块中。此字段仅在启用 INCOMPAT_EA_INODE 功能时使用。 |
0x8 |
__le32 |
e_value_size |
属性值长度。 |
0xC |
__le32 |
e_hash |
属性名称和属性值的哈希值。内核不更新 inode 内部属性的哈希值,因此在这种情况下,此值必须为零,因为 e2fsck 会验证任何非零哈希值,无论 xattr 位于何处。 |
0x10 |
char |
e_name[e_name_len] |
属性名称。不包含尾随 NULL。 |
属性值可以跟在条目表的末尾。似乎要求它们对齐到 4 字节边界。这些值从块的末尾开始存储,并向 xattr_header/xattr_entry 表的方向增长。当两者冲突时,溢出部分会放入一个单独的磁盘块中。如果磁盘块填满,文件系统将返回 -ENOSPC。
ext4_xattr_entry
的前四个字段设置为零,以标记键列表的结束。
属性名称索引¶
从逻辑上讲,扩展属性是一系列键值对。键假定为以 NULL 结尾的字符串。为了减少键占用的磁盘空间量,键字符串的开头会与属性名称索引进行匹配。如果找到匹配项,则设置属性名称索引字段,并从键名中删除匹配的字符串。以下是名称索引值到键前缀的映射:
名称索引 |
键前缀 |
---|---|
0 |
(无前缀) |
1 |
“user.” |
2 |
“system.posix_acl_access” |
3 |
“system.posix_acl_default” |
4 |
“trusted.” |
6 |
“security.” |
7 |
“system.” (仅限 inline_data?) |
8 |
“system.richacl” (仅限 SuSE 内核?) |
例如,如果属性键是“user.fubar”,则属性名称索引设置为 1,并且“fubar”名称记录在磁盘上。
POSIX ACL¶
POSIX ACL 存储在 Linux 内核(和 libacl)内部 ACL 格式的简化版本中。主要区别在于版本号不同 (1),并且 e_id
字段仅针对命名的用户和组 ACL 存储。