diff mbox series

[1/3] Add ioctl_ficlone01 test

Message ID 20240530-ioctl_ficlone-v1-1-fa96f07d0fca@suse.com
State Accepted
Headers show
Series Add ioctl_ficlone testing suite | expand

Commit Message

Andrea Cervesato May 30, 2024, 7:15 a.m. UTC
From: Andrea Cervesato <andrea.cervesato@suse.com>

This test verifies that ioctl() FICLONE feature clones file content
from one file to an another.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
 include/lapi/fs.h                                 |   4 +
 runtest/syscalls                                  |   2 +
 testcases/kernel/syscalls/ioctl/.gitignore        |   1 +
 testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c | 116 ++++++++++++++++++++++
 4 files changed, 123 insertions(+)

Comments

Cyril Hrubis May 30, 2024, 10:48 a.m. UTC | #1
Hi!
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
> + */
> +
> +/*\
> + * [Description]
> + *
> + * This test verifies that ioctl() FICLONE feature clones file content from
> + * one file to an another.
> + *
> + * [Algorithm]
> + *
> + * * populate source file
> + * * clone source content inside destination file
> + * * verify that source content has been cloned inside destination file
> + * * write a single byte inside destination file
> + * * verify that source content didn't change while destination did

This is very minor but I find dashes to be a better choice for lists
inside of C comments.

> + */
> +
> +#include "tst_test.h"
> +#include "lapi/fs.h"
> +
> +#define MNTPOINT "mnt"
> +#define SRCPATH MNTPOINT "/file0"
> +#define DSTPATH MNTPOINT "/file1"
> +
> +#define FILEDATA "qwerty"
> +#define FILESIZE sizeof(FILEDATA)
> +
> +static int src_fd = -1;
> +static int dst_fd = -1;
> +
> +static void run(void)
> +{
> +	char buff[FILESIZE];
> +	struct stat src_stat;
> +	struct stat dst_stat;
> +
> +	src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640);
> +	dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640);
> +
> +	tst_res(TINFO, "Writing data inside src file");
> +
> +	SAFE_WRITE(1, src_fd, FILEDATA, FILESIZE);
> +	SAFE_FSYNC(src_fd);
> +
> +	TST_EXP_PASS(ioctl(dst_fd, FICLONE, src_fd));
> +	if (TST_RET == -1)
> +		return;
> +
> +	SAFE_FSYNC(dst_fd);
> +
> +	tst_res(TINFO, "Verifing that data is cloned between files");
> +
> +	SAFE_FSTAT(src_fd, &src_stat);
> +	SAFE_FSTAT(dst_fd, &dst_stat);
> +
> +	TST_EXP_EXPR(src_stat.st_ino != dst_stat.st_ino,
> +		"inode is different. %lu != %lu",
> +		src_stat.st_ino,
> +		dst_stat.st_ino);
> +
> +	TST_EXP_EQ_LI(src_stat.st_size, dst_stat.st_size);
> +
> +	SAFE_READ(0, dst_fd, buff, FILESIZE);
> +
> +	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
> +		"dst file has the src file content (\"%s\" - %ld bytes)",
> +		buff,
> +		FILESIZE);
> +
> +	tst_res(TINFO, "Writing a byte inside dst file");
> +
> +	SAFE_WRITE(SAFE_WRITE_ALL, dst_fd, "a", 1);
> +	SAFE_FSYNC(dst_fd);
> +
> +	tst_res(TINFO, "Verifing that src file content didn't change");
> +
> +	SAFE_FSTAT(src_fd, &src_stat);
> +	SAFE_FSTAT(dst_fd, &dst_stat);
> +
> +	TST_EXP_EQ_LI(dst_stat.st_size, src_stat.st_size + 1);
> +
> +	SAFE_READ(0, src_fd, buff, FILESIZE);
> +
> +	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
> +		"src file content didn't change");

So you append to the file but then only read the initial part? That does
not sound right. I guess that easiest solution is to seek to the start
of the file or od pwrite() so that we overwrite the first byte rather
than appending.

Or at least check the return value from the read.

> +	SAFE_CLOSE(src_fd);
> +	SAFE_CLOSE(dst_fd);
> +	src_fd = -1;
> +	dst_fd = -1;

