* [PATCH] Xfsprogs: add fiemap command to xfs_io V2
@ 2010-11-12 17:19 Josef Bacik
2010-11-18 5:10 ` Dave Chinner
0 siblings, 1 reply; 4+ messages in thread
From: Josef Bacik @ 2010-11-12 17:19 UTC (permalink / raw)
To: xfs
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@redhat.com>
---
V1->V2: add checks to make sure the system supports fiemap so xfsprogs builds on
things other than linux :).
configure.in | 1 +
include/builddefs.in | 1 +
io/Makefile | 7 +
io/fiemap.c | 342 +++++++++++++++++++++++++++++++++++++++++++++++++
io/init.c | 1 +
io/io.h | 6 +
m4/package_libcdev.m4 | 8 +
7 files changed, 366 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..0d9894a
--- /dev/null
+++ b/io/fiemap.c
@@ -0,0 +1,342 @@
+/*
+ * 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"
+" 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
+"\n"
+" bmap 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"
+" Note: the bmap for non-regular files can be obtained provided the file\n"
+" was opened appropriately (in particular, must be opened read-only).\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 *cur_extent,
+ __u64 *last_logical)
+{
+ __u64 lstart;
+ __u64 len;
+ __u64 block;
+ char lbuf[32];
+ char bbuf[32];
+ 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 - 1);
+ 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));
+ }
+ snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", lstart,
+ lstart + len - 1);
+ snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, block + len - 1);
+ 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 *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 - 1LL);
+ if (lflag)
+ printf(_(" %llu blocks\n"),
+ lstart - *last_logical);
+ else
+ printf("\n");
+ (*cur_extent)++;
+ }
+
+ printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent,
+ lstart, lstart + len - 1LL, block,
+ block + len - 1);
+
+ 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 num_extents = 32;
+ 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':
+ num_extents = atoi(optarg);
+ nflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ return command_usage(&fiemap_cmd);
+ }
+ }
+
+ 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;
+ }
+
+ memset(fiemap, 0, map_size);
+ fiemap->fm_flags = fiemap_flags;
+ fiemap->fm_length = -1;
+ fiemap->fm_extent_count = nflag ? 0 : 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;
+ }
+
+ if (!nflag) {
+ if (fiemap->fm_mapped_extents > num_extents) {
+ num_extents = fiemap->fm_mapped_extents;
+ map_size = sizeof(struct fiemap) +
+ (num_extents * sizeof(struct fiemap_extent));
+ fiemap = realloc(fiemap, map_size);
+ if (!fiemap) {
+ fprintf(stderr, _("%s: realloc of %d bytes "
+ "failed.\n"), progname,
+ map_size);
+ exitcode = 1;
+ return 0;
+ }
+ }
+ memset(fiemap, 0, map_size);
+ fiemap->fm_flags = fiemap_flags;
+ 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;
+ }
+ }
+
+ 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"));
+ }
+
+ 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, &cur_extent, &last_logical);
+ else
+ print_plain(extent, lflag, blocksize,
+ &cur_extent, &last_logical);
+ if (extent->fe_flags & FIEMAP_EXTENT_LAST)
+ break;
+ }
+
+ 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");
+ }
+ }
+
+ 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@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] Xfsprogs: add fiemap command to xfs_io V2
2010-11-12 17:19 [PATCH] Xfsprogs: add fiemap command to xfs_io V2 Josef Bacik
@ 2010-11-18 5:10 ` Dave Chinner
2010-11-18 8:25 ` Josef Bacik
0 siblings, 1 reply; 4+ messages in thread
From: Dave Chinner @ 2010-11-18 5:10 UTC (permalink / raw)
To: Josef Bacik; +Cc: xfs
On Fri, Nov 12, 2010 at 12:19:34PM -0500, Josef Bacik wrote:
> 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@redhat.com>
> ---
> V1->V2: add checks to make sure the system supports fiemap so xfsprogs builds on
> things other than linux :).
> +static void
> +fiemap_help(void)
> +{
> + printf(_(
> +"\n"
> +" prints the block mapping for a file's data or attribute forks"
> +"\n"
> +" Example:\n"
> +" 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
'fiemap -vp' ?
> +"\n"
> +" bmap prints the map of disk blocks used by the current file.\n"
fiemap prints...
> +" 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"
> +" Note: the bmap for non-regular files can be obtained provided the file\n"
> +" was opened appropriately (in particular, must be opened read-only).\n"
> +"\n"));
This last note about non-regular files is not true for fiemap,
right?
> +}
> +
> +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 *cur_extent,
> + __u64 *last_logical)
> +{
> + __u64 lstart;
> + __u64 len;
> + __u64 block;
> + char lbuf[32];
> + char bbuf[32];
I don't think these two buffers are large enough to hold 2
64 bit integers as strings.
> +static void
> +print_plain(
> + struct fiemap_extent *extent,
> + int lflag,
> + int blocksize,
> + 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 - 1LL);
> + if (lflag)
> + printf(_(" %llu blocks\n"),
> + lstart - *last_logical);
> + else
> + printf("\n");
> + (*cur_extent)++;
> + }
> +
> + printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent,
> + lstart, lstart + len - 1LL, block,
> + block + len - 1);
Why the "1LL" here and not anywhere else?
> +
> + 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 num_extents = 32;
> + 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':
> + num_extents = atoi(optarg);
> + nflag = 1;
> + break;
> + case 'v':
> + vflag++;
> + break;
> + default:
> + return command_usage(&fiemap_cmd);
> + }
> + }
> +
> + map_size = sizeof(struct fiemap) +
> + (num_extents * sizeof(struct fiemap_extent));
Need to validate num_extents before using it.
> + fiemap = malloc(map_size);
> + if (!fiemap) {
> + fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
> + progname, map_size);
> + exitcode = 1;
> + return 0;
> + }
> +
> + memset(fiemap, 0, map_size);
> + fiemap->fm_flags = fiemap_flags;
> + fiemap->fm_length = -1;
> + fiemap->fm_extent_count = nflag ? 0 : 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;
> + }
> +
> + if (!nflag) {
> + if (fiemap->fm_mapped_extents > num_extents) {
> + num_extents = fiemap->fm_mapped_extents;
> + map_size = sizeof(struct fiemap) +
> + (num_extents * sizeof(struct fiemap_extent));
> + fiemap = realloc(fiemap, map_size);
> + if (!fiemap) {
> + fprintf(stderr, _("%s: realloc of %d bytes "
> + "failed.\n"), progname,
> + map_size);
> + exitcode = 1;
> + return 0;
> + }
> + }
> + memset(fiemap, 0, map_size);
> + fiemap->fm_flags = fiemap_flags;
> + 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;
> + }
> + }
Hmmm. I'd prefer to see a loop mapping and printing N extents at a
time rather than one massive allocation and hoping it fits in memory
(think of a file with hundreds of thousands of extents). That's a
problem with the existing xfs_bmap code - should we be duplicating
that problem here?
Otherwise seems fine.
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] Xfsprogs: add fiemap command to xfs_io V2
2010-11-18 5:10 ` Dave Chinner
@ 2010-11-18 8:25 ` Josef Bacik
0 siblings, 0 replies; 4+ messages in thread
From: Josef Bacik @ 2010-11-18 8:25 UTC (permalink / raw)
To: Dave Chinner; +Cc: Josef Bacik, xfs
On Thu, Nov 18, 2010 at 04:10:29PM +1100, Dave Chinner wrote:
> On Fri, Nov 12, 2010 at 12:19:34PM -0500, Josef Bacik wrote:
> > 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@redhat.com>
> > ---
> > V1->V2: add checks to make sure the system supports fiemap so xfsprogs builds on
> > things other than linux :).
> > +static void
> > +fiemap_help(void)
> > +{
> > + printf(_(
> > +"\n"
> > +" prints the block mapping for a file's data or attribute forks"
> > +"\n"
> > +" Example:\n"
> > +" 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
>
> 'fiemap -vp' ?
>
> > +"\n"
> > +" bmap prints the map of disk blocks used by the current file.\n"
>
> fiemap prints...
>
> > +" 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"
> > +" Note: the bmap for non-regular files can be obtained provided the file\n"
> > +" was opened appropriately (in particular, must be opened read-only).\n"
> > +"\n"));
>
> This last note about non-regular files is not true for fiemap,
> right?
>
> > +}
> > +
> > +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 *cur_extent,
> > + __u64 *last_logical)
> > +{
> > + __u64 lstart;
> > + __u64 len;
> > + __u64 block;
> > + char lbuf[32];
> > + char bbuf[32];
>
> I don't think these two buffers are large enough to hold 2
> 64 bit integers as strings.
>
> > +static void
> > +print_plain(
> > + struct fiemap_extent *extent,
> > + int lflag,
> > + int blocksize,
> > + 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 - 1LL);
> > + if (lflag)
> > + printf(_(" %llu blocks\n"),
> > + lstart - *last_logical);
> > + else
> > + printf("\n");
> > + (*cur_extent)++;
> > + }
> > +
> > + printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent,
> > + lstart, lstart + len - 1LL, block,
> > + block + len - 1);
>
> Why the "1LL" here and not anywhere else?
>
> > +
> > + 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 num_extents = 32;
> > + 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':
> > + num_extents = atoi(optarg);
> > + nflag = 1;
> > + break;
> > + case 'v':
> > + vflag++;
> > + break;
> > + default:
> > + return command_usage(&fiemap_cmd);
> > + }
> > + }
> > +
> > + map_size = sizeof(struct fiemap) +
> > + (num_extents * sizeof(struct fiemap_extent));
>
> Need to validate num_extents before using it.
>
> > + fiemap = malloc(map_size);
> > + if (!fiemap) {
> > + fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
> > + progname, map_size);
> > + exitcode = 1;
> > + return 0;
> > + }
> > +
> > + memset(fiemap, 0, map_size);
> > + fiemap->fm_flags = fiemap_flags;
> > + fiemap->fm_length = -1;
> > + fiemap->fm_extent_count = nflag ? 0 : 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;
> > + }
> > +
> > + if (!nflag) {
> > + if (fiemap->fm_mapped_extents > num_extents) {
> > + num_extents = fiemap->fm_mapped_extents;
> > + map_size = sizeof(struct fiemap) +
> > + (num_extents * sizeof(struct fiemap_extent));
> > + fiemap = realloc(fiemap, map_size);
> > + if (!fiemap) {
> > + fprintf(stderr, _("%s: realloc of %d bytes "
> > + "failed.\n"), progname,
> > + map_size);
> > + exitcode = 1;
> > + return 0;
> > + }
> > + }
> > + memset(fiemap, 0, map_size);
> > + fiemap->fm_flags = fiemap_flags;
> > + 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;
> > + }
> > + }
>
> Hmmm. I'd prefer to see a loop mapping and printing N extents at a
> time rather than one massive allocation and hoping it fits in memory
> (think of a file with hundreds of thousands of extents). That's a
> problem with the existing xfs_bmap code - should we be duplicating
> that problem here?
>
Heh you know thats what I did before but decided it was better to do it the way
bmap did it. Thanks for the review, I'll fix all these things up,
Josef
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] Xfsprogs: add fiemap command to xfs_io V2
@ 2010-11-19 19:28 Josef Bacik
0 siblings, 0 replies; 4+ messages in thread
From: Josef Bacik @ 2010-11-19 19:28 UTC (permalink / raw)
To: xfs, david
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@redhat.com>
---
V1->V2:
-various formatting problems
-loop calling fiemap with a max of 32 extents at a time
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@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-11-19 19:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-12 17:19 [PATCH] Xfsprogs: add fiemap command to xfs_io V2 Josef Bacik
2010-11-18 5:10 ` Dave Chinner
2010-11-18 8:25 ` Josef Bacik
-- strict thread matches above, loose matches on Subject: below --
2010-11-19 19:28 Josef Bacik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox