]>
Commit | Line | Data |
---|---|---|
cb323159 A |
1 | #include <darwintest.h> |
2 | #include <stdlib.h> | |
3 | #include <sys/stat.h> | |
4 | #include <sys/syslimits.h> | |
5 | #include <sys/time.h> | |
6 | #include <time.h> | |
7 | #include <unistd.h> | |
8 | ||
9 | T_GLOBAL_META(T_META_CHECK_LEAKS(false)); | |
10 | ||
11 | T_DECL(settimeofday, "check setting and getting time of day", | |
12 | T_META_ASROOT(true)) | |
13 | { | |
14 | struct timeval origtime = {}; | |
15 | struct timezone origtz = {}; | |
16 | int ret = gettimeofday(&origtime, &origtz); | |
17 | T_ASSERT_POSIX_SUCCESS(ret, "get current time with gettimeofday(2)"); | |
18 | ||
19 | #if TARGET_OS_BRIDGE | |
20 | /* | |
21 | * bridgeOS is not allowed to set the time -- only the macOS side can. | |
22 | */ | |
23 | T_SKIP("bridgeOS is not allowed to call settimeofday(2)"); | |
24 | #endif /* TARGET_OS_BRIDGE */ | |
25 | ||
26 | struct timeval newtime = {}; | |
27 | newtime = origtime; | |
28 | newtime.tv_sec -= 60; | |
29 | ret = settimeofday(&newtime, NULL); | |
30 | T_ASSERT_POSIX_SUCCESS(ret, | |
31 | "set time back 60 seconds with settimeofday(2)"); | |
32 | ||
33 | ret = gettimeofday(&newtime, NULL); | |
34 | T_ASSERT_POSIX_SUCCESS(ret, "get new time with gettimeofday(2)"); | |
35 | ||
36 | T_ASSERT_GT(origtime.tv_sec, newtime.tv_sec, | |
37 | "new time should be before original time"); | |
38 | ||
39 | newtime = origtime; | |
40 | newtime.tv_sec += 1; | |
41 | ret = settimeofday(&newtime, NULL); | |
42 | T_ASSERT_POSIX_SUCCESS(ret, | |
43 | "set time close to original value with gettimeofday(2)"); | |
44 | } | |
45 | ||
46 | static char tmppath[PATH_MAX] = ""; | |
47 | ||
48 | static void | |
49 | cleanup_tmpfile(void) | |
50 | { | |
51 | if (tmppath[0] != '\0') { | |
52 | unlink(tmppath); | |
53 | } | |
54 | } | |
55 | ||
56 | static int | |
57 | create_tmpfile(void) | |
58 | { | |
59 | const char *tmpdir = getenv("TMPDIR"); | |
60 | strlcat(tmppath, tmpdir ? tmpdir : "/tmp", sizeof(tmppath)); | |
61 | strlcat(tmppath, "xnu_quick_test.XXXXX", sizeof(tmppath)); | |
62 | int fd = mkstemp(tmppath); | |
63 | T_ASSERT_POSIX_SUCCESS(fd, "created temporary file at %s", tmppath); | |
64 | T_ATEND(cleanup_tmpfile); | |
65 | return fd; | |
66 | } | |
67 | ||
68 | T_DECL(futimes, "check that futimes updates file times", | |
69 | T_META_RUN_CONCURRENTLY(true)) | |
70 | { | |
71 | int tmpfd = create_tmpfile(); | |
72 | ||
73 | struct stat stbuf = {}; | |
74 | int ret = fstat(tmpfd, &stbuf); | |
75 | T_ASSERT_POSIX_SUCCESS(ret, "get file metadata with fstat(2)"); | |
76 | struct timeval amtimes[2] = {}; | |
77 | TIMESPEC_TO_TIMEVAL(&amtimes[0], &stbuf.st_atimespec); | |
78 | TIMESPEC_TO_TIMEVAL(&amtimes[1], &stbuf.st_mtimespec); | |
79 | ||
80 | amtimes[0].tv_sec -= 120; | |
81 | amtimes[1].tv_sec -= 120; | |
82 | ||
83 | ret = futimes(tmpfd, amtimes); | |
84 | T_ASSERT_POSIX_SUCCESS(ret, "update file times with utimes(2)"); | |
85 | ||
86 | ret = fstat(tmpfd, &stbuf); | |
87 | T_ASSERT_POSIX_SUCCESS(ret, "get file metadata after update with fstat(2)"); | |
88 | struct timeval newamtimes[2] = {}; | |
89 | TIMESPEC_TO_TIMEVAL(&newamtimes[0], &stbuf.st_atimespec); | |
90 | TIMESPEC_TO_TIMEVAL(&newamtimes[1], &stbuf.st_mtimespec); | |
91 | ||
92 | /* | |
93 | * Reading the metadata shouldn't count as an access. | |
94 | */ | |
95 | T_ASSERT_EQ(amtimes[0].tv_sec, newamtimes[0].tv_sec, | |
96 | "access time matches what was set"); | |
97 | T_ASSERT_EQ(amtimes[1].tv_sec, newamtimes[1].tv_sec, | |
98 | "modification time matches what was set"); | |
99 | } |