fuchsia_archive/
error.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 crate::{ChunkType, DIRECTORY_ENTRY_LEN, INDEX_ENTRY_LEN};
6use std::io;
7
8#[non_exhaustive]
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
11    #[error("Names can be no longer than 65,535 bytes, supplied name was {0} bytes")]
12    NameTooLong(usize),
13
14    #[error("The length of the concatenated path data must fit in a u32")]
15    TooMuchPathData,
16
17    #[error("Writing archive")]
18    Write(#[source] io::Error),
19
20    #[error("Copying content chunk to archive")]
21    Copy(#[source] io::Error),
22
23    #[error("Seeking within archive")]
24    Seek(#[source] io::Error),
25
26    #[error("Reading archive")]
27    Read(#[source] io::Error),
28
29    #[error("Getting archive size")]
30    GetSize(#[source] io::Error),
31
32    #[error(
33        "Content chunk had expected size {expected} but Reader supplied {actual} at archive \
34             path: {}",
35        format_path_for_error(path)
36    )]
37    ContentChunkSizeMismatch { expected: u64, actual: u64, path: Vec<u8> },
38
39    #[error("Missing directory chunk index entry")]
40    MissingDirectoryChunkIndexEntry,
41
42    #[error("Missing directory names chunk index entry")]
43    MissingDirectoryNamesChunkIndexEntry,
44
45    #[error("Serialize index")]
46    SerializeIndex(#[source] std::io::Error),
47
48    #[error("Serialize directory chunk index entry")]
49    SerializeDirectoryChunkIndexEntry(#[source] std::io::Error),
50
51    #[error("Serialize directory names chunk index entry")]
52    SerializeDirectoryNamesChunkIndexEntry(#[source] std::io::Error),
53
54    #[error("Serialize directory entry")]
55    SerializeDirectoryEntry(#[source] std::io::Error),
56
57    #[error("Invalid magic bytes, expected [c8, bf, 0b, 48, ad, ab, c5, 11], found {0:02x?}")]
58    InvalidMagic([u8; 8]),
59
60    #[error("Bad length of index entries, expected multiple of {INDEX_ENTRY_LEN}, found {0}")]
61    InvalidIndexEntriesLen(u64),
62
63    #[error(
64        "Index entries not strictly increasing, {} followed by {}",
65        ascii_chunk(prev),
66        ascii_chunk(next)
67    )]
68    IndexEntriesOutOfOrder { prev: ChunkType, next: ChunkType },
69
70    #[error(
71        "Invalid chunk offset for chunk {}, expected {expected}, actual {actual}",
72        ascii_chunk(chunk_type)
73    )]
74    InvalidChunkOffset { chunk_type: ChunkType, expected: u64, actual: u64 },
75
76    #[error(
77        "Invalid chunk length for chunk {}, offset + length overflows, offset: {offset}, length: \
78         {length}",
79        ascii_chunk(chunk_type)
80    )]
81    InvalidChunkLength { chunk_type: ChunkType, offset: u64, length: u64 },
82
83    #[error("Bad length of directory names chunk, expected multiple of 8, found {0}")]
84    InvalidDirectoryNamesChunkLen(u64),
85
86    #[error(
87        "Bad length of directory chunk, expected multiple of {DIRECTORY_ENTRY_LEN}, found {0}"
88    )]
89    InvalidDirectoryChunkLen(u64),
90
91    #[error(
92        "Unsorted directory entry {entry_index} has name {} but comes after name \
93         {}",
94        format_path_for_error(name),
95        format_path_for_error(previous_name)
96    )]
97    DirectoryEntriesOutOfOrder { entry_index: usize, name: Vec<u8>, previous_name: Vec<u8> },
98
99    #[error("Directory entry has a zero length name")]
100    ZeroLengthName,
101
102    #[error("Directory entry name starts with '/' {}", format_path_for_error(.0))]
103    NameStartsWithSlash(Vec<u8>),
104
105    #[error("Directory entry name ends with '/' {}", format_path_for_error(.0))]
106    NameEndsWithSlash(Vec<u8>),
107
108    #[error("Directory entry name contains the null character {}", format_path_for_error(.0))]
109    NameContainsNull(Vec<u8>),
110
111    #[error(
112        "Directory entry name contains an empty segment (consecutive '/') {}",
113        format_path_for_error(.0)
114    )]
115    NameContainsEmptySegment(Vec<u8>),
116
117    #[error("Directory entry name contains a segment of '.' {}", format_path_for_error(.0))]
118    NameContainsDotSegment(Vec<u8>),
119
120    #[error("Directory entry name contains a segment of '..' {}", format_path_for_error(.0))]
121    NameContainsDotDotSegment(Vec<u8>),
122
123    #[error(
124        "Path data for directory entry {entry_index} has offset {offset} larger than \
125             directory names chunk size {chunk_size}"
126    )]
127    PathDataOffsetTooLarge { entry_index: usize, offset: usize, chunk_size: usize },
128
129    #[error(
130        "Path data for directory entry {entry_index} has offset {offset} plus length \
131             {length} larger than directory names chunk size {chunk_size}"
132    )]
133    PathDataLengthTooLarge { entry_index: usize, offset: usize, length: u16, chunk_size: usize },
134
135    #[error("Path data not utf8: {path:?}")]
136    PathDataInvalidUtf8 { source: std::str::Utf8Error, path: Vec<u8> },
137
138    #[error("Path not present in archive: {}", format_path_for_error(.0))]
139    PathNotPresent(Vec<u8>),
140
141    #[error("Attempted to read past the end of a content chunk")]
142    ReadPastEnd,
143
144    #[error("Content chunk offset larger than u64::MAX")]
145    ContentChunkOffsetOverflow,
146
147    #[error(
148        "Directory entry for {} has a bad content chunk offset, expected {expected} actual \
149         {actual}",
150        format_path_for_error(name)
151    )]
152    InvalidContentChunkOffset { name: Vec<u8>, expected: u64, actual: u64 },
153
154    #[error(
155        "Directory entry for {} implies a content chunk end that overflows u64, offset: \
156         {offset}, length: {length}",
157        format_path_for_error(name)
158    )]
159    ContentChunkEndOverflow { name: Vec<u8>, offset: u64, length: u64 },
160
161    #[error(
162        "Archive has {archive_size} bytes, but content chunk (including padding) for {} ends \
163         at {lower_bound}",
164        format_path_for_error(name)
165    )]
166    ContentChunkBeyondArchive { name: Vec<u8>, lower_bound: u64, archive_size: u64 },
167
168    #[error(
169        "The content chunk for {} is {chunk_size} bytes but the system does not support \
170         buffers larger than {} bytes",
171        format_path_for_error(name),
172        usize::MAX
173    )]
174    ContentChunkDoesNotFitInMemory { name: Vec<u8>, chunk_size: u64 },
175}
176
177// Displays ChunkType as ascii characters if possible, escapes bytes that are out of range.
178// Takes an array reference so it can be called with tuple fields in thiserror format strings.
179fn ascii_chunk(bytes: &[u8; 8]) -> String {
180    let v: Vec<u8> = bytes.iter().copied().flat_map(std::ascii::escape_default).collect();
181    String::from_utf8_lossy(&v).into_owned()
182}
183
184// Debug formats `path`, first converting it to an &str if it is valid UTF-8.
185fn format_path_for_error(path: &[u8]) -> String {
186    std::str::from_utf8(path).map(|s| format!("{s:?}")).unwrap_or_else(|_| format!("{path:?}"))
187}
188
189#[cfg(test)]
190mod tests {
191    #[test]
192    fn ascii_chunk() {
193        assert_eq!(
194            super::ascii_chunk(&[b'A', 0, b'b', b'\\', 255, b'-', b'_', b'\n']),
195            r"A\x00b\\\xff-_\n"
196        );
197    }
198}