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 buf = malloc(bufsize);
99 memset(buf, 'a', bufsize);
101 for (i = 0; i < blocks; i++) {
104 ret = write(fd, buf, bufsize);
106 printf("Short write\n");
111 #ifdef HAVE_FALLOCATE
113 ret = fallocate(fd, 0, cur_offset, blocksize);
115 printf("Error fallocating\n");
118 /* fallthrough; seek to end of prealloc space */
121 ret = lseek(fd, blocksize, SEEK_CUR);
122 if (ret == (off_t)-1) {
123 printf("Error lseeking\n");
129 printf("Hrm, unrecognized flag in map\n");
133 cur_offset += blocksize;
142 show_extent_block(struct fiemap_extent *extent, int blocksize)
144 __u64 logical = extent->fe_logical;
145 __u64 phys = extent->fe_physical;
146 __u64 length = extent->fe_length;
147 int flags = extent->fe_flags;
149 printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
150 "flags: 0x%03X tot: %llu\n",
151 logical / blocksize, (logical + length - 1) / blocksize,
152 phys / blocksize, (phys + length - 1) / blocksize,
154 (length / blocksize));
158 show_extents(struct fiemap *fiemap, int blocksize)
162 for (i = 0; i < fiemap->fm_mapped_extents; i++)
163 show_extent_block(&fiemap->fm_extents[i], blocksize);
167 check_flags(struct fiemap *fiemap, int blocksize)
169 struct fiemap_extent *extent;
170 __u64 aligned_offset, aligned_length;
173 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
174 extent = &fiemap->fm_extents[c];
176 aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
177 aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
179 if ((aligned_offset != extent->fe_physical ||
180 aligned_length != extent->fe_length) &&
181 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
182 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
183 "but the extent is unaligned: %llu\n",
185 (extent->fe_logical / blocksize));
189 if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
190 !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
191 printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
192 "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
194 (extent->fe_logical / blocksize));
198 if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
199 aligned_offset == extent->fe_physical &&
200 aligned_length == extent->fe_length) {
201 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
202 "offset and length is blocksize aligned: "
205 (extent->fe_logical / blocksize));
209 if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
210 c + 1 < fiemap->fm_mapped_extents) {
211 printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
212 " more extents left: %llu\n",
214 (extent->fe_logical / blocksize));
218 if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
219 !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
220 printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
221 "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
223 (extent->fe_logical / blocksize));
227 if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
228 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
229 printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
230 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
232 (extent->fe_logical / blocksize));
236 if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
237 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
238 printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
239 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
241 (extent->fe_logical / blocksize));
250 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
251 int last, int prealloc)
253 struct fiemap_extent *extent;
254 __u64 orig_offset = logical_offset;
257 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
259 extent = &fiemap->fm_extents[c];
261 start = extent->fe_logical;
262 end = extent->fe_logical + extent->fe_length;
264 if (logical_offset > end)
267 if (logical_offset + blocksize < start)
270 if (logical_offset >= start &&
271 logical_offset < end) {
273 !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
274 printf("ERROR: preallocated extent is not "
275 "marked with FIEMAP_EXTENT_UNWRITTEN: "
278 (start / blocksize));
282 if (logical_offset + blocksize > end) {
283 logical_offset = end+1;
293 printf("ERROR: couldn't find extent at %llu\n",
294 (unsigned long long)(orig_offset / blocksize));
296 !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
297 printf("ERROR: last extent not marked as last: %llu\n",
298 (unsigned long long)(orig_offset / blocksize));
302 return (!found) ? -1 : 0;
306 check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
308 static int warning_printed = 0;
310 size_t buf_len = sizeof(char) * blocksize;
313 block = (int)(logical_offset / blocksize);
314 if (ioctl(fd, FIBMAP, &block) < 0) {
315 perror("Can't fibmap file");
320 printf("ERROR: FIEMAP claimed there was data at a block "
321 "which should be a hole, and FIBMAP confirmend that "
322 "it is in fact a hole, so FIEMAP is wrong: %llu\n",
323 (unsigned long long)(logical_offset / blocksize));
327 buf = malloc(buf_len);
329 perror("Could not allocate temporary buffer");
333 if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
334 perror("Error reading from file");
339 for (i = 0; i < buf_len; i++) {
341 printf("ERROR: FIEMAP claimed there was data (%c) at "
342 "block %llu that should have been a hole, and "
343 "FIBMAP confirmed that it was allocated, but "
344 "it should be filled with 0's, but it was not "
345 "so you have a big problem!\n",
347 (unsigned long long)(logical_offset / blocksize));
353 if (warning_printed || quiet) {
358 printf("HEY FS PERSON: your fs is weird. I specifically wanted a\n"
359 "hole and you allocated a block anyway. FIBMAP confirms that\n"
360 "you allocated a block, and the block is filled with 0's so\n"
361 "everything is kosher, but you still allocated a block when\n"
362 "didn't need to. This may or may not be what you wanted,\n"
363 "which is why I'm only printing this message once, in case\n"
364 "you didn't do it on purpose. This was at block %llu.\n",
365 (unsigned long long)(logical_offset / blocksize));
373 check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
375 struct fiemap_extent *extent;
378 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
380 extent = &fiemap->fm_extents[c];
382 start = extent->fe_logical;
383 end = extent->fe_logical + extent->fe_length;
385 if (logical_offset > end)
387 if (logical_offset + blocksize < start)
390 if (logical_offset >= start &&
391 logical_offset < end) {
393 if (check_weird_fs_hole(fd, logical_offset,
397 printf("ERROR: found an allocated extent where a hole "
399 (unsigned long long)(start / blocksize));
407 static int query_fiemap_count(int fd, int blocks, int blocksize)
409 struct fiemap fiemap = { 0, };
411 fiemap.fm_length = blocks * blocksize;
413 if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fiemap) < 0) {
414 perror("FIEMAP query ioctl failed");
422 compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfile)
424 struct fiemap *fiemap;
426 int blocks_to_map, ret, cur_extent = 0, last_data;
427 __u64 map_start, map_length;
430 if (query_fiemap_count(fd, blocks, blocksize) < 0)
433 blocks_to_map = (random() % blocks) + 1;
434 fiebuf = malloc(sizeof(struct fiemap) +
435 (blocks_to_map * sizeof(struct fiemap_extent)));
437 perror("Could not allocate fiemap buffers");
441 fiemap = (struct fiemap *)fiebuf;
443 map_length = blocks_to_map * blocksize;
445 for (i = 0; i < blocks; i++) {
450 fiemap->fm_flags = syncfile ? FIEMAP_FLAG_SYNC : 0;
451 fiemap->fm_extent_count = blocks_to_map;
452 fiemap->fm_mapped_extents = 0;
455 fiemap->fm_start = map_start;
456 fiemap->fm_length = map_length;
458 ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
460 perror("FIEMAP ioctl failed");
465 if (check_flags(fiemap, blocksize))
468 for (i = cur_extent, c = 1; i < blocks; i++, c++) {
469 __u64 logical_offset = i * blocksize;
471 if (c > fiemap->fm_mapped_extents) {
478 if (check_data(fiemap, logical_offset,
479 blocksize, last_data == i, 0))
483 if (check_hole(fiemap, fd, logical_offset,
488 if (check_data(fiemap, logical_offset,
489 blocksize, last_data == i, 1))
493 printf("ERROR: weird value in map: %c\n",
499 map_start = i * blocksize;
500 } while (cur_extent < blocks);
505 printf("map is '%s'\n", map);
506 show_extents(fiemap, blocksize);
511 main(int argc, char **argv)
513 int blocksize = 0; /* filesystem blocksize */
514 int fd; /* file descriptor */
517 char *fname; /* filename to map */
518 char *map = NULL; /* file map to generate */
519 int runs = -1; /* the number of runs to have */
520 int blocks = 0; /* the number of blocks to generate */
521 int maxblocks = 0; /* max # of blocks to create */
522 int prealloc = 1; /* whether or not to do preallocation */
523 int syncfile = 0; /* whether fiemap should sync file first */
526 while ((opt = getopt(argc, argv, "m:r:s:p:qS")) != -1) {
529 map = strdup(optarg);
532 prealloc = atoi(optarg);;
533 #ifndef HAVE_FALLOCATE
535 printf("Not built with preallocation support\n");
549 /* sync file before mapping */
558 if (runs != -1 && map)
561 fname = argv[optind++];
565 fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0644);
567 perror("Can't open file");
571 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
572 perror("Can't get filesystem block size");
577 #ifdef HAVE_FALLOCATE
578 /* if fallocate passes, then we can do preallocation, else not */
580 prealloc = !((int)fallocate(fd, 0, 0, blocksize));
582 printf("preallocation not supported, disabling\n");
588 if (ftruncate(fd, 0)) {
589 perror("Can't truncate file");
595 blocks = strlen(map);
601 /* max file size 2mb / block size */
602 maxblocks = (2 * 1024 * 1024) / blocksize;
605 printf("Starting infinite run, if you don't see any output "
606 "then its working properly.\n");
609 blocks = random() % maxblocks;
612 printf("Skipping 0 length file\n");
616 map = generate_file_mapping(blocks, prealloc);
618 printf("Could not create map\n");
623 rc = create_file_from_mapping(fd, map, blocks, blocksize);
625 perror("Could not create file\n");
631 rc = compare_fiemap_and_map(fd, map, blocks, blocksize, syncfile);
633 printf("Problem comparing fiemap and map\n");
642 if (ftruncate(fd, 0)) {
643 perror("Could not truncate file\n");
648 if (lseek(fd, 0, SEEK_SET)) {
649 perror("Could not seek set\n");