1.1.11
by Michael Vogt
Import upstream version 8.9 |
1 |
/*
|
2 |
* Based upon original code by Ric Wheeler and Mark Lord.
|
|
3 |
*
|
|
4 |
* Copyright (c) EMC Corporation 2008
|
|
5 |
* Copyright (c) Mark Lord 2008
|
|
6 |
*
|
|
7 |
* You may use/distribute this freely, under the terms of either
|
|
8 |
* (your choice) the GNU General Public License version 2,
|
|
9 |
* or a BSD style license.
|
|
10 |
*/
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
11 |
#define _FILE_OFFSET_BITS 64
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
12 |
#include <unistd.h> |
13 |
#include <string.h> |
|
14 |
#include <stdlib.h> |
|
15 |
#include <stdio.h> |
|
16 |
#include <fcntl.h> |
|
17 |
#include <errno.h> |
|
18 |
#include <sys/ioctl.h> |
|
19 |
#include <sys/stat.h> |
|
20 |
#include <linux/types.h> |
|
21 |
#include <linux/fs.h> |
|
22 |
||
23 |
#include "hdparm.h" |
|
24 |
||
1.1.13
by Stephen Gran
Import upstream version 9.27 |
25 |
static const unsigned int sector_bytes = 512; // FIXME someday |
26 |
||
1.1.11
by Michael Vogt
Import upstream version 8.9 |
27 |
struct file_extent { |
28 |
__u64 byte_offset; |
|
29 |
__u64 first_block; |
|
30 |
__u64 last_block; |
|
31 |
__u64 block_count; |
|
32 |
};
|
|
33 |
||
1.1.13
by Stephen Gran
Import upstream version 9.27 |
34 |
static void handle_extent (struct file_extent ext, unsigned int sectors_per_block, __u64 start_lba) |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
35 |
{
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
36 |
char lba_info[64], len_info[32]; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
37 |
__u64 begin_lba, end_lba; |
38 |
__u64 nsectors = ext.block_count * sectors_per_block; |
|
39 |
||
40 |
if (ext.first_block) { |
|
1.1.12
by Stephen Gran
Import upstream version 9.15 |
41 |
begin_lba = start_lba + ( ext.first_block * sectors_per_block); |
42 |
end_lba = start_lba + ((ext.last_block + 1) * sectors_per_block) - 1; |
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
43 |
} else { |
44 |
begin_lba = end_lba = 0; |
|
45 |
}
|
|
46 |
||
47 |
if (ext.first_block) |
|
48 |
sprintf(lba_info, "%10llu %10llu", begin_lba, end_lba); |
|
49 |
else
|
|
50 |
strcpy(lba_info, " - - "); |
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
51 |
if (!ext.first_block && !nsectors) |
52 |
strcpy(len_info, " - "); |
|
53 |
else
|
|
54 |
sprintf(len_info, "%10llu", nsectors); |
|
55 |
printf("%12llu %s %s\n", ext.byte_offset, lba_info, len_info); |
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
56 |
}
|
57 |
||
1.1.13
by Stephen Gran
Import upstream version 9.27 |
58 |
static int walk_fibmap (int fd, struct stat *st, unsigned int sectors_per_block, __u64 start_lba) |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
59 |
{
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
60 |
struct file_extent ext; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
61 |
unsigned long num_blocks; |
1.1.13
by Stephen Gran
Import upstream version 9.27 |
62 |
__u64 blk_idx, hole = ~0ULL; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
63 |
|
64 |
/*
|
|
65 |
* How many calls to FIBMAP do we need?
|
|
66 |
* FIBMAP returns a filesystem block number (counted from the start of the device)
|
|
67 |
* for each file block. This can be converted to a disk LBA using the filesystem
|
|
68 |
* blocksize and LBA offset obtained earlier.
|
|
69 |
*/
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
70 |
num_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
71 |
memset(&ext, 0, sizeof(ext)); |
72 |
||
73 |
/*
|
|
74 |
* Loop through the file, building a map of the extents.
|
|
75 |
* All of this is done in filesystem blocks size (fs_blksize) units.
|
|
76 |
*
|
|
77 |
* Assumptions:
|
|
78 |
* Throughout the file, there can be any number of blocks backed by holes
|
|
79 |
* or by allocated blocks. Tail-packed files are special - if we find a file
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
80 |
* that has a size and has no allocated blocks, we could flag it as a "tail-packed"
|
81 |
* file if we cared: data is packed into the tail space of the inode block.
|
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
82 |
*/
|
83 |
for (blk_idx = 0; blk_idx < num_blocks; blk_idx++) { |
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
84 |
unsigned int blknum = blk_idx; |
85 |
__u64 blknum64; |
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
86 |
/*
|
87 |
* FIBMAP takes a block index as input and on return replaces it with a
|
|
88 |
* block number relative to the beginning of the filesystem/partition.
|
|
89 |
* An output value of zero means "unallocated", or a "hole" in a sparse file.
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
90 |
* Note that this is a 32-bit value, so it will not work properly on
|
91 |
* files/filesystems with more than 4 billion blocks (~16TB),
|
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
92 |
*/
|
93 |
if (ioctl(fd, FIBMAP, &blknum) == -1) { |
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
94 |
int err = errno; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
95 |
perror("ioctl(FIBMAP)"); |
96 |
return err; |
|
97 |
}
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
98 |
blknum64 = blknum; /* work in 64-bits as much as possible */ |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
99 |
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
100 |
if (blk_idx && blknum64 == (ext.last_block + 1)) { |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
101 |
/*
|
102 |
* Continuation of extent: Bump last_block and block_count.
|
|
103 |
*/
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
104 |
ext.last_block = blknum64 ? blknum64 : hole; |
1.1.11
by Michael Vogt
Import upstream version 8.9 |
105 |
ext.block_count++; |
106 |
} else { |
|
107 |
/*
|
|
108 |
* New extent: print previous extent (if any), and re-init the extent record.
|
|
109 |
*/
|
|
1.1.13
by Stephen Gran
Import upstream version 9.27 |
110 |
if (blk_idx) |
111 |
handle_extent(ext, sectors_per_block, start_lba); |
|
112 |
ext.first_block = blknum64; |
|
113 |
ext.last_block = blknum64 ? blknum64 : hole; |
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
114 |
ext.block_count = 1; |
1.1.13
by Stephen Gran
Import upstream version 9.27 |
115 |
ext.byte_offset = blk_idx * st->st_blksize; |
116 |
}
|
|
117 |
}
|
|
118 |
handle_extent(ext, sectors_per_block, start_lba); |
|
119 |
return 0; |
|
120 |
}
|
|
121 |
||
122 |
#define FE_COUNT 8000
|
|
123 |
#define FE_FLAG_LAST (1 << 0)
|
|
124 |
#define FE_FLAG_UNKNOWN (1 << 1)
|
|
125 |
#define FE_FLAG_UNALLOC (1 << 2)
|
|
126 |
#define FE_FLAG_NOALIGN (1 << 8)
|
|
127 |
||
128 |
#define EXTENT_UNKNOWN (FE_FLAG_UNKNOWN | FE_FLAG_UNALLOC | FE_FLAG_NOALIGN)
|
|
129 |
||
130 |
struct fe_s { |
|
131 |
__u64 logical; |
|
132 |
__u64 physical; |
|
133 |
__u64 length; |
|
134 |
__u64 reserved64[2]; |
|
135 |
__u32 flags; |
|
136 |
__u32 reserved32[3]; |
|
137 |
};
|
|
138 |
||
139 |
struct fm_s { |
|
140 |
__u64 start; |
|
141 |
__u64 length; |
|
142 |
__u32 flags; |
|
143 |
__u32 mapped_extents; |
|
144 |
__u32 extent_count; |
|
145 |
__u32 reserved; |
|
146 |
};
|
|
147 |
||
148 |
struct fs_s { |
|
149 |
struct fm_s fm; |
|
150 |
struct fe_s fe[FE_COUNT]; |
|
151 |
};
|
|
152 |
||
153 |
#define FIEMAP _IOWR('f', 11, struct fm_s)
|
|
154 |
||
155 |
static int walk_fiemap (int fd, unsigned int sectors_per_block, __u64 start_lba) |
|
156 |
{
|
|
157 |
unsigned int i, done = 0; |
|
158 |
unsigned int blksize = sectors_per_block * sector_bytes; |
|
159 |
struct fs_s fs; |
|
160 |
||
161 |
memset(&fs, 0, sizeof(fs)); |
|
162 |
do { |
|
163 |
fs.fm.length = ~0ULL; |
|
164 |
fs.fm.flags = 0; |
|
165 |
fs.fm.extent_count = FE_COUNT; |
|
166 |
||
167 |
if (-1 == ioctl(fd, FIEMAP, &fs)) { |
|
168 |
int err = errno; |
|
169 |
//perror("ioctl(FIEMAP)");
|
|
170 |
return err; |
|
171 |
}
|
|
172 |
||
173 |
if (0) fprintf(stderr, "ioctl(FIEMAP) returned %llu extents\n", (__u64)fs.fm.mapped_extents); |
|
174 |
if (!fs.fm.mapped_extents) { |
|
175 |
done = 1; |
|
176 |
} else { |
|
177 |
struct file_extent ext; |
|
178 |
memset(&ext, 0, sizeof(ext)); |
|
179 |
for (i = 0; i < fs.fm.mapped_extents; i++) { |
|
180 |
__u64 phy_blk, ext_len; |
|
181 |
||
182 |
ext.byte_offset = fs.fe[i].logical; |
|
183 |
if (0) fprintf(stderr, "log=%llu phy=%llu len=%llu flags=0x%x\n", fs.fe[i].logical, |
|
184 |
fs.fe[i].physical, fs.fe[i].length, fs.fe[i].flags); |
|
185 |
if (fs.fe[i].flags & EXTENT_UNKNOWN) { |
|
186 |
ext.first_block = 0; |
|
187 |
ext.last_block = 0; |
|
188 |
ext.block_count = 0; /* FIEMAP returns garbage for this. Ugh. */ |
|
189 |
} else { |
|
190 |
phy_blk = fs.fe[i].physical / blksize; |
|
191 |
ext_len = fs.fe[i].length / blksize; |
|
192 |
||
193 |
ext.first_block = phy_blk; |
|
194 |
ext.last_block = phy_blk + ext_len - 1; |
|
195 |
ext.block_count = ext_len; |
|
196 |
}
|
|
197 |
handle_extent(ext, sectors_per_block, start_lba); |
|
198 |
||
199 |
if (fs.fe[i].flags & FE_FLAG_LAST) { |
|
200 |
/*
|
|
201 |
* Hit an ext4 bug in 2.6.29.4, where some FIEMAP calls
|
|
202 |
* had the LAST flag set in the final returned extent,
|
|
203 |
* even though there were *plenty* more extents to be had
|
|
204 |
* from continued FIEMAP calls.
|
|
205 |
*
|
|
206 |
* So, we'll ignore it here, and instead rely on getting
|
|
207 |
* a zero count back from fs.fm.mapped_extents at the end.
|
|
208 |
*/
|
|
209 |
if (0) fprintf(stderr, "%s: ignoring LAST bit\n", __func__); |
|
210 |
//done = 1;
|
|
211 |
}
|
|
212 |
||
213 |
}
|
|
214 |
fs.fm.start = (fs.fe[i-1].logical + fs.fe[i-1].length); |
|
215 |
}
|
|
216 |
} while (!done); |
|
217 |
return 0; |
|
218 |
}
|
|
219 |
||
220 |
int do_filemap (const char *file_name) |
|
221 |
{
|
|
222 |
int fd, err; |
|
223 |
struct stat st; |
|
224 |
__u64 start_lba = 0; |
|
225 |
unsigned int sectors_per_block; |
|
226 |
||
227 |
if ((fd = open(file_name, O_RDONLY)) == -1) { |
|
228 |
err = errno; |
|
229 |
perror(file_name); |
|
230 |
return err; |
|
231 |
}
|
|
232 |
if (fstat(fd, &st) == -1) { |
|
233 |
err = errno; |
|
234 |
perror(file_name); |
|
235 |
return err; |
|
236 |
}
|
|
237 |
if (!S_ISREG(st.st_mode)) { |
|
238 |
fprintf(stderr, "%s: not a regular file\n", file_name); |
|
239 |
close(fd); |
|
240 |
return EINVAL; |
|
241 |
}
|
|
242 |
||
243 |
/*
|
|
244 |
* Get the filesystem starting LBA:
|
|
245 |
*/
|
|
246 |
err = get_dev_t_geometry(st.st_dev, NULL, NULL, NULL, &start_lba, NULL); |
|
247 |
if (err) { |
|
248 |
close(fd); |
|
249 |
return err; |
|
250 |
}
|
|
251 |
||
252 |
sectors_per_block = st.st_blksize / sector_bytes; |
|
253 |
printf("\n%s:\n filesystem blocksize %lu, begins at LBA %llu;" |
|
254 |
" assuming %u byte sectors.\n", |
|
255 |
file_name, (unsigned long)st.st_blksize, start_lba, sector_bytes); |
|
256 |
printf("%12s %10s %10s %10s\n", "byte_offset", "begin_LBA", "end_LBA", "sectors"); |
|
257 |
||
258 |
if (st.st_size == 0) { |
|
259 |
struct file_extent ext; |
|
260 |
memset(&ext, 0, sizeof(ext)); |
|
261 |
handle_extent(ext, sectors_per_block, start_lba); |
|
262 |
close(fd); |
|
263 |
return 0; |
|
264 |
}
|
|
265 |
||
266 |
err = walk_fiemap(fd, sectors_per_block, start_lba); |
|
267 |
if (err) |
|
268 |
err = walk_fibmap(fd, &st, sectors_per_block, start_lba); |
|
1.1.11
by Michael Vogt
Import upstream version 8.9 |
269 |
close (fd); |
270 |
return 0; |
|
271 |
}
|