如何在 FS_IOC_FIEMAP 中使用 ioctl
How to use ioctl with FS_IOC_FIEMAP
我的问题是处理稀疏文件读取并了解文件范围在哪里以围绕它执行一些逻辑。
因为没有直接的 API 调用来解决这些问题,我决定使用 ioctl api 来做到这一点。我从 cp 命令如何通过查看他们的代码处理复制稀疏文件的问题得到了这个想法,并最终看到了这个。
因此,我尝试在用户 space 的示例程序 运行 中做同样的事情,但它出错了 "Invalid argument"。我不确定我遗漏了什么,或者用户 space 是否可以做到这一点。我在 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>
#include "fiemap.h" //This is from https://github.com/coreutils/coreutils/blob/df88fce71651afb2c3456967a142db0ae4bf9906/src/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;
}
由于您在调用 ioctl()
之前未正确设置 fiemap_buf.f
参数,因此 EINVAL
可能来自 fiemap
无效内容而不是来自FS_IOC_FIEMAP
请求标识符支持自身。
例如,ioctl_fiemap()
(来自内核)将评估 fiemap.fm_extent_count
以确定它是否大于 FIEMAP_MAX_EXTENTS
和 return -EINVAL
在这种情况下。由于没有对 fiemap
执行内存重置或参数化,这很可能是问题的根本原因。
请注意,根据您引用的 coreutils
代码,它在调用 ioctl()
之前执行 fiemap
的正确参数化:
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 接口是推荐的接口,但请注意,根据文件系统,将未写入的范围(分配的零)表示为空洞。
我的问题是处理稀疏文件读取并了解文件范围在哪里以围绕它执行一些逻辑。
因为没有直接的 API 调用来解决这些问题,我决定使用 ioctl api 来做到这一点。我从 cp 命令如何通过查看他们的代码处理复制稀疏文件的问题得到了这个想法,并最终看到了这个。
因此,我尝试在用户 space 的示例程序 运行 中做同样的事情,但它出错了 "Invalid argument"。我不确定我遗漏了什么,或者用户 space 是否可以做到这一点。我在 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>
#include "fiemap.h" //This is from https://github.com/coreutils/coreutils/blob/df88fce71651afb2c3456967a142db0ae4bf9906/src/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;
}
由于您在调用 ioctl()
之前未正确设置 fiemap_buf.f
参数,因此 EINVAL
可能来自 fiemap
无效内容而不是来自FS_IOC_FIEMAP
请求标识符支持自身。
例如,ioctl_fiemap()
(来自内核)将评估 fiemap.fm_extent_count
以确定它是否大于 FIEMAP_MAX_EXTENTS
和 return -EINVAL
在这种情况下。由于没有对 fiemap
执行内存重置或参数化,这很可能是问题的根本原因。
请注意,根据您引用的 coreutils
代码,它在调用 ioctl()
之前执行 fiemap
的正确参数化:
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 接口是推荐的接口,但请注意,根据文件系统,将未写入的范围(分配的零)表示为空洞。