Skip to content
btrfs.rs 9.08 KiB
Newer Older
Luker's avatar
Luker committed
/*
 * Copyright (c) 2022 Luca Fulchir<luker@fenrirproject.org>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::errors::MkError;
use crate::Output;

/// Supported checksum algorithms
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum Checksum {
    Crc32c,
    Xxhash,
    Sha256,
    Blake2,
}
impl ToString for Checksum {
    fn to_string(&self) -> String {
        match self {
            Checksum::Crc32c => "crc32".to_owned(),
            Checksum::Xxhash => "xxhash".to_owned(),
            Checksum::Sha256 => "sha256".to_owned(),
            Checksum::Blake2 => "blake2".to_owned(),
        }
    }
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum Profile {
    Raid0,
    Raid1,
    Raid1c3,
    Raid1c4,
    Raid5,
    Raid6,
    Raid10,
    Single,
    Dup,
}
impl ToString for Profile {
    fn to_string(&self) -> String {
        match self {
            Profile::Raid0 => "raid0".to_owned(),
            Profile::Raid1 => "raid1".to_owned(),
            Profile::Raid1c3 => "raid1c3".to_owned(),
            Profile::Raid1c4 => "raid1c4".to_owned(),
            Profile::Raid5 => "raid5".to_owned(),
            Profile::Raid6 => "raid6".to_owned(),
            Profile::Raid10 => "raid10".to_owned(),
            Profile::Single => "single".to_owned(),
            Profile::Dup => "dup".to_owned(),
        }
    }
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum HasFeature {
    Enable(Feature),
    Disable(Feature),
}
impl ToString for HasFeature {
    fn to_string(&self) -> String {
        match self {
            HasFeature::Enable(rf) => rf.to_string(),
            HasFeature::Disable(rf) => "^".to_owned() + &rf.to_string(),
        }
    }
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum Feature {
    MixedBg,
    Extref,
    Raid56,
    SkinnyMetadata,
    NoHoles,
    Raid1c34,
    Zoned,
}
impl ToString for Feature {
    fn to_string(&self) -> String {
        match self {
            Feature::MixedBg => "mixed-bg".to_owned(),
            Feature::Extref => "extref".to_owned(),
            Feature::Raid56 => "raid56".to_owned(),
            Feature::SkinnyMetadata => "skinny-metadata".to_owned(),
            Feature::NoHoles => "no-holes".to_owned(),
            Feature::Raid1c34 => "raid1c34".to_owned(),
            Feature::Zoned => "zoned".to_owned(),
        }
    }
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum HasRuntimeFeature {
    Enable(RuntimeFeature),
    Disable(RuntimeFeature),
}
impl ToString for HasRuntimeFeature {
    fn to_string(&self) -> String {
        match self {
            HasRuntimeFeature::Enable(rf) => rf.to_string(),
            HasRuntimeFeature::Disable(rf) => "^".to_owned() + &rf.to_string(),
        }
    }
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum RuntimeFeature {
    Quota,
    FreeSpaceTree,
}
impl ToString for RuntimeFeature {
    fn to_string(&self) -> String {
        match self {
            RuntimeFeature::Quota => "quota".to_owned(),
            RuntimeFeature::FreeSpaceTree => "free-space-tree".to_owned(),
        }
    }
}

/// options to pass to `mkfs.btrfs`
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub enum Options {
    /// `--label LABEL` specify label
    Label(String),
    /// `--uuid UUID` specify the uuid to use
    Uuid(::uuid::Uuid),
    /// `--byte-count <size>` filesystem size (default:max)
    ByteCount(u64),
    /// `--csum <type>` checksum algorithm (default: crc32)
    CheckSum(Checksum),
    /// `--data <profile>` set the various raid levels
    Data(Profile),
    /// `--metadata <profile>` metadata raid levels
    Metadata(Profile),
    /// `--mixed` mix metadata and data blocks (better disk usage, worse
    /// performance)
    Mixed,
    /// `--nodesize <size>`
    ///
    /// Specify the nodesize, the tree block size in which btrfs stores
    /// metadata. The default value is 16KiB (16384) or the page size,
    /// whichever is bigger. Must be a multiple of the sectorsize
    /// and a power of 2, but not larger than 64KiB (65536). Leafsize always
    /// equals nodesize and the options are aliases.
    ///
    /// Smaller node size increases fragmentation but leads to taller b-trees
    /// which in turn leads to lower locking contention. Higher node sizes
    /// give better packing and less fragmentation at the cost of more
    /// expensive memory operations while updating the metadata blocks.
    ///     Note
    ///     versions up to 3.11 set the nodesize to 4k.
    NodeSize(u16),
    /// `--sectorsize <size>`
    ///
    /// Specify the sectorsize, the minimum data block allocation unit.
    ///
    /// The default value is the page size and is autodetected. If the
    /// sectorsize differs from the page size, the created filesystem may
    /// not be mountable by the running kernel. Therefore it
    /// is not recommended to use this option unless you are going to mount it
    /// on a system with the appropriate page size.
    SectorSize(u64),
    /// `--nodiscard` do not execute TRIM operations
    NoDiscard,
    /// `--rootdir <dir>` copy the root level from the given directory
    RootDir(::std::path::PathBuf),
    /// `--shrink` shrink the filesystem to the minimum size
    /// only usable with `RootDir`
    Shrink,
    /// `--features <feature>`
    /// List of features to enable or disable at mkfs time
    Features(Vec<HasFeature>),
    /// `--runtime-features <feature>`
    /// List of runtime features to enable or disable
    RuntimeFeatures(Vec<HasRuntimeFeature>),
    /// `--force`
    /// overwrite even with already-existing filesystems
    Force,
}

impl ToString for Options {
    fn to_string(&self) -> String {
        match self {
            Options::Label(l) => "--label \"".to_owned() + &l + "\"",
            Options::Uuid(u) => "--uuid ".to_owned() + &u.to_string(),
            Options::ByteCount(bytes) => {
                "--byte-count ".to_owned() + &bytes.to_string()
            }
            Options::CheckSum(checksum) => {
                "--checksum ".to_owned() + &checksum.to_string()
            }
            Options::Data(profile) => {
                "--data ".to_owned() + &profile.to_string()
            }
            Options::Metadata(profile) => {
                "--metadata ".to_owned() + &profile.to_string()
            }
            Options::Mixed => "--mixed".to_owned(),
            Options::NodeSize(size) => {
                "--nodeisize ".to_owned() + &size.to_string()
            }
            Options::SectorSize(size) => {
                "--sectorsize ".to_owned() + &size.to_string()
            }
            Options::NoDiscard => "--nodiscard".to_owned(),
            Options::RootDir(path) => {
                "--rootdir ".to_owned() + path.to_str().unwrap()
            }
            Options::Shrink => "--shrink".to_owned(),
            Options::Features(features) => {
                let mut v = Vec::with_capacity(features.len());
                for f in features.iter() {
                    v.push(f.to_string());
                }
                "--features ".to_owned() + &v.join(",")
            }
            Options::RuntimeFeatures(runtime_features) => {
                let mut v = Vec::with_capacity(runtime_features.len());
                for f in runtime_features.iter() {
                    v.push(f.to_string());
                }
                "--runtime-features ".to_owned() + &v.join(",")
            }
            Options::Force => "--force".to_owned(),
        }
    }
}

/// Wrapper on `mkfs.btrfs`
pub(in crate) struct Btrfs {
    _opt: Vec<Options>,
    _out: Output,
}

impl Btrfs {
    /// new instance of swap fs
    pub fn new() -> Self {
        Btrfs {
            _opt: Vec::new(),
            _out: Output::Quiet,
        }
    }
    pub fn set_option(&mut self, option: Options) -> &Self {
        self._opt.push(option);
        self
    }
    pub fn set_output(&mut self, dst: Output) -> &Self {
        self._out = dst;
        self
    }

    /// write the filesystem to disk
    #[must_use]
    pub fn write(mut self, dev: &::udev::Device) -> Result<(), MkError> {
        let mut str_opts = Vec::<String>::with_capacity(self._opt.len() + 1);
        for opt in self._opt.iter() {
            str_opts.push(opt.to_string());
        }
        let node = super::dev_to_node(dev)?;
        str_opts.push(node);
        let (out, err) = self._out.to_pair();
        super::run("mkfs.btrfs", str_opts, out, err)
    }
}