我的问题是处理稀疏文件读取并了解文件的范围以便围绕它执行一些逻辑。
由于没有直接的 API 调用来解决这些问题,我决定使用 ioctl api 来完成此操作。我从 cp 命令如何通过检查他们的代码来处理复制稀疏文件的问题中得到了这个想法,并最终看到了这一点。
因此,我尝试在用户空间中运行的示例程序中执行相同的操作,但出现“无效参数”错误。我不确定我缺少什么,或者这是否可以从用户空间实现。我在 ext4 文件系统上的 ubuntu 14.04 上运行。这可能是底层支持这些请求模式的设备驱动程序的问题吗?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
/* https://github.com/coreutils/coreutils/blob/df88fce71651afb2c3456967a142db0ae4bf9906/src/fiemap.h */
#include "fiemap.h"
int main(int argc, char* argv[]) {
int input_fd;
if (argc != 2) {
printf ("Usage: ioctl file1");
return 1;
}
/* Create input file descriptor */
input_fd = open (argv [1], O_RDWR);
if (input_fd < 0) {
perror ("open");
return 2;
}
union { struct fiemap f; char c[4096]; } fiemap_buf;
struct fiemap *fiemap = &fiemap_buf.f;
int s = ioctl(input_fd, FS_IOC_FIEMAP, fiemap);
if (s == 0) {
printf("ioctl success\n");
} else {
printf("ioctl failure\n");
char * errmsg = strerror(errno);
printf("error: %d %s\n", errno, errmsg);
}
/* Close file descriptors */
close (input_fd);
return s;
}
由于您在调用
fiemap_buf.f
之前没有正确设置 ioctl()
参数,因此 EINVAL
很可能来自 fiemap
无效内容,而不是来自 FS_IOC_FIEMAP
请求标识符支持本身。
例如,
ioctl_fiemap()
(来自内核)将评估fiemap.fm_extent_count
,以确定它是否大于FIEMAP_MAX_EXTENTS
,并在这种情况下返回-EINVAL
。由于 fiemap
上没有执行内存重置或参数化,这很可能是问题的根本原因。
请注意,从您引用的
coreutils
代码中,它在调用 fiemap
之前执行正确的 ioctl()
参数化:
fiemap->fm_start = scan->scan_start;
fiemap->fm_flags = scan->fm_flags;
fiemap->fm_extent_count = count;
fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
注意不推荐使用 fiemap,因为您必须确保传递 FIEMAP_FLAG_SYNC,这有副作用。 lseek()、SEEK_DATA 和 SEEK_HOLE 接口是推荐的接口,但请注意,根据文件系统,它们会将未写入的范围(分配的零)表示为空洞。