fuchsia_fatfs/
util.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use chrono::offset::TimeZone;
6use chrono::Local;
7use fatfs::{Date, DateTime, FatfsError, Time};
8use std::io::{self, ErrorKind};
9use zx::Status;
10
11/// Returns the equivalent of the given DOS time as ns past the unix epoch.
12pub fn dos_to_unix_time(dos_time: DateTime) -> u64 {
13    let datetime = chrono::DateTime::<Local>::from(dos_time);
14    datetime.timestamp_nanos_opt().unwrap() as u64
15}
16
17/// Returns the equivalent of the given DOS time as ns past the unix epoch.
18pub fn dos_date_to_unix_time(dos_date: Date) -> u64 {
19    let datetime = chrono::DateTime::<Local>::from(DateTime {
20        date: dos_date,
21        time: Time { hour: 0, min: 0, sec: 0, millis: 0 },
22    });
23    datetime.timestamp_nanos_opt().unwrap() as u64
24}
25
26/// Returns the given unix timestamp in ns as a FAT-compatible DateTime.
27pub fn unix_to_dos_time(timestamp: u64) -> DateTime {
28    DateTime::from(Local.timestamp_nanos(timestamp as i64))
29}
30
31pub fn fatfs_error_to_status(error: io::Error) -> Status {
32    match error.kind() {
33        ErrorKind::AddrInUse => Status::ADDRESS_IN_USE,
34        ErrorKind::AddrNotAvailable => Status::UNAVAILABLE,
35        ErrorKind::AlreadyExists => Status::ALREADY_EXISTS,
36        ErrorKind::BrokenPipe => Status::PEER_CLOSED,
37        ErrorKind::ConnectionAborted => Status::CONNECTION_ABORTED,
38        ErrorKind::ConnectionRefused => Status::CONNECTION_REFUSED,
39        ErrorKind::ConnectionReset => Status::CONNECTION_RESET,
40        ErrorKind::Interrupted => Status::INTERRUPTED_RETRY,
41        ErrorKind::InvalidData => Status::IO_INVALID,
42        ErrorKind::InvalidInput => Status::INVALID_ARGS,
43        ErrorKind::NotConnected => Status::NOT_CONNECTED,
44        ErrorKind::NotFound => Status::NOT_FOUND,
45        ErrorKind::PermissionDenied => Status::ACCESS_DENIED,
46        ErrorKind::TimedOut => Status::TIMED_OUT,
47        ErrorKind::UnexpectedEof => Status::BUFFER_TOO_SMALL,
48        ErrorKind::WouldBlock => Status::BAD_STATE,
49        ErrorKind::WriteZero => Status::NO_SPACE,
50        ErrorKind::Other => {
51            error
52                .into_inner()
53                .and_then(|b| b.downcast::<FatfsError>().ok())
54                .map(|b| {
55                    match *b {
56                        // errors caused in boot_sector.rs
57                        FatfsError::UnknownVersion => Status::NOT_SUPPORTED,
58                        FatfsError::InvalidBootSectorSig => Status::NOT_SUPPORTED,
59                        FatfsError::VolumeTooSmall => Status::NO_SPACE,
60                        // errors caused in dir.rs:
61                        FatfsError::IsDirectory => Status::NOT_FILE,
62                        FatfsError::NotDirectory => Status::NOT_DIR,
63                        FatfsError::DirectoryNotEmpty => Status::NOT_EMPTY,
64                        FatfsError::FileNameTooLong => Status::BAD_PATH,
65                        FatfsError::FileNameEmpty => Status::BAD_PATH,
66                        FatfsError::FileNameBadCharacter => Status::BAD_PATH,
67                        // errors caused in fs.rs
68                        FatfsError::TooManySectors => Status::OUT_OF_RANGE,
69                        // errors caused in table.rs
70                        FatfsError::NoSpace => Status::NO_SPACE,
71                        // Other errors (which come from boot_sector.rs and fs.rs) indicate
72                        // that the filesystem is corrupted or invalid in some way.
73                        // We don't enumerate them here, because there's a lot of them.
74                        _ => Status::INVALID_ARGS,
75                    }
76                })
77                .unwrap_or(Status::IO)
78        }
79        _ => Status::IO,
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use fatfs::{Date, Time};
87
88    fn dos_datetime(
89        year: u16,
90        month: u16,
91        day: u16,
92        hour: u16,
93        min: u16,
94        sec: u16,
95        millis: u16,
96    ) -> DateTime {
97        DateTime { date: Date { year, month, day }, time: Time { hour, min, sec, millis } }
98    }
99
100    const NS_PER_MS: u64 = 1_000_000;
101
102    fn ms_to_ns(ts: u64) -> u64 {
103        ts * NS_PER_MS
104    }
105
106    #[fuchsia::test]
107    fn test_dos_to_unix_time() {
108        let earliest_time = dos_datetime(1980, 1, 1, 0, 0, 0, 0);
109        assert_eq!(dos_to_unix_time(earliest_time), ms_to_ns(315532800000));
110
111        let latest_time = dos_datetime(2107, 12, 31, 23, 59, 59, 999);
112        assert_eq!(dos_to_unix_time(latest_time), ms_to_ns(4354819199999));
113
114        let time = dos_datetime(2038, 1, 19, 3, 14, 7, 0);
115        assert_eq!(dos_to_unix_time(time), ms_to_ns(2147483647000));
116    }
117
118    #[fuchsia::test]
119    fn test_unix_to_dos_time() {
120        let earliest_time = dos_datetime(1980, 1, 1, 0, 0, 0, 0);
121        assert_eq!(earliest_time, unix_to_dos_time(ms_to_ns(315532800000)));
122
123        let latest_time = dos_datetime(2107, 12, 31, 23, 59, 59, 999);
124        assert_eq!(latest_time, unix_to_dos_time(ms_to_ns(4354819199999)));
125
126        let time = dos_datetime(2038, 1, 19, 3, 14, 7, 0);
127        assert_eq!(time, unix_to_dos_time(ms_to_ns(2147483647000)));
128    }
129}