text: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the
   * Common Development and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   * or http://www.opensolaris.org/os/licensing.
   * See the License for the specific language governing permissions
   * and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each
   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   * If applicable, add the following below this CDDL HEADER, with the
   * fields enclosed by brackets "[]" replaced with your own identifying
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
  
  /*
   * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  /*
   * Copyright (c) 2013 by Delphix. All rights reserved.
   */
  
  /*
   * Copyright (c) 2018, Joyent, Inc.
   */
  
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <utime.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <strings.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <libgen.h>
  
  #define	ST_ATIME 0
  #define	ST_CTIME 1
  #define	ST_MTIME 2
  
  #define	ALL_MODE (mode_t)(S_IRWXU|S_IRWXG|S_IRWXO)
  
  typedef struct timetest {
  	int	type;
  	char	*name;
  	int	(*func)(const char *pfile);
  } timetest_t;
  
  static char tfile[BUFSIZ] = { 0 };
  
  extern int errno;
  
  /*
   * DESCRIPTION:
   * 	Verify time will be changed correctly after each operation.
   *
   * STRATEGY:
   *	1. Define time test array.
   *	2. Loop through each item in this array.
   *	3. Verify the time is changed after each operation.
   *
   */
  
  static int
  get_file_time(const char *pfile, int what, time_t *ptr)
  {
  	struct stat stat_buf;
  
  	if (pfile == NULL || ptr == NULL) {
  		return (-1);
  	}
  
  	if (stat(pfile, &stat_buf) == -1) {
  		return (-1);
  	}
  
  	switch (what) {
  		case ST_ATIME:
  			*ptr = stat_buf.st_atime;
  			return (0);
  		case ST_CTIME:
  			*ptr = stat_buf.st_ctime;
  			return (0);
  		case ST_MTIME:
  			*ptr = stat_buf.st_mtime;
  			return (0);
  		default:
  			return (-1);
  	}
  }
  
  static int
  do_read(const char *pfile)
  {
  	int fd, ret = 0;
  	char buf[BUFSIZ] = { 0 };
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	if ((fd = open(pfile, O_RDONLY, ALL_MODE)) == -1) {
  		return (-1);
  	}
  	if (read(fd, buf, sizeof (buf)) == -1) {
  		(void) fprintf(stderr, "read(%d, buf, %d) failed with errno "
  		    "%d\n", fd, sizeof (buf), errno);
  		return (1);
  	}
  	(void) close(fd);
  
  	return (ret);
  }
  
  static int
  do_write(const char *pfile)
  {
  	int fd, ret = 0;
  	char buf[BUFSIZ] = "call function do_write()";
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	if ((fd = open(pfile, O_WRONLY, ALL_MODE)) == -1) {
  		return (-1);
  	}
  	if (write(fd, buf, strlen(buf)) == -1) {
  		(void) fprintf(stderr, "write(%d, buf, %d) failed with errno "
  		    "%d\n", fd, strlen(buf), errno);
  		return (1);
  	}
  	(void) close(fd);
  
  	return (ret);
  }
  
  static int
  do_link(const char *pfile)
  {
  	int ret = 0;
  	char link_file[BUFSIZ] = { 0 };
  	char *dname;
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	/*
  	 * Figure out source file directory name, and create
  	 * the link file in the same directory.
  	 */
  	dname = dirname(strdup(pfile));
  	(void) snprintf(link_file, BUFSIZ, "%s/%s", dname, "link_file");
  
  	if (link(pfile, link_file) == -1) {
  		(void) fprintf(stderr, "link(%s, %s) failed with errno %d\n",
  		    pfile, link_file, errno);
  		free((void *)dirname);
  		return (1);
  	}
  
  	(void) unlink(link_file);
  	free((void *)dirname);
  	return (ret);
  }
  
  static int
  do_creat(const char *pfile)
  {
  	int fd, ret = 0;
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	if ((fd = creat(pfile, ALL_MODE)) == -1) {
  		(void) fprintf(stderr, "creat(%s, ALL_MODE) failed with errno "
  		    "%d\n", pfile, errno);
  		return (1);
  	}
  	(void) close(fd);
  
  	return (ret);
  }
  
  static int
  do_utime(const char *pfile)
  {
  	int ret = 0;
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	/*
  	 * Times of the file are set to the current time
  	 */
  	if (utime(pfile, NULL) == -1) {
  		(void) fprintf(stderr, "utime(%s, NULL) failed with errno "
  		    "%d\n", pfile, errno);
  		return (1);
  	}
  
  	return (ret);
  }
  
  static int
  do_chmod(const char *pfile)
  {
  	int ret = 0;
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	if (chmod(pfile, ALL_MODE) == -1) {
  		(void) fprintf(stderr, "chmod(%s, ALL_MODE) failed with "
  		    "errno %d\n", pfile, errno);
  		return (1);
  	}
  
  	return (ret);
  }
  
  static int
  do_chown(const char *pfile)
  {
  	int ret = 0;
  
  	if (pfile == NULL) {
  		return (-1);
  	}
  
  	if (chown(pfile, getuid(), getgid()) == -1) {
  		(void) fprintf(stderr, "chown(%s, %d, %d) failed with errno "
  		    "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
  		return (1);
  	}
  
  	return (ret);
  }
  
  static void
  cleanup(void)
  {
  	if ((strlen(tfile) != 0) && (access(tfile, F_OK) == 0)) {
  		(void) unlink(tfile);
  	}
  }
  
  static timetest_t timetest_table[] = {
  	{ ST_ATIME,	"st_atime",	do_read		},
  	{ ST_ATIME,	"st_atime",	do_utime	},
  	{ ST_MTIME,	"st_mtime",	do_creat	},
  	{ ST_MTIME,	"st_mtime",	do_write	},
  	{ ST_MTIME,	"st_mtime",	do_utime	},
  	{ ST_CTIME,	"st_ctime",	do_creat	},
  	{ ST_CTIME,	"st_ctime",	do_write	},
  	{ ST_CTIME,	"st_ctime",	do_chmod	},
  	{ ST_CTIME,	"st_ctime",	do_chown 	},
  	{ ST_CTIME,	"st_ctime",	do_link		},
  	{ ST_CTIME,	"st_ctime",	do_utime	},
  };
  
  #define	NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0]))
  
  /* ARGSUSED */
  int
  main(int argc, char *argv[])
  {
  	int i, ret, fd;
  	char *penv[] = {"TESTDIR", "TESTFILE0"};
  
  	(void) fprintf(stdout, "Verify [acm]time is modified appropriately.\n");
  	(void) atexit(cleanup);
  
  	/*
  	 * Get the environment variable values.
  	 */
  	for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
  		if ((penv[i] = getenv(penv[i])) == NULL) {
  			(void) fprintf(stderr, "getenv(penv[%d])\n", i);
  			return (1);
  		}
  	}
  	(void) snprintf(tfile, sizeof (tfile), "%s/%s", penv[0], penv[1]);
  
  	/*
  	 * If the test file is exists, remove it first.
  	 */
  	if (access(tfile, F_OK) == 0) {
  		(void) unlink(tfile);
  	}
  	ret = 0;
  	if ((fd = open(tfile, O_WRONLY | O_CREAT | O_TRUNC, ALL_MODE)) == -1) {
  		(void) fprintf(stderr, "open(%s) failed: %d\n", tfile, errno);
  		return (1);
  	}
  	(void) close(fd);
  
  	for (i = 0; i < NCOMMAND; i++) {
  		time_t t1, t2;
  
  		/*
  		 * Get original time before operating.
  		 */
  		ret = get_file_time(tfile, timetest_table[i].type, &t1);
  		if (ret != 0) {
  			(void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
  			    tfile, timetest_table[i].type, ret);
  			return (1);
  		}
  
  		/*
  		 * Sleep 2 seconds, then invoke command on given file
  		 */
  		(void) sleep(2);
  		(void) timetest_table[i].func(tfile);
  
  		/*
  		 * Get time after operating.
  		 */
  		ret = get_file_time(tfile, timetest_table[i].type, &t2);
  		if (ret != 0) {
  			(void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
  			    tfile, timetest_table[i].type, ret);
  			return (1);
  		}
  
  		if (t1 == t2) {
  			(void) fprintf(stderr, "%s: t1(%ld) == t2(%ld)\n",
  			    timetest_table[i].name, (long)t1, (long)t2);
  			return (1);
  		} else {
  			(void) fprintf(stderr, "%s: t1(%ld) != t2(%ld)\n",
  			    timetest_table[i].name, (long)t1, (long)t2);
  		}
  	}
  
  	(void) fprintf(stdout, "PASS\n");
  	return (0);
  }
  