| .. SPDX-License-Identifier: GPL-2.0 |
| |
| Extended Attributes |
| ------------------- |
| |
| Extended attributes (xattrs) are typically stored in a separate data |
| block on the disk and referenced from inodes via ``inode.i_file_acl*``. |
| The first use of extended attributes seems to have been for storing file |
| ACLs and other security data (selinux). With the ``user_xattr`` mount |
| option it is possible for users to store extended attributes so long as |
| all attribute names begin with “user”; this restriction seems to have |
| disappeared as of Linux 3.0. |
| |
| There are two places where extended attributes can be found. The first |
| place is between the end of each inode entry and the beginning of the |
| next inode entry. For example, if inode.i\_extra\_isize = 28 and |
| sb.inode\_size = 256, then there are 256 - (128 + 28) = 100 bytes |
| available for in-inode extended attribute storage. The second place |
| where extended attributes can be found is in the block pointed to by |
| ``inode.i_file_acl``. As of Linux 3.11, it is not possible for this |
| block to contain a pointer to a second extended attribute block (or even |
| the remaining blocks of a cluster). In theory it is possible for each |
| attribute's value to be stored in a separate data block, though as of |
| Linux 3.11 the code does not permit this. |
| |
| Keys are generally assumed to be ASCIIZ strings, whereas values can be |
| strings or binary data. |
| |
| Extended attributes, when stored after the inode, have a header |
| ``ext4_xattr_ibody_header`` that is 4 bytes long: |
| |
| .. list-table:: |
| :widths: 8 8 24 40 |
| :header-rows: 1 |
| |
| * - Offset |
| - Type |
| - Name |
| - Description |
| * - 0x0 |
| - \_\_le32 |
| - h\_magic |
| - Magic number for identification, 0xEA020000. This value is set by the |
| Linux driver, though e2fsprogs doesn't seem to check it(?) |
| |
| The beginning of an extended attribute block is in |
| ``struct ext4_xattr_header``, which is 32 bytes long: |
| |
| .. list-table:: |
| :widths: 8 8 24 40 |
| :header-rows: 1 |
| |
| * - Offset |
| - Type |
| - Name |
| - Description |
| * - 0x0 |
| - \_\_le32 |
| - h\_magic |
| - Magic number for identification, 0xEA020000. |
| * - 0x4 |
| - \_\_le32 |
| - h\_refcount |
| - Reference count. |
| * - 0x8 |
| - \_\_le32 |
| - h\_blocks |
| - Number of disk blocks used. |
| * - 0xC |
| - \_\_le32 |
| - h\_hash |
| - Hash value of all attributes. |
| * - 0x10 |
| - \_\_le32 |
| - h\_checksum |
| - Checksum of the extended attribute block. |
| * - 0x14 |
| - \_\_u32 |
| - h\_reserved[3] |
| - Zero. |
| |
| The checksum is calculated against the FS UUID, the 64-bit block number |
| of the extended attribute block, and the entire block (header + |
| entries). |
| |
| Following the ``struct ext4_xattr_header`` or |
| ``struct ext4_xattr_ibody_header`` is an array of |
| ``struct ext4_xattr_entry``; each of these entries is at least 16 bytes |
| long. When stored in an external block, the ``struct ext4_xattr_entry`` |
| entries must be stored in sorted order. The sort order is |
| ``e_name_index``, then ``e_name_len``, and finally ``e_name``. |
| Attributes stored inside an inode do not need be stored in sorted order. |
| |
| .. list-table:: |
| :widths: 8 8 24 40 |
| :header-rows: 1 |
| |
| * - Offset |
| - Type |
| - Name |
| - Description |
| * - 0x0 |
| - \_\_u8 |
| - e\_name\_len |
| - Length of name. |
| * - 0x1 |
| - \_\_u8 |
| - e\_name\_index |
| - Attribute name index. There is a discussion of this below. |
| * - 0x2 |
| - \_\_le16 |
| - e\_value\_offs |
| - Location of this attribute's value on the disk block where it is stored. |
| Multiple attributes can share the same value. For an inode attribute |
| this value is relative to the start of the first entry; for a block this |
| value is relative to the start of the block (i.e. the header). |
| * - 0x4 |
| - \_\_le32 |
| - e\_value\_inum |
| - The inode where the value is stored. Zero indicates the value is in the |
| same block as this entry. This field is only used if the |
| INCOMPAT\_EA\_INODE feature is enabled. |
| * - 0x8 |
| - \_\_le32 |
| - e\_value\_size |
| - Length of attribute value. |
| * - 0xC |
| - \_\_le32 |
| - e\_hash |
| - Hash value of attribute name and attribute value. The kernel doesn't |
| update the hash for in-inode attributes, so for that case this value |
| must be zero, because e2fsck validates any non-zero hash regardless of |
| where the xattr lives. |
| * - 0x10 |
| - char |
| - e\_name[e\_name\_len] |
| - Attribute name. Does not include trailing NULL. |
| |
| Attribute values can follow the end of the entry table. There appears to |
| be a requirement that they be aligned to 4-byte boundaries. The values |
| are stored starting at the end of the block and grow towards the |
| xattr\_header/xattr\_entry table. When the two collide, the overflow is |
| put into a separate disk block. If the disk block fills up, the |
| filesystem returns -ENOSPC. |
| |
| The first four fields of the ``ext4_xattr_entry`` are set to zero to |
| mark the end of the key list. |
| |
| Attribute Name Indices |
| ~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Logically speaking, extended attributes are a series of key=value pairs. |
| The keys are assumed to be NULL-terminated strings. To reduce the amount |
| of on-disk space that the keys consume, the beginning of the key string |
| is matched against the attribute name index. If a match is found, the |
| attribute name index field is set, and matching string is removed from |
| the key name. Here is a map of name index values to key prefixes: |
| |
| .. list-table:: |
| :widths: 16 64 |
| :header-rows: 1 |
| |
| * - Name Index |
| - Key Prefix |
| * - 0 |
| - (no prefix) |
| * - 1 |
| - “user.” |
| * - 2 |
| - “system.posix\_acl\_access” |
| * - 3 |
| - “system.posix\_acl\_default” |
| * - 4 |
| - “trusted.” |
| * - 6 |
| - “security.” |
| * - 7 |
| - “system.” (inline\_data only?) |
| * - 8 |
| - “system.richacl” (SuSE kernels only?) |
| |
| For example, if the attribute key is “user.fubar”, the attribute name |
| index is set to 1 and the “fubar” name is recorded on disk. |
| |
| POSIX ACLs |
| ~~~~~~~~~~ |
| |
| POSIX ACLs are stored in a reduced version of the Linux kernel (and |
| libacl's) internal ACL format. The key difference is that the version |
| number is different (1) and the ``e_id`` field is only stored for named |
| user and group ACLs. |