rc_zip/
error.rs

1//! All error types used in this crate
2
3use std::{error, fmt, io};
4
5use crate::parse::Method;
6
7use super::encoding;
8
9/// An alias for `Result<T, rc_zip::Error>`
10pub type Result<T> = std::result::Result<T, Error>;
11
12/// Any zip-related error, from invalid archives to encoding problems.
13#[derive(Debug)]
14pub enum Error {
15    /// Not a valid zip file, or a variant that is unsupported.
16    Format(FormatError),
17
18    /// Something is not supported by this crate
19    Unsupported(UnsupportedError),
20
21    /// Invalid UTF-8, Shift-JIS, or any problem encountered while decoding text in general.
22    Encoding(encoding::DecodingError),
23
24    /// I/O-related error
25    IO(io::Error),
26
27    /// Decompression-related error
28    Decompression {
29        /// The compression method that failed
30        method: Method,
31        /// Additional information
32        msg: String,
33    },
34
35    /// Could not read as a zip because size could not be determined
36    UnknownSize,
37}
38
39impl Error {
40    /// Create a new error indicating that the given method is not supported.
41    pub fn method_not_supported(method: Method) -> Self {
42        Self::Unsupported(UnsupportedError::MethodNotSupported(method))
43    }
44
45    /// Create a new error indicating that the given method is not enabled.
46    pub fn method_not_enabled(method: Method) -> Self {
47        Self::Unsupported(UnsupportedError::MethodNotEnabled(method))
48    }
49}
50
51impl From<FormatError> for Error {
52    fn from(fmt_err: FormatError) -> Self {
53        Self::Format(fmt_err)
54    }
55}
56
57impl From<UnsupportedError> for Error {
58    fn from(unsupported: UnsupportedError) -> Self {
59        Self::Unsupported(unsupported)
60    }
61}
62
63impl From<encoding::DecodingError> for Error {
64    fn from(enc: encoding::DecodingError) -> Self {
65        Self::Encoding(enc)
66    }
67}
68
69impl From<io::Error> for Error {
70    fn from(io: io::Error) -> Self {
71        Self::IO(io)
72    }
73}
74
75impl From<Error> for io::Error {
76    fn from(e: Error) -> Self {
77        match e {
78            Error::IO(e) => e,
79            e => io::Error::other(e),
80        }
81    }
82}
83
84impl fmt::Display for Error {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::Format(format) => write!(f, "format: {format}"),
88            Self::Unsupported(unsupported) => write!(f, "unsupported: {unsupported}"),
89            Self::Encoding(enc) => write!(f, "encoding: {enc:?}"),
90            Self::IO(io) => write!(f, "io: {io}"),
91            Self::Decompression { method, msg } => {
92                write!(f, "{method:?} decompression error: {msg}")
93            }
94            Self::UnknownSize => f.write_str("size must be known to open zip file"),
95        }
96    }
97}
98
99impl error::Error for Error {}
100
101/// Some part of the zip format is not supported by this crate.
102#[derive(Debug)]
103pub enum UnsupportedError {
104    /// The compression method is not supported.
105    MethodNotSupported(Method),
106
107    /// The compression method is supported, but not enabled in this build.
108    MethodNotEnabled(Method),
109
110    /// The zip file uses a version of LZMA that is not supported.
111    LzmaVersionUnsupported {
112        /// major version read from LZMA properties header, cf. appnote 5.8.8
113        major: u8,
114        /// minor version read from LZMA properties header, cf. appnote 5.8.8
115        minor: u8,
116    },
117
118    /// The LZMA properties header is not the expected size.
119    LzmaPropertiesHeaderWrongSize {
120        /// expected size in bytes
121        expected: u16,
122        /// actual size in bytes, read from a u16, cf. appnote 5.8.8
123        actual: u16,
124    },
125}
126
127impl fmt::Display for UnsupportedError {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            Self::MethodNotSupported(m) => write!(f, "compression method not supported: {m:?}"),
131            Self::MethodNotEnabled(m) => write!(
132                f,
133                "compression method supported, but not enabled in this build: {m:?}"
134            ),
135            Self::LzmaVersionUnsupported { major, minor } => {
136                write!(f, "only LZMA2.0 is supported, found LZMA{major}.{minor}")
137            }
138            Self::LzmaPropertiesHeaderWrongSize { expected, actual } => {
139                write!(f, "LZMA properties header wrong size: expected {expected} bytes, got {actual} bytes")
140            }
141        }
142    }
143}
144
145impl error::Error for UnsupportedError {}
146
147/// Specific zip format errors, mostly due to invalid zip archives but that could also stem from
148/// implementation shortcomings.
149#[derive(Debug)]
150pub enum FormatError {
151    /// The end of central directory record was not found.
152    ///
153    /// This usually indicates that the file being read is not a zip archive.
154    DirectoryEndSignatureNotFound,
155
156    /// The zip64 end of central directory record could not be parsed.
157    ///
158    /// This is only returned when a zip64 end of central directory *locator* was found,
159    /// so the archive should be zip64, but isn't.
160    Directory64EndRecordInvalid,
161
162    /// Corrupted/partial zip file: the offset we found for the central directory
163    /// points outside of the current file.
164    DirectoryOffsetPointsOutsideFile,
165
166    /// The central record is corrupted somewhat.
167    ///
168    /// This can happen when the end of central directory record advertises
169    /// a certain number of files, but we weren't able to read the same number of central directory
170    /// headers.
171    InvalidCentralRecord {
172        /// expected number of files
173        expected: u16,
174        /// actual number of files
175        actual: u16,
176    },
177
178    /// An extra field (that we support) was not decoded correctly.
179    ///
180    /// This can indicate an invalid zip archive, or an implementation error in this crate.
181    InvalidExtraField,
182
183    /// The header offset of an entry is invalid.
184    ///
185    /// This can indicate an invalid zip archive, or an invalid user-provided global offset
186    InvalidHeaderOffset,
187
188    /// End of central directory record claims an impossible number of files.
189    ///
190    /// Each entry takes a minimum amount of size, so if the overall archive size is smaller than
191    /// claimed_records_count * minimum_entry_size, we know it's not a valid zip file.
192    ImpossibleNumberOfFiles {
193        /// number of files claimed in the end of central directory record
194        claimed_records_count: u64,
195        /// total size of the zip file
196        zip_size: u64,
197    },
198
199    /// The local file header (before the file data) could not be parsed correctly.
200    InvalidLocalHeader,
201
202    /// The data descriptor (after the file data) could not be parsed correctly.
203    InvalidDataDescriptor,
204
205    /// The uncompressed size didn't match
206    WrongSize {
207        /// expected size in bytes (from the local header, data descriptor, etc.)
208        expected: u64,
209        /// actual size in bytes (from decompressing the entry)
210        actual: u64,
211    },
212
213    /// The CRC-32 checksum didn't match.
214    WrongChecksum {
215        /// expected checksum (from the data descriptor, etc.)
216        expected: u32,
217        /// actual checksum (from decompressing the entry)
218        actual: u32,
219    },
220}
221
222impl fmt::Display for FormatError {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        match self {
225            Self::DirectoryEndSignatureNotFound => {
226                f.write_str("end of central directory record not found")
227            }
228            Self::Directory64EndRecordInvalid => {
229                f.write_str("zip64 end of central directory record not found")
230            }
231            Self::DirectoryOffsetPointsOutsideFile => {
232                f.write_str("directory offset points outside of file")
233            }
234            Self::InvalidCentralRecord { expected, actual } => {
235                write!(
236                    f,
237                    "invalid central record: expected to read {expected} files, got {actual}"
238                )
239            }
240            Self::InvalidExtraField => f.write_str("could not decode extra field"),
241            Self::InvalidHeaderOffset => f.write_str("invalid header offset"),
242            Self::ImpossibleNumberOfFiles {
243                claimed_records_count,
244                zip_size,
245            } => {
246                write!(
247                    f,
248                    "impossible number of files: claims to have {claimed_records_count}, but zip size is {zip_size}"
249                )
250            }
251            Self::InvalidLocalHeader => f.write_str("invalid local file header"),
252            Self::InvalidDataDescriptor => f.write_str("invalid data descriptor"),
253            Self::WrongSize { expected, actual } => {
254                write!(
255                    f,
256                    "uncompressed size didn't match: expected {expected}, got {actual}"
257                )
258            }
259            Self::WrongChecksum { expected, actual } => {
260                write!(
261                    f,
262                    "checksum didn't match: expected {expected:x?}, got {actual:x?}"
263                )
264            }
265        }
266    }
267}
268
269impl error::Error for FormatError {}