filefrag using FIEMAP
PATCH 1/1 filefrag using FIEMAP
This patch adds FIEMAP support to the filefrag utility. It also changes the output format of filefrag by adding a "-e" option. This would make the output more readable and easy for scripting.
For cluster filesystems like Lustre, the extent information will be displayed in lun offset order.
Signed-off-by Andreas Dilger <[email protected]>
Signed-off-by Kalpak Shah <[email protected]>
misc/filefrag.c +222 -35
@@ -38,13 +38,24 @@ extern int optind;#include <sys/vfs.h> #include <sys/ioctl.h> #include <linux/fd.h> +#include <ext2fs/ext2_types.h> +#include <ext2fs/fiemap.h> -int verbose =0 ; +#define LUSTRE_SUPER_MAGIC 0x0BD00BD0 -#define FIBMAP _IO(0x00,1)/* bmap access */ -#define FIGETBSZ _IO(0x00,2)/* get the block size used for bmap */ +int verbose =0 ; +int extent_format =0 ;/* Print output in extent format */ +int is_lustre =0 ;/* Indicates lustre filesystem */ +int lun_offset =0 ;/* extents are in lun offset order */ + +#define FILEFRAG_FIEMAP_INCOMPAT_UNSUPP (FIEMAP_FLAG_INCOMPAT & \ + ~(FIEMAP_FLAG_LUN_OFFSET)) + +#define FIBMAP _IO(0x00, 1)/* bmap access */ +#define FIGETBSZ _IO(0x00, 2)/* get the block size used for bmap */ +#define EXT4_IOC_FIEMAP _IOWR('f', 10, struct fiemap)/* get file extent info*/ -#define EXT4_EXTENTS_FL 0x00080000/* Inode uses extents */ +#define EXT4_EXTENTS_FL 0x00080000/* Inode uses extents */ #define EXT3_IOC_GETFLAGS _IOR('f', 1, long)static unsigned int div_ceil (unsigned int a,unsigned int b)
@@ -54,21 +65,168 @@ static unsigned int div_ceil(unsigned inreturn ((a -1 ) / b) +1 ; } -static unsigned long get_bmap (int fd,unsigned long block) +static int get_bmap (int fd,unsigned long block,unsigned long *phy_blk) {int ret;unsigned int b; b = block; - ret =ioctl (fd,FIBMAP , &b);/* FIBMAP takes a pointer to an integer */ + ret =ioctl (fd,FIBMAP , &b);/* FIBMAP takes pointer to integer */ if (ret <0 ) {if (errno ==EPERM ) { -fprintf (stderr,"No permission to use FIBMAP ioctl; must have root privileges\n" ); +fprintf (stderr,"No permission to use FIBMAP ioctl; " +"must have root privileges\n" );exit (1 ); }perror ("FIBMAP" ); } -return b; + *phy_blk = b; + +return ret; +} + +static void print_extent_info (struct fiemap_extent *fm_extent,int cur_ex, +unsigned long long logical_blk,int bs) +{ +__u64 phy_blk; +unsigned long ext_len; +char flags[256 ] ="" ; + + phy_blk = fm_extent->fe_offset / bs; + ext_len = fm_extent->fe_length / bs; + +if (fm_extent->fe_flags &FIEMAP_EXTENT_HOLE ) +strcat (flags,"hole," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_EOF ) +strcat (flags,"eof," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_DELALLOC ) +strcat (flags,"delalloc," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_UNWRITTEN ) +strcat (flags,"unwritten," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_UNMAPPED ) +strcat (flags,"unmapped," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_SECONDARY ) +strcat (flags,"secondary," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_NO_DIRECT ) +strcat (flags,"no_direct," ); +if (fm_extent->fe_flags &FIEMAP_EXTENT_ERROR ) +strcat (flags,"mapping_error" ); + +/* Remove trailing comma, if any */ +if (strlen (flags)) + flags[strlen (flags) -1 ] ='\0' ; + +printf ("%5d:%10llu..%10llu:%10llu..%10llu:%10lu:%3d: %s\n" , + cur_ex, logical_blk, logical_blk + ext_len -1 , + phy_blk, phy_blk ? phy_blk + ext_len :0 , ext_len, + fm_extent->fe_lun, flags); +} + +int filefrag_fiemap (int fd,int bs,int *num_extents) +{ +char buf[4096 ] ="" ; +struct fiemap *fiemap = (struct fiemap *)buf; +int count = (sizeof (buf) -sizeof (*fiemap)) / +sizeof (struct fiemap_extent ); +unsigned long long logical_blk =0 , last_blk =0 ; +unsigned long flags =0 ; +int lun_offset_tried =0 ; +int tot_extents =0 , previous_lun =0 ; +int last =0 , eof =0 ; +int i, rc; + +memset (fiemap,0 ,sizeof (struct fiemap )); + fiemap->fm_extent_count = count; + fiemap->fm_length = ~0ULL ; + +if (lun_offset) + flags |=FIEMAP_FLAG_LUN_OFFSET ; + +if (!verbose) + flags |=FIEMAP_FLAG_NUM_EXTENTS ; + +if (extent_format && verbose) +printf (" ext:\t logical: start..end physical: start..end: " +"length: lun: flags:\n" ); + +retry_wo_lun_offset: +do { + fiemap->fm_length = ~0ULL ; + fiemap->fm_flags = flags; + fiemap->fm_extent_count = count =1 ; + rc =ioctl (fd,EXT4_IOC_FIEMAP , (unsigned long ) fiemap); +if (rc == -EOPNOTSUPP && (flags &FIEMAP_FLAG_LUN_OFFSET )) { + flags &= ~FIEMAP_FLAG_LUN_OFFSET ; + goto retry_wo_lun_offset; + } +if (rc) +return rc; + +if (!verbose) { + *num_extents = fiemap->fm_extent_count; + goto out; + } + +if (fiemap->fm_flags &FILEFRAG_FIEMAP_INCOMPAT_UNSUPP ) { +static int fiemap_incompat_printed; + +if (fiemap_incompat_printed ==0 ) { +fprintf (stderr,"\nFIEMAP failed with " +"incompatible feature %x\n" , + fiemap->fm_flags & +FILEFRAG_FIEMAP_INCOMPAT_UNSUPP ); + fiemap_incompat_printed =1 ; + } +return -EOPNOTSUPP ; + } + + +/* If 0 extents are returned, then more ioctls are not needed */ +if (fiemap->fm_extent_count ==0 ) +break ; + +for (i =0 ; i < fiemap->fm_extent_count; i++) { +__u64 phy_blk, phy_start; +unsigned long ext_len; + + phy_blk = fiemap->fm_extents[i].fe_offset / bs; + ext_len = fiemap->fm_extents[i].fe_length / bs; + +if (previous_lun != fiemap->fm_extents[i].fe_lun) { +if (flags &FIEMAP_FLAG_LUN_OFFSET ) + logical_blk =0 ; + previous_lun = fiemap->fm_extents[i].fe_lun; + } + +if (extent_format) { +print_extent_info (&fiemap->fm_extents[i], + tot_extents, logical_blk, bs); + }else if (logical_blk && phy_blk != last_blk+1 ) { +printf ("Discontinuity: Block %llu is at %llu " +"(was %llu)\n" , logical_blk, phy_blk, + last_blk); + } + + logical_blk += ext_len; + last_blk = phy_blk + ext_len -1 ; +if (fiemap->fm_extents[i].fe_flags &FIEMAP_EXTENT_LAST ) + last =1 ; + tot_extents++; + } + +/* For LUN_OFFSET mappings, if EXTENT_LAST is not yet found then +* fm_start needs to be the same as it was for earlier ioctl. +* Else we ask for extents starting from where the last mapping +* ended. */ +if (flags &FIEMAP_FLAG_LUN_OFFSET ) + fiemap->fm_start =0 ; +else + fiemap->fm_start += fiemap->fm_length; + }while (last ==0 ); + + *num_extents = tot_extents; +out: +return 0 ; }#define EXT2_DIRECT 12
@@ -86,9 +244,11 @@ static void frag_report(const char *fileunsigned long block, last_block =0 , numblocks, i;long bpib;/* Blocks per indirect block */ long cylgroups; -int discont =0 , expected; +int num_extents =0 , expected;int is_ext2 =0 ;unsigned int flags; +unsigned long first_blk, last_blk; +int rc;if (statfs (filename, &fsinfo) <0 ) {perror ("statfs" );
@@ -113,6 +273,15 @@ static void frag_report(const char *fileprintf ("Filesystem type is: %lx\n" , (unsigned long ) fsinfo.f_type); } + +/* Check if filesystem is lustre */ +if (fsinfo.f_type ==LUSTRE_SUPER_MAGIC ) { + is_lustre =1 ; +/* For lustre fs we will always display in extent format */ + extent_format++; + lun_offset++; + } + cylgroups =div_ceil (fsinfo.f_blocks, fsinfo.f_bsize*8 );if (verbose) { printf("Filesystem cylinder groups is approximately %ld\n",
@@ -135,7 +304,8 @@ static void frag_report(const char *fileif (ioctl (fd,EXT3_IOC_GETFLAGS , &flags) <0 ) flags =0 ;if (flags &EXT4_EXTENTS_FL ) { -printf ("File is stored in extents format\n" ); +if (verbose) +printf ("File is stored in extents format\n" ); is_ext2 =0 ; }if (verbose)
@@ -143,37 +313,48 @@ static void frag_report(const char *file bpib = bs /4 ; numblocks = (fileinfo.st_size + (bs-1 )) / bs;if (verbose) { +int rc1, rc2; +printf ("File size of %s is %lld (%ld blocks)\n" , filename, (long long ) fileinfo.st_size, numblocks); -printf ("First block: %lu\nLast block: %lu\n" , -get_bmap (fd,0 ),get_bmap (fd, numblocks -1 )); +if (extent_format ==0 ) { + rc1 =get_bmap (fd,0 , &first_blk); + rc2 =get_bmap (fd, numblocks -1 , &last_blk); +if (rc1 ==0 && rc2 ==0 ) +printf ("First block: %lu\nLast block: %lu\n" , + first_blk, last_blk); + } } -for (i=0 ; i < numblocks; i++) { -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) % (bpib*bpib*bpib)) ==0 ) - last_block++; - } - block =get_bmap (fd, i); -if (block ==0 ) -continue ; -if (last_block && (block != last_block +1 ) ) { -if (verbose) -printf ("Discontinuity: Block %ld is at %lu (was %lu)\n" , - i, block, last_block); - discont++; +if (is_ext2 || (filefrag_fiemap (fd, bs, &num_extents) !=0 )) { +for (i =0 ; i < numblocks; i++) { +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) % + (bpib*bpib*bpib)) ==0 ) + last_block++; + } + rc =get_bmap (fd, i, &block); +if (block ==0 ) +continue ; +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; } - last_block = block; } -if (discont==0 ) +if (num_extents ==1 )printf ("%s: 1 extent found" , filename);else -printf ("%s: %d extents found" , filename, discont+1 ); +printf ("%s: %d extents found" , filename, num_extents); expected = (numblocks/((bs*8 )-(fsinfo.f_files/8 /cylgroups)-3 ))+1 ; -if (is_ext2 && expected != discont+1 ) +if (is_ext2 && expected != num_extents)printf (", perfection would be %d extent%s\n" , expected, (expected>1 ) ?"s" :"" );else
@@ -183,7 +364,7 @@ static void frag_report(const char *filestatic void usage (const char *progname) { -fprintf (stderr,"Usage: %s [-v] file ...\n" , progname); +fprintf (stderr,"Usage: %s [-vel] file ...\n" , progname);exit (1 ); }
@@ -192,11 +373,17 @@ int main(int argc, char**argv)char **cpp;int c; -while ((c =getopt (argc, argv,"v" )) !=EOF ) +while ((c =getopt (argc, argv,"vel" )) !=EOF )switch (c) {case 'v' : verbose++;break ; +case 'e' : + extent_format++; +break ; +case 'l' : + lun_offset++; +break ;default :usage (argv[0 ]);break ;
lib/ext2fs/fiemap.h new file +49 -0
49 lines not on this page · raw message