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
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
31 #include <linux/types.h>
32 #include <linux/fiemap.h>
34 /* Global for non-critical message suppression */
40 printf("Usage: fiemap-tester [-m map] [-r number of runs] [-s seed] [-qS]");
41 printf("[-p preallocate (1/0)] ");
43 printf(" -m map : generate a file with the map given and test\n");
44 printf(" -p 0/1 : turn block preallocation on or off\n");
45 printf(" -r count : number of runs to execute (default infinity)\n");
46 printf(" -s seed : seed for random map generator (default 1)\n");
47 printf(" -q : be quiet about non-errors\n");
48 printf(" -S : sync file before mapping (via ioctl flags)\n");
49 printf("-m and -r cannot be used together\n");
54 generate_file_mapping(int blocks, int prealloc)
57 int num_types = 2, cur_block = 0;
60 map = malloc(sizeof(char) * blocks);
68 for (i = 0; i < blocks; i++) {
69 long num = random() % num_types;
88 create_file_from_mapping(int fd, char *map, int blocks, int blocksize)
90 int cur_offset = 0, ret = 0, bufsize;
94 bufsize = sizeof(char) * blocksize;
95 if (posix_memalign((void **)&buf, 4096, bufsize))
98 memset(buf, 'a', bufsize);
100 for (i = 0; i < blocks; i++) {
103 ret = write(fd, buf, bufsize);
105 printf("Short write\n");
110 #ifdef HAVE_FALLOCATE
112 ret = fallocate(fd, 0, cur_offset, blocksize);
114 printf("Error fallocating\n");
117 /* fallthrough; seek to end of prealloc space */
120 ret = lseek(fd, blocksize, SEEK_CUR);
121 if (ret == (off_t)-1) {
122 printf("Error lseeking\n");
128 printf("Hrm, unrecognized flag in map\n");
132 cur_offset += blocksize;
141 show_extent_block(struct fiemap_extent *extent, int blocksize)
143 __u64 logical = extent->fe_logical;
144 __u64 phys = extent->fe_physical;
145 __u64 length = extent->fe_length;
146 int flags = extent->fe_flags;
148 printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
149 "flags: 0x%03X tot: %llu\n",
150 logical / blocksize, (logical + length - 1) / blocksize,
151 phys / blocksize, (phys + length - 1) / blocksize,
153 (length / blocksize));
157 show_extents(struct fiemap *fiemap, int blocksize)
161 for (i = 0; i < fiemap->fm_mapped_extents; i++)
162 show_extent_block(&fiemap->fm_extents[i], blocksize);
166 check_flags(struct fiemap *fiemap, int blocksize)
168 struct fiemap_extent *extent;
169 __u64 aligned_offset, aligned_length;
172 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
173 extent = &fiemap->fm_extents[c];
175 aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
176 aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
178 if ((aligned_offset != extent->fe_physical ||
179 aligned_length != extent->fe_length) &&
180 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
181 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
182 "but the extent is unaligned: %llu\n",
184 (extent->fe_logical / blocksize));
188 if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
189 !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
190 printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
191 "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
193 (extent->fe_logical / blocksize));
197 if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
198 aligned_offset == extent->fe_physical &&
199 aligned_length == extent->fe_length) {
200 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
201 "offset and length is blocksize aligned: "
204 (extent->fe_logical / blocksize));
208 if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
209 c + 1 < fiemap->fm_mapped_extents) {
210 printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
211 " more extents left: %llu\n",
213 (extent->fe_logical / blocksize));
217 if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
218 !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
219 printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
220 "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
222 (extent->fe_logical / blocksize));
226 if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
227 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
228 printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
229 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
231 (extent->fe_logical / blocksize));
235 if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
236 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
237 printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
238 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
240 (extent->fe_logical / blocksize));
249 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
250 int last, int prealloc)
252 struct fiemap_extent *extent;
253 __u64 orig_offset = logical_offset;
256 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
258 extent = &fiemap->fm_extents[c];
260 start = extent->fe_logical;
261 end = extent->fe_logical + extent->fe_length;
263 if (logical_offset > end)
266 if (logical_offset + blocksize < start)
269 if (logical_offset >= start &&
270 logical_offset < end) {
272 !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
273 printf("ERROR: preallocated extent is not "
274 "marked with FIEMAP_EXTENT_UNWRITTEN: "
277 (start / blocksize));
281 if (logical_offset + blocksize > end) {
282 logical_offset = end+1;
292 printf("ERROR: couldn't find extent at %llu\n",
293 (unsigned long long)(orig_offset / blocksize));
295 !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
296 printf("ERROR: last extent not marked as last: %llu\n",
297 (unsigned long long)(orig_offset / blocksize));
301 return (!found) ? -1 : 0;
305 check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
307 static int warning_printed = 0;
309 size_t buf_len = sizeof(char) * blocksize;
312 block = (int)(logical_offset / blocksize);
313 if (ioctl(fd, FIBMAP, &block) < 0) {
314 perror("Can't fibmap file");
319 printf("ERROR: FIEMAP claimed there was data at a block "
320 "which should be a hole, and FIBMAP confirmend that "
321 "it is in fact a hole, so FIEMAP is wrong: %llu\n",
322 (unsigned long long)(logical_offset / blocksize));
326 buf = malloc(buf_len);
328 perror("Could not allocate temporary buffer");
332 if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
333 perror("Error reading from file");
338 for (i = 0; i < buf_len; i++) {
340 printf("ERROR: FIEMAP claimed there was data (%c) at "
341 "block %llu that should have been a hole, and "
342 "FIBMAP confirmed that it was allocated, but "
343 "it should be filled with 0's, but it was not "
344 "so you have a big problem!\n",
346 (unsigned long long)(logical_offset / blocksize));
352 if (warning_printed || quiet) {
357 printf("HEY FS PERSON: your fs is weird. I specifically wanted a\n"
358 "hole and you allocated a block anyway. FIBMAP confirms that\n"
359 "you allocated a block, and the block is filled with 0's so\n"
360 "everything is kosher, but you still allocated a block when\n"
361 "didn't need to. This may or may not be what you wanted,\n"
362 "which is why I'm only printing this message once, in case\n"
363 "you didn't do it on purpose. This was at block %llu.\n",
364 (unsigned long long)(logical_offset / blocksize));
372 check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
374 struct fiemap_extent *extent;
377 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
379 extent = &fiemap->fm_extents[c];
381 start = extent->fe_logical;
382 end = extent->fe_logical + extent->fe_length;
384 if (logical_offset > end)
386 if (logical_offset + blocksize < start)
389 if (logical_offset >= start &&
390 logical_offset < end) {
392 if (check_weird_fs_hole(fd, logical_offset,
396 printf("ERROR: found an allocated extent where a hole "
398 (unsigned long long)(start / blocksize));
406 static int query_fiemap_count(int fd, int blocks, int blocksize)
408 struct fiemap fiemap = { 0, };
410 fiemap.fm_length = blocks * blocksize;
412 if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fiemap) < 0) {
413 perror("FIEMAP query ioctl failed");
421 compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfile)
423 struct fiemap *fiemap;
425 int blocks_to_map, ret, cur_extent = 0, last_data = 0;
426 __u64 map_start, map_length;
429 if (query_fiemap_count(fd, blocks, blocksize) < 0)
432 blocks_to_map = (random() % blocks) + 1;
433 fiebuf = malloc(sizeof(struct fiemap) +
434 (blocks_to_map * sizeof(struct fiemap_extent)));
436 perror("Could not allocate fiemap buffers");
440 fiemap = (struct fiemap *)fiebuf;
442 map_length = blocks_to_map * blocksize;
444 for (i = 0; i < blocks; i++) {
449 fiemap->fm_flags = syncfile ? FIEMAP_FLAG_SYNC : 0;
450 fiemap->fm_extent_count = blocks_to_map;
451 fiemap->fm_mapped_extents = 0;
454 fiemap->fm_start = map_start;
455 fiemap->fm_length = map_length;
457 ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
459 perror("FIEMAP ioctl failed");
464 if (check_flags(fiemap, blocksize))
467 for (i = cur_extent, c = 1; i < blocks; i++, c++) {
468 __u64 logical_offset = i * blocksize;
470 if (c > fiemap->fm_mapped_extents) {
477 if (check_data(fiemap, logical_offset,
478 blocksize, last_data == i, 0))
482 if (check_hole(fiemap, fd, logical_offset,
487 if (check_data(fiemap, logical_offset,
488 blocksize, last_data == i, 1))
492 printf("ERROR: weird value in map: %c\n",
498 map_start = i * blocksize;
499 } while (cur_extent < blocks);
504 printf("map is '%s'\n", map);
505 show_extents(fiemap, blocksize);
510 main(int argc, char **argv)
512 int blocksize = 0; /* filesystem blocksize */
513 int fd; /* file descriptor */
516 char *fname; /* filename to map */
517 char *map = NULL; /* file map to generate */
518 int runs = -1; /* the number of runs to have */
519 int blocks = 0; /* the number of blocks to generate */
520 int maxblocks = 0; /* max # of blocks to create */
521 int prealloc = 1; /* whether or not to do preallocation */
522 int syncfile = 0; /* whether fiemap should sync file first */
525 while ((opt = getopt(argc, argv, "m:r:s:p:qS")) != -1) {
528 map = strdup(optarg);
531 prealloc = atoi(optarg);;
532 #ifndef HAVE_FALLOCATE
534 printf("Not built with preallocation support\n");
548 /* sync file before mapping */
557 if (runs != -1 && map)
560 fname = argv[optind++];
564 fd = open(fname, O_RDWR|O_CREAT|O_TRUNC|O_DIRECT, 0644);
566 perror("Can't open file");
570 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
571 perror("Can't get filesystem block size");
576 #ifdef HAVE_FALLOCATE
577 /* if fallocate passes, then we can do preallocation, else not */
579 prealloc = !((int)fallocate(fd, 0, 0, blocksize));
581 printf("preallocation not supported, disabling\n");
587 if (ftruncate(fd, 0)) {
588 perror("Can't truncate file");
594 blocks = strlen(map);
600 /* max file size 2mb / block size */
601 maxblocks = (2 * 1024 * 1024) / blocksize;
604 printf("Starting infinite run, if you don't see any output "
605 "then its working properly.\n");
608 blocks = random() % maxblocks;
611 printf("Skipping 0 length file\n");
615 map = generate_file_mapping(blocks, prealloc);
617 printf("Could not create map\n");
622 rc = create_file_from_mapping(fd, map, blocks, blocksize);
624 perror("Could not create file\n");
630 rc = compare_fiemap_and_map(fd, map, blocks, blocksize, syncfile);
632 printf("Problem comparing fiemap and map\n");
641 if (ftruncate(fd, 0)) {
642 perror("Could not truncate file\n");
647 if (lseek(fd, 0, SEEK_SET)) {
648 perror("Could not seek set\n");