/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 * 
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 * 
 * http://www.sgi.com 
 * 
 * For further information regarding this notice, see: 
 * 
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 */
/* $Id$ */
/**********************************************************
* 
*    OS Test - Silicon Graphics, Inc.
* 
*    TEST IDENTIFIER	: symlink01 (symlink)
* 
*    TEST TITLE		: Make a Symbolic Link to a File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 5
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: readlink01 (readlink)
* 
*    TEST TITLE		: Reads Value of a Symbolic Link
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 4
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: stat04  (stat)
* 
*    TEST TITLE		: Gets File Status Indirectly From a Symbolic Link 
*                         File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: lstat01 (lstat)
* 
*    TEST TITLE		: Get file Status About a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: mkdir05 (mkdir)
* 
*    TEST TITLE		: Fail When Making a Directory File Indirectly From
*                         a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 1
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: rmdir03 (rmdir)
* 
*    TEST TITLE		: Fail When Removing a Directory File Indirectly 
*                         From a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 1
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: chdir01 (chdir)
* 
*    TEST TITLE		: Changes Current Working DIrectory Location 
*                         Indirectly From a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: link01 (link)
* 
*    TEST TITLE		: Creates a Link To a File Indirectly From a 
*                         Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: unlink01 (unlink)
* 
*    TEST TITLE		: Removes a Link To a File And Not Any Object File
*                         Which Maybe Pointed At
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 1
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: chmod01 (chmod)
* 
*    TEST TITLE		: Change Object File Permissions Indirectly From a
*                         Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: utime01 (utime)
* 
*    TEST TITLE		: Set File Access And Modify Object File Times
*                         Indirectly From a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: rename01 (rename)
* 
*    TEST TITLE		: Rename a Symbolic Link File And Not Any Object
*                         File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 3
* 
*    WALL CLOCK TIME	: 3
*
*    TEST IDENTIFIER	: open01 (open)
* 
*    TEST TITLE		: Create/Open a File For Reading Or Writing
*                         Indirectly From a Symbolic Link File
* 
*    PARENT DOCUMENT	: symtds01
* 
*    TEST CASE TOTAL	: 5
* 
*    WALL CLOCK TIME	: 3
*
*    EXECUTED BY	: whom ever
* 
*    CPU TYPES		: ALL
* 
*    AUTHOR		: David Fenner
* 
*    CO-PILOT		: Jon Hendrickson
* 
*    DATE STARTED	: 07/25/90
* 
*    INITIAL RELEASE	: UNICOS 6.0
* 
*    TEST CASES
* 
*       For symlink
*
*         1. Create symbolic link with abnormal object name path
*         2. Create symbolic link with normal object name path
*         3. Create symbolic link with path to an existing object file
*         4. Receive EEXIST error when creating an already existing symbolic link file.
*         5. Receive ENAMETOOLONG error when creating symbolic link which exceeds PATH_MAX in length
* 
*       For readlink
*
*         1. Read a symbolic link file which points at no object file
*         2. Read a symbolic link file which points at an object file
*         3. Receive ENAMETOOLONG error when reading symbolic link which exceeds PATH_MAX in length
*         4. Receive an EINVAL error when reading a file which is not a symbolic
*            link file.
* 
*       For stat
* 
*         1. Get object file status through symbolic link file
*         2. Receive ENOENT error when accessing non-existent object file through symbolic link file
*         3. Receive ELOOP error when nesting of symbolic links exceed maximum
*
*       For lstat
* 
*         1. Get symbolic link file status when pointing at no object file
*         2. Get symbolic link file status when pointing at an object file
*         3. Get object file status when argument is not a symbolic link
*            file.
*
*       For mkdir
*
*         1. Receive EEXIST error when creating a directory through a symbolic link file
* 
*       For rmdir
*
*         1. Receive ENOTDIR error when removing an existing directory through a symbolic link file
* 
*       For chdir
*
*         1. Change current working directory through a symbolic link file
*         2. Receive ENOENT error when accessing non-existent directory through symbolic link file
*         3. Receive ELOOP error when nesting of symbolic links exceed maximum
* 
*       For link
* 
*         1. Link an object file to a new file through symbolic link file
*         2. Receive ENOENT error when accessing non-existent object file through symbolic link file
*         3. Receive ELOOP error when nesting of symbolic links exceed maximum
*
*       For unlink
*
*         1. Delete a symbolic link file and not the object file which it points at
* 
*       For chmod
* 
*         1. Change file permissions of object file through a symbolic link file
*         2. Receive ENOENT error when accessing non-existent directory through symbolic link file
*         3. Receive ELOOP error when nesting of symbolic links exceed maximum
* 
*       For utime
*
*         1. Change inode times of object file through a symbolic link file
*         2. Receive ENOENT error when accessing non-existent directory through symbolic link file
*         3. Receive ELOOP error when nesting of symbolic links exceed maximum
* 
*       For rename
*
*         1. Rename a symbolic link file which points at no object file
*         2. Rename a symbolic link file which points at an object file without any object file alterations.
*         3. Receive EXDEV when trying to rename a symbolic link file to an address outside of current file system
*
*       For open
*
*         1. Create an object file through a symbolic link file
*         2. Open an object file through a symbolic link file
*         3. Receive EEXIST error when exclusively creating an object file through a symbolic link file
*         4. Receive ENOENT error when accessing non-existent object file through symbolic link file
*         5. Receive ELOOP error when nesting of symbolic links exceed maximum
*
*    ENVIRONMENTAL NEEDS
*	None
* 
*    DETAILED DESCRIPTION
*
*       Self-documenting code so see below
*
*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/

#include <signal.h>
#include <string.h>
#include <fcntl.h>             /* open(2) system call */
#include <errno.h>
#include <sys/types.h>
#include <utime.h>             /* utime(2) system call */
#include <sys/param.h>
#include <sys/stat.h>          /* stat(2) and lstat(2) system calls */

#include "test.h"
#include "usctest.h"

void setup();
void cleanup(void) __attribute__((noreturn));
void help();
void delete_files();
void do_EEXIST();
void do_ENOENT();
void do_ELOOP();
void do_ENOTDIR();
void do_EXDEV();
void do_ENAMETOOLONG();
void do_EINVAL();
void do_readlink();
void do_stat();
void do_chdir();
void do_link();
void do_unlink();
void do_chmod();
void do_utime();
void do_rename();
void do_open();

#define S_FILE "symbolic"    /* Name of symbolic link file */
#define O_FILE "object"      /* Name of object file */
#define A_S_FILE "asymbolic" /* Another name for a symbolic link file */
#define Y_A_S_FILE "/NiCkEr" /* Yet another symbolic link file */
#define BIG_STRING "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"

#define DEFAULT_TCID  "symlink01"
#define ALL 1

#define SYMLINK "symlink01"
#define READLINK "readlink01"
#define STAT "stat04"
#define LSTAT "lstat01"
#define MKDIR "mkdir05"
#define RMDIR "rmdir03"
#define CHDIR "chdir01"
#define LINK "link01"
#define UNLINK "unlink01"
#define CHMOD "chmod01"
#define UTIME "utime01"
#define RENAME "rename01"
#define OPEN "open01"

