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");
}
}
}
}