This is not needed, the SAFE_CLOSE() macro sets the fd to -1.

> +	remove(SRCPATH);
> +	remove(DSTPATH);

Please use SAFE_UNLINK() instead.

> +}
> +
> +static void cleanup(void)
> +{
> +	if (src_fd != -1)
> +		SAFE_CLOSE(src_fd);
> +
> +	if (dst_fd != -1)
> +		SAFE_CLOSE(dst_fd);
> +}
> +
> +static struct tst_test test = {
> +	.test_all = run,
> +	.cleanup = cleanup,
> +	.min_kver = "4.5",
> +	.needs_root = 1,
> +	.mount_device = 1,
> +	.mntpoint = MNTPOINT,
> +	.dev_fs_type = "btrfs",


I suppose that we need .use_filesystems or similar and convert the
dev_fs_type to an array so that we can run this test on xfs as well...

> +};
> 
> -- 
> 2.35.3
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp
Andrea Cervesato May 31, 2024, 7:15 a.m. UTC | #2
Hi!

On 5/30/24 12:48, Cyril Hrubis wrote:
> Hi!
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
>> + */
>> +
>> +/*\
>> + * [Description]
>> + *
>> + * This test verifies that ioctl() FICLONE feature clones file content from
>> + * one file to an another.
>> + *
>> + * [Algorithm]
>> + *
>> + * * populate source file
>> + * * clone source content inside destination file
>> + * * verify that source content has been cloned inside destination file
>> + * * write a single byte inside destination file
>> + * * verify that source content didn't change while destination did
> This is very minor but I find dashes to be a better choice for lists
> inside of C comments.
>
>> + */
>> +
>> +#include "tst_test.h"
>> +#include "lapi/fs.h"
>> +
>> +#define MNTPOINT "mnt"
>> +#define SRCPATH MNTPOINT "/file0"
>> +#define DSTPATH MNTPOINT "/file1"
>> +
>> +#define FILEDATA "qwerty"
>> +#define FILESIZE sizeof(FILEDATA)
>> +
>> +static int src_fd = -1;
>> +static int dst_fd = -1;
>> +
>> +static void run(void)
>> +{
>> +	char buff[FILESIZE];
>> +	struct stat src_stat;
>> +	struct stat dst_stat;
>> +
>> +	src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640);
>> +	dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640);
>> +
>> +	tst_res(TINFO, "Writing data inside src file");
>> +
>> +	SAFE_WRITE(1, src_fd, FILEDATA, FILESIZE);
>> +	SAFE_FSYNC(src_fd);
>> +
>> +	TST_EXP_PASS(ioctl(dst_fd, FICLONE, src_fd));
>> +	if (TST_RET == -1)
>> +		return;
>> +
>> +	SAFE_FSYNC(dst_fd);
>> +
>> +	tst_res(TINFO, "Verifing that data is cloned between files");
>> +
>> +	SAFE_FSTAT(src_fd, &src_stat);
>> +	SAFE_FSTAT(dst_fd, &dst_stat);
>> +
>> +	TST_EXP_EXPR(src_stat.st_ino != dst_stat.st_ino,
>> +		"inode is different. %lu != %lu",
>> +		src_stat.st_ino,
>> +		dst_stat.st_ino);
>> +
>> +	TST_EXP_EQ_LI(src_stat.st_size, dst_stat.st_size);
>> +
>> +	SAFE_READ(0, dst_fd, buff, FILESIZE);
>> +
>> +	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
>> +		"dst file has the src file content (\"%s\" - %ld bytes)",
>> +		buff,
>> +		FILESIZE);
>> +
>> +	tst_res(TINFO, "Writing a byte inside dst file");
>> +
>> +	SAFE_WRITE(SAFE_WRITE_ALL, dst_fd, "a", 1);
>> +	SAFE_FSYNC(dst_fd);
>> +
>> +	tst_res(TINFO, "Verifing that src file content didn't change");
>> +
>> +	SAFE_FSTAT(src_fd, &src_stat);
>> +	SAFE_FSTAT(dst_fd, &dst_stat);
>> +
>> +	TST_EXP_EQ_LI(dst_stat.st_size, src_stat.st_size + 1);
>> +
>> +	SAFE_READ(0, src_fd, buff, FILESIZE);
>> +
>> +	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
>> +		"src file content didn't change");
> So you append to the file but then only read the initial part? That does
> not sound right. I guess that easiest solution is to seek to the start
> of the file or od pwrite() so that we overwrite the first byte rather
> than appending.
>
> Or at least check the return value from the read.
>
>> +	SAFE_CLOSE(src_fd);
>> +	SAFE_CLOSE(dst_fd);
>> +	src_fd = -1;
>> +	dst_fd = -1;
> This is not needed, the SAFE_CLOSE() macro sets the fd to -1.
>
>> +	remove(SRCPATH);
>> +	remove(DSTPATH);
> Please use SAFE_UNLINK() instead.
>
>> +}
>> +
>> +static void cleanup(void)
>> +{
>> +	if (src_fd != -1)
>> +		SAFE_CLOSE(src_fd);
>> +
>> +	if (dst_fd != -1)
>> +		SAFE_CLOSE(dst_fd);
>> +}
>> +
>> +static struct tst_test test = {
>> +	.test_all = run,
>> +	.cleanup = cleanup,
>> +	.min_kver = "4.5",
>> +	.needs_root = 1,
>> +	.mount_device = 1,
>> +	.mntpoint = MNTPOINT,
>> +	.dev_fs_type = "btrfs",
>
> I suppose that we need .use_filesystems or similar and convert the
> dev_fs_type to an array so that we can run this test on xfs as well...
This might be tricky to implement, since we need to adapt .dev_fs_ops as 
well..
>> +};
>>
>> -- 
>> 2.35.3
>>
>>
>> -- 
>> Mailing list info: https://lists.linux.it/listinfo/ltp