#define cktcsid(s1,s2) (!strcmp(s1,s2))
#define BUFMAX 512
#define MODE 0700
#define MASK 0100777           /* A regular file with r,w,x for all mask */

/*
 * Lets be optimistic and only define messages for passing test cases
 */
const char *msgs[] = {
 "Creation of symbolic link file to no object file is ok",
 "Creation of symbolic link file and object file via symbolic link is ok", 
 "Creating an existing symbolic link file error is caught", 
 "Creating a symbolic link which exceeds maximum pathname error is caught",
 "Reading of symbolic link file contents checks out ok",
 "Reading a symbolic link which exceeds maximum pathname error is caught",
 "Getting stat info about object file through symbolic link file is ok",
 "Stat(2) error when accessing non-existent object through symbolic link is caught",
 "lstat(2) of symbolic link file which points to no object file is ok",
 "lstat(2) of symbolic link file which points at an object file is ok",
 "mkdir(2) of object file through symbolic link file failed as expected", 
 "rmdir(2) of object file through symbolic link file failed as expected", 
 "chdir(2) to object file location through symbolic link file is ok", 
 "chdir(2) to non-existent object file location through symbolic link file failed as expected", 
 "link(2) to a symbolic link, which is pointing to an existing object file worked - file created and link count adjusted",
 "link(2) to a symbolic link, which is pointing to a non-existing object file worked ok - file created and link count adjusted.",
 "unlink(2) of symbolic link file with no object file removal is ok",
 "chmod(2) of object file permissions through symbolic link file is ok",
 "chmod(2) error when accessing non-existent object through symbolic link is caught",
 "utime(2) change of object file access and modify times through symbolic link file is ok",
 "utime(2) error when accessing non-existent object through symbolic link is caught",
 "rename(3) of symbolic link file name which points at no object file is ok",
 "rename(3) of symbolic link file name which points at object file is ok",
 "rename(3) error of symbolic link file name across file systems is caught",
 "open(2) with (O_CREAT | O_RDWR) to create object file through symbolic link file and all writes, reads, and lseeks are ok",
 "open(2) with O_RDWR of existing  object file through symbolic link file and all writes, reads, and lseeks are ok",
 "open(2) with (O_CREAT | O_EXCL) error  is caught when creating object file through symbolic link file",
 "open(2) error with O_RDWR is caught when processing symbolic link file which points at no object file",
 "Nested symbolic link access condition caught.  ELOOP is returned",
 "Reading a nonsymbolic link file error condition is caught.  EINVAL is returned",
 "lstat(2) of object file returns object file inode information",
 "NULL"
};

/*
 * Define test object setup and validation functions
 */
int creat_both(), creat_symlink(), creat_path_max(), ck_symlink(),
    creat_object(), ck_object(), ck_both(), ck_path_max();


/*
 *  Define test cases
 */
