// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) International Business Machines  Corp., 2002
 * Author: Cyril Hrubis
 *
 * Test Description:
 *  This test verifies that flock() cannot unlock a file locked by another
 *  task.
 *
 * Test Steps:
 *  Fork a child processes The parent flocks a file with LOCK_EX Child waits
 *  for that to happen, then checks to make sure it is locked.  Child then
 *  tries to unlock the file. If the unlock succeeds, the child attempts to
 *  lock the file with LOCK_EX. The test passes if the child is able to lock
 *  the file.
 */

#include <errno.h>
#include <stdlib.h>
#include <sys/file.h>

#include "tst_test.h"

static void childfunc(int fd)
{
	int fd2;
	TST_CHECKPOINT_WAIT(0);

	fd2 = SAFE_OPEN("testfile", O_RDWR);
	if (flock(fd2, LOCK_EX | LOCK_NB) != -1)
		tst_brk(TBROK, "CHILD: The file was not already locked");

	TEST(flock(fd, LOCK_UN));
	if (TST_RET == -1) {
		tst_res(TFAIL, "CHILD: Unable to unlock file locked by "
			"parent: %s", tst_strerrno(TST_ERR));
		exit(1);
	} else {
		tst_res(TPASS, "CHILD: File locked by parent unlocked");
	}

	TEST(flock(fd2, LOCK_EX | LOCK_NB));
	if (TST_RET == -1) {
		tst_res(TFAIL, "CHILD: Unable to unlock file after "
			"unlocking: %s", tst_strerrno(TST_ERR));
		exit(1);
	} else {
		tst_res(TPASS, "Locking after unlock passed");
	}

	SAFE_CLOSE(fd);
	SAFE_CLOSE(fd2);

	exit(0);
}

static void verify_flock(void)
{
	int fd1;
	pid_t pid;

	fd1 = SAFE_OPEN("testfile", O_RDWR);

	pid = SAFE_FORK();
	if (pid == 0)
		childfunc(fd1);

	TEST(flock(fd1, LOCK_EX | LOCK_NB));
	if (TST_RET != 0) {
		tst_res(TFAIL | TTERRNO,
			"Parent: Initial attempt to flock() failed");
	} else {
		tst_res(TPASS,
			"Parent: Initial attempt to flock() passed");
	}

	TST_CHECKPOINT_WAKE(0);

	tst_reap_children();

	SAFE_CLOSE(fd1);
}

static void setup(void)
{
	int fd;

	fd = SAFE_OPEN("testfile", O_CREAT | O_TRUNC | O_RDWR, 0666);
	SAFE_CLOSE(fd);
}

static struct tst_test test = {
	.test_all = verify_flock,
	.needs_checkpoints = 1,
	.forks_child = 1,
	.setup = setup,
};
