]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_io: support the new getfsmap ioctl
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 21 Jun 2017 22:14:30 +0000 (17:14 -0500)
committerEric Sandeen <sandeen@redhat.com>
Wed, 21 Jun 2017 22:14:30 +0000 (17:14 -0500)
Plumb in all the pieces we need to have xfs_io query the GETFSMAP ioctl
for an arbitrary filesystem.

[sandeen: minor doc fixes]

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
io/Makefile
io/copy_file_range.c
io/encrypt.c
io/fsmap.c [new file with mode: 0644]
io/init.c
io/io.h
io/open.c
io/pwrite.c
io/reflink.c
io/sendfile.c
man/man8/xfs_io.8

index 435ccff578627a6854e20d8de0bcde774d6a25c2..47b0a669bce060dea0cd175eb505efe66f0bf598 100644 (file)
@@ -99,6 +99,13 @@ ifeq ($(HAVE_MREMAP),yes)
 LCFLAGS += -DHAVE_MREMAP
 endif
 
+# On linux we get fsmap from the system or define it ourselves
+# so include this based on platform type.  If this reverts to only
+# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
+ifeq ($(PKG_PLATFORM),linux)
+CFILES += fsmap.c
+endif
+
 default: depend $(LTCOMMAND)
 
 include $(BUILDRULES)
index 249c64907514a606d147368e999f87f442f23c98..d1dfc5a5e33a50c9af3867770351151d5271f142 100644 (file)
@@ -121,7 +121,7 @@ copy_range_f(int argc, char **argv)
        if (optind != argc - 1)
                return command_usage(&copy_range_cmd);
 
-       fd = openfile(argv[optind], NULL, IO_READONLY, 0);
+       fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
        if (fd < 0)
                return 0;
 
index d844c5e355163dc6bec98e5264c9ba321915cccb..26ab97ce614b242287a4e8c3e78108742634affe 100644 (file)
@@ -20,6 +20,7 @@
 #include "platform_defs.h"
 #include "command.h"
 #include "init.h"
+#include "path.h"
 #include "io.h"
 
 #ifndef ARRAY_SIZE
