xfsqa: Add fiemap exerciser
authorEric Sandeen <sandeen@redhat.com>
Sat, 6 Mar 2010 00:24:54 +0000 (11:24 +1100)
committerDave Chinner <david@fromorbit.com>
Sat, 6 Mar 2010 00:24:54 +0000 (11:24 +1100)
Preliminary fiemap testing support based on a test util written by
Josef Bacik.

For now it's only run with preallocation disabled, because xfs has a
tendency to fill in holes with data blocks (EOF prealloc stuff I
think) and similar for explicit preallocation, so this is breaking
the preallocation tests for now, when it finds a "data" block where
it expects a preallocated block.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
225 [new file with mode: 0644]
225.out [new file with mode: 0644]
aclocal.m4
configure.in
group
include/builddefs.in
src/Makefile
src/fiemap-tester.c [new file with mode: 0644]

diff --git a/225 b/225
new file mode 100644 (file)
index 0000000..673e93e
--- /dev/null
+++ b/225
@@ -0,0 +1,62 @@
+#! /bin/sh
+# FS QA Test No. 225
+#
+# Run the fiemap (file extent mapping) tester
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2009 Eric Sandeen.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+# creator
+owner=sandeen@sandeen.net
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=tmp/$$
+status=1  # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       _cleanup_testdir
+}
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+
+_setup_testdir
+
+fiemapfile=$TEST_DIR/fiemap.$$
+
+[ -x $here/src/fiemap-tester ] || _notrun "fiemap-tester not built"
+
+echo "fiemap run without preallocation"
+$here/src/fiemap-tester -q -p 0 -r 200 /mnt/test/fiemapfile
+
+rm -f $fiemapfile
+rm -f $seq.full
+
+status=0
+exit
diff --git a/225.out b/225.out
new file mode 100644 (file)
index 0000000..7bc9312
--- /dev/null
+++ b/225.out
@@ -0,0 +1,2 @@
+QA output created by 225
+fiemap run without preallocation
index 02f56f20bb0a2a238309e829f46a6023e1c9d32f..6457d3907031720bf5f7ad64e6f17f4c605fc019 100644 (file)
 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 # PARTICULAR PURPOSE.
 
+AC_DEFUN([AC_PACKAGE_WANT_LINUX_FIEMAP_H],
+  [ AC_CHECK_HEADERS([linux/fiemap.h], [ have_fiemap=true ], [ have_fiemap=false ])
+    AC_SUBST(have_fiemap)
+  ])
+
+AC_DEFUN([AC_PACKAGE_WANT_FALLOCATE],
+  [ AC_MSG_CHECKING([for fallocate])
+    AC_TRY_LINK([
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <fcntl.h>
+#include <linux/falloc.h>
+    ], [
+         fallocate(0, 0, 0, 0);
+    ], have_fallocate=yes
+       AC_MSG_RESULT(yes),
+       AC_MSG_RESULT(no))
+    AC_SUBST(have_fallocate)
+  ])
 m4_include([m4/multilib.m4])
 m4_include([m4/package_acldev.m4])
 m4_include([m4/package_aiodev.m4])
index 0d6f1533af2cd90d107a2f0d19e3f48ba4255e37..45b7fd18b338b51d595ac278202afc6a30328802 100644 (file)
@@ -64,6 +64,8 @@ in
                AC_PACKAGE_WANT_GDBM
                AC_PACKAGE_WANT_AIO
                AC_PACKAGE_WANT_DMAPI
+               AC_PACKAGE_WANT_LINUX_FIEMAP_H
+               AC_PACKAGE_WANT_FALLOCATE
                ;;
 esac
 
diff --git a/group b/group
index 5d675f0d6ea36bfbbf7a30d5f8c60f7520a03ba4..8d4a83a6f0dd3696a7617034142bcb3f1a255fda 100644 (file)
--- a/group
+++ b/group
@@ -338,3 +338,4 @@ deprecated
 222 auto fsr ioctl quick
 223 auto quick
 224 auto
+225 auto quick
index 23a499176757ecd799d8d1dabf52d344f1f991a5..3bea050221651328766800a2c226de84f2bef6df 100644 (file)
@@ -60,6 +60,8 @@ HAVE_DB = @have_db@
 HAVE_AIO = @have_aio@
 HAVE_DMAPI = @have_dmapi@
 HAVE_ATTR_LIST = @have_attr_list@
