getfattr command. To set or delete extended attributes, use the setfattr command. ACLs control should use the getfacl and setfacl commands.
di_forkoff value in the inode's core. If this value is zero, the inode does not contain any extended attributes. Non-zero, the byte offset into the literal area = di_forkoff * 8, which also determines the 2048 byte maximum size for an inode. Attributes must be allocated on a 64-bit boundary on the disk except shortform attributes (they are tightly packed). To determine the offset into the inode itself, add 100 (0x64) to di_forkoff * 8.
di_aformat is set to "local" and the attributes are stored in the inode's literal area starting at offset di_forkoff * 8.
typedef struct xfs_attr_shortform {
struct xfs_attr_sf_hdr {
__be16 totsize;
__u8 count;
} hdr;
struct xfs_attr_sf_entry {
__uint8_t namelen;
__uint8_t valuelen;
__uint8_t flags;
__uint8_t nameval[1];
} list[1];
} xfs_attr_shortform_t;
typedef struct xfs_attr_sf_hdr xfs_attr_sf_hdr_t;
typedef struct xfs_attr_sf_entry xfs_attr_sf_entry_t;

namelen and valuelen specify the size of the two byte arrays containing the name and value pairs. valuelen is zero for extended attributes with no value.
nameval[] is a single array where it's size is the sum of namelen and valuelen. The names and values are not null terminated on-disk. The value immediately follows the name in the array.
flags specifies the namespace for the attribute (0 = "user"):
|
Flag
|
Description
|
|---|---|
XFS_ATTR_ROOT
|
The attribute's namespace is "trusted".
|
XFS_ATTR_SECURE
|
The attribute's namespace is "secure".
|
# setfattr -n user.empty few_attr # setfattr -n trusted.trust -v val1 few_attr
xfs_db> inode <inode#> xfs_db> p core.magic = 0x494e core.mode = 0100644 ... core.naextents = 0 core.forkoff = 15 core.aformat = 1 (local) ... a.sfattr.hdr.totsize = 24 a.sfattr.hdr.count = 2 a.sfattr.list[0].namelen = 5 a.sfattr.list[0].valuelen = 0 a.sfattr.list[0].root = 0 a.sfattr.list[0].secure = 0 a.sfattr.list[0].name = "empty" a.sfattr.list[1].namelen = 5 a.sfattr.list[1].valuelen = 4 a.sfattr.list[1].root = 1 a.sfattr.list[1].secure = 0 a.sfattr.list[1].name = "trust" a.sfattr.list[1].value = "val1"
0xdc.

di_forkoff remains unchanged (and all those zeros in the dump above remain unused):
xfs_db> inode <inode#> xfs_db> p ... core.naextents = 1 core.forkoff = 15 core.aformat = 2 (extents) ... a.bmx[0] = [startoff,startblock,blockcount,extentflag] 0:[0,37534,1,0]
di_forkoff change as attributes are added:
xfs_db> inode <inode#> xfs_db> p ... core.naextents = 0 core.forkoff = 15 core.aformat = 1 (local) ... a.sfattr.hdr.totsize = 17 a.sfattr.hdr.count = 1 a.sfattr.list[0].namelen = 10 a.sfattr.list[0].valuelen = 0 a.sfattr.list[0].root = 0 a.sfattr.list[0].secure = 0 a.sfattr.list[0].name = "empty_attr"
xfs_db> p ... core.naextents = 0 core.forkoff = 15 core.aformat = 1 (local) ... a.sfattr.hdr.totsize = 31 a.sfattr.hdr.count = 2 a.sfattr.list[0].namelen = 10 a.sfattr.list[0].valuelen = 0 a.sfattr.list[0].root = 0 a.sfattr.list[0].secure = 0 a.sfattr.list[0].name = "empty_attr" a.sfattr.list[1].namelen = 7 a.sfattr.list[1].valuelen = 4 a.sfattr.list[1].root = 1 a.sfattr.list[1].secure = 0 a.sfattr.list[1].name = "trust_a" a.sfattr.list[1].value = "val1"

xfs_db> p core.naextents = 0 core.forkoff = 10 core.aformat = 1 (local) ... a.sfattr.hdr.totsize = 69 a.sfattr.hdr.count = 4 a.sfattr.list[0].namelen = 10 a.sfattr.list[0].valuelen = 0 a.sfattr.list[0].root = 0 a.sfattr.list[0].secure = 0 a.sfattr.list[0].name = "empty_attr" a.sfattr.list[1].namelen = 7 a.sfattr.list[1].valuelen = 4 a.sfattr.list[1].root = 1 a.sfattr.list[1].secure = 0 a.sfattr.list[1].name = "trust_a" a.sfattr.list[1].value = "val1" a.sfattr.list[2].namelen = 6 a.sfattr.list[2].valuelen = 12 a.sfattr.list[2].root = 0 a.sfattr.list[2].secure = 0 a.sfattr.list[2].name = "second" a.sfattr.list[2].value = "second_value" a.sfattr.list[3].namelen = 6 a.sfattr.list[3].valuelen = 8 a.sfattr.list[3].root = 0 a.sfattr.list[3].secure = 1 a.sfattr.list[3].name = "policy" a.sfattr.list[3].value = "contents"
