fencryption_lib/
pack.rs

1//! Create/unpack packs.
2//!
3//! A pack is a file with all the contents of a directory
4//! inside of it.
5//!
6//! Uses [`metadata`] in order to ease the process of
7//! storing/separating the files inside of it.
8
9use std::{
10    fs::OpenOptions,
11    io::{self, Read, Write},
12    path::{Path, PathBuf},
13};
14
15use serde::{Deserialize, Serialize};
16
17use crate::{
18    io::{stream, DEFAULT_BUF_LEN},
19    metadata,
20    walk_dir::walk_dir,
21};
22
23#[derive(Serialize, Deserialize, Debug)]
24struct PackEntryMetadata(PathBuf, u64);
25
26impl PackEntryMetadata {
27    pub fn new<P>(path: P, file_len: u64) -> Self
28    where
29        P: AsRef<Path>,
30    {
31        PackEntryMetadata(path.as_ref().to_owned(), file_len)
32    }
33
34    pub fn path(&self) -> PathBuf {
35        self.0.to_owned()
36    }
37
38    pub fn file_len(&self) -> u64 {
39        self.1
40    }
41}
42
43/// Enum of the different possible pack errors.
44#[derive(Debug)]
45pub enum ErrorKind {
46    Io(io::Error),
47    MetadataError(metadata::ErrorKind),
48    ConversionError,
49    PathAlreadyExists,
50    PathNotFound,
51    PathError,
52}
53type Result<T, E = ErrorKind> = std::result::Result<T, E>;
54
55/// Create the pack file with the contents of the specified
56/// directory.
57pub fn create<P1, P2>(input_dir_path: P1, output_dir_path: P2) -> Result<()>
58where
59    P1: AsRef<Path>,
60    P2: AsRef<Path>,
61{
62    let mut dest = OpenOptions::new()
63        .write(true)
64        .create(true)
65        .open(output_dir_path.as_ref())
66        .map_err(|e| ErrorKind::Io(e))?;
67
68    let walk_dir = walk_dir(&input_dir_path).map_err(|e| ErrorKind::Io(e))?;
69
70    for entry in walk_dir {
71        let entry = entry.map_err(|e| ErrorKind::Io(e))?;
72
73        if entry.path().is_file() {
74            let mut source = OpenOptions::new()
75                .read(true)
76                .open(entry.path())
77                .map_err(|e| ErrorKind::Io(e))?;
78
79            // Creates file header.
80            let metadata = metadata::encode(PackEntryMetadata::new(
81                input_dir_path
82                    .as_ref()
83                    .strip_prefix(entry.path())
84                    .map_err(|_| ErrorKind::PathError)?,
85                entry.metadata().map_err(|e| ErrorKind::Io(e))?.len(),
86            ))
87            .map_err(|e| ErrorKind::MetadataError(e))?;
88
89            // Writes file header to the pack.
90            dest.write_all(
91                &[
92                    (metadata.len() as u16).to_be_bytes().as_ref(),
93                    metadata.as_ref(),
94                ]
95                .concat(),
96            )
97            .map_err(|e| ErrorKind::Io(e))?;
98
99            stream(&mut source, &mut dest).map_err(|e| ErrorKind::Io(e))?;
100        }
101    }
102
103    Ok(())
104}
105
106/// Unpack the pack from the associated pack file (fails
107/// if the pack file doesn't exist).
108pub fn unpack<P1, P2>(input_dir_path: P1, output_dir_path: P2) -> Result<()>
109where
110    P1: AsRef<Path>,
111    P2: AsRef<Path>,
112{
113    let mut source = OpenOptions::new()
114        .read(true)
115        .open(input_dir_path.as_ref())
116        .map_err(|e| ErrorKind::Io(e))?;
117
118    loop {
119        let mut len_bytes = [0u8; 2];
120        source
121            .read_exact(&mut len_bytes)
122            .map_err(|e| ErrorKind::Io(e))?;
123        let len = u16::from_be_bytes(len_bytes) as usize;
124        let mut metadata_bytes = vec![0u8; len];
125        source
126            .read_exact(&mut metadata_bytes)
127            .map_err(|e| ErrorKind::Io(e))?;
128        let metadata = metadata::decode::<PackEntryMetadata>(&metadata_bytes)
129            .map_err(|e| ErrorKind::MetadataError(e))?;
130
131        let mut file = OpenOptions::new()
132            .write(true)
133            .create(true)
134            .open(output_dir_path.as_ref().join(metadata.path()))
135            .map_err(|e| ErrorKind::Io(e))?;
136
137        let file_len = metadata.file_len();
138
139        // Gets the number of chunks to read before reaching
140        // the last bytes.
141        let chunks = file_len.div_euclid(DEFAULT_BUF_LEN as u64);
142        // Gets the number of the remaining bytes (after
143        // reading all chunks).
144        let rem_len = file_len.rem_euclid(DEFAULT_BUF_LEN as u64) as usize;
145
146        // Reads all chunks and writes them to the output
147        // file.
148        let mut buffer = [0u8; DEFAULT_BUF_LEN];
149        for _ in 0..chunks {
150            source
151                .read_exact(&mut buffer)
152                .map_err(|e| ErrorKind::Io(e))?;
153            file.write_all(&buffer).map_err(|e| ErrorKind::Io(e))?;
154        }
155
156        // Reads the remaining bytes and writes them to
157        // the output file.
158        let mut last = vec![0u8; rem_len];
159        source.read_exact(&mut last).map_err(|e| ErrorKind::Io(e))?;
160        file.write_all(&last).map_err(|e| ErrorKind::Io(e))?;
161    }
162}