Product SiteDocumentation Site

Chapter 8.  Extended Attributes

8.1. Shortform Attributes
8.2. Leaf Attributes
8.3. Node Attributes
8.4. B+tree Attributes
Extended attributes implement the ability for a user to attach name:value pairs to inodes within the XFS filesystem. They could be used to store meta-information about the file.
The attribute names can be up to 256 bytes in length, terminated by the first 0 byte. The intent is that they be printable ASCII (or other character set) names for the attribute. The values can be up to 64KB of arbitrary binary data. Some XFS internal attributes (eg. parent pointers) use non-printable names for the attribute.
Access Control Lists (ACLs) and Data Migration Facility (DMF) use extended attributes to store their associated metadata with an inode.
XFS uses two disjoint attribute name spaces associated with every inode. They are the root and user address spaces. The root address space is accessible only to the superuser, and then only by specifying a flag argument to the function call. Other users will not see or be able to modify attributes in the root address space. The user address space is protected by the normal file permissions mechanism, so the owner of the file can decide who is able to see and/or modify the value of attributes on any particular file.
To view extended attributes from the command line, use the getfattr command. To set or delete extended attributes, use the setfattr command. ACLs control should use the getfacl and setfacl commands.
XFS attributes supports three namespaces: "user", "trusted" (or "root" using IRIX terminology) and "secure".
The location of the attribute fork in the inode's literal area is specified by the 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.
The following four sections describe each of the on-disk formats.

8.1.  Shortform Attributes

When the all extended attributes can fit within the inode's attribute fork, the inode's di_aformat is set to "local" and the attributes are stored in the inode's literal area starting at offset di_forkoff * 8.
Shortform attributes use the following structures:
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;
64
  • 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".

xfs_db Example:

A file is created and two attributes are set:
# setfattr -n user.empty few_attr
# setfattr -n trusted.trust -v val1 few_attr
Using xfs_db, we dump the inode:
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"
We can determine the actual inode offset to be 220 (15 x 8 + 100) or 0xdc.
Examining the raw dump, the second attribute is highlighted:
code65
Adding another attribute with attr1, the format is converted to extents and 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]
Performing the same steps with attr2, adding one attribute at a time, you can see 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"
Attribute added:
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"
Another attribute is added:
code66
One more is added:
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"
A raw dump is shown to compare with the attr1 dump on a prior page, the header is highlighted:
code67
It can be clearly seen that attr2 allows many more attributes to be stored in an inode before they are moved to another filesystem block.
MediaWiki Appliance - Powered by TurnKey Linux