Fiemap Ioctl¶
fiemap ioctl 是用户空间获取文件范围映射的一种有效方法。 与逐块映射(如 bmap)不同,fiemap 返回范围列表。
请求基础¶
fiemap 请求在 struct fiemap
中编码
-
struct fiemap¶
文件范围映射
定义:
struct fiemap {
__u64 fm_start;
__u64 fm_length;
__u32 fm_flags;
__u32 fm_mapped_extents;
__u32 fm_extent_count;
struct fiemap_extent fm_extents[];
};
成员
fm_start
开始映射的字节偏移量(包括)(输入)
fm_length
用户空间所需的映射的逻辑长度(输入)
fm_flags
请求的 FIEMAP_FLAG_* 标志(输入/输出)
fm_mapped_extents
已映射的范围数(输出)
fm_extent_count
fm_extents 数组的大小(输入)
fm_extents
映射的范围数组(输出)
fm_start 和 fm_length 指定进程需要映射的文件中的逻辑范围。 返回的范围反映磁盘上的范围 - 也就是说,第一个返回范围的逻辑偏移量可能在 fm_start 之前开始,最后一个返回范围覆盖的范围可能在 fm_length 之后结束。 所有偏移量和长度均以字节为单位。
可以在 fm_flags 中设置某些标志来修改查找映射的方式。 如果内核不理解某些特定标志,它将返回 EBADR,并且 fm_flags 的内容将包含导致错误的标志集。 如果内核与传递的所有标志兼容,则 fm_flags 的内容将保持不变。 用户空间需要确定拒绝特定标志对其操作是否是致命的。 此方案旨在允许 fiemap 接口在未来增长,而不会失去与旧软件的兼容性。
fm_extent_count 指定 fm_extents[] 数组中可用于返回范围的元素数量。 如果 fm_extent_count 为零,则 fm_extents[] 数组将被忽略(不会返回任何范围),并且 fm_mapped_extents 计数将保存 fm_extents[] 中保存文件当前映射所需的范围数。 请注意,没有任何东西可以阻止文件在调用 FIEMAP 之间发生更改。
以下标志可以在 fm_flags 中设置
- FIEMAP_FLAG_SYNC
如果设置了此标志,内核将在映射范围之前同步文件。
- FIEMAP_FLAG_XATTR
如果设置了此标志,则返回的范围将描述 inode 的扩展属性查找树,而不是其数据树。
- FIEMAP_FLAG_CACHE
此标志请求缓存范围。
范围映射¶
范围信息在嵌入的 fm_extents 数组中返回,用户空间必须与 fiemap 结构一起分配该数组。 fiemap_extents[] 数组中的元素数量应通过 fm_extent_count 传递。 内核映射的范围数将通过 fm_mapped_extents 返回。 如果分配的 fiemap_extents 数小于映射请求范围所需的数量,则可以在 fm_extent[] 数组中映射的最大范围数将返回,并且 fm_mapped_extents 将等于 fm_extent_count。 在这种情况下,数组中的最后一个范围将不会完成请求的范围,并且不会设置 FIEMAP_EXTENT_LAST 标志(请参阅有关范围标志的下一节)。
每个范围都由单个 fiemap_extent 结构描述,如 fm_extents 中返回的
-
struct fiemap_extent¶
一个 fiemap 范围的描述
定义:
struct fiemap_extent {
__u64 fe_logical;
__u64 fe_physical;
__u64 fe_length;
__u32 fe_flags;
};
成员
fe_logical
文件中范围的字节偏移量
fe_physical
磁盘上范围的字节偏移量
fe_length
此范围的字节长度
fe_flags
此范围的 FIEMAP_EXTENT_* 标志
所有偏移量和长度均以字节为单位,并反映磁盘上的偏移量和长度。 范围的逻辑偏移量可以在请求之前开始,或者其逻辑长度可以扩展到请求之后,这是有效的。 除非返回 FIEMAP_EXTENT_NOT_ALIGNED,否则 fe_logical、fe_physical 和 fe_length 将与文件系统的块大小对齐。 除了标记为 FIEMAP_EXTENT_MERGED 的范围外,相邻的范围不会合并。
fe_flags 字段包含描述返回范围的标志。 特殊标志 FIEMAP_EXTENT_LAST 始终在文件的最后一个范围上设置,以便进行 fiemap 调用的进程可以确定何时没有更多范围可用,而无需再次调用 ioctl。
某些标志是有意模糊的,并且始终在存在其他更具体的标志时设置。 这样,寻找一般属性的程序不必知道所有现有和未来的标志,这些标志意味着该属性。
例如,如果设置了 FIEMAP_EXTENT_DATA_INLINE 或 FIEMAP_EXTENT_DATA_TAIL,则还将设置 FIEMAP_EXTENT_NOT_ALIGNED。 寻找内联或尾部打包数据的程序可以锁定特定标志。 但是,仅仅不希望尝试对未对齐范围进行操作的软件可以锁定 FIEMAP_EXTENT_NOT_ALIGNED,而不必担心所有现在和将来的可能暗示未对齐数据的标志。 请注意,相反的情况并非如此 - FIEMAP_EXTENT_NOT_ALIGNED 单独出现是有效的。
- FIEMAP_EXTENT_LAST
这通常是文件中的最后一个范围。 尝试映射超出此范围可能会返回空值。 某些实现设置此标志以指示此范围是用户查询的范围中的最后一个(通过 fiemap->fm_length)。
- FIEMAP_EXTENT_UNKNOWN
此范围的位置当前未知。 这可能表明数据存储在无法访问的卷上,或者尚未为该文件分配存储空间。
- FIEMAP_EXTENT_DELALLOC
这也将设置 FIEMAP_EXTENT_UNKNOWN。
延迟分配 - 虽然此范围存在数据,但尚未分配其物理位置。
- FIEMAP_EXTENT_ENCODED
此范围不包含普通文件系统块,而是经过编码(例如,加密或压缩)。 通过 I/O 读取到块设备中的此范围的数据将具有未定义的结果。
请注意,尝试通过写入指示的位置来就地更新数据,而无需文件系统的帮助,或者在使用 FIEMAP 接口返回的信息访问数据时,始终是未定义的,而文件系统已挂载。 换句话说,用户应用程序只能在文件系统卸载时通过 I/O 读取到块设备的范围数据,并且只有在 FIEMAP_EXTENT_ENCODED 标志已清除的情况下; 在任何其他情况下,用户应用程序都不得尝试通过块设备读取或写入文件系统。
- FIEMAP_EXTENT_DATA_ENCRYPTED
这也将设置 FIEMAP_EXTENT_ENCODED 此范围中的数据已由文件系统加密。
- FIEMAP_EXTENT_NOT_ALIGNED
不能保证范围偏移量和长度已对齐到块。
- FIEMAP_EXTENT_DATA_INLINE
这也将设置 FIEMAP_EXTENT_NOT_ALIGNED 数据位于元数据块中。
- FIEMAP_EXTENT_DATA_TAIL
这也将设置 FIEMAP_EXTENT_NOT_ALIGNED 数据与来自其他文件的数据打包到一个块中。
- FIEMAP_EXTENT_UNWRITTEN
未写入范围 - 已分配该范围,但其数据尚未初始化。 这表明如果通过文件系统读取范围的数据将全部为零,但如果直接从设备读取,则内容是未定义的。
- FIEMAP_EXTENT_MERGED
当文件不支持范围时,将设置此标志,即,它使用基于块的寻址方案。 由于为用户空间中的每个块返回一个范围将非常低效,因此内核将尝试将大多数相邻块合并为“范围”。
- FIEMAP_EXTENT_SHARED
设置此标志以请求与其他文件共享空间。
VFS -> 文件系统实现¶
希望支持 fiemap 的文件系统必须在其 inode_operations 结构上实现 ->fiemap 回调。 fs ->fiemap 调用负责定义其支持的 fiemap 标志集,并在每个发现的范围上调用一个帮助器函数
struct inode_operations {
...
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
->fiemap 传递 struct fiemap_extent_info
,它描述了 fiemap 请求
-
struct fiemap_extent_info¶
对文件系统的 fiemap 请求
定义:
struct fiemap_extent_info {
unsigned int fi_flags;
unsigned int fi_extents_mapped;
unsigned int fi_extents_max;
struct fiemap_extent __user *fi_extents_start;
};
成员
fi_flags
从用户传递的标志
fi_extents_mapped
映射的范围数
fi_extents_max
fiemap_extent 数组的大小
fi_extents_start
fiemap_extent 数组的开头
文件系统不应直接访问此结构的任何部分。 文件系统处理程序应容忍信号,并在收到致命信号后返回 EINTR。
标志检查应通过 fiemap_prep() 帮助程序在 ->fiemap 回调的开头完成
int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags);
struct fieinfo 应作为从 ioctl_fiemap() 收到的内容传入。 文件系统理解的 fiemap 标志集应通过 fs_flags 传递。 如果 fiemap_prep 发现无效的用户标志,它会将错误值放置在 fieinfo->fi_flags 中,并返回 -EBADR。 如果文件系统从 fiemap_prep() 收到 -EBADR,它应立即退出,并将该错误返回给 ioctl_fiemap()。 此外,范围针对支持的最大文件大小进行验证。
对于请求范围中的每个范围,文件系统应调用帮助程序函数 fiemap_fill_next_extent()
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
u64 phys, u64 len, u32 flags, u32 dev);
fiemap_fill_next_extent() 将使用传递的值来填充 fm_extents 数组中的下一个空闲范围。 代表调用文件系统自动从特定标志设置“常规”范围标志,以便不会破坏用户空间 API。
fiemap_fill_next_extent() 成功时返回 0,当用户提供的 fm_extents 数组已满时返回 1。 如果在将范围复制到用户内存时遇到错误,将返回 -EFAULT。