Skip to main content

mitosis/clone_backend/
copy.rs

1//! Copy back end for disks without reflink: the same tree walk as the reflink back end, but each
2//! regular file is copied in full. Chosen only by `backend = "copy"`, never by `auto`.
3
4use super::{
5    apparent_size, clone_tree, create_dst, fs_create_dir, fs_remove_dir, fs_rename_dir, io_error,
6    CloneBackend,
7};
8use crate::error::Result;
9use std::fs::{File, Metadata};
10use std::path::Path;
11
12/// Printed on every clone create.
13pub const SLOW_WARNING: &str =
14    "backend \"copy\" makes full copies; clones are slow and use full disk space";
15
16pub struct CopyBackend;
17
18impl CloneBackend for CopyBackend {
19    fn name(&self) -> &'static str {
20        "copy"
21    }
22
23    fn check_supported(&self, _path: &Path) -> Result<()> {
24        Ok(())
25    }
26
27    fn clone_dir(&self, src: &Path, dst: &Path) -> Result<()> {
28        clone_tree(src, dst, &copy_file)
29    }
30
31    fn create_dir(&self, path: &Path) -> Result<()> {
32        fs_create_dir(path)
33    }
34
35    fn remove_dir(&self, path: &Path) -> Result<()> {
36        fs_remove_dir(path)
37    }
38
39    fn rename_dir(&self, from: &Path, to: &Path) -> Result<()> {
40        fs_rename_dir(from, to)
41    }
42
43    /// Nothing is shared, so every byte is private.
44    fn private_size(&self, path: &Path) -> Option<u64> {
45        apparent_size(path).ok()
46    }
47}
48
49/// Also the file copy of the ZFS back end's `copy_dir`.
50pub(super) fn copy_file(src: &Path, dst: &Path, md: &Metadata) -> Result<()> {
51    let mut s = File::open(src).map_err(|e| io_error("open", src, e))?;
52    let mut d = create_dst(dst, md)?;
53    std::io::copy(&mut s, &mut d).map_err(|e| io_error("copy", src, e))?;
54    Ok(())
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use std::fs::{create_dir, read_link, read_to_string, symlink_metadata, write};
61
62    #[test]
63    fn copy_backend_clone_is_isolated() {
64        let tmp = tempfile::tempdir().unwrap();
65        let (src, dst) = (tmp.path().join("src"), tmp.path().join("dst"));
66        create_dir(&src).unwrap();
67        write(src.join("a.txt"), "hello").unwrap();
68        create_dir(src.join("sub")).unwrap();
69        write(src.join("sub/b.txt"), "x".repeat(1_000_000)).unwrap();
70        std::os::unix::fs::symlink("/elsewhere", src.join("link")).unwrap();
71
72        CopyBackend.check_supported(tmp.path()).unwrap();
73        CopyBackend.clone_dir(&src, &dst).unwrap();
74        write(dst.join("a.txt"), "changed").unwrap();
75        assert_eq!(read_to_string(src.join("a.txt")).unwrap(), "hello");
76        assert!(symlink_metadata(dst.join("link"))
77            .unwrap()
78            .file_type()
79            .is_symlink());
80        assert_eq!(
81            read_link(dst.join("link")).unwrap(),
82            Path::new("/elsewhere")
83        );
84        assert_eq!(CopyBackend.private_size(&dst), Some(1_000_005 + 2));
85        assert!(CopyBackend.clone_dir(&src, &dst).is_err());
86    }
87}