[PATCH 0/2 v2] hook xfs to fiemap ioctl

Eric Sandeen
2008-10-25T13:26:39Z

Update & resend, including some checkpatch fixes, a bugfix, and
incorporating some of Christoph's and Dave's comments.

Also, here's a very hacky (don't laugh!) test app. I'll clean it
up at some point :)

-Eric

------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <ext2fs/ext2_types.h>

/*************************************************/
/* All this should come from fiemap.h eventually */
struct fiemap_extent {
__u64 fe_logical; /* logical offset in bytes for the start of
* the extent from the beginning of the file */
__u64 fe_physical; /* physical offset in bytes for the start
* of the extent from the beginning of the disk */
__u64 fe_length; /* length in bytes for this extent */
__u64 fe_reserved64[2];
__u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
__u32 fe_reserved[3];
};

struct fiemap {
__u64 fm_start; /* logical offset (inclusive) at
* which to start mapping (in) */
__u64 fm_length; /* logical length of mapping which
* userspace wants (in) */
__u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
__u32 fm_mapped_extents;/* number of extents that were mapped (out) */
__u32 fm_extent_count; /* size of fm_extents array (in) */
__u32 fm_reserved;
struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
};

#define FIEMAP_MAX_OFFSET (~0ULL)

#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */

#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)

#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */
#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */
#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending.
* Sets EXTENT_UNKNOWN. */
#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read
* while fs is unmounted */
#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs.
* Sets EXTENT_NO_BYPASS. */
#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be
* block aligned. */
#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata.
* Sets EXTENT_NOT_ALIGNED.*/
#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block.
* Sets EXTENT_NOT_ALIGNED.*/
#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but
* no data (i.e. zero). */
#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
* support extents. Result
* merged for efficiency. */

#define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */
#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap)
// #define FS_IOC_FIECOUNT _IOWR('f', 12, struct fiecount)

/* End of what should be coming from fiemap.h */
/**********************************************/

void usage(void)
{
printf("Usage: fiemap [-vrSCL] [-s start] [-l length] [-c buf count] [-m max] filename\n");
printf(" -v : verbose mode\n");
printf(" -r : raw output: print raw ioctl structure values\n");
printf(" -S : set FIEMAP_FLAG_SYNC to sync before mapping\n");
printf(" -C : set FIEMAP_FLAG_NUM_EXTENTS to only get extent count, not mapping\n");
printf(" -X : set FIEMAP_FLAG_XATTR to report xattr mapping\n");
printf(" -s start : start of mapping in bytes (default 0)\n");
printf(" -l length : length of mapping in bytes (default to end of file)\n");
printf(" -c count : count of extents in ioctl input structure (default 32)\n");
printf(" -m max : max nr of ioctls to call before exit (default 512)\n");
exit(EXIT_FAILURE);
}

#define EXABYTES(x) ((long long)(x) << 60)
#define PETABYTES(x) ((long long)(x) << 50)
#define TERABYTES(x) ((long long)(x) << 40)
#define GIGABYTES(x) ((long long)(x) << 30)
#define MEGABYTES(x) ((long long)(x) << 20)
#define KILOBYTES(x) ((long long)(x) << 10)

long long
cvtnum(char *s)
{
long long i;
char *sp;
int c;

i = strtoll(s, &sp, 0);
if (i == 0 && sp == s)
return -1LL;
if (*sp == '\0')
return i;
if (sp[1] != '\0')
return -1LL;

c = tolower(*sp);
switch (c) {
case 'k':
return KILOBYTES(i);
case 'm':
return MEGABYTES(i);
case 'g':
return GIGABYTES(i);
case 't':
return TERABYTES(i);
case 'p':
return PETABYTES(i);
case 'e':
return EXABYTES(i);
}

return -1LL;
}

void show_extents_table(struct fiemap *fiemap, int blocksize, int start_extent, int *is_last)
{
unsigned int i;

for (i = 0; i < fiemap->fm_mapped_extents; i++) {
__u64 logical = fiemap->fm_extents[i].fe_logical;
__u64 phys = fiemap->fm_extents[i].fe_physical;
__u64 length = fiemap->fm_extents[i].fe_length;
int flags = fiemap->fm_extents[i].fe_flags;
__u64 last_logical = 0;

if (i) {
last_logical =
fiemap->fm_extents[i-1].fe_logical +
fiemap->fm_extents[i-1].fe_length - 1;
}

printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu flags: 0x%03X tot: %llu\n",
i + start_extent,
logical / blocksize,
(logical + length - 1) / blocksize,
phys / blocksize,
(phys + length - 1) / blocksize,
flags,
(length / blocksize));

if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
*is_last = 1;
break; /* XXX should we? or should look for exents filled in past last? */
}
}