struct all_test_cases
{
    const char *tcid;
    int test_fail;
    int errno_val;
    int pass_msg;
    int (*test_setup)();
    int (*ck_test)();
    const char *fn_arg[3];
    
} test_objects[] = {
    {SYMLINK, 0, 0, 0, creat_symlink, ck_symlink, {"%bc+eFhi!k", S_FILE, NULL}},
    {SYMLINK, 0, 0, 0, creat_symlink, ck_symlink,  {O_FILE, S_FILE, NULL}},
    {SYMLINK, 0, 0, 1, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {SYMLINK, 1, EEXIST, 2, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {SYMLINK, 1, ENAMETOOLONG, 3, creat_path_max, ck_path_max, {O_FILE, S_FILE, NULL}},
    {READLINK, 0, 0, 4, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {READLINK, 0, 0, 4, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {READLINK, 1, ENAMETOOLONG, 5, creat_path_max, ck_path_max, {O_FILE, S_FILE, NULL}},
    {READLINK, 1, EINVAL, 29, creat_object, ck_object, {O_FILE, NULL, NULL}},
    {STAT, 0, 0, 6, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {STAT, 1, ENOENT, 7, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {STAT, 1, ELOOP, 28, creat_symlink, ck_symlink, {S_FILE, S_FILE, NULL}},
    {LSTAT, 0, 0, 8, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {LSTAT, 0, 0, 9, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {LSTAT, 0, 0, 30, creat_object, ck_object, {O_FILE, NULL, NULL}},
    {MKDIR, 1, EEXIST, 10, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {RMDIR, 1, ENOTDIR, 11, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {CHDIR, 0, 0, 12, creat_symlink, ck_symlink, {O_FILE, S_FILE, O_FILE}},
    {CHDIR, 1, ENOENT, 13, creat_symlink, ck_symlink, {"%bc+eFhi!k", S_FILE, NULL}},
    {CHDIR, 1, ELOOP, 28, creat_symlink, ck_symlink, {S_FILE, S_FILE, NULL}},
    {LINK, 0, 0, 14, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {LINK, 0, 0, 15, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    /* The following link test case is invalid - leaving it defined so */
    /* I don't have to change all the entries in the all_tcses array after link */
    {LINK, -1, -1, -1, creat_symlink, ck_symlink, {NULL, NULL, NULL}},
    {UNLINK, 0, 0, 16, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {CHMOD, 0, 0, 17, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {CHMOD, 1, ENOENT, 18, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {CHMOD, 1, ELOOP, 28, creat_symlink, ck_symlink, {S_FILE, S_FILE, NULL}},
    {UTIME, 0, 0, 19, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {UTIME, 1, ENOENT, 20, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {UTIME, 1, ELOOP, 28, creat_symlink, ck_symlink, {S_FILE, S_FILE, NULL}},
    {RENAME, 0, 0, 21, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {RENAME, 0, 0, 22, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {RENAME, -1, EXDEV, 23, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {OPEN, 0, 0, 24, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {OPEN, 0, 0, 25, creat_both, ck_both, {O_FILE, S_FILE, O_FILE}},
    {OPEN, -1, EEXIST, 26, creat_symlink, ck_symlink, {O_FILE, S_FILE, O_FILE}},
    {OPEN, 1, ENOENT, 27, creat_symlink, ck_symlink, {O_FILE, S_FILE, NULL}},
    {OPEN, 1, ELOOP, 28, creat_symlink, ck_symlink, {S_FILE, S_FILE, NULL}}
};

/*
 * Define tcses
 */
struct tcses
{
    const char *tcid;
    const char *syscall;
    int test_cases;		/* number of entries in test_objects array */
    struct all_test_cases *tc_ptr;
    const char *desc;
} all_tcses[] = {

   { SYMLINK,  "symlink",  5, &test_objects[0],
		"Make a Symbolic Link to a File" },
   { READLINK, "readlink", 4, &test_objects[5],
		"Reads Value of a Symbolic Link" },
   { STAT,     "stat",     3, &test_objects[9],
		"Gets File Status Indirectly From a Symbolic Link file" },
   { LSTAT,    "lstat",    3, &test_objects[12],
		"Get file Status About a Symbolic Link File" },
   { MKDIR,    "mkdir",    1, &test_objects[15],
		"Fail When Making a Directory File Indirectly from a symlink" },
   { RMDIR,    "rmdir",    1, &test_objects[16],
		"Fail When Removing a Directory File Indirectly from a symlink" },
   { CHDIR,    "chdir",    3, &test_objects[17],
		"Changes CWD Location Indirectly from a symlink" },
   { LINK,     "link",     2, &test_objects[20],
		"Creates a Link To a File Indirectly From a Symbolic" },
   { UNLINK,   "unlink",   1, &test_objects[23],
		"Removes a Link To a File but not the Object File" },
   { CHMOD,    "chmod",    3, &test_objects[24],
		"Change Object File Permissions Indirectly From a Symbolic" },
   { UTIME,    "utime",    3, &test_objects[27],
		"Set File Access And Modify Object File Times via symlink" },
   { RENAME,   "rename",   3, &test_objects[30],
		"Rename a Symbolic Link File And Not Any Object file" },
   { OPEN,     "open",     5, &test_objects[33],
		"Create/Open a File For Reading Or Writing via symlink" },
};

/*
 * Define GLOBAL variables
 */

int TST_TOTAL;
int TEST_RESULT;
time_t a_time_value = 100;
const char  *TCID = NULL;
char  *Selectedtests = NULL;		/* Name (tcid) of selected test cases */
char test_msg[BUFMAX];
char full_path[PATH_MAX+1+1]; /* Add one for '\0' and another to exceed the PATH_MAX limit, see creat_path_max() */
extern int Tst_count;
extern char *TESTDIR;
extern char *strrchr();
extern int errno;

struct stat asymlink, statter;
char Buffer[1024];
char Buf[1024];

char *Tcid = NULL;

option_t Options[] = {
    { "T:",  NULL, &Tcid },	    /* -T tcid option */
    { NULL, NULL, NULL }
};

/***********************************************************************
 * MAIN
 ***********************************************************************/
int
main(int argc, char *argv[])
{
    struct tcses *tcs_ptr, *get_tcs_info();
    int do_syscalltests();
    void cleanup();
    int lc;             /* loop counter */
    const char *msg;          /* message returned from parse_opts */


   /***************************************************************
    * parse standard options, and exit if there is an error
    ***************************************************************/
   if ( (msg=parse_opts(argc, argv, Options, &help)) != (char *) NULL ) {
        tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
        tst_exit();
   }

   /*
    * If the -T option was used, use that TCID or use the default
    */
   if ( Tcid != NULL ) {
       TCID = Tcid;
       Selectedtests=Tcid;

   }
   else {
       TCID = DEFAULT_TCID;
#ifndef ALL
       Selectedtests = DEFAULT_TCID;
#else
       Selectedtests = NULL;
#endif
    }

   /*
    * Get test case specification information and assign TST_TOTAL
    */
   if ((tcs_ptr=get_tcs_info(Selectedtests)) == NULL) {
      TST_TOTAL=1;
      tst_brkm(TBROK, cleanup,
	"Unknown symbolic link test case specification executed");
   }

   /***************************************************************
    * perform global setup for test
    ***************************************************************/

   setup();

    /***************************************************************
     * check looping state if -c option given
     ***************************************************************/
    for (lc=0; TEST_LOOPING(lc); lc++) {

        /* reset Tst_count in case we are looping. */
        Tst_count=0;

        /*
         * Execute tcs testing function and all defined test cases
         */
        do_syscalltests(tcs_ptr);

    }   /* End for TEST_LOOPING */

    /*
     * End appropriately
     */
    cleanup();

    return 0;
}

/***********************************************************************
 *  This function maps the name of the process to a test case specification
 *  defined in the all_tcses array of tcses structures.  Either a pointer
 *  to the mapped test case specification information is returned or a
 *  null pointer.
 *
 *      Argument is path to program name.
 ***********************************************************************/
struct tcses *get_tcs_info(ptr)
char *ptr;
{
    unsigned ctr;
    struct tcses *tcs_ptr;

#if ALL
    if ( ptr == NULL ) {

	TST_TOTAL=0;
	for (ctr=1; ctr < sizeof(all_tcses)/sizeof(struct tcses); ctr++)
	TST_TOTAL += all_tcses[ctr].test_cases;
	return all_tcses;
    }
#endif

    
    for(ctr=0; ctr < (sizeof(all_tcses)/sizeof(struct tcses)); ctr++) {
       if ( strcmp(ptr, all_tcses[ctr].tcid) == 0 || 
	    strcmp(ptr, all_tcses[ctr].syscall) == 0 ) {
          tcs_ptr = &all_tcses[ctr];
	  TCID = all_tcses[ctr].tcid;
          TST_TOTAL=tcs_ptr->test_cases;
          return(tcs_ptr);
       }
       
    }
    return(NULL);
}

/***********************************************************************
 *  Determines if what path points at is a symbolic link file
 *
 *      Argument is path to symbolic link file.
 *
 *  Return status is one if a symbolic link file.  Zero if not a symbolic
 *  link file and a minus one if the path doesn't point at a file.
 ***********************************************************************/
int
see_if_a_symlink(path)
char *path;
{
   if (lstat(path, &asymlink) < 0)
      return(-1);

   if ((asymlink.st_mode & S_IFMT) == S_IFLNK)
      return(1);
   else
      return(0);
}

/***********************************************************************
 * This function performs without any hesitation, file(s) deletions
 ***********************************************************************/
void
delete_files(path1, path2)
char *path1, *path2;
{
   unlink(path1);
   unlink(path2);
}

/***********************************************************************
 *
 * This routine creates a symbolic link file.
 *
 *      Argument one is symbolic link pathname to point at.
 *      Argument two is name of symbolic link file.
 *
 ***********************************************************************/
int
creat_symlink(path1, path2)
char *path1, *path2;
{
   TEST( symlink(path1, path2) );
   errno=TEST_ERRNO;
   if (TEST_RETURN == -1) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"symlink(2) Failure when creating setup %s object file: errno:%d %s",
	path1, errno, strerror(errno));
      return(0);
   } 
   else {
	sprintf(Buf, "symlink(%s, %s) was succesful.\n", path1, path2);
	strcat(Buffer, Buf);
#if DEBUG
	tst_resm(TPASS, "symlink(%s, %s) was succesful.", path1, path2);
#endif
   }
   return(1);
}

/***********************************************************************
 *
 * This routine creates a regular file.
 *
 *      Argument one is a pathname
 *
 ***********************************************************************/
int
creat_object(path1)
char *path1;
{
   int fd;
   if ((fd=creat(path1, MODE)) == -1) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"creat(2) Failure when creating setup %s object file: errno:%d %s",
	path1, errno, strerror(errno));
      return(0);
   } 
   else {
	sprintf(Buf, "creat(%s, %#o) was succesful.\n", path1, MODE);
	strcat(Buffer, Buf);
#if DEBUG
	tst_resm(TPASS, "creat(%s, %#o) was succesful.", path1, MODE);
#endif
   }
   if (close(fd) == -1) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"close(2) Failure when closing setup %s object file: errno:%d %s",
	path1, errno, strerror(errno));
      return(0);
   }
   return(1);
}

/***********************************************************************
 *
 * This routine creates a symbolic link file and a regular file.
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 *
 ***********************************************************************/
int
creat_both(path1, path2, path3)
char *path1, *path2, *path3;
{
   if (creat_symlink(path1, path2) == -1)
      return(0);
   else if (creat_object(path3) == -1)
      return(0);
   return(1);
}

/***********************************************************************
 *
 * This routine checks if symbolic link file is a symbolic link file.
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 *
 ***********************************************************************/
int
ck_symlink(path1, path2, path3)
char *path1, *path2, *path3;
{
   int ret;

   if ((ret=see_if_a_symlink(path2)) == -1) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"lstat(2) Failure when accessing %s symbolic link file which should contain %s path to %s file ",
	path2, path1, path3);
      return(0);
   }
   else if (ret == 0) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"%s is not a symbolic link file which contains %s path to %s file",
	path2, path1, path3);
      return(0);
   }
   return(1);
}

/***********************************************************************
 *
 * This routine checks if symbolic link file points at object file.
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 *
 ***********************************************************************/
int
ck_both(path1, path2, path3)
char *path1, *path2, *path3;
{
   if (ck_symlink(path1, path2, path3) == 0)
      return(0);
   else if ((stat(path3, &statter) == -1) && (errno == ENOENT)) {
      TEST_RESULT=TBROK;
      sprintf(test_msg, "stat(2) Failure when accessing %s object file ", path3);
      return(0);
   }
   else if ((stat(path2, &asymlink) == -1) && (errno == ENOENT)) {
      TEST_RESULT=TBROK;
      sprintf(test_msg, "stat(2) Failure when accessing %s symbolic link file ",
	path2);
      return(0);
   }
   else if (statter.st_ino != asymlink.st_ino) {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"stat(2) Failure when accessing %s object file through %s symbolic link file ",
	path3, path2);
      return(0);
   }
   return(1);
   
}

/***********************************************************************
 * This routine populates full_path with a pathname whose length exceeds
 * the PATH_MAX define value in param.h
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 ***********************************************************************/
int
creat_path_max(path1, path2, path3)
char *path1, *path2, *path3;
{
   int ctr, to_go, size, whole_chunks;
   char buf [PATH_MAX];
   char *cwd;

   if ((cwd = getcwd(buf, sizeof (buf))) == NULL)
   {
      TEST_RESULT=TBROK;
      sprintf(test_msg,
	"getcwd(3) Failure in setup of %s %s %s test case object elements",
	path1, path2, path3);
      return(0);
   }
   size = strlen(cwd);

   to_go = PATH_MAX - size;
   size = strlen(path1);
   whole_chunks = to_go / size;
   strcpy(full_path, cwd);
   for (ctr=0; ctr < whole_chunks; ctr++) {
       strcat(full_path, path1);
   }
   size= strlen(full_path);
   to_go = PATH_MAX - size;
   strcat(full_path, "/");
   for (ctr=0; ctr < to_go; ctr++)
       strcat(full_path, "Z");

   return(1);
}

/***********************************************************************
 * This routine checks that full_path's  length exceeds the PATH_MAX
 * define value in param.h
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 ***********************************************************************/
int
ck_path_max(path1, path2, path3)
char *path1, *path2, *path3;
{
   if (strlen(full_path) == (PATH_MAX+1))
      return(1);
   else
   {
      TEST_RESULT=TBROK;
      sprintf(test_msg, "%s %d %s %s %s %s",
	"full_path character array length was not", (PATH_MAX+1),
	"characters long for test case object elements",
	path1, path2, path3);
      return(0);
   }
}

/***********************************************************************
 * This routine checks if the stat(2) and lstat(2) calls return the same
 * information when the path is not a symbolic link file
 *
 *      Argument one is a pathname of object file
 *      Argument two is symbolic link file name
 *      Argument three is regular file name
 *
 ***********************************************************************/
int
ck_object(path1, path2, path3)
char *path1, *path2, *path3;
{
    int ret;

    if ((ret=see_if_a_symlink(path1)) < 0) {
       TEST_RESULT=TFAIL;
       sprintf(test_msg,
	   "lstat(2) failed to return inode information for a regular object file");
       return(0);
    }
    else if (ret == 1) {
       TEST_RESULT=TFAIL;
       sprintf(test_msg, 
	   "lstat(2) detected a regular object file as a symbolic link file");
       return(0);
    }
    else if (stat(path1, &statter) == -1) {
       TEST_RESULT=TBROK;
       sprintf(test_msg,
	   "stat(2) failed to return inode information for a regular object file");
       return(0);
    }
    else if (bcmp((char *)&statter, (char *)&asymlink, sizeof(statter)) != 0) {
       TEST_RESULT=TFAIL;
       sprintf(test_msg,
	   "lstat(2) and stat(2) do not return same inode information for an object file");
       return(0);
        
    }
    return(1);
}

/***********************************************************************
 * Main test case processing function
 *
 *  Argument is a ptr into the all_tcses array of structures of type tcses
 ***********************************************************************/
int
do_syscalltests(tcs)
struct tcses *tcs;
{
   int ctr, ret;
   struct all_test_cases *tc_ptr;

   /*
    * loop through desired number of test cases
    */
   for (ctr=0, tc_ptr=tcs->tc_ptr; ctr < TST_TOTAL; ctr++, tc_ptr++) {

       if (tc_ptr->test_fail < 0) continue;

       Buffer[0]='\0';

       /*
	* If running all test cases for all tcid, set the TCID if needed.
        */
       if ( Selectedtests == NULL ) {
	   if ( !TCID || strcmp(TCID, tc_ptr->tcid) != 0 ) {
               TCID = tc_ptr->tcid;
               Tst_count=0;
	   }
       }
       /*
        * Insure that we are executing the correct tcs test case
        */
       if (strcmp(TCID, tc_ptr->tcid) != 0) {
          tst_resm(TBROK, "%s TCID attempted to execute %s %d %d test case",
	     TCID, tc_ptr->tcid, tc_ptr->test_fail, tc_ptr->errno_val);
          continue;
       }
       TEST_RESULT=TPASS;
       delete_files(S_FILE, O_FILE);
       /* 
        * Perform test case setup
        */
       ret = (tc_ptr->test_setup)(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1],
	   tc_ptr->fn_arg[2], tc_ptr->errno_val);

       /* If an expected error, try it out */

       if (tc_ptr->test_fail) {
          /* 
           * Try to perform test verification function
           */
          if (! (tc_ptr->ck_test)(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1],
			tc_ptr->fn_arg[2], tc_ptr->errno_val))
             tst_resm(TEST_RESULT, test_msg);
          else if (tc_ptr->errno_val == EEXIST)
             do_EEXIST(tc_ptr);
          else if (tc_ptr->errno_val == ENOENT)
             do_ENOENT(tc_ptr);
          else if (tc_ptr->errno_val == ELOOP)
             do_ELOOP(tc_ptr);
          else if (tc_ptr->errno_val == ENOTDIR)
             do_ENOTDIR(tc_ptr);
          else if (tc_ptr->errno_val == EXDEV)
             do_EXDEV(tc_ptr);
          else if (tc_ptr->errno_val == ENAMETOOLONG)
             do_ENAMETOOLONG(tc_ptr);
          else if (tc_ptr->errno_val == EINVAL)
             do_EINVAL(tc_ptr);
          else
             tst_resm(TBROK, "Test Case Declaration Error");
       }
       else if (ret == 1) {  /*  No setup function error */
 
          if (tc_ptr->errno_val != 0)
             tst_resm(TBROK, "Test Case Declaration Error");
          else {
             /* 
              * Perform test verification function
              */
             ret=(tc_ptr->ck_test)(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1],
		tc_ptr->fn_arg[2], tc_ptr->errno_val);

             /* Perform requested symbolic link system call test */

             if ((cktcsid(tc_ptr->tcid, SYMLINK)) ||
				(cktcsid(tc_ptr->tcid, LSTAT))) {
                if (ret == 1)
                   tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);   
                else
                   tst_resm(TEST_RESULT, test_msg);
             }
             else if (ret == 0)
                tst_resm(TEST_RESULT, test_msg);
             else if (cktcsid(tc_ptr->tcid, READLINK))
                do_readlink(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, STAT))
                do_stat(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, CHDIR))
                do_chdir(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, LINK))
                do_link(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, UNLINK))
                do_unlink(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, CHMOD))
                do_chmod(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, UTIME))
                do_utime(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, RENAME))
                do_rename(tc_ptr);
             else if (cktcsid(tc_ptr->tcid, OPEN))
                do_open(tc_ptr);
             else
                tst_resm(TBROK, "Unknown test case processing actions declared");
          }
       }
       else
          tst_resm(TBROK, "Test Case Declaration Error");
   }
   return(0);
}