+HAVE_FIEMAP = @have_fiemap@
+HAVE_FALLOCATE = @have_fallocate@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
 
index d86d50a2b2984bbc8510c90203b614db7e2e6f9b..2f95fe20ee93edbe4139c7a0fec513436bbe3885 100644 (file)
@@ -25,6 +25,14 @@ ifeq ($(HAVE_XLOG_ASSIGN_LSN), true)
 LINUX_TARGETS += loggen
 endif
 
+ifeq ($(HAVE_FIEMAP), true)
+LINUX_TARGETS += fiemap-tester
+endif
+
+ifeq ($(HAVE_FALLOCATE),yes)
+LCFLAGS += -DHAVE_FALLOCATE
+endif
+
 IRIX_TARGETS = open_unlink
 
 ifeq ($(PKG_PLATFORM),linux)
diff --git a/src/fiemap-tester.c b/src/fiemap-tester.c
new file mode 100644 (file)
index 0000000..69016a9
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 2009 Josef Bacik
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/fiemap.h>
+
+/* Global for non-critical message suppression */
+int quiet;
+
+static void
+usage(void)
+{
+       printf("Usage: fiemap-tester [-m map] [-r number of runs] [-s seed] [-q]");
+#ifdef HAVE_FALLOCATE
+       printf("[-p preallocate (1/0)] ");
+#endif
+       printf("filename\n");
+       printf("  -m map    : generate a file with the map given and test\n");
+#ifdef HAVE_FALLOCATE
+       printf("  -p 0/1    : turn block preallocation on or off\n");
+#endif
+       printf("  -r count  : number of runs to execute (default infinity)\n");
+       printf("  -s seed   : seed for random map generator (default 1)\n");
+       printf("  -q        : be quiet about non-errors\n");
+       printf("-m and -r cannot be used together\n");
+       exit(EXIT_FAILURE);
+}
+
+static char *
+generate_file_mapping(int blocks, int prealloc)
+{
+       char *map;
+       int num_types = 2, cur_block = 0;
+       int i = 0;
+
+       map = malloc(sizeof(char) * blocks);
+       if (!map)
+               return NULL;
+
+       if (prealloc)
+               num_types++;
+
+
+       for (i = 0; i < blocks; i++) {
+               long num = random() % num_types;
+               switch (num) {
+               case 0:
+                       map[cur_block] = 'D';
+                       break;
+               case 1:
+                       map[cur_block] = 'H';
+                       break;
+               case 2:
+                       map[cur_block] = 'P';
+                       break;
+               }
+               cur_block++;
+       }
+
+       return map;
+}
+
+static int
+create_file_from_mapping(int fd, char *map, int blocks, int blocksize)
+{
+       int cur_offset = 0, ret = 0, bufsize;
+       char *buf;
+       int i = 0;
+
+       bufsize = sizeof(char) * blocksize;
+       buf = malloc(bufsize);
+       if (!buf)
+               return -1;
+
+       memset(buf, 'a', bufsize);
+
+       for (i = 0; i < blocks; i++) {
+               switch (map[i]) {
+               case 'D':
+                       ret = write(fd, buf, bufsize);
+                       if (ret < bufsize) {
+                               printf("Short write\n");
+                               ret = -1;
+                               goto out;
+                       }
+                       break;
+#ifdef HAVE_FALLOCATE
+               case 'P':
+                       ret = fallocate(fd, 0, cur_offset, blocksize);
+                       if (ret < 0) {
+                               printf("Error fallocating\n");
+                               goto out;
+                       }
+                       /* fallthrough; seek to end of prealloc space */
+#endif
+               case 'H':
+                       ret = lseek(fd, blocksize, SEEK_CUR);
+                       if (ret == (off_t)-1) {
+                               printf("Error lseeking\n");
+                               ret = -1;
+                               goto out;
+                       }
+                       break;
+               default:
+                       printf("Hrm, unrecognized flag in map\n");
+                       ret = -1;
+                       goto out;
+               }
+               cur_offset += blocksize;
+       }
+
+       ret = 0;
+out:
+       return ret;
+}
+
+static void
+show_extent_block(struct fiemap_extent *extent, int blocksize)
+{
+       __u64   logical = extent->fe_logical;
+       __u64   phys = extent->fe_physical;
+       __u64   length = extent->fe_length;
+       int     flags = extent->fe_flags;
+
+       printf("logical: [%8llu..%8llu] phys: %8llu..%8llu "
+              "flags: 0x%03X tot: %llu\n",
+               logical / blocksize, (logical + length - 1) / blocksize,
+               phys / blocksize, (phys + length - 1) / blocksize,
+               flags,
+               (length / blocksize));
+}
+
+static void
+show_extents(struct fiemap *fiemap, int blocksize)
+{
+       unsigned int i;
+
+       for (i = 0; i < fiemap->fm_mapped_extents; i++)
+               show_extent_block(&fiemap->fm_extents[i], blocksize);
+}
+
+static int
+check_flags(struct fiemap *fiemap, int blocksize)
+{
+       struct fiemap_extent *extent;
+       __u64 aligned_offset, aligned_length;
+       int c;
+
+       for (c = 0; c < fiemap->fm_mapped_extents; c++) {
+               extent = &fiemap->fm_extents[c];
+
+               aligned_offset = extent->fe_physical & ~((__u64)blocksize - 1);
+               aligned_length = extent->fe_length & ~((__u64)blocksize - 1);
+
+               if ((aligned_offset != extent->fe_physical ||
+                    aligned_length != extent->fe_length) &&
+                   !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
+                       printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is not set "
+                              "but the extent is unaligned: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED &&
+                   !(extent->fe_flags & FIEMAP_EXTENT_ENCODED)) {
+                       printf("ERROR: FIEMAP_EXTENT_DATA_ENCRYPTED is set, "
+                              "but FIEMAP_EXTENT_ENCODED is not set: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED &&
+                   aligned_offset == extent->fe_physical &&
+                   aligned_length == extent->fe_length) {
+                       printf("ERROR: FIEMAP_EXTENT_NOT_ALIGNED is set but "
+                              "offset and length is blocksize aligned: "
+                              "%llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_LAST &&
+                   c + 1 < fiemap->fm_mapped_extents) {
+                       printf("ERROR: FIEMAP_EXTENT_LAST is set but there are"
+                              " more extents left: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_DELALLOC &&
+                   !(extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)) {
+                       printf("ERROR: FIEMAP_EXTENT_DELALLOC is set but "
+                              "FIEMAP_EXTENT_UNKNOWN is not set: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE &&
+                   !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
+                       printf("ERROR: FIEMAP_EXTENT_DATA_INLINE is set but "
+                              "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+
+               if (extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL &&
+                   !(extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)) {
+                       printf("ERROR: FIEMAP_EXTENT_DATA_TAIL is set but "
+                              "FIEMAP_EXTENT_NOT_ALIGNED is not set: %llu\n",
+                              (unsigned long long)
+                              (extent->fe_logical / blocksize));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static int
+check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
+          int last, int prealloc)
+{
+       struct fiemap_extent *extent;
+       __u64 orig_offset = logical_offset;
+       int c, found = 0;
+
+       for (c = 0; c < fiemap->fm_mapped_extents; c++) {
+               __u64 start, end;
+               extent = &fiemap->fm_extents[c];
+
+               start = extent->fe_logical;
+               end = extent->fe_logical + extent->fe_length;
+
+               if (logical_offset > end)
+                       continue;
+
+               if (logical_offset + blocksize < start)
+                       break;
+
+               if (logical_offset >= start &&
+                   logical_offset < end) {
+                       if (prealloc &&
+                           !(extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+                               printf("ERROR: preallocated extent is not "
+                                      "marked with FIEMAP_EXTENT_UNWRITTEN: "
+                                      "%llu\n",
+                                      (unsigned long long)
+                                      (start / blocksize));
+                               return -1;
+                       }
+
+                       if (logical_offset + blocksize > end) {
+                               logical_offset = end+1;
+                               continue;
+                       } else {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (!found) {
+               printf("ERROR: couldn't find extent at %llu\n",
+                      (unsigned long long)(orig_offset / blocksize));
+       } else if (last &&
+                  !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
+               printf("ERROR: last extent not marked as last: %llu\n",
+                      (unsigned long long)(orig_offset / blocksize));
+               found = 0;
+       }
+
+       return (!found) ? -1 : 0;
+}
+
+static int
+check_weird_fs_hole(int fd, __u64 logical_offset, int blocksize)
+{
+       static int warning_printed = 0;
+       int block, i;
+       size_t buf_len = sizeof(char) * blocksize;
+       char *buf;
+
+       block = (int)(logical_offset / blocksize);
+       if (ioctl(fd, FIBMAP, &block) < 0) {
+               perror("Can't fibmap file");
+               return -1;
+       }
+
+       if (!block) {
+               printf("ERROR: FIEMAP claimed there was data at a block "
+                      "which should be a hole, and FIBMAP confirmend that "
+                      "it is in fact a hole, so FIEMAP is wrong: %llu\n",
+                      (unsigned long long)(logical_offset / blocksize));
+               return -1;
+       }
+
+       buf = malloc(buf_len);
+       if (!buf) {
+               perror("Could not allocate temporary buffer");
+               return -1;
+       }
+
+       if (pread(fd, buf, buf_len, (off_t)logical_offset) < 0) {
+               perror("Error reading from file");
+               free(buf);
+               return -1;
+       }
+
+       for (i = 0; i < buf_len; i++) {
+               if (buf[i] != 0) {
+                       printf("ERROR: FIEMAP claimed there was data (%c) at "
+                              "block %llu that should have been a hole, and "
+                              "FIBMAP confirmed that it was allocated, but "
+                              "it should be filled with 0's, but it was not "
+                              "so you have a big problem!\n",
+                              buf[i],
+                              (unsigned long long)(logical_offset / blocksize));
+                       free(buf);
+                       return -1;
+               }
+       }
+
+       if (warning_printed || quiet) {
+               free(buf);
+               return 0;
+       }
+
+       printf("HEY FS PERSON: your fs is weird.  I specifically wanted a\n"
+              "hole and you allocated a block anyway.  FIBMAP confirms that\n"
+              "you allocated a block, and the block is filled with 0's so\n"
+              "everything is kosher, but you still allocated a block when\n"
+              "didn't need to.  This may or may not be what you wanted,\n"
+              "which is why I'm only printing this message once, in case\n"
+              "you didn't do it on purpose. This was at block %llu.\n",
+              (unsigned long long)(logical_offset / blocksize));
+       warning_printed = 1;
+       free(buf);
+
+       return 0;
+}
+
+static int
+check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
+{
+       struct fiemap_extent *extent;
+       int c;
+
+       for (c = 0; c < fiemap->fm_mapped_extents; c++) {
+               __u64 start, end;
+               extent = &fiemap->fm_extents[c];
+
+               start = extent->fe_logical;
+               end = extent->fe_logical + extent->fe_length;
+
+               if (logical_offset > end)
+                       continue;
+               if (logical_offset + blocksize < start)
+                       break;
+
+               if (logical_offset >= start &&
+                   logical_offset < end) {
+
+                       if (check_weird_fs_hole(fd, logical_offset,
+                                               blocksize) == 0)
+                               break;
+
+                       printf("ERROR: found an allocated extent where a hole "
+                              "should be: %llu\n",
+                              (unsigned long long)(start / blocksize));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static int
+compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize)
+{
+       struct fiemap *fiemap;
+       char *fiebuf;
+       int blocks_to_map, ret, cur_extent = 0, last_data;
+       __u64 map_start, map_length;
+       int i, c;
+
+       blocks_to_map = (random() % blocks) + 1;
+       fiebuf = malloc(sizeof(struct fiemap) +
+                       (blocks_to_map * sizeof(struct fiemap_extent)));
+       if (!fiebuf) {
+               perror("Could not allocate fiemap buffers");
+               return -1;
+       }
+
+       fiemap = (struct fiemap *)fiebuf;
+       map_start = 0;
+       map_length = blocks_to_map * blocksize;
+
+       for (i = 0; i < blocks; i++) {
+               if (map[i] != 'H')
+                       last_data = i;
+       }
+
+       fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+       fiemap->fm_extent_count = blocks_to_map;
+       fiemap->fm_mapped_extents = 0;
+
+       do {
+               fiemap->fm_start = map_start;
+               fiemap->fm_length = map_length;
+
+               ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+               if (ret < 0) {
+                       perror("FIEMAP ioctl failed");
+                       free(fiemap);
+                       return -1;
+               }
+
+               if (check_flags(fiemap, blocksize))
+                       goto error;
+
+               for (i = cur_extent, c = 1; i < blocks; i++, c++) {
+                       __u64 logical_offset = i * blocksize;
+
+                       if (c > blocks_to_map)
+                               break;
+
+                       switch (map[i]) {
+                       case 'D':
+                               if (check_data(fiemap, logical_offset,
+                                              blocksize, last_data == i, 0))
+                                       goto error;
+                               break;
+                       case 'H':
+                               if (check_hole(fiemap, fd, logical_offset,
+                                              blocksize))
+                                       goto error;
+                               break;
+                       case 'P':
+                               if (check_data(fiemap, logical_offset,
+                                              blocksize, last_data == i, 1))
+                                       goto error;
+                               break;
+                       default:
+                               printf("ERROR: weird value in map: %c\n",
+                                      map[i]);
+                               goto error;
+                       }
+               }
+               cur_extent = i;
+               map_start = i * blocksize;
+       } while (cur_extent < blocks);
+
+       ret = 0;
+       return ret;
+error:
+       printf("map is '%s'\n", map);
+       show_extents(fiemap, blocksize);
+       return -1;
+}
+
+int
+main(int argc, char **argv)
+{
+       int     blocksize = 0;  /* filesystem blocksize */
+       int     fd;             /* file descriptor */
+       int     opt;
+       int     rc;
+       char    *fname;         /* filename to map */
+       char    *map = NULL;    /* file map to generate */
+       int     runs = -1;      /* the number of runs to have */
+       int     blocks = 0;     /* the number of blocks to generate */
+       int     maxblocks = 0;  /* max # of blocks to create */
+       int     prealloc = 1;   /* whether or not to do preallocation */
+       int     seed = 1;
+
+       while ((opt = getopt(argc, argv, "m:r:s:p:q")) != -1) {
+               switch(opt) {
+               case 'm':
+                       map = strdup(optarg);
+                       break;
+               case 'p':
+                       prealloc = atoi(optarg);;
+#ifndef HAVE_FALLOCATE
+                       if (prealloc)
+                               printf("Not built with preallocation support\n");
+                       usage();
+#endif
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               /* sync file before mapping */
+               case 'r':
+                       runs = atoi(optarg);
+                       break;
+               case 's':
+                       seed = atoi(optarg);
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       if (runs != -1 && map)
+               usage();
+
+       fname = argv[optind++];
+       if (!fname)
+               usage();
+
+       fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0644);
+       if (fd < 0) {
+               perror("Can't open file");
+               exit(1);
+       }
+
+       if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
+               perror("Can't get filesystem block size");
+               close(fd);
+               exit(1);
+       }
+
+#ifdef HAVE_FALLOCATE
+       /* if fallocate passes, then we can do preallocation, else not */
+       if (prealloc) {
+               prealloc = !((int)fallocate(fd, 0, 0, blocksize));
+               if (!prealloc)
+                       printf("preallocation not supported, disabling\n");
+       }
+#else
+       prealloc = 0;
+#endif
+
+       if (ftruncate(fd, 0)) {
+               perror("Can't truncate file");
+               close(fd);
+               exit(1);
+       }
+
+       if (map) {
+               blocks = strlen(map);
+               runs = 0;
+       }
+
+       srandom(seed);
+
+       /* max file size 2mb / block size */
+       maxblocks = (2 * 1024 * 1024) / blocksize;
+
+       if (runs == -1)
+               printf("Starting infinite run, if you don't see any output "
+                      "then its working properly.\n");
+       do {
+               if (!map) {
+                       blocks = random() % maxblocks;
+                       if (blocks == 0) {
+                               if (!quiet)
+                                       printf("Skipping 0 length file\n");
+                               continue;
+                       }
+
+                       map = generate_file_mapping(blocks, prealloc);
+                       if (!map) {
+                               printf("Could not create map\n");
+                               exit(1);
+                       }
+               }
+
+               rc = create_file_from_mapping(fd, map, blocks, blocksize);
+               if (rc) {
+                       perror("Could not create file\n");
+                       free(map);
+                       close(fd);
+                       exit(1);
+               }
+
+               rc = compare_fiemap_and_map(fd, map, blocks, blocksize);
+               if (rc) {
+                       printf("Problem comparing fiemap and map\n");
+                       free(map);
+                       close(fd);
+                       exit(1);
+               }
+
+               free(map);
+               map = NULL;
+
+               if (ftruncate(fd, 0)) {
+                       perror("Could not truncate file\n");
+                       close(fd);
+                       exit(1);
+               }
+
+               if (lseek(fd, 0, SEEK_SET)) {
+                       perror("Could not seek set\n");
+                       close(fd);
+                       exit(1);
+               }
+
+               if (runs) runs--;
+       } while (runs != 0);
+
+       close(fd);
+
+       return 0;
+}