/* * Copyright (c) 2019 Darren Tucker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #define TMPFILE "utimensat.tmp" #define TMPFILE2 "utimensat.tmp2" #ifndef AT_SYMLINK_NOFOLLOW # define AT_SYMLINK_NOFOLLOW 0x80000000 #endif int utimensat(int, const char *, const struct timespec[2], int); static void cleanup(void) { (void)unlink(TMPFILE); (void)unlink(TMPFILE2); } static void fail(char *msg, long expect, long got) { int saved_errno = errno; if (expect == got && got == 0) fprintf(stderr, "utimensat: %s: %s\n", msg, strerror(saved_errno)); else fprintf(stderr, "utimensat: %s: expected %ld got %ld\n", msg, expect, got); cleanup(); exit(1); } int main(void) { int fd; struct stat sb; struct timespec ts[2]; cleanup(); if ((fd = open(TMPFILE, O_CREAT, 0600)) == -1) fail("open", 0, 0); close(fd); ts[0].tv_sec = 12345678; ts[0].tv_nsec = 23456789; ts[1].tv_sec = 34567890; ts[1].tv_nsec = 45678901; if (utimensat(AT_FDCWD, TMPFILE, ts, AT_SYMLINK_NOFOLLOW) == -1) fail("utimensat", 0, 0); if (stat(TMPFILE, &sb) == -1) fail("stat", 0, 0 ); if (sb.st_atime != 12345678) fail("st_atime", 0, 0 ); if (sb.st_mtime != 34567890) fail("st_mtime", 0, 0 ); #if 0 /* * Results expected to be rounded to the nearest microsecond. * Depends on timestamp precision in kernel and filesystem so * disabled by default. */ if (sb.st_atim.tv_nsec != 23456000) fail("atim.tv_nsec", 23456000, sb.st_atim.tv_nsec); if (sb.st_mtim.tv_nsec != 45678000) fail("mtim.tv_nsec", 45678000, sb.st_mtim.tv_nsec); #endif /* * POSIX specifies that when given a symlink, AT_SYMLINK_NOFOLLOW * should update the symlink and not the destination. The compat * code doesn't have a way to do this, so where possible it fails * with instead of following a symlink when explicitly asked not to. * Here we just test that it does not update the destination. */ if (rename(TMPFILE, TMPFILE2) == -1) fail("rename", 0, 0); if (symlink(TMPFILE2, TMPFILE) == -1) fail("symlink", 0, 0); ts[0].tv_sec = 11223344; ts[1].tv_sec = 55667788; (void)utimensat(AT_FDCWD, TMPFILE, ts, AT_SYMLINK_NOFOLLOW); if (stat(TMPFILE2, &sb) == -1) fail("stat", 0, 0 ); if (sb.st_atime == 11223344) fail("utimensat symlink st_atime", 0, 0 ); if (sb.st_mtime == 55667788) fail("utimensat symlink st_mtime", 0, 0 ); cleanup(); exit(0); }