/* * 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 https://opensource.org/licenses/CDDL-1.0. * 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. */ #include #include #ifndef __FreeBSD__ #include #endif #include #include #include #include #include #include #include #include #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; const char *name; int (*func)(const char *pfile); } timetest_t; static char tfile[BUFSIZ] = { 0 }; /* * 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 ssize_t get_dirnamelen(const char *path) { const char *end = strrchr(path, '/'); return (end ? end - path : -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, %zd) failed with errno " "%d\n", fd, sizeof (buf), errno); (void) close(fd); 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, (int)strlen(buf), errno); (void) close(fd); return (1); } (void) close(fd); return (ret); } static int do_link(const char *pfile) { int ret = 0; char link_file[BUFSIZ + 16] = { 0 }; if (pfile == NULL) { return (-1); } /* * Figure out source file directory name, and create * the link file in the same directory. */ (void) snprintf(link_file, sizeof (link_file), "%.*s/%s", (int)get_dirnamelen(pfile), pfile, "link_file"); if (link(pfile, link_file) == -1) { (void) fprintf(stderr, "link(%s, %s) failed with errno %d\n", pfile, link_file, errno); return (1); } (void) unlink(link_file); 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); } #ifndef __FreeBSD__ static int do_xattr(const char *pfile) { int ret = 0; const char *value = "user.value"; if (pfile == NULL) { return (-1); } if (setxattr(pfile, "user.x", value, strlen(value), 0) == -1) { (void) fprintf(stderr, "setxattr(%s, %d, %d) failed with errno " "%d\n", pfile, (int)getuid(), (int)getgid(), errno); return (1); } return (ret); } #endif 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 }, #ifndef __FreeBSD__ { ST_CTIME, "st_ctime", do_xattr }, #endif }; #define NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0])) int main(void) { int i, ret, fd; const char *penv[] = {"TESTDIR", "TESTFILE0"}; (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 exists, remove it first. */ if (access(tfile, F_OK) == 0) { (void) unlink(tfile); } 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); 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); } /* * Ideally, time change would be exactly two seconds, but allow * a little slack in case of scheduling delays or similar. */ long delta = (long)t2 - (long)t1; if (delta < 2 || delta > 4) { (void) fprintf(stderr, "%s: BAD time change: t1(%ld), t2(%ld)\n", timetest_table[i].name, (long)t1, (long)t2); return (1); } else { (void) fprintf(stderr, "%s: good time change: t1(%ld), t2(%ld)\n", timetest_table[i].name, (long)t1, (long)t2); } } return (0); }