[PATCH] Xfsprogs: add fiemap command to xfs_io

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When trying to add a test for hole punching I noticed that the "bmap" command
only works on XFS, which makes testing hole punching on other fs's kind of
difficult.  To fix this I've added an fiemap command that works almost exactly
like bmap.  It is formatted similarly and takes similar flags, the only thing
thats different is obviously it doesn't spit out AG info and it doesn't make
finding prealloc space optional.  This is my first foray into all of this stuff
so a good hard look would be appreciated.  I tested it with a few different
files to make sure bmap and fiemap both looked the same.  Thanks,

Signed-off-by: Josef Bacik <josef@xxxxxxxxxx>
---
 configure.in          |    1 +
 include/builddefs.in  |    1 +
 io/Makefile           |    7 +
 io/fiemap.c           |  347 +++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c             |    1 +
 io/io.h               |    6 +
 m4/package_libcdev.m4 |    8 +
 7 files changed, 371 insertions(+), 0 deletions(-)
 create mode 100644 io/fiemap.c

diff --git a/configure.in b/configure.in
index 30907de..16441ce 100644
--- a/configure.in
+++ b/configure.in
@@ -103,6 +103,7 @@ AC_HAVE_SENDFILE
 AC_HAVE_GETMNTENT
 AC_HAVE_GETMNTINFO
 AC_HAVE_FALLOCATE
+AC_HAVE_FIEMAP
 AC_HAVE_BLKID_TOPO($enable_blkid)
 
 AC_TYPE_PSINT
diff --git a/include/builddefs.in b/include/builddefs.in
index 93d1e67..f895ed9 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -98,6 +98,7 @@ HAVE_SENDFILE = @have_sendfile@
 HAVE_GETMNTENT = @have_getmntent@
 HAVE_GETMNTINFO = @have_getmntinfo@
 HAVE_FALLOCATE = @have_fallocate@
+HAVE_FIEMAP = @have_fiemap@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall 
 #	   -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl
diff --git a/io/Makefile b/io/Makefile
index fc98166..9d79dca 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -44,6 +44,13 @@ else
 LSRCFILES += sendfile.c
 endif
 
+ifeq ($(HAVE_FIEMAP),yes)
+CFILES += fiemap.c
+LCFLAGS += -DHAVE_FIEMAP
+else
+LSRCFILES += fiemap.c
+endif
+
 ifeq ($(PKG_PLATFORM),irix)
 LSRCFILES += inject.c resblks.c
 else
