diff --git a/src/bin/utils/cmds.rs b/src/bin/utils/cmds.rs index 75783fc95b..2f1b357739 100644 --- a/src/bin/utils/cmds.rs +++ b/src/bin/utils/cmds.rs @@ -84,6 +84,20 @@ pool is encrypted, setting this option has no effect on the prediction."), .action(ArgAction::Append) .help("Size of filesystem to be made for this pool. May be specified multiple times, one for each filesystem. Units are bytes. Must be at least 512 MiB and less than 4 PiB.") .next_line_help(true) + ) + .arg( + Arg::new("integrity_tag_size") + .long("integrity-tag-size") + .num_args(1) + .help("Size of the integrity checksums to be stored in the integrity metadata. The checksum size depends on the algorithm used for checksums. Units are bytes.") + .next_line_help(true) + ) + .arg( + Arg::new("integrity_journal_size") + .long("integrity-journal-size") + .num_args(1) + .help("Size of the integrity journal. Default is 128 MiB. Units are bytes.") + .next_line_help(true) ), Command::new("filesystem") .about("Predicts the space usage when creating a Stratis filesystem.") @@ -130,6 +144,22 @@ impl<'a> UtilCommand<'a> for StratisPredictUsage { .collect::, _>>() }) .transpose()?, + sub_m + .get_one::("integrity_journal_size") + .map(|s| s.parse::().map(Bytes::from)) + .transpose()? + .map(|b| { + if b % 4096u64 != Bytes(0) { + Err(format!("Value {b} is not aligned to 4096")) + } else { + Ok(b.sectors()) + } + }) + .transpose()?, + sub_m + .get_one::("integrity_tag_size") + .map(|s| s.parse::().map(Bytes::from)) + .transpose()?, LevelFilter::from_str( matches .get_one::("log-level") diff --git a/src/bin/utils/predict_usage.rs b/src/bin/utils/predict_usage.rs index a7757fa4b5..1c9d602f05 100644 --- a/src/bin/utils/predict_usage.rs +++ b/src/bin/utils/predict_usage.rs @@ -13,7 +13,10 @@ use serde_json::{json, Value}; use devicemapper::{Bytes, Sectors}; -use stratisd::engine::{crypt_metadata_size, integrity_meta_space, ThinPoolSizeParams, BDA}; +use stratisd::engine::{ + crypt_metadata_size, integrity_meta_space, ThinPoolSizeParams, BDA, + DEFAULT_INTEGRITY_BLOCK_SIZE, DEFAULT_INTEGRITY_JOURNAL_SIZE, DEFAULT_INTEGRITY_TAG_SIZE, +}; // 2^FS_SIZE_START_POWER is the logical size of the smallest Stratis // filesystem for which usage data exists in FSSizeLookup::internal, i.e., @@ -161,14 +164,23 @@ pub fn predict_filesystem_usage( Ok(()) } -fn predict_pool_metadata_usage(device_sizes: Vec) -> Result> { +fn predict_pool_metadata_usage( + device_sizes: Vec, + journal_size: Option, + tag_size: Option, +) -> Result> { let stratis_metadata_alloc = BDA::default().extended_size().sectors(); let stratis_avail_sizes = device_sizes .iter() .map(|&s| { info!("Total size of device: {:}", s); - let integrity_deduction = integrity_meta_space(s); + let integrity_deduction = integrity_meta_space( + s, + journal_size.unwrap_or(DEFAULT_INTEGRITY_JOURNAL_SIZE.sectors()), + DEFAULT_INTEGRITY_BLOCK_SIZE, + tag_size.unwrap_or(DEFAULT_INTEGRITY_TAG_SIZE), + ); info!( "Deduction for stratis metadata: {:}", stratis_metadata_alloc @@ -205,6 +217,8 @@ pub fn predict_pool_usage( overprovisioned: bool, device_sizes: Vec, filesystem_sizes: Option>, + journal_size: Option, + tag_size: Option, log_level: LevelFilter, ) -> Result<(), Box> { Builder::new().filter(None, log_level).init(); @@ -216,7 +230,7 @@ pub fn predict_pool_usage( let device_sizes = device_sizes.iter().map(|s| s.sectors()).collect::>(); let total_size: Sectors = device_sizes.iter().cloned().sum(); - let non_metadata_size = predict_pool_metadata_usage(device_sizes)?; + let non_metadata_size = predict_pool_metadata_usage(device_sizes, journal_size, tag_size)?; let size_params = ThinPoolSizeParams::new(non_metadata_size)?; let total_non_data = 2usize * size_params.meta_size() + size_params.mdv_size(); diff --git a/src/dbus_api/api/manager_3_0/methods.rs b/src/dbus_api/api/manager_3_0/methods.rs index 46a2d256e7..7f9cda5e7e 100644 --- a/src/dbus_api/api/manager_3_0/methods.rs +++ b/src/dbus_api/api/manager_3_0/methods.rs @@ -13,6 +13,7 @@ use futures::executor::block_on; use crate::{ dbus_api::{ + api::shared::EncryptionParams, blockdev::create_dbus_blockdev, consts, filesystem::create_dbus_filesystem, @@ -28,8 +29,6 @@ use crate::{ stratis::StratisError, }; -type EncryptionParams = (Option<(bool, String)>, Option<(bool, (String, String))>); - pub fn destroy_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let message: &Message = m.msg; let mut iter = message.iter_init(); @@ -329,6 +328,8 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { name, &devs.map(Path::new).collect::>(), EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(), + None, + None, ))); match create_result { Ok(pool_uuid_action) => match pool_uuid_action { diff --git a/src/dbus_api/api/manager_3_5/methods.rs b/src/dbus_api/api/manager_3_5/methods.rs index 8cd3a05093..e3f12233da 100644 --- a/src/dbus_api/api/manager_3_5/methods.rs +++ b/src/dbus_api/api/manager_3_5/methods.rs @@ -65,6 +65,8 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { name, &devs.map(Path::new).collect::>(), EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(), + None, + None, ))); match create_result { Ok(pool_uuid_action) => match pool_uuid_action { diff --git a/src/dbus_api/api/manager_3_8/api.rs b/src/dbus_api/api/manager_3_8/api.rs index 627d7fd955..37067df11c 100644 --- a/src/dbus_api/api/manager_3_8/api.rs +++ b/src/dbus_api/api/manager_3_8/api.rs @@ -6,7 +6,10 @@ use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property}; use crate::dbus_api::{ api::{ - manager_3_8::{methods::start_pool, props::get_stopped_pools}, + manager_3_8::{ + methods::{create_pool, start_pool}, + props::get_stopped_pools, + }, prop_conv::StoppedOrLockedPools, }, consts, @@ -31,6 +34,50 @@ pub fn start_pool_method(f: &Factory, TData>) -> Method, TData>) -> Method, TData> { + f.method("CreatePool", (), create_pool) + .in_arg(("name", "s")) + .in_arg(("devices", "as")) + // Optional key description of key in the kernel keyring + // b: true if the pool should be encrypted and able to be + // unlocked with a passphrase associated with this key description. + // s: key description + // + // Rust representation: (bool, String) + .in_arg(("key_desc", "(bs)")) + // Optional Clevis information for binding on initialization. + // b: true if the pool should be encrypted and able to be unlocked + // using Clevis. + // s: pin name + // s: JSON config for Clevis use + // + // Rust representation: (bool, (String, String)) + .in_arg(("clevis_info", "(b(ss))")) + // Optional journal size for integrity metadata reservation. + // b: true if the size should be specified. + // false if the default should be used. + // i: Integer representing journal size in bytes. + // + // Rust representation: (bool, u64) + .in_arg(("journal_size", "(bt)")) + // Optional tag size for integrity metadata reservation. + // b: true if the size should be specified. + // false if the default should be used. + // i: Integer representing tag size in bytes. + // + // Rust representation: (bool, u8) + .in_arg(("tag_size", "(by)")) + // In order from left to right: + // b: true if a pool was created and object paths were returned + // o: Object path for Pool + // a(o): Array of object paths for block devices + // + // Rust representation: (bool, (dbus::Path, Vec)) + .out_arg(("result", "(b(oao))")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + pub fn stopped_pools_property(f: &Factory, TData>) -> Property, TData> { f.property::(consts::STOPPED_POOLS_PROP, ()) .access(Access::Read) diff --git a/src/dbus_api/api/manager_3_8/methods.rs b/src/dbus_api/api/manager_3_8/methods.rs index 5b7a40d943..de1660d462 100644 --- a/src/dbus_api/api/manager_3_8/methods.rs +++ b/src/dbus_api/api/manager_3_8/methods.rs @@ -2,19 +2,30 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use dbus::{arg::OwnedFd, Message, Path}; +use std::path::Path; + +use dbus::{ + arg::{Array, OwnedFd}, + Message, +}; use dbus_tree::{MTSync, MethodInfo, MethodResult}; use futures::executor::block_on; +use devicemapper::Bytes; + use crate::{ dbus_api::{ + api::shared::EncryptionParams, blockdev::create_dbus_blockdev, filesystem::create_dbus_filesystem, pool::create_dbus_pool, types::{DbusErrorEnum, TData, OK_STRING}, util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option}, }, - engine::{Name, PoolIdentifier, PoolUuid, StartAction, UnlockMethod}, + engine::{ + CreateAction, EncryptionInfo, KeyDescription, Name, PoolIdentifier, PoolUuid, StartAction, + UnlockMethod, + }, stratis::StratisError, }; @@ -25,8 +36,12 @@ pub fn start_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let dbus_context = m.tree.get_data(); let default_return: ( bool, - (Path<'static>, Vec>, Vec>), - ) = (false, (Path::default(), Vec::new(), Vec::new())); + ( + dbus::Path<'static>, + Vec>, + Vec>, + ), + ) = (false, (dbus::Path::default(), Vec::new(), Vec::new())); let return_message = message.method_return(); let id_str: &str = get_next_arg(&mut iter, 0)?; @@ -130,3 +145,102 @@ pub fn start_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { OK_STRING.to_string(), )]) } + +pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let base_path = m.path.get_name(); + let message: &Message = m.msg; + let mut iter = message.iter_init(); + + let name: &str = get_next_arg(&mut iter, 0)?; + let devs: Array<'_, &str, _> = get_next_arg(&mut iter, 1)?; + let (key_desc_tuple, clevis_tuple): EncryptionParams = ( + Some(get_next_arg(&mut iter, 2)?), + Some(get_next_arg(&mut iter, 3)?), + ); + let journal_size_tuple: (bool, u64) = get_next_arg(&mut iter, 4)?; + let tag_size_tuple: (bool, u8) = get_next_arg(&mut iter, 5)?; + + let return_message = message.method_return(); + + let default_return: (bool, (dbus::Path<'static>, Vec>)) = + (false, (dbus::Path::default(), Vec::new())); + + let key_desc = match key_desc_tuple.and_then(tuple_to_option) { + Some(kds) => match KeyDescription::try_from(kds) { + Ok(kd) => Some(kd), + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }, + None => None, + }; + + let clevis_info = match clevis_tuple.and_then(tuple_to_option) { + Some((pin, json_string)) => match serde_json::from_str(json_string.as_str()) { + Ok(j) => Some((pin, j)), + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Serde(e)); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }, + None => None, + }; + + let journal_size = tuple_to_option(journal_size_tuple).map(Bytes::from); + let tag_size = tuple_to_option(tag_size_tuple).map(Bytes::from); + + let dbus_context = m.tree.get_data(); + let create_result = handle_action!(block_on(dbus_context.engine.create_pool( + name, + &devs.map(Path::new).collect::>(), + EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(), + journal_size, + tag_size, + ))); + match create_result { + Ok(pool_uuid_action) => match pool_uuid_action { + CreateAction::Created(uuid) => { + let guard = match block_on(dbus_context.engine.get_pool(PoolIdentifier::Uuid(uuid))) + { + Some(g) => g, + None => { + let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Msg( + format!("Pool with UUID {uuid} was successfully started but appears to have been removed before it could be exposed on the D-Bus") + )); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }; + + let (pool_name, pool_uuid, pool) = guard.as_tuple(); + let pool_path = + create_dbus_pool(dbus_context, base_path.clone(), &pool_name, pool_uuid, pool); + let mut bd_paths = Vec::new(); + for (bd_uuid, tier, bd) in pool.blockdevs() { + bd_paths.push(create_dbus_blockdev( + dbus_context, + pool_path.clone(), + bd_uuid, + tier, + bd, + )); + } + + Ok(vec![return_message.append3( + (true, (pool_path, bd_paths)), + DbusErrorEnum::OK as u16, + OK_STRING.to_string(), + )]) + } + CreateAction::Identity => Ok(vec![return_message.append3( + default_return, + DbusErrorEnum::OK as u16, + OK_STRING.to_string(), + )]), + }, + Err(x) => { + let (rc, rs) = engine_to_dbus_err_tuple(&x); + Ok(vec![return_message.append3(default_return, rc, rs)]) + } + } +} diff --git a/src/dbus_api/api/manager_3_8/mod.rs b/src/dbus_api/api/manager_3_8/mod.rs index 48fc8b4d99..eb29edbbb5 100644 --- a/src/dbus_api/api/manager_3_8/mod.rs +++ b/src/dbus_api/api/manager_3_8/mod.rs @@ -6,4 +6,4 @@ mod api; mod methods; mod props; -pub use api::{start_pool_method, stopped_pools_property}; +pub use api::{create_pool_method, start_pool_method, stopped_pools_property}; diff --git a/src/dbus_api/api/mod.rs b/src/dbus_api/api/mod.rs index 84533c63bf..3775a53d88 100644 --- a/src/dbus_api/api/mod.rs +++ b/src/dbus_api/api/mod.rs @@ -152,7 +152,7 @@ pub fn get_base_tree<'a>( ) .add( f.interface(consts::MANAGER_INTERFACE_NAME_3_8, ()) - .add_m(manager_3_5::create_pool_method(&f)) + .add_m(manager_3_8::create_pool_method(&f)) .add_m(manager_3_0::set_key_method(&f)) .add_m(manager_3_0::unset_key_method(&f)) .add_m(manager_3_0::list_keys_method(&f)) diff --git a/src/dbus_api/api/shared.rs b/src/dbus_api/api/shared.rs index e2132b6d88..ec21bccc13 100644 --- a/src/dbus_api/api/shared.rs +++ b/src/dbus_api/api/shared.rs @@ -22,6 +22,8 @@ use crate::{ engine::{AllLockReadGuard, DevUuid, Engine, FilesystemUuid, Pool, PoolUuid, StratisUuid}, }; +pub type EncryptionParams = (Option<(bool, String)>, Option<(bool, (String, String))>); + pub fn get_managed_objects_method( f: &Factory, TData>, ) -> Method, TData> { diff --git a/src/engine/engine.rs b/src/engine/engine.rs index dc20fd76fa..ed16f88e6e 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -381,6 +381,8 @@ pub trait Engine: Debug + Report + Send + Sync { name: &str, blockdev_paths: &[&Path], encryption_info: Option<&EncryptionInfo>, + journal_size: Option, + tag_size: Option, ) -> StratisResult>; /// Handle a libudev event. diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 17396d5fc2..468fac880b 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -16,6 +16,7 @@ pub use self::{ crypt_metadata_size, get_dm, get_dm_init, integrity_meta_space, register_clevis_token, set_up_crypt_logging, unshare_mount_namespace, StaticHeader, StaticHeaderResult, StratEngine, StratKeyActions, ThinPoolSizeParams, BDA, CLEVIS_TANG_TRUST_URL, + DEFAULT_INTEGRITY_BLOCK_SIZE, DEFAULT_INTEGRITY_JOURNAL_SIZE, DEFAULT_INTEGRITY_TAG_SIZE, }, structures::{AllLockReadGuard, ExclusiveGuard, SharedGuard, Table}, types::{ diff --git a/src/engine/sim_engine/engine.rs b/src/engine/sim_engine/engine.rs index 1761c2c7e9..09c408b044 100644 --- a/src/engine/sim_engine/engine.rs +++ b/src/engine/sim_engine/engine.rs @@ -14,6 +14,8 @@ use futures::executor::block_on; use serde_json::{json, Value}; use tokio::sync::RwLock; +use devicemapper::Bytes; + use crate::{ engine::{ engine::{Engine, HandleEvents, KeyActions, Pool, Report}, @@ -129,6 +131,8 @@ impl Engine for SimEngine { name: &str, blockdev_paths: &[&Path], encryption_info: Option<&EncryptionInfo>, + _: Option, + _: Option, ) -> StratisResult> { validate_name(name)?; let name = Name::new(name.to_owned()); @@ -440,6 +444,8 @@ mod tests { "name", strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None )) .unwrap() .changed() @@ -451,10 +457,11 @@ mod tests { /// Destroying a pool with devices should succeed fn destroy_pool_w_devices() { let engine = SimEngine::default(); - let uuid = test_async!(engine.create_pool("name", strs_to_paths!(["/s/d"]), None)) - .unwrap() - .changed() - .unwrap(); + let uuid = + test_async!(engine.create_pool("name", strs_to_paths!(["/s/d"]), None, None, None)) + .unwrap() + .changed() + .unwrap(); assert!(test_async!(engine.destroy_pool(uuid)).is_ok()); } @@ -463,10 +470,11 @@ mod tests { fn destroy_pool_w_filesystem() { let engine = SimEngine::default(); let pool_name = "pool_name"; - let uuid = test_async!(engine.create_pool(pool_name, strs_to_paths!(["/s/d"]), None)) - .unwrap() - .changed() - .unwrap(); + let uuid = + test_async!(engine.create_pool(pool_name, strs_to_paths!(["/s/d"]), None, None, None)) + .unwrap() + .changed() + .unwrap(); { let mut pool = test_async!(engine.get_mut_pool(PoolIdentifier::Uuid(uuid))).unwrap(); pool.create_filesystems(pool_name, uuid, &[("test", None, None)]) @@ -482,9 +490,9 @@ mod tests { let name = "name"; let engine = SimEngine::default(); let devices = strs_to_paths!(["/s/d"]); - test_async!(engine.create_pool(name, devices, None)).unwrap(); + test_async!(engine.create_pool(name, devices, None, None, None)).unwrap(); assert_matches!( - test_async!(engine.create_pool(name, devices, None)), + test_async!(engine.create_pool(name, devices, None, None, None)), Ok(CreateAction::Identity) ); } @@ -494,11 +502,13 @@ mod tests { fn create_pool_name_collision_different_args() { let name = "name"; let engine = SimEngine::default(); - test_async!(engine.create_pool(name, strs_to_paths!(["/s/d"]), None)).unwrap(); + test_async!(engine.create_pool(name, strs_to_paths!(["/s/d"]), None, None, None)).unwrap(); assert!(test_async!(engine.create_pool( name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .is_err()); } @@ -509,7 +519,7 @@ mod tests { let path = "/s/d"; let engine = SimEngine::default(); assert_matches!( - test_async!(engine.create_pool("name", strs_to_paths!([path, path]), None)) + test_async!(engine.create_pool("name", strs_to_paths!([path, path]), None, None, None)) .unwrap() .changed() .map( @@ -541,6 +551,8 @@ mod tests { name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -559,6 +571,8 @@ mod tests { "old_name", strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -578,6 +592,8 @@ mod tests { "old_name", strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -586,6 +602,8 @@ mod tests { new_name, strs_to_paths!(["/dev/four", "/dev/five", "/dev/six"]), None, + None, + None, )) .unwrap(); assert!(test_async!(engine.rename_pool(uuid, new_name)).is_err()); @@ -600,6 +618,8 @@ mod tests { new_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap(); assert_matches!( diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index 3edc16d0f7..b475b5170e 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -897,6 +897,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -917,6 +919,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -945,6 +949,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -976,6 +982,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -996,6 +1004,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1016,6 +1026,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1036,6 +1048,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1064,6 +1078,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1082,6 +1098,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1107,6 +1125,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1130,6 +1150,8 @@ mod tests { pool_name, strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() @@ -1157,6 +1179,8 @@ mod tests { "pool_name", strs_to_paths!(["/dev/one", "/dev/two", "/dev/three"]), None, + None, + None, )) .unwrap() .changed() diff --git a/src/engine/strat_engine/backstore/backstore/v2.rs b/src/engine/strat_engine/backstore/backstore/v2.rs index 3034098fe0..5be778c441 100644 --- a/src/engine/strat_engine/backstore/backstore/v2.rs +++ b/src/engine/strat_engine/backstore/backstore/v2.rs @@ -11,8 +11,9 @@ use either::Either; use serde_json::Value; use devicemapper::{ - CacheDev, CacheDevTargetTable, CacheTargetParams, DevId, Device, DmDevice, DmFlags, DmOptions, - LinearDev, LinearDevTargetParams, LinearTargetParams, Sectors, TargetLine, TargetTable, + Bytes, CacheDev, CacheDevTargetTable, CacheTargetParams, DevId, Device, DmDevice, DmFlags, + DmOptions, LinearDev, LinearDevTargetParams, LinearTargetParams, Sectors, TargetLine, + TargetTable, }; use crate::{ @@ -437,12 +438,14 @@ impl Backstore { devices: UnownedDevices, mda_data_size: MDADataSize, encryption_info: Option<&EncryptionInfo>, + integrity_journal_size: Option, + integrity_tag_size: Option, ) -> StratisResult { - let data_tier = DataTier::::new(BlockDevMgr::::initialize( - pool_uuid, - devices, - mda_data_size, - )?); + let data_tier = DataTier::::new( + BlockDevMgr::::initialize(pool_uuid, devices, mda_data_size)?, + integrity_journal_size, + integrity_tag_size, + ); let mut backstore = Backstore { data_tier, @@ -1248,8 +1251,15 @@ mod tests { let initdatadevs = get_devices(initdatapaths).unwrap(); let initcachedevs = get_devices(initcachepaths).unwrap(); - let mut backstore = - Backstore::initialize(pool_uuid, initdatadevs, MDADataSize::default(), None).unwrap(); + let mut backstore = Backstore::initialize( + pool_uuid, + initdatadevs, + MDADataSize::default(), + None, + None, + None, + ) + .unwrap(); invariant(&backstore); @@ -1340,8 +1350,15 @@ mod tests { let devices1 = get_devices(paths1).unwrap(); let devices2 = get_devices(paths2).unwrap(); - let mut backstore = - Backstore::initialize(pool_uuid, devices1, MDADataSize::default(), None).unwrap(); + let mut backstore = Backstore::initialize( + pool_uuid, + devices1, + MDADataSize::default(), + None, + None, + None, + ) + .unwrap(); for path in paths1 { assert_eq!( @@ -1404,6 +1421,8 @@ mod tests { "tang".to_string(), json!({"url": env::var("TANG_URL").expect("TANG_URL env var required"), "stratis:tang:trust_url": true}), ))), + None, + None, ) .unwrap(); backstore.alloc(pool_uuid, &[Sectors(512)]).unwrap(); @@ -1478,6 +1497,8 @@ mod tests { json!({"url": env::var("TANG_URL").expect("TANG_URL env var required"), "stratis:tang:trust_url": true}), ), )), + None, + None, ).unwrap(); cmd::udev_settle().unwrap(); diff --git a/src/engine/strat_engine/backstore/blockdev/mod.rs b/src/engine/strat_engine/backstore/blockdev/mod.rs index 986a2a8a2b..9fa01c99dc 100644 --- a/src/engine/strat_engine/backstore/blockdev/mod.rs +++ b/src/engine/strat_engine/backstore/blockdev/mod.rs @@ -95,17 +95,6 @@ pub trait InternalBlockDev { /// * Otherwise, `Some(_)` fn calc_new_size(&self) -> StratisResult>; - /// Grow the block device if the underlying physical device has grown in size. - /// Return an error and leave the size as is if the device has shrunk. - /// Do nothing if the device is the same size as recorded in the metadata. - /// - /// This method does not need to block IO to the extended crypt device prior - /// to rollback because of per-pool locking. Growing the device will acquire - /// an exclusive lock on the pool and therefore the thin pool cannot be - /// extended to use the larger or unencrypted block device size until the - /// transaction has been completed successfully. - fn grow(&mut self) -> StratisResult; - /// Load the pool-level metadata for the given block device. fn load_state(&self) -> StratisResult, &DateTime)>>; diff --git a/src/engine/strat_engine/backstore/blockdev/v1.rs b/src/engine/strat_engine/backstore/blockdev/v1.rs index f5002cd952..dd628fca0f 100644 --- a/src/engine/strat_engine/backstore/blockdev/v1.rs +++ b/src/engine/strat_engine/backstore/blockdev/v1.rs @@ -336,72 +336,16 @@ impl StratBlockDev { } } - #[cfg(test)] - pub fn invariant(&self) { - assert!(self.total_size() == self.used.size()); - } -} - -impl InternalBlockDev for StratBlockDev { - fn uuid(&self) -> DevUuid { - self.bda.dev_uuid() - } - - fn device(&self) -> &Device { - &self.dev - } - - fn physical_path(&self) -> &Path { - self.devnode() - } - - fn blksizes(&self) -> StratSectorSizes { - self.blksizes - } - - fn metadata_version(&self) -> StratSigblockVersion { - self.bda.sigblock_version() - } - - fn total_size(&self) -> BlockdevSize { - self.bda.dev_size() - } - - fn available(&self) -> Sectors { - self.used.available() - } - - fn metadata_size(&self) -> Sectors { - self.bda.extended_size().sectors() - } - - fn max_stratis_metadata_size(&self) -> MDADataSize { - self.bda.max_data_size() - } - - fn in_use(&self) -> bool { - self.used.used() > self.metadata_size() - } - - fn alloc(&mut self, size: Sectors) -> PerDevSegments { - self.used.alloc_front(size) - } - - fn calc_new_size(&self) -> StratisResult> { - let s = Self::scan_blkdev_size( - self.physical_path(), - self.underlying_device.crypt_handle().is_some(), - )?; - if Some(s) == self.new_size - || (self.new_size.is_none() && s == self.bda.dev_size().sectors()) - { - Ok(None) - } else { - Ok(Some(s)) - } - } - - fn grow(&mut self) -> StratisResult { + /// Grow the block device if the underlying physical device has grown in size. + /// Return an error and leave the size as is if the device has shrunk. + /// Do nothing if the device is the same size as recorded in the metadata. + /// + /// This method does not need to block IO to the extended crypt device prior + /// to rollback because of per-pool locking. Growing the device will acquire + /// an exclusive lock on the pool and therefore the thin pool cannot be + /// extended to use the larger or unencrypted block device size until the + /// transaction has been completed successfully. + pub fn grow(&mut self) -> StratisResult { /// Precondition: size > h.blkdev_size fn needs_rollback(bd: &mut StratBlockDev, size: BlockdevSize) -> StratisResult<()> { let mut f = OpenOptions::new() @@ -472,6 +416,70 @@ impl InternalBlockDev for StratBlockDev { } } } + #[cfg(test)] + pub fn invariant(&self) { + assert!(self.total_size() == self.used.size()); + } +} + +impl InternalBlockDev for StratBlockDev { + fn uuid(&self) -> DevUuid { + self.bda.dev_uuid() + } + + fn device(&self) -> &Device { + &self.dev + } + + fn physical_path(&self) -> &Path { + self.devnode() + } + + fn blksizes(&self) -> StratSectorSizes { + self.blksizes + } + + fn metadata_version(&self) -> StratSigblockVersion { + self.bda.sigblock_version() + } + + fn total_size(&self) -> BlockdevSize { + self.bda.dev_size() + } + + fn available(&self) -> Sectors { + self.used.available() + } + + fn metadata_size(&self) -> Sectors { + self.bda.extended_size().sectors() + } + + fn max_stratis_metadata_size(&self) -> MDADataSize { + self.bda.max_data_size() + } + + fn in_use(&self) -> bool { + self.used.used() > self.metadata_size() + } + + fn alloc(&mut self, size: Sectors) -> PerDevSegments { + self.used.alloc_front(size) + } + + fn calc_new_size(&self) -> StratisResult> { + let s = Self::scan_blkdev_size( + self.physical_path(), + self.underlying_device.crypt_handle().is_some(), + )?; + if Some(s) == self.new_size + || (self.new_size.is_none() && s == self.bda.dev_size().sectors()) + { + Ok(None) + } else { + Ok(Some(s)) + } + } fn load_state(&self) -> StratisResult, &DateTime)>> { let mut f = OpenOptions::new() diff --git a/src/engine/strat_engine/backstore/blockdev/v2.rs b/src/engine/strat_engine/backstore/blockdev/v2.rs index 4c872b0cb6..06c5a745f3 100644 --- a/src/engine/strat_engine/backstore/blockdev/v2.rs +++ b/src/engine/strat_engine/backstore/blockdev/v2.rs @@ -14,7 +14,7 @@ use std::{ use chrono::{DateTime, Utc}; use serde_json::Value; -use devicemapper::{Bytes, Device, Sectors, IEC}; +use devicemapper::{Bytes, Device, Sectors}; use crate::{ engine::{ @@ -43,14 +43,19 @@ use crate::{ /// Return the amount of space required for integrity for a device of the given size. /// -/// This is a slight overestimation for the sake of simplicity. Because it uses the whole disk +/// This default is a slight overestimation for the sake of simplicity. Because it uses the whole disk /// size, once the integrity metadata size is calculated, the remaining data size is now smaller /// than the metadata region could support for integrity. /// The result is divisible by 8 sectors. -pub fn integrity_meta_space(total_space: Sectors) -> Sectors { +pub fn integrity_meta_space( + total_space: Sectors, + journal_size: Sectors, + block_size: Bytes, + tag_size: Bytes, +) -> Sectors { Bytes(4096).sectors() - + Bytes::from(64 * IEC::Mi).sectors() - + Bytes::from((*total_space * 32u64 + 4095) & !4095).sectors() + + journal_size + + Bytes::from(((*total_space.bytes() / *block_size) * *tag_size + 4095) & !4095).sectors() } #[derive(Debug)] @@ -206,6 +211,61 @@ impl StratBlockDev { } } + /// Grow the block device if the underlying physical device has grown in size. + /// Return an error and leave the size as is if the device has shrunk. + /// Do nothing if the device is the same size as recorded in the metadata. + /// + /// This will also extend integrity metadata reservations according to the new + /// size of the device. + pub fn grow( + &mut self, + integrity_journal_size: Sectors, + integrity_block_size: Bytes, + integrity_tag_size: Bytes, + ) -> StratisResult { + let size = BlockdevSize::new(Self::scan_blkdev_size(self.devnode())?); + let metadata_size = self.bda.dev_size(); + match size.cmp(&metadata_size) { + Ordering::Less => Err(StratisError::Msg( + "The underlying device appears to have shrunk; you may experience data loss" + .to_string(), + )), + Ordering::Equal => Ok(false), + Ordering::Greater => { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .open(self.devnode())?; + let mut h = static_header(&mut f)?.ok_or_else(|| { + StratisError::Msg(format!( + "No static header found on device {}", + self.devnode().display() + )) + })?; + + h.blkdev_size = size; + let h = StaticHeader::write_header(&mut f, h, MetadataLocation::Both)?; + + self.bda.header = h; + self.used.increase_size(size.sectors()); + + let integrity_grow = integrity_meta_space( + size.sectors(), + integrity_journal_size, + integrity_block_size, + integrity_tag_size, + ) - self + .integrity_meta_allocs + .iter() + .map(|(_, len)| *len) + .sum::(); + self.alloc_int_meta_back(integrity_grow); + + Ok(true) + } + } + } + #[cfg(test)] pub fn invariant(&self) { assert!(self.total_size() == self.used.size()); @@ -269,46 +329,6 @@ impl InternalBlockDev for StratBlockDev { } } - fn grow(&mut self) -> StratisResult { - let size = BlockdevSize::new(Self::scan_blkdev_size(self.devnode())?); - let metadata_size = self.bda.dev_size(); - match size.cmp(&metadata_size) { - Ordering::Less => Err(StratisError::Msg( - "The underlying device appears to have shrunk; you may experience data loss" - .to_string(), - )), - Ordering::Equal => Ok(false), - Ordering::Greater => { - let mut f = OpenOptions::new() - .write(true) - .read(true) - .open(self.devnode())?; - let mut h = static_header(&mut f)?.ok_or_else(|| { - StratisError::Msg(format!( - "No static header found on device {}", - self.devnode().display() - )) - })?; - - h.blkdev_size = size; - let h = StaticHeader::write_header(&mut f, h, MetadataLocation::Both)?; - - self.bda.header = h; - self.used.increase_size(size.sectors()); - - let integrity_grow = integrity_meta_space(size.sectors()) - - self - .integrity_meta_allocs - .iter() - .map(|(_, len)| *len) - .sum::(); - self.alloc_int_meta_back(integrity_grow); - - Ok(true) - } - } - } - fn load_state(&self) -> StratisResult, &DateTime)>> { let mut f = OpenOptions::new().read(true).open(&*self.devnode)?; match (self.bda.load_state(&mut f)?, self.bda.last_update_time()) { diff --git a/src/engine/strat_engine/backstore/blockdevmgr.rs b/src/engine/strat_engine/backstore/blockdevmgr.rs index 04525081c3..6a1807886e 100644 --- a/src/engine/strat_engine/backstore/blockdevmgr.rs +++ b/src/engine/strat_engine/backstore/blockdevmgr.rs @@ -165,6 +165,15 @@ impl BlockDevMgr { self.encryption_info().is_some() } + pub fn grow(&mut self, dev: DevUuid) -> StratisResult { + let bd = self + .block_devs + .iter_mut() + .find(|bd| bd.uuid() == dev) + .ok_or_else(|| StratisError::Msg(format!("Block device with UUID {dev} not found")))?; + bd.grow() + } + #[cfg(test)] fn invariant(&self) { let pool_uuids = self @@ -234,6 +243,25 @@ impl BlockDevMgr { Ok(bdev_uuids) } + pub fn grow( + &mut self, + dev: DevUuid, + integrity_journal_size: Sectors, + integrity_block_size: Bytes, + integrity_tag_size: Bytes, + ) -> StratisResult { + let bd = self + .block_devs + .iter_mut() + .find(|bd| bd.uuid() == dev) + .ok_or_else(|| StratisError::Msg(format!("Block device with UUID {dev} not found")))?; + bd.grow( + integrity_journal_size, + integrity_block_size, + integrity_tag_size, + ) + } + #[cfg(test)] fn invariant(&self) { let pool_uuids = self @@ -467,15 +495,6 @@ where self.block_devs.iter().map(|bd| bd.metadata_size()).sum() } - pub fn grow(&mut self, dev: DevUuid) -> StratisResult { - let bd = self - .block_devs - .iter_mut() - .find(|bd| bd.uuid() == dev) - .ok_or_else(|| StratisError::Msg(format!("Block device with UUID {dev} not found")))?; - bd.grow() - } - /// Tear down devicemapper devices for the block devices in this BlockDevMgr. pub fn teardown(&mut self) -> StratisResult<()> { let errs = self.block_devs.iter_mut().fold(Vec::new(), |mut errs, bd| { diff --git a/src/engine/strat_engine/backstore/data_tier.rs b/src/engine/strat_engine/backstore/data_tier.rs index 04fd6fc9b5..4f74f1d573 100644 --- a/src/engine/strat_engine/backstore/data_tier.rs +++ b/src/engine/strat_engine/backstore/data_tier.rs @@ -7,7 +7,7 @@ #[cfg(test)] use std::collections::HashSet; -use devicemapper::Sectors; +use devicemapper::{Bytes, Sectors, IEC}; use crate::{ engine::{ @@ -32,6 +32,10 @@ use crate::{ stratis::StratisResult, }; +pub const DEFAULT_INTEGRITY_JOURNAL_SIZE: Bytes = Bytes(128 * IEC::Mi as u128); +pub const DEFAULT_INTEGRITY_BLOCK_SIZE: Bytes = Bytes(4 * IEC::Ki as u128); +pub const DEFAULT_INTEGRITY_TAG_SIZE: Bytes = Bytes(64u128); + /// Handles the lowest level, base layer of this tier. #[derive(Debug)] pub struct DataTier { @@ -39,6 +43,12 @@ pub struct DataTier { pub(super) block_mgr: BlockDevMgr, /// The list of segments granted by block_mgr and used by dm_device pub(super) segments: AllocatedAbove, + /// Integrity journal size. + integrity_journal_size: Option, + /// Integrity block size. + integrity_block_size: Option, + /// Integrity tag size. + integrity_tag_size: Option, } impl DataTier { @@ -52,6 +62,9 @@ impl DataTier { DataTier { block_mgr, segments: AllocatedAbove { inner: vec![] }, + integrity_journal_size: None, + integrity_block_size: None, + integrity_tag_size: None, } } @@ -97,6 +110,10 @@ impl DataTier { pub fn blockdevs_mut(&mut self) -> Vec<(DevUuid, &mut v1::StratBlockDev)> { self.block_mgr.blockdevs_mut() } + + pub fn grow(&mut self, dev: DevUuid) -> StratisResult { + self.block_mgr.grow(dev) + } } impl DataTier { @@ -105,16 +122,32 @@ impl DataTier { /// Initially 0 segments are allocated. /// /// WARNING: metadata changing event - pub fn new(mut block_mgr: BlockDevMgr) -> DataTier { + pub fn new( + mut block_mgr: BlockDevMgr, + integrity_journal_size: Option, + integrity_tag_size: Option, + ) -> DataTier { + let integrity_journal_size = + integrity_journal_size.unwrap_or_else(|| DEFAULT_INTEGRITY_JOURNAL_SIZE.sectors()); + let integrity_block_size = DEFAULT_INTEGRITY_BLOCK_SIZE; + let integrity_tag_size = integrity_tag_size.unwrap_or(DEFAULT_INTEGRITY_TAG_SIZE); for (_, bd) in block_mgr.blockdevs_mut() { // NOTE: over-allocates integrity metadata slightly. Some of the // total size of the device will not make use of the integrity // metadata. - bd.alloc_int_meta_back(integrity_meta_space(bd.total_size().sectors())); + bd.alloc_int_meta_back(integrity_meta_space( + bd.total_size().sectors(), + integrity_journal_size, + integrity_block_size, + integrity_tag_size, + )); } DataTier { block_mgr, segments: AllocatedAbove { inner: vec![] }, + integrity_journal_size: Some(integrity_journal_size), + integrity_block_size: Some(integrity_block_size), + integrity_tag_size: Some(integrity_tag_size), } } @@ -142,10 +175,10 @@ impl DataTier { assert_eq!(bds.len(), uuids.len()); for bd in bds { bd.alloc_int_meta_back(integrity_meta_space( - // NOTE: Subtracting metadata size works here because the only metadata currently - // recorded in a newly created block device is the BDA. If this becomes untrue in - // the future, this code will no longer work. - bd.total_size().sectors() - bd.metadata_size(), + bd.total_size().sectors(), + self.integrity_journal_size.expect("Must be some in V2"), + self.integrity_block_size.expect("Must be some in V2"), + self.integrity_tag_size.expect("Must be some in V2"), )); } Ok(uuids) @@ -179,6 +212,15 @@ impl DataTier { pub fn blockdevs_mut(&mut self) -> Vec<(DevUuid, &mut v2::StratBlockDev)> { self.block_mgr.blockdevs_mut() } + + pub fn grow(&mut self, dev: DevUuid) -> StratisResult { + self.block_mgr.grow( + dev, + self.integrity_journal_size.expect("Must be Some in V2"), + self.integrity_block_size.expect("Must be Some in V2"), + self.integrity_tag_size.expect("Must be Some in V2"), + ) + } } impl DataTier @@ -207,6 +249,9 @@ where Ok(DataTier { block_mgr, segments, + integrity_journal_size: data_tier_save.integrity_journal_size, + integrity_block_size: data_tier_save.integrity_block_size, + integrity_tag_size: data_tier_save.integrity_tag_size, }) } @@ -265,10 +310,6 @@ where self.block_mgr.load_state() } - pub fn grow(&mut self, dev: DevUuid) -> StratisResult { - self.block_mgr.grow(dev) - } - /// Return the partition of the block devs that are in use and those /// that are not. pub fn partition_by_use(&self) -> BlockDevPartition<'_, B> { @@ -301,6 +342,9 @@ where allocs: vec![self.segments.record()], devs: self.block_mgr.record(), }, + integrity_journal_size: self.integrity_journal_size, + integrity_block_size: self.integrity_block_size, + integrity_tag_size: self.integrity_tag_size, } } } @@ -439,7 +483,7 @@ mod tests { ) .unwrap(); - let mut data_tier = DataTier::::new(mgr); + let mut data_tier = DataTier::::new(mgr, None, None); data_tier.invariant(); // A data_tier w/ some devices but nothing allocated diff --git a/src/engine/strat_engine/backstore/mod.rs b/src/engine/strat_engine/backstore/mod.rs index 58add8867b..3311b24253 100644 --- a/src/engine/strat_engine/backstore/mod.rs +++ b/src/engine/strat_engine/backstore/mod.rs @@ -14,6 +14,9 @@ mod shared; pub use self::{ blockdev::v2::integrity_meta_space, + data_tier::{ + DEFAULT_INTEGRITY_BLOCK_SIZE, DEFAULT_INTEGRITY_JOURNAL_SIZE, DEFAULT_INTEGRITY_TAG_SIZE, + }, devices::{find_stratis_devs_by_uuid, get_devno_from_path, ProcessedPathInfos, UnownedDevices}, }; diff --git a/src/engine/strat_engine/engine.rs b/src/engine/strat_engine/engine.rs index 8c4fb8c504..ceb7788df4 100644 --- a/src/engine/strat_engine/engine.rs +++ b/src/engine/strat_engine/engine.rs @@ -17,7 +17,7 @@ use tokio::{ task::{spawn_blocking, JoinHandle}, }; -use devicemapper::DmNameBuf; +use devicemapper::{Bytes, DmNameBuf}; use crate::{ engine::{ @@ -494,9 +494,20 @@ impl Engine for StratEngine { name: &str, blockdev_paths: &[&Path], encryption_info: Option<&EncryptionInfo>, + journal_size: Option, + tag_size: Option, ) -> StratisResult> { validate_name(name)?; let name = Name::new(name.to_owned()); + let rounded_journal_size = match journal_size { + Some(b) => { + if b % 4096u64 != Bytes(0) { + return Err(StratisError::Msg(format!("{b} is not a multiple of 4096"))); + } + Some(b.sectors()) + } + None => None, + }; validate_paths(blockdev_paths)?; @@ -558,6 +569,8 @@ impl Engine for StratEngine { &cloned_name, unowned_devices, cloned_enc_info.as_ref(), + rounded_journal_size, + tag_size, ) })??; pools.insert(Name::new(name.to_string()), pool_uuid, AnyPool::V2(pool)); @@ -909,7 +922,7 @@ mod test { let engine = StratEngine::initialize().unwrap(); let name1 = "name1"; - let uuid1 = test_async!(engine.create_pool(name1, paths, None)) + let uuid1 = test_async!(engine.create_pool(name1, paths, None, None, None)) .unwrap() .changed() .unwrap(); @@ -1014,13 +1027,13 @@ mod test { let engine = StratEngine::initialize().unwrap(); let name1 = "name1"; - let uuid1 = test_async!(engine.create_pool(name1, paths1, None)) + let uuid1 = test_async!(engine.create_pool(name1, paths1, None, None, None)) .unwrap() .changed() .unwrap(); let name2 = "name2"; - let uuid2 = test_async!(engine.create_pool(name2, paths2, None)) + let uuid2 = test_async!(engine.create_pool(name2, paths2, None, None, None)) .unwrap() .changed() .unwrap(); @@ -1480,7 +1493,7 @@ mod test { fn test_start_stop(paths: &[&Path]) { let engine = StratEngine::initialize().unwrap(); let name = "pool_name"; - let uuid = test_async!(engine.create_pool(name, paths, None)) + let uuid = test_async!(engine.create_pool(name, paths, None, None, None)) .unwrap() .changed() .unwrap(); diff --git a/src/engine/strat_engine/mod.rs b/src/engine/strat_engine/mod.rs index 37fd872abf..1a1c12fd08 100644 --- a/src/engine/strat_engine/mod.rs +++ b/src/engine/strat_engine/mod.rs @@ -29,7 +29,10 @@ pub use self::{backstore::ProcessedPathInfos, pool::v1::StratPool}; pub use self::pool::inspection as pool_inspection; pub use self::{ - backstore::integrity_meta_space, + backstore::{ + integrity_meta_space, DEFAULT_INTEGRITY_BLOCK_SIZE, DEFAULT_INTEGRITY_JOURNAL_SIZE, + DEFAULT_INTEGRITY_TAG_SIZE, + }, crypt::{ crypt_metadata_size, register_clevis_token, set_up_crypt_logging, CLEVIS_TANG_TRUST_URL, }, diff --git a/src/engine/strat_engine/pool/v2.rs b/src/engine/strat_engine/pool/v2.rs index d28c5a1831..1b57ccffd0 100644 --- a/src/engine/strat_engine/pool/v2.rs +++ b/src/engine/strat_engine/pool/v2.rs @@ -153,14 +153,22 @@ impl StratPool { name: &str, devices: UnownedDevices, encryption_info: Option<&EncryptionInfo>, + journal_size: Option, + tag_size: Option, ) -> StratisResult<(PoolUuid, StratPool)> { let pool_uuid = PoolUuid::new_v4(); // FIXME: Initializing with the minimum MDA size is not necessarily // enough. If there are enough devices specified, more space will be // required. - let mut backstore = - Backstore::initialize(pool_uuid, devices, MDADataSize::default(), encryption_info)?; + let mut backstore = Backstore::initialize( + pool_uuid, + devices, + MDADataSize::default(), + encryption_info, + journal_size, + tag_size, + )?; let thinpool = ThinPool::::new( pool_uuid, @@ -1300,7 +1308,8 @@ mod tests { stratis_devices.error_on_not_empty().unwrap(); let name = "stratis-test-pool"; - let (uuid, mut pool) = StratPool::initialize(name, unowned_devices2, None).unwrap(); + let (uuid, mut pool) = + StratPool::initialize(name, unowned_devices2, None, None, None).unwrap(); invariant(&pool, name); let metadata1 = pool.record(name); @@ -1388,7 +1397,8 @@ mod tests { stratis_devices.error_on_not_empty().unwrap(); let name = "stratis-test-pool"; - let (uuid, mut pool) = StratPool::initialize(name, unowned_devices, None).unwrap(); + let (uuid, mut pool) = + StratPool::initialize(name, unowned_devices, None, None, None).unwrap(); invariant(&pool, name); pool.init_cache(uuid, name, cache_path, true).unwrap(); @@ -1428,7 +1438,8 @@ mod tests { stratis_devices.error_on_not_empty().unwrap(); let name = "stratis-test-pool"; - let (pool_uuid, mut pool) = StratPool::initialize(name, unowned_devices1, None).unwrap(); + let (pool_uuid, mut pool) = + StratPool::initialize(name, unowned_devices1, None, None, None).unwrap(); invariant(&pool, name); let fs_name = "stratis_test_filesystem"; @@ -1512,7 +1523,8 @@ mod tests { let (stratis_devices, unowned_devices) = devices.unpack(); stratis_devices.error_on_not_empty().unwrap(); - let (uuid, mut pool) = StratPool::initialize(name, unowned_devices, None).unwrap(); + let (uuid, mut pool) = + StratPool::initialize(name, unowned_devices, None, None, None).unwrap(); invariant(&pool, name); assert_eq!(pool.action_avail, ActionAvailability::Full); @@ -1528,7 +1540,7 @@ mod tests { let (stratis_devices, unowned_devices) = devices.unpack(); stratis_devices.error_on_not_empty().unwrap(); - let (_, mut pool) = StratPool::initialize(name, unowned_devices, None).unwrap(); + let (_, mut pool) = StratPool::initialize(name, unowned_devices, None, None, None).unwrap(); invariant(&pool, name); assert_eq!(pool.action_avail, ActionAvailability::Full); @@ -1568,7 +1580,7 @@ mod tests { stratis_devices.error_on_not_empty().unwrap(); let (pool_uuid, mut pool) = - StratPool::initialize(pool_name, unowned_devices, None).unwrap(); + StratPool::initialize(pool_name, unowned_devices, None, None, None).unwrap(); let (_, fs_uuid, _) = pool .create_filesystems( @@ -1678,7 +1690,7 @@ mod tests { fn test_grow_physical_pre_grow(paths: &[&Path]) { let pool_name = Name::new("pool".to_string()); let engine = StratEngine::initialize().unwrap(); - let pool_uuid = test_async!(engine.create_pool(&pool_name, paths, None)) + let pool_uuid = test_async!(engine.create_pool(&pool_name, paths, None, None, None)) .unwrap() .changed() .unwrap(); diff --git a/src/engine/strat_engine/serde_structs.rs b/src/engine/strat_engine/serde_structs.rs index a9c907139d..bb23225b44 100644 --- a/src/engine/strat_engine/serde_structs.rs +++ b/src/engine/strat_engine/serde_structs.rs @@ -14,7 +14,7 @@ use serde::{Serialize, Serializer}; -use devicemapper::{Sectors, ThinDevId}; +use devicemapper::{Bytes, Sectors, ThinDevId}; use crate::engine::types::{DevUuid, Features, FilesystemUuid}; @@ -117,6 +117,12 @@ pub struct BackstoreSave { #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct DataTierSave { pub blockdev: BlockDevSave, + #[serde(skip_serializing_if = "Option::is_none")] + pub integrity_journal_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub integrity_block_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub integrity_tag_size: Option, } #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] diff --git a/src/engine/strat_engine/thinpool/thinpool.rs b/src/engine/strat_engine/thinpool/thinpool.rs index aea8df8a71..d95d72b96d 100644 --- a/src/engine/strat_engine/thinpool/thinpool.rs +++ b/src/engine/strat_engine/thinpool/thinpool.rs @@ -3157,6 +3157,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let size = ThinPoolSizeParams::new(backstore.datatier_usable_size()).unwrap(); @@ -3259,6 +3261,8 @@ mod tests { first_devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3394,6 +3398,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); warn!("Available: {}", backstore.available_in_backstore()); @@ -3519,6 +3525,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3588,6 +3596,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3667,6 +3677,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3733,6 +3745,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3794,6 +3808,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -3900,6 +3916,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( @@ -4038,6 +4056,8 @@ mod tests { devices, MDADataSize::default(), None, + None, + None, ) .unwrap(); let mut pool = ThinPool::::new( diff --git a/src/jsonrpc/server/pool.rs b/src/jsonrpc/server/pool.rs index 104c9459ae..bbb365b509 100644 --- a/src/jsonrpc/server/pool.rs +++ b/src/jsonrpc/server/pool.rs @@ -45,7 +45,10 @@ pub async fn pool_create<'a>( enc_info: Option<&'a EncryptionInfo>, ) -> StratisResult { Ok( - match engine.create_pool(name, blockdev_paths, enc_info).await? { + match engine + .create_pool(name, blockdev_paths, enc_info, None, None) + .await? + { CreateAction::Created(_) => true, CreateAction::Identity => false, },