2 * Copyright (c) 2009 Josef Bacik
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License V2
7 * as published by the Free Software Foundation.
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
30 #include <linux/types.h>
31 #include <linux/fiemap.h>
33 /* Global for non-critical message suppression */
39 printf("Usage: fiemap-tester [-m map] [-r number of runs] [-s seed] [-q]");
40 printf("[-p preallocate (1/0)] ");
42 printf(" -m map : generate a file with the map given and test\n");
43 printf(" -p 0/1 : turn block preallocation on or off\n");
44 printf(" -r count : number of runs to execute (default infinity)\n");
45 printf(" -s seed : seed for random map generator (default 1)\n");
46 printf(" -q : be quiet about non-errors\n");
47 printf("-m and -r cannot be used together\n");
52 generate_file_mapping(int blocks, int prealloc)
55 int num_types = 2, cur_block = 0;
58 map = malloc(sizeof(char) * blocks);
66 for (i = 0; i < blocks; i++) {
67 long num = random() % num_types;
86 create_file_from_mapping(int fd, char *map, int blocks, int blocksize)
88 int cur_offset = 0, ret = 0, bufsize;
92 bufsize = sizeof(char) * blocksize;
93 buf = malloc(bufsize);
97 memset(buf, 'a', bufsize);
99 for (i = 0; i < blocks; i++) {
102 ret = write(fd, buf, bufsize);
104 printf("Short write\n");
109 #ifdef HAVE_FALLOCATE
111 ret = fallocate(fd, 0, cur_offset, blocksize);
113 printf("Error fallocating\n");
116 /* fallthrough; seek to end of prealloc space */
119 ret = lseek(fd, blocksize, SEEK_CUR);
120 if (ret == (off_t)-1) {
121 printf("Error lseeking\n");
127 printf("Hrm, unrecognized flag in map\n");
131 cur_offset += blocksize;
140 show_extent_block(struct fiemap_extent *extent, int blocksize)
142 __u64 logical = extent->fe_logical;
143 __u64 phys = extent->fe_physical;
144 __u64 length = extent->fe_length;
145 int flags = extent->fe_flags;
147 printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
148 "flags: 0x%03X tot: %llu\n",
149 logical / blocksize, (logical + length - 1) / blocksize,
150 phys / blocksize, (phys + length - 1) / blocksize,
152 (length / blocksize));
156 show_extents(struct fiemap *fiemap, int blocksize)
160 for (i = 0; i < fiemap->fm_mapped_extents; i++)
161 show_extent_block(&fiemap->fm_extents[i], blocksize);
165 check_flags(struct fiemap *fiemap, int blocksize)
167 struct fiemap_extent *extent;
168 __u64 aligned_offset, aligned_length;
171 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
172 extent = &fiemap->fm_extents[c];
174 aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
175 aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
177 if ((aligned_offset != extent->fe_physical ||
178 aligned_length != extent->fe_length) &&
179 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
180 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
181 "but the extent is unaligned: %llu\n",
183 (extent->fe_logical / blocksize));
187 if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
188 !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
189 printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
190 "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
192 (extent->fe_logical / blocksize));
196 if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
197 aligned_offset == extent->fe_physical &&
198 aligned_length == extent->fe_length) {
199 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
200 "offset and length is blocksize aligned: "
203 (extent->fe_logical / blocksize));
207 if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
208 c + 1 < fiemap->fm_mapped_extents) {
209 printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
210 " more extents left: %llu\n",
212 (extent->fe_logical / blocksize));
216 if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
217 !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
218 printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
219 "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
221 (extent->fe_logical / blocksize));
225 if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
226 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
227 printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
228 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
230 (extent->fe_logical / blocksize));
234 if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
235 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
236 printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
237 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
239 (extent->fe_logical / blocksize));
248 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
249 int last, int prealloc)
251 struct fiemap_extent *extent;
252 __u64 orig_offset = logical_offset;
255 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
257 extent = &fiemap->fm_extents[c];
259 start = extent->fe_logical;
260 end = extent->fe_logical + extent->fe_length;
262 if (logical_offset > end)
265 if (logical_offset + blocksize < start)
268 if (logical_offset >= start &&
269 logical_offset < end) {
271 !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
272 printf("ERROR: preallocated extent is not "
273 "marked with FIEMAP_EXTENT_UNWRITTEN: "
276 (start / blocksize));
280 if (logical_offset + blocksize > end) {
281 logical_offset = end+1;
291 printf("ERROR: couldn't find extent at %llu\n",
292 (unsigned long long)(orig_offset / blocksize));
294 !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
295 printf("ERROR: last extent not marked as last: %llu\n",
296 (unsigned long long)(orig_offset / blocksize));
300 return (!found) ? -1 : 0;
304 check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
306 static int warning_printed = 0;
308 size_t buf_len = sizeof(char) * blocksize;
311 block = (int)(logical_offset / blocksize);
312 if (ioctl(fd, FIBMAP, &block) < 0) {
313 perror("Can't fibmap file");
318 printf("ERROR: FIEMAP claimed there was data at a block "
319 "which should be a hole, and FIBMAP confirmend that "
320 "it is in fact a hole, so FIEMAP is wrong: %llu\n",
321 (unsigned long long)(logical_offset / blocksize));
325 buf = malloc(buf_len);
327 perror("Could not allocate temporary buffer");
331 if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
332 perror("Error reading from file");
337 for (i = 0; i < buf_len; i++) {
339 printf("ERROR: FIEMAP claimed there was data (%c) at "
340 "block %llu that should have been a hole, and "
341 "FIBMAP confirmed that it was allocated, but "
342 "it should be filled with 0's, but it was not "
343 "so you have a big problem!\n",
345 (unsigned long long)(logical_offset / blocksize));
351 if (warning_printed || quiet) {
356 printf("HEY FS PERSON: your fs is weird. I specifically wanted a\n"
357 "hole and you allocated a block anyway. FIBMAP confirms that\n"
358 "you allocated a block, and the block is filled with 0's so\n"
359 "everything is kosher, but you still allocated a block when\n"
360 "didn't need to. This may or may not be what you wanted,\n"
361 "which is why I'm only printing this message once, in case\n"
362 "you didn't do it on purpose. This was at block %llu.\n",
363 (unsigned long long)(logical_offset / blocksize));
371 check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
373 struct fiemap_extent *extent;
376 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
378 extent = &fiemap->fm_extents[c];
380 start = extent->fe_logical;
381 end = extent->fe_logical + extent->fe_length;
383 if (logical_offset > end)
385 if (logical_offset + blocksize < start)
388 if (logical_offset >= start &&
389 logical_offset < end) {
391 if (check_weird_fs_hole(fd, logical_offset,
395 printf("ERROR: found an allocated extent where a hole "
397 (unsigned long long)(start / blocksize));
405 static int query_fiemap_count(int fd, int blocks, int blocksize)
407 struct fiemap fiemap = { 0, };
409 fiemap.fm_length = blocks * blocksize;
411 if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fiemap) < 0) {
412 perror("FIEMAP query ioctl failed");
420 compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize)
422 struct fiemap *fiemap;
424 int blocks_to_map, ret, cur_extent = 0, last_data;
425 __u64 map_start, map_length;
428 if (query_fiemap_count(fd, blocks, blocksize) < 0)
431 blocks_to_map = (random() % blocks) + 1;
432 fiebuf = malloc(sizeof(struct fiemap) +
433 (blocks_to_map * sizeof(struct fiemap_extent)));
435 perror("Could not allocate fiemap buffers");
439 fiemap = (struct fiemap *)fiebuf;
441 map_length = blocks_to_map * blocksize;
443 for (i = 0; i < blocks; i++) {
448 fiemap->fm_flags = FIEMAP_FLAG_SYNC;
449 fiemap->fm_extent_count = blocks_to_map;
450 fiemap->fm_mapped_extents = 0;
453 fiemap->fm_start = map_start;
454 fiemap->fm_length = map_length;
456 ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
458 perror("FIEMAP ioctl failed");
463 if (check_flags(fiemap, blocksize))
466 for (i = cur_extent, c = 1; i < blocks; i++, c++) {
467 __u64 logical_offset = i * blocksize;
469 if (c > blocks_to_map)
474 if (check_data(fiemap, logical_offset,
475 blocksize, last_data == i, 0))
479 if (check_hole(fiemap, fd, logical_offset,
484 if (check_data(fiemap, logical_offset,
485 blocksize, last_data == i, 1))
489 printf("ERROR: weird value in map: %c\n",
495 map_start = i * blocksize;
496 } while (cur_extent < blocks);
501 printf("map is '%s'\n", map);
502 show_extents(fiemap, blocksize);
507 main(int argc, char **argv)
509 int blocksize = 0; /* filesystem blocksize */
510 int fd; /* file descriptor */
513 char *fname; /* filename to map */
514 char *map = NULL; /* file map to generate */
515 int runs = -1; /* the number of runs to have */
516 int blocks = 0; /* the number of blocks to generate */
517 int maxblocks = 0; /* max # of blocks to create */
518 int prealloc = 1; /* whether or not to do preallocation */
521 while ((opt = getopt(argc, argv, "m:r:s:p:q")) != -1) {
524 map = strdup(optarg);
527 prealloc = atoi(optarg);;
528 #ifndef HAVE_FALLOCATE
530 printf("Not built with preallocation support\n");
538 /* sync file before mapping */
550 if (runs != -1 && map)
553 fname = argv[optind++];
557 fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0644);
559 perror("Can't open file");
563 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
564 perror("Can't get filesystem block size");
569 #ifdef HAVE_FALLOCATE
570 /* if fallocate passes, then we can do preallocation, else not */
572 prealloc = !((int)fallocate(fd, 0, 0, blocksize));
574 printf("preallocation not supported, disabling\n");
580 if (ftruncate(fd, 0)) {
581 perror("Can't truncate file");
587 blocks = strlen(map);
593 /* max file size 2mb / block size */
594 maxblocks = (2 * 1024 * 1024) / blocksize;
597 printf("Starting infinite run, if you don't see any output "
598 "then its working properly.\n");
601 blocks = random() % maxblocks;
604 printf("Skipping 0 length file\n");
608 map = generate_file_mapping(blocks, prealloc);
610 printf("Could not create map\n");
615 rc = create_file_from_mapping(fd, map, blocks, blocksize);
617 perror("Could not create file\n");
623 rc = compare_fiemap_and_map(fd, map, blocks, blocksize);
625 printf("Problem comparing fiemap and map\n");
634 if (ftruncate(fd, 0)) {
635 perror("Could not truncate file\n");
640 if (lseek(fd, 0, SEEK_SET)) {
641 perror("Could not seek set\n");