diff --git a/io/fiemap.c b/io/fiemap.c
new file mode 100644
index 0000000..fa990cc
--- /dev/null
+++ b/io/fiemap.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ * 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
+ */
+
+#include <xfs/xfs.h>
+#include <xfs/command.h>
+#include <linux/fiemap.h>
+#include <linux/fs.h>
+#include "init.h"
+#include "io.h"
+
+static cmdinfo_t fiemap_cmd;
+
+static void
+fiemap_help(void)
+{
+	printf(_(
+"\n"
+" prints the block mapping for a file's data or attribute forks"
+"\n"
+" Example:\n"
+" 'fiemap -v' - tabular format verbose map\n"
+"\n"
+" fiemap prints the map of disk blocks used by the current file.\n"
+" The map lists each extent used by the file, as well as regions in the\n"
+" file that do not have any corresponding blocks (holes).\n"
+" By default, each line of the listing takes the following form:\n"
+"     extent: [startoffset..endoffset]: startblock..endblock\n"
+" Holes are marked by replacing the startblock..endblock with 'hole'.\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -a -- prints the attribute fork map instead of the data fork.\n"
+" -l -- also displays the length of each extent in 512-byte blocks.\n"
+" -n -- query n extents.\n"
+" -v -- Verbose information\n"
+"\n"));
+}
+
+static int
+numlen(
+	__u64	val,
+	int	base)
+{
+	__u64	tmp;
+	int	len;
+
+	for (len = 0, tmp = val; tmp > 0; tmp = tmp/base)
+		len++;
+	return (len == 0 ? 1 : len);
+}
+
+static void
+print_verbose(
+	struct fiemap_extent	*extent,
+	int			blocksize,
+	int			foff_w,
+	int			boff_w,
+	int			tot_w,
+	int			flg_w,
+	int			max_extents,
+	int			*cur_extent,
+	__u64			*last_logical)
+{
+	__u64			lstart;
+	__u64			len;
+	__u64			block;
+	char			lbuf[48];
+	char			bbuf[48];
+	char			flgbuf[16];
+
+	lstart = extent->fe_logical / blocksize;
+	len = extent->fe_length / blocksize;
+	block = extent->fe_physical / blocksize;
+
+	memset(lbuf, 0, sizeof(lbuf));
+	memset(bbuf, 0, sizeof(bbuf));
+
+	if (lstart != *last_logical) {
+		snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", *last_logical,
+			 lstart - 1ULL);
+		printf("%4d: %-*s %-*s %*llu\n", *cur_extent, foff_w, lbuf,
+		       boff_w, _("hole"), tot_w, lstart - *last_logical);
+		(*cur_extent)++;
+		memset(lbuf, 0, sizeof(lbuf));
+	}
+
+	if ((*cur_extent + 1) == max_extents)
+		return;
+
+	snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", lstart,
+		 lstart + len - 1ULL);
+	snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, block + len - 1ULL);
+	snprintf(flgbuf, sizeof(flgbuf), "0x%x", extent->fe_flags);
+	printf("%4d: %-*s %-*s %*llu %*s\n", *cur_extent, foff_w, lbuf,
+	       boff_w, bbuf, tot_w, len, flg_w, flgbuf);
+
+	(*cur_extent)++;
+	*last_logical = lstart + len;
+}
+
+static void
+print_plain(
+	struct fiemap_extent	*extent,
+	int			lflag,
+	int			blocksize,
+	int			max_extents,
+	int			*cur_extent,
+	__u64			*last_logical)
+{
+	__u64			lstart;
+	__u64			block;
+	__u64			len;
+
+	lstart = extent->fe_logical / blocksize;
+	len = extent->fe_length / blocksize;
+	block = extent->fe_physical / blocksize;
+
+	if (lstart != *last_logical) {
+		printf("\t%d: [%llu..%llu]: hole", *cur_extent,
+		       *last_logical, lstart - 1ULL);
+		if (lflag)
+			printf(_(" %llu blocks\n"),
+			       lstart - *last_logical);
+		else
+			printf("\n");
+		(*cur_extent)++;
+	}
+
+	if ((*cur_extent + 1) == max_extents)
+		return;
+
+	printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent,
+	       lstart, lstart + len - 1ULL, block,
+	       block + len - 1ULL);
+
+	if (lflag)
+		printf(_(" %llu blocks\n"), len);
+	else
+		printf("\n");
+	(*cur_extent)++;
+	*last_logical = lstart + len;
+}
+
+int
+fiemap_f(
+	int		argc,
+	char		**argv)
+{
+	struct fiemap	*fiemap;
+	int		max_extents = 0;
+	int		num_extents = 32;
+	int		last = 0;
+	int		nflag = 0;
+	int		lflag = 0;
+	int		vflag = 0;
+	int		fiemap_flags = FIEMAP_FLAG_SYNC;
+	int		c;
+	int		i;
+	int		map_size;
+	int		ret;
+	int		cur_extent = 0;
+	int		foff_w = 16;	/* 16 just for a good minimum range */
+	int		boff_w = 16;
+	int		tot_w = 5;	/* 5 since its just one number */
+	int		flg_w = 5;
+	__u64		blocksize = 512;
+	__u64		last_logical = 0;
+	struct stat	st;
+
+	while ((c = getopt(argc, argv, "aln:v")) != EOF) {
+		switch (c) {
+		case 'a':
+			fiemap_flags |= FIEMAP_FLAG_XATTR;
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'n':
+			max_extents = atoi(optarg);
+			nflag = 1;
+			break;
+		case 'v':
+			vflag++;
+			break;
+		default:
+			return command_usage(&fiemap_cmd);
+		}
+	}
+
+	if (max_extents)
+		num_extents = min(num_extents, max_extents);
+	map_size = sizeof(struct fiemap) +
+		(num_extents * sizeof(struct fiemap_extent));
+	fiemap = malloc(map_size);
+	if (!fiemap) {
+		fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
+			progname, map_size);
+		exitcode = 1;
+		return 0;
+	}
+
+	printf("%s:\n", file->name);
+
+	if (vflag) {
+		for (i = 0; i < fiemap->fm_mapped_extents; i++) {
+			char			lbuf[32];
+			char			bbuf[32];
+			__u64			logical;
+			__u64			block;
+			__u64			len;
+			struct fiemap_extent	*extent;
+
+			extent = &fiemap->fm_extents[i];
+			logical = extent->fe_logical / blocksize;
+			len = extent->fe_length / blocksize;
+			block = extent->fe_physical / blocksize;
+
+			snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]", logical,
+				 logical + len - 1);
+			snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block,
+				 block + len - 1);
+			foff_w = max(foff_w, strlen(lbuf));
+			boff_w = max(boff_w, strlen(bbuf));
+			tot_w = max(tot_w, numlen(len, 10));
+			flg_w = max(flg_w, numlen(extent->fe_flags, 16));
+			if (extent->fe_flags & FIEMAP_EXTENT_LAST)
+				break;
+		}
+		printf("%4s: %-*s %-*s %*s %*s\n", _("EXT"),
+		       foff_w, _("FILE-OFFSET"),
+		       boff_w, _("BLOCK-RANGE"),
+		       tot_w, _("TOTAL"),
+		       flg_w, _("FLAGS"));
+	}
+
+	while (!last && ((cur_extent + 1) != max_extents)) {
+		if (max_extents)
+			num_extents = min(num_extents,
+					  max_extents - (cur_extent + 1));
+
+		memset(fiemap, 0, map_size);
+		fiemap->fm_flags = fiemap_flags;
+		fiemap->fm_start = last_logical;
+		fiemap->fm_length = -1;
+		fiemap->fm_extent_count = num_extents;
+
+		ret = ioctl(file->fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+		if (ret < 0) {
+			fprintf(stderr, "%s: ioctl(FS_IOC_FIEMAP) [\"%s\"]: "
+				"%s\n", progname, file->name, strerror(errno));
+			free(fiemap);
+			exitcode = 1;
+			return 0;
+		}
+
+		/* No more extents to map, exit */
+		if (!fiemap->fm_mapped_extents)
+			break;
+
+		for (i = 0; i < fiemap->fm_mapped_extents; i++) {
+			struct fiemap_extent	*extent;
+
+			extent = &fiemap->fm_extents[i];
+			if (vflag)
+				print_verbose(extent, blocksize, foff_w,
+					      boff_w, tot_w, flg_w,
+					      max_extents, &cur_extent,
+					      &last_logical);
+			else
+				print_plain(extent, lflag, blocksize,
+					    max_extents, &cur_extent,
+					    &last_logical);
+			if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
+				last = 1;
+				break;
+			}
+
+			if ((cur_extent + 1) == max_extents)
+				break;
+		}
+	}
+
+	if ((cur_extent + 1) == max_extents)
+		goto out;
+
+	memset(&st, 0, sizeof(st));
+	if (fstat(file->fd, &st)) {
+		fprintf(stderr, "%s: fstat failed: %s\n", progname,
+			strerror(errno));
+		free(fiemap);
+		exitcode = 1;
+		return 0;
+	}
+
+	if (cur_extent && last_logical < (st.st_size / blocksize)) {
+		char	lbuf[32];
+
+		snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:",
+			 last_logical, (st.st_size / blocksize) - 1);
+		if (vflag) {
+			printf("%4d: %-*s %-*s %*llu\n", cur_extent,
+			       foff_w, lbuf, boff_w, _("hole"), tot_w,
+			       (st.st_size / blocksize) - last_logical);
+		} else {
+			printf("\t%d: %s %s", cur_extent, lbuf,
+			       _("hole"));
+			if (lflag)
+				printf(_(" %llu blocks\n"),
+				       (st.st_size / blocksize) -
+				       last_logical);
+			else
+				printf("\n");
+		}
+	}
+
+out:
+	free(fiemap);
+	return 0;
+}
+
+void
+fiemap_init(void)
+{
+	fiemap_cmd.name = "fiemap";
+	fiemap_cmd.cfunc = fiemap_f;
+	fiemap_cmd.argmin = 0;
+	fiemap_cmd.argmax = -1;
+	fiemap_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
+	fiemap_cmd.args = _("[-alv] [-n nx]");
+	fiemap_cmd.oneline = _("print block mapping for a file");
+	fiemap_cmd.help = fiemap_help;
+
+	add_command(&fiemap_cmd);
+}
diff --git a/io/init.c b/io/init.c
index f8fc25d..a166ad1 100644
--- a/io/init.c
+++ b/io/init.c
@@ -71,6 +71,7 @@ init_commands(void)
 	parent_init();
 	pread_init();
 	prealloc_init();
+	fiemap_init();
 	pwrite_init();
 	quit_init();
 	resblks_init();
diff --git a/io/io.h b/io/io.h
index 630897d..2923362 100644
--- a/io/io.h
+++ b/io/io.h
@@ -135,3 +135,9 @@ extern void		mincore_init(void);
 #else
 #define mincore_init()	do { } while (0)
 #endif
+
+#ifdef HAVE_FIEMAP
+extern void		fiemap_init(void);
+#else
+#define fiemap_init()	do { } while (0)
+#endif
diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
index 1c1859d..8192181 100644
--- a/m4/package_libcdev.m4
+++ b/m4/package_libcdev.m4
@@ -116,3 +116,11 @@ AC_DEFUN([AC_HAVE_FALLOCATE],
        AC_MSG_RESULT(no))
     AC_SUBST(have_fallocate)
   ])
+
+#
+# Check if we have the fiemap ioctl (Linux)
+#
+AC_DEFUN([AC_HAVE_FIEMAP],
+  [ AC_CHECK_HEADERS([linux/fiemap.h], [ have_fiemap=yes ], [ have_fiemap=no ])
+    AC_SUBST(have_fiemap)
+  ])
-- 
1.6.6.1

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs


[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux