Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifications to integrity metadata reservation #3716

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/bin/utils/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down Expand Up @@ -130,6 +144,22 @@ impl<'a> UtilCommand<'a> for StratisPredictUsage {
.collect::<Result<Vec<_>, _>>()
})
.transpose()?,
sub_m
.get_one::<String>("integrity_journal_size")
.map(|s| s.parse::<u64>().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::<String>("integrity_tag_size")
.map(|s| s.parse::<u8>().map(Bytes::from))
.transpose()?,
LevelFilter::from_str(
matches
.get_one::<String>("log-level")
Expand Down
22 changes: 18 additions & 4 deletions src/bin/utils/predict_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.,
Expand Down Expand Up @@ -161,14 +164,23 @@ pub fn predict_filesystem_usage(
Ok(())
}

fn predict_pool_metadata_usage(device_sizes: Vec<Sectors>) -> Result<Sectors, Box<dyn Error>> {
fn predict_pool_metadata_usage(
device_sizes: Vec<Sectors>,
journal_size: Option<Sectors>,
tag_size: Option<Bytes>,
) -> Result<Sectors, Box<dyn Error>> {
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
Expand Down Expand Up @@ -205,6 +217,8 @@ pub fn predict_pool_usage(
overprovisioned: bool,
device_sizes: Vec<Bytes>,
filesystem_sizes: Option<Vec<Bytes>>,
journal_size: Option<Sectors>,
tag_size: Option<Bytes>,
log_level: LevelFilter,
) -> Result<(), Box<dyn Error>> {
Builder::new().filter(None, log_level).init();
Expand All @@ -216,7 +230,7 @@ pub fn predict_pool_usage(
let device_sizes = device_sizes.iter().map(|s| s.sectors()).collect::<Vec<_>>();
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();
Expand Down
5 changes: 3 additions & 2 deletions src/dbus_api/api/manager_3_0/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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>, TData>) -> MethodResult {
let message: &Message = m.msg;
let mut iter = message.iter_init();
Expand Down Expand Up @@ -329,6 +328,8 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
name,
&devs.map(Path::new).collect::<Vec<&Path>>(),
EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(),
None,
None,
)));
match create_result {
Ok(pool_uuid_action) => match pool_uuid_action {
Expand Down
2 changes: 2 additions & 0 deletions src/dbus_api/api/manager_3_5/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
name,
&devs.map(Path::new).collect::<Vec<&Path>>(),
EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(),
None,
None,
)));
match create_result {
Ok(pool_uuid_action) => match pool_uuid_action {
Expand Down
49 changes: 48 additions & 1 deletion src/dbus_api/api/manager_3_8/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,6 +34,50 @@ pub fn start_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TDa
.out_arg(("return_string", "s"))
}

pub fn create_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, 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<dbus::Path>))
.out_arg(("result", "(b(oao))"))
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn stopped_pools_property(f: &Factory<MTSync<TData>, TData>) -> Property<MTSync<TData>, TData> {
f.property::<StoppedOrLockedPools, _>(consts::STOPPED_POOLS_PROP, ())
.access(Access::Read)
Expand Down
122 changes: 118 additions & 4 deletions src/dbus_api/api/manager_3_8/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -25,8 +36,12 @@ pub fn start_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
let dbus_context = m.tree.get_data();
let default_return: (
bool,
(Path<'static>, Vec<Path<'static>>, Vec<Path<'static>>),
) = (false, (Path::default(), Vec::new(), Vec::new()));
(
dbus::Path<'static>,
Vec<dbus::Path<'static>>,
Vec<dbus::Path<'static>>,
),
) = (false, (dbus::Path::default(), Vec::new(), Vec::new()));
let return_message = message.method_return();

let id_str: &str = get_next_arg(&mut iter, 0)?;
Expand Down Expand Up @@ -130,3 +145,102 @@ pub fn start_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
OK_STRING.to_string(),
)])
}

pub fn create_pool(m: &MethodInfo<'_, MTSync<TData>, 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<dbus::Path<'static>>)) =
(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::<Vec<&Path>>(),
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)])
}
}
}
2 changes: 1 addition & 1 deletion src/dbus_api/api/manager_3_8/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
2 changes: 1 addition & 1 deletion src/dbus_api/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions src/dbus_api/api/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MTSync<TData>, TData>,
) -> Method<MTSync<TData>, TData> {
Expand Down
2 changes: 2 additions & 0 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ pub trait Engine: Debug + Report + Send + Sync {
name: &str,
blockdev_paths: &[&Path],
encryption_info: Option<&EncryptionInfo>,
journal_size: Option<Bytes>,
tag_size: Option<Bytes>,
) -> StratisResult<CreateAction<PoolUuid>>;

/// Handle a libudev event.
Expand Down
1 change: 1 addition & 0 deletions src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
Loading
Loading