luminol_filesystem/archiver/filesystem/
impls.rs1use color_eyre::eyre::WrapErr;
19use itertools::Itertools;
20use rand::Rng;
21use std::io::{
22 prelude::*,
23 BufReader, BufWriter,
24 ErrorKind::{AlreadyExists, InvalidData},
25 SeekFrom,
26};
27
28use super::super::util::{
29 advance_magic, move_file_and_truncate, read_file_xor, read_u32_xor, regress_magic,
30};
31use super::{Entry, File, FileSystem, MAGIC};
32use crate::{DirEntry, Error, Metadata, OpenFlags, Result};
33
34impl<T> crate::FileSystem for FileSystem<T>
35where
36 T: crate::File,
37{
38 type File = File<T>;
39
40 fn open_file(
41 &self,
42 path: impl AsRef<camino::Utf8Path>,
43 flags: OpenFlags,
44 ) -> Result<Self::File> {
45 let path = path.as_ref();
46 let c = format!(
47 "While opening file {path:?} in a version {} archive",
48 self.version
49 );
50 let mut tmp = crate::host::File::new()
51 .wrap_err("While creating a temporary file")
52 .wrap_err_with(|| c.clone())?;
53 let mut created = false;
54
55 {
56 let mut archive = self.archive.lock();
57 let mut trie = self.trie.write();
58
59 if flags.contains(OpenFlags::Create) && !trie.contains_file(path) {
60 created = true;
61 match self.version {
62 1 | 2 => {
63 archive
64 .seek(SeekFrom::Start(8))
65 .wrap_err("While reading the header of the archive")
66 .wrap_err_with(|| c.clone())?;
67 let mut reader = BufReader::new(archive.as_file());
68 let mut magic = MAGIC;
69 let mut i = 0;
70 while let Ok(path_len) =
71 read_u32_xor(&mut reader, advance_magic(&mut magic))
72 {
73 for _ in 0..path_len {
74 advance_magic(&mut magic);
75 }
76 reader.seek(SeekFrom::Current(path_len as i64)).wrap_err_with(|| format!("While reading the file length (path length = {path_len}) of file #{i} in the archive")).wrap_err_with(|| c.clone())?;
77 let entry_len = read_u32_xor(&mut reader, advance_magic(&mut magic)).wrap_err_with(|| format!("While reading the file length (path length = {path_len}) of file #{i} in the archive")).wrap_err_with(|| c.clone())?;
78 reader.seek(SeekFrom::Current(entry_len as i64)).wrap_err_with(|| format!("While seeking forward by {entry_len} bytes to read file #{} in the archive", i + 1)).wrap_err_with(|| c.clone())?;
79 i += 1;
80 }
81 drop(reader);
82 regress_magic(&mut magic);
83
84 let archive_len = archive
85 .seek(SeekFrom::End(0))
86 .wrap_err(
87 "While writing the path length of the new file to the archive",
88 )
89 .wrap_err_with(|| c.clone())?;
90 let mut writer = BufWriter::new(archive.as_file());
91 writer
92 .write_all(
93 &(path.as_str().bytes().len() as u32 ^ advance_magic(&mut magic))
94 .to_le_bytes(),
95 )
96 .wrap_err(
97 "While writing the path length of the new file to the archive",
98 )
99 .wrap_err_with(|| c.clone())?;
100 writer
101 .write_all(
102 &path
103 .as_str()
104 .bytes()
105 .map(|b| {
106 let b = if b == b'/' { b'\\' } else { b };
107 b ^ advance_magic(&mut magic) as u8
108 })
109 .collect_vec(),
110 )
111 .wrap_err("While writing the path of the new file to the archive")
112 .wrap_err_with(|| c.clone())?;
113 writer
114 .write_all(&advance_magic(&mut magic).to_le_bytes())
115 .wrap_err(
116 "While writing the file length of the new file to the archive",
117 )
118 .wrap_err_with(|| c.clone())?;
119 writer
120 .flush()
121 .wrap_err("While flushing the archive after writing its contents")
122 .wrap_err_with(|| c.clone())?;
123 drop(writer);
124
125 trie.create_file(
126 path,
127 Entry {
128 header_offset: archive_len,
129 body_offset: archive_len + path.as_str().bytes().len() as u64 + 8,
130 size: 0,
131 start_magic: magic,
132 },
133 );
134 }
135
136 3 => {
137 let mut tmp = crate::host::File::new()
138 .wrap_err("While creating a temporary file")
139 .wrap_err_with(|| c.clone())?;
140
141 let extra_data_len = path.as_str().bytes().len() as u32 + 16;
142 let mut headers = Vec::new();
143
144 archive
145 .seek(SeekFrom::Start(12))
146 .wrap_err("While reading the header of the archive")
147 .wrap_err_with(|| c.clone())?;
148 let mut reader = BufReader::new(archive.as_file());
149 let mut position = 12;
150 let mut i = 0;
151 while let Ok(offset) = read_u32_xor(&mut reader, self.base_magic) {
152 if offset == 0 {
153 break;
154 }
155 headers.push((position, offset));
156 reader.seek(SeekFrom::Current(8)).wrap_err_with(|| format!("While reading the path length (file offset = {offset}) of file #{i} in the archive")).wrap_err_with(|| c.clone())?;
157 let path_len = read_u32_xor(&mut reader, self.base_magic).wrap_err_with(|| format!("While reading the path length (file offset = {offset}) of file #{i} in the archive")).wrap_err_with(|| c.clone())?;
158 position = reader.seek(SeekFrom::Current(path_len as i64)).wrap_err_with(|| format!("While seeking forward by {path_len} bytes to read file #{} in the archive", i + 1)).wrap_err_with(|| c.clone())?;
159 i += 1;
160 }
161 drop(reader);
162
163 archive
164 .seek(SeekFrom::Start(position))
165 .wrap_err("While copying the archive body into a temporary file")
166 .wrap_err_with(|| c.clone())?;
167 std::io::copy(archive.as_file(), &mut tmp)
168 .wrap_err("While copying the archive body into a temporary file")
169 .wrap_err_with(|| c.clone())?;
170 tmp.flush()
171 .wrap_err("While copying the archive body into a temporary file")
172 .wrap_err_with(|| c.clone())?;
173
174 let magic: u32 = rand::thread_rng().gen();
175 let archive_len = archive
176 .metadata()
177 .wrap_err("While getting the size of the archive")
178 .wrap_err_with(|| c.clone())?
179 .size as u32
180 + extra_data_len;
181 let mut writer = BufWriter::new(archive.as_file());
182 for (i, (position, offset)) in headers.into_iter().enumerate() {
183 writer
184 .seek(SeekFrom::Start(position))
185 .wrap_err_with(|| {
186 format!("While rewriting the file offset of file #{i} to the archive")
187 })
188 .wrap_err_with(|| c.clone())?;
189 writer
190 .write_all(
191 &((offset + extra_data_len) ^ self.base_magic).to_le_bytes(),
192 )
193 .wrap_err_with(|| {
194 format!("While rewriting the file offset of file #{i} to the archive")
195 })
196 .wrap_err_with(|| c.clone())?;
197 }
198 writer
199 .seek(SeekFrom::Start(position))
200 .wrap_err(
201 "While writing the file offset of the new file to the archive",
202 )
203 .wrap_err_with(|| c.clone())?;
204 writer
205 .write_all(&(archive_len ^ self.base_magic).to_le_bytes())
206 .wrap_err(
207 "While writing the file offset of the new file to the archive",
208 )
209 .wrap_err_with(|| c.clone())?;
210 writer
211 .write_all(&self.base_magic.to_le_bytes())
212 .wrap_err(
213 "While writing the file length of the new file to the archive",
214 )
215 .wrap_err_with(|| c.clone())?;
216 writer
217 .write_all(&(magic ^ self.base_magic).to_le_bytes())
218 .wrap_err(
219 "While writing the base magic value of the new file to the archive",
220 )
221 .wrap_err_with(|| c.clone())?;
222 writer
223 .write_all(
224 &(path.as_str().bytes().len() as u32 ^ self.base_magic)
225 .to_le_bytes(),
226 )
227 .wrap_err(
228 "While writing the path length of the new file to the archive",
229 )
230 .wrap_err_with(|| c.clone())?;
231 writer
232 .write_all(
233 &path
234 .as_str()
235 .bytes()
236 .enumerate()
237 .map(|(i, b)| {
238 let b = if b == b'/' { b'\\' } else { b };
239 b ^ (self.base_magic >> (8 * (i % 4))) as u8
240 })
241 .collect_vec(),
242 )
243 .wrap_err("While writing the path of the new file to the archive")
244 .wrap_err_with(|| c.clone())?;
245 tmp.seek(SeekFrom::Start(0)).wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
246 std::io::copy(&mut tmp, &mut writer).wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
247 writer.flush().wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
248 drop(writer);
249
250 trie.create_file(
251 path,
252 Entry {
253 header_offset: position,
254 body_offset: archive_len as u64,
255 size: 0,
256 start_magic: magic,
257 },
258 );
259 }
260
261 _ => return Err(Error::InvalidArchiveVersion(self.version).into()),
262 }
263 } else if !flags.contains(OpenFlags::Truncate) {
264 let entry = *trie
265 .get_file(path)
266 .ok_or(Error::NotExist)
267 .wrap_err("While copying the file within the archive into a temporary file")
268 .wrap_err_with(|| c.clone())?;
269 archive
270 .seek(SeekFrom::Start(entry.body_offset))
271 .wrap_err("While copying the file within the archive into a temporary file")
272 .wrap_err_with(|| c.clone())?;
273
274 let mut adapter = BufReader::new(archive.as_file().take(entry.size));
275 std::io::copy(
276 &mut read_file_xor(&mut adapter, entry.start_magic),
277 &mut tmp,
278 )
279 .wrap_err("While copying the file within the archive into a temporary file")
280 .wrap_err_with(|| c.clone())?;
281 tmp.flush()
282 .wrap_err("While copying the file within the archive into a temporary file")
283 .wrap_err_with(|| c.clone())?;
284 } else if !trie.contains_file(path) {
285 return Err(Error::NotExist.into());
286 }
287 }
288
289 tmp.seek(SeekFrom::Start(0))
290 .wrap_err("While copying the file within the archive into a temporary file")
291 .wrap_err_with(|| c.clone())?;
292 Ok(File {
293 archive: flags
294 .contains(OpenFlags::Write)
295 .then(|| self.archive.clone()),
296 trie: flags.contains(OpenFlags::Write).then(|| self.trie.clone()),
297 path: path.to_owned(),
298 read_allowed: flags.contains(OpenFlags::Read),
299 tmp,
300 modified: parking_lot::Mutex::new(
301 !created && flags.contains(OpenFlags::Write) && flags.contains(OpenFlags::Truncate),
302 ),
303 version: self.version,
304 base_magic: self.base_magic,
305 })
306 }
307
308 fn metadata(&self, path: impl AsRef<camino::Utf8Path>) -> Result<Metadata> {
309 let path = path.as_ref();
310 let trie = self.trie.read();
311 if let Some(entry) = trie.get_file(path) {
312 Ok(Metadata {
313 is_file: true,
314 size: entry.size,
315 })
316 } else if let Some(size) = trie.get_dir_size(path) {
317 Ok(Metadata {
318 is_file: false,
319 size: size as u64,
320 })
321 } else {
322 Err(Error::NotExist.into())
323 }
324 }
325
326 fn rename(
327 &self,
328 from: impl AsRef<camino::Utf8Path>,
329 to: impl AsRef<camino::Utf8Path>,
330 ) -> Result<()> {
331 let from = from.as_ref();
332 let to = to.as_ref();
333
334 let mut archive = self.archive.lock();
335 let mut trie = self.trie.write();
336 let c = format!(
337 "While renaming {from:?} to {to:?} in a version {} archive",
338 self.version
339 );
340
341 if trie.contains_dir(from) {
342 return Err(Error::NotSupported.into());
343 }
344 if trie.contains(to) {
345 return Err(Error::IoError(AlreadyExists.into()).into());
346 }
347 if !trie.contains_dir(
348 from.parent()
349 .ok_or(Error::NotExist)
350 .wrap_err_with(|| c.clone())?,
351 ) {
352 return Err(Error::NotExist.into());
353 }
354 let Some(old_entry) = trie.get_file(from).copied() else {
355 return Err(Error::NotExist.into());
356 };
357
358 let archive_len = archive
359 .metadata()
360 .wrap_err("While getting the length of the archive")
361 .wrap_err_with(|| c.clone())?
362 .size;
363 let from_len = from.as_str().bytes().len();
364 let to_len = to.as_str().bytes().len();
365
366 if from_len != to_len {
367 match self.version {
368 1 | 2 => {
369 let mut tmp = crate::host::File::new()
371 .wrap_err("While creating a temporary file")
372 .wrap_err_with(|| c.clone())?;
373 archive.seek(SeekFrom::Start(old_entry.body_offset)).wrap_err("While copying the contents of the file within the archive into a temporary file").wrap_err_with(|| c.clone())?;
374 let mut reader = BufReader::new(archive.as_file().take(old_entry.size));
375 std::io::copy(
376 &mut read_file_xor(&mut reader, old_entry.start_magic),
377 &mut tmp,
378 ).wrap_err("While copying the contents of the file within the archive into a temporary file").wrap_err_with(|| c.clone())?;
379 tmp.flush().wrap_err("While copying the contents of the file within the archive into a temporary file").wrap_err_with(|| c.clone())?;
380 drop(reader);
381
382 move_file_and_truncate(
384 &mut archive,
385 &mut trie,
386 from,
387 self.version,
388 self.base_magic,
389 )
390 .wrap_err("While relocating the file header to the end of the archive")
391 .wrap_err_with(|| c.clone())?;
392 let mut new_entry = *trie
393 .get_file(from)
394 .ok_or(Error::InvalidHeader)
395 .wrap_err("While relocating the file header to the end of the archive")
396 .wrap_err_with(|| c.clone())?;
397 trie.remove_file(from)
398 .ok_or(Error::InvalidHeader)
399 .wrap_err("While relocating the file header to the end of the archive")
400 .wrap_err_with(|| c.clone())?;
401 new_entry.size = old_entry.size;
402
403 let mut magic = new_entry.start_magic;
404 regress_magic(&mut magic);
405 regress_magic(&mut magic);
406 for _ in from.as_str().bytes() {
407 regress_magic(&mut magic);
408 }
409
410 archive
412 .seek(SeekFrom::Start(new_entry.header_offset))
413 .wrap_err("While rewriting the path length of the file to the archive")
414 .wrap_err_with(|| c.clone())?;
415 let mut writer = BufWriter::new(archive.as_file());
416 writer
417 .write_all(&(to_len as u32 ^ advance_magic(&mut magic)).to_le_bytes())
418 .wrap_err("While rewriting the path length of the file to the archive")
419 .wrap_err_with(|| c.clone())?;
420 writer
421 .write_all(
422 &to.as_str()
423 .bytes()
424 .map(|b| {
425 let b = if b == b'/' { b'\\' } else { b };
426 b ^ advance_magic(&mut magic) as u8
427 })
428 .collect_vec(),
429 )
430 .wrap_err("While rewriting the path of the file to the archive")
431 .wrap_err_with(|| c.clone())?;
432 writer
433 .write_all(
434 &(old_entry.size as u32 ^ advance_magic(&mut magic)).to_le_bytes(),
435 )
436 .wrap_err("While rewriting the file length of the file to the archive")
437 .wrap_err_with(|| c.clone())?;
438
439 new_entry.start_magic = magic;
440
441 tmp.seek(SeekFrom::Start(0))
443 .wrap_err("While relocating the file contents to the end of the archive")
444 .wrap_err_with(|| c.clone())?;
445 let mut reader = BufReader::new(&mut tmp);
446 std::io::copy(&mut read_file_xor(&mut reader, magic), &mut writer)
447 .wrap_err("While relocating the file contents to the end of the archive")
448 .wrap_err_with(|| c.clone())?;
449 writer
450 .flush()
451 .wrap_err("While relocating the file contents to the end of the archive")
452 .wrap_err_with(|| c.clone())?;
453 drop(writer);
454
455 trie.create_file(to, new_entry);
456 }
457
458 3 => {
459 let mut tmp = crate::host::File::new()
461 .wrap_err("While creating a temporary file")
462 .wrap_err_with(|| c.clone())?;
463 archive
464 .seek(SeekFrom::Start(
465 old_entry.header_offset + from_len as u64 + 16,
466 ))
467 .wrap_err("While copying the contents of the archive into a temporary file")
468 .wrap_err_with(|| c.clone())?;
469 std::io::copy(archive.as_file(), &mut tmp)
470 .wrap_err("While copying the contents of the archive into a temporary file")
471 .wrap_err_with(|| c.clone())?;
472 tmp.flush()
473 .wrap_err("While copying the contents of the archive into a temporary file")
474 .wrap_err_with(|| c.clone())?;
475
476 archive
478 .seek(SeekFrom::Start(old_entry.header_offset + 12))
479 .wrap_err("While rewriting the path length of the file to the archive")
480 .wrap_err_with(|| c.clone())?;
481 let mut writer = BufWriter::new(archive.as_file());
482 writer
483 .write_all(&(to_len as u32 ^ self.base_magic).to_le_bytes())
484 .wrap_err("While rewriting the path length of the file to the archive")
485 .wrap_err_with(|| c.clone())?;
486 writer
487 .write_all(
488 &to.as_str()
489 .bytes()
490 .enumerate()
491 .map(|(i, b)| {
492 let b = if b == b'/' { b'\\' } else { b };
493 b ^ (self.base_magic >> (8 * (i % 4))) as u8
494 })
495 .collect_vec(),
496 )
497 .wrap_err("While rewriting the path of the file to the archive")
498 .wrap_err_with(|| c.clone())?;
499 trie.remove_file(from)
500 .ok_or(Error::InvalidHeader)
501 .wrap_err("While rewriting the header of the file to the archive")
502 .wrap_err_with(|| c.clone())?;
503 trie.create_file(to, old_entry);
504
505 tmp.seek(SeekFrom::Start(0)).wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
507 std::io::copy(&mut tmp, &mut writer).wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
508 writer.flush().wrap_err("While copying a temporary file containing the archive body into the archive").wrap_err_with(|| c.clone())?;
509 drop(writer);
510
511 archive
513 .seek(SeekFrom::Start(12))
514 .wrap_err("While rewriting the header of the archive")
515 .wrap_err_with(|| c.clone())?;
516 let mut reader = BufReader::new(archive.as_file());
517 let mut headers = Vec::new();
518 let mut i = 0;
519 while let Ok(current_body_offset) = read_u32_xor(&mut reader, self.base_magic) {
520 if current_body_offset == 0 {
521 break;
522 }
523 let current_header_offset = reader
524 .stream_position()
525 .wrap_err_with(|| {
526 format!("While reading the path length of file #{i} in the archive")
527 })
528 .wrap_err_with(|| c.clone())?
529 .checked_sub(4)
530 .ok_or(Error::InvalidHeader)
531 .wrap_err_with(|| {
532 format!("While reading the path length of file #{i} in the archive")
533 })
534 .wrap_err_with(|| c.clone())?;
535 reader
536 .seek(SeekFrom::Current(8))
537 .wrap_err_with(|| {
538 format!("While reading the path length of file #{i} in the archive")
539 })
540 .wrap_err_with(|| c.clone())?;
541 let current_path_len = read_u32_xor(&mut reader, self.base_magic)
542 .wrap_err_with(|| {
543 format!("While reading the path length of file #{i} in the archive")
544 })
545 .wrap_err_with(|| c.clone())?;
546
547 let mut current_path = vec![0; current_path_len as usize];
548 reader.read_exact(&mut current_path).wrap_err_with(|| format!("While reading the path (path length = {current_path_len} of file #{i}) in the archive")).wrap_err_with(|| c.clone())?;
549 for (i, byte) in current_path.iter_mut().enumerate() {
550 let char = *byte ^ (self.base_magic >> (8 * (i % 4))) as u8;
551 if char == b'\\' {
552 *byte = b'/';
553 } else {
554 *byte = char;
555 }
556 }
557 let current_path =
558 String::from_utf8(current_path).map_err(|_| Error::PathUtf8Error).wrap_err_with(|| format!("While reading the path (path length = {current_path_len}) of file #{i} in the archive")).wrap_err_with(|| c.clone())?;
559
560 let current_body_offset = (current_body_offset as u64)
561 .checked_add_signed(to_len as i64 - from_len as i64)
562 .ok_or(Error::InvalidHeader)
563 .wrap_err_with(|| {
564 format!(
565 "While reading the header (path = {current_path}) of file #{i} in the archive"
566 )
567 })
568 .wrap_err_with(|| c.clone())?;
569 trie.get_file_mut(¤t_path)
570 .ok_or(Error::InvalidHeader)
571 .wrap_err_with(|| {
572 format!(
573 "While reading the header (path = {current_path}) of file #{i} in the archive"
574 )
575 })
576 .wrap_err_with(|| c.clone())?
577 .body_offset = current_body_offset;
578 headers.push((current_header_offset, current_body_offset as u32));
579 i += 1;
580 }
581 drop(reader);
582 let mut writer = BufWriter::new(archive.as_file());
583 for (i, (position, offset)) in headers.into_iter().enumerate() {
584 writer.seek(SeekFrom::Start(position))?;
585 writer
586 .write_all(&(offset ^ self.base_magic).to_le_bytes())
587 .wrap_err_with(|| {
588 format!(
589 "While rewriting the file offset of file #{i} to the archive"
590 )
591 })
592 .wrap_err_with(|| c.clone())?;
593 }
594 writer
595 .flush()
596 .wrap_err("While flushing the archive after writing its contents")
597 .wrap_err_with(|| c.clone())?;
598 drop(writer);
599 }
600
601 _ => return Err(Error::InvalidHeader.into()),
602 }
603
604 if to_len < from_len {
605 archive.set_len(
606 archive_len
607 .checked_add_signed(to_len as i64 - from_len as i64)
608 .ok_or(Error::InvalidHeader)
609 .wrap_err("While truncating the archive")
610 .wrap_err_with(|| c.clone())?,
611 )?;
612 archive
613 .flush()
614 .wrap_err("While flushing the archive after writing its contents")
615 .wrap_err_with(|| c.clone())?;
616 }
617 } else {
618 match self.version {
619 1 | 2 => {
620 let mut magic = old_entry.start_magic;
621 for _ in from.as_str().bytes() {
622 regress_magic(&mut magic);
623 }
624 archive
625 .seek(SeekFrom::Start(old_entry.header_offset + 4))
626 .wrap_err("While rewriting the path of the file in-place to the archive")
627 .wrap_err_with(|| c.clone())?;
628 archive
629 .write_all(
630 &to.as_str()
631 .bytes()
632 .map(|b| {
633 let b = if b == b'/' { b'\\' } else { b };
634 b ^ advance_magic(&mut magic) as u8
635 })
636 .collect_vec(),
637 )
638 .wrap_err("While rewriting the path of the file in-place to the archive")
639 .wrap_err_with(|| c.clone())?;
640 archive
641 .flush()
642 .wrap_err("While rewriting the path of the file in-place to the archive")
643 .wrap_err_with(|| c.clone())?;
644 }
645
646 3 => {
647 archive
648 .seek(SeekFrom::Start(old_entry.header_offset + 16))
649 .wrap_err("While rewriting the path of the file in-place to the archive")
650 .wrap_err_with(|| c.clone())?;
651 archive
652 .write_all(
653 &to.as_str()
654 .bytes()
655 .enumerate()
656 .map(|(i, b)| {
657 let b = if b == b'/' { b'\\' } else { b };
658 b ^ (self.base_magic >> (8 * (i % 4))) as u8
659 })
660 .collect_vec(),
661 )
662 .wrap_err("While rewriting the path of the file in-place to the archive")
663 .wrap_err_with(|| c.clone())?;
664 archive
665 .flush()
666 .wrap_err("While rewriting the path of the file in-place to the archive")
667 .wrap_err_with(|| c.clone())?;
668 }
669
670 _ => return Err(Error::InvalidHeader.into()),
671 }
672 }
673
674 Ok(())
675 }
676
677 fn create_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
678 let path = path.as_ref();
679 let mut trie = self.trie.write();
680 if trie.contains_file(path) {
681 return Err(Error::IoError(AlreadyExists.into())).wrap_err_with(|| {
682 format!(
683 "While creating a directory at {path:?} within a version {} archive",
684 self.version
685 )
686 });
687 }
688 trie.create_dir(path);
689 Ok(())
690 }
691
692 fn exists(&self, path: impl AsRef<camino::Utf8Path>) -> Result<bool> {
693 let trie = self.trie.read();
694 Ok(trie.contains(path))
695 }
696
697 fn remove_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
698 let path = path.as_ref();
699 let c = format!(
700 "While removing a directory at {path:?} within a version {} archive",
701 self.version
702 );
703 if !self.trie.read().contains_dir(path) {
704 return Err(Error::NotExist).wrap_err_with(|| c.clone());
705 }
706
707 let paths = self
708 .trie
709 .read()
710 .iter_prefix(path)
711 .ok_or(Error::NotExist)
712 .wrap_err_with(|| c.clone())?
713 .map(|(k, _)| k)
714 .collect_vec();
715 for file_path in paths {
716 self.remove_file(&file_path)
717 .wrap_err_with(|| format!("While removing a file {file_path:?} within the archive"))
718 .wrap_err_with(|| c.clone())?;
719 }
720
721 self.trie
722 .write()
723 .remove_dir(path)
724 .then_some(())
725 .ok_or(Error::NotExist)
726 .wrap_err_with(|| c.clone())?;
727 Ok(())
728 }
729
730 fn remove_file(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
731 let path = path.as_ref();
732 let path_len = path.as_str().bytes().len() as u64;
733 let mut archive = self.archive.lock();
734 let mut trie = self.trie.write();
735 let c = format!(
736 "While removing a file at {path:?} within a version {} archive",
737 self.version
738 );
739
740 let entry = *trie
741 .get_file(path)
742 .ok_or(Error::NotExist)
743 .wrap_err_with(|| c.clone())?;
744 let archive_len = archive.metadata().wrap_err_with(|| c.clone())?.size;
745
746 move_file_and_truncate(&mut archive, &mut trie, path, self.version, self.base_magic)
747 .wrap_err("While relocating the file header to the end of the archive")
748 .wrap_err_with(|| c.clone())?;
749
750 match self.version {
751 1 | 2 => {
752 archive
753 .set_len(
754 archive_len
755 .checked_sub(entry.size + path_len + 8)
756 .ok_or(Error::IoError(InvalidData.into()))
757 .wrap_err("While truncating the archive")
758 .wrap_err_with(|| c.clone())?,
759 )
760 .wrap_err("While truncating the archive")
761 .wrap_err_with(|| c.clone())?;
762 archive
763 .flush()
764 .wrap_err("While flushing the archive after writing its contents")
765 .wrap_err_with(|| c.clone())?;
766 }
767
768 3 => {
769 let mut tmp = crate::host::File::new()
771 .wrap_err("While creating a temporary file")
772 .wrap_err_with(|| c.clone())?;
773 archive
774 .seek(SeekFrom::Start(entry.header_offset + path_len + 16))
775 .wrap_err("While copying the header of the archive into a temporary file")
776 .wrap_err_with(|| c.clone())?;
777 std::io::copy(archive.as_file(), &mut tmp)
778 .wrap_err("While copying the header of the archive into a temporary file")
779 .wrap_err_with(|| c.clone())?;
780 tmp.flush()
781 .wrap_err("While copying the header of the archive into a temporary file")
782 .wrap_err_with(|| c.clone())?;
783 tmp.seek(SeekFrom::Start(0)).wrap_err("While copying a temporary file containing the archive header into the archive").wrap_err_with(|| c.clone())?;
784 archive.seek(SeekFrom::Start(entry.header_offset)).wrap_err("While copying a temporary file containing the archive header into the archive").wrap_err_with(|| c.clone())?;
785 std::io::copy(&mut tmp, archive.as_file()).wrap_err("While copying a temporary file containing the archive header into the archive").wrap_err_with(|| c.clone())?;
786
787 archive
788 .set_len(
789 archive_len
790 .checked_sub(entry.size + path_len + 16)
791 .ok_or(Error::IoError(InvalidData.into()))
792 .wrap_err("While truncating the archive")
793 .wrap_err_with(|| c.clone())?,
794 )
795 .wrap_err("While truncating the archive")
796 .wrap_err_with(|| c.clone())?;
797 archive
798 .flush()
799 .wrap_err("While flushing the archive after writing its contents")
800 .wrap_err_with(|| c.clone())?;
801 }
802
803 _ => {
804 return Err(Error::InvalidArchiveVersion(self.version)).wrap_err_with(|| c.clone())
805 }
806 }
807
808 trie.remove_file(path);
809 Ok(())
810 }
811
812 fn read_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<Vec<DirEntry>> {
813 let path = path.as_ref();
814 let trie = self.trie.read();
815 let c = format!(
816 "While reading the contents of the directory {path:?} in a version {} archive",
817 self.version
818 );
819 if let Some(iter) = trie.iter_dir(path) {
820 iter.map(|(name, _)| {
821 let path = if path == "" {
822 name.into()
823 } else {
824 format!("{path}/{name}").into()
825 };
826 let metadata = self
827 .metadata(&path)
828 .wrap_err_with(|| {
829 format!("While getting the metadata of {path:?} in the archive")
830 })
831 .wrap_err_with(|| c.clone())?;
832 Ok(DirEntry { path, metadata })
833 })
834 .try_collect()
835 } else {
836 Err(Error::NotExist).wrap_err_with(|| c.clone())
837 }
838 }
839}