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]");
41 printf("[-p preallocate (1/0)] ");
44 printf(" -m map : generate a file with the map given and test\n");
46 printf(" -p 0/1 : turn block preallocation on or off\n");
48 printf(" -r count : number of runs to execute (default infinity)\n");
49 printf(" -s seed : seed for random map generator (default 1)\n");
50 printf(" -q : be quiet about non-errors\n");
51 printf("-m and -r cannot be used together\n");
56 generate_file_mapping(int blocks, int prealloc)
59 int num_types = 2, cur_block = 0;
62 map = malloc(sizeof(char) * blocks);
70 for (i = 0; i < blocks; i++) {
71 long num = random() % num_types;
90 create_file_from_mapping(int fd, char *map, int blocks, int blocksize)
92 int cur_offset = 0, ret = 0, bufsize;
96 bufsize = sizeof(char) * blocksize;
97 buf = malloc(bufsize);
101 memset(buf, 'a', bufsize);
103 for (i = 0; i < blocks; i++) {
106 ret = write(fd, buf, bufsize);
108 printf("Short write\n");
113 #ifdef HAVE_FALLOCATE
115 ret = fallocate(fd, 0, cur_offset, blocksize);
117 printf("Error fallocating\n");
120 /* fallthrough; seek to end of prealloc space */
123 ret = lseek(fd, blocksize, SEEK_CUR);
124 if (ret == (off_t)-1) {
125 printf("Error lseeking\n");
131 printf("Hrm, unrecognized flag in map\n");
135 cur_offset += blocksize;
144 show_extent_block(struct fiemap_extent *extent, int blocksize)
146 __u64 logical = extent->fe_logical;
147 __u64 phys = extent->fe_physical;
148 __u64 length = extent->fe_length;
149 int flags = extent->fe_flags;
151 printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
152 "flags: 0x%03X tot: %llu\n",
153 logical / blocksize, (logical + length - 1) / blocksize,
154 phys / blocksize, (phys + length - 1) / blocksize,
156 (length / blocksize));
160 show_extents(struct fiemap *fiemap, int blocksize)
164 for (i = 0; i < fiemap->fm_mapped_extents; i++)
165 show_extent_block(&fiemap->fm_extents[i], blocksize);
169 check_flags(struct fiemap *fiemap, int blocksize)
171 struct fiemap_extent *extent;
172 __u64 aligned_offset, aligned_length;
175 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
176 extent = &fiemap->fm_extents[c];
178 aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
179 aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
181 if ((aligned_offset != extent->fe_physical ||
182 aligned_length != extent->fe_length) &&
183 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
184 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
185 "but the extent is unaligned: %llu\n",
187 (extent->fe_logical / blocksize));
191 if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
192 !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
193 printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
194 "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
196 (extent->fe_logical / blocksize));
200 if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
201 aligned_offset == extent->fe_physical &&
202 aligned_length == extent->fe_length) {
203 printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
204 "offset and length is blocksize aligned: "
207 (extent->fe_logical / blocksize));
211 if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
212 c + 1 < fiemap->fm_mapped_extents) {
213 printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
214 " more extents left: %llu\n",
216 (extent->fe_logical / blocksize));
220 if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
221 !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
222 printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
223 "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
225 (extent->fe_logical / blocksize));
229 if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
230 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
231 printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
232 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
234 (extent->fe_logical / blocksize));
238 if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
239 !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
240 printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
241 "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
243 (extent->fe_logical / blocksize));
252 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
253 int last, int prealloc)
255 struct fiemap_extent *extent;
256 __u64 orig_offset = logical_offset;
259 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
261 extent = &fiemap->fm_extents[c];
263 start = extent->fe_logical;
264 end = extent->fe_logical + extent->fe_length;
266 if (logical_offset > end)
269 if (logical_offset + blocksize < start)
272 if (logical_offset >= start &&
273 logical_offset < end) {
275 !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
276 printf("ERROR: preallocated extent is not "
277 "marked with FIEMAP_EXTENT_UNWRITTEN: "
280 (start / blocksize));
284 if (logical_offset + blocksize > end) {
285 logical_offset = end+1;
295 printf("ERROR: couldn't find extent at %llu\n",
296 (unsigned long long)(orig_offset / blocksize));
298 !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
299 printf("ERROR: last extent not marked as last: %llu\n",
300 (unsigned long long)(orig_offset / blocksize));
304 return (!found) ? -1 : 0;
308 check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
310 static int warning_printed = 0;
312 size_t buf_len = sizeof(char) * blocksize;
315 block = (int)(logical_offset / blocksize);
316 if (ioctl(fd, FIBMAP, &block) < 0) {
317 perror("Can't fibmap file");
322 printf("ERROR: FIEMAP claimed there was data at a block "
323 "which should be a hole, and FIBMAP confirmend that "
324 "it is in fact a hole, so FIEMAP is wrong: %llu\n",
325 (unsigned long long)(logical_offset / blocksize));
329 buf = malloc(buf_len);
331 perror("Could not allocate temporary buffer");
335 if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
336 perror("Error reading from file");
341 for (i = 0; i < buf_len; i++) {
343 printf("ERROR: FIEMAP claimed there was data (%c) at "
344 "block %llu that should have been a hole, and "
345 "FIBMAP confirmed that it was allocated, but "
346 "it should be filled with 0's, but it was not "
347 "so you have a big problem!\n",
349 (unsigned long long)(logical_offset / blocksize));
355 if (warning_printed || quiet) {
360 printf("HEY FS PERSON: your fs is weird. I specifically wanted a\n"
361 "hole and you allocated a block anyway. FIBMAP confirms that\n"
362 "you allocated a block, and the block is filled with 0's so\n"
363 "everything is kosher, but you still allocated a block when\n"
364 "didn't need to. This may or may not be what you wanted,\n"
365 "which is why I'm only printing this message once, in case\n"
366 "you didn't do it on purpose. This was at block %llu.\n",
367 (unsigned long long)(logical_offset / blocksize));
375 check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
377 struct fiemap_extent *extent;
380 for (c = 0; c < fiemap->fm_mapped_extents; c++) {
382 extent = &fiemap->fm_extents[c];
384 start = extent->fe_logical;
385 end = extent->fe_logical + extent->fe_length;
387 if (logical_offset > end)
389 if (logical_offset + blocksize < start)
392 if (logical_offset >= start &&
393 logical_offset < end) {
395 if (check_weird_fs_hole(fd, logical_offset,
399 printf("ERROR: found an allocated extent where a hole "
401 (unsigned long long)(start / blocksize));
410 compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize)
412 struct fiemap *fiemap;
414 int blocks_to_map, ret, cur_extent = 0, last_data;
415 __u64 map_start, map_length;
418 blocks_to_map = (random() % blocks) + 1;
419 fiebuf = malloc(sizeof(struct fiemap) +
420 (blocks_to_map * sizeof(struct fiemap_extent)));
422 perror("Could not allocate fiemap buffers");
426 fiemap = (struct fiemap *)fiebuf;
428 map_length = blocks_to_map * blocksize;
430 for (i = 0; i < blocks; i++) {
435 fiemap->fm_flags = FIEMAP_FLAG_SYNC;
436 fiemap->fm_extent_count = blocks_to_map;
437 fiemap->fm_mapped_extents = 0;
440 fiemap->fm_start = map_start;
441 fiemap->fm_length = map_length;
443 ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
445 perror("FIEMAP ioctl failed");
450 if (check_flags(fiemap, blocksize))
453 for (i = cur_extent, c = 1; i < blocks; i++, c++) {
454 __u64 logical_offset = i * blocksize;
456 if (c > blocks_to_map)
461 if (check_data(fiemap, logical_offset,
462 blocksize, last_data == i, 0))
466 if (check_hole(fiemap, fd, logical_offset,
471 if (check_data(fiemap, logical_offset,
472 blocksize, last_data == i, 1))
476 printf("ERROR: weird value in map: %c\n",
482 map_start = i * blocksize;
483 } while (cur_extent < blocks);
488 printf("map is '%s'\n", map);
489 show_extents(fiemap, blocksize);
494 main(int argc, char **argv)
496 int blocksize = 0; /* filesystem blocksize */
497 int fd; /* file descriptor */
500 char *fname; /* filename to map */
501 char *map = NULL; /* file map to generate */
502 int runs = -1; /* the number of runs to have */
503 int blocks = 0; /* the number of blocks to generate */
504 int maxblocks = 0; /* max # of blocks to create */
505 int prealloc = 1; /* whether or not to do preallocation */
508 while ((opt = getopt(argc, argv, "m:r:s:p:q")) != -1) {
511 map = strdup(optarg);
514 prealloc = atoi(optarg);;
515 #ifndef HAVE_FALLOCATE
517 printf("Not built with preallocation support\n");
524 /* sync file before mapping */
536 if (runs != -1 && map)
539 fname = argv[optind++];
543 fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0644);
545 perror("Can't open file");
549 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
550 perror("Can't get filesystem block size");
555 #ifdef HAVE_FALLOCATE
556 /* if fallocate passes, then we can do preallocation, else not */
558 prealloc = !((int)fallocate(fd, 0, 0, blocksize));
560 printf("preallocation not supported, disabling\n");
566 if (ftruncate(fd, 0)) {
567 perror("Can't truncate file");
573 blocks = strlen(map);
579 /* max file size 2mb / block size */
580 maxblocks = (2 * 1024 * 1024) / blocksize;
583 printf("Starting infinite run, if you don't see any output "
584 "then its working properly.\n");
587 blocks = random() % maxblocks;
590 printf("Skipping 0 length file\n");
594 map = generate_file_mapping(blocks, prealloc);
596 printf("Could not create map\n");
601 rc = create_file_from_mapping(fd, map, blocks, blocksize);
603 perror("Could not create file\n");
609 rc = compare_fiemap_and_map(fd, map, blocks, blocksize);
611 printf("Problem comparing fiemap and map\n");
620 if (ftruncate(fd, 0)) {
621 perror("Could not truncate file\n");
626 if (lseek(fd, 0, SEEK_SET)) {
627 perror("Could not seek set\n");