/***********************************************************************
 * This routine checks for the return of EEXIST errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_EEXIST(tc_ptr)
struct all_test_cases *tc_ptr;
{
    if (cktcsid(tc_ptr->tcid, SYMLINK)) {

       TEST( symlink(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1]) );
	errno=TEST_ERRNO;
       if ((TEST_RETURN == -1) && (errno == EEXIST))
	   if ( STD_FUNCTIONAL_TEST )
               tst_resm(TPASS, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
       else
           tst_resm(TFAIL, "%s %s",
		"Expected EEXIST error when creating a symbolic link file",
		"which already existed");
     }
     else if (cktcsid(tc_ptr->tcid, MKDIR)) {

	TEST ( mkdir(tc_ptr->fn_arg[1],MODE) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == EEXIST))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else {

           tst_resm(TFAIL, "%s %s",
		"Expected EEXIST error when creating a directory by a symbolic",
		"link file which pointed at no object file");
           rmdir(tc_ptr->fn_arg[1]);
        }
     }
     else if (cktcsid(tc_ptr->tcid, OPEN)) {

        TEST( open(tc_ptr->fn_arg[1], (O_EXCL | O_CREAT)) )
 	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == EEXIST))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
	        "Expected EEXIST error for exclusively opening an object file",
		"through a symbolic link file was not received:",
		errno, strerror(errno));
        close (TEST_RETURN);
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of ENOENT errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_ENOENT(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (cktcsid(tc_ptr->tcid, STAT)) {

        if ((stat(tc_ptr->fn_arg[1], &asymlink) == -1) && (errno == ENOENT))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error for stating a non-existent directory",
		"through a symbolic link file was not received:",
	  	errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, CHDIR)) {
        if ((chdir(tc_ptr->fn_arg[1]) == -1) && (errno == ENOENT))
	    if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else {
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error for changing to a non-existent",
		"directory through a symbolic link file was not received:",
		errno, strerror(errno));
           chdir(TESTDIR);
        }
     }
     else if (cktcsid(tc_ptr->tcid, LINK)) {

        if ((link(tc_ptr->fn_arg[1], "nick") == -1) && (errno == ENOENT))
	  if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
             tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
        {
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error condition when link(2) a symbolic",
		"link which pointed at no object:", errno, strerror(errno));
           delete_files("nick", NULL);
        }
     }
     else if (cktcsid(tc_ptr->tcid, CHMOD)) {

        if ((chmod(tc_ptr->fn_arg[1], MODE) == -1) && (errno == ENOENT))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error condition when chmod(2) a symbolic",
		"link which pointed at no object,", errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, UTIME)) {

        if ((utime(tc_ptr->fn_arg[1], NULL) == -1) && (errno == ENOENT))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error condition when utime(2) a symbolic",
		"link which pointed at no object:", errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, OPEN)) {

        if ((open(tc_ptr->fn_arg[1], O_RDWR) == -1) && (errno == ENOENT))
	    if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOENT error for opening a non-existent object",
		" file through a symbolic link file was not received,",
		errno, strerror(errno));
        close (TEST_RETURN);
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of ELOOP errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_ELOOP(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (cktcsid(tc_ptr->tcid, STAT)) {

	TEST( stat(tc_ptr->fn_arg[1], &asymlink) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == ELOOP))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
              tst_resm(TEST_RESULT, "%s errno:%d %s",
	  	"Expected ELOOP errno from stat(2) (nested symb link),",
		 errno, strerror(errno));
	   else
		Tst_count++;
     }
     else if (cktcsid(tc_ptr->tcid, CHDIR)) {

	TEST( chdir(tc_ptr->fn_arg[1]) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == ELOOP))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
              tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else {

           tst_resm(TFAIL, "%s errno:%d %s",
		"Expected ELOOP error condition when chdir(2) a nested symbolic link:",
		errno, strerror(errno));
           chdir(TESTDIR);
        }
     }
     else if (cktcsid(tc_ptr->tcid, LINK)) {

	TEST ( link(tc_ptr->fn_arg[1], O_FILE) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == ELOOP))
	    if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s errno:%d %s",
		"Expected ELOOP error condition when link(2) a nested symbolic link:",
		errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, CHMOD)) {

	TEST ( chmod(tc_ptr->fn_arg[1], MODE) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == ELOOP))
	    if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	    else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s errno:%d %s",
		"Expected ELOOP error condition when chmod(2) a nested symbolic link:",
		errno, strerror(errno));
        return;
     }
     else if (cktcsid(tc_ptr->tcid, UTIME)) {

	TEST( utime(tc_ptr->fn_arg[1], NULL) );
	errno=TEST_ERRNO;

        if ((TEST_RETURN == -1) && (errno == ELOOP))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s errno:%d %s",
		"Expected ELOOP error condition when utime(2) a nested symbolic link:",
		errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, OPEN)) {

        int fd;
        TEST( open(tc_ptr->fn_arg[1], O_CREAT) );
	fd=TEST_RETURN;
	errno=TEST_ERRNO;
        if ((fd == -1) && (errno == ELOOP))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, "%s errno:%d %s",
		"Expected ELOOP error condition when open(2) a nested symbolic link:",
		errno, strerror(errno));
	close (fd);
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of ENOTDIR errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_ENOTDIR(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (cktcsid(tc_ptr->tcid, RMDIR)) {

	TEST( mkdir(tc_ptr->fn_arg[0], MODE) );
	errno=TEST_ERRNO;
        if (TEST_RETURN == -1)
            tst_resm(TBROK, "mkdir(2) Failure when creating %s",
		tc_ptr->fn_arg[0]);
        else if ((rmdir(tc_ptr->fn_arg[1]) == -1) && (errno == ENOTDIR)) {

	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
           rmdir(tc_ptr->fn_arg[0]);
        }
        else
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected ENOTDIR error for removing a non-existent",
		"directory through a symbolic link file was not received,",
		errno, strerror(errno));
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of EXDEV errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_EXDEV(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (cktcsid(tc_ptr->tcid, RENAME)) {

	TEST( rename(tc_ptr->fn_arg[1], Y_A_S_FILE) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == EXDEV)) {
           if (see_if_a_symlink(Y_A_S_FILE) == -1)
	      if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                  tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	      else
		  Tst_count++;
           else
              tst_resm(TFAIL, "%s %s %s file outside of current file system",
		"rename(3) returned -1 when trying to move symbolic link file",
		"outside of current file system, but created", Y_A_S_FILE);
        }
        else {
           tst_resm(TFAIL, "%s %s errno:%d %s",
		"Expected EXDEV error for renaming an existing symbolic",
		"link file to a location outside of existing file system,",
		errno, strerror(errno));
           delete_files("/NiCkEr", NULL);
        }
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of ENAMETOOLONG errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_ENAMETOOLONG(tc_ptr)
struct all_test_cases *tc_ptr;
{
     int ret;

     if (cktcsid(tc_ptr->tcid, SYMLINK)) {

	TEST( symlink(tc_ptr->fn_arg[0], full_path) );
	errno=TEST_ERRNO;
        if ((TEST_RETURN == -1) && (errno == ENAMETOOLONG))
           if (see_if_a_symlink(full_path) == -1)
	      if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                  tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	      else
		  Tst_count++;
           else
              tst_resm(TFAIL, "%s %s %d %s",
		"symlink(2) returned -1 when trying to create a symbolic",
		"link file whose name exceeded", (PATH_MAX+1),
		"characters, but it created the symbolic link file");
        else
           tst_resm(TFAIL,
		"Expected ENAMETOOLONG error when creating %s symbolic link file with a path exceeding %d characters: errno:%d %s",
		tc_ptr->fn_arg[1], (PATH_MAX+1), errno, strerror(errno));
     }
     else if (cktcsid(tc_ptr->tcid, READLINK)) {

        char scratch[PATH_MAX+1];

	ret=readlink(full_path, scratch, strlen(full_path));
        if (( ret == -1) && (errno == ENAMETOOLONG))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  ) 
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL,
	       "Expected ENAMETOOLONG error when reading %s symbolic link file with a path exceeding %d characters: errno:%d %s",
		tc_ptr->fn_arg[1], (PATH_MAX+1), errno, strerror(errno));
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks for the return of EINVAL errno from requested
 * system call
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_EINVAL(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (cktcsid(tc_ptr->tcid, READLINK)) {
	TEST( readlink(tc_ptr->fn_arg[0], test_msg, BUFMAX) );
	errno=TEST_ERRNO;
        if (TEST_RETURN == -1) {
           if (errno == EINVAL) {
	      if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                  tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	      else
		Tst_count++;
           } else
              tst_resm(TFAIL, "readlink(2) ret:-1, errno:%d, : Exp errno:%d",
		errno, EINVAL);
        }
        else {
           tst_resm(TFAIL, "readlink(2) did not returned -1 when reading %s",
		"a file which is not a symbolic link file");
	}
     }
     else
        tst_resm(TBROK, "Unknown test case processing actions declared");
}

/***********************************************************************
 * This routine checks out the readlink(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_readlink(tc_ptr)
struct all_test_cases *tc_ptr;
{
     char scratch[PATH_MAX];
     int ret;

     ret=readlink(tc_ptr->fn_arg[1], scratch, strlen(tc_ptr->fn_arg[0]));

     /*** the TEST macro cannot be used here for some reason ****/

     if (ret == -1) {
        tst_resm(TFAIL,
	    "readlink(2) failure on %s symbolic link file, errno=%d",
	    tc_ptr->fn_arg[1], errno);

     }
     else if (strncmp(tc_ptr->fn_arg[0], scratch,
			strlen(tc_ptr->fn_arg[0])) != 0) {

        /* Must null terminate scratch because readlink(2) doesn't */

        scratch[strlen(tc_ptr->fn_arg[0])] = '\0';
 
        tst_resm(TFAIL,
	   "readlink(2) Error : Expected %s symbolic link file contents but %s actual contents were returned",
	   tc_ptr->fn_arg[0], scratch);
     }
     else if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
        tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
     else
	Tst_count++;
}

/***********************************************************************
 * This routine checks out the stat(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_stat(tc_ptr)
struct all_test_cases *tc_ptr;
{
    if (statter.st_dev != asymlink.st_dev)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object device info %ld != stat of object file device info %ld",
	    statter.st_dev, asymlink.st_dev);

    else if (statter.st_mode != asymlink.st_mode)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object file permissions %ld != stat of object file permissions %ld",
	    statter.st_mode, asymlink.st_mode);

    else if (statter.st_nlink != asymlink.st_nlink)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object file link count %ld != stat of object file link count %ld",
	    statter.st_nlink, asymlink.st_nlink);

    else if (statter.st_uid != asymlink.st_uid)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object file uid %ld != stat of object file uid %ld",
	    statter.st_uid, asymlink.st_uid);

    else if (statter.st_gid != asymlink.st_gid)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object file gid %ld != stat of object file gid %ld",
	    statter.st_gid, asymlink.st_gid);

    else if (statter.st_size != asymlink.st_size)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object file size %ld != stat of object file size %ld",
	    statter.st_size, asymlink.st_size);

    else if (statter.st_atime != asymlink.st_atime)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object access time %ld != stat of object file access time %ld",
	    statter.st_atime, asymlink.st_atime);

    else if (statter.st_mtime != asymlink.st_mtime)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object modify time %ld != stat of object file modify time %ld",
	    statter.st_atime, asymlink.st_atime);

    else if (statter.st_ctime != asymlink.st_ctime)
        tst_resm(TFAIL,
	    "stat of symbolic link reference to object change time %ld != stat of object file change time %ld",
	    statter.st_atime, asymlink.st_atime);
    else if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
        tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
     else
	Tst_count++;
}

/***********************************************************************
 * This routine checks out the chdir(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_chdir(tc_ptr)
struct all_test_cases *tc_ptr;
{
     if (mkdir(tc_ptr->fn_arg[2],MODE) == -1)
        tst_resm(TFAIL, "Could not create a setup directory file");
     else {

	sprintf(Buf, "mkdir(%s, %#o) was successful\n", tc_ptr->fn_arg[2],MODE);
	strcat(Buffer, Buf);

        if (chdir(tc_ptr->fn_arg[1]) == -1)
           tst_resm(TFAIL, "%sCould not change a directory file through a %s",
		Buffer, "symbolic link which which pointed at object");
        else {

	   char buf [PATH_MAX];
           char *cwd;
           char expected_location[PATH_MAX];
           /*
           *  Build expected current directory position
           */
           strcpy(expected_location, TESTDIR);
           strcat(expected_location, "/");
           strcat(expected_location, tc_ptr->fn_arg[2]);

           if ((cwd = getcwd(buf, sizeof (buf))) == NULL)
              tst_resm(TFAIL, "getcwd(3) FAILURE");
           else if (strcmp(cwd, expected_location) == 0)
	      if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                  tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
     	      else
		  Tst_count++;
           else {
              tst_resm(TFAIL, "%s%s %s %s not equal to expected %s", Buffer,
		"chdir(2) returned successfully, but getcwd(3) indicated",
		"new current working directory location", cwd, expected_location);
           }
           chdir(TESTDIR);
        }
        rmdir(tc_ptr->fn_arg[2]);
     }
}

/***********************************************************************
 * This routine checks out the link(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_link(tc_ptr)
struct all_test_cases *tc_ptr;
{
    struct stat stbuf;

    if (link(tc_ptr->fn_arg[1], "nick") == -1) {
        tst_resm(TFAIL, "%slink(%s, \"nick\") failed, errno: %d\n%s %s",
	    Buffer, tc_ptr->fn_arg[1], errno,
	    "link of new file to object file via symbolic link file failed",
	    "when expected not to");
    }
    else {
	sprintf(Buf, "link(%s, \"nick\") was successful\n", tc_ptr->fn_arg[1]);
	strcat(Buffer, Buf);

	if ( STD_FUNCTIONAL_TEST ) {
	  /*
	   * Check that links counts were properly set
	   */
          if (lstat(tc_ptr->fn_arg[1], &asymlink) == -1) {
            tst_resm(TBROK, "lstat(%s) failed. errno: %d",
		tc_ptr->fn_arg[1], errno);

          } else if (lstat("nick", &statter) == -1) {
           tst_resm(TBROK, "lstat(nick) failed, errno:%d", errno);
          } else {
            if (statter.st_ino == asymlink.st_ino) {
                if ((statter.st_nlink ==2) && (asymlink.st_nlink == 2)) {
		   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                       tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
		   else
			Tst_count++;
                } else {
		   lstat(tc_ptr->fn_arg[2], &stbuf);

                   tst_resm(TFAIL, 
		       "%slink(%s, %s) failed to adjust link count.\n\
		count for nick is %d, count for %s is %d, count for %s is %d.",
		       Buffer, tc_ptr->fn_arg[1], "nick",
		       statter.st_nlink, tc_ptr->fn_arg[1], asymlink.st_nlink,
			tc_ptr->fn_arg[2], stbuf.st_nlink);
		}
	    } else {
		tst_resm(TFAIL, 
		    "%sA lstat of %s (ino:%llu) and of\n\t\t\
%s (ino:%llu), does not show them being the same ino.", Buffer,
		   tc_ptr->fn_arg[1], asymlink.st_ino, "nick", statter.st_ino);
	    }
          }
	}
        delete_files("nick", NULL);
    }
}

