最大尺寸为
src_length
取决于文件系统,通常为 16 MiB。
然而,我已经能够src_length
通过一次调用成功使用> 1 Gib ioctl
。关于 16 MiB 的警告是否完全是夸大其词,至少对于btrfs
?
另外,根据VFS 文档,
实现必须处理传入 len == 0 的调用者;这意味着“重新映射到源文件的末尾”。
但是,当我尝试设置src_length
为时0
,ioctl
呼叫成功但没有执行任何操作。
我是否误读了这两句话,或者btrfs
实现根本不符合(很好)文档?我正在使用 kernel 的 Linux Mint 20 上进行测试5.4.0-62-generic
。我用来filefrag -sv FILE1 FILE2
检查文件的块级分配,看看它们是否重复。我正在使用下面的程序来删除重复的文件。有问题的文件位于btrfs
使用sudo mkfs.btrfs -mraid1 -draid1 /dev/mapper/sda1_crypt /dev/mapper/sdb1_crypt
.
设想:
$ cp -f file1 file2
$ filefrag -sv file1 file2 # see that files use different extents (are not deduplicated)
$ myprog file1 file2
$ filefrag -sv file1 file2 # see that files use the same extents (have been deduplicated)
删除两个文件重复的程序:
// deduplicate srcfile and targetfile if contents are identical
// usage: myprog srcfile targetfile
// compile with: gcc myprog.c -o myprog
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main(int argc, char**argv)
{
struct stat st;
long size;
__u64 buf[2048]; /* __u64 for proper field alignment */
struct file_dedupe_range *range = (struct file_dedupe_range *)buf;
memset(range, 0, sizeof(struct file_dedupe_range));
memset(&range->info, 0, sizeof(struct file_dedupe_range_info));
long srcfd = open(argv[1], O_RDONLY);
if (srcfd < 0) { perror("open-src"); exit(1); }
if (fstat(srcfd, &st) < 0) { perror("stat-src"); exit(1); }
size = st.st_size;
long tgtfd = open(argv[2], O_RDWR);
if (tgtfd < 0) { perror("open-tgt"); exit(1); }
if (fstat(tgtfd, &st) < 0) { perror("stat-tgt"); exit(1); }
if (size != st.st_size) {
fprintf(stderr, "SIZE DIFF\n");
exit(1);
}
range->src_offset = 0;
range->src_length = size;
// range->src_length = 0; // I expected this to work
range->dest_count = 1;
range->info[0].dest_fd = tgtfd;
range->info[0].dest_offset = 0;
while (range->src_length > 0) {
if (ioctl(srcfd, FIDEDUPERANGE, range) < 0) { perror("ioctl"); exit(1); }
fprintf(stderr, "bytes_deduped: %llu\n", range->info[0].bytes_deduped);
fprintf(stderr, "status: %d\n", range->info[0].status);
if (range->info[0].status == FILE_DEDUPE_RANGE_DIFFERS) {
fprintf(stderr, "DIFFERS\n");
break;
} else if (range->info[0].status == FILE_DEDUPE_RANGE_SAME) {
fprintf(stderr, "SAME\n");
} else {
fprintf(stderr, "ERROR\n");
break;
}
if (range->info[0].bytes_deduped >= range->src_length) { break; }
range->src_length -= range->info[0].bytes_deduped;
range->src_offset += range->info[0].bytes_deduped;
range->info[0].dest_offset += range->info[0].bytes_deduped;
}
exit(0);
}
答案1
旧版本btrfs
(例如 4.15)的每次FIDEDUPERANGE
调用有 16 MiB 的限制,并且会悄悄地将超大请求减少到 16 MiB。我忘记了更改发生的确切时间,但当前版本btrfs
(即 5.16)以 16 MiB 块循环。不过,我认为 linux(btrfs
现在不是)仍然默默地减少了超过 1 GiB 的请求。如果您希望使用FIDEDUPERANGE
旧版本的btrfs
,您绝对应该遵守 16 MiB 的限制。此外,其他文件系统可能也有类似的限制。
至于src_length = 0
,您确实应该查阅各个 s 的文档ioctl
以获取有关如何使用它们的说明。您正确引用的man
页面意味着不会删除任何内容。FIDEDUPERANGE
src_length = 0
关于你引用的VFS页面,事情很复杂。 处理改编自最初设计和单独实现的多个sremap_file_range()
的功能。在clone s中,表示克隆到文件末尾。在 dedup 中,表示不进行任何重复数据删除。我忘记了具体时间,但有人努力统一克隆和重复数据删除功能。然而,改变当前支持的.在版本 5.16 中,有一个奇怪的 hack,涉及将参数转换为取决于是克隆还是重复数据删除调用。人们很容易说 VFS 文档是错误的,因为我认为这里的行为没有改变。但是,我不确定其他文件系统是否已经实现了这种含义,所以它很复杂。ioctl
btrfs
btrfs
ioctl
src_length == 0
ioctl
src_length == 0
ioctl
btrfs
btrfs_remap_file_range_prep()
len
remap_file_range()
ioctl
btrfs
remap_file_range()
len == 0