Skip to content

Instantly share code, notes, and snippets.

@gicmo
Created March 11, 2019 17:53
Show Gist options
  • Save gicmo/f6dba471f28cdcca7c5f1aa61f324f95 to your computer and use it in GitHub Desktop.
Save gicmo/f6dba471f28cdcca7c5f1aa61f324f95 to your computer and use it in GitHub Desktop.
Copy a file via copy_file_range and print the file mappings
/* gcc -Wall -o cfr cfr.c */
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/fiemap.h>
#ifndef MIN
#define MIN(a, b) ({__typeof(a) x = (a), y = (b); x < y ? x : y;})
#endif
static struct fiemap *
fiemap_for (int fd)
{
struct fiemap *fm;
uint32_t sz;
int r;
fm = malloc (sizeof (struct fiemap));
if (fm == NULL)
return NULL;
memset (fm, 0, sizeof (struct fiemap));
fm->fm_length = FIEMAP_MAX_OFFSET;
fm->fm_flags = FIEMAP_FLAG_SYNC; /* sync the file before mapping extents */
/* query for fm_mapped_extents */
r = ioctl (fd, FS_IOC_FIEMAP, fm);
if (r == -1)
{
free (fm);
return NULL;
}
sz = sizeof (struct fiemap_extent) * fm->fm_mapped_extents;
fm = realloc (fm, sizeof (struct fiemap) + sz);
if (fm == NULL)
return NULL;
fm->fm_flags = FIEMAP_FLAG_SYNC;
fm->fm_extent_count = fm->fm_mapped_extents;
fm->fm_mapped_extents = 0;
r = ioctl (fd, FS_IOC_FIEMAP, fm);
if (r == -1)
{
free (fm);
return NULL;
}
return fm;
}
static struct flags_name {
uint32_t flag;
const char *name;
} flags_names[] =
{
{FIEMAP_EXTENT_SHARED, "shared"},
{FIEMAP_EXTENT_MERGED, "merged"},
{FIEMAP_EXTENT_UNWRITTEN, "unwriten"},
{FIEMAP_EXTENT_DATA_TAIL, "data-tail"},
{FIEMAP_EXTENT_DATA_INLINE, "data-inline"},
{FIEMAP_EXTENT_NOT_ALIGNED, "not-aligned"},
{FIEMAP_EXTENT_DATA_ENCRYPTED, "data-encrypted"},
{FIEMAP_EXTENT_ENCODED, "encoded"},
{FIEMAP_EXTENT_DELALLOC, "delalloc"},
{FIEMAP_EXTENT_UNKNOWN, "unknown"},
{FIEMAP_EXTENT_LAST, "last"},
{0, NULL}
};
static void
print_fiemap (struct fiemap *fm, const char *fn)
{
printf (" %s \n", fn);
for (uint32_t i = 0; i < fm->fm_mapped_extents; i++)
{
struct fiemap_extent *e = &fm->fm_extents[i];
uint32_t flags = e->fe_flags;
int printed = 0;
printf ("[%.4d] %llx %llx %llx ", i,
e->fe_logical,
e->fe_physical,
e->fe_length);
for (struct flags_name *n = flags_names; n->name; n++)
{
if (flags & n->flag)
{
if (printed)
printf (", ");
else
printed = 1;
printf ("%s", n->name);
}
}
printf ("\n");
}
printf ("\n");
}
int
main (int argc, char **argv)
{
struct fiemap *fm = NULL;
struct stat st = {0, };
const char *src;
const char *dst;
loff_t os = 0, od = 0;
loff_t nleft;
ssize_t n;
int s, d, r;
if (argc < 3)
{
printf ("usage: %s: <source> <target>", argv[0]);
return EXIT_FAILURE;
}
src = argv[1];
dst = argv[2];
s = open (src, O_RDONLY);
if (s == -1)
{
perror ("could not open source");
return EXIT_FAILURE;
}
d = open (dst, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (d == -1)
{
perror ("could not open target");
return EXIT_FAILURE;
}
r = fstat (s, &st);
if (r == -1)
{
perror ("could not stat source");
return EXIT_FAILURE;
}
nleft = st.st_size;
printf ("blocksize: %ld\n", (long) st.st_blksize);
printf ("filesize: %lld\n", (long long) nleft);
do {
n = copy_file_range (s, &os,
d, &od,
MIN (st.st_blksize, nleft),
0 /* flags */);
if (n == -1)
{
perror ("copy_file_range");
return EXIT_FAILURE;
}
printf ("%zd ", n);
nleft -= n;
} while (nleft > 0);
printf ("\nnleft: %lld\n", (long long) nleft);
/* https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt */
fm = fiemap_for (s);
if (fm == NULL)
{
perror ("fiemap for source");
return EXIT_FAILURE;
}
print_fiemap (fm, src);
free (fm);
fm = fiemap_for (d);
if (fm == NULL)
{
perror ("fiemap for dest");
return EXIT_FAILURE;
}
print_fiemap (fm, dst);
free (fm);
(void) close (s);
(void) close (d);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment