mirror of https://github.com/vitalif/e2fsprogs
filefrag: improvements to filefrag FIEMAP handling
Update the filefrag program to allow displaying the extents in some different formats. Try and stay within 80 columns. * add -k option to print extents in kB-sized units (like df -k) * add -b {blocksize} to print extents in blocksize units * add -e option to print extent format, even when FIBMAP is used * add -X option to print extents in hexadecimal format Internally, the FIBMAP handling code has been moved into its own function like FIEMAP, so that the code is more modular. Extent offsets are now handled in bytes instead of in blocks, to allow printing extents with arbitrary block sizes. The extent header printing also moved into its own function so that it can be shared between the FIEMAP and FIBMAP handling routines, since it got more complex with the different output options. Only print error about FIBMAP being root-only a single time. Print the filesystem type if it changes between specified files. Add fsync() for FIBMAP if "-s" is given. Signed-off-by: Andreas Dilger <adilger@whamcloud.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>debian-1.42.9
parent
0ce0172984
commit
2508eaa71e
|
@ -5,7 +5,10 @@ filefrag \- report on file fragmentation
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B filefrag
|
.B filefrag
|
||||||
[
|
[
|
||||||
.B \-Bbsvx
|
.BI \-b blocksize
|
||||||
|
]
|
||||||
|
[
|
||||||
|
.B \-BeksvxX
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
.I files...
|
.I files...
|
||||||
|
@ -27,8 +30,21 @@ If FIEMAP is not supported then filefrag will fall back to using FIBMAP.
|
||||||
Force the use of the older FIBMAP ioctl instead of the FIEMAP ioctl for
|
Force the use of the older FIBMAP ioctl instead of the FIEMAP ioctl for
|
||||||
testing purposes.
|
testing purposes.
|
||||||
.TP
|
.TP
|
||||||
.B \-b
|
.BI \-b blocksize
|
||||||
Use 1024 byte blocksize for the output.
|
Use
|
||||||
|
.I blocksize
|
||||||
|
in bytes for output instead of the filesystem blocksize.
|
||||||
|
For compatibility with earlier versions of
|
||||||
|
.BR filefrag ,
|
||||||
|
if
|
||||||
|
.I blocksize
|
||||||
|
is unspecified it defaults to 1024 bytes.
|
||||||
|
.TP
|
||||||
|
.B \-e
|
||||||
|
Print output in extent format, even for block-mapped files.
|
||||||
|
.TP
|
||||||
|
.BI \-k
|
||||||
|
Use 1024\-byte blocksize for output (identical to '\-b 1024').
|
||||||
.TP
|
.TP
|
||||||
.B \-s
|
.B \-s
|
||||||
Sync the file before requesting the mapping.
|
Sync the file before requesting the mapping.
|
||||||
|
@ -38,6 +54,9 @@ Be verbose when checking for file fragmentation.
|
||||||
.TP
|
.TP
|
||||||
.B \-x
|
.B \-x
|
||||||
Display mapping of extended attributes.
|
Display mapping of extended attributes.
|
||||||
|
.TP
|
||||||
|
.B \-X
|
||||||
|
Display extent block numbers in hexadecimal format.
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
.B filefrag
|
.B filefrag
|
||||||
was written by Theodore Ts'o <tytso@mit.edu>.
|
was written by Theodore Ts'o <tytso@mit.edu>.
|
||||||
|
|
360
misc/filefrag.c
360
misc/filefrag.c
|
@ -16,8 +16,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
fputs("This program is only supported on Linux!\n", stderr);
|
fputs("This program is only supported on Linux!\n", stderr);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define _LARGEFILE64_SOURCE
|
#define _LARGEFILE64_SOURCE
|
||||||
|
@ -40,23 +40,28 @@ extern int optind;
|
||||||
#include <sys/vfs.h>
|
#include <sys/vfs.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/fd.h>
|
#include <linux/fd.h>
|
||||||
|
#include <ext2fs/ext2fs.h>
|
||||||
#include <ext2fs/ext2_types.h>
|
#include <ext2fs/ext2_types.h>
|
||||||
#include <ext2fs/fiemap.h>
|
#include <ext2fs/fiemap.h>
|
||||||
|
|
||||||
int verbose = 0;
|
int verbose = 0;
|
||||||
int no_bs = 0; /* Don't use the files blocksize, use 1K blocksize */
|
int blocksize; /* Use specified blocksize (default 1kB) */
|
||||||
int sync_file = 0; /* fsync file before getting the mapping */
|
int sync_file = 0; /* fsync file before getting the mapping */
|
||||||
int xattr_map = 0; /* get xattr mapping */
|
int xattr_map = 0; /* get xattr mapping */
|
||||||
int force_bmap = 0;
|
int force_bmap; /* force use of FIBMAP instead of FIEMAP */
|
||||||
int logical_width = 12;
|
int force_extent; /* print output in extent format always */
|
||||||
int physical_width = 14;
|
int logical_width = 8;
|
||||||
unsigned long long filesize;
|
int physical_width = 10;
|
||||||
|
char *ext_fmt = "%4d: %*llu..%*llu: %*llu..%*llu: %6llu: %s\n";
|
||||||
|
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)
|
||||||
|
|
||||||
#define FIBMAP _IO(0x00, 1) /* bmap access */
|
#define FIBMAP _IO(0x00, 1) /* bmap access */
|
||||||
#define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */
|
#define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */
|
||||||
|
|
||||||
|
#define LUSTRE_SUPER_MAGIC 0x0BD00BD0
|
||||||
|
|
||||||
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
|
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
|
||||||
#define EXT3_IOC_GETFLAGS _IOR('f', 1, long)
|
#define EXT3_IOC_GETFLAGS _IOR('f', 1, long)
|
||||||
|
|
||||||
|
@ -102,30 +107,47 @@ static int get_bmap(int fd, unsigned long block, unsigned long *phy_blk)
|
||||||
if (errno == EPERM) {
|
if (errno == EPERM) {
|
||||||
fprintf(stderr, "No permission to use FIBMAP ioctl; "
|
fprintf(stderr, "No permission to use FIBMAP ioctl; "
|
||||||
"must have root privileges\n");
|
"must have root privileges\n");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
perror("FIBMAP");
|
|
||||||
}
|
}
|
||||||
*phy_blk = b;
|
*phy_blk = b;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
|
static void print_extent_header(void)
|
||||||
unsigned long long expected, int blk_shift)
|
|
||||||
{
|
{
|
||||||
__u64 phy_blk;
|
printf(" ext: %*s %*s length: %*s flags:\n",
|
||||||
|
logical_width * 2 + 3,
|
||||||
|
"logical_offset:",
|
||||||
|
physical_width * 2 + 3, "physical_offset:",
|
||||||
|
physical_width + 1,
|
||||||
|
"expected:");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
|
||||||
|
unsigned long long expected, int blk_shift,
|
||||||
|
ext2fs_struct_stat *st)
|
||||||
|
{
|
||||||
|
unsigned long long physical_blk;
|
||||||
unsigned long long logical_blk;
|
unsigned long long logical_blk;
|
||||||
unsigned long ext_len;
|
unsigned long long ext_len;
|
||||||
|
unsigned long long ext_blks;
|
||||||
char flags[256] = "";
|
char flags[256] = "";
|
||||||
|
|
||||||
/* For inline data all offsets should be in terms of bytes, not blocks */
|
/* For inline data all offsets should be in bytes, not blocks */
|
||||||
if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE)
|
if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE)
|
||||||
blk_shift = 0;
|
blk_shift = 0;
|
||||||
|
|
||||||
ext_len = fm_extent->fe_length >> blk_shift;
|
ext_len = fm_extent->fe_length >> blk_shift;
|
||||||
|
ext_blks = (fm_extent->fe_length - 1) >> blk_shift;
|
||||||
logical_blk = fm_extent->fe_logical >> blk_shift;
|
logical_blk = fm_extent->fe_logical >> blk_shift;
|
||||||
phy_blk = fm_extent->fe_physical >> blk_shift;
|
physical_blk = fm_extent->fe_physical >> blk_shift;
|
||||||
|
|
||||||
|
if (expected)
|
||||||
|
sprintf(flags, ext_fmt == hex_fmt ? "%*llx: " : "%*llu: ",
|
||||||
|
physical_width, expected >> blk_shift);
|
||||||
|
else
|
||||||
|
sprintf(flags, "%.*s ", physical_width, " ");
|
||||||
|
|
||||||
if (fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)
|
if (fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)
|
||||||
strcat(flags, "unknown,");
|
strcat(flags, "unknown,");
|
||||||
|
@ -144,28 +166,24 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
|
||||||
if (fm_extent->fe_flags & FIEMAP_EXTENT_MERGED)
|
if (fm_extent->fe_flags & FIEMAP_EXTENT_MERGED)
|
||||||
strcat(flags, "merged,");
|
strcat(flags, "merged,");
|
||||||
|
|
||||||
if (fm_extent->fe_logical + fm_extent->fe_length >= filesize)
|
if (fm_extent->fe_logical + fm_extent->fe_length >= st->st_size)
|
||||||
strcat(flags, "eof,");
|
strcat(flags, "eof,");
|
||||||
|
|
||||||
/* Remove trailing comma, if any */
|
/* Remove trailing comma, if any */
|
||||||
if (flags[0])
|
if (flags[0])
|
||||||
flags[strlen(flags) - 1] = '\0';
|
flags[strlen(flags) - 1] = '\0';
|
||||||
|
|
||||||
if (expected)
|
printf(ext_fmt, cur_ex, logical_width, logical_blk,
|
||||||
printf("%4d %*llu %*llu %*llu %6lu %s\n",
|
logical_width, logical_blk + ext_blks,
|
||||||
cur_ex, logical_width, logical_blk,
|
physical_width, physical_blk,
|
||||||
physical_width, phy_blk, physical_width, expected,
|
physical_width, physical_blk + ext_blks,
|
||||||
ext_len, flags);
|
ext_len, flags);
|
||||||
else
|
|
||||||
printf("%4d %*llu %*llu %*s %6lu %s\n",
|
|
||||||
cur_ex, logical_width, logical_blk,
|
|
||||||
physical_width, phy_blk, physical_width, "",
|
|
||||||
ext_len, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int filefrag_fiemap(int fd, int blk_shift, int *num_extents)
|
static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
|
||||||
|
ext2fs_struct_stat *st)
|
||||||
{
|
{
|
||||||
char buf[4096] = "";
|
char buf[16384];
|
||||||
struct fiemap *fiemap = (struct fiemap *)buf;
|
struct fiemap *fiemap = (struct fiemap *)buf;
|
||||||
struct fiemap_extent *fm_ext = &fiemap->fm_extents[0];
|
struct fiemap_extent *fm_ext = &fiemap->fm_extents[0];
|
||||||
int count = (sizeof(buf) - sizeof(*fiemap)) /
|
int count = (sizeof(buf) - sizeof(*fiemap)) /
|
||||||
|
@ -206,21 +224,13 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (verbose && !fiemap_header_printed) {
|
if (verbose && !fiemap_header_printed) {
|
||||||
printf(" ext %*s %*s %*s length flags\n", logical_width,
|
print_extent_header();
|
||||||
"logical", physical_width, "physical",
|
|
||||||
physical_width, "expected");
|
|
||||||
fiemap_header_printed = 1;
|
fiemap_header_printed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < fiemap->fm_mapped_extents; i++) {
|
for (i = 0; i < fiemap->fm_mapped_extents; i++) {
|
||||||
__u64 phy_blk, logical_blk;
|
if (fm_ext[i].fe_logical != 0 &&
|
||||||
unsigned long ext_len;
|
fm_ext[i].fe_physical != expected) {
|
||||||
|
|
||||||
phy_blk = fm_ext[i].fe_physical >> blk_shift;
|
|
||||||
ext_len = fm_ext[i].fe_length >> blk_shift;
|
|
||||||
logical_blk = fm_ext[i].fe_logical >> blk_shift;
|
|
||||||
|
|
||||||
if (logical_blk && phy_blk != expected) {
|
|
||||||
tot_extents++;
|
tot_extents++;
|
||||||
} else {
|
} else {
|
||||||
expected = 0;
|
expected = 0;
|
||||||
|
@ -229,16 +239,16 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents)
|
||||||
}
|
}
|
||||||
if (verbose)
|
if (verbose)
|
||||||
print_extent_info(&fm_ext[i], n, expected,
|
print_extent_info(&fm_ext[i], n, expected,
|
||||||
blk_shift);
|
blk_shift, st);
|
||||||
|
|
||||||
expected = phy_blk + ext_len;
|
expected = fm_ext[i].fe_physical + fm_ext[i].fe_length;
|
||||||
if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
|
if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
|
||||||
last = 1;
|
last = 1;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fiemap->fm_start = (fm_ext[i-1].fe_logical +
|
fiemap->fm_start = (fm_ext[i - 1].fe_logical +
|
||||||
fm_ext[i-1].fe_length);
|
fm_ext[i - 1].fe_length);
|
||||||
} while (last == 0);
|
} while (last == 0);
|
||||||
|
|
||||||
*num_extents = tot_extents;
|
*num_extents = tot_extents;
|
||||||
|
@ -248,26 +258,95 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents)
|
||||||
|
|
||||||
#define EXT2_DIRECT 12
|
#define EXT2_DIRECT 12
|
||||||
|
|
||||||
|
static int filefrag_fibmap(int fd, int blk_shift, int *num_extents,
|
||||||
|
ext2fs_struct_stat *st,
|
||||||
|
unsigned long numblocks, int is_ext2)
|
||||||
|
{
|
||||||
|
struct fiemap_extent fm_ext;
|
||||||
|
unsigned long i, last_block;
|
||||||
|
unsigned long long logical;
|
||||||
|
/* Blocks per indirect block */
|
||||||
|
const long bpib = st->st_blksize / 4;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
if (force_extent) {
|
||||||
|
memset(&fm_ext, 0, sizeof(fm_ext));
|
||||||
|
fm_ext.fe_flags = FIEMAP_EXTENT_MERGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_file)
|
||||||
|
fsync(fd);
|
||||||
|
|
||||||
|
for (i = 0, logical = 0, *num_extents = 0, count = last_block = 0;
|
||||||
|
i < numblocks;
|
||||||
|
i++, logical += st->st_blksize) {
|
||||||
|
unsigned long block = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (is_ext2 && last_block) {
|
||||||
|
if (((i - EXT2_DIRECT) % bpib) == 0)
|
||||||
|
last_block++;
|
||||||
|
if (((i - EXT2_DIRECT - bpib) % (bpib * bpib)) == 0)
|
||||||
|
last_block++;
|
||||||
|
if (((i - EXT2_DIRECT - bpib - bpib * bpib) %
|
||||||
|
(((unsigned long long)bpib) * bpib * bpib)) == 0)
|
||||||
|
last_block++;
|
||||||
|
}
|
||||||
|
rc = get_bmap(fd, i, &block);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
if (block == 0)
|
||||||
|
continue;
|
||||||
|
if (*num_extents == 0) {
|
||||||
|
(*num_extents)++;
|
||||||
|
if (force_extent) {
|
||||||
|
print_extent_header();
|
||||||
|
fm_ext.fe_physical = block * st->st_blksize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
if (force_extent && last_block != 0 &&
|
||||||
|
(block != last_block + 1 ||
|
||||||
|
fm_ext.fe_logical + fm_ext.fe_length != logical)) {
|
||||||
|
print_extent_info(&fm_ext, *num_extents - 1,
|
||||||
|
(last_block + 1) * st->st_blksize,
|
||||||
|
blk_shift, st);
|
||||||
|
fm_ext.fe_logical = logical;
|
||||||
|
fm_ext.fe_physical = block * st->st_blksize;
|
||||||
|
fm_ext.fe_length = 0;
|
||||||
|
(*num_extents)++;
|
||||||
|
} else if (verbose && last_block && (block != last_block + 1)) {
|
||||||
|
printf("Discontinuity: Block %ld is at %lu (was %lu)\n",
|
||||||
|
i, block, last_block + 1);
|
||||||
|
(*num_extents)++;
|
||||||
|
}
|
||||||
|
fm_ext.fe_length += st->st_blksize;
|
||||||
|
last_block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force_extent)
|
||||||
|
print_extent_info(&fm_ext, *num_extents - 1,
|
||||||
|
last_block * st->st_blksize, blk_shift, st);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static void frag_report(const char *filename)
|
static void frag_report(const char *filename)
|
||||||
{
|
{
|
||||||
struct statfs fsinfo;
|
static struct statfs fsinfo;
|
||||||
#ifdef HAVE_FSTAT64
|
ext2fs_struct_stat st;
|
||||||
struct stat64 fileinfo;
|
int blk_shift;
|
||||||
#else
|
|
||||||
struct stat fileinfo;
|
|
||||||
#endif
|
|
||||||
int bs;
|
int bs;
|
||||||
long fd;
|
long fd;
|
||||||
unsigned long block, last_block = 0, numblocks, i, count = 0;
|
unsigned long numblocks;
|
||||||
long bpib; /* Blocks per indirect block */
|
int data_blocks_per_cyl = 1;
|
||||||
long cylgroups;
|
int num_extents = 1, expected = ~0;
|
||||||
int num_extents = 0, expected;
|
|
||||||
int is_ext2 = 0;
|
int is_ext2 = 0;
|
||||||
static int once = 1;
|
static dev_t last_device;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
int rc;
|
int width;
|
||||||
|
|
||||||
#ifdef HAVE_OPEN64
|
#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
|
||||||
fd = open64(filename, O_RDONLY);
|
fd = open64(filename, O_RDONLY);
|
||||||
#else
|
#else
|
||||||
fd = open(filename, O_RDONLY);
|
fd = open(filename, O_RDONLY);
|
||||||
|
@ -277,103 +356,98 @@ static void frag_report(const char *filename)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statfs(filename, &fsinfo) < 0) {
|
#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
|
||||||
perror("statfs");
|
if (fstat64(fd, &st) < 0) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef HAVE_FSTAT64
|
|
||||||
if (stat64(filename, &fileinfo) < 0) {
|
|
||||||
#else
|
#else
|
||||||
if (stat(filename, &fileinfo) < 0) {
|
if (fstat(fd, &st) < 0) {
|
||||||
#endif
|
#endif
|
||||||
perror("stat");
|
perror("stat");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (last_device != st.st_dev) {
|
||||||
|
if (fstatfs(fd, &fsinfo) < 0) {
|
||||||
|
perror("fstatfs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (verbose)
|
||||||
|
printf("Filesystem type is: %lx\n",
|
||||||
|
(unsigned long) fsinfo.f_type);
|
||||||
|
}
|
||||||
|
st.st_blksize = fsinfo.f_bsize;
|
||||||
if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0)
|
if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0)
|
||||||
flags = 0;
|
flags = 0;
|
||||||
if (!(flags & EXT4_EXTENTS_FL) &&
|
if (!(flags & EXT4_EXTENTS_FL) &&
|
||||||
((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) ||
|
((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) ||
|
||||||
(fsinfo.f_type == 0xef53)))
|
(fsinfo.f_type == 0xef53)))
|
||||||
is_ext2++;
|
is_ext2++;
|
||||||
if (verbose && once)
|
|
||||||
printf("Filesystem type is: %lx\n",
|
|
||||||
(unsigned long) fsinfo.f_type);
|
|
||||||
|
|
||||||
cylgroups = div_ceil(fsinfo.f_blocks, fsinfo.f_bsize*8);
|
if (is_ext2) {
|
||||||
if (verbose && is_ext2 && once)
|
long cylgroups = div_ceil(fsinfo.f_blocks, fsinfo.f_bsize * 8);
|
||||||
printf("Filesystem cylinder groups is approximately %ld\n",
|
|
||||||
cylgroups);
|
|
||||||
|
|
||||||
physical_width = int_log10(fsinfo.f_blocks);
|
if (verbose && last_device != st.st_dev)
|
||||||
if (physical_width < 8)
|
printf("Filesystem cylinder groups approximately %ld\n",
|
||||||
physical_width = 8;
|
cylgroups);
|
||||||
|
|
||||||
if (ioctl(fd, FIGETBSZ, &bs) < 0) { /* FIGETBSZ takes an int */
|
data_blocks_per_cyl = fsinfo.f_bsize * 8 -
|
||||||
perror("FIGETBSZ");
|
(fsinfo.f_files / 8 / cylgroups) - 3;
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
last_device = st.st_dev;
|
||||||
|
|
||||||
if (no_bs)
|
width = int_log10(fsinfo.f_blocks);
|
||||||
bs = 1024;
|
if (width > physical_width)
|
||||||
|
physical_width = width;
|
||||||
|
|
||||||
bpib = bs / 4;
|
numblocks = (st.st_size + fsinfo.f_bsize - 1) / fsinfo.f_bsize;
|
||||||
numblocks = (fileinfo.st_size + (bs-1)) / bs;
|
if (blocksize != 0)
|
||||||
logical_width = int_log10(numblocks);
|
blk_shift = int_log2(blocksize);
|
||||||
if (logical_width < 7)
|
else
|
||||||
logical_width = 7;
|
blk_shift = int_log2(fsinfo.f_bsize);
|
||||||
filesize = (long long)fileinfo.st_size;
|
|
||||||
|
width = int_log10(numblocks);
|
||||||
|
if (width > logical_width)
|
||||||
|
logical_width = width;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("File size of %s is %lld (%ld block%s, blocksize %d)\n",
|
printf("File size of %s is %llu (%lu block%s of %d bytes)\n",
|
||||||
filename, (long long) fileinfo.st_size, numblocks,
|
filename, (unsigned long long)st.st_size,
|
||||||
numblocks == 1 ? "" : "s", bs);
|
numblocks * fsinfo.f_bsize >> blk_shift,
|
||||||
|
numblocks == 1 ? "" : "s", 1 << blk_shift);
|
||||||
|
|
||||||
if (force_bmap ||
|
if (force_bmap ||
|
||||||
filefrag_fiemap(fd, int_log2(bs), &num_extents) != 0) {
|
filefrag_fiemap(fd, blk_shift, &num_extents, &st) != 0) {
|
||||||
for (i = 0, count = 0; i < numblocks; i++) {
|
expected = filefrag_fibmap(fd, blk_shift, &num_extents,
|
||||||
if (is_ext2 && last_block) {
|
&st, numblocks, is_ext2);
|
||||||
if (((i-EXT2_DIRECT) % bpib) == 0)
|
if (expected < 0) {
|
||||||
last_block++;
|
if (errno == EINVAL || errno == ENOTTY) {
|
||||||
if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0)
|
fprintf(stderr, "%s: FIBMAP unsupported\n",
|
||||||
last_block++;
|
filename);
|
||||||
if (((i-EXT2_DIRECT-bpib-bpib*bpib) %
|
} else if (errno != EPERM) {
|
||||||
(((__u64) bpib)*bpib*bpib)) == 0)
|
fprintf(stderr, "%s: FIBMAP error: %s",
|
||||||
last_block++;
|
filename, strerror(errno));
|
||||||
}
|
}
|
||||||
rc = get_bmap(fd, i, &block);
|
goto out_close;
|
||||||
if (block == 0)
|
|
||||||
continue;
|
|
||||||
if (!num_extents)
|
|
||||||
num_extents++;
|
|
||||||
count++;
|
|
||||||
if (last_block && (block != last_block+1) ) {
|
|
||||||
if (verbose)
|
|
||||||
printf("Discontinuity: Block %ld is at "
|
|
||||||
"%lu (was %lu)\n",
|
|
||||||
i, block, last_block+1);
|
|
||||||
num_extents++;
|
|
||||||
}
|
|
||||||
last_block = block;
|
|
||||||
}
|
}
|
||||||
|
expected = expected / data_blocks_per_cyl + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_extents == 1)
|
if (num_extents == 1)
|
||||||
printf("%s: 1 extent found", filename);
|
printf("%s: 1 extent found", filename);
|
||||||
else
|
else
|
||||||
printf("%s: %d extents found", filename, num_extents);
|
printf("%s: %d extents found", filename, num_extents);
|
||||||
/* count, and thus expected, only set for indirect FIBMAP'd files */
|
/* count, and thus expected, only set for indirect FIBMAP'd files */
|
||||||
if (is_ext2) {
|
if (is_ext2 && expected && expected < num_extents)
|
||||||
expected = (count/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1;
|
printf(", perfection would be %d extent%s\n", expected,
|
||||||
if (expected && expected < num_extents)
|
(expected > 1) ? "s" : "");
|
||||||
printf(", perfection would be %d extent%s\n", expected,
|
else
|
||||||
(expected>1) ? "s" : "");
|
|
||||||
} else
|
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
|
out_close:
|
||||||
close(fd);
|
close(fd);
|
||||||
once = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usage(const char *progname)
|
static void usage(const char *progname)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: %s [-Bbvsx] file ...\n", progname);
|
fprintf(stderr, "Usage: %s [-b{blocksize}] [-BeklsvxX] file ...\n",
|
||||||
|
progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,23 +456,61 @@ int main(int argc, char**argv)
|
||||||
char **cpp;
|
char **cpp;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "Bbsvx")) != EOF)
|
while ((c = getopt(argc, argv, "Bb::eksvxX")) != EOF)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'B':
|
case 'B':
|
||||||
force_bmap++;
|
force_bmap++;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
no_bs++;
|
if (optarg) {
|
||||||
|
char *end;
|
||||||
|
blocksize = strtoul(optarg, &end, 0);
|
||||||
|
if (end) {
|
||||||
|
switch (end[0]) {
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
blocksize *= 1024;
|
||||||
|
/* no break */
|
||||||
|
case 'm':
|
||||||
|
case 'M':
|
||||||
|
blocksize *= 1024;
|
||||||
|
/* no break */
|
||||||
|
case 'k':
|
||||||
|
case 'K':
|
||||||
|
blocksize *= 1024;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { /* Allow -b without argument for compat. Remove
|
||||||
|
* this eventually so "-b {blocksize}" works */
|
||||||
|
fprintf(stderr, "%s: -b needs a blocksize "
|
||||||
|
"option, assuming 1024-byte blocks.\n",
|
||||||
|
argv[0]);
|
||||||
|
blocksize = 1024;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'e':
|
||||||
verbose++;
|
force_extent++;
|
||||||
|
if (!verbose)
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
blocksize = 1024;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
sync_file++;
|
sync_file++;
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
xattr_map++;
|
xattr_map++;
|
||||||
break;
|
break;
|
||||||
|
case 'X':
|
||||||
|
ext_fmt = hex_fmt;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue