1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2009 Josef Bacik
14 #include <sys/ioctl.h>
15 #include <sys/types.h>
17 #include <sys/statfs.h>
20 #include <linux/types.h>
21 #include <linux/fiemap.h>
23 /* Global for non-critical message suppression */
29 printf("Usage: fiemap-tester [-m map] [-r number of runs] [-s seed] [-qS]");
30 printf("[-p preallocate (1/0)] ");
32 printf(" -m map : generate a file with the map given and test\n");
33 printf(" -p 0/1 : turn block preallocation on or off\n");
34 printf(" -r count : number of runs to execute (default infinity)\n");
35 printf(" -s seed : seed for random map generator (default 1)\n");
36 printf(" -q : be quiet about non-errors\n");
37 printf(" -S : sync file before mapping (via ioctl flags)\n");
38 printf("-m and -r cannot be used together\n");
43 generate_file_mapping(int blocks, int prealloc)
46 int num_types = 2, cur_block = 0;
49 map = malloc(sizeof(char) * blocks);
57 for (i = 0; i < blocks; i++) {
58 long num = random() % num_types;
77 create_file_from_mapping(int fd, char *map, int blocks, int blocksize)
79 int cur_offset = 0, ret = 0, bufsize;
83 bufsize = sizeof(char) * blocksize;
84 if (posix_memalign((void **)&buf, 4096, bufsize))
87 memset(buf, 'a', bufsize);
89 for (i = 0; i < blocks; i++) {
92 ret = write(fd, buf, bufsize);
94 printf("Short write\n");
101 ret = fallocate(fd, 0, cur_offset, blocksize);
103 printf("Error fallocating\n");
106 /* fallthrough; seek to end of prealloc space */
109 ret = lseek(fd, blocksize, SEEK_CUR);
110 if (ret == (off_t)-1) {
111 printf("Error lseeking\n");
117 printf("Hrm, unrecognized flag in map\n");
121 cur_offset += blocksize;
130 show_extent_block(struct fiemap_extent *extent, int blocksize)
132 __u64 logical = extent->fe_logical;
133 __u64 phys = extent->fe_physical;
134 __u64 length = extent->fe_length;
135 int flags = extent->fe_flags;
137 printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
138 "flags: 0x%03X tot: %llu\n",
139 logical / blocksize, (logical + length - 1) / blocksize,
140 phys / blocksize, (phys + length - 1) / blocksize,
142 (length / blocksize));
146 show_extents(struct fiemap *fiemap, int blocksize)
150 for (i = 0; i < fiemap->fm_mapped_extents; i++)
151 show_extent_block(&fiemap->fm_extents[i], blocksize);
155 check_flags(struct fiemap *fiemap, int blocksize)
157 struct fiemap_extent *extent;
158 __u64 aligned_offset, aligned_length;
161 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
162 extent = &fiemap->fm_extents[c];
164 aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
165 aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
167 if ((aligned_offset != extent->fe_physical ||
168 aligned_length != extent->fe_length) &&
169 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
170 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
171 "but the extent is unaligned: %llu\n",
173 (extent->fe_logical / blocksize));
177 if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
178 !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
179 printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
180 "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
182 (extent->fe_logical / blocksize));
186 if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
187 aligned_offset == extent->fe_physical &&
188 aligned_length == extent->fe_length) {
189 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
190 "offset and length is blocksize aligned: "
193 (extent->fe_logical / blocksize));
197 if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
198 c + 1 < fiemap->fm_mapped_extents) {
199 printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
200 " more extents left: %llu\n",
202 (extent->fe_logical / blocksize));
206 if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
207 !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
208 printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
209 "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
211 (extent->fe_logical / blocksize));
215 if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
216 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
217 printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
218 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
220 (extent->fe_logical / blocksize));
224 if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
225 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
226 printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
227 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
229 (extent->fe_logical / blocksize));
238 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
239 int last, int prealloc)
241 struct fiemap_extent *extent;
242 __u64 orig_offset = logical_offset;
245 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
247 extent = &fiemap->fm_extents[c];
249 start = extent->fe_logical;
250 end = extent->fe_logical + extent->fe_length;
252 if (logical_offset > end)
255 if (logical_offset + blocksize < start)
258 if (logical_offset >= start &&
259 logical_offset < end) {
261 !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
262 printf("ERROR: preallocated extent is not "
263 "marked with FIEMAP_EXTENT_UNWRITTEN: "
266 (start / blocksize));
270 if (logical_offset + blocksize > end) {
271 logical_offset = end+1;
281 printf("ERROR: couldn't find extent at %llu\n",
282 (unsigned long long)(orig_offset / blocksize));
284 !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
285 printf("ERROR: last extent not marked as last: %llu\n",
286 (unsigned long long)(orig_offset / blocksize));
290 return (!found) ? -1 : 0;
294 check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
296 static int warning_printed = 0;
298 size_t buf_len = sizeof(char) * blocksize;
301 block = (int)(logical_offset / blocksize);
302 if (ioctl(fd, FIBMAP, &block) < 0) {
303 perror("Can't fibmap file");
308 printf("ERROR: FIEMAP claimed there was data at a block "
309 "which should be a hole, and FIBMAP confirmend that "
310 "it is in fact a hole, so FIEMAP is wrong: %llu\n",
311 (unsigned long long)(logical_offset / blocksize));
315 buf = malloc(buf_len);
317 perror("Could not allocate temporary buffer");
321 if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
322 perror("Error reading from file");
327 for (i = 0; i < buf_len; i++) {
329 printf("ERROR: FIEMAP claimed there was data (%c) at "
330 "block %llu that should have been a hole, and "
331 "FIBMAP confirmed that it was allocated, but "
332 "it should be filled with 0's, but it was not "
333 "so you have a big problem!\n",
335 (unsigned long long)(logical_offset / blocksize));
341 if (warning_printed || quiet) {
346 printf("HEY FS PERSON: your fs is weird. I specifically wanted a\n"
347 "hole and you allocated a block anyway. FIBMAP confirms that\n"
348 "you allocated a block, and the block is filled with 0's so\n"
349 "everything is kosher, but you still allocated a block when\n"
350 "didn't need to. This may or may not be what you wanted,\n"
351 "which is why I'm only printing this message once, in case\n"
352 "you didn't do it on purpose. This was at block %llu.\n",
353 (unsigned long long)(logical_offset / blocksize));
361 check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
363 struct fiemap_extent *extent;
366 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
368 extent = &fiemap->fm_extents[c];
370 start = extent->fe_logical;
371 end = extent->fe_logical + extent->fe_length;
373 if (logical_offset > end)
375 if (logical_offset + blocksize < start)
378 if (logical_offset >= start &&
379 logical_offset < end) {
381 if (check_weird_fs_hole(fd, logical_offset,
385 printf("ERROR: found an allocated extent where a hole "
387 (unsigned long long)(start / blocksize));
395 static int query_fiemap_count(int fd, int blocks, int blocksize)
397 struct fiemap fiemap = { 0, };
399 fiemap.fm_length = blocks * blocksize;
401 if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fiemap) < 0) {
402 perror("FIEMAP query ioctl failed");
410 compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfile)
412 struct fiemap *fiemap;
414 int blocks_to_map, ret, cur_extent = 0, last_data = 0;
415 __u64 map_start, map_length;
418 if (query_fiemap_count(fd, blocks, blocksize) < 0)
421 blocks_to_map = (random() % blocks) + 1;
422 fiebuf = malloc(sizeof(struct fiemap) +
423 (blocks_to_map * sizeof(struct fiemap_extent)));
425 perror("Could not allocate fiemap buffers");
429 fiemap = (struct fiemap *)fiebuf;
431 map_length = blocks_to_map * blocksize;
433 for (i = 0; i < blocks; i++) {
438 fiemap->fm_flags = syncfile ? FIEMAP_FLAG_SYNC : 0;
439 fiemap->fm_extent_count = blocks_to_map;
440 fiemap->fm_mapped_extents = 0;
443 fiemap->fm_start = map_start;
444 fiemap->fm_length = map_length;
446 ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
448 perror("FIEMAP ioctl failed");
453 if (check_flags(fiemap, blocksize))
456 for (i = cur_extent, c = 1; i < blocks; i++, c++) {
457 __u64 logical_offset = i * blocksize;
459 if (c > fiemap->fm_mapped_extents) {
466 if (check_data(fiemap, logical_offset,
467 blocksize, last_data == i, 0))
471 if (check_hole(fiemap, fd, logical_offset,
476 if (check_data(fiemap, logical_offset,
477 blocksize, last_data == i, 1))
481 printf("ERROR: weird value in map: %c\n",
487 map_start = i * blocksize;
488 } while (cur_extent < blocks);
493 printf("map is '%s'\n", map);
494 show_extents(fiemap, blocksize);
499 main(int argc, char **argv)
501 int blocksize = 0; /* filesystem blocksize */
502 int fd; /* file descriptor */
505 char *fname; /* filename to map */
506 char *map = NULL; /* file map to generate */
507 int runs = -1; /* the number of runs to have */
508 int blocks = 0; /* the number of blocks to generate */
509 int maxblocks = 0; /* max # of blocks to create */
510 int prealloc = 1; /* whether or not to do preallocation */
511 int syncfile = 0; /* whether fiemap should sync file first */
514 while ((opt = getopt(argc, argv, "m:r:s:p:qS")) != -1) {
517 map = strdup(optarg);
520 prealloc = atoi(optarg);;
521 #ifndef HAVE_FALLOCATE
523 printf("Not built with preallocation support\n");
537 /* sync file before mapping */
546 if (runs != -1 && map)
549 fname = argv[optind++];
553 fd = open(fname, O_RDWR|O_CREAT|O_TRUNC|O_DIRECT, 0644);
555 perror("Can't open file");
559 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
562 if (fstatfs(fd, &buf) == 0) {
563 blocksize = buf.f_bsize;
565 perror("Can't get filesystem block size");
571 if (blocksize <= 0) {
572 printf("Illegal filesystem block size\n");
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");