summaryrefslogtreecommitdiff
path: root/usr/src/test/libc-tests
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/test/libc-tests')
-rw-r--r--usr/src/test/libc-tests/cfg/symbols/sys_time_h.cfg14
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run15
-rw-r--r--usr/src/test/libc-tests/tests/Makefile4
-rw-r--r--usr/src/test/libc-tests/tests/utimes.c521
4 files changed, 553 insertions, 1 deletions
diff --git a/usr/src/test/libc-tests/cfg/symbols/sys_time_h.cfg b/usr/src/test/libc-tests/cfg/symbols/sys_time_h.cfg
index afed10a6bb..8ca9bacb9d 100644
--- a/usr/src/test/libc-tests/cfg/symbols/sys_time_h.cfg
+++ b/usr/src/test/libc-tests/cfg/symbols/sys_time_h.cfg
@@ -11,6 +11,7 @@
#
# Copyright 2015 Garrett D'Amore <garrett@damore.org>
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
#
#
@@ -40,3 +41,16 @@ func | utimes |\
int |\
const char *; const struct timeval [2] |\
sys/time.h | -POSIX+ -XPG3+ SUS+
+
+func | lutimes |\
+ int |\
+ const char *; const struct timeval [2] |\
+ sys/time.h | -ALL
+
+func | futimes |\
+ int |\
+ int; const struct timeval [2] |\
+ sys/time.h | -ALL
+
+define | TIMEVAL_TO_TIMESPEC | | sys/time.h | -ALL
+define | TIMESPEC_TO_TIMEVAL | | sys/time.h | -ALL
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index c819079ef6..e8323a549b 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -13,6 +13,7 @@
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2019 Joyent, Inc.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
#
[DEFAULT]
@@ -45,7 +46,11 @@ outputdir = /var/tmp/test_results
[/opt/libc-tests/tests/random/chacha]
[/opt/libc-tests/tests/random/inz_child]
[/opt/libc-tests/tests/random/inz_inval]
+#
+# root privs required for mlock privileges
+#
[/opt/libc-tests/tests/random/inz_mlock]
+user = root
[/opt/libc-tests/tests/random/inz_region]
[/opt/libc-tests/tests/random/inz_split]
[/opt/libc-tests/tests/random/inz_split_vpp]
@@ -57,7 +62,11 @@ outputdir = /var/tmp/test_results
[/opt/libc-tests/tests/random/arc4random_preforkall]
[/opt/libc-tests/tests/random/arc4random_forksig]
[/opt/libc-tests/tests/random/arc4random_preforksig]
+#
+# root privs required for DTrace
+#
[/opt/libc-tests/tests/random/arc4key.ksh]
+user = root
[/opt/libc-tests/tests/regex/regex_test]
@@ -120,8 +129,14 @@ timeout = 600
[/opt/libc-tests/tests/timespec_get.64]
[/opt/libc-tests/tests/uchar.32]
[/opt/libc-tests/tests/uchar.64]
+[/opt/libc-tests/tests/utimes.32]
+[/opt/libc-tests/tests/utimes.64]
+#
+# root privs required for priority changes
+#
[/opt/libc-tests/tests/pthread_attr_get_np]
+user=root
[/opt/libc-tests/tests/symbols]
pre = setup
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index 63f108e83c..fcb903edeb 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -13,6 +13,7 @@
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2015 Garrett D'Amore <garrett@damore.org>
# Copyright 2019 Joyent, Inc.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
#
SUBDIRS = \
@@ -53,7 +54,8 @@ PROGS = \
wcsncasecmp \
wcsncasecmp-7344 \
wcsncasecmp-7350 \
- uchar
+ uchar \
+ utimes
SCRIPTS = \
quick_exit \
diff --git a/usr/src/test/libc-tests/tests/utimes.c b/usr/src/test/libc-tests/tests/utimes.c
new file mode 100644
index 0000000000..7585289fe6
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/utimes.c
@@ -0,0 +1,521 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * Test the implementation of the various *utimes() and *utimens() functions
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+timespec_t testtimes[] = {
+ {
+ .tv_sec = 1280793678,
+ .tv_nsec = 123456789
+ },
+ {
+ .tv_sec = 1492732800,
+ .tv_nsec = 17
+ },
+ {
+ .tv_sec = 1320796855,
+ .tv_nsec = 9
+ },
+ {
+ .tv_sec = 1498953611,
+ .tv_nsec = 987654321
+ }
+};
+
+enum ttype {
+ UTIMES,
+ LUTIMES,
+ FUTIMES,
+ FUTIMESAT,
+ FUTIMENS,
+ UTIMENSAT
+};
+
+static bool
+compare_times(struct stat *st, bool trunc, timespec_t *atim, timespec_t *mtim,
+ bool invert)
+{
+ bool ret = true;
+
+ if (st->st_atim.tv_sec != atim->tv_sec) {
+ ret = false;
+ } else if (st->st_atim.tv_nsec != (
+ trunc ? atim->tv_nsec / 1000 * 1000 : atim->tv_nsec)) {
+ ret = false;
+ } else if (st->st_mtim.tv_sec != mtim->tv_sec) {
+ ret = false;
+ } else if (st->st_mtim.tv_nsec != (
+ trunc ? mtim->tv_nsec / 1000 * 1000 : mtim->tv_nsec)) {
+ ret = false;
+ }
+
+ if ((!ret && !invert) || (ret && invert)) {
+ printf(" actual atime: %ld.%.9ld\n",
+ st->st_atim.tv_sec, st->st_atim.tv_nsec);
+ printf(" actual mtime: %ld.%.9ld\n",
+ st->st_mtim.tv_sec, st->st_mtim.tv_nsec);
+ }
+
+ return (ret);
+}
+
+static bool
+compare_filetime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
+ bool invert)
+{
+ struct stat st;
+
+ if (stat(path, &st) == -1)
+ err(EXIT_FAILURE, "stat %s", path);
+
+ return (compare_times(&st, trunc, atim, mtim, invert));
+}
+
+static bool
+compare_linktime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
+ bool invert)
+{
+ struct stat st;
+
+ if (lstat(path, &st) == -1)
+ err(EXIT_FAILURE, "lstat %s", path);
+
+ return (compare_times(&st, trunc, atim, mtim, invert));
+}
+
+static bool
+reset(char *path, timespec_t *atim, timespec_t *mtim)
+{
+ if (utimes(path, NULL) == -1)
+ err(EXIT_FAILURE, "utimes reset");
+ if (compare_filetime(path, true, atim, mtim, true)) {
+ warnx("reset failed");
+ return (false);
+ }
+ return (true);
+}
+
+static bool
+reset_link(char *lpath, timespec_t *atim, timespec_t *mtim)
+{
+ if (lutimes(lpath, NULL) == -1)
+ err(EXIT_FAILURE, "lutimes reset");
+ if (compare_linktime(lpath, true, atim, mtim, true)) {
+ warnx("link reset failed");
+ return (false);
+ }
+ return (true);
+}
+
+static bool
+runtest(enum ttype fn, char *dir, timespec_t *atim, timespec_t *mtim)
+{
+ char path[MAXPATHLEN + 1];
+ char lpath[MAXPATHLEN + 1];
+ struct timespec ts[2];
+ struct timeval tv[2];
+ int fd, lfd, dfd, ret = true;
+
+ ts[0] = *atim;
+ ts[1] = *mtim;
+ TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]);
+
+ if (snprintf(path, sizeof (path), "%s/file", dir) >= sizeof (path))
+ err(EXIT_FAILURE, "snprintf failed to build file path");
+
+ if ((fd = open(path, O_CREAT, 0644)) == -1)
+ err(EXIT_FAILURE, "open file %s", path);
+
+ if (snprintf(lpath, sizeof (lpath), "%s/link", dir) >= sizeof (path))
+ err(EXIT_FAILURE, "snprintf failed to build link path");
+
+ if (symlink(path, lpath) == -1)
+ err(EXIT_FAILURE, "link(%s)", lpath);
+
+ if ((lfd = open(lpath, O_RDWR)) == -1)
+ err(EXIT_FAILURE, "open link(%s)", lpath);
+
+ if ((dfd = open(dir, O_DIRECTORY|O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "open dir(%s)", dir);
+
+ switch (fn) {
+ case UTIMES:
+ printf("..... utimes()\n");
+
+ if (utimes(path, tv) == -1)
+ err(EXIT_FAILURE, "utimes(%s)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed on file");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (utimes(lpath, tv) == -1)
+ err(EXIT_FAILURE, "utimes(%s), link", lpath);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed on file through link");
+ ret = false;
+ }
+
+ break;
+
+ case LUTIMES:
+ printf("..... lutimes()\n");
+
+ /* Use lutimes() against a plain file */
+ if (lutimes(path, tv) == -1)
+ err(EXIT_FAILURE, "lutimes(%s)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed on file");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* Set the time on the link, not on the target */
+ if (lutimes(lpath, tv) == -1)
+ err(EXIT_FAILURE, "lutimes(%s)", lpath);
+ if (!compare_linktime(lpath, true, atim, mtim, false)) {
+ warnx("link time is incorrect");
+ ret = false;
+ }
+ if (compare_filetime(path, true, atim, mtim, true)) {
+ warnx("target time was updated incorrectly");
+ ret = false;
+ }
+
+ /* Reset the time on the path and link to the current time */
+ if (!reset(path, atim, mtim) || !reset_link(lpath, atim, mtim))
+ ret = false;
+
+ /* and modify the target */
+ if (utimes(path, tv) == -1)
+ err(EXIT_FAILURE, "utimes(%s)", path);
+ /* Now the target should match but the link should not */
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("target time is incorrect");
+ ret = false;
+ }
+ if (compare_linktime(lpath, true, atim, mtim, true)) {
+ warnx("link time was updated incorrectly");
+ ret = false;
+ }
+ break;
+
+ case FUTIMES:
+ printf("..... futimes()\n");
+
+ if (futimes(fd, tv) == -1)
+ err(EXIT_FAILURE, "futimes(%s)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed on file");
+ ret = false;
+ }
+
+ break;
+
+ case FUTIMESAT: {
+ int rfd;
+ printf("..... futimesat()\n");
+
+ /* NULL path, should modify the file for 'fd' */
+ if (futimesat(fd, NULL, tv) == -1)
+ err(EXIT_FAILURE, "futimesat(fd, NULL)");
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed with null path");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* random descriptor, FQ path, descriptor is ignored */
+ if ((rfd = open("/dev/null", O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "open(/dev/null)");
+ if (futimesat(rfd, path, tv) == -1)
+ err(EXIT_FAILURE, "futimesat(dnfd, %s)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed with random descriptor and fq path");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (futimesat(rfd, lpath, tv) == -1)
+ err(EXIT_FAILURE, "futimesat(dnfd, %s), link", lpath);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed through link with "
+ "random descriptor, fq path");
+ ret = false;
+ }
+
+ (void) close(rfd);
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* relative to a directory */
+ if (futimesat(dfd, "file", tv) == -1)
+ err(EXIT_FAILURE, "futimesat(dir, file)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed relative to a directory");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (futimesat(dfd, "link", tv) == -1)
+ err(EXIT_FAILURE, "futimesat(dir, link)");
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed through link relative to a directory");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* AT_FDCWD */
+ if (fchdir(dfd) == -1)
+ err(EXIT_FAILURE, "fchdir(%s)", dir);
+ if (futimesat(AT_FDCWD, "file", tv) == -1)
+ err(EXIT_FAILURE, "futimesat(AT_FDCWD, file)", path);
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed with AT_FDCWD relative");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (futimesat(AT_FDCWD, "link", tv) == -1)
+ err(EXIT_FAILURE, "futimesat(AT_FDCWD, link)");
+ if (!compare_filetime(path, true, atim, mtim, false)) {
+ warnx("failed through link with AT_FDCWD relative");
+ ret = false;
+ }
+
+ break;
+ }
+
+ case FUTIMENS:
+ printf("..... futimens()\n");
+ if (futimens(fd, ts) == -1)
+ err(EXIT_FAILURE, "futimesns(%s)", path);
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed with plain file fd");
+ ret = false;
+ }
+
+ break;
+
+ case UTIMENSAT: {
+ int rfd;
+
+ printf("..... utimensat()\n");
+
+ /* NULL path, expect EFAULT (cf. futimesat()) */
+ if (utimensat(fd, NULL, ts, 0) != -1 || errno != EFAULT) {
+ warnx("null path should return EFAULT but got %d/%s",
+ errno, strerror(errno));
+ ret = false;
+ }
+
+ /* random descriptor, FQ path, descriptor is ignored */
+ if ((rfd = open("/dev/null", O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "open(/dev/null)");
+ if (utimensat(rfd, path, ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(dnfd, %s)", path);
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed with random descriptor, fq path");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (utimensat(rfd, lpath, ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(dnfd, link %s)", lpath);
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed against link with random descriptor, "
+ "fq path");
+ ret = false;
+ }
+
+ (void) close(rfd);
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* relative to a directory */
+ if (utimensat(dfd, "file", ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(dir, file)", path);
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed relative to a directory");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (utimensat(dfd, "link", ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(dir, link)", path);
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed through link relative to a directory");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* AT_FDCWD */
+ if (fchdir(dfd) == -1)
+ err(EXIT_FAILURE, "fchdir(%s)", dir);
+ if (utimensat(AT_FDCWD, "file", ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(AT_FDCWD, file)");
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed with AT_FDCWD relative");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /* repeat against symbolic link path */
+ if (utimensat(AT_FDCWD, "link", ts, 0) == -1)
+ err(EXIT_FAILURE, "utimensat(AT_FDCWD, link)");
+ if (!compare_filetime(path, false, atim, mtim, false)) {
+ warnx("failed through link with AT_FDCWD relative");
+ ret = false;
+ }
+
+ if (!reset(path, atim, mtim))
+ ret = false;
+
+ /*
+ * Check that none of the above operations have changed the
+ * timestamp on the link.
+ */
+ if (compare_linktime(lpath, true, atim, mtim, true)) {
+ warnx("link timestamp was unexpectedly modified");
+ ret = false;
+ }
+
+ /* Set the time on the link, not on the target */
+ if (utimensat(AT_FDCWD, "link", ts, AT_SYMLINK_NOFOLLOW) == -1)
+ err(EXIT_FAILURE, "utimensat(AT_FDCWD, lflag)");
+ if (!compare_linktime(lpath, false, atim, mtim, false)) {
+ warnx("link time is incorrect");
+ ret = false;
+ }
+ if (compare_filetime(path, false, atim, mtim, true)) {
+ warnx("target time was updated incorrectly");
+ ret = false;
+ }
+ }
+ break;
+ }
+
+ (void) close(dfd);
+ (void) close(lfd);
+ (void) close(fd);
+
+ if (unlink(lpath) == -1)
+ err(EXIT_FAILURE, "unlink(%s)", lpath);
+ if (unlink(path) == -1)
+ err(EXIT_FAILURE, "unlink(%s)", path);
+
+ if (!ret)
+ warnx("Test failed");
+
+ return (ret);
+}
+
+static bool
+runtests(char *dir, timespec_t *atim, timespec_t *mtim)
+{
+ bool ret = true;
+
+ printf("Testing:\n... atime: %ld.%.9ld\n... mtime: %ld.%.9ld\n",
+ atim->tv_sec, atim->tv_nsec, mtim->tv_sec, mtim->tv_nsec);
+
+ if (!runtest(UTIMES, dir, atim, mtim))
+ ret = false;
+ if (!runtest(LUTIMES, dir, atim, mtim))
+ ret = false;
+ if (!runtest(FUTIMES, dir, atim, mtim))
+ ret = false;
+ if (!runtest(FUTIMESAT, dir, atim, mtim))
+ ret = false;
+ if (!runtest(FUTIMENS, dir, atim, mtim))
+ ret = false;
+ if (!runtest(UTIMENSAT, dir, atim, mtim))
+ ret = false;
+
+ return (ret);
+}
+
+int
+main(void)
+{
+ char dir[] = "/tmp/utimes.XXXXXX";
+ int ret = EXIT_SUCCESS;
+ int i;
+
+ if (mkdtemp(dir) == NULL)
+ err(EXIT_FAILURE, "failed to create temporary directory");
+
+ for (i = 0; i < ARRAY_SIZE(testtimes); i += 2) {
+ if (!runtests(dir, &testtimes[i], &testtimes[i + 1]))
+ ret = EXIT_FAILURE;
+ }
+
+ /*
+ * Some tests will have changed directory into 'dir' to test the
+ * AT_FDCWD case. Change back to / to avoid EBUSY when removing dir.
+ */
+ if (chdir("/") == -1)
+ err(EXIT_FAILURE, "chdir(/)");
+ if (rmdir(dir) == -1)
+ err(EXIT_FAILURE, "rmdir %s", dir);
+
+ return (ret);
+}