[LTP] [PATCH] linkat01: Split into 2 cases and convert to new API

Cyril Hrubis chrubis@suse.cz
Wed Jul 17 13:43:12 CEST 2024


Hi!
>  #define TEST_DIR1 "olddir"
>  #define TEST_DIR2 "newdir"
>  #define TEST_DIR3 "deldir"
>  #define TEST_FILE1 "oldfile"
> -#define TEST_FILE2 "newfile"
> -#define TEST_FIFO "fifo"
>  
> -#define DPATHNAME_FMT	"%s/" TEST_DIR2 "/" TEST_FILE1
> -#define SPATHNAME_FMT	"%s/" TEST_DIR1 "/" TEST_FILE1
> +#define DPATHNAME_FMT "%s/" TEST_DIR2 "/" TEST_FILE1
> +#define SPATHNAME_FMT "%s/" TEST_DIR1 "/" TEST_FILE1
>  
>  static char dpathname[PATH_MAX];
>  static char spathname[PATH_MAX];
> -static int olddirfd, newdirfd = -1, cwd_fd = AT_FDCWD, stdinfd = 0, badfd =
> -    -1, deldirfd;
> -
> -struct test_struct {
> +static int olddirfd;
> +static int newdirfd = -1;
> +static int cwd_fd = AT_FDCWD;
> +static int stdinfd;
> +static int badfd = -1;
> +static int deldirfd;
> +
> +static struct tcase {
>  	int *oldfd;
>  	const char *oldfn;
>  	int *newfd;
> @@ -95,222 +46,122 @@ struct test_struct {
>  	int flags;
>  	const char *referencefn1;
>  	const char *referencefn2;

These two should better be named:

	const char *filename;
	const char *linkname;

So that it's clear where they point to.


Moreover the actual paths seems to be the same for all testcases, so I
suppose that we can just define two macros instead as:

#define FILENAME TEST_DIR1 "/" TEST_FILE1
#define LINKNAME TEST_DIR2 "/" TEST_FILE1

And remove these two members from the structure.

> -	int expected_errno;
> -} test_desc[] = {
> +} tcases[] = {
>  	/* 1. relative paths */
> -	{
> -	&olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 2. abs path at source */
> -	{
> -	&olddirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0},
> -	    /* 3. abs path at dst */
> -	{
> -	&olddirfd, TEST_FILE1, &newdirfd, dpathname, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 4. relative paths to cwd */
> -	{
> -	&cwd_fd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 5. relative paths to cwd */
> -	{
> -	&olddirfd, TEST_FILE1, &cwd_fd, TEST_DIR2 "/" TEST_FILE1, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 6. abs path at source */
> -	{
> -	&cwd_fd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0},
> -	    /* 7. abs path at dst */
> -	{
> -	&olddirfd, TEST_FILE1, &cwd_fd, dpathname, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 8. relative paths to invalid */
> -	{
> -	&stdinfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> -		    0, 0, ENOTDIR},
> -	    /* 9. relative paths to invalid */
> -	{
> -	&olddirfd, TEST_FILE1, &stdinfd, TEST_DIR2 "/" TEST_FILE1, 0,
> -		    0, 0, ENOTDIR},
> -	    /* 10. abs path at source */
> -	{
> -	&stdinfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0},
> -	    /* 11. abs path at dst */
> -	{
> -	&olddirfd, TEST_FILE1, &stdinfd, dpathname, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 12. relative paths to bad */
> -	{
> -	&badfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> -		    0, 0, EBADF},
> -	    /* 13. relative paths to bad */
> -	{
> -	&olddirfd, TEST_FILE1, &badfd, TEST_DIR2 "/" TEST_FILE1, 0,
> -		    0, 0, EBADF},
> -	    /* 14. abs path at source */
> -	{
> -	&badfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0},
> -	    /* 15. abs path at dst */
> -	{
> -	&olddirfd, TEST_FILE1, &badfd, dpathname, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 16. relative paths to deleted */
> -	{
> -	&deldirfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> -		    0, 0, ENOENT},
> -	    /* 17. relative paths to deleted */
> -	{
> -	&olddirfd, TEST_FILE1, &deldirfd, TEST_DIR2 "/" TEST_FILE1, 0,
> -		    0, 0, ENOENT},
> -	    /* 18. abs path at source */
> -	{
> -	&deldirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0, 0},
> -	    /* 19. abs path at dst */
> -	{
> -	&olddirfd, TEST_FILE1, &deldirfd, dpathname, 0,
> -		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
> -	    /* 20. x-device link */
> -	{
> -	&cwd_fd, "/proc/cpuinfo", &newdirfd, TEST_FILE1, 0, 0, 0, EXDEV},
> -	    /* 21. directory link */
> -	{
> -	&olddirfd, ".", &newdirfd, TEST_FILE1, 0, 0, 0, EPERM},
> -	    /* 22. invalid flag */
> -	{
> -	&olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 1, 0, 0, EINVAL},
> -	    /* 23. fifo link */
> -	    /* XXX (garrcoop): Removed because it hangs the overall test. Need to
> -	     * find a legitimate means to exercise this functionality, if in fact
> -	     * it's a valid testcase -- which it should be.
> -	     */
> -	    /* { &olddirfd, TEST_FIFO, &newdirfd, TEST_FILE1, 0,
> -	       TEST_DIR1"/"TEST_FIFO, TEST_DIR2"/"TEST_FILE1, 0 } */
> +	{&olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 2. absolution path at source */
> +	{&olddirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0},
> +	/* 3. absolution path at destination */
> +	{&olddirfd, TEST_FILE1, &newdirfd, dpathname, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 4. relative paths to cwd */
> +	{&cwd_fd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 5. cwd to relative paths */
> +	{&olddirfd, TEST_FILE1, &cwd_fd, TEST_DIR2 "/" TEST_FILE1, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 6. cwd to absolution paths */
> +	{&cwd_fd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0},
> +	/* 7. absolution paths to cwd */
> +	{&olddirfd, TEST_FILE1, &cwd_fd, dpathname, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 8. invalid directory to absolution paths */
> +	{&stdinfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0},
> +	/* 9. absolution paths to invalid directory */
> +	{&olddirfd, TEST_FILE1, &stdinfd, dpathname, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 10. bad fd to absolution paths */
> +	{&badfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0},
> +	/* 11. absolution paths to bad fd */
> +	{&olddirfd, TEST_FILE1, &badfd, dpathname, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
> +	/* 12. deleted to absolution paths */
> +	{&deldirfd, spathname, &newdirfd, TEST_FILE1, 0, 0, 0},
> +	/* 13. absolution paths to deleted */
> +	{&olddirfd, TEST_FILE1, &deldirfd, dpathname, 0,
> +		TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1},
>  };
>  
> -char *TCID = "linkat01";
> -int TST_TOTAL = sizeof(test_desc) / sizeof(*test_desc);
> -
> -static int mylinkat(int olddirfd, const char *oldfilename, int newdirfd,
> -		    const char *newfilename, int flags)
> +static void setup(void)
>  {
> -	return tst_syscall(__NR_linkat, olddirfd, oldfilename, newdirfd,
> -		       newfilename, flags);
> -}
> -
> -int main(int ac, char **av)
> -{
> -	int lc;
> -	int i;
> -
> -	tst_parse_opts(ac, av, NULL, NULL);
> -
> -	setup();
> -
> -	for (lc = 0; TEST_LOOPING(lc); lc++) {
> +	char *cwd;
> +	int fd;
>  
> -		tst_count = 0;
> +	cwd = get_current_dir_name();
> +	if (cwd == NULL) {
> +		tst_brk(TFAIL | TERRNO,
> +			"Failed to get current working directory.");
> +	}
>  
> -		for (i = 0; i < TST_TOTAL; i++) {
> -			setup_every_copy();
> -			mylinkat_test(&test_desc[i]);
> -		}
> +	SAFE_MKDIR(TEST_DIR1, 0700);
> +	SAFE_MKDIR(TEST_DIR3, 0700);
> +	olddirfd = SAFE_OPEN(TEST_DIR1, O_DIRECTORY);
> +	deldirfd = SAFE_OPEN(TEST_DIR3, O_DIRECTORY);
> +	fd = SAFE_OPEN(TEST_DIR1 "/" TEST_FILE1, O_CREAT | O_EXCL, 0600);
> +	SAFE_CLOSE(fd);
>  
> -	}
> +	snprintf(dpathname, sizeof(dpathname), DPATHNAME_FMT, cwd);
> +	snprintf(spathname, sizeof(spathname), SPATHNAME_FMT, cwd);
>  
> -	cleanup();
> -	tst_exit();
> +	free(cwd);
>  }
>  
> -static void setup_every_copy(void)
> +static void setup_every_case(void)
>  {
>  	close(newdirfd);
>  	unlink(dpathname);
>  	rmdir(TEST_DIR2);
>  
> -	SAFE_MKDIR(cleanup, TEST_DIR2, 0700);
> -	newdirfd = SAFE_OPEN(cleanup, TEST_DIR2, O_DIRECTORY);
> +	SAFE_MKDIR(TEST_DIR2, 0700);
> +	newdirfd = SAFE_OPEN(TEST_DIR2, O_DIRECTORY);

As far as I can tell the only thing that needs to be done on each
iteration is the unlink of dpathname. The rest could be moved into the
test setup.

>  }
>  
> -static void mylinkat_test(struct test_struct *desc)
> +static void verify_linkat(unsigned int i)
>  {
> -	int fd;
> -
> -	TEST(mylinkat
> -	     (*desc->oldfd, desc->oldfn, *desc->newfd, desc->newfn,
> -	      desc->flags));
> -
> -	if (TEST_ERRNO == desc->expected_errno) {
> -		if (TEST_RETURN == 0 && desc->referencefn1 != NULL) {
> -			int tnum = rand(), vnum = ~tnum;
> -			fd = SAFE_OPEN(cleanup, desc->referencefn1,
> -				       O_RDWR);
> -			SAFE_WRITE(cleanup, SAFE_WRITE_ALL, fd, &tnum,
> -				sizeof(tnum));
> -			SAFE_CLOSE(cleanup, fd);
> +	struct tcase *tc = &tcases[i];
>  
> -			fd = SAFE_OPEN(cleanup, desc->referencefn2,
> -				       O_RDONLY);
> -			SAFE_READ(cleanup, 1, fd, &vnum, sizeof(vnum));
> -			SAFE_CLOSE(cleanup, fd);
> +	int fd;
>  
> -			if (tnum == vnum)
> -				tst_resm(TPASS,
> -					 "linkat is functionality correct");
> -			else {
> -				tst_resm(TFAIL,
> -					 "The link file's content isn't "
> -					 "as same as the original file's "
> -					 "although linkat returned 0");
> +	setup_every_case();
> +
> +	if (tc->referencefn1 != NULL) {
> +		TST_EXP_PASS_SILENT(linkat(*tc->oldfd, tc->oldfn,
> +			*tc->newfd, tc->newfn, tc->flags));
> +		if (TST_PASS) {
> +			int tnum = rand();
> +			int vnum = ~tnum;
> +
> +			fd = SAFE_OPEN(tc->referencefn1, O_RDWR);
> +			SAFE_WRITE(SAFE_WRITE_ALL, fd, &tnum, sizeof(tnum));
> +			SAFE_CLOSE(fd);
> +
> +			fd = SAFE_OPEN(tc->referencefn2, O_RDONLY);
> +			SAFE_READ(1, fd, &vnum, sizeof(vnum));
> +			SAFE_CLOSE(fd);
> +
> +			if (tnum == vnum) {
> +				tst_res(TPASS,
> +					"linkat is functionality correct");
> +			} else {
> +				tst_res(TFAIL,
> +					"The link file's content isn't "
> +					"as same as the original file's "
> +					"although linkat returned 0");

Can we be shorter here and more to the point?

Something as:

	tst_res(TFAIL, "Read wrong file content from the hard link.");

>  			}

Can we put this check that the file content is reacheable from the link
as well to a separat function?

>  		} else {
> -			if (TEST_RETURN == 0)
> -				tst_resm(TPASS,
> -					 "linkat succeeded as expected");
> -			else
> -				tst_resm(TPASS | TTERRNO,
> -					 "linkat failed as expected");
> +			tst_res(TFAIL, "linkat failed");

If we are priting PASS/FAIL manually it would make more sense to use
TST_EXP_PASS() instead of the silent variant.

>  		}
>  	} else {
> -		if (TEST_RETURN == 0)
> -			tst_resm(TFAIL, "linkat succeeded unexpectedly");
> -		else
> -			tst_resm(TFAIL | TTERRNO,
> -				 "linkat failed unexpectedly; expected %d - %s",
> -				 desc->expected_errno,
> -				 strerror(desc->expected_errno));
> -	}
> -}
> -
> -void setup(void)
> -{
> -	char *cwd;
> -	int fd;
> -
> -	tst_sig(NOFORK, DEF_HANDLER, cleanup);
> -
> -	tst_tmpdir();
> -
> -	cwd = get_current_dir_name();
> -	if (cwd == NULL) {
> -		tst_brkm(TFAIL | TERRNO, cleanup,
> -			 "Failed to get current working directory");
> +		TST_EXP_PASS(linkat(*tc->oldfd, tc->oldfn,
> +			*tc->newfd, tc->newfn, tc->flags),
> +			"linkat");
>  	}
> -
> -	SAFE_MKDIR(cleanup, TEST_DIR1, 0700);
> -	SAFE_MKDIR(cleanup, TEST_DIR3, 0700);
> -	olddirfd = SAFE_OPEN(cleanup, TEST_DIR1, O_DIRECTORY);
> -	deldirfd = SAFE_OPEN(cleanup, TEST_DIR3, O_DIRECTORY);
> -	SAFE_RMDIR(cleanup, TEST_DIR3);
> -	fd = SAFE_OPEN(cleanup, TEST_DIR1 "/" TEST_FILE1, O_CREAT | O_EXCL, 0600);
> -	SAFE_CLOSE(cleanup, fd);
> -	SAFE_MKFIFO(cleanup, TEST_DIR1 "/" TEST_FIFO, 0600);
> -
> -	snprintf(dpathname, sizeof(dpathname), DPATHNAME_FMT, cwd);
> -	snprintf(spathname, sizeof(spathname), SPATHNAME_FMT, cwd);
> -
> -	free(cwd);
>  }
>  
> -static void cleanup(void)
> -{
> -	tst_rmdir();
> -}
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.setup = setup,
> +	.test = verify_linkat,
> +	.needs_tmpdir = 1,
> +};
> diff --git a/testcases/kernel/syscalls/linkat/linkat03.c b/testcases/kernel/syscalls/linkat/linkat03.c
> new file mode 100644
> index 000000000..4ee25106e
> --- /dev/null
> +++ b/testcases/kernel/syscalls/linkat/linkat03.c
> @@ -0,0 +1,120 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) International Business Machines  Corp., 2006
> + * Copyright (c) Linux Test Project, 2006-2024
> + */
> +
> +/*\
> + * [Description]
> + *
> + * Check the basic function of linkat that returns error.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include "tst_test.h"
> +#include "tst_safe_macros.h"
> +#include "lapi/syscalls.h"
> +#include "lapi/fcntl.h"
> +
> +#define TEST_DIR1 "olddir"
> +#define TEST_DIR2 "newdir"
> +#define TEST_DIR3 "deldir"
> +#define TEST_FILE1 "oldfile"
> +
> +static int olddirfd;
> +static int newdirfd = -1;
> +static int cwd_fd = AT_FDCWD;
> +static int stdinfd;
> +static int badfd = -1;
> +static int deldirfd;
> +
> +static struct tcase {
> +	int *oldfd;
> +	const char *oldfn;
> +	int *newfd;
> +	const char *newfn;
> +	int flags;
> +	const char *referencefn1;
> +	const char *referencefn2;

These two pointers are not used for anything.

> +	int expected_errno;
> +} tcases[] = {
> +	/* 1. invalid directory to relative paths */
> +	{&stdinfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> +		0, 0, ENOTDIR},
> +	/* 2. relative paths to invalid directory */
> +	{&olddirfd, TEST_FILE1, &stdinfd, TEST_DIR2 "/" TEST_FILE1, 0,
> +		0, 0, ENOTDIR},
> +	/* 3. bad fd to relative paths */
> +	{&badfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> +		0, 0, EBADF},
> +	/* 4. relative paths to bad fd */
> +	{&olddirfd, TEST_FILE1, &badfd, TEST_DIR2 "/" TEST_FILE1, 0,
> +		0, 0, EBADF},
> +	/* 5. deleted to relative paths */
> +	{&deldirfd, TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1, 0,
> +		0, 0, ENOENT},
> +	/* 6. relative paths to deleted */
> +	{&olddirfd, TEST_FILE1, &deldirfd, TEST_DIR2 "/" TEST_FILE1, 0,
> +		0, 0, ENOENT},
> +	/* 7. x-device link */
> +	{&cwd_fd, "/proc/cpuinfo", &newdirfd, TEST_FILE1, 0, 0, 0, EXDEV},
> +	/* 8. directory link */
> +	{&olddirfd, ".", &newdirfd, TEST_FILE1, 0, 0, 0, EPERM},
> +	/* 9. invalid flag */
> +	{&olddirfd, TEST_FILE1, &newdirfd, TEST_FILE1, 1, 0, 0, EINVAL},
> +};
> +
> +static void setup(void)
> +{
> +	char *cwd;
> +	int fd;
> +
> +	cwd = get_current_dir_name();
> +	if (cwd == NULL) {
> +		tst_brk(TFAIL | TERRNO,
> +			"Failed to get current working directory.");
> +	}
> +
> +	SAFE_MKDIR(TEST_DIR1, 0700);
> +	SAFE_MKDIR(TEST_DIR3, 0700);
> +	olddirfd = SAFE_OPEN(TEST_DIR1, O_DIRECTORY);
> +	deldirfd = SAFE_OPEN(TEST_DIR3, O_DIRECTORY);
> +	SAFE_RMDIR(TEST_DIR3);
> +	fd = SAFE_OPEN(TEST_DIR1 "/" TEST_FILE1, O_CREAT | O_EXCL, 0600);
> +	SAFE_CLOSE(fd);
> +
> +	free(cwd);

The cwd does not seem to be used at all.

> +}
> +
> +static void setup_every_case(void)
> +{
> +	close(newdirfd);
> +	rmdir(TEST_DIR2);
> +
> +	SAFE_MKDIR(TEST_DIR2, 0700);
> +	newdirfd = SAFE_OPEN(TEST_DIR2, O_DIRECTORY);

As far as I can tell this can be done once in the test setup()

> +}
> +
> +static void verify_linkat(unsigned int i)
> +{
> +	struct tcase *tc = &tcases[i];
> +
> +	setup_every_case();
> +
> +	TST_EXP_FAIL(linkat(*tc->oldfd, tc->oldfn, *tc->newfd,
> +		tc->newfn, tc->flags),
> +		tc->expected_errno,
> +		"linkat failed as expected");
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.setup = setup,
> +	.test = verify_linkat,
> +	.needs_tmpdir = 1,
> +};
> -- 
> 2.39.3
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list