요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
extended attribute 저장 위치
1-28extended attribute(xattr)는 일반적으로 disk의 별도 data block에 저장하고 inode의 `inode.i_file_acl*`에서 참조합니다. 초기에는 file ACL과 SELinux 같은 security data를 저장하는 데 사용된 것으로 보입니다.
`user_xattr` mount option을 사용하면 사용자가 extended attribute를 저장할 수 있었습니다. 원래 모든 attribute name이 `user`로 시작해야 했지만 Linux 3.0부터 이 제한이 사라진 것으로 보입니다.
xattr를 찾을 수 있는 첫 번째 위치는 inode entry 끝과 다음 inode entry 시작 사이입니다. 예를 들어 `inode.i_extra_isize = 28`, `sb.inode_size = 256`이면 in-inode xattr에 사용할 수 있는 공간은 `256 - (128 + 28) = 100 bytes`입니다.
두 번째 위치는 `inode.i_file_acl`이 가리키는 block입니다. Linux 3.11 시점에는 이 block이 두 번째 xattr block이나 cluster의 나머지 block을 가리킬 수 없습니다. 이론상 attribute value마다 별도 data block을 사용할 수 있지만 당시 code는 허용하지 않았습니다.
key는 일반적으로 ASCIIZ string으로 간주하고 value는 string 또는 binary data일 수 있습니다.
inode 내부와 외부 block의 용량 및 제약을 비교합니다.
.. 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.
xattr header와 checksum
29-84inode 뒤에 저장한 xattr는 4-byte `ext4_xattr_ibody_header`로 시작합니다. offset `0x0`의 `__le32 h_magic`은 식별용 magic number `0xEA020000`입니다. Linux driver가 설정하지만 e2fsprogs는 이를 검사하지 않는 것으로 보인다는 주석이 있습니다.
외부 xattr block은 32-byte `struct ext4_xattr_header`로 시작합니다. `h_magic` 뒤에 reference count, 사용 disk block 수, 모든 attribute의 hash, xattr block checksum, 0으로 채운 reserved field가 이어집니다.
checksum은 filesystem UUID, xattr block의 64-bit block number, header와 entry를 포함한 block 전체를 대상으로 계산합니다.
외부 xattr block의 32-byte header field입니다.
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).
xattr entry, 정렬과 value 배치
85-147`struct ext4_xattr_header` 또는 `struct ext4_xattr_ibody_header` 뒤에는 `struct ext4_xattr_entry` 배열이 옵니다. 각 entry는 최소 16 bytes입니다.
external block의 entry는 `e_name_index`, `e_name_len`, `e_name` 순으로 비교한 정렬 순서를 지켜야 합니다. inode 내부 attribute는 정렬할 필요가 없습니다.
`e_value_offs`는 value가 있는 위치입니다. 여러 attribute가 같은 value를 공유할 수 있습니다. inode attribute에서는 첫 entry 시작을 기준으로 하고 block attribute에서는 header를 포함한 block 시작을 기준으로 합니다.
`e_value_inum`은 value를 저장한 inode를 나타냅니다. 0이면 entry와 같은 block에 value가 있습니다. 이 field는 `INCOMPAT_EA_INODE` feature가 활성화됐을 때만 사용합니다.
`e_hash`는 attribute name과 value의 hash입니다. kernel은 in-inode attribute의 hash를 갱신하지 않으므로 이 경우 0이어야 합니다. `e2fsck`가 xattr 위치와 관계없이 0이 아닌 hash를 검증하기 때문입니다. `e_name`은 trailing NULL을 포함하지 않습니다.
attribute value는 entry table 뒤에 올 수 있고 4-byte boundary에 align해야 하는 것으로 보입니다. value는 block 끝에서 xattr header와 entry table 방향으로 자랍니다. 두 영역이 충돌하면 overflow를 별도 disk block에 두며 block도 가득 차면 filesystem이 `-ENOSPC`를 반환합니다.
key list의 끝은 `ext4_xattr_entry` 첫 네 field를 모두 0으로 설정해 표시합니다.
최소 16-byte entry와 가변 name field입니다.
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.
name index 압축과 POSIX ACL
148-191논리적으로 xattr는 `key=value` 쌍의 연속입니다. key는 NULL-terminated string으로 간주합니다. key가 차지하는 on-disk 공간을 줄이기 위해 key 시작 부분을 attribute name index의 prefix와 비교합니다.
prefix가 일치하면 `e_name_index`를 설정하고 일치한 문자열은 disk에 기록하는 key name에서 제거합니다. 예를 들어 `user.fubar`는 index 1을 저장하고 name에는 `fubar`만 기록합니다.
name index 0은 prefix가 없습니다. 1은 `user.`, 2는 `system.posix_acl_access`, 3은 `system.posix_acl_default`, 4는 `trusted.`, 6은 `security.`, 7은 `system.`이며 inline_data 전용일 수 있습니다. 8은 `system.richacl`이며 SuSE kernel 전용일 수 있습니다.
POSIX ACL은 Linux kernel과 libacl 내부 ACL format을 축약한 형태로 저장합니다. 핵심 차이는 version number가 1이고 `e_id` field는 이름이 지정된 user와 group ACL에만 저장한다는 점입니다.
index가 대체하는 key prefix를 원문 값 그대로 보존합니다.
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.
요약·해설
attributes.rst:1-191ext4 xattr는 inode 내부 여유 공간 또는 `inode.i_file_acl`이 가리키는 외부 block에 저장됩니다. header, 정렬된 entry, block 끝에서 역방향으로 자라는 value 영역으로 구성하며 name index가 `user.`, `security.` 같은 prefix를 압축합니다.
inode에서 key와 value를 찾는 기본 순서입니다.