blob: 4a4b1e756d8ce8f8b4d11da4017fe93935b1f9eb [file]
/*
* 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;
}