/***********************************************************************
 * This routine checks out the unlink(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_unlink(tc_ptr)
struct all_test_cases *tc_ptr;
{
    if (stat(tc_ptr->fn_arg[2], &asymlink) == -1)
        tst_resm(TBROK,
	    "stat(2) Failure when accessing %s object file", tc_ptr->fn_arg[2]);
    else if (unlink(tc_ptr->fn_arg[1]) == -1)
        tst_resm(TFAIL, "unlink(2) failed when removing symbolic link file");
    else {
	sprintf(Buf, "unlink(%s) was successful\n", tc_ptr->fn_arg[1]);
	strcat(Buffer, Buf);
	if (stat(tc_ptr->fn_arg[2], &statter) == -1)
            tst_resm(TFAIL,"%s %s",
	        "unlink(2) failed because it not only removed symbolic link",
	        "file which pointed at object file, but object file as well");
	
        else
            if ((statter.st_ino == asymlink.st_ino) &&
		(statter.st_dev == asymlink.st_dev) &&
		(statter.st_size == asymlink.st_size))

	       if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                   tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	       else
		    Tst_count++;
            else
               tst_resm(TFAIL, "%s%s %s %s", Buffer,
		    "unlink(2) failed because it not only removed symbolic link",
		    "file which pointed at object file, but it changed object",
		    "file inode information");
    }

}

/***********************************************************************
 * This routine checks out the chmod(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_chmod(tc_ptr)
struct all_test_cases *tc_ptr;
{
    if (stat(tc_ptr->fn_arg[2], &asymlink) == -1)
        tst_resm(TBROK,
	    "stat(2) Failure when accessing %s object file", tc_ptr->fn_arg[2]);
    else if (chmod(tc_ptr->fn_arg[1], (MODE | MASK)) == -1) 
        tst_resm(TFAIL, "%s%s %s", Buffer,
	    "chmod(2) failed when changing file permission",
	    "through symbolic link file");
    else { 
	sprintf(Buf, "chmod(%s, %#o) was successful\n", tc_ptr->fn_arg[1], 
		(MODE | MASK));
	strcat(Buffer, Buf);

	if (stat(tc_ptr->fn_arg[2], &statter) == -1)
            tst_resm(TBROK,
	        "stat(2) Failure when accessing %s object file", tc_ptr->fn_arg[2]);
        else
            if ((statter.st_mode == (MODE | MASK)) &&
			    (statter.st_mode != asymlink.st_mode))
	       if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                   tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	       else
		    Tst_count++;
            else
               tst_resm(TFAIL, "%s%s %o to %o %s", Buffer,
		    "chmod(2) failed to change object file permissions from",
		    asymlink.st_mode, (MODE | MASK),
		    "through symbolic link file");
    }

}

/***********************************************************************
 * This routine checks out the utime(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_utime(tc_ptr)
struct all_test_cases *tc_ptr;
{
     struct utimbuf utimes;

     if (stat(tc_ptr->fn_arg[2], &asymlink) == -1)
        tst_resm(TBROK, "stat(2) Failure when accessing %s object file",
	    tc_ptr->fn_arg[2]);
     else {
        /* Now add a few values to access and modify times */

        utimes.actime = asymlink.st_atime + a_time_value;
        utimes.modtime = asymlink.st_mtime + a_time_value;
       
        /* Now hand off to utime(2) via symbolic link file*/

        if (utime(tc_ptr->fn_arg[1], &utimes) == -1)
            tst_resm(TFAIL, "%s %s",
	        "utime(2) failed to process object file access and modify",
		"time updates through symbolic link");
        else {
           /* Now verify changes were made */

           if (stat(tc_ptr->fn_arg[2], &statter) == -1)
              tst_resm(TBROK,
		"stat(2) Failure when accessing %s object file",
		tc_ptr->fn_arg[2]);
           else {
              time_t temp, diff;

              temp = statter.st_atime - asymlink.st_atime;
              diff = (statter.st_mtime - asymlink.st_mtime) - temp;

              if (! diff) 
		 if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                     tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
		 else
		     Tst_count++;
              else
                 tst_resm(TFAIL, "%s %s %d greater than original times",
		    "utime(2) failed to change object file access and",
		    "modify times through symbolic link to a value",
		    a_time_value);
           }
        }
     }
}

