Created
March 11, 2019 17:53
-
-
Save gicmo/f6dba471f28cdcca7c5f1aa61f324f95 to your computer and use it in GitHub Desktop.
Copy a file via copy_file_range and print the file mappings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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