* [PATCH 1/3] document the XFS_IOC_GETFSMAPX ioctl
2016-05-05 19:47 [RFC 0/3] getfsmapx ioctl Darrick J. Wong
@ 2016-05-05 19:50 ` Darrick J. Wong
2016-05-05 19:51 ` [PATCH 2/3] xfs: introduce " Darrick J. Wong
2016-05-05 19:52 ` [PATCH 3/3] xfs_io: support the new getfsmap ioctl Darrick J. Wong
2 siblings, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:50 UTC (permalink / raw)
To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
Ross Zwisler, Dan Williams
Cc: linux-fsdevel, linux-api, xfs, linux-btrfs, linux-ext4
Document the new XFS_IOC_GETFSMAPX that returns the physical layout
of a (disk-based) filesystem.
(Yes, the leading 'X' needs to fall off...)
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
man2/ioctl_getfsmapx.2 | 253 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 253 insertions(+)
create mode 100644 man2/ioctl_getfsmapx.2
diff --git a/man2/ioctl_getfsmapx.2 b/man2/ioctl_getfsmapx.2
new file mode 100644
index 0000000..b79a8e5
--- /dev/null
+++ b/man2/ioctl_getfsmapx.2
@@ -0,0 +1,253 @@
+.\" Copyright (C) 2016 Oracle. All rights reserved.
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" 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
+.\" %%%LICENSE_END
+.TH IOCTL-XFS_IOC_GETFSMAPX 2 2016-05-05 "Linux" "Linux Programmer's Manual"
+.SH NAME
+ioctl_getfsmapx \- retrieve the physical layout of the filesystem
+.SH SYNOPSIS
+.br
+.B #include <sys/ioctl.h>
+.br
+.B #include <linux/fs.h>
+.sp
+.BI "int ioctl(int " fd ", XFS_IOC_GETFSMAPX, struct getfsmapx * " arg );
+.SH DESCRIPTION
+This
+.BR ioctl (2)
+retrieves physical extent mappings for a filesystem. This information can
+be used to discover which files are mapped to a physical block, examine
+free space, or find known bad blocks, among other things.
+
+The sole argument to this ioctl should be an array of the following
+structure:
+.in +4n
+.nf
+
+struct getfsmapx {
+ __s64 fmv_device; /* device id */
+ __s64 fmv_block; /* starting block */
+ __s64 fmv_owner; /* owner id */
+ __s64 fmv_offset; /* file offset of segment */
+ __s64 fmv_length; /* length of segment, blocks */
+ __s32 fmv_oflags; /* mapping flags */
+ __s32 fmv_iflags; /* control flags (1st structure) */
+ __s32 fmv_count; /* # of entries in array incl. input */
+ __s32 fmv_entries; /* # of entries filled in (output). */
+ __s64 fmv_unused1; /* future use, must be zero */
+};
+
+.fi
+.in
+The array must contain at least two elements. The first two array
+elements specify the lowest and highest reverse-mapping keys, respectively,
+for which userspace would like physical mapping information. A reverse
+mapping key consists of the tuple (device, block, owner, offset). The
+owner and offset fields are part of the key because some filesystems
+support sharing physical blocks between multiple files and therefore may
+return multiple mappings for a given physical block.
+
+.SS Fields of struct getfsmapx
+.PP
+The
+.I fmv_device
+field contains a 64-bit cookie to uniquely identify the underlying storage
+device if the filesystem supports multiple devices. If not, the field
+should be
+.BR FMV_DEV_DEFAULT "."
+
+.PP
+The
+.I fmv_block
+field contains the 512-byte sector address of the extent.
+
+.PP
+The
+.I fmv_owner
+field contains the owner of the extent. This is generally an inode
+number, though if
+.B FMV_OF_SPECIAL_OWNER
+is set in the
+.I fmv_oflags
+field, then the owner value is one of the following special values:
+.TP
+.B FMV_OWN_FREE
+Free space.
+.TP
+.B FMV_OWN_UNKNOWN
+This extent has an unknown owner.
+.TP
+.B FMV_OWN_FS
+Static filesystem metadata.
+.TP
+.B FMV_OWN_LOG
+The filesystem journal.
+.TP
+.B FMV_OWN_AG
+Allocation group metadata.
+.TP
+.B FMV_OWN_INOBT
+The inode index, if one is provided.
+.TP
+.B FMV_OWN_INODES
+Inodes.
+.TP
+.B FMV_OWN_REFC
+Reference counting indexes.
+.TP
+.B FMV_OWN_COW
+This extent is being used to stage a copy-on-write.
+.TP
+.B FMV_OWN_DEFECTIVE:
+This extent has been marked defective either by the filesystem or the
+underlying device.
+
+.PP
+The
+.I fmv_offset
+field contains the logical address of the reverse mapping record, in units
+of 512-byte blocks. This field has no meaning if the
+.BR FMV_OF_SPECIAL_OWNER " or " FMV_OF_EXTENT_MAP
+flags are set in
+.IR fmv_oflags "."
+
+.PP
+The
+.I fmv_length
+field contains the length of the extent, in units of 512-byte blocks.
+This field must be zero in the second array element.
+
+.PP
+The
+.I fmv_oflags
+field is a bitmask of extent state flags. The bits are:
+.TP
+.B FMV_OF_PREALLOC
+The extent is allocated but not yet written.
+.TP
+.B FMV_OF_ATTR_FORK
+This extent contains extended attribute data.
+.TP
+.B FMV_OF_EXTENT_MAP
+This extent contains extent map information for the owner.
+.TP
+.B FMV_OF_SHARED
+Parts of this extent may be shared.
+.TP
+.B FMV_OF_SPECIAL_OWNER
+The
+.I fmv_owner
+field contains a special value instead of an inode number.
+.TP
+.B FMV_OF_LAST
+This is the last record in the filesystem.
+
+.PP
+The
+.I fmv_iflags
+field is a bitmask passed to the kernel to alter the output. There are no
+flags defined, so this value must be zero in the first two array elements.
+
+.PP
+The
+.I fmv_count
+field contains the number of elements in the array being passed to the
+kernel. This count must include the two control elements at the start of
+the array. The value must be specified in the first array element; in the
+second element this field must be zero.
+
+If this value is 2,
+.I fmv_entries
+will be set to the number of records that would have been returned had
+the array been large enough; no extent information will be returned.
+
+.PP
+The
+.I fmv_entries
+field contains the number of elements in the array that contain useful
+information if the ioctl returns a non-error value. This value includes
+the two control elements at the start of the array and is only set in the
+first array element; in the second, this field must be zero.
+
+.PP
+The
+.I fmv_unused1
+field must be zero in the first two array elements.
+
+.SS Array Elements
+.PP
+The key fields (fmv_device, fmv_block, fmv_owner, fmv_offset) of the first
+element of the array specify the lowest extent record in the keyspace that
+the caller wants returned. For example, if the key is set to
+(FMV_DEV_DEFAULT, 36, 0, 0), the filesystem will only return records for
+extents starting at or above sector 36 on disk. For convenience, the
+.I fmv_length
+field will be added to the
+.IR fmv_block " and " fmv_offset
+fields as appropriate so that the (fmv_device, fmv_block, fmv_owner,
+fmv_offset, fmv_length) fields in the last array element can be copied
+into the first element to seed the next ioctl call.
+
+The key fields of the second element of the array specify the highest
+extent record in the keyspace that the caller wants returned. Returning
+to our example above, if that example key were instead passed in via the
+second array element, the filesystem will not return records for extents
+going past sector 36 on disk. For convenience, the four key fields can be
+set to ~0 (all ones) to signify "end of filesystem".
+
+If
+.I fmv_count
+in the first element of the array is 2, then
+.I fmv_entries
+in the first element of the array will be set to the number of extent
+records found in the filesystem. Otherwise,
+.I fmv_entries
+will be set to the number of extents actually returned, and the subsequent
+array elements will be filled out with extent information. In these
+subsequent array elements, the fields
+.IR fmv_iflags ", " fmv_count ", " fmv_entries ", and " fmv_unused1
+will be set to zero by the filesystem.
+
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EINVAL
+The array is not long enough, or a non-zero value was passed in one of the
+fields that must be zero.
+.TP
+.B EFAULT
+The pointer passed in was not mapped to a valid memory address.
+.TP
+.B EBADF
+.IR fd
+is not open for reading.
+.TP
+.B EPERM
+This query is not allowed.
+.TP
+.B EOPNOTSUPP
+The filesystem does not support this command.
+
+.SH CONFORMING TO
+This API is Linux-specific. Not all filesystems support it.
+.fi
+.in
+.SH SEE ALSO
+.BR ioctl (2)
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] xfs_io: support the new getfsmap ioctl
2016-05-05 19:47 [RFC 0/3] getfsmapx ioctl Darrick J. Wong
2016-05-05 19:50 ` [PATCH 1/3] document the XFS_IOC_GETFSMAPX ioctl Darrick J. Wong
2016-05-05 19:51 ` [PATCH 2/3] xfs: introduce " Darrick J. Wong
@ 2016-05-05 19:52 ` Darrick J. Wong
2 siblings, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:52 UTC (permalink / raw)
To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
Ross Zwisler, Dan Williams
Cc: linux-fsdevel, linux-api, linux-ext4, linux-btrfs, xfs
Add a new command, 'fsmap', to xfs_io so that we can query the filesystem
extent map on a live filesystem.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
io/Makefile | 2
io/fsmap.c | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++
io/init.c | 1
io/io.h | 1
man/man8/xfs_io.8 | 47 +++++
5 files changed, 535 insertions(+), 1 deletion(-)
create mode 100644 io/fsmap.c
diff --git a/io/Makefile b/io/Makefile
index 0b53f41..6439e1d 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -11,7 +11,7 @@ HFILES = init.h io.h
CFILES = init.c \
attr.c bmap.c file.c freeze.c fsync.c getrusage.c imap.c link.c \
mmap.c open.c parent.c pread.c prealloc.c pwrite.c seek.c shutdown.c \
- sync.c truncate.c reflink.c
+ sync.c truncate.c reflink.c fsmap.c
LLDLIBS = $(LIBXCMD) $(LIBHANDLE)
LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
diff --git a/io/fsmap.c b/io/fsmap.c
new file mode 100644
index 0000000..bf72555
--- /dev/null
+++ b/io/fsmap.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2016 Oracle.
+ * 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 "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "io.h"
+#include "input.h"
+
+static cmdinfo_t fsmap_cmd;
+
+static void
+fsmap_help(void)
+{
+ printf(_(
+"\n"
+" prints the block mapping for an XFS filesystem"
+"\n"
+" Example:\n"
+" 'fsmap -vp' - tabular format verbose map, including unwritten extents\n"
+"\n"
+" fsmap prints the map of disk blocks used by the whole filesystem.\n"
+" The map lists each extent used by the file, as well as regions in the\n"
+" filesystem that do not have any corresponding blocks (free space).\n"
+" By default, each line of the listing takes the following form:\n"
+" extent: [startoffset..endoffset] owner startblock..endblock\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -n -- query n extents.\n"
+" -v -- Verbose information, specify ag info. Show flags legend on 2nd -v\n"
+"\n"));
+}
+
+static int
+numlen(
+ off64_t val)
+{
+ off64_t tmp;
+ int len;
+
+ for (len = 0, tmp = val; tmp > 0; tmp = tmp/10)
+ len++;
+ return (len == 0 ? 1 : len);
+}
+
+static const char *
+special_owner(
+ __int64_t owner)
+{
+ switch (owner) {
+ case FMV_OWN_FREE:
+ return _("free space");
+ case FMV_OWN_UNKNOWN:
+ return _("unknown");
+ case FMV_OWN_FS:
+ return _("static fs metadata");
+ case FMV_OWN_LOG:
+ return _("journalling log");
+ case FMV_OWN_AG:
+ return _("per-AG metadata");
+ case FMV_OWN_INOBT:
+ return _("inode btree");
+ case FMV_OWN_INODES:
+ return _("inodes");
+ case FMV_OWN_REFC:
+ return _("refcount btree");
+ case FMV_OWN_COW:
+ return _("cow reservation");
+ case FMV_OWN_DEFECTIVE:
+ return _("defective");
+ default:
+ return _("unknown");
+ }
+}
+
+static void
+dump_map(
+ unsigned long long nr,
+ struct getfsmapx *map)
+{
+ unsigned long long i;
+ struct getfsmapx *p;
+
+ for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+ printf("\t%llu: [%lld..%lld]: ", i + nr,
+ (long long) p->fmv_block,
+ (long long)(p->fmv_block + p->fmv_length - 1));
+ if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+ printf("%s", special_owner(p->fmv_owner));
+ else if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+ printf(_("inode %lld extent map"),
+ (long long) p->fmv_owner);
+ else
+ printf(_("inode %lld %lld..%lld"),
+ (long long) p->fmv_owner,
+ (long long) p->fmv_offset,
+ (long long)(p->fmv_offset + p->fmv_length - 1));
+ printf(_(" %lld blocks\n"),
+ (long long)p->fmv_length);
+ }
+}
+
+/*
+ * Verbose mode displays:
+ * extent: [startblock..endblock]: startoffset..endoffset \
+ * ag# (agoffset..agendoffset) totalbbs flags
+ */
+#define MINRANGE_WIDTH 16
+#define MINAG_WIDTH 2
+#define MINTOT_WIDTH 5
+#define NFLG 7 /* count of flags */
+#define FLG_NULL 00000000 /* Null flag */
+#define FLG_SHARED 01000000 /* shared extent */
+#define FLG_ATTR_FORK 00100000 /* attribute fork */
+#define FLG_PRE 00010000 /* Unwritten extent */
+#define FLG_BSU 00001000 /* Not on begin of stripe unit */
+#define FLG_ESU 00000100 /* Not on end of stripe unit */
+#define FLG_BSW 00000010 /* Not on begin of stripe width */
+#define FLG_ESW 00000001 /* Not on end of stripe width */
+static void
+dump_map_verbose(
+ unsigned long long nr,
+ struct getfsmapx *map,
+ bool *dumped_flags,
+ struct xfs_fsop_geom *fsgeo)
+{
+ unsigned long long i;
+ struct getfsmapx *p;
+ int agno;
+ off64_t agoff, bbperag;
+ int foff_w, boff_w, aoff_w, tot_w, agno_w, own_w, nr_w;
+ char rbuf[32], bbuf[32], abuf[32], obuf[32], nbuf[32];
+ int sunit, swidth;
+ int flg = 0;
+
+ foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
+ nr_w = 4;
+ tot_w = MINTOT_WIDTH;
+ bbperag = (off64_t)fsgeo->agblocks *
+ (off64_t)fsgeo->blocksize / BBSIZE;
+ sunit = (fsgeo->sunit * fsgeo->blocksize) / BBSIZE;
+ swidth = (fsgeo->swidth * fsgeo->blocksize) / BBSIZE;
+
+ /*
+ * Go through the extents and figure out the width
+ * needed for all columns.
+ */
+ for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+ if (p->fmv_oflags & FMV_OF_PREALLOC ||
+ p->fmv_oflags & FMV_OF_ATTR_FORK ||
+ p->fmv_oflags & FMV_OF_SHARED)
+ flg = 1;
+ if (sunit &&
+ (p->fmv_block % sunit != 0 ||
+ ((p->fmv_block + p->fmv_length) % sunit) != 0 ||
+ p->fmv_block % swidth != 0 ||
+ ((p->fmv_block + p->fmv_length) % swidth) != 0))
+ flg = 1;
+ if (flg)
+ *dumped_flags = true;
+ snprintf(nbuf, sizeof(nbuf), "%llu", nr + i);
+ nr_w = max(nr_w, strlen(nbuf));
+ snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+ (long long) p->fmv_block,
+ (long long)(p->fmv_block + p->fmv_length - 1));
+ boff_w = max(boff_w, strlen(bbuf));
+ if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+ own_w = max(own_w, strlen(special_owner(p->fmv_owner)));
+ else {
+ snprintf(obuf, sizeof(obuf), "%lld",
+ (long long)p->fmv_owner);
+ own_w = max(own_w, strlen(obuf));
+ }
+ if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+ foff_w = max(foff_w, strlen(_("extent_map")));
+ else if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+ ;
+ else {
+ snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+ (long long) p->fmv_offset,
+ (long long)(p->fmv_offset + p->fmv_length - 1));
+ foff_w = max(foff_w, strlen(rbuf));
+ }
+ agno = p->fmv_block / bbperag;
+ agoff = p->fmv_block - (agno * bbperag);
+ snprintf(abuf, sizeof(abuf),
+ "(%lld..%lld)",
+ (long long)agoff,
+ (long long)(agoff + p->fmv_length - 1));
+ aoff_w = max(aoff_w, strlen(abuf));
+ tot_w = max(tot_w,
+ numlen(p->fmv_length));
+ }
+ agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount));
+ if (nr == 0)
+ printf("%*s: %-*s %-*s %-*s %*s %-*s %*s%s\n",
+ nr_w, _("EXT"),
+ boff_w, _("BLOCK-RANGE"),
+ own_w, _("OWNER"),
+ foff_w, _("FILE-OFFSET"),
+ agno_w, _("AG"),
+ aoff_w, _("AG-OFFSET"),
+ tot_w, _("TOTAL"),
+ flg ? _(" FLAGS") : "");
+ for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+ flg = FLG_NULL;
+ if (p->fmv_oflags & FMV_OF_PREALLOC)
+ flg |= FLG_PRE;
+ if (p->fmv_oflags & FMV_OF_ATTR_FORK)
+ flg |= FLG_ATTR_FORK;
+ if (p->fmv_oflags & FMV_OF_SHARED)
+ flg |= FLG_SHARED;
+ /*
+ * If striping enabled, determine if extent starts/ends
+ * on a stripe unit boundary.
+ */
+ if (sunit) {
+ if (p->fmv_block % sunit != 0) {
+ flg |= FLG_BSU;
+ }
+ if (((p->fmv_block +
+ p->fmv_length ) % sunit ) != 0) {
+ flg |= FLG_ESU;
+ }
+ if (p->fmv_block % swidth != 0) {
+ flg |= FLG_BSW;
+ }
+ if (((p->fmv_block +
+ p->fmv_length ) % swidth ) != 0) {
+ flg |= FLG_ESW;
+ }
+ }
+ snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+ (long long) p->fmv_block,
+ (long long)(p->fmv_block + p->fmv_length - 1));
+ if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER) {
+ snprintf(obuf, sizeof(obuf), "%s",
+ special_owner(p->fmv_owner));
+ snprintf(rbuf, sizeof(rbuf), " ");
+ } else {
+ snprintf(obuf, sizeof(obuf), "%lld",
+ (long long)p->fmv_owner);
+ snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+ (long long) p->fmv_offset,
+ (long long)(p->fmv_offset + p->fmv_length - 1));
+ }
+ agno = p->fmv_block / bbperag;
+ agoff = p->fmv_block - (agno * bbperag);
+ snprintf(abuf, sizeof(abuf),
+ "(%lld..%lld)",
+ (long long)agoff,
+ (long long)(agoff + p->fmv_length - 1));
+ if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+ printf("%*llu: %-*s %-*s %-*s %*d %-*s %*lld\n",
+ nr_w, nr + i,
+ boff_w, bbuf,
+ own_w, obuf,
+ foff_w, _("extent map"),
+ agno_w, agno,
+ aoff_w, abuf,
+ tot_w, (long long)p->fmv_length);
+ else {
+ printf("%*llu: %-*s %-*s %-*s", nr_w, nr + i,
+ boff_w, bbuf, own_w, obuf, foff_w, rbuf);
+ printf(" %*d %-*s", agno_w, agno,
+ aoff_w, abuf);
+ printf(" %*lld", tot_w,
+ (long long)p->fmv_length);
+ if (flg == FLG_NULL) {
+ printf("\n");
+ } else {
+ printf(" %-*.*o\n", NFLG, NFLG, flg);
+ }
+ }
+ }
+}
+
+static void
+dump_verbose_key(void)
+{
+ printf(_(" FLAG Values:\n"));
+ printf(_(" %*.*o Shared extent\n"),
+ NFLG+1, NFLG+1, FLG_SHARED);
+ printf(_(" %*.*o Attribute fork\n"),
+ NFLG+1, NFLG+1, FLG_ATTR_FORK);
+ printf(_(" %*.*o Unwritten preallocated extent\n"),
+ NFLG+1, NFLG+1, FLG_PRE);
+ printf(_(" %*.*o Doesn't begin on stripe unit\n"),
+ NFLG+1, NFLG+1, FLG_BSU);
+ printf(_(" %*.*o Doesn't end on stripe unit\n"),
+ NFLG+1, NFLG+1, FLG_ESU);
+ printf(_(" %*.*o Doesn't begin on stripe width\n"),
+ NFLG+1, NFLG+1, FLG_BSW);
+ printf(_(" %*.*o Doesn't end on stripe width\n"),
+ NFLG+1, NFLG+1, FLG_ESW);
+}
+
+int
+fsmap_f(
+ int argc,
+ char **argv)
+{
+ struct getfsmapx *p;
+ struct getfsmapx *nmap;
+ struct getfsmapx *map;
+ struct xfs_fsop_geom fsgeo;
+ long long start = 0;
+ long long end = -1;
+ int nmap_size;
+ int map_size;
+ int nflag = 0;
+ int vflag = 0;
+ int fmv_iflags = 0; /* flags for XFS_IOC_GETFSMAPX */
+ int i = 0;
+ int c;
+ unsigned long long nr = 0;
+ size_t fsblocksize, fssectsize;
+ bool dumped_flags = false;
+
+ init_cvtnum(&fsblocksize, &fssectsize);
+
+ while ((c = getopt(argc, argv, "n:v")) != EOF) {
+ switch (c) {
+ case 'n': /* number of extents specified */
+ nflag = atoi(optarg);
+ break;
+ case 'v': /* Verbose output */
+ vflag++;
+ break;
+ default:
+ return command_usage(&fsmap_cmd);
+ }
+ }
+
+ if (argc > optind) {
+ start = cvtnum(fsblocksize, fssectsize, argv[optind]);
+ if (start < 0) {
+ fprintf(stderr,
+ _("Bad rmap start_fsb %s.\n"),
+ argv[optind]);
+ return 0;
+ }
+ }
+
+ if (argc > optind + 1) {
+ end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
+ if (end < 0) {
+ fprintf(stderr,
+ _("Bad rmap end_fsb %s.\n"),
+ argv[optind + 1]);
+ return 0;
+ }
+ }
+
+ if (vflag) {
+ c = xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo);
+ if (c < 0) {
+ fprintf(stderr,
+ _("%s: can't get geometry [\"%s\"]: %s\n"),
+ progname, file->name, strerror(errno));
+ exitcode = 1;
+ return 0;
+ }
+ }
+
+ map_size = nflag ? nflag + 2 : 32; /* initial guess - 32 */
+ map = malloc(map_size * sizeof(*map));
+ if (map == NULL) {
+ fprintf(stderr, _("%s: malloc of %lu bytes failed.\n"),
+ progname, map_size * sizeof(*map));
+ exitcode = 1;
+ return 0;
+ }
+
+ map->fmv_iflags = fmv_iflags;
+ map->fmv_device = FMV_DEV_DEFAULT;
+ map->fmv_block = start / 512;
+ map->fmv_owner = 0;
+ map->fmv_offset = 0;
+ map->fmv_length = 0;
+ (map + 1)->fmv_device = ULLONG_MAX;
+ (map + 1)->fmv_block = (unsigned long long)end / 512;
+ (map + 1)->fmv_owner = ULLONG_MAX;
+ (map + 1)->fmv_offset = ULLONG_MAX;
+
+ /* Count mappings */
+ if (!nflag) {
+ map->fmv_count = 2;
+ i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+ if (i < 0) {
+ fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+ " iflags=0x%x [\"%s\"]: %s\n"),
+ progname, map->fmv_iflags, file->name,
+ strerror(errno));
+ free(map);
+ exitcode = 1;
+ return 0;
+ }
+ if (map->fmv_entries > map_size * 2) {
+ unsigned long long nr;
+
+ nr = 5ULL * map->fmv_entries / 4 + 2;
+ nmap_size = nr > INT_MAX ? INT_MAX : nr;
+ nmap = realloc(map, nmap_size * sizeof(*map));
+ if (nmap == NULL) {
+ fprintf(stderr,
+ _("%s: cannot realloc %lu bytes\n"),
+ progname, map_size*sizeof(*map));
+ } else {
+ map = nmap;
+ map_size = nmap_size;
+ }
+ }
+ }
+
+ map->fmv_count = map_size;
+ do {
+ /* Get some extents */
+ i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+ if (i < 0) {
+ fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+ " iflags=0x%x [\"%s\"]: %s\n"),
+ progname, map->fmv_iflags, file->name,
+ strerror(errno));
+ free(map);
+ exitcode = 1;
+ return 0;
+ }
+
+ if (map->fmv_entries == 0)
+ break;
+
+ if (!vflag)
+ dump_map(nr, map);
+ else
+ dump_map_verbose(nr, map, &dumped_flags, &fsgeo);
+
+ p = map + 1 + map->fmv_entries;
+ if (p->fmv_oflags & FMV_OF_LAST)
+ break;
+
+ nr += map->fmv_entries;
+ map->fmv_device = p->fmv_device;
+ map->fmv_block = p->fmv_block;
+ map->fmv_owner = p->fmv_owner;
+ map->fmv_offset = p->fmv_offset;
+ map->fmv_oflags = p->fmv_oflags;
+ map->fmv_length = p->fmv_length;
+ } while(true);
+
+ if (dumped_flags)
+ dump_verbose_key();
+
+ free(map);
+ return 0;
+}
+
+void
+fsmap_init(void)
+{
+ fsmap_cmd.name = "fsmap";
+ fsmap_cmd.cfunc = fsmap_f;
+ fsmap_cmd.argmin = 0;
+ fsmap_cmd.argmax = -1;
+ fsmap_cmd.flags = CMD_NOMAP_OK;
+ fsmap_cmd.args = _("[-v] [-n nx] [start] [end]");
+ fsmap_cmd.oneline = _("print filesystem mapping for a range of blocks");
+ fsmap_cmd.help = fsmap_help;
+
+ add_command(&fsmap_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 51f1f5c..4ae8274 100644
--- a/io/init.c
+++ b/io/init.c
@@ -60,6 +60,7 @@ init_commands(void)
file_init();
flink_init();
freeze_init();
+ fsmap_init();
fsync_init();
getrusage_init();
help_init();
diff --git a/io/io.h b/io/io.h
index 172b1f8..cef1763 100644
--- a/io/io.h
+++ b/io/io.h
@@ -97,6 +97,7 @@ extern void bmap_init(void);
extern void file_init(void);
extern void flink_init(void);
extern void freeze_init(void);
+extern void fsmap_init(void);
extern void fsync_init(void);
extern void getrusage_init(void);
extern void help_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 34f90c7..60c46ee 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -267,6 +267,53 @@ ioctl. Options behave as described in the
.BR xfs_bmap (8)
manual page.
.TP
+.BI "fsmap [ \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
+Prints the mapping of disk blocks used by an XFS filesystem. The map
+lists each extent used by files, allocation group metadata,
+journalling logs, and static filesystem metadata, as well as any
+regions that are unused. Each line of the listings takes the
+following form:
+.PP
+.RS
+.IR extent ": [" startblock .. endblock "]: " owner " " startoffset .. endoffset " " length
+.PP
+Static filesystem metadata, allocation group metadata, btrees,
+journalling logs, and free space are marked by replacing the
+.IR startoffset .. endoffset
+with the appropriate marker. All blocks, offsets, and lengths are specified
+in units of 512-byte blocks, no matter what the filesystem's block size is.
+.BI "The optional " start " and " end " arguments can be used to constrain
+the output to a particular range of disk blocks.
+.RE
+.RS 1.0i
+.PD 0
+.TP
+.BI \-n " num_extents"
+If this option is given,
+.B xfs_fsmap
+obtains the extent list of the file in groups of
+.I num_extents
+extents. In the absence of
+.BR \-n ", " xfs_fsmap
+queries the system for the number of extents in the filesystem and uses that
+value to compute the group size.
+.TP
+.B \-v
+Shows verbose information. When this flag is specified, additional AG
+specific information is appended to each line in the following form:
+.IP
+.RS 1.2i
+.IR agno " (" startagblock .. endagblock ") " nblocks " " flags
+.RE
+.IP
+A second
+.B \-v
+option will print out the
+.I flags
+legend.
+.RE
+.PD
+.TP
.BI "extsize [ \-R | \-D ] [ " value " ]"
Display and/or modify the preferred extent size used when allocating
space for the currently open file. If the
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 4+ messages in thread