tcler's blog --- 其实我是一个程序员
Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious.

NFSv4.2 copy_file_range test

update 2020-01-19: latest code:

https://github.com/tcler/linux-network-filesystems/tree/master/utils/syscall_wrapper/copy_file_range.c
https://github.com/tcler/linux-network-filesystems/tree/master/utils/syscall_wrapper/copy_file_range.pl
https://github.com/tcler/linux-network-filesystems/tree/master/utils/syscall_wrapper/copy_file_range.py

#!/usr/bin/python

import sys
import os
import errno
from ctypes import *

libc = CDLL("libc.so.6", use_errno=True)
SYS_COPY_RANGE = 326 #syscall number __NR_copy_file_range from usr/include/asm/unistd_64.h

def copyfile(f_in, f_out):
	INTP = POINTER(c_uint64)
	size = os.stat(f_in.name).st_size
	copied = 0
	lsize = size

	while lsize != 0:
		pos = c_uint64(copied)
		addr = addressof(pos)
		offset = cast(addr, INTP)
		print("> Offset: %s lsize: %s; fdin %d fdout %d" % (copied, lsize, f_in.fileno(), f_out.fileno()))

		ret = libc.syscall(c_int(SYS_COPY_RANGE),
			     c_int(f_in.fileno()), POINTER(c_uint64)(), #offset,
			     c_int(f_out.fileno()), POINTER(c_uint64)(), #offset,
			     c_uint64(lsize), 0)

		if ret < 0:
			sys.stderr.write("[err] SYS_COPY_RANGE fail: errno %d %s" % (get_errno(), os.strerror(get_errno())))
			return ret
		if ret == 0:
			sys.stderr.write("[warn] SYS_COPY_RANGE return 0: errno %d %s" % (get_errno(), os.strerror(get_errno())))

		copied = copied + ret;
		lsize = lsize - ret;
		print("SYS_COPY_RANGE returns: %s" % ret)
		print("Total: %s" % copied)
	return ret


if len(sys.argv) != 3:
	print("Usage: " + sys.argv[0] + " src dst")
	sys.exit(1)

if not os.path.exists(sys.argv[1]):
	print("ERROR: " + sys.argv[1] + " does not exist :(")
	sys.exit(1)

f_in = open(sys.argv[1], "r")
f_out = open(sys.argv[2], "w")
copyfile(f_in, f_out)
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>

loff_t copy_file_range(int fd_in, loff_t *off_in, int fd_out,
		       loff_t *off_out, size_t len, unsigned int flags)
{
#ifndef __NR_copy_file_range
#define __NR_copy_file_range 326
#endif

	return syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
		       off_out, len, flags);
}

int main(int argc, char **argv)
{
	int fd_in, fd_out;
	struct stat stat;
	loff_t len, ret;
	char buf[2];

	if (argc != 3) {
		fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	fd_in = open(argv[1], O_RDONLY);
	if (fd_in == -1) {
		perror("open (argv[1])");
		exit(EXIT_FAILURE);
	}

	if (fstat(fd_in, &stat) == -1) {
		perror("fstat");
		exit(EXIT_FAILURE);
	}
	len = stat.st_size;

	fd_out = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, 0644);
	if (fd_out == -1) {
		perror("open (argv[2])");
		exit(EXIT_FAILURE);
	}

	do {
		ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
		if (ret == -1) {
			perror("copy_file_range");
			exit(EXIT_FAILURE);
		}

		len -= ret;
	} while (len > 0);

	close(fd_in);
	close(fd_out);
	exit(EXIT_SUCCESS);
}