/* * Copyright (c) 2022 Luca Fulchir * * 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 ` filesystem size (default:max) ByteCount(u64), /// `--csum ` checksum algorithm (default: crc32) CheckSum(Checksum), /// `--data ` set the various raid levels Data(Profile), /// `--metadata ` metadata raid levels Metadata(Profile), /// `--mixed` mix metadata and data blocks (better disk usage, worse /// performance) Mixed, /// `--nodesize ` /// /// 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 ` /// /// 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 ` 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 ` /// List of features to enable or disable at mkfs time Features(Vec), /// `--runtime-features ` /// List of runtime features to enable or disable RuntimeFeatures(Vec), /// `--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, _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::::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) } }