Andrea
Cyril Hrubis May 31, 2024, 8:01 a.m. UTC | #3
Hi!
> > I suppose that we need .use_filesystems or similar and convert the
> > dev_fs_type to an array so that we can run this test on xfs as well...
> This might be tricky to implement, since we need to adapt .dev_fs_ops as 
> well..

I guess that we need to do a bigger surgery and put all the device
related flags into a structure, e.g.

struct tst_fs {
	const char *dev_fs_type;

	const char *const *mkfs_opts;
        const char *const *mkfs_extra_opts;

	unsigned int mnt_flags;
	const char *mnt_data;
};

struct tst_test {
	...
	struct tst_fs filesystems[];
	...
};

That way we can specify all the options per a filesystems. I can try to
do prepare the patchset next week.
Andrea Cervesato May 31, 2024, 8:27 a.m. UTC | #4
Ok thank you

On 5/31/24 10:01, Cyril Hrubis wrote:
> Hi!
>>> I suppose that we need .use_filesystems or similar and convert the
>>> dev_fs_type to an array so that we can run this test on xfs as well...
>> This might be tricky to implement, since we need to adapt .dev_fs_ops as
>> well..
> I guess that we need to do a bigger surgery and put all the device
> related flags into a structure, e.g.
>
> struct tst_fs {
> 	const char *dev_fs_type;
>
> 	const char *const *mkfs_opts;
>          const char *const *mkfs_extra_opts;
>
> 	unsigned int mnt_flags;
> 	const char *mnt_data;
> };
>
> struct tst_test {
> 	...
> 	struct tst_fs filesystems[];
> 	...
> };
>
> That way we can specify all the options per a filesystems. I can try to
> do prepare the patchset next week.
>
diff mbox series

Patch

diff --git a/include/lapi/fs.h b/include/lapi/fs.h
index 635979b02..cc3b7636c 100644
--- a/include/lapi/fs.h
+++ b/include/lapi/fs.h
@@ -48,6 +48,10 @@ 
 # define FS_VERITY_FL	   0x00100000 /* Verity protected inode */
 #endif
 
