1 /// Test that we can set a file timestamp to a date past the year 2038 with
2 /// `utimensat` and read it back again.
3 ///
4 /// See tests/time/y2038.rs for more information about y2038 testing.
5 #[cfg(not(all(target_env = "musl", target_pointer_width = "32")))]
6 #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
7 #[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))]
8 #[test]
test_y2038_with_utimensat()9 fn test_y2038_with_utimensat() {
10 use rustix::fs::{
11 cwd, fstat, openat, statat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps,
12 };
13 use std::convert::TryInto;
14
15 let tmp = tempfile::tempdir().unwrap();
16 let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap();
17
18 let m_sec = 1_u64 << 32;
19 let m_nsec = 17_u32;
20 let a_sec = m_sec + 1;
21 let a_nsec = m_nsec + 1;
22
23 let timestamps = Timestamps {
24 last_modification: Timespec {
25 tv_sec: m_sec as _,
26 tv_nsec: m_nsec as _,
27 },
28 last_access: Timespec {
29 tv_sec: a_sec as _,
30 tv_nsec: a_nsec as _,
31 },
32 };
33 let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap();
34
35 match utimensat(&dir, "foo", ×tamps, AtFlags::empty()) {
36 Ok(()) => (),
37
38 // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support
39 // is not available in this version of the OS.
40 #[cfg(target_pointer_width = "32")]
41 Err(rustix::io::Errno::OVERFLOW) => return,
42
43 Err(err) => panic!("unexpected error: {:?}", err),
44 }
45
46 // Use `statat` to read back the timestamp.
47 let stat = statat(&dir, "foo", AtFlags::empty()).unwrap();
48
49 assert_eq!(
50 TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
51 m_sec
52 );
53
54 #[cfg(not(target_os = "netbsd"))]
55 assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
56 #[cfg(target_os = "netbsd")]
57 assert_eq!(stat.st_mtimensec as u32, m_nsec);
58
59 assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
60
61 #[cfg(not(target_os = "netbsd"))]
62 assert!(
63 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
64 || stat.st_atime_nsec as u32 >= a_nsec
65 );
66 #[cfg(target_os = "netbsd")]
67 assert!(
68 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
69 || stat.st_atimensec as u32 >= a_nsec
70 );
71
72 // Now test the same thing, but with `fstat`.
73 let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap();
74 let stat = fstat(&file).unwrap();
75
76 assert_eq!(
77 TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
78 m_sec
79 );
80
81 #[cfg(not(target_os = "netbsd"))]
82 assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
83 #[cfg(target_os = "netbsd")]
84 assert_eq!(stat.st_mtimensec as u32, m_nsec);
85
86 assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
87
88 #[cfg(not(target_os = "netbsd"))]
89 assert!(
90 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
91 || stat.st_atime_nsec as u32 >= a_nsec
92 );
93 #[cfg(target_os = "netbsd")]
94 assert!(
95 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
96 || stat.st_atimensec as u32 >= a_nsec
97 );
98 }
99
100 /// Test that we can set a file timestamp to a date past the year 2038 with
101 /// `futimens` and read it back again.
102 ///
103 /// See tests/time/y2038.rs for more information about y2038 testing.
104 #[cfg(not(all(target_env = "musl", target_pointer_width = "32")))]
105 #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
106 #[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))]
107 #[test]
test_y2038_with_futimens()108 fn test_y2038_with_futimens() {
109 use rustix::fs::{
110 cwd, fstat, futimens, openat, statat, AtFlags, Mode, OFlags, Timespec, Timestamps,
111 };
112 use std::convert::TryInto;
113
114 let tmp = tempfile::tempdir().unwrap();
115 let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap();
116
117 let m_sec = 1_u64 << 32;
118 let m_nsec = 17_u32;
119 let a_sec = m_sec + 1;
120 let a_nsec = m_nsec + 1;
121
122 let timestamps = Timestamps {
123 last_modification: Timespec {
124 tv_sec: m_sec as _,
125 tv_nsec: m_nsec as _,
126 },
127 last_access: Timespec {
128 tv_sec: a_sec as _,
129 tv_nsec: a_nsec as _,
130 },
131 };
132 let file = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap();
133
134 match futimens(&file, ×tamps) {
135 Ok(()) => (),
136
137 // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support
138 // is not available in this version of the OS.
139 #[cfg(target_pointer_width = "32")]
140 Err(rustix::io::Errno::OVERFLOW) => return,
141
142 Err(err) => panic!("unexpected error: {:?}", err),
143 }
144
145 // Use `statat` to read back the timestamp.
146 let stat = statat(&dir, "foo", AtFlags::empty()).unwrap();
147
148 assert_eq!(TryInto::<u64>::try_into(stat.st_mtime).unwrap(), m_sec);
149
150 #[cfg(not(target_os = "netbsd"))]
151 assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
152 #[cfg(target_os = "netbsd")]
153 assert_eq!(stat.st_mtimensec as u32, m_nsec);
154
155 assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() >= a_sec);
156
157 #[cfg(not(target_os = "netbsd"))]
158 assert!(
159 TryInto::<u64>::try_into(stat.st_atime).unwrap() > a_sec
160 || stat.st_atime_nsec as u32 >= a_nsec
161 );
162 #[cfg(target_os = "netbsd")]
163 assert!(
164 TryInto::<u64>::try_into(stat.st_atime).unwrap() > a_sec
165 || stat.st_atimensec as u32 >= a_nsec
166 );
167
168 // Now test the same thing, but with `fstat`.
169 let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap();
170 let stat = fstat(&file).unwrap();
171
172 assert_eq!(
173 TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
174 m_sec
175 );
176
177 #[cfg(not(target_os = "netbsd"))]
178 assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
179 #[cfg(target_os = "netbsd")]
180 assert_eq!(stat.st_mtimensec as u32, m_nsec);
181
182 assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
183
184 #[cfg(not(target_os = "netbsd"))]
185 assert!(
186 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
187 || stat.st_atime_nsec as u32 >= a_nsec
188 );
189 #[cfg(target_os = "netbsd")]
190 assert!(
191 TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
192 || stat.st_atimensec as u32 >= a_nsec
193 );
194 }
195