#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#define __USE_GNU
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <linux/fiemap.h>

struct move_extent {
    int orig_fd;
    int donor_fd;
    uint64_t orig_start;
    uint64_t donor_start;
    uint64_t len;
    uint64_t moved_len;
};

#define EXT4_IOC_MOVE_EXT   _IOWR('f', 15, struct move_extent)

#define EXTENT_MAX_COUNT    512

int getextents(int fd)
{
    struct stat stbuf;
    struct fiemap *fmap;
    int extents;

    if((fmap = malloc(sizeof(*fmap) + EXTENT_MAX_COUNT *
                    sizeof(struct fiemap_extent))) == NULL) {
        perror("malloc");
        return -1;
    }

    fstat(fd, &stbuf);

    fmap->fm_start = 0;
    fmap->fm_length = stbuf.st_size;
    fmap->fm_flags = 0;
    fmap->fm_extent_count = EXTENT_MAX_COUNT;

    if(ioctl(fd, FS_IOC_FIEMAP, fmap) < 0) {
        perror("fiemap");
        return -1;
    }

    extents = fmap->fm_mapped_extents;
    free(fmap);

    return extents;
}

int main(int argc, char **argv)
{
    char *orig, *donor;
    struct move_extent me;
    int donorfd, origfd;
    int off, len;

    if(argc != 5) {
        printf("Usage: %s <orig> <donor> <offset> <len>\n", argv[0]);
        exit(1);
    }
    orig = argv[1];
    donor = argv[2];
    off = atoi(argv[3]);
    len = atoi(argv[4]);

    if((donorfd = open(donor, O_RDONLY | O_EXCL)) < 0) {
        perror("open donor");
        exit(1);
    }

    if((origfd = open(orig, O_RDONLY | O_EXCL)) < 0) {
        perror("open orig");
        exit(1);
    }
    printf("orig extents: %i\n", getextents(origfd));
    printf("donor extents: %i\n", getextents(donorfd));

    me.orig_fd = origfd;
    me.donor_fd = donorfd;
    me.orig_start = off;
    me.donor_start = off;
    me.len = len;
    me.moved_len = 0;

    if(ioctl(origfd, EXT4_IOC_MOVE_EXT, &me) < 0) {
        perror("ioctl");
        exit(1);
    }

    printf("moved len = %li\n", me.moved_len);

    close(donorfd);
    close(origfd);

    return 0;
}
