[LTP] [PATCH v1] syscalls/copy_file_range: add/restructured tests
Christian Amann
camann@suse.com
Tue Apr 23 09:01:00 CEST 2019
copy_file_range01:
restructured testcase, removed unnecessary code,
improved readability and shortened output (only
failures get printed now).
copy_file_range02:
add testcases which test basic functions and error
handling of the syscall.
copy_file_range03:
add testcase to check if this operation updates
timestamps accordingly.
Signed-off-by: Christian Amann <camann@suse.com>
---
.../kernel/syscalls/copy_file_range/.gitignore | 2 +
.../syscalls/copy_file_range/copy_file_range.h | 31 +++
.../syscalls/copy_file_range/copy_file_range01.c | 229 +++++++++++----------
.../syscalls/copy_file_range/copy_file_range02.c | 123 +++++++++++
.../syscalls/copy_file_range/copy_file_range03.c | 78 +++++++
5 files changed, 355 insertions(+), 108 deletions(-)
create mode 100644 testcases/kernel/syscalls/copy_file_range/copy_file_range.h
create mode 100644 testcases/kernel/syscalls/copy_file_range/copy_file_range02.c
create mode 100644 testcases/kernel/syscalls/copy_file_range/copy_file_range03.c
diff --git a/testcases/kernel/syscalls/copy_file_range/.gitignore b/testcases/kernel/syscalls/copy_file_range/.gitignore
index 6807420ef..e9e35f60f 100644
--- a/testcases/kernel/syscalls/copy_file_range/.gitignore
+++ b/testcases/kernel/syscalls/copy_file_range/.gitignore
@@ -1 +1,3 @@
/copy_file_range01
+/copy_file_range02
+/copy_file_range03
diff --git a/testcases/kernel/syscalls/copy_file_range/copy_file_range.h b/testcases/kernel/syscalls/copy_file_range/copy_file_range.h
new file mode 100644
index 000000000..e95a6f042
--- /dev/null
+++ b/testcases/kernel/syscalls/copy_file_range/copy_file_range.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 SUSE LLC
+ * Author: Christian Amann <camann@suse.com>
+ */
+
+#ifndef __COPY_FILE_RANGE_H__
+#define __COPY_FILE_RANGE_H__
+
+#include "lapi/syscalls.h"
+
+#define SUCCESS 0
+
+#define MNTPOINT "mnt_point"
+#define FILE_SRC_PATH "file_src"
+#define FILE_DEST_PATH "file_dest"
+#define FILE_RDONL_PATH "file_rdonl"
+#define FILE_DIR_PATH "file_dir"
+#define FILE_MNTED_PATH MNTPOINT"/file_mnted"
+
+
+#define CONTENT "ABCDEFGHIJKLMNOPQRSTUVWXYZ12345\n"
+
+static int sys_copy_file_range(int fd_in, loff_t *off_in,
+ int fd_out, loff_t *off_out, size_t len, unsigned int flags)
+{
+ return tst_syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
+ off_out, len, flags);
+}
+
+#endif /* __COPY_FILE_RANGE_H__ */
diff --git a/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c b/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c
index 61a6042d9..840061815 100644
--- a/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c
+++ b/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Copyright (c) Linux Test Project, 2017
+ * Copyright (c) Linux Test Project, 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -12,55 +13,28 @@
* the GNU General Public License for more details.
*/
+/*
+ * This tests the fundamental functionalities of the copy_file_range
+ * syscall. It does so by copying the contents of one file into
+ * another using various different combinations for length and
+ * input/output offsets.
+ *
+ * After a copy is done this test checks if the contents of both files
+ * are equal at the given offsets. It is also inspected if the offsets
+ * of the file descriptors are advanced correctly.
+ */
+
#define _GNU_SOURCE
+
#include <stdio.h>
-#include <errno.h>
#include <stdlib.h>
#include "tst_test.h"
#include "tst_safe_stdio.h"
-#include "lapi/syscalls.h"
-
-#define TEST_FILE_1 "copy_file_range_ltp01.txt"
-#define TEST_FILE_2 "copy_file_range_ltp02.txt"
-#define STR "abcdefghijklmnopqrstuvwxyz12345\n"
-
-#define verbose 0
-
-static size_t *len_arr;
-static loff_t **off_arr;
-static int len_sz, off_sz;
+#include "copy_file_range.h"
-static void setup(void)
-{
- int i, fd, page_size;
-
- page_size = getpagesize();
-
- fd = SAFE_OPEN(TEST_FILE_1, O_RDWR | O_CREAT, 0664);
- /* Writing page_size * 4 of data into test file */
- for (i = 0; i < (int)(page_size * 4); i++)
- SAFE_WRITE(1, fd, STR, strlen(STR));
- SAFE_CLOSE(fd);
-
- len_sz = 4;
- len_arr = malloc(sizeof(size_t) * len_sz);
- len_arr[0] = 11;
- len_arr[1] = page_size - 1;
- len_arr[2] = page_size;
- len_arr[3] = page_size + 1;
-
- off_sz = 6;
- off_arr = malloc(sizeof(loff_t *) * off_sz);
- for (i = 1; i < off_sz; i++)
- off_arr[i] = malloc(sizeof(loff_t));
-
- off_arr[0] = NULL;
- *off_arr[1] = 0;
- *off_arr[2] = 17;
- *off_arr[3] = page_size - 1;
- *off_arr[4] = page_size;
- *off_arr[5] = page_size + 1;
-}
+static int page_size;
+static int errcount, numcopies;
+static int fd_in, fd_out;
static int check_file_content(const char *fname1, const char *fname2,
loff_t *off1, loff_t *off2, size_t len)
@@ -90,52 +64,37 @@ static int check_file_content(const char *fname1, const char *fname2,
}
static int check_file_offset(const char *m, int fd, loff_t len,
- loff_t *off_ori, loff_t *off_after)
+ loff_t *off_before, loff_t *off_after)
{
+ loff_t fd_off = SAFE_LSEEK(fd, 0, SEEK_CUR);
int ret = 0;
- if (off_ori) {
- /* FD should stay untouched, and off_in/out is updated */
- loff_t fd_off = SAFE_LSEEK(fd, 0, SEEK_CUR);
-
- if (fd_off == 0) {
- if (verbose)
- tst_res(TPASS, "%s FD offset unchanged", m);
- } else {
- tst_res(TFAIL, "%s FD offset changed: %ld",
+ if (off_before) {
+ /*
+ * copy_file_range offset is given:
+ * - fd offset should stay 0,
+ * - copy_file_range offset is updated
+ */
+ if (fd_off != 0) {
+ tst_res(TFAIL,
+ "%s fd offset unexpectedly changed: %ld",
m, (long)fd_off);
ret = 1;
- }
-
- if (!off_after) {
- tst_res(TFAIL, "%s offset is NULL", m);
- ret = 1;
- }
- if ((off_after) && (*off_ori + len == *off_after)) {
- if (verbose) {
- tst_res(TPASS, "%s offset advanced as"
- " expected: %ld", m, (long)*off_after);
- }
- } else {
+ } else if (*off_before + len != *off_after) {
tst_res(TFAIL, "%s offset unexpected value: %ld",
m, (long)*off_after);
ret = 1;
}
- } else {
- /* FD offset is advanced by len */
- loff_t fd_off = SAFE_LSEEK(fd, 0, SEEK_CUR);
-
- if (fd_off == len) {
- if (verbose) {
- tst_res(TPASS, "%s FD offset changed as"
- " expected: %ld", m, (long)fd_off);
- }
- } else {
- tst_res(TFAIL, "%s FD offset unexpected value: %ld",
+ }
+ /*
+ * no copy_file_range offset given:
+ * - fd offset advanced by length
+ */
+ else if (fd_off != len) {
+ tst_res(TFAIL, "%s fd offset unexpected value: %ld",
m, (long)fd_off);
- ret = 1;
- }
+ ret = 1;
}
return ret;
@@ -143,77 +102,131 @@ static int check_file_offset(const char *m, int fd, loff_t len,
static void test_one(size_t len, loff_t *off_in, loff_t *off_out)
{
+ int ret;
size_t to_copy = len;
- int fd_in, fd_out, ret;
- loff_t *off_in_ori = off_in;
- loff_t *off_out_ori = off_out;
- loff_t off_in_copy;
- loff_t off_out_copy;
+ loff_t off_in_value_copy, off_out_value_copy;
+ loff_t *off_new_in = &off_in_value_copy;
+ loff_t *off_new_out = &off_out_value_copy;
char str_off_in[32], str_off_out[32];
if (off_in) {
- off_in_copy = *off_in;
- off_in = &off_in_copy;
+ off_in_value_copy = *off_in;
sprintf(str_off_in, "%ld", (long)*off_in);
} else {
+ off_new_in = NULL;
strcpy(str_off_in, "NULL");
}
if (off_out) {
- off_out_copy = *off_out;
- off_out = &off_out_copy;
+ off_out_value_copy = *off_out;
sprintf(str_off_out, "%ld", (long)*off_out);
} else {
+ off_new_out = NULL;
strcpy(str_off_out, "NULL");
}
- fd_in = SAFE_OPEN(TEST_FILE_1, O_RDONLY);
- fd_out = SAFE_OPEN(TEST_FILE_2, O_CREAT | O_WRONLY | O_TRUNC, 0644);
-
/*
* copy_file_range() will return the number of bytes copied between
* files. This could be less than the length originally requested.
*/
do {
- TEST(tst_syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
- off_out, to_copy, 0));
+ TEST(sys_copy_file_range(fd_in, off_new_in, fd_out,
+ off_new_out, to_copy, 0));
if (TST_RET == -1) {
tst_res(TFAIL | TTERRNO, "copy_file_range() failed");
- SAFE_CLOSE(fd_in);
- SAFE_CLOSE(fd_out);
+ errcount++;
return;
}
to_copy -= TST_RET;
} while (to_copy > 0);
- ret = check_file_content(TEST_FILE_1, TEST_FILE_2,
- off_in_ori, off_out_ori, len);
- if (ret)
+ ret = check_file_content(FILE_SRC_PATH, FILE_DEST_PATH,
+ off_in, off_out, len);
+ if (ret) {
tst_res(TFAIL, "file contents do not match");
+ errcount++;
+ return;
+ }
- ret |= check_file_offset("(in)", fd_in, len, off_in_ori, off_in);
- ret |= check_file_offset("(out)", fd_out, len, off_out_ori, off_out);
+ ret |= check_file_offset("(in)", fd_in, len, off_in, off_new_in);
+ ret |= check_file_offset("(out)", fd_out, len, off_out, off_new_out);
- tst_res(ret == 0 ? TPASS : TFAIL, "off_in: %s, off_out: %s, len: %ld",
- str_off_in, str_off_out, (long)len);
+ if (ret != 0) {
+ tst_res(TFAIL, "off_in: %s, off_out: %s, len: %ld",
+ str_off_in, str_off_out, (long)len);
+ errcount++;
+ }
+}
- SAFE_CLOSE(fd_in);
- SAFE_CLOSE(fd_out);
+static void openFiles(void)
+{
+ fd_in = SAFE_OPEN(FILE_SRC_PATH, O_RDONLY);
+ fd_out = SAFE_OPEN(FILE_DEST_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+}
+
+static void closeFiles(void)
+{
+ if (fd_out > 0)
+ SAFE_CLOSE(fd_out);
+ if (fd_in > 0)
+ SAFE_CLOSE(fd_in);
}
static void copy_file_range_verify(void)
{
- int i, j, k;
+ errcount = numcopies = 0;
+ size_t len_arr[] = {11, page_size-1, page_size, page_size+1};
+ loff_t off_arr_values[] = {0, 17, page_size-1, page_size, page_size+1};
+
+ int num_offsets = ARRAY_SIZE(off_arr_values) + 1;
+ loff_t *off_arr[num_offsets];
- for (i = 0; i < len_sz; i++)
- for (j = 0; j < off_sz; j++)
- for (k = 0; k < off_sz; k++)
+ off_arr[0] = NULL;
+ for (int i = 1; i < num_offsets; i++)
+ off_arr[i] = &off_arr_values[i-1];
+
+ /* Test all possible cobinations of given lengths and offsets */
+ for (int i = 0; i < (int)ARRAY_SIZE(len_arr); i++)
+ for (int j = 0; j < num_offsets; j++)
+ for (int k = 0; k < num_offsets; k++) {
+ openFiles();
test_one(len_arr[i], off_arr[j], off_arr[k]);
+ closeFiles();
+ numcopies++;
+ }
+
+ if (errcount == 0)
+ tst_res(TPASS,
+ "copy_file_range completed all %d copy jobs successfully!",
+ numcopies);
+ else
+ tst_res(TINFO, "copy_file_range failed %d of %d copy jobs.",
+ errcount, numcopies);
+}
+
+static void setup(void)
+{
+ int fd;
+
+ page_size = getpagesize();
+
+ fd = SAFE_OPEN(FILE_SRC_PATH, O_RDWR | O_CREAT, 0664);
+ /* Writing page_size * 4 of data into test file */
+ for (int i = 0; i < (int)(page_size * 4); i++)
+ SAFE_WRITE(1, fd, CONTENT, strlen(CONTENT));
+ SAFE_CLOSE(fd);
+}
+
+static void cleanup(void)
+{
+ closeFiles();
}
static struct tst_test test = {
.setup = setup,
+ .cleanup = cleanup,
.needs_tmpdir = 1,
+ .min_kver = "4.5",
.test_all = copy_file_range_verify,
};
diff --git a/testcases/kernel/syscalls/copy_file_range/copy_file_range02.c b/testcases/kernel/syscalls/copy_file_range/copy_file_range02.c
new file mode 100644
index 000000000..43bb73b9c
--- /dev/null
+++ b/testcases/kernel/syscalls/copy_file_range/copy_file_range02.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 SUSE LLC
+ * Author: Christian Amann <camann@suse.com>
+ */
+
+/*
+ * Tests basic functions and error handling of the
+ * copy_file_range syscall
+ *
+ * 1) Copy contents of one file to another without
+ * offset or anything special -> SUCCESS
+ * 2) Copy contents with offset in source file but keep
+ * the length the same so that it exeeds EOF
+ * -> SUCCESS, copy bytes until EOF
+ * 3) Try to copy contents to file open as readonly
+ * -> EBADF
+ * 4) Try to copy contents to file on different mounted
+ * filesystem -> EXDEV
+ * 5) Try to copy contents to directory -> EISDIR
+ *
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include "tst_test.h"
+#include "copy_file_range.h"
+
+#define SUCCESS 0
+#define MNTPOINT "mnt_point"
+#define FILE_RDONL_PATH "file_rdonl"
+#define FILE_DIR_PATH "file_dir"
+#define FILE_MNTED_PATH MNTPOINT"/file_mnted"
+
+
+static int fd_src;
+static int fd_dest;
+static int fd_rdonly;
+static int fd_dir;
+static int fd_mnted;
+
+static struct tcase {
+ loff_t off_in;
+ loff_t off_out;
+ size_t length;
+ int *copy_to_fd;
+ int bytes_copied;
+ int exp_err;
+} tcases[] = {
+ {0, 0, strlen(CONTENT), &fd_dest, strlen(CONTENT), SUCCESS},
+ {0, 10, strlen(CONTENT), &fd_dest, strlen(CONTENT), SUCCESS},
+ {10, 0, strlen(CONTENT), &fd_dest, strlen(CONTENT)-10, SUCCESS},
+ {0, 0, strlen(CONTENT), &fd_rdonly, -1, EBADF},
+ {0, 0, strlen(CONTENT), &fd_mnted, -1, EXDEV},
+ {0, 0, strlen(CONTENT), &fd_dir, -1, EISDIR},
+};
+
+static void verify_copy_file_range(unsigned int n)
+{
+ struct tcase *tc = &tcases[n];
+
+ loff_t off_in = tc->off_in;
+ loff_t off_out = tc->off_out;
+
+ TEST(sys_copy_file_range(fd_src, &off_in,
+ *tc->copy_to_fd, &off_out, tc->length, 0));
+ if (tc->exp_err != TST_ERR) {
+ tst_res(TFAIL,
+ "copy_file_range failed with %s, expected %s",
+ tst_strerrno(TST_ERR), tst_strerrno(tc->exp_err));
+ return;
+ }
+
+ if (tc->bytes_copied != TST_RET) {
+ tst_res(TFAIL,
+ "Wrong number of bytes copied. Expected %d, copied %ld",
+ tc->bytes_copied, TST_RET);
+ return;
+ }
+
+ tst_res(TPASS, "copy_file_range ended with %s as expected",
+ tst_strerrno(tc->exp_err));
+}
+
+static void cleanup(void)
+{
+ if (fd_dest > 0)
+ SAFE_CLOSE(fd_dest);
+ if (fd_src > 0)
+ SAFE_CLOSE(fd_src);
+ if (fd_rdonly > 0)
+ SAFE_CLOSE(fd_rdonly);
+ if (fd_mnted > 0)
+ SAFE_CLOSE(fd_mnted);
+ if (fd_dir > 0)
+ SAFE_CLOSE(fd_dir);
+}
+
+static void setup(void)
+{
+ SAFE_MKDIR(FILE_DIR_PATH, 0777);
+
+ fd_dir = SAFE_OPEN(FILE_DIR_PATH, O_RDONLY | O_DIRECTORY);
+ fd_mnted = SAFE_OPEN(FILE_MNTED_PATH, O_RDWR | O_CREAT, 0664);
+ fd_rdonly = SAFE_OPEN(FILE_RDONL_PATH, O_RDONLY | O_CREAT, 0664);
+ fd_src = SAFE_OPEN(FILE_SRC_PATH, O_RDWR | O_CREAT, 0664);
+ fd_dest = SAFE_OPEN(FILE_DEST_PATH, O_RDWR | O_CREAT, 0664);
+
+ SAFE_WRITE(1, fd_src, CONTENT, strlen(CONTENT));
+}
+
+static struct tst_test test = {
+ .test = verify_copy_file_range,
+ .tcnt = ARRAY_SIZE(tcases),
+ .setup = setup,
+ .cleanup = cleanup,
+ .min_kver = "4.5",
+ .needs_root = 1,
+ .mount_device = 1,
+ .mntpoint = MNTPOINT,
+ .dev_fs_type = "ext4",
+};
diff --git a/testcases/kernel/syscalls/copy_file_range/copy_file_range03.c b/testcases/kernel/syscalls/copy_file_range/copy_file_range03.c
new file mode 100644
index 000000000..5a63193ce
--- /dev/null
+++ b/testcases/kernel/syscalls/copy_file_range/copy_file_range03.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 SUSE LLC
+ * Author: Christian Amann <camann@suse.com>
+ */
+
+/*
+ * Copies the contents of one file into another and
+ * checks if the timestamp gets updated in the process.
+ */
+
+#define _GNU_SOURCE
+
+#include "tst_test.h"
+#include "copy_file_range.h"
+
+static int fd_src;
+static int fd_dest;
+
+unsigned long getTimestamp(int fd)
+{
+ struct stat filestat;
+
+ fstat(fd, &filestat);
+ return filestat.st_mtime;
+}
+
+static void verify_copy_file_range_timestamp(void)
+{
+ loff_t offset;
+ unsigned long timestamp, updated_timestamp;
+
+ timestamp = getTimestamp(fd_dest);
+ usleep(1000000);
+
+ offset = 0;
+ TEST(sys_copy_file_range(fd_src, &offset,
+ fd_dest, 0, strlen(CONTENT), 0));
+ if (TST_RET == -1) {
+ tst_res(TFAIL, "copy_file_range unexpectedly failed!");
+ return;
+ }
+
+ updated_timestamp = getTimestamp(fd_dest);
+
+ if (timestamp == updated_timestamp) {
+ tst_res(TFAIL, "copy_file_range did not update timestamp!");
+ return;
+ }
+
+ tst_res(TPASS, "copy_file_range sucessfully updated the timestamp.");
+}
+
+static void cleanup(void)
+{
+ if (fd_dest > 0)
+ SAFE_CLOSE(fd_dest);
+ if (fd_src > 0)
+ SAFE_CLOSE(fd_src);
+}
+
+static void setup(void)
+{
+ fd_dest = SAFE_OPEN(FILE_DEST_PATH, O_RDWR | O_CREAT, 0664);
+ fd_src = SAFE_OPEN(FILE_SRC_PATH, O_RDWR | O_CREAT, 0664);
+ SAFE_WRITE(1, fd_src, CONTENT, strlen(CONTENT));
+ SAFE_CLOSE(fd_src);
+ fd_src = SAFE_OPEN(FILE_SRC_PATH, O_RDONLY);
+}
+
+
+static struct tst_test test = {
+ .test_all = verify_copy_file_range_timestamp,
+ .setup = setup,
+ .cleanup = cleanup,
+ .min_kver = "4.5",
+ .needs_tmpdir = 1,
+};
--
2.16.4
More information about the ltp
mailing list