1use super::CompressionFormat;
2use flate2::read::{DeflateDecoder, GzDecoder};
3use positioned_io::ReadAt;
4use std::{
5 fmt::{Debug, Formatter},
6 fs::File,
7 io::Read,
8 ops::Deref,
9 sync::Arc,
10 time::SystemTime,
11};
12
13#[derive(Clone, Copy)]
14pub struct EntryMode(u32);
15
16impl EntryMode {
17 #[inline]
18 pub const fn new(mode: u32) -> Self {
19 Self(mode)
20 }
21
22 #[inline]
24 pub const fn bits(&self) -> u32 {
25 self.0
26 }
27
28 #[inline]
30 pub const fn set_bits(&mut self, mode: u32) {
31 self.0 = mode;
32 }
33
34 #[inline]
36 pub const fn user(&self) -> (bool, bool, bool) {
37 (
38 self.0 & 0o400 != 0,
39 self.0 & 0o200 != 0,
40 self.0 & 0o100 != 0,
41 )
42 }
43
44 #[inline]
46 pub const fn set_user(&mut self, read: bool, write: bool, execute: bool) {
47 self.0 &= !0o700;
48 self.0 |= (read as u32) << 6 | (write as u32) << 5 | (execute as u32) << 4;
49 }
50
51 #[inline]
53 pub const fn group(&self) -> (bool, bool, bool) {
54 (
55 self.0 & 0o040 != 0,
56 self.0 & 0o020 != 0,
57 self.0 & 0o010 != 0,
58 )
59 }
60
61 #[inline]
63 pub const fn set_group(&mut self, read: bool, write: bool, execute: bool) {
64 self.0 &= !0o070;
65 self.0 |= (read as u32) << 3 | (write as u32) << 2 | (execute as u32) << 1;
66 }
67
68 #[inline]
70 pub const fn other(&self) -> (bool, bool, bool) {
71 (
72 self.0 & 0o004 != 0,
73 self.0 & 0o002 != 0,
74 self.0 & 0o001 != 0,
75 )
76 }
77
78 #[inline]
80 pub const fn set_other(&mut self, read: bool, write: bool, execute: bool) {
81 self.0 &= !0o007;
82 self.0 |= (read as u32) | (write as u32) << 1 | (execute as u32) << 2;
83 }
84}
85
86impl Debug for EntryMode {
87 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88 let mut mode = String::with_capacity(9);
89
90 mode.push(if self.0 & 0o400 != 0 { 'r' } else { '-' });
91 mode.push(if self.0 & 0o200 != 0 { 'w' } else { '-' });
92 mode.push(if self.0 & 0o100 != 0 { 'x' } else { '-' });
93 mode.push(if self.0 & 0o040 != 0 { 'r' } else { '-' });
94 mode.push(if self.0 & 0o020 != 0 { 'w' } else { '-' });
95 mode.push(if self.0 & 0o010 != 0 { 'x' } else { '-' });
96 mode.push(if self.0 & 0o004 != 0 { 'r' } else { '-' });
97 mode.push(if self.0 & 0o002 != 0 { 'w' } else { '-' });
98 mode.push(if self.0 & 0o001 != 0 { 'x' } else { '-' });
99
100 write!(f, "{} ({:o})", mode, self.0)
101 }
102}
103
104impl Default for EntryMode {
105 #[inline]
106 fn default() -> Self {
107 Self(0o644)
108 }
109}
110
111impl From<u32> for EntryMode {
112 #[inline]
113 fn from(mode: u32) -> Self {
114 Self(mode)
115 }
116}
117
118impl From<EntryMode> for u32 {
119 #[inline]
120 fn from(mode: EntryMode) -> Self {
121 mode.0
122 }
123}
124
125impl From<std::fs::Permissions> for EntryMode {
126 #[inline]
127 fn from(permissions: std::fs::Permissions) -> Self {
128 #[cfg(unix)]
129 {
130 use std::os::unix::fs::PermissionsExt;
131
132 Self(permissions.mode())
133 }
134 #[cfg(not(unix))]
135 {
136 Self(if permissions.readonly() { 0o444 } else { 0o666 })
137 }
138 }
139}
140
141impl From<EntryMode> for std::fs::Permissions {
142 #[inline]
143 fn from(permissions: EntryMode) -> std::fs::Permissions {
144 #[cfg(unix)]
145 {
146 use std::os::unix::fs::PermissionsExt;
147
148 std::fs::Permissions::from_mode(permissions.0)
149 }
150 #[cfg(not(unix))]
151 {
152 let mut fs_permissions: std::fs::Permissions = unsafe { std::mem::zeroed() };
153 fs_permissions.set_readonly(permissions.0 & 0o444 != 0);
154
155 fs_permissions
156 }
157 }
158}
159
160impl Deref for EntryMode {
161 type Target = u32;
162
163 #[inline]
164 fn deref(&self) -> &Self::Target {
165 &self.0
166 }
167}
168
169pub struct FileEntry {
170 pub name: String,
171 pub mode: EntryMode,
172 pub owner: (u32, u32),
173 pub mtime: SystemTime,
174
175 pub compression: CompressionFormat,
176 pub size_compressed: Option<u64>,
177 pub size_real: u64,
178 pub size: u64,
179
180 pub file: Arc<File>,
181 pub offset: u64,
182 pub decoder: Option<Box<dyn Read + Sync + Send>>,
183 pub consumed: u64,
184}
185
186impl Clone for FileEntry {
187 fn clone(&self) -> Self {
188 Self {
189 name: self.name.clone(),
190 mode: self.mode,
191 owner: self.owner,
192 mtime: self.mtime,
193 compression: self.compression,
194 size_compressed: self.size_compressed,
195 size_real: self.size_real,
196 size: self.size,
197 file: Arc::clone(&self.file),
198 decoder: None,
199 offset: self.offset,
200 consumed: 0,
201 }
202 }
203}
204
205impl Debug for FileEntry {
206 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
207 f.debug_struct("FileEntry")
208 .field("name", &self.name)
209 .field("mode", &self.mode)
210 .field("owner", &self.owner)
211 .field("mtime", &self.mtime)
212 .field("offset", &self.offset)
213 .field("compression", &self.compression)
214 .field("size", &self.size)
215 .field("size_real", &self.size_real)
216 .field("size_compressed", &self.size_compressed)
217 .finish()
218 }
219}
220
221impl Read for FileEntry {
222 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
223 if self.consumed >= self.size {
224 return Ok(0);
225 }
226
227 let remaining = self.size - self.consumed;
228
229 match self.compression {
230 CompressionFormat::None => {
231 let bytes_read = self.file.read_at(self.offset + self.consumed, buf)?;
232
233 if bytes_read > remaining as usize {
234 self.consumed += remaining;
235 return Ok(remaining as usize);
236 }
237
238 self.consumed += bytes_read as u64;
239 Ok(bytes_read)
240 }
241 CompressionFormat::Gzip => {
242 if self.decoder.is_none() {
243 let reader = BoundedReader {
244 file: Arc::clone(&self.file),
245 offset: self.offset,
246 position: 0,
247 size: self.size_compressed.unwrap(),
248 };
249
250 let decoder = Box::new(GzDecoder::new(reader));
251 self.decoder = Some(decoder);
252 }
253
254 let decoder = self.decoder.as_mut().unwrap();
255 let bytes_read = decoder.read(buf)?;
256
257 if bytes_read > remaining as usize {
258 self.decoder = None;
259 self.consumed += remaining;
260 return Ok(remaining as usize);
261 }
262
263 self.consumed += bytes_read as u64;
264 Ok(bytes_read)
265 }
266 CompressionFormat::Deflate => {
267 if self.decoder.is_none() {
268 let reader = BoundedReader {
269 file: Arc::clone(&self.file),
270 offset: self.offset,
271 position: 0,
272 size: self.size_compressed.unwrap(),
273 };
274
275 let decoder = Box::new(DeflateDecoder::new(reader));
276 self.decoder = Some(decoder);
277 }
278
279 let decoder = self.decoder.as_mut().unwrap();
280 let bytes_read = decoder.read(buf)?;
281
282 if bytes_read > remaining as usize {
283 self.decoder = None;
284 self.consumed += remaining;
285 return Ok(remaining as usize);
286 }
287
288 self.consumed += bytes_read as u64;
289 Ok(bytes_read)
290 }
291
292 #[cfg(feature = "brotli")]
293 CompressionFormat::Brotli => {
294 if self.decoder.is_none() {
295 let reader = BoundedReader {
296 file: Arc::clone(&self.file),
297 offset: self.offset,
298 position: 0,
299 size: self.size_compressed.unwrap(),
300 };
301
302 let decoder = Box::new(brotli::Decompressor::new(reader, 4096));
303 self.decoder = Some(decoder);
304 }
305
306 let decoder = self.decoder.as_mut().unwrap();
307 let bytes_read = decoder.read(buf)?;
308
309 if bytes_read > remaining as usize {
310 self.decoder = None;
311 self.consumed += remaining;
312 return Ok(remaining as usize);
313 }
314
315 self.consumed += bytes_read as u64;
316 Ok(bytes_read)
317 }
318 #[cfg(not(feature = "brotli"))]
319 CompressionFormat::Brotli => Err(std::io::Error::new(
320 std::io::ErrorKind::InvalidData,
321 "Brotli support is not enabled. Please enable the 'brotli' feature.",
322 )),
323 }
324 }
325}
326
327#[derive(Clone, Debug)]
328pub struct DirectoryEntry {
329 pub name: String,
330 pub mode: EntryMode,
331 pub owner: (u32, u32),
332 pub mtime: SystemTime,
333 pub entries: Vec<Entry>,
334}
335
336#[derive(Clone, Debug)]
337pub struct SymlinkEntry {
338 pub name: String,
339 pub mode: EntryMode,
340 pub owner: (u32, u32),
341 pub mtime: SystemTime,
342 pub target: String,
343 pub target_dir: bool,
344}
345
346#[derive(Clone, Debug)]
347pub enum Entry {
348 File(Box<FileEntry>),
349 Directory(Box<DirectoryEntry>),
350 Symlink(Box<SymlinkEntry>),
351}
352
353impl Entry {
354 #[inline]
358 pub const fn name(&self) -> &str {
359 match self {
360 Entry::File(entry) => entry.name.as_str(),
361 Entry::Directory(entry) => entry.name.as_str(),
362 Entry::Symlink(entry) => entry.name.as_str(),
363 }
364 }
365
366 #[inline]
369 pub const fn mode(&self) -> EntryMode {
370 match self {
371 Entry::File(entry) => entry.mode,
372 Entry::Directory(entry) => entry.mode,
373 Entry::Symlink(entry) => entry.mode,
374 }
375 }
376
377 #[inline]
380 pub const fn owner(&self) -> (u32, u32) {
381 match self {
382 Entry::File(entry) => entry.owner,
383 Entry::Directory(entry) => entry.owner,
384 Entry::Symlink(entry) => entry.owner,
385 }
386 }
387
388 #[inline]
391 pub const fn mtime(&self) -> SystemTime {
392 match self {
393 Entry::File(entry) => entry.mtime,
394 Entry::Directory(entry) => entry.mtime,
395 Entry::Symlink(entry) => entry.mtime,
396 }
397 }
398
399 #[inline]
400 pub const fn is_file(&self) -> bool {
401 matches!(self, Entry::File(_))
402 }
403
404 #[inline]
405 pub const fn is_directory(&self) -> bool {
406 matches!(self, Entry::Directory(_))
407 }
408
409 #[inline]
410 pub const fn is_symlink(&self) -> bool {
411 matches!(self, Entry::Symlink(_))
412 }
413}
414
415struct BoundedReader {
416 file: Arc<File>,
417 offset: u64,
418 size: u64,
419 position: u64,
420}
421
422impl Read for BoundedReader {
423 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
424 if self.position >= self.size {
425 return Ok(0);
426 }
427
428 let remaining = self.size - self.position;
429 let to_read = std::cmp::min(buf.len(), remaining as usize);
430
431 let bytes_read = self
432 .file
433 .read_at(self.offset + self.position, &mut buf[..to_read])?;
434 self.position += bytes_read as u64;
435
436 Ok(bytes_read)
437 }
438}