fixed up version of testcfr.c

Rick Macklem <[email protected]>
Newsgroups gmane.ietf.nfsv4
Message-ID <YQBPR0101MB16522F3788B0286B8BD0E0A0DD930@YQBPR0101MB1652.CANPRD01.PROD.OUTLOOK.COM>
Attached is a version of testcfr.c that seems to work for a Linux
NFSv4.2 client when compiled with "-DDOSYNC". DOSYNC adds
some fsync(2) calls plus close()/open() after doing the copy_file_range().
{ I'm not sure that a client app should need to do these things, but that's
  up to the Linux community. }

At least for Fedora30, a copy_file_range() where the in_fd and out_fd are
the same file and the byte range overlaps returns success, but the data
seems bogus. However, this occurs when run on a local FS as well, so
I don't think it's a Linux NFSv4.2 client bug.

Good luck with it, if you try it, rick

_______________________________________________
nfsv4 mailing list
[email protected]
https://www.ietf.org/mailman/listinfo/nfsv4
testcfr.c (text/plain, 7.7 KB)
#define	_LARGEFILE64_SOURCE 1
#define	_GNU_SOURCE 1
#define	_FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <unistd.h>

static char junkbuf[128 * 1024];
static char bufa[150], bufb[150];
#define	TESTSIZ		7
struct item {
	loff_t	seekoff;
	loff_t	seekout;
	loff_t	xfer;
} list[TESTSIZ] = {
	{ 0,	0,	0 },
	{ 256 * 1024, 256 * 1024, 5 },
	{ 256 * 1024, 256 * 1024, 4 },
	{ 256 * 1024, 256 * 1024, 8 },
	{ 256 * 1024, 256 * 1024, 9 },
	{ 256 * 1024, 256 * 1024, 256 * 1024 + 8 },
	{ 0,	256 * 1024, 8 },
};

/*
 * Write xfer bytes into outfd.
 */
static void
junk_write(int outfd, loff_t xfer)
{
	size_t len;
	ssize_t outsiz;

	do {
		if (xfer > sizeof(junkbuf))
			len = sizeof(junkbuf);
		else
			len = xfer;
		outsiz = write(outfd, junkbuf, len);
		if (outsiz != len)
			err(1, "Can't write junk");
		xfer -= outsiz;
	} while (xfer > 0);
}

/* Compare the two files for same data. */
static void
comp_files(int infd, int outfd, loff_t seekoff, loff_t seekout, loff_t xfer)
{
	char buf[128 * 1024], buf2[128 * 1024];
	ssize_t insiz, outsiz;

	if (seekoff == seekout) {
		lseek64(infd, 0, SEEK_SET);
		lseek64(outfd, 0, SEEK_SET);
		xfer = 0;
	} else {
		lseek64(infd, seekoff, SEEK_SET);
		lseek64(outfd, seekout, SEEK_SET);
	}
	do {
		insiz = read(infd, buf, sizeof(buf));
		if (insiz < 0)
			err(1, "Can't read infd");
		outsiz = read(outfd, buf2, sizeof(buf2));
		if (outsiz < 0)
			err(1, "Can't read outfd");
		if (xfer == 0) {
			if (insiz < outsiz)
				errx(1, "Premature EOF on infd");
			if (outsiz < insiz)
				errx(1, "Premature EOF on outfd");
		} else {
			if (insiz > outsiz)
				insiz = outsiz;
			if (insiz > xfer)
				insiz = xfer;
		}
		if (insiz > 0 && memcmp(buf, buf2, insiz) != 0)
			errx(1, "File data not same");
		if (xfer > 0) {
			xfer -= insiz;
			if (xfer == 0)
				insiz = 0;
		}
	} while (insiz > 0);
}

/*
 * Copy a file range from infd to outfd.
 */
