@@ -9,7 +9,7 @@ LTCOMMAND = xfs_io
LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
HFILES = init.h io.h
CFILES = init.c \
- attr.c bmap.c cowextsize.c encrypt.c file.c freeze.c fsync.c \
+ attr.c bmap.c cowextsize.c encrypt.c file.c freeze.c fsmap.c fsync.c \
getrusage.c imap.c link.c mmap.c open.c parent.c pread.c prealloc.c \
pwrite.c reflink.c seek.c shutdown.c sync.c truncate.c utimes.c
@@ -121,7 +121,7 @@ copy_range_f(int argc, char **argv)
if (optind != argc - 1)
return command_usage(©_range_cmd);
- fd = openfile(argv[optind], NULL, IO_READONLY, 0);
+ fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
if (fd < 0)
return 0;
@@ -20,6 +20,7 @@
#include "platform_defs.h"
#include "command.h"
#include "init.h"
+#include "path.h"
#include "io.h"
#ifndef ARRAY_SIZE
new file mode 100644
@@ -0,0 +1,553 @@
+/*
+ * 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 an XFS filesystem"
+"\n"
+" Example:\n"
+" 'fsmap -dlrv [-n nr] [startoff] [endoff]' - 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"
+" -d -- query only the data device.\n"
+" -l -- query only the log device.\n"
+" -r -- query only the realtime device.\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 FMR_OWN_FREE:
+ return _("free space");
+ case FMR_OWN_UNKNOWN:
+ return _("unknown");
+ case FMR_OWN_FS:
+ return _("static fs metadata");
+ case FMR_OWN_LOG:
+ return _("journalling log");
+ case FMR_OWN_AG:
+ return _("per-AG metadata");
+ case FMR_OWN_INOBT:
+ return _("inode btree");
+ case FMR_OWN_INODES:
+ return _("inodes");
+ case FMR_OWN_REFC:
+ return _("refcount btree");
+ case FMR_OWN_COW:
+ return _("cow reservation");
+ case FMR_OWN_DEFECTIVE:
+ return _("defective");
+ default:
+ return _("unknown");
+ }
+}
+
+static void
+dump_map(
+ unsigned long long *nr,
+ struct fsmap_head *head)
+{
+ unsigned long long i;
+ struct fsmap *p;
+
+ 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));
+ if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+ printf("%s", special_owner(p->fmr_owner));
+ else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+ printf(_("inode %lld extent map"),
+ (long long) p->fmr_owner);
+ else
+ printf(_("inode %lld %lld..%lld"),
+ (long long)p->fmr_owner,
+ (long long)BTOBBT(p->fmr_offset),
+ (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+ printf(_(" %lld blocks\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_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 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];
+ 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)));
+ 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)));
+ }
+ agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount));
+ 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));
+ 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 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 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 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, "dln:rv")) != EOF) {
+ switch (c) {
+ case 'd': /* data device */
+ dflag = 1;
+ break;
+ case 'l': /* log device */
+ lflag = 1;
+ break;
+ case 'n': /* number of extents specified */
+ nflag = atoi(optarg);
+ 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)
+ return command_usage(&fsmap_cmd);
+
+ if (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 = 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 : 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 = xfsctl(file->name, file->fd, XFS_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 > INT_MAX ? INT_MAX : 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 = xfsctl(file->name, file->fd, XFS_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(&nr, head);
+ else
+ dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
+
+ p = &head->fmh_recs[head->fmh_entries - 1];
+ if (p->fmr_flags & FMR_OF_LAST)
+ break;
+
+ head->fmh_keys[0] = *p;
+ } 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;
+ 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);
+}
@@ -66,6 +66,7 @@ init_commands(void)
file_init();
flink_init();
freeze_init();
+ fsmap_init();
fsync_init();
getrusage_init();
help_init();
@@ -138,6 +139,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, "");
@@ -147,6 +149,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':
@@ -211,11 +214,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++;
}
@@ -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;
@@ -98,6 +102,7 @@ extern void encrypt_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);
@@ -144,8 +144,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;
@@ -210,6 +212,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;
}
@@ -218,7 +228,8 @@ addfile(
char *name,
int fd,
xfs_fsop_geom_t *geometry,
- int flags)
+ int flags,
+ struct fs_path *fs_path)
{
char *filename;
@@ -246,6 +257,7 @@ addfile(
file->flags = flags;
file->name = filename;
file->geom = *geometry;
+ file->fs_path = *fs_path;
return 0;
}
@@ -287,6 +299,7 @@ open_f(
char *sp;
mode_t mode = 0600;
xfs_fsop_geom_t geometry = { 0 };
+ struct fs_path fsp;
if (argc == 1) {
if (file)
@@ -349,14 +362,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;
}
@@ -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);
@@ -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;
@@ -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) {
@@ -301,6 +301,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 ": " 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.
+.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
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> --- io/Makefile | 2 io/copy_file_range.c | 2 io/encrypt.c | 1 io/fsmap.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++++++ io/init.c | 8 + io/io.h | 9 + io/open.c | 21 ++ io/pwrite.c | 2 io/reflink.c | 4 io/sendfile.c | 2 man/man8/xfs_io.8 | 47 ++++ 11 files changed, 637 insertions(+), 14 deletions(-) create mode 100644 io/fsmap.c -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html