summaryrefslogtreecommitdiffstats
path: root/vendor/rustix/tests/fs/y2038.rs
blob: 4299d709be235e651c5f0581b2dde73ee88cf1af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/// Test that we can set a file timestamp to a date past the year 2038 with
/// `utimensat` and read it back again.
///
/// See tests/time/y2038.rs for more information about y2038 testing.
#[cfg(not(all(target_env = "musl", target_pointer_width = "32")))]
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
#[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))]
#[test]
fn test_y2038_with_utimensat() {
    use rustix::fs::{
        cwd, fstat, openat, statat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps,
    };
    use std::convert::TryInto;

    let tmp = tempfile::tempdir().unwrap();
    let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap();

    let m_sec = 1_u64 << 32;
    let m_nsec = 17_u32;
    let a_sec = m_sec + 1;
    let a_nsec = m_nsec + 1;

    let timestamps = Timestamps {
        last_modification: Timespec {
            tv_sec: m_sec as _,
            tv_nsec: m_nsec as _,
        },
        last_access: Timespec {
            tv_sec: a_sec as _,
            tv_nsec: a_nsec as _,
        },
    };
    let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap();

    match utimensat(&dir, "foo", &timestamps, AtFlags::empty()) {
        Ok(()) => (),

        // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support
        // is not available in this version of the OS.
        #[cfg(target_pointer_width = "32")]
        Err(rustix::io::Errno::OVERFLOW) => return,

        Err(e) => panic!("unexpected error: {:?}", e),
    }

    // Use `statat` to read back the timestamp.
    let stat = statat(&dir, "foo", AtFlags::empty()).unwrap();

    assert_eq!(
        TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
        m_sec
    );
    assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
    assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
    assert!(
        TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
            || stat.st_atime_nsec as u32 >= a_nsec
    );

    // Now test the same thing, but with `fstat`.
    let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap();
    let stat = fstat(&file).unwrap();

    assert_eq!(
        TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
        m_sec
    );
    assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
    assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
    assert!(
        TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
            || stat.st_atime_nsec as u32 >= a_nsec
    );
}

/// Test that we can set a file timestamp to a date past the year 2038 with
/// `futimens` and read it back again.
///
/// See tests/time/y2038.rs for more information about y2038 testing.
#[cfg(not(all(target_env = "musl", target_pointer_width = "32")))]
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
#[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))]
#[test]
fn test_y2038_with_futimens() {
    use rustix::fs::{
        cwd, fstat, futimens, openat, statat, AtFlags, Mode, OFlags, Timespec, Timestamps,
    };
    use std::convert::TryInto;

    let tmp = tempfile::tempdir().unwrap();
    let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap();

    let m_sec = 1_u64 << 32;
    let m_nsec = 17_u32;
    let a_sec = m_sec + 1;
    let a_nsec = m_nsec + 1;

    let timestamps = Timestamps {
        last_modification: Timespec {
            tv_sec: m_sec as _,
            tv_nsec: m_nsec as _,
        },
        last_access: Timespec {
            tv_sec: a_sec as _,
            tv_nsec: a_nsec as _,
        },
    };
    let file = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap();

    match futimens(&file, &timestamps) {
        Ok(()) => (),

        // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support
        // is not available in this version of the OS.
        #[cfg(target_pointer_width = "32")]
        Err(rustix::io::Errno::OVERFLOW) => return,

        Err(e) => panic!("unexpected error: {:?}", e),
    }

    // Use `statat` to read back the timestamp.
    let stat = statat(&dir, "foo", AtFlags::empty()).unwrap();

    assert_eq!(TryInto::<u64>::try_into(stat.st_mtime).unwrap(), m_sec);
    assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
    assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() >= a_sec);
    assert!(
        TryInto::<u64>::try_into(stat.st_atime).unwrap() > a_sec
            || stat.st_atime_nsec as u32 >= a_nsec
    );

    // Now test the same thing, but with `fstat`.
    let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap();
    let stat = fstat(&file).unwrap();

    assert_eq!(
        TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64,
        m_sec
    );
    assert_eq!(stat.st_mtime_nsec as u32, m_nsec);
    assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec);
    assert!(
        TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec
            || stat.st_atime_nsec as u32 >= a_nsec
    );
}