#include #include #include #include #include #include #include #include #include #include #include static char junkbuf[128 * 1024]; #define TESTSIZ 7 struct item { off_t seekoff; off_t seekout; off_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, off_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, off_t seekoff, off_t seekout, off_t xfer) { char buf[128 * 1024], buf2[128 * 1024]; ssize_t insiz, outsiz; if (seekoff == seekout) { lseek(infd, 0, SEEK_SET); lseek(outfd, 0, SEEK_SET); xfer = 0; } else { lseek(infd, seekoff, SEEK_SET); lseek(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, off_t xfer) { size_t len; ssize_t ret; while (xfer > 0) { if (xfer > SIZE_T_MAX) len = SIZE_T_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; off_t seekoff, seekout, xfer; bool check_alloc; char cp; if (argc != 3) errx(1, "Usage: testcfr "); /* 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'; } 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); } lseek(infd, seekoff, SEEK_SET); write(infd, "XXXX", 4); lseek(infd, 256 * 1024, SEEK_CUR); write(infd, "YYYY", 4); lseek(infd, 512 * 1024, SEEK_CUR); write(infd, "ZZZZ", 4); lseek(infd, 0, SEEK_SET); lseek(outfd, 0, SEEK_SET); if (fstat(infd, &st) < 0) err(1, "can't fstat %s", argv[1]); check_alloc = false; if (st.st_size < SSIZE_MAX) check_alloc = true; if (i == 0) { /* Test invalid arguments. */ ret = copy_file_range(infd, NULL, outfd, NULL, 0, 0); if (ret != 0) err(1, "Copy with len == 0 didn't return 0"); ret = copy_file_range(infd, NULL, infd, NULL, 1, 0); if (ret >= 0) err(1, "Didn't fail for in/out fd same"); seekoff = seekout = OFF_MAX; ret = copy_file_range(infd, &seekoff, outfd, &seekout, 1, 0); if (ret > 0) err(1, "Should have found OFF_MAX plus " "1 invalid"); seekoff = seekout = OFF_MAX; ret = copy_file_range(infd, &seekoff, outfd, &seekout, SIZE_T_MAX, 0); if (ret > 0) err(1, "Should have found OFF_MAX plus " "SIZE_T_MAX invalid"); seekoff = seekout = st.st_size - 2; ret = copy_file_range(infd, &seekoff, outfd, &seekout, 3, 0); if (ret != 2) err(1, "Copy for 3 bytes should be 2 bytes"); seekoff = seekout = st.st_size - 2; ret = copy_file_range(infd, &seekoff, outfd, &seekout, 2, 0); if (ret != 2) err(1, "Copy of 2 bytes should work"); seekoff = seekout = st.st_size; ret = copy_file_range(infd, &seekoff, outfd, &seekout, SSIZE_MAX, 0); if (ret != 0) err(1, "Copy of zero bytes should work"); for (j = 0; j < TESTSIZ; j++) { lseek(infd, list[j].seekoff, SEEK_SET); lseek(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); /* Compare infd with outfd. */ comp_files(infd, outfd, list[j].seekoff, list[j].seekout, list[j].xfer); printf("compared ok\n"); fflush(stdout); } } 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 { lseek(outfd, seekoff, SEEK_SET); junk_write(outfd, xfer); } } printf("seekoff=%jd xfer=%jd\n", (intmax_t)seekoff, (intmax_t)xfer); fflush(stdout); lseek(infd, seekoff, SEEK_SET); lseek(outfd, seekoff, SEEK_SET); copy_range(infd, outfd, xfer); /* 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) warnx("allocation not same"); } } } }