volaris_tools/
erase.rs

1//! This provides functionality for "shredding" a file.
2//!
3//! This will not be effective on flash storage, and if you are planning to release a program that uses this function, I'd recommend putting the default number of passes to 1.
4
5use std::io::{Read, Seek, Write};
6use std::path::Path;
7use std::sync::Arc;
8
9use crate::storage::Storage;
10
11#[derive(Debug)]
12pub enum Error {
13    OpenFile,
14    Overwrite(crate::overwrite::Error),
15    RemoveFile,
16}
17
18impl std::fmt::Display for Error {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            Error::OpenFile => f.write_str("Unable to open file"),
22            Error::Overwrite(inner) => write!(f, "Unable to overwrite file: {inner}"),
23            Error::RemoveFile => f.write_str("Unable to remove file"),
24        }
25    }
26}
27
28impl std::error::Error for Error {}
29
30pub struct Request<P: AsRef<Path>> {
31    pub path: P,
32    pub passes: i32,
33}
34
35pub fn execute<RW, P>(stor: Arc<impl Storage<RW> + 'static>, req: Request<P>) -> Result<(), Error>
36where
37    RW: Read + Write + Seek,
38    P: AsRef<Path>,
39{
40    let file = stor.write_file(req.path).map_err(|_| Error::OpenFile)?;
41    let buf_capacity = stor.file_len(&file).map_err(|_| Error::OpenFile)?;
42
43    crate::overwrite::execute(crate::overwrite::Request {
44        writer: file
45            .try_writer()
46            .expect("We're confident that we're in writing mode"),
47        buf_capacity,
48        passes: req.passes,
49    })
50    .map_err(Error::Overwrite)?;
51
52    stor.remove_file(file).map_err(|_| Error::RemoveFile)?;
53
54    Ok(())
55}
56
57#[cfg(test)]
58mod tests {
59    use std::path::PathBuf;
60
61    use crate::storage::InMemoryStorage;
62
63    use super::*;
64
65    #[test]
66    fn should_erase_file() {
67        let stor = Arc::new(InMemoryStorage::default());
68        stor.add_hello_txt();
69
70        let req = Request {
71            path: "hello.txt",
72            passes: 2,
73        };
74        match execute(stor.clone(), req) {
75            Ok(_) => assert_eq!(stor.files().get(&PathBuf::from("hello.txt")), None),
76            _ => unreachable!(),
77        }
78    }
79
80    #[test]
81    fn should_not_open_file() {
82        let stor = Arc::new(InMemoryStorage::default());
83
84        let req = Request {
85            path: "hello.txt",
86            passes: 2,
87        };
88        match execute(stor, req) {
89            Err(Error::OpenFile) => {}
90            _ => unreachable!(),
91        }
92    }
93}