Whamcloud - gitweb
libext2fs: add a regression test for in-inode xattrs
[tools/e2fsprogs.git] / misc / filefrag.c
index 6308bc6..9dab747 100644 (file)
@@ -51,21 +51,24 @@ extern int optind;
 #include <ext2fs/ext2fs.h>
 #include <ext2fs/ext2_types.h>
 #include <ext2fs/fiemap.h>
+#include "../version.h"
 
 int verbose = 0;
 unsigned int blocksize;        /* Use specified blocksize (default 1kB) */
 int sync_file = 0;     /* fsync file before getting the mapping */
 int precache_file = 0; /* precache the file before getting the mapping */
 int xattr_map = 0;     /* get xattr mapping */
-int force_bmap;        /* force use of FIBMAP instead of FIEMAP */
+int force_bmap;                /* force use of FIBMAP instead of FIEMAP */
 int force_extent;      /* print output in extent format always */
 int use_extent_cache;  /* Use extent cache */
+int device_offset;     /* extents report device-relative offsets */
 int logical_width = 8;
 int physical_width = 10;
 const char *ext_fmt = "%4d: %*llu..%*llu: %*llu..%*llu: %6llu: %s\n";
 const char *hex_fmt = "%4d: %*llx..%*llx: %*llx..%*llx: %6llx: %s\n";
 
-#define FILEFRAG_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+#define FILEFRAG_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR |\
+                                     FIEMAP_FLAG_DEVICE_ORDER)
 
 #define FIBMAP         _IO(0x00, 1)    /* bmap access */
 #define FIGETBSZ       _IO(0x00, 2)    /* get the block size used for bmap */
@@ -124,21 +127,59 @@ static void print_extent_header(void)
 {
        printf(" ext: %*s %*s length: %*s flags:\n",
               logical_width * 2 + 3,
-              "logical_offset:",
+              device_offset ? "device_logical:" : "logical_offset:",
               physical_width * 2 + 3, "physical_offset:",
-              physical_width + 1,
-              "expected:");
+              device_offset ? 5 : physical_width + 1,
+              device_offset ? " dev:" : "expected:");
 }
 
 static void print_flag(__u32 *flags, __u32 mask, char *buf, const char *name)
 {
+       char hex[sizeof(mask) * 2 + 4]; /* 2 chars/byte + 0x, + NUL */
+
        if ((*flags & mask) == 0)
                return;
 
+       if (name == NULL) {
+               sprintf(hex, "%#04x,", mask);
+               name = hex;
+       }
        strcat(buf, name);
        *flags &= ~mask;
 }
 
+static void print_flags(__u32 fe_flags, char *flags, int len, int print_unknown)
+{
+       __u32 mask;
+
+       print_flag(&fe_flags, FIEMAP_EXTENT_LAST, flags, "last,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_UNKNOWN, flags, "unknown_loc,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_DELALLOC, flags, "delalloc,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_ENCODED, flags, "encoded,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_ENCRYPTED, flags,"encrypted,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_NOT_ALIGNED, flags, "not_aligned,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_INLINE, flags, "inline,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_TAIL, flags, "tail_packed,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_UNWRITTEN, flags, "unwritten,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_MERGED, flags, "merged,");
+       print_flag(&fe_flags, FIEMAP_EXTENT_SHARED, flags, "shared,");
+       print_flag(&fe_flags, EXT4_FIEMAP_EXTENT_HOLE, flags, "hole,");
+
+       print_flag(&fe_flags, FIEMAP_EXTENT_NET, flags, "net,");
+
+       if (!print_unknown)
+               goto out;
+
+       /* print any unknown flags as hex values */
+       for (mask = 1; fe_flags != 0 && mask != 0; mask <<= 1)
+               print_flag(&fe_flags, mask, flags, NULL);
+out:
+       /* Remove trailing comma, if any */
+       if (flags[0])
+               flags[strnlen(flags, len) - 1] = '\0';
+
+}
+
 static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
                              unsigned long long expected, int blk_shift,
                              ext2fs_struct_stat *st)
@@ -148,7 +189,6 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
        unsigned long long ext_len;
        unsigned long long ext_blks;
        unsigned long long ext_blks_phys;
-       __u32 fe_flags, mask;
        char flags[256] = "";
 
        /* For inline data all offsets should be in bytes, not blocks */
@@ -164,44 +204,19 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
                physical_blk = fm_extent->fe_physical >> blk_shift;
        }
 
-       fe_flags = fm_extent->fe_flags;
-       if (expected &&
-           !(fe_flags & FIEMAP_EXTENT_UNKNOWN) &&
-           !(fe_flags & EXT4_FIEMAP_EXTENT_HOLE))
+       if (device_offset)
+               sprintf(flags, "%04x: ", fm_extent->fe_device & 0xffff);
+       else if (expected &&
+           !(fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN) &&
+           !(fm_extent->fe_flags & EXT4_FIEMAP_EXTENT_HOLE))
                sprintf(flags, ext_fmt == hex_fmt ? "%*llx: " : "%*llu: ",
                        physical_width, expected >> blk_shift);
