summaryrefslogtreecommitdiff
path: root/src/commands/subvolume.rs
blob: 6f5f2f2b2dac0533f7abb8bb39d99b8561245bc8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::{env, path::PathBuf};

use anyhow::{Context, Result};
use bch_bindgen::c::BCH_SUBVOL_SNAPSHOT_RO;
use clap::{Parser, Subcommand};

use crate::wrappers::handle::BcachefsHandle;

#[derive(Parser, Debug)]
pub struct Cli {
    #[command(subcommand)]
    subcommands: Subcommands,
}

/// Subvolumes-related commands
#[derive(Subcommand, Debug)]
enum Subcommands {
    #[command(visible_aliases = ["new"])]
    Create {
        /// Paths
        #[arg(required = true)]
        targets: Vec<PathBuf>,
    },

    #[command(visible_aliases = ["del"])]
    Delete {
        /// Path
        #[arg(required = true)]
        targets: Vec<PathBuf>,
    },

    #[command(allow_missing_positional = true, visible_aliases = ["snap"])]
    Snapshot {
        /// Make snapshot read only
        #[arg(long, short)]
        read_only: bool,
        source:    Option<PathBuf>,
        dest:      PathBuf,
    },
}

pub fn subvolume(argv: Vec<String>) -> Result<()> {
    let cli = Cli::parse_from(argv);

    match cli.subcommands {
        Subcommands::Create { targets } => {
            for target in targets {
                let target = if target.is_absolute() {
                    target
                } else {
                    env::current_dir()
                        .map(|p| p.join(target))
                        .context("unable to get current directory")?
                };

                if let Some(dirname) = target.parent() {
                    let fs = unsafe { BcachefsHandle::open(dirname) };
                    fs.create_subvolume(target)
                        .context("Failed to create the subvolume")?;
                }
            }
        }
        Subcommands::Delete { targets } => {
            for target in targets {
                let target = target
                    .canonicalize()
                    .context("subvolume path does not exist or can not be canonicalized")?;

                if let Some(dirname) = target.parent() {
                    let fs = unsafe { BcachefsHandle::open(dirname) };
                    fs.delete_subvolume(target)
                        .context("Failed to delete the subvolume")?;
                }
            }
        }
        Subcommands::Snapshot {
            read_only,
            source,
            dest,
        } => {
            if let Some(dirname) = dest.parent() {
                let dot = PathBuf::from(".");
                let dir = if dirname.as_os_str().is_empty() {
                    &dot
                } else {
                    dirname
                };
                let fs = unsafe { BcachefsHandle::open(dir) };

                fs.snapshot_subvolume(
                    if read_only {
                        BCH_SUBVOL_SNAPSHOT_RO
                    } else {
                        0x0
                    },
                    source,
                    dest,
                )
                .context("Failed to snapshot the subvolume")?;
            }
        }
    }

    Ok(())
}