diff --git a/io/fsmap.c b/io/fsmap.c
new file mode 100644 (file)
index 0000000..9e70c7b
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "path.h"
+#include "io.h"
+#include "input.h"
+
+static cmdinfo_t       fsmap_cmd;
+static dev_t           xfs_data_dev;
+
+static void
+fsmap_help(void)
+{
+       printf(_(
+"\n"
+" Prints the block mapping for the filesystem hosting the current file"
+"\n"
+" fsmap prints the map of disk blocks used by the whole filesystem.\n"
+" When possible, owner and offset information will be included in the\n"
+" space report.\n"
+"\n"
+" By default, each line of the listing takes the following form:\n"
+"     extent: major:minor [startblock..endblock]: owner startoffset..endoffset length\n"
+" The owner field is either an inode number or a special value.\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -d -- query only the data device (default).\n"
+" -l -- query only the log device.\n"
+" -r -- query only the realtime device.\n"
+" -n -- query n extents at a time.\n"
+" -m -- output machine-readable format.\n"
+" -v -- Verbose information, show AG and offsets.  Show flags legend on 2nd -v\n"
+"\n"));
+}
+
+#define OWNER_BUF_SZ   32
+static const char *
+special_owner(
+       int64_t         owner,
+       char            *buf)
+{
+       switch (owner) {
+       case XFS_FMR_OWN_FREE:
+               return _("free space");
+       case XFS_FMR_OWN_UNKNOWN:
+               return _("unknown");
+       case XFS_FMR_OWN_FS:
+               return _("static fs metadata");
+       case XFS_FMR_OWN_LOG:
+               return _("journalling log");
+       case XFS_FMR_OWN_AG:
+               return _("per-AG metadata");
+       case XFS_FMR_OWN_INOBT:
+               return _("inode btree");
+       case XFS_FMR_OWN_INODES:
+               return _("inodes");
+       case XFS_FMR_OWN_REFC:
+               return _("refcount btree");
+       case XFS_FMR_OWN_COW:
+               return _("cow reservation");
+       case XFS_FMR_OWN_DEFECTIVE:
+               return _("defective");
+       default:
+               snprintf(buf, OWNER_BUF_SZ, _("special %u:%u"),
+                               FMR_OWNER_TYPE(owner), FMR_OWNER_CODE(owner));
+               return buf;
+       }
+}
+
+static void
+dump_map(
+       unsigned long long      *nr,
+       struct fsmap_head       *head)
+{
+       unsigned long long      i;
+       struct fsmap            *p;
+       char                    owner[OWNER_BUF_SZ];
+       char                    *fork;
+
+       for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+               printf("\t%llu: %u:%u [%lld..%lld]: ", i + (*nr),
+                       major(p->fmr_device), minor(p->fmr_device),
+                       (long long)BTOBBT(p->fmr_physical),
+                       (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+               fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? _("attr") : _("data");
+               if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+                       printf("%s", special_owner(p->fmr_owner, owner));
+               else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+                       printf(_("inode %lld %s extent map"),
+                               (long long) p->fmr_owner, fork);
+               else
+                       printf(_("inode %lld %s %lld..%lld"),
+                               (long long)p->fmr_owner, fork,
+                               (long long)BTOBBT(p->fmr_offset),
+                               (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+               printf(_(" %lld\n"),
+                       (long long)BTOBBT(p->fmr_length));
+       }
+
+       (*nr) += head->fmh_entries;
+}
+
+static void
+dump_map_machine(
+       unsigned long long      *nr,
+       struct fsmap_head       *head)
+{
+       unsigned long long      i;
+       struct fsmap            *p;
+       char                    *fork;
+
+       printf(_("EXT,MAJOR,MINOR,PSTART,PEND,OWNER,OSTART,OEND,LENGTH\n"));
+       for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+               printf("%llu,%u,%u,%lld,%lld,", i + (*nr),
+                       major(p->fmr_device), minor(p->fmr_device),
+                       (long long)BTOBBT(p->fmr_physical),
+                       (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+               fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? "attr" : "data";
+               if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+                       printf("special_%u:%u,,,", FMR_OWNER_TYPE(p->fmr_owner),
+                               FMR_OWNER_CODE(p->fmr_owner));
+               else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+                       printf(_("inode_%lld_%s_bmbt,,,"),
+                               (long long) p->fmr_owner, fork);
+               else
+                       printf(_("inode_%lld_%s,%lld,%lld,"),
+                               (long long)p->fmr_owner, fork,
+                               (long long)BTOBBT(p->fmr_offset),
+                               (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+               printf("%lld\n",
+                       (long long)BTOBBT(p->fmr_length));
+       }
+
+       (*nr) += head->fmh_entries;
+}
+
+/*
+ * Verbose mode displays:
+ *   extent: major:minor [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_ATTR_FORK   01000000        /* attribute fork */
+#define        FLG_SHARED      00100000        /* shared extent */
+#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 fsmap_head       *head,
+       bool                    *dumped_flags,
+       struct xfs_fsop_geom    *fsgeo)
+{
+       unsigned long long      i;
+       struct fsmap            *p;
+       int                     agno;
+       off64_t                 agoff, bperag;
+       int                     foff_w, boff_w, aoff_w, tot_w, agno_w, own_w;
+       int                     nr_w, dev_w;
+       char                    rbuf[32], bbuf[32], abuf[32], obuf[32];
+       char                    nbuf[32], dbuf[32], gbuf[32];
+       char                    owner[OWNER_BUF_SZ];
+       int                     sunit, swidth;
+       int                     flg = 0;
+
+       foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
+       dev_w = 3;
+       nr_w = 4;
+       tot_w = MINTOT_WIDTH;
+       bperag = (off64_t)fsgeo->agblocks *
+                 (off64_t)fsgeo->blocksize;
+       sunit = (fsgeo->sunit * fsgeo->blocksize);
+       swidth = (fsgeo->swidth * fsgeo->blocksize);
+
+       /*
+        * Go through the extents and figure out the width
+        * needed for all columns.
+        */
+       for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+               if (p->fmr_flags & FMR_OF_PREALLOC ||
+                   p->fmr_flags & FMR_OF_ATTR_FORK ||
+                   p->fmr_flags & FMR_OF_SHARED)
+                       flg = 1;
+               if (sunit &&
+                   (p->fmr_physical  % sunit != 0 ||
+                    ((p->fmr_physical + p->fmr_length) % sunit) != 0 ||
+                    p->fmr_physical % swidth != 0 ||
+                    ((p->fmr_physical + p->fmr_length) % swidth) != 0))
+                       flg = 1;
+               if (flg)
+                       *dumped_flags = true;
+               snprintf(nbuf, sizeof(nbuf), "%llu", (*nr) + i);
+               nr_w = max(nr_w, strlen(nbuf));
+               if (head->fmh_oflags & FMH_OF_DEV_T)
+                       snprintf(dbuf, sizeof(dbuf), "%u:%u",
+                               major(p->fmr_device),
+                               minor(p->fmr_device));
+               else
+                       snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
+               dev_w = max(dev_w, strlen(dbuf));
+               snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+                       (long long)BTOBBT(p->fmr_physical),
+                       (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+               boff_w = max(boff_w, strlen(bbuf));
+               if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+                       own_w = max(own_w, strlen(
+                                       special_owner(p->fmr_owner, owner)));
+               else {
+                       snprintf(obuf, sizeof(obuf), "%lld",
+                               (long long)p->fmr_owner);
+                       own_w = max(own_w, strlen(obuf));
+               }
+               if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+                       foff_w = max(foff_w, strlen(_("extent_map")));
+               else if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+                       ;
+               else {
+                       snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+                               (long long)BTOBBT(p->fmr_offset),
+                               (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+                       foff_w = max(foff_w, strlen(rbuf));
+               }
+               if (p->fmr_device == xfs_data_dev) {
+                       agno = p->fmr_physical / bperag;
+                       agoff = p->fmr_physical - (agno * bperag);
+                       snprintf(abuf, sizeof(abuf),
+                               "(%lld..%lld)",
+                               (long long)BTOBBT(agoff),
+                               (long long)BTOBBT(agoff + p->fmr_length - 1));
+               } else
+                       abuf[0] = 0;
+               aoff_w = max(aoff_w, strlen(abuf));
+               tot_w = max(tot_w,
+                       numlen(BTOBBT(p->fmr_length), 10));
+       }
+       agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount, 10));
+       if (*nr == 0)
+               printf("%*s: %-*s %-*s %-*s %-*s %*s %-*s %*s%s\n",
+                       nr_w, _("EXT"),
+                       dev_w, _("DEV"),
+                       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 = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+               flg = FLG_NULL;
+               if (p->fmr_flags & FMR_OF_PREALLOC)
+                       flg |= FLG_PRE;
+               if (p->fmr_flags & FMR_OF_ATTR_FORK)
+                       flg |= FLG_ATTR_FORK;
+               if (p->fmr_flags & FMR_OF_SHARED)
+                       flg |= FLG_SHARED;
+               /*
+                * If striping enabled, determine if extent starts/ends
+                * on a stripe unit boundary.
+                */
+               if (sunit) {
+                       if (p->fmr_physical  % sunit != 0)
+                               flg |= FLG_BSU;
+                       if (((p->fmr_physical +
+                             p->fmr_length ) % sunit ) != 0)
+                               flg |= FLG_ESU;
+                       if (p->fmr_physical % swidth != 0)
+                               flg |= FLG_BSW;
+                       if (((p->fmr_physical +
+                             p->fmr_length ) % swidth ) != 0)
+                               flg |= FLG_ESW;
+               }
+               if (head->fmh_oflags & FMH_OF_DEV_T)
+                       snprintf(dbuf, sizeof(dbuf), "%u:%u",
+                               major(p->fmr_device),
+                               minor(p->fmr_device));
+               else
+                       snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
+               snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+                       (long long)BTOBBT(p->fmr_physical),
+                       (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+               if (p->fmr_flags & FMR_OF_SPECIAL_OWNER) {
+                       snprintf(obuf, sizeof(obuf), "%s",
+                               special_owner(p->fmr_owner, owner));
+                       snprintf(rbuf, sizeof(rbuf), " ");
+               } else {
+                       snprintf(obuf, sizeof(obuf), "%lld",
+                               (long long)p->fmr_owner);
+                       snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+                               (long long)BTOBBT(p->fmr_offset),
+                               (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+               }
+               if (p->fmr_device == xfs_data_dev) {
+                       agno = p->fmr_physical / bperag;
+                       agoff = p->fmr_physical - (agno * bperag);
+                       snprintf(abuf, sizeof(abuf),
+                               "(%lld..%lld)",
+                               (long long)BTOBBT(agoff),
+                               (long long)BTOBBT(agoff + p->fmr_length - 1));
+                       snprintf(gbuf, sizeof(gbuf),
+                               "%lld",
+                               (long long)agno);
+               } else {
+                       abuf[0] = 0;
+                       gbuf[0] = 0;
+               }
+               if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+                       printf("%*llu: %-*s %-*s %-*s %-*s %-*s %-*s %*lld\n",
+                               nr_w, (*nr) + i,
+                               dev_w, dbuf,
+                               boff_w, bbuf,
+                               own_w, obuf,
+                               foff_w, _("extent map"),
+                               agno_w, gbuf,
+                               aoff_w, abuf,
+                               tot_w, (long long)BTOBBT(p->fmr_length));
+               else {
+                       printf("%*llu: %-*s %-*s %-*s %-*s", nr_w, (*nr) + i,
+                               dev_w, dbuf, boff_w, bbuf, own_w, obuf,
+                               foff_w, rbuf);
+                       printf(" %-*s %-*s", agno_w, gbuf,
+                               aoff_w, abuf);
+                       printf(" %*lld", tot_w,
+                               (long long)BTOBBT(p->fmr_length));
+                       if (flg == FLG_NULL)
+                               printf("\n");
+                       else
+                               printf(" %-*.*o\n", NFLG, NFLG, flg);
+               }
+       }
+
+       (*nr) += head->fmh_entries;
+}
+
+static void
+dump_verbose_key(void)
+{
+       printf(_(" FLAG Values:\n"));
+       printf(_("    %*.*o Attribute fork\n"),
+               NFLG+1, NFLG+1, FLG_ATTR_FORK);
+       printf(_("    %*.*o Shared extent\n"),
+               NFLG+1, NFLG+1, FLG_SHARED);
+       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 fsmap            *p;
+       struct fsmap_head       *nhead;
+       struct fsmap_head       *head;
+       struct fsmap            *l, *h;
+       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                     mflag = 0;
+       int                     i = 0;
+       int                     c;
+       unsigned long long      nr = 0;
+       size_t                  fsblocksize, fssectsize;
+       struct fs_path          *fs;
+       static bool             tab_init;
+       bool                    dumped_flags = false;
+       int                     dflag, lflag, rflag;
+
+       init_cvtnum(&fsblocksize, &fssectsize);
+
+       dflag = lflag = rflag = 0;
+       while ((c = getopt(argc, argv, "dlmn:rv")) != EOF) {
+               switch (c) {
+               case 'd':       /* data device */
+                       dflag = 1;
+                       break;
+               case 'l':       /* log device */
+                       lflag = 1;
+                       break;
+               case 'm':       /* machine readable format */
+                       mflag++;
+                       break;
+               case 'n':       /* number of extents specified */
+                       nflag = cvt_u32(optarg, 10);
+                       if (errno)
+                               return command_usage(&fsmap_cmd);
+                       break;
+               case 'r':       /* rt device */
+                       rflag = 1;
+                       break;
+               case 'v':       /* Verbose output */
+                       vflag++;
+                       break;
+               default:
+                       return command_usage(&fsmap_cmd);
+               }
+       }
+
+       if ((dflag + lflag + rflag > 1) || (mflag > 0 && vflag > 0) ||
+           (argc > optind && dflag + lflag + rflag == 0))
+               return command_usage(&fsmap_cmd);
+
+       if (argc > optind) {
+               start = cvtnum(fsblocksize, fssectsize, argv[optind]);
+               if (start < 0) {
+                       fprintf(stderr,
+                               _("Bad rmap start_bblock %s.\n"),
+                               argv[optind]);
+                       return 0;
+               }
+               start <<= BBSHIFT;
+       }
+
+       if (argc > optind + 1) {
+               end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
+               if (end < 0) {
+                       fprintf(stderr,
+                               _("Bad rmap end_bblock %s.\n"),
+                               argv[optind + 1]);
+                       return 0;
+               }
+               end <<= BBSHIFT;
+       }
+
+       if (vflag) {
+               c = ioctl(file->fd, XFS_IOC_FSGEOMETRY, &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 : 131072 / sizeof(struct fsmap);
+       head = malloc(fsmap_sizeof(map_size));
+       if (head == NULL) {
+               fprintf(stderr, _("%s: malloc of %zu bytes failed.\n"),
+                       progname, fsmap_sizeof(map_size));
+               exitcode = 1;
+               return 0;
+       }
+
+       memset(head, 0, sizeof(*head));
+       l = head->fmh_keys;
+       h = head->fmh_keys + 1;
+       if (dflag) {
+               l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
+       } else if (lflag) {
+               l->fmr_device = h->fmr_device = file->fs_path.fs_logdev;
+       } else if (rflag) {
+               l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
+       } else {
+               l->fmr_device = 0;
+               h->fmr_device = UINT_MAX;
+       }
+       l->fmr_physical = start;
+       h->fmr_physical = end;
+       h->fmr_owner = ULLONG_MAX;
+       h->fmr_flags = UINT_MAX;
+       h->fmr_offset = ULLONG_MAX;
+
+       /* Count mappings */
+       if (!nflag) {
+               head->fmh_count = 0;
+               i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
+               if (i < 0) {
+                       fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
+                               " iflags=0x%x [\"%s\"]: %s\n"),
+                               progname, head->fmh_iflags, file->name,
+                               strerror(errno));
+                       free(head);
+                       exitcode = 1;
+                       return 0;
+               }
+               if (head->fmh_entries > map_size + 2) {
+                       map_size = 11ULL * head->fmh_entries / 10;
+                       nmap_size = map_size > (1 << 24) ? (1 << 24) : map_size;
+                       nhead = realloc(head, fsmap_sizeof(nmap_size));
+                       if (nhead == NULL) {
+                               fprintf(stderr,
+                                       _("%s: cannot realloc %zu bytes\n"),
+                                       progname, fsmap_sizeof(nmap_size));
+                       } else {
+                               head = nhead;
+                               map_size = nmap_size;
+                       }
+               }
+       }
+
+       /*
+        * If this is an XFS filesystem, remember the data device.
+        * (We report AG number/block for data device extents on XFS).
+        */
+       if (!tab_init) {
+               fs_table_initialise(0, NULL, 0, NULL);
+               tab_init = true;
+       }
+       fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
+       xfs_data_dev = fs ? fs->fs_datadev : 0;
+
+       head->fmh_count = map_size;
+       do {
+               /* Get some extents */
+               i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
+               if (i < 0) {
+                       fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
+                               " iflags=0x%x [\"%s\"]: %s\n"),
+                               progname, head->fmh_iflags, file->name,
+                               strerror(errno));
+                       free(head);
+                       exitcode = 1;
+                       return 0;
+               }
+
+               if (head->fmh_entries == 0)
+                       break;
+
+               if (vflag)
+                       dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
+               else if (mflag)
+                       dump_map_machine(&nr, head);
+               else
+                       dump_map(&nr, head);
+
+               p = &head->fmh_recs[head->fmh_entries - 1];
+               if (p->fmr_flags & FMR_OF_LAST)
+                       break;
+               fsmap_advance(head);
+       } while (true);
+
+       if (dumped_flags)
+               dump_verbose_key();
+
+       free(head);
+       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 | CMD_FLAG_FOREIGN_OK;
+       fsmap_cmd.args = _("[-d|-l|-r] [-m|-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);
+}
index c15a1e1b9e9fb5e3f7cb5eb13914c90e8fc406cb..20d5f80df606469727be6dc8a0d755d5be951fac 100644 (file)
--- a/io/init.c
+++ b/io/init.c
@@ -66,6 +66,7 @@ init_commands(void)
        file_init();
        flink_init();
        freeze_init();
+       fsmap_init();
        fsync_init();
        getrusage_init();
        help_init();
@@ -139,6 +140,7 @@ init(
        char            *sp;
        mode_t          mode = 0600;
        xfs_fsop_geom_t geometry = { 0 };
+       struct fs_path  fsp;
 
        progname = basename(argv[0]);
        setlocale(LC_ALL, "");
@@ -148,6 +150,7 @@ init(
        pagesize = getpagesize();
        gettimeofday(&stopwatch, NULL);
 
+       fs_table_initialise(0, NULL, 0, NULL);
        while ((c = getopt(argc, argv, "ac:C:dFfim:p:nrRstTVx")) != EOF) {
                switch (c) {
                case 'a':
@@ -212,11 +215,12 @@ init(
        }
 
        while (optind < argc) {
-               if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
+               c = openfile(argv[optind], &geometry, flags, mode, &fsp);
+               if (c < 0)
                        exit(1);
                if (!platform_test_xfs_fd(c))
                        flags |= IO_FOREIGN;
-               if (addfile(argv[optind], c, &geometry, flags) < 0)
+               if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
                        exit(1);
                optind++;
        }
diff --git a/io/io.h b/io/io.h
index 952bdb8823861413871a0ea3930db78ca46f57dc..6a0fe657b24dd397e0f2b8b2a2f16f58baa7dc8d 100644 (file)
--- a/io/io.h
+++ b/io/io.h
@@ -17,6 +17,7 @@
  */
 
 #include "xfs.h"
+#include "path.h"
 
 /*
  * Read/write patterns (default is always "forward")
@@ -47,6 +48,7 @@ typedef struct fileio {
        int             flags;          /* flags describing file state */
        char            *name;          /* file name at time of open */
        xfs_fsop_geom_t geom;           /* XFS filesystem geometry */
+       struct fs_path  fs_path;        /* XFS path information */
 } fileio_t;
 
 extern fileio_t                *filetable;     /* open file table */
@@ -76,8 +78,10 @@ extern void *check_mapping_range(mmap_region_t *, off64_t, size_t, int);
  */
 
 extern off64_t         filesize(void);
-extern int             openfile(char *, xfs_fsop_geom_t *, int, mode_t);
-extern int             addfile(char *, int , xfs_fsop_geom_t *, int);
+extern int             openfile(char *, xfs_fsop_geom_t *, int, mode_t,
+                                struct fs_path *);
+extern int             addfile(char *, int , xfs_fsop_geom_t *, int,
+                               struct fs_path *);
 extern void            printxattr(uint, int, int, const char *, int, int);
 
 extern unsigned int    recurse_all;
@@ -174,3 +178,9 @@ extern void         readdir_init(void);
 extern void            reflink_init(void);
 
 extern void            cowextsize_init(void);
+
+#ifdef HAVE_GETFSMAP
+extern void            fsmap_init(void);
+#else
+# define fsmap_init()  do { } while (0)
+#endif
index 2ed55cf1aa8713f8edb2c30b27eca5c31c70b402..b50f0681045daaa288d3989b3f5ca8de723237d1 100644 (file)
--- a/io/open.c
+++ b/io/open.c
@@ -52,8 +52,10 @@ openfile(
        char            *path,
        xfs_fsop_geom_t *geom,
        int             flags,
-       mode_t          mode)
+       mode_t          mode,
+       struct fs_path  *fs_path)
 {
+       struct fs_path  *fsp;
        int             fd;
        int             oflags;
 
@@ -118,6 +120,14 @@ openfile(
                        }
                }
        }
+
+       if (fs_path) {
+               fsp = fs_table_lookup(path, FS_MOUNT_POINT);
+               if (!fsp)
+                       memset(fs_path, 0, sizeof(*fs_path));
+               else
+                       *fs_path = *fsp;
+       }
        return fd;
 }
 
@@ -126,7 +136,8 @@ addfile(
        char            *name,
        int             fd,
        xfs_fsop_geom_t *geometry,
-       int             flags)
+       int             flags,
+       struct fs_path  *fs_path)
 {
        char            *filename;
 
@@ -154,6 +165,7 @@ addfile(
        file->flags = flags;
        file->name = filename;
        file->geom = *geometry;
+       file->fs_path = *fs_path;
        return 0;
 }
 
@@ -195,6 +207,7 @@ open_f(
        char            *sp;
        mode_t          mode = 0600;
        xfs_fsop_geom_t geometry = { 0 };
+       struct fs_path  fsp;
 
        if (argc == 1) {
                if (file)
@@ -257,14 +270,14 @@ open_f(
                return -1;
        }
 
-       fd = openfile(argv[optind], &geometry, flags, mode);
+       fd = openfile(argv[optind], &geometry, flags, mode, &fsp);
        if (fd < 0)
                return 0;
 
        if (!platform_test_xfs_fd(fd))
                flags |= IO_FOREIGN;
 
-       addfile(argv[optind], fd, &geometry, flags);
+       addfile(argv[optind], fd, &geometry, flags, &fsp);
        return 0;
 }
 
index 7c0bb7fb7d2bebf4332a20c4bb1d6b41651a4290..1c5dfca1e075d3764dbc83cc80bd3655876bda7c 100644 (file)
@@ -357,7 +357,7 @@ pwrite_f(
                return 0;
 
        c = IO_READONLY | (dflag ? IO_DIRECT : 0);
-       if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0))
+       if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0))
                return 0;
 
        gettimeofday(&t1, NULL);
index fe05d1eb162d33c39630f970eab7f29123a2458a..f584e8f1fe43893b1ede6e70d42e60f0b43754d1 100644 (file)
@@ -154,7 +154,7 @@ dedupe_f(
                return 0;
        }
 
-       fd = openfile(infile, NULL, IO_READONLY, 0);
+       fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
        if (fd < 0)
                return 0;
 
@@ -278,7 +278,7 @@ reflink_f(
        }
 
 clone_all:
-       fd = openfile(infile, NULL, IO_READONLY, 0);
+       fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
        if (fd < 0)
                return 0;
 
index edd31c92e0333d0d87e174369bd27f84e3c4c845..063fa7f4811417ca62bf549ada3f81b89023ea2d 100644 (file)
@@ -115,7 +115,7 @@ sendfile_f(
 
        if (!infile)
                fd = filetable[fd].fd;
-       else if ((fd = openfile(infile, NULL, IO_READONLY, 0)) < 0)
+       else if ((fd = openfile(infile, NULL, IO_READONLY, 0, NULL)) < 0)
                return 0;
 
        if (optind == argc - 2) {
index 29a036cded4adcbbcafb96214aec8fd5abbea624..273b9c54c52d13358799e59847ead104c14adede 100644 (file)
@@ -301,6 +301,105 @@ ioctl.  Options behave as described in the
 .BR xfs_bmap (8)
 manual page.
 .TP
+.BI "fsmap [ \-d | \-l | \-r ] [ \-m | \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
+Prints the mapping of disk blocks used by the filesystem hosting the current
+file.  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 ": " major ":" minor " [" 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.
+The optional
+.I start
+and
+.I end
+arguments can be used to constrain the output to a particular range of
+disk blocks.
+.RE
+.RS 1.0i
+.PD 0
+.TP
+.BI \-d
+Display only extents from the data device.
+This option only applies for XFS filesystems.
+.TP
+.BI \-l
+Display only extents from the external log device.
+This option only applies to XFS filesystems.
+.TP
+.BI \-r
+Display only extents from the realtime device.
+This option only applies to XFS filesystems.
+.TP
+.BI \-m
+Display results in a machine readable format (CSV).
+This option is not compatible with the
+.B \-v
+flag.
+The columns of the output are: extent number, device major, device minor,
+physical start, physical end, owner, offset start, offset end, length.
+The start, end, and length numbers are provided in units of 512b.
+The owner field is a special string that takes the form:
+
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.I inode_%lld_data
+for inode data.
+.TP
+.I inode_%lld_data_bmbt
+for inode data extent maps.
+.TP
+.I inode_%lld_attr
+for inode extended attribute data.
+.TP
+.I inode_%lld_attr_bmbt
+for inode extended attribute extent maps.
+.TP
+.I special_%u:%u
+for other filesystem metadata.
+.PD
+.RE
+
+.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 extents in groups of 131,072 records.
+.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.
+This option is not compatible with the
+.B \-m
+flag.
+.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