-       else
-               sprintf(flags, "%.*s  ", physical_width, "                   ");
-
-       print_flag(&fe_flags, FIEMAP_EXTENT_LAST, flags, "last,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_UNKNOWN, flags, "unknown_loc,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_DELALLOC, flags, "delalloc,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_ENCODED, flags, "encoded,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_ENCRYPTED, flags,"encrypted,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_NOT_ALIGNED, flags, "not_aligned,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_INLINE, flags, "inline,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_DATA_TAIL, flags, "tail_packed,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_UNWRITTEN, flags, "unwritten,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_MERGED, flags, "merged,");
-       print_flag(&fe_flags, FIEMAP_EXTENT_SHARED, flags, "shared,");
-       print_flag(&fe_flags, EXT4_FIEMAP_EXTENT_HOLE, flags, "hole,");
-       /* print any unknown flags as hex values */
-       for (mask = 1; fe_flags != 0 && mask != 0; mask <<= 1) {
-               char hex[sizeof(mask) * 2 + 4]; /* 2 chars/byte + 0x, + NUL */
 
-               if ((fe_flags & mask) == 0)
-                       continue;
-               sprintf(hex, "%#04x,", mask);
-               print_flag(&fe_flags, mask, flags, hex);
-       }
+       print_flags(fm_extent->fe_flags, flags, sizeof(flags), 1);
 
        if (fm_extent->fe_logical + fm_extent->fe_length >=
-           (unsigned long long) st->st_size)
-               strcat(flags, "eof,");
-
-       /* Remove trailing comma, if any */
-       if (flags[0] != '\0')
-               flags[strnlen(flags, sizeof(flags)) - 1] = '\0';
+           (unsigned long long)st->st_size)
+               strcat(flags, flags[0] ? ",eof" : "eof");
 
        if ((fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN) ||
            (fm_extent->fe_flags & EXT4_FIEMAP_EXTENT_HOLE)) {
@@ -233,6 +248,7 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
        unsigned long cmd = FS_IOC_FIEMAP;
        int fiemap_header_printed = 0;
        int tot_extents = 0, n = 0;
+       int previous_device = -1;
        int last = 0;
        int rc;
 
@@ -251,6 +267,12 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
        if (use_extent_cache)
                cmd = EXT4_IOC_GET_ES_CACHE;
 
+       if (device_offset) {
+               flags |= FIEMAP_FLAG_DEVICE_ORDER;
+               memset(fm_ext, 0, sizeof(struct fiemap_extent));
+       }
+
+retry_wo_device_order:
        do {
                fiemap->fm_length = ~0ULL;
                fiemap->fm_flags = flags;
@@ -265,6 +287,10 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
                                                "flags %x\n",
                                       fiemap->fm_flags);
                                fiemap_incompat_printed = 1;
+                       } else if (rc == EBADR && (fiemap->fm_flags &
+                                                  FIEMAP_FLAG_DEVICE_ORDER)) {
+                               flags &= ~FIEMAP_FLAG_DEVICE_ORDER;
+                               goto retry_wo_device_order;
                        }
                        return rc;
                }
@@ -283,9 +309,10 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
                                         fm_last.fe_length;
                        expected = fm_last.fe_physical +
                                   fm_ext[i].fe_logical - fm_last.fe_logical;
-                       if (fm_ext[i].fe_logical != 0 &&
-                           fm_ext[i].fe_physical != expected &&
-                           fm_ext[i].fe_physical != expected_dense) {
+                       if ((fm_ext[i].fe_logical != 0 &&
+                            fm_ext[i].fe_physical != expected &&
+                            fm_ext[i].fe_physical != expected_dense) ||
+                           ((fm_ext[i].fe_device & 0xffff) != previous_device)) {
                                tot_extents++;
                        } else {
                                expected = 0;
@@ -299,10 +326,23 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
                                last = 1;
                        fm_last = fm_ext[i];
                        n++;
+                       previous_device = fm_ext[i].fe_device & 0xffff;
                }
 
-               fiemap->fm_start = (fm_ext[i - 1].fe_logical +
-                                   fm_ext[i - 1].fe_length);
+               /* For DEVICE_ORDER mappings, if EXTENT_LAST not yet found then
+                * fm_start needs to be the same as it was for earlier ioctl.
+                * The first extent is used to pass the end offset and device
+                * of the last FIEMAP call.  Otherwise, we ask for extents
+                * starting from where the last mapping ended. */
+               if (flags & FIEMAP_FLAG_DEVICE_ORDER) {
+                       fm_ext[0].fe_logical =  fm_ext[i - 1].fe_logical +
+                                               fm_ext[i - 1].fe_length;
+                       fm_ext[0].fe_device =   fm_ext[i - 1].fe_device;
+                       fiemap->fm_start =      0;
+               } else {
+                       fiemap->fm_start =      fm_ext[i - 1].fe_logical +
+                                               fm_ext[i - 1].fe_length;
+               }
        } while (last == 0);
 
        *num_extents = tot_extents;
