v2: Update & resend, including some checkpatch fixes, a bugfix, and
incorporating some of Christoph's and Dave's comments.
v3: Address more review comments, and split into 3 patches this time.
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;
}
|