/***********************************************************************
 * This routine checks out the rename(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_rename(tc_ptr)
struct all_test_cases *tc_ptr;
{
     int pts_at_object = 0;

     
     if (stat(tc_ptr->fn_arg[2], &statter) != -1)
        pts_at_object=1;

     TEST (rename(tc_ptr->fn_arg[1], A_S_FILE) );
     errno=TEST_ERRNO;
     if (TEST_RETURN == -1)
        tst_resm(TFAIL, "rename(3) failed to rename %s symbolic link file to %s",
	    tc_ptr->fn_arg[2], A_S_FILE);
     else if (pts_at_object)
        if (ck_both(tc_ptr->fn_arg[0], A_S_FILE, tc_ptr->fn_arg[2]))
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        else
           tst_resm(TFAIL, test_msg);
     else if ( ! ck_symlink(tc_ptr->fn_arg[0], A_S_FILE, NULL)) 
        tst_resm(TFAIL, test_msg);
     else if (stat(tc_ptr->fn_arg[1], &asymlink) != -1)
        tst_resm(TFAIL,
	    "rename(3) did not remove %s when renaming to %s",
	    tc_ptr->fn_arg[1], A_S_FILE);
     else if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
        tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
     else
	Tst_count++;
}

/***********************************************************************
 * This routine checks out the open(2) system call for a successful
 * invocation
 *
 *   Argument is pointer to test_objects array of structures of type
 *   all_test_cases
 ***********************************************************************/
void
do_open(tc_ptr)
struct all_test_cases *tc_ptr;
{
     int fd = -1;
     int ret, pts_at_object = 0;
     char scratch[PATH_MAX];

     
     if (stat(tc_ptr->fn_arg[2], &statter) != -1)
        pts_at_object=1;

     if (pts_at_object) {
	TEST( open(tc_ptr->fn_arg[1], O_RDWR) );
        errno=TEST_ERRNO;
        if ((fd=TEST_RETURN) == -1) {
           tst_resm(TFAIL,
	       "open(2) Failure when opening object file through symbolic link file");
           return;
        }
     }
     else {
	TEST(open(tc_ptr->fn_arg[1], (O_CREAT | O_RDWR), MODE) );
	errno=TEST_ERRNO;
        if ((fd=TEST_RETURN) == -1) {
           tst_resm(TFAIL,
	       "open(2) Failure when creating object file through symbolic link file");
           return;
        }
     }
     if ((ret=write(fd, BIG_STRING, strlen(BIG_STRING))) == -1) {
        tst_resm(TFAIL,
	    "write(2) Failure to object file opened through a symbolic link file: errno:%d",
	    errno);
     }
     else if (ret != strlen(BIG_STRING)) {
        tst_resm(TFAIL,
	    "write(2) Failed to write %d bytes to object file opened through a symbolic link file",
	    strlen(BIG_STRING));
     }
     else if (lseek(fd, 0L, 0) == -1) {
        tst_resm(TFAIL,
	    "lseek(2) Failed to position to beginning of object file opened through a symbolic link file: errno = %d",
	    errno);
     }
     else if ((ret=read(fd, scratch, strlen(BIG_STRING))) == -1) {
        tst_resm(TFAIL,
	    "read(2) Failure of object file opened through a symbolic link file: errno = %d",
	    errno);
     }
     else if (ret != strlen(BIG_STRING)) {
        tst_resm(TFAIL,
	    "read(2) Failed to read %d bytes to object file opened through a symbolic link file",
	    strlen(BIG_STRING));
     }
     else if (strncmp(BIG_STRING, scratch, strlen(BIG_STRING)) != 0) {
        tst_resm(TFAIL,
	    "Content of write(2) and read(2) Failed to object file through symbolic link file was not as expected. Expected %s and read returned %s",
	    BIG_STRING, scratch);
     }
     else {
        /*
         *  Close off symbolic link file to object file access
         */
        if (close(fd) == -1) {
           tst_resm(TFAIL,
	       "close(2) Failure when closing object file accessed symbolic link file");
        }
        /*
         * Now, lets open up and read object file and compare contents
        */
        else if ((fd=open(tc_ptr->fn_arg[0], O_RDONLY)) == -1) {
           tst_resm(TFAIL, "open(2) Failure when opening %s file: errno:%d %s",
	       tc_ptr->fn_arg[0], errno, strerror(errno));
        }
        else if ((ret=read(fd, scratch, strlen(BIG_STRING))) == -1) {
           tst_resm(TFAIL,
	       "read(2) Failure of object file opened through a symbolic link file: errno:%d", errno);
        }
        else if (ret != strlen(BIG_STRING)) {
           tst_resm(TFAIL,
	       "read(2) Failed to read %d bytes to object file opened through a symbolic link file",
		strlen(BIG_STRING));
        }
        else if (strncmp(BIG_STRING, scratch, strlen(BIG_STRING)) != 0) {
           tst_resm(TFAIL,
	       "Content of write(2) and read(2) Failed to object file through symbolic link file was not as expected. Expected %s and read returned %s",
		BIG_STRING, scratch);
        }
        else if (pts_at_object) {
	   if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
               tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	   else
		Tst_count++;
        }
        else { /* Insure newly created object file is pointed at */
           if (ck_both(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1], tc_ptr->fn_arg[0]))
	      if ( TEST_RESULT != TPASS || STD_FUNCTIONAL_TEST  )
                  tst_resm(TEST_RESULT, msgs[tc_ptr->pass_msg]);
	      else
	 	  Tst_count++;
           else
              tst_resm(TFAIL, test_msg);
        }
        close(fd);
     }
}

/***************************************************************
 * setup() - performs all ONE TIME setup for this test.
 ***************************************************************/
void
setup()
{
    /* capture signals */
    tst_sig(FORK, DEF_HANDLER, cleanup);

    /* create a temporary directory and go to it */
    tst_tmpdir();

    /* Pause if that option was specified */
    TEST_PAUSE;

}       /* End setup() */


/***************************************************************
 * cleanup() - performs all ONE TIME cleanup for this test at
 *              completion or premature exit.
 ***************************************************************/
void
cleanup()
{
    /*
     * print timing stats if that option was specified.
     */
    TEST_CLEANUP;

    /* remove temporary directory and all files in it. */
    tst_rmdir();

    /* exit with return code appropriate for results */
    tst_exit();

}       /* End cleanup() */


/*
 *
 */
void help()
{
    unsigned ind;

    printf("   -T id  Determines which tests cases to execute:\n");

    for (ind=0; ind<sizeof(all_tcses)/sizeof(struct tcses ); ind++) {
	printf("  %s/%s - %s\n", all_tcses[ind].tcid, all_tcses[ind].syscall,
	    all_tcses[ind].desc);
    }
}
