xref: /third_party/rust/crates/rustix/tests/fs/y2038.rs (revision b8a62b91)
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]
9fn 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", &timestamps, 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]
108fn 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, &timestamps) {
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