if (fiemap->fm_mapped_extents < fiemap->fm_extent_count)
*is_last = 1;
}

void show_extents_raw(struct fiemap *fiemap, int start_extent, int *is_last)
{
unsigned int i;

for (i = 0; i < fiemap->fm_mapped_extents; i++) {
printf("Extent %3u: logical: %10lld physical: %10lld length: %10lld flags 0x%03X\n",
i + start_extent,
fiemap->fm_extents[i].fe_logical,
fiemap->fm_extents[i].fe_physical,
fiemap->fm_extents[i].fe_length,
fiemap->fm_extents[i].fe_flags);

if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
*is_last = 1;
break;
/* XXX should we? or should look for extents wrongly filled in past last? */
}
}

if (fiemap->fm_mapped_extents < fiemap->fm_extent_count)
*is_last = 1;
}

int main(int argc, char**argv)
{
int blocksize = 0; /* filesystem blocksize */
uint count = 32; /* extent count */
int fd; /* file descriptor */
int last = 0; /* last extent found */
int xattr = 0; /* return xattr mapping */
int maxioctls = 512; /* max ioctls to try */
int opt;
int rc;
int raw = 0; /* raw output format */
int sync = 0; /* sync file before mapping */
int verbose = 0; /* verbose output */
char *fname; /* filename to map */
char *fiebuf; /* fiemap buffer / ioctl argument */
__u64 lstart = 0; /* logical input mapping start */
__u64 llength = ~0ULL;/* logical input mapping length */
uint start_ext = 0; /* starting extent nr. for this batch */
__u32 flags = 0;
struct fiemap *fiemap;

/* XXX er, can llength be ~0ULL if start > 0? */
while ((opt = getopt(argc, argv, "s:l:c:m:rSCLXv")) != -1) {
switch(opt) {
/* logical mapping offset */
case 's':
lstart = cvtnum(optarg);
break;
/* logical mapping length */
case 'l':
llength = cvtnum(optarg);
break;
/* count of extent buffers to send */
case 'c':
count = atoi(optarg);
break;
/* max nr. of ioctls to try (safety net) */
case 'm':
maxioctls = atoi(optarg);
break;
/* raw format output */
case 'r':
raw++;
break;
/* sync file before mapping */
case 'S':
sync++;
break;
/* return extents for xattrs */
case 'X':
xattr++;
break;
/* be verbose */
case 'v':
verbose++;
break;
default:
usage();
}
}

fname = argv[optind++];
if (!fname)
usage();

/* Set the flags for the header */
if (sync)
flags |= FIEMAP_FLAG_SYNC;
if (xattr)
flags |= FIEMAP_FLAG_XATTR;

/* The whole buffer, extent maps and all */
fiebuf = malloc(sizeof (struct fiemap) + (count * sizeof(struct fiemap_extent)));
if (!fiebuf) {
perror("Could not allocate fiemap buffers");
exit(1);
}

/* set up the header */
fiemap = (struct fiemap *)fiebuf;

fiemap->fm_start = lstart;
fiemap->fm_length = llength;
fiemap->fm_flags = flags;
fiemap->fm_extent_count = count;
fiemap->fm_mapped_extents = 0; /* output only */

fd = open(fname, O_RDONLY);
if (fd < 0) {
perror("Can't open file");
exit(1);
}

if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
perror("Can't get block size");
close(fd);
return;
}

do {
if (verbose)
printf("Input: start %llu length %llu flags 0x%X count %u\n",
fiemap->fm_start, fiemap->fm_length,
fiemap->fm_flags, fiemap->fm_extent_count);

rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
if (rc < 0) {
perror("FIEMAP ioctl failed");
printf("flags 0x%x\n", fiemap->fm_flags);
close(fd);
exit(1);
}
if (verbose)
printf("Output: start %llu length %llu flags 0x%X count %u mapped %u\n",
fiemap->fm_start, fiemap->fm_length,
fiemap->fm_flags, fiemap->fm_extent_count,
fiemap->fm_mapped_extents);

if (raw)
show_extents_raw(fiemap, start_ext, &last);
else
show_extents_table(fiemap, blocksize, start_ext, &last);

if (!last) {
int last_map = fiemap->fm_mapped_extents - 1;
__u64 foo; /* XXX Ummmm */

foo = fiemap->fm_extents[last_map].fe_logical +
fiemap->fm_extents[last_map].fe_length;
/* Set up the next call arguments */ /* move to top */
fiemap->fm_start = foo;
fiemap->fm_length = lstart + llength - foo;
fiemap->fm_extent_count = count;
}

/* keeps track of extent nrs. we've printed; rather, pass by value! */
start_ext += fiemap->fm_mapped_extents;

/* XXX need to stop if we've reached what we asked for */
maxioctls--;

} while (!last && maxioctls > 0);

close(fd);

return 0;
}