+#ifndef FICLONE
+# define FICLONE		_IOW(0x94, 9, int)
+#endif
+
 /*
  * Helper function to get MAX_LFS_FILESIZE.
  * Missing PAGE_SHIFT on some libc prevents defining MAX_LFS_FILESIZE.
diff --git a/runtest/syscalls b/runtest/syscalls
index cf06ee563..07e940f8c 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -591,6 +591,8 @@  ioctl_ns07 ioctl_ns07
 
 ioctl_sg01 ioctl_sg01
 
+ioctl_ficlone01 ioctl_ficlone01
+
 inotify_init1_01 inotify_init1_01
 inotify_init1_02 inotify_init1_02
 
diff --git a/testcases/kernel/syscalls/ioctl/.gitignore b/testcases/kernel/syscalls/ioctl/.gitignore
index 5fff7a61d..5404aa93f 100644
--- a/testcases/kernel/syscalls/ioctl/.gitignore
+++ b/testcases/kernel/syscalls/ioctl/.gitignore
@@ -22,3 +22,4 @@ 
 /ioctl_ns06
 /ioctl_ns07
 /ioctl_sg01
+/ioctl_ficlone01
diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c
new file mode 100644
index 000000000..29c1eb848
--- /dev/null
+++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c
@@ -0,0 +1,116 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
+ */
+
+/*\
+ * [Description]
+ *
+ * This test verifies that ioctl() FICLONE feature clones file content from
+ * one file to an another.
+ *
+ * [Algorithm]
+ *
+ * * populate source file
+ * * clone source content inside destination file
+ * * verify that source content has been cloned inside destination file
+ * * write a single byte inside destination file
+ * * verify that source content didn't change while destination did
+ */
+
+#include "tst_test.h"
+#include "lapi/fs.h"
+
+#define MNTPOINT "mnt"
+#define SRCPATH MNTPOINT "/file0"
+#define DSTPATH MNTPOINT "/file1"
+
+#define FILEDATA "qwerty"
+#define FILESIZE sizeof(FILEDATA)
+
+static int src_fd = -1;
+static int dst_fd = -1;
+
+static void run(void)
+{
+	char buff[FILESIZE];
+	struct stat src_stat;
+	struct stat dst_stat;
+
+	src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640);
+	dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640);
+
+	tst_res(TINFO, "Writing data inside src file");
+
+	SAFE_WRITE(1, src_fd, FILEDATA, FILESIZE);
+	SAFE_FSYNC(src_fd);
+
+	TST_EXP_PASS(ioctl(dst_fd, FICLONE, src_fd));
+	if (TST_RET == -1)
+		return;
+
+	SAFE_FSYNC(dst_fd);
+
+	tst_res(TINFO, "Verifing that data is cloned between files");
+
+	SAFE_FSTAT(src_fd, &src_stat);
+	SAFE_FSTAT(dst_fd, &dst_stat);
+
+	TST_EXP_EXPR(src_stat.st_ino != dst_stat.st_ino,
+		"inode is different. %lu != %lu",
+		src_stat.st_ino,
+		dst_stat.st_ino);
+
+	TST_EXP_EQ_LI(src_stat.st_size, dst_stat.st_size);
+
+	SAFE_READ(0, dst_fd, buff, FILESIZE);
+
+	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
+		"dst file has the src file content (\"%s\" - %ld bytes)",
+		buff,
+		FILESIZE);
+
+	tst_res(TINFO, "Writing a byte inside dst file");
+
+	SAFE_WRITE(SAFE_WRITE_ALL, dst_fd, "a", 1);
+	SAFE_FSYNC(dst_fd);
+
+	tst_res(TINFO, "Verifing that src file content didn't change");
+
+	SAFE_FSTAT(src_fd, &src_stat);
+	SAFE_FSTAT(dst_fd, &dst_stat);
+
+	TST_EXP_EQ_LI(dst_stat.st_size, src_stat.st_size + 1);
+
+	SAFE_READ(0, src_fd, buff, FILESIZE);
+
+	TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE),
+		"src file content didn't change");
+
+	SAFE_CLOSE(src_fd);
+	SAFE_CLOSE(dst_fd);
+	src_fd = -1;
+	dst_fd = -1;
+
+	remove(SRCPATH);
+	remove(DSTPATH);
+}
+
+static void cleanup(void)
+{
+	if (src_fd != -1)
+		SAFE_CLOSE(src_fd);
+
+	if (dst_fd != -1)
+		SAFE_CLOSE(dst_fd);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.cleanup = cleanup,
+	.min_kver = "4.5",
+	.needs_root = 1,
+	.mount_device = 1,
+	.mntpoint = MNTPOINT,
+	.dev_fs_type = "btrfs",
+};