/*
 *
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   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
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * NAME
 *	fcntl18.c
 *
 * DESCRIPTION
 *	Test to check the error conditions in fcntl system call
 *
 * USAGE
 *	fcntl18
 *
 * HISTORY
 *	07/2001 Ported by Wayne Boyer
 *
 * RESTRICTIONS
 *	NONE
 */

#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include "test.h"
#include "usctest.h"

#define INVAL_FLAG	-1
#define INVAL_MIN	(-2147483647L-1L)

int fd;
char string[40] = "";

char *TCID = "fcntl18";
int TST_TOTAL = 1;
struct passwd *pass;

void setup(void);
void cleanup(void);
int fail;

int main(int ac, char **av)
{
	int retval;
	struct flock fl;
	int pid, status;

	const char *msg;

	if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) {
		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
	}

	setup();		/* global setup */

/* //block1: */
#ifndef UCLINUX
	/* Skip since uClinux does not implement memory protection */
	tst_resm(TINFO, "Enter block 1");
	fail = 0;
	if ((fd = open("temp.dat", O_CREAT | O_RDWR, 0777)) < 0) {	//mode must be specified when O_CREATE is in the flag
		tst_resm(TFAIL, "file opening error");
		fail = 1;
	}

	/* Error condition if address is bad */
	retval = fcntl(fd, F_GETLK, (struct flock *)INVAL_FLAG);
	if (errno == EFAULT) {
		tst_resm(TPASS, "Test F_GETLK: for errno EFAULT PASSED");
	} else {
		tst_resm(TFAIL, "Test F_GETLK: for errno EFAULT FAILED");
		fail = 1;
	}
	if (fail) {
		tst_resm(TINFO, "Block 1 FAILED");
	} else {
		tst_resm(TINFO, "Block 1 PASSED");
	}
	tst_resm(TINFO, "Exit block 1");
#else
	tst_resm(TINFO, "Skip block 1 on uClinux");
#endif

/* //block2: */
#ifndef UCLINUX
	/* Skip since uClinux does not implement memory protection */
	tst_resm(TINFO, "Enter block 2");
	fail = 0;
	/* Error condition if address is bad */
	retval = fcntl(fd, F_GETLK64, (struct flock *)INVAL_FLAG);
	if (errno == EFAULT) {
		tst_resm(TPASS, "Test F_GETLK64: for errno EFAULT PASSED");
	} else {
		tst_resm(TFAIL, "Test F_GETLK64: for errno EFAULT FAILED");
		fail = 1;
	}
	if (fail) {
		tst_resm(TINFO, "Block 2 FAILED");
	} else {
		tst_resm(TINFO, "Block 2 PASSED");
	}
	tst_resm(TINFO, "Exit block 2");
#else
	tst_resm(TINFO, "Skip block 2 on uClinux");
#endif

/* //block3: */
	tst_resm(TINFO, "Enter block 3");
	fail = 0;
	if ((pid = FORK_OR_VFORK()) == 0) {	/* child */
		fail = 0;
		pass = getpwnam("nobody");
		retval = setreuid(-1, pass->pw_uid);
		if (retval < 0) {
			tst_resm(TFAIL, "setreuid to user nobody failed, "
				 "errno: %d", errno);
			fail = 1;
		}

		/* Error condition: invalid cmd */
		retval = fcntl(fd, INVAL_FLAG, &fl);
		if (errno == EINVAL) {
			tst_resm(TPASS, "Test for errno EINVAL PASSED");
		} else {
			tst_resm(TFAIL, "Test for errno EINVAL FAILED, "
				 "got: %d", errno);
			fail = 1;
		}
		exit(fail);
	} else {		/* parent */
		waitpid(pid, &status, 0);
		if (WEXITSTATUS(status) != 0) {
			tst_resm(TFAIL, "child returned bad exit status");
			fail = 1;
		}
		if (fail) {
			tst_resm(TINFO, "Block 3 FAILED");
		} else {
			tst_resm(TINFO, "Block 3 PASSED");
		}
	}
	tst_resm(TINFO, "Exit block 3");

	cleanup();
	tst_exit();

}

/*
 * setup()
 *	performs all ONE TIME setup for this test
 */
void setup(void)
{

	tst_sig(FORK, DEF_HANDLER, cleanup);

	umask(0);

	TEST_PAUSE;

	tst_tmpdir();

	sprintf(string, "./fcntl18.%d.1", getpid());
	unlink(string);
}

/*
 * cleanup()
 *	performs all the ONE TIME cleanup for this test at completion or
 *	or premature exit.
 */
void cleanup(void)
{
	/*
	 * print timing status if that option was specified.
	 * print errno log if that option was specified
	 */
	close(fd);

	TEST_CLEANUP;

	tst_rmdir();

	unlink("temp.dat");

}
