| /* |
| * 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 <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #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); |
| } |