@@ -326,6 +366,7 @@ static int filefrag_fibmap(int fd, int blk_shift, int *num_extents,
        memset(&fm_ext, 0, sizeof(fm_ext));
        memset(&fm_last, 0, sizeof(fm_last));
        if (force_extent) {
+               fm_ext.fe_device = st->st_dev;
                fm_ext.fe_flags = FIEMAP_EXTENT_MERGED;
        }
 
@@ -374,9 +415,9 @@ static int filefrag_fibmap(int fd, int blk_shift, int *num_extents,
                        if (verbose && expected != 0) {
                                printf("Discontinuity: Block %llu is at %llu "
                                       "(was %llu)\n",
-                                       fm_ext.fe_logical / st->st_blksize,
-                                       fm_ext.fe_physical / st->st_blksize,
-                                       expected / st->st_blksize);
+                                      (unsigned long long) (fm_ext.fe_logical / st->st_blksize),
+                                      (unsigned long long) (fm_ext.fe_physical / st->st_blksize),
+                                      (unsigned long long) (expected / st->st_blksize));
                        }
                        /* create the new extent */
                        fm_last = fm_ext;
@@ -438,13 +479,13 @@ static int frag_report(const char *filename)
                goto out_close;
        }
 
-       if (last_device != st.st_dev) {
+       if ((last_device != st.st_dev) || !st.st_dev) {
                if (fstatfs(fd, &fsinfo) < 0) {
                        rc = -errno;
                        perror("fstatfs");
                        goto out_close;
                }
-               if (ioctl(fd, FIGETBSZ, &blksize) < 0)
+               if ((ioctl(fd, FIGETBSZ, &blksize) < 0) || !blksize)
                        blksize = fsinfo.f_bsize;
                if (verbose)
                        printf("Filesystem type is: %lx\n",
@@ -460,6 +501,13 @@ static int frag_report(const char *filename)
                        is_ext2 = 1;
        }
 
+       /* Check if filesystem is Lustre.  Always print in extent format
+        * with 1kB blocks, using the device-relative logical offsets. */
+       if (fsinfo.f_type == LUSTRE_SUPER_MAGIC) {
+               device_offset = 1;
+               blocksize = blocksize ?: 1024;
+       }
+
        if (is_ext2) {
                long cylgroups = div_ceil(fsinfo.f_blocks, blksize * 8);
 
@@ -492,8 +540,8 @@ static int frag_report(const char *filename)
                __u32 state;
 
                printf("File size of %s is %llu (%llu block%s of %d bytes)",
-                      filename, (unsigned long long)st.st_size,
-                      numblocks * blksize >> blk_shift,
+                      filename, (unsigned long long) st.st_size,
+                      (unsigned long long) (numblocks * blksize >> blk_shift),
                       numblocks == 1 ? "" : "s", 1 << blk_shift);
                if (use_extent_cache &&
                    ioctl(fd, EXT4_IOC_GETSTATE, &state) == 0 &&
@@ -522,8 +570,8 @@ static int frag_report(const char *filename)
                                           &st, numblocks, is_ext2);
                if (expected < 0) {
                        if (expected == -EINVAL || expected == -ENOTTY) {
-                               fprintf(stderr, "%s: FIBMAP unsupported\n",
-                                       filename);
+                               fprintf(stderr, "%s: FIBMAP%s unsupported\n",
+                                       filename, force_bmap ? "" : "/FIEMAP");
                        } else if (expected == -EPERM) {
                                fprintf(stderr,
                                        "%s: FIBMAP requires root privileges\n",
@@ -567,11 +615,13 @@ int main(int argc, char**argv)
 {
        char **cpp;
        int rc = 0, c;
+       int version = 0;
 
-       while ((c = getopt(argc, argv, "Bb::eEkPsvxX")) != EOF) {
+       while ((c = getopt(argc, argv, "Bb::eEkPlsvxXV")) != EOF) {
                switch (c) {
                case 'B':
                        force_bmap++;
+                       force_extent = 0;
                        break;
                case 'b':
                        if (optarg) {
@@ -636,12 +686,18 @@ int main(int argc, char**argv)
                case 'P':
                        precache_file++;
                        break;
+               case 'l':
+                       device_offset++;
+                       break;
                case 's':
                        sync_file++;
                        break;
                case 'v':
                        verbose++;
                        break;
+               case 'V':
+                       version++;
+                       break;
                case 'x':
                        xattr_map++;
                        break;
@@ -653,6 +709,17 @@ int main(int argc, char**argv)
                        break;
                }
        }
+       if (version) {
+               /* Print version number and exit */
+               printf("filefrag %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+               if (version + verbose > 1) {
+                       char flags[256] = "";
+
+                       print_flags(0xffffffff, flags, sizeof(flags), 0);
+                       printf("supported: %s\n", flags);
+               }
+               exit(0);
+       }
 
        if (optind == argc)
                usage(argv[0]);