-
Notifications
You must be signed in to change notification settings - Fork 83
/
fiemap.c
135 lines (110 loc) · 3.06 KB
/
fiemap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* fiemap.c
*
* Abstract and add helpers to the fiemap ioctl.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include "debug.h"
#include "fiemap.h"
#include "util.h"
/*
* Invoke an empty fiemap ioctl to fetch the number
* of extent for this file.
* Returns 0 on error.
*/
static unsigned int fiemap_count_extents(int fd)
{
struct fiemap fiemap = {0,};
int err;
fiemap.fm_length = ~0ULL;
err = ioctl(fd, FS_IOC_FIEMAP, &fiemap);
if (err < 0) {
perror("fiemap_count_extents");
return 0;
}
return fiemap.fm_mapped_extents;
}
struct fiemap_extent *get_extent(struct fiemap *fiemap, size_t loff,
unsigned int *index)
{
struct fiemap_extent *extent;
size_t ext_end_off;
for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) {
extent = &fiemap->fm_extents[i];
ext_end_off = extent->fe_logical + extent->fe_length - 1;
if (ext_end_off < loff)
continue;
if (index)
*index = i;
return extent;
}
return NULL;
}
struct fiemap *do_fiemap(int fd)
{
int err;
struct fiemap *fiemap = NULL;
unsigned int count = fiemap_count_extents(fd);
/*
* Our structure must be large enough to fit:
* - one struct fiemap = 32 bytes
* - $count struct fiemap_extent = count * 56 bytes
* - $count struct fiemap_extent* = count * 4 bytes
* See https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
*/
fiemap = calloc(1, sizeof(struct fiemap) +
count * (sizeof(struct fiemap_extent) +
sizeof(struct fiemap_extent *)));
fiemap->fm_start = 0;
fiemap->fm_length = ~0ULL;
fiemap->fm_extent_count = count;
err = ioctl(fd, FS_IOC_FIEMAP, fiemap);
if (err < 0) {
perror("fiemap");
free(fiemap);
return NULL;
}
if (fiemap->fm_mapped_extents != count)
dprintf("do_fiemap: file changed between fiemap calls\n");
return fiemap;
}
int fiemap_count_shared(int fd, size_t start_off, size_t end_off, uint64_t *shared)
{
_cleanup_(freep) struct fiemap *fiemap = NULL;
struct fiemap_extent *extent;
size_t extent_loff;
size_t extent_end;
abort_on(start_off >= end_off);
fiemap = do_fiemap(fd);
if (!fiemap)
return 1;
*shared = 0;
for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) {
extent = &fiemap->fm_extents[i];
extent_end = extent->fe_logical + extent->fe_length;
extent_loff = extent->fe_logical;
if (start_off <= extent_end && end_off >= extent_loff) {
if (!(extent->fe_flags & FIEMAP_EXTENT_DELALLOC)
&& extent->fe_flags & FIEMAP_EXTENT_SHARED) {
if (extent_loff < start_off)
extent_loff = start_off;
if (end_off < extent_end)
extent_end = end_off;
*shared += extent_end - extent_loff;
}
}
}
return 0;
}