btrfs xattr
linux 文件系统扩展属性 xattr
扩展属性(Extended Attributes,简称 xattr)支持给文件添加用户自定义的额外属性(Key-value 的键值对)
setfattr -n user.testattr -v abc test #设置test文件user.testattr属性为"abc"
getfattr -n user.testattr test # 读取test文件的user.testattr属性
setfattr -x user.testattr test # 删除test文件的user.testattr属性
attr -lq test # 列举test文件的属性
Btrfs Design
Btrees Introduction
在 btrfs(B-tree File System) 文件系统中,所有的 metadata 都由 BTree 管理。
文件系统由树木森林构造,superblock 指向构成文件系统的所有 B 树:
- FS Tree:管理文件相关的元数据,如 inode,dir 等;
- Chunk tree:管理磁盘设备,每一个磁盘设备都在 Chunk Tree 中有一个 item ;
- Extent Tree:管理磁盘空间分配,btrfs 每分配一段磁盘空间,便将该磁盘空间的信息插入到 Extent tree。通过查询 Extent Tree 得到空闲的磁盘空间信息;
- Checksum Tree:保存数据块的校验和。
Btree Data structure
以 FS Tree 为例:
结点分为内部结点和叶子结点
内部结点只用于查找,不存储数据,仅仅持有[key,block-pointer]对。
叶子结点持有[item,data]键值对。叶子结点分为 header
、item
数组和 data
数组两个部分。item
由头向尾生长,data
由尾向头生长。根据 item
中的 offset
和 size
字段可以找到其对应的 data
。item
中的 key
标识一个唯一的 item
。
文件系统由 object 构成(文件、目录等),每个 object 有自己的 64bit object_id
。每个 object 可以有若干不同类型的 item
,item
的 key
由三部分构成:object_id
、type
标识 item
的类型,offset
标识 item
在 object 中的偏移量。
文件 object:
item | 功能 | Key type | Key offset |
---|---|---|---|
Inode item | inode | BTRFS_INODE_ITEM_KEY | 0 |
extent data item | 存储文件数据 | BTRFS_EXTENT_DATA_KEY | 该数据块在文件中的 offset |
… |
目录 object:
item | 功能 | Key type | Key offset |
---|---|---|---|
Inode item | inode | BTRFS_INODE_ITEM_KEY | 0 |
dir item | 根据文件名查找文件的 object_id | BTRFS_DIR_ITEM_KEY | 64 bit filename hash |
index item | 文件遍历 | BTRFS_DIR_INDEX_KEY | Inode Sequence number |
Xattr item | 扩展属性 | BTRFS_XATTR_ITEM_KEY | 64 bit filename hash |
… |
btrfs xattr 支持
Btrfs 将扩展属性以 Xattr item 形式维护,存储在目录的 items 中,类似于一个目录项,复用 dir item 数据结构。
Struct btrfs_disk_key | transid | data_len | name_len | type | name | data |
---|---|---|---|---|---|---|
obj_id | BTRFS_XATTR_ITEM_KEY | hash(“test”) | 3 | 13 | BTRFS_FT_XATTR |
btrfs_setxattr
、btrfs_getxattr
、btrfs_removexattr
、btrfs_listxattr
分别对应设置扩展属性、读取扩展属性、删除扩展属性、列举扩展属性。
以 btrfs_setxattr
为例:
btrfs_insert_xattr_item:
leaf = path->nodes[0];
btrfs_cpu_key_to_disk(&disk_key, &location);
// write key
btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR);
btrfs_set_dir_name_len(leaf, dir_item, name_len); // write name_len
btrfs_set_dir_transid(leaf, dir_item, trans->transid);
btrfs_set_dir_data_len(leaf, dir_item, data_len); // write data_len
name_ptr = (unsigned long)(dir_item + 1);
data_ptr = (unsigned long)((char *)name_ptr + name_len);
write_extent_buffer(leaf, name, name_ptr, name_len); // write name of xattr
write_extent_buffer(leaf, data, data_ptr, data_len); // write data of xattr
btrfs_mark_buffer_dirty(path->nodes[0]);
btrfs_match_dir_item_name:
//walks through all the entries in a dir item and finds one for a specific name
leaf = path->nodes[0];
dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
total_len = btrfs_item_size_nr(leaf, path->slots[0]);
while (cur < total_len) {
this_len = sizeof(*dir_item) +
btrfs_dir_name_len(leaf, dir_item) +
btrfs_dir_data_len(leaf, dir_item);
name_ptr = (unsigned long)(dir_item + 1);
if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) // find matched xattr
return dir_item;
cur += this_len;
dir_item = (struct btrfs_dir_item *)((char *)dir_item +
this_len);
}