| /* |
| * Copyright 2021 The Emscripten Authors. All rights reserved. |
| * Emscripten is available under two separate licenses, the MIT license and the |
| * University of Illinois/NCSA Open Source License. Both these licenses can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <time.h> |
| #include <sys/stat.h> |
| #include <utime.h> |
| |
| |
| void create_file(const char *path, const char *buffer, int mode) { |
| int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); |
| assert(fd >= 0); |
| |
| int err = write(fd, buffer, sizeof(char) * strlen(buffer)); |
| assert(err == (sizeof(char) * strlen(buffer))); |
| |
| close(fd); |
| } |
| |
| void setup() { |
| mkdir("folder", 0777); |
| create_file("folder/file", "dracones", 0777); |
| symlink("file", "folder/file-link"); |
| } |
| |
| void check_times(int fd, struct timespec* expected, int tolerance) { |
| struct stat s; |
| int rtn = fstatat(fd, "", &s, AT_EMPTY_PATH); |
| assert(rtn == 0); |
| printf("atime: tv_sec=%lld tv_nsec=%ld\n", s.st_atim.tv_sec, s.st_atim.tv_nsec); |
| printf("mtime: tv_sec=%lld tv_nsec=%ld\n", s.st_mtim.tv_sec, s.st_mtim.tv_nsec); |
| printf("expected atime: tv_sec=%lld tv_nsec=%ld\n", expected[0].tv_sec, expected[0].tv_nsec); |
| printf("expected mtime: tv_sec=%lld tv_nsec=%ld\n", expected[1].tv_sec, expected[1].tv_nsec); |
| if (tolerance) { |
| assert(llabs(expected[0].tv_sec - s.st_atim.tv_sec) <= tolerance); |
| assert(llabs(expected[1].tv_sec - s.st_mtim.tv_sec) <= tolerance); |
| } else { |
| assert(expected[0].tv_sec == s.st_atim.tv_sec); |
| assert(expected[1].tv_sec == s.st_mtim.tv_sec); |
| } |
| } |
| |
| void test() { |
| int err; |
| struct stat s; |
| |
| int fd = open("folder/file", O_RDONLY); |
| assert(fd >= 0); |
| |
| // empty path - with AT_EMPTY_PATH (stat's the fd itself, a file) |
| memset(&s, 0, sizeof(s)); |
| err = fstatat(fd, "", &s, AT_EMPTY_PATH); |
| assert(!err); |
| assert(s.st_dev); |
| assert(s.st_ino); |
| assert(S_ISREG(s.st_mode)); |
| assert(s.st_nlink); |
| assert(s.st_rdev == 0); |
| assert(s.st_size == 8); |
| assert(s.st_ctime); |
| #if defined(__EMSCRIPTEN__) && !defined(NODERAWFS) && !defined(NODEFS) |
| assert(s.st_blksize == 4096); |
| assert(s.st_blocks == 1); |
| #endif |
| |
| struct timespec times[2]; |
| times[0].tv_sec = s.st_atim.tv_sec; |
| times[0].tv_nsec = s.st_atim.tv_nsec; |
| times[1].tv_sec = s.st_mtim.tv_sec; |
| times[1].tv_nsec = s.st_mtim.tv_nsec; |
| |
| // set the timestamp to the current value |
| err = futimens(fd, times); |
| assert(!err); |
| check_times(fd, times, 0); |
| |
| // UTIME_OMIT means that the timeval is ignored, so |
| // this call should do nothing. |
| printf("check double UTIME_OMIT...\n"); |
| struct timespec newtimes[2]; |
| newtimes[0].tv_sec = 42; |
| newtimes[0].tv_nsec = UTIME_OMIT; |
| newtimes[1].tv_sec = 42; |
| newtimes[1].tv_nsec = UTIME_OMIT; |
| err = futimens(fd, newtimes); |
| assert(!err); |
| check_times(fd, times, 0); |
| |
| // Setting just one of the two times to UTIME_OMIT means |
| // the other should be honored. |
| printf("check single UTIME_OMIT...\n"); |
| newtimes[0].tv_sec = 41; |
| newtimes[0].tv_nsec = UTIME_OMIT; |
| newtimes[1].tv_sec = 42; |
| newtimes[1].tv_nsec = 88; |
| err = futimens(fd, newtimes); |
| assert(!err); |
| |
| times[1].tv_sec = 42; |
| times[1].tv_nsec = 88; |
| check_times(fd, times, 0); |
| |
| // UTIME_NOW means use the current date and ignore the seconds value |
| printf("check single UTIME_NOW...\n"); |
| newtimes[0].tv_sec = 99; |
| newtimes[0].tv_nsec = UTIME_NOW; |
| newtimes[1].tv_sec = 99; |
| newtimes[1].tv_nsec = UTIME_NOW; |
| err = futimens(fd, newtimes); |
| assert(!err); |
| |
| struct timespec now; |
| err = clock_gettime(CLOCK_REALTIME, &now); |
| printf("now: %lld %ld\n", now.tv_sec, now.tv_nsec); |
| assert(!err); |
| times[0].tv_sec = now.tv_sec; |
| times[0].tv_nsec = now.tv_nsec; |
| times[1].tv_sec = now.tv_sec; |
| times[1].tv_nsec = now.tv_nsec; |
| check_times(fd, times, 1); |
| |
| printf("check setting time to 0...\n"); |
| struct utimbuf tb = {0}; |
| utime("folder/file", &tb); |
| times[0].tv_sec = 0; |
| times[0].tv_nsec = 0; |
| times[1].tv_sec = 0; |
| times[1].tv_nsec = 0; |
| check_times(fd, times, 0); |
| |
| close(fd); |
| |
| // TODO: |
| // printf("check utime on empty path...\n"); |
| // assert(utime("", &tb) == -1); |
| // assert(errno == ENOENT); |
| |
| puts("done"); |
| } |
| |
| int main() { |
| setup(); |
| test(); |
| return 0; |
| } |