1use crate::{
2 carchive::{self, archive_entry, archive_read_free},
3 prelude::*,
4};
5
6use libc::{c_int, c_void};
7
8use crate::carchive::archive;
9use std::{
10 ffi::CString,
11 io::{Error as IOError, ErrorKind, Read, Seek, SeekFrom},
12 mem::MaybeUninit,
13};
14
15const BUFFER_SIZE: usize = 16384;
16
17pub struct ArchiveReader<R: Read + Seek> {
23 archive_reader: Option<*mut archive>,
24 #[allow(dead_code)]
25 fileref: Box<SourceReader<R>>,
26 current_entry: Option<Metadata>,
27}
28
29struct SourceReader<R: Read + Seek> {
30 obj: R,
31 buffer: Box<[u8]>,
32}
33
34unsafe extern "C" fn archivereader_read<R: Read + Seek>(
35 archive: *mut carchive::archive,
36 client_data: *mut c_void,
37 buffer: *mut *const c_void,
38) -> carchive::la_ssize_t {
39 let reader = (client_data as *mut SourceReader<R>).as_mut().unwrap();
40 *buffer = reader.buffer.as_ptr() as *const c_void;
41
42 match reader.obj.read(reader.buffer.as_mut()) {
43 Ok(size) => size as carchive::la_ssize_t,
44 Err(e) => {
45 let description = CString::new(e.to_string()).unwrap();
46
47 carchive::archive_set_error(
48 archive,
49 e.raw_os_error().unwrap_or(0),
50 description.as_ptr(),
51 );
52
53 -1
54 }
55 }
56}
57
58unsafe extern "C" fn archivereader_seek<R: Read + Seek>(
59 _: *mut carchive::archive,
60 client_data: *mut c_void,
61 offset: carchive::la_int64_t,
62 whence: c_int,
63) -> i64 {
64 let seeker = (client_data as *mut SourceReader<R>).as_mut().unwrap();
65 let whence = match whence {
66 0 => SeekFrom::Start(offset as u64),
67 1 => SeekFrom::Current(offset),
68 2 => SeekFrom::End(offset),
69 _ => return -1,
70 };
71
72 match seeker.obj.seek(whence) {
73 Ok(offset) => offset as i64,
74 Err(_) => -1,
75 }
76}
77
78impl<R: Read + Seek> ArchiveReader<R> {
79 pub fn new(source: R) -> Result<Self> {
81 let buffer = [0; BUFFER_SIZE];
82 let mut fref = Box::new(SourceReader {
83 obj: source,
84 buffer: Box::new(buffer),
85 });
86
87 unsafe {
88 Ok(ArchiveReader {
89 archive_reader: Some(ArchiveReader::start(&mut fref)?),
90 fileref: fref,
91 current_entry: Option::None,
92 })
93 }
94 }
95
96 unsafe fn start(fref: &mut Box<SourceReader<R>>) -> Result<*mut archive> {
97 let archive_reader = carchive::archive_read_new();
98
99 if archive_reader.is_null() {
100 return Err(Error::NullArchive);
101 }
102
103 match carchive::archive_read_support_filter_all(archive_reader) {
104 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
105 _ => return Err(Error::from(archive_reader)),
106 };
107
108 match carchive::archive_read_support_format_all(archive_reader) {
109 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
110 _ => return Err(Error::from(archive_reader)),
111 };
112
113 match carchive::archive_read_set_seek_callback(
114 archive_reader,
115 Some(archivereader_seek::<R>),
116 ) {
117 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
118 _ => return Err(Error::from(archive_reader)),
119 }
120
121 match carchive::archive_read_open(
122 archive_reader,
123 std::ptr::addr_of_mut!(**fref) as *mut c_void,
124 None,
125 Some(archivereader_read::<R>),
126 None,
127 ) {
128 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
129 _ => return Err(Error::from(archive_reader)),
130 };
131
132 Ok(archive_reader)
133 }
134
135 pub fn list_files(self) -> Result<Vec<Metadata>> {
139 let archive = self.get_archive()?;
140
141 let mut outlist = Vec::<Metadata>::new();
142
143 loop {
144 unsafe {
145 let mut entry = std::mem::MaybeUninit::<*mut archive_entry>::uninit();
146 match carchive::archive_read_next_header(archive, entry.as_mut_ptr()) {
147 carchive::ARCHIVE_EOF => break,
148 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => {
149 let entry = entry.assume_init();
150 outlist.push(entry.into());
151 }
152 _ => return Err(Error::from(archive)),
153 };
154 }
155 }
156
157 Ok(outlist)
158 }
159
160 pub fn current_metadata(&self) -> Option<&Metadata> {
162 self.current_entry.as_ref()
163 }
164
165 pub fn reader_whole_archive(self, dest_path: &str, flags: i32) -> Result<()> {
169 let archive = self.get_archive()?;
170 let current_dir = std::env::current_dir()?;
171 std::fs::create_dir_all(dest_path)?;
172 std::env::set_current_dir(dest_path)?;
173
174 loop {
175 unsafe {
176 let mut entry = std::mem::MaybeUninit::<*mut archive_entry>::uninit();
177 match carchive::archive_read_next_header(archive, entry.as_mut_ptr()) {
178 carchive::ARCHIVE_EOF => break,
179 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => {
180 let entry_ptr = entry.assume_init();
181 match carchive::archive_read_extract(archive, entry_ptr, flags) {
182 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
183 _ => {
184 let _ = std::env::set_current_dir(current_dir);
185 return Err(Error::from(archive));
186 }
187 }
188 }
189 _ => {
190 let _ = std::env::set_current_dir(current_dir);
191 return Err(Error::from(archive));
192 }
193 };
194 }
195 }
196
197 std::env::set_current_dir(current_dir)?;
198 Ok(())
199 }
200
201 pub fn reader_seek_obj(&mut self, filename: &str) -> Result<()> {
206 let archive = self.get_archive()?;
207
208 unsafe {
209 self.fileref.obj.seek(SeekFrom::Start(0))?;
210 match archive_read_free(archive) {
211 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
212 _ => return Err(archive.into()),
213 };
214
215 let archive = ArchiveReader::start(&mut self.fileref)?;
216 self.archive_reader = Some(archive);
217
218 loop {
219 let mut entry = std::mem::MaybeUninit::<*mut archive_entry>::uninit();
220 let hdr_result = carchive::archive_read_next_header(archive, entry.as_mut_ptr());
221 let entry = entry.assume_init();
222 match hdr_result {
223 carchive::ARCHIVE_EOF => {
224 return Err(IOError::new(
225 ErrorKind::NotFound,
226 format!("path {} doesn't exist inside archive", filename),
227 )
228 .into());
229 }
230 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => (),
231 _ => return Err(archive.into()),
232 };
233
234 let meta: Metadata = entry.into();
235 if meta.filepath() == filename {
236 self.current_entry = Some(meta);
237 return Ok(());
238 }
239 }
240 }
241 }
242
243 fn get_archive(&self) -> Result<*mut archive> {
244 if let Some(a) = self.archive_reader {
245 Ok(a)
246 } else {
247 Err(Error::NullArchive)
248 }
249 }
250
251 fn free(&mut self) -> Result<()> {
253 let archive = self.get_archive()?;
254 match unsafe { archive_read_free(archive) } {
255 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => Ok(()),
256 _ => Err(Error::from(archive)),
257 }
258 }
259}
260
261impl<R: Read + Seek> Read for ArchiveReader<R> {
262 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
263 let archive = self.get_archive()?;
264 let read_size = unsafe {
265 carchive::archive_read_data(archive, buf.as_mut_ptr() as *mut c_void, buf.len())
266 };
267
268 if read_size >= 0 {
269 Ok(read_size as usize)
270 } else {
271 Err(Error::from(archive).into())
272 }
273 }
274}
275
276impl<R: Read + Seek> Iterator for ArchiveReader<R> {
277 type Item = Metadata;
278
279 fn next(&mut self) -> Option<Self::Item> {
280 let archive;
281 let mut entry = MaybeUninit::<*mut archive_entry>::uninit();
282
283 if let Ok(res) = self.get_archive() {
284 archive = res;
285 } else {
286 return Option::None;
287 }
288
289 unsafe {
290 match carchive::archive_read_next_header(archive, entry.as_mut_ptr()) {
291 carchive::ARCHIVE_OK | carchive::ARCHIVE_WARN => {
292 let entry_ptr = entry.assume_init();
293 let meta: Metadata = entry_ptr.into();
294 self.current_entry = Some(meta.clone());
295 Some(meta)
296 }
297 _ => {
298 self.current_entry = None;
299 Option::None
300 }
301 }
302 }
303 }
304}
305
306impl<R: Read + Seek> Drop for ArchiveReader<R> {
307 fn drop(&mut self) {
308 drop(self.free());
309 }
310}
311
312impl Drop for archive_entry {
313 fn drop(&mut self) {
314 unsafe { carchive::archive_entry_free(self) };
315 }
316}