static void
copy_range(int infd, int outfd, loff_t xfer)
{
	size_t len;
	ssize_t ret;

	while (xfer > 0) {
		if (xfer > INT_MAX)
			len = INT_MAX;
		else
			len = xfer;
		ret = copy_file_range(infd, NULL, outfd, NULL, len, 0);
		if (ret <= 0)
			err(1, "Copy range failed!");
		xfer -= ret;
	}
}

int
main(int argc, char *argv[])
{
	int i, infd, j, outfd, ret;
	struct stat st, outst;
	loff_t seekoff, seekout, xfer;
	bool check_alloc;
	char cp;

	if (argc != 3)
		errx(1, "Usage: testcfr <infile> <outfile>");
	/* Fill in junk_buf with the alphabet over and over and over again. */
	cp = 'a';
	for (i = 0; i < sizeof(junkbuf); i++) {
		junkbuf[i] = cp++;
		if (cp > 'z')
			cp = 'a';
	}
	/* Fill in bufa with 50 'A's, 50 'B's and 50 'C's. */
	for (i = 0; i < sizeof(bufa); i++) {
		if (i < 50)
			bufa[i] = 'A';
		else if (i < 100)
			bufa[i] = 'B';
		else
			bufa[i] = 'C';
	}
	infd = open(argv[1], O_CREAT | O_RDWR, 0644);
	if (infd < 0)
		err(1, "can't open %s", argv[1]);
	outfd = open(argv[2], O_CREAT | O_RDWR, 0644);
	if (outfd < 0)
		err(1, "can't create %s", argv[2]);

	seekoff = 0;
	/*
	 * Create the input file as a sparse file and then copy file ranges
	 * of it to the output file and compare the two files.
	 */
	for (i = 0; i < 2; i++) {
		if (i > 0) {
			seekoff = 1024 * 1024 * 1024;
			seekoff *= 5;
			ftruncate(infd, 0);
			ftruncate(outfd, 0);
		}
		lseek64(infd, seekoff, SEEK_SET);
		write(infd, bufa, sizeof(bufa));
		lseek64(infd, 256 * 1024, SEEK_CUR);
		write(infd, "YYYY", 4);
		lseek64(infd, 512 * 1024, SEEK_CUR);
		write(infd, "ZZZZ", 4);
#ifdef DOSYNC
		fsync(infd);
#endif

		lseek64(infd, 0, SEEK_SET);
		lseek64(outfd, 0, SEEK_SET);
		if (fstat(infd, &st) < 0)
			err(1, "can't fstat %s", argv[1]);
		check_alloc = false;

		if (i == 0) {
			/* Test invalid arguments. */
			ret = copy_file_range(infd, NULL, outfd, NULL, 0, 0);
			printf("zero length copy ret=%d\n", ret);
			fflush(stdout);
			seekoff = 0;
			seekout = 100;
			ret = copy_file_range(infd, &seekoff, infd, &seekout,
			    50, 0);
			if (ret >= 0) {
				printf("Copy of non-overlapping byte range of"
				    " same file does not fail ret %d\n", ret);
				lseek64(infd, 0, SEEK_SET);
				read(infd, bufb, sizeof(bufb));
				lseek64(infd, 0, SEEK_SET);
				write(infd, bufa, sizeof(bufa));
				for (j = 0; j < sizeof(bufb); j++) {
					if ((j < 50 && bufb[j] != 'A') ||
					    (j >= 50 && j < 100 && bufb[j] !=
					     'B') ||
					    (j >= 100 && bufb[i] != 'A')) {
						printf("Non-overlap copy bad!!\n");
						break;
					}
				}
				fflush(stdout);
			}
			seekoff = 0;
			seekout = 50;
			ret = copy_file_range(infd, &seekoff, infd, &seekout,
			    100, 0);
			if (ret >= 0) {
				printf("Copy of overlapping byte range of"
				    " same file does not fail ret %d\n", ret);
				lseek64(infd, 0, SEEK_SET);
				read(infd, bufb, sizeof(bufb));
				lseek64(infd, 0, SEEK_SET);
				write(infd, bufa, sizeof(bufa));
				for (j = 0; j < sizeof(bufb); j++) {
					if ((j < 100 && bufb[j] != 'A') ||
					    (j >= 100 && bufb[j] != 'B')) {
						printf("Overlapping copy bad!!\n");
						break;
					}
				}
				fflush(stdout);
			}
			seekoff = seekout = LLONG_MAX;
			ret = copy_file_range(infd, &seekoff, outfd, &seekout,
			    1, 0);
			if (ret >= 0) {
				printf("LLONG_MAX + 1 returns %d\n", ret);
				fflush(stdout);
			}
			seekoff = seekout = LLONG_MAX;
			ret = copy_file_range(infd, &seekoff, outfd, &seekout,
			    INT_MAX, 0);
			if (ret >= 0) {
				printf("Should have found LLONG_MAX plus "
				    "INT_MAX invalid\n");
				fflush(stdout);
			}
			seekoff = seekout = st.st_size - 2;
			ret = copy_file_range(infd, &seekoff, outfd, &seekout,
			    3, 0);
			if (ret >= 0) {
				printf("Exceeded file size by 1 byte returns "
				    "%d instead of failing with EINVAL\n", ret);
				fflush(stdout);
			}
			seekoff = seekout = st.st_size - 2;
			ret = copy_file_range(infd, &seekoff, outfd, &seekout,
			    2, 0);
			if (ret != 2) {
				printf("Copy of 2 bytes should work\n");
				fflush(stdout);
			}
			for (j = 0; j < TESTSIZ; j++) {
				lseek64(infd, list[j].seekoff, SEEK_SET);
				lseek64(outfd, list[j].seekout, SEEK_SET);
				if (list[j].xfer == 0)
					list[j].xfer = st.st_size -
					    list[j].seekoff;
				copy_range(infd, outfd, list[j].xfer);
#ifdef DOSYNC
				close(outfd);
				outfd = open(argv[2], O_RDWR, 0644);
				if (outfd < 0)
					err(1, "can't create %s", argv[2]);
#endif
		
				/* Compare infd with outfd. */
				comp_files(infd, outfd, list[j].seekoff,
				    list[j].seekout, list[j].xfer);
			}
		}
		for (j = 0; j < 60; j++) {
			if ((j % 6) == 0) {
				seekoff = 0;
				xfer = st.st_size;
				ftruncate(outfd, 0);
			} else {
				seekoff = random();
				seekoff *= 4;
				if (seekoff <= 0)
					seekoff = 128 * 1024;
				seekoff %= st.st_size / 2;
				xfer = random();
				xfer *= 4;
				if (xfer <= 0 || xfer > st.st_size - seekoff)
					xfer = st.st_size - seekoff;

				if ((j % 2) == 0) {
					ftruncate(outfd, seekoff);
					xfer = st.st_size - seekoff;
				} else {
					lseek64(outfd, seekoff, SEEK_SET);
					junk_write(outfd, xfer);
#ifdef DOSYNC
					fsync(outfd);
#endif
				}
			}
			printf("seekoff=%jd xfer=%jd\n", (intmax_t)seekoff,
			    (intmax_t)xfer);
			fflush(stdout);
			lseek64(infd, seekoff, SEEK_SET);
			lseek64(outfd, seekoff, SEEK_SET);
			copy_range(infd, outfd, xfer);
#ifdef DOSYNC
			close(outfd);
			outfd = open(argv[2], O_RDWR, 0644);
			if (outfd < 0)
				err(1, "can't create %s", argv[2]);
#endif

			/* Compare infd with outfd. */
			comp_files(infd, outfd, seekoff, seekoff, 0);
			if (check_alloc && seekoff == 0) {
				if (fstat(outfd, &outst) < 0)
					err(1, "can't fstat %s", argv[2]);
				if (st.st_blocks != outst.st_blocks)
					printf("allocation not same");
			}
		}
	}
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.