Skip to content

Commit

Permalink
Add the ability to start and stop pools
Browse files Browse the repository at this point in the history
This commit replaces the previous approach of unlocking encrypted pools
with full support for starting and stopping pools. Locked encrypted
pools are now simply considered stopped.

The behavior of stopping pools is as follows. A pool that is started
when the system shuts down will be started after booting the system
again. A pool that is stopped will remain stopped and will not be set up
until it is started again. This operation requires a change to the
metadata schema to store whether a pool is stopped or started. Migration
from older versions of stratisd is handled.

Stopping an encrypted pool will lock all of the encrypted devices.
  • Loading branch information
jbaublitz committed Jun 23, 2022
1 parent 83d0b8d commit ca53152
Show file tree
Hide file tree
Showing 46 changed files with 1,774 additions and 517 deletions.
8 changes: 4 additions & 4 deletions dracut/90stratis-clevis/stratis-clevis-rootfs-setup
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if [ -z "$STRATIS_ROOTFS_UUID" ]; then
fi

i=0
while ! stratis-min pool is-locked "$STRATIS_ROOTFS_UUID" >/dev/null; do
while ! stratis-min pool is-stopped "$STRATIS_ROOTFS_UUID" >/dev/null; do
echo Waiting on pool with UUID $STRATIS_ROOTFS_UUID...
sleep 1
if [ "$i" = 5 ]; then
Expand All @@ -15,10 +15,10 @@ while ! stratis-min pool is-locked "$STRATIS_ROOTFS_UUID" >/dev/null; do
i=$(($i + 1))
done

if $(stratis-min pool is-locked "$STRATIS_ROOTFS_UUID"); then
if $(stratis-min pool is-stopped "$STRATIS_ROOTFS_UUID"); then
if $(stratis-min pool is-bound "$STRATIS_ROOTFS_UUID"); then
if ! stratis-min pool unlock clevis "$STRATIS_ROOTFS_UUID"; then
echo Failed to unlock pool with UUID $STRATIS_ROOTFS_UUID using Clevis >&2
if ! stratis-min pool start --unlock-method=clevis "$STRATIS_ROOTFS_UUID"; then
echo Failed to start pool with UUID $STRATIS_ROOTFS_UUID using Clevis >&2
exit 1
fi
fi
Expand Down
23 changes: 15 additions & 8 deletions dracut/90stratis/stratis-rootfs-setup
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if [ -z "$STRATIS_ROOTFS_UUID" ]; then
fi

i=0
while ! stratis-min pool is-locked "$STRATIS_ROOTFS_UUID" >/dev/null; do
while ! stratis-min pool is-stopped "$STRATIS_ROOTFS_UUID" >/dev/null; do
echo Waiting on pool with UUID $STRATIS_ROOTFS_UUID...
sleep 1
if [ "$i" = 5 ]; then
Expand All @@ -15,12 +15,19 @@ while ! stratis-min pool is-locked "$STRATIS_ROOTFS_UUID" >/dev/null; do
i=$(($i + 1))
done

if $(stratis-min pool is-locked "$STRATIS_ROOTFS_UUID"); then
if ! plymouth ask-for-password \
--command="stratis-min pool unlock --prompt keyring $STRATIS_ROOTFS_UUID" \
--prompt="Enter password for Stratis pool with UUID $STRATIS_ROOTFS_UUID containing root filesystem" \
--number-of-tries=3; then
echo Failed to unlock pool with UUID $STRATIS_ROOTFS_UUID using a passphrase >&2
exit 1
if $(stratis-min pool is-stopped "$STRATIS_ROOTFS_UUID"); then
if $(stratis-min pool is-encrypted "$STRATIS_ROOTFS_UUID"); then
if ! plymouth ask-for-password \
--command="stratis-min pool start --prompt --unlock-method=keyring $STRATIS_ROOTFS_UUID" \
--prompt="Enter password for Stratis pool with UUID $STRATIS_ROOTFS_UUID containing root filesystem" \
--number-of-tries=3; then
echo Failed to start pool with UUID $STRATIS_ROOTFS_UUID using a passphrase >&2
exit 1
fi
else
if ! stratis-min pool start "$STRATIS_ROOTFS_UUID"; then
echo Failed to start pool with UUID $STRATIS_ROOTFS_UUID >&2
exit 1
fi
fi
fi
36 changes: 22 additions & 14 deletions src/bin/stratis-min/stratis-min.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,20 @@ fn parse_args() -> Command<'static> {
Command::new("unset").arg(Arg::new("key_desc").required(true)),
]),
Command::new("pool").subcommands(vec![
Command::new("unlock")
.arg(Arg::new("unlock_method").required(true))
.arg(Arg::new("pool_uuid").required(false))
Command::new("start")
.arg(Arg::new("pool_uuid").required(true))
.arg(
Arg::new("unlock_method")
.long("--unlock-method")
.takes_value(true),
)
.arg(
Arg::new("prompt")
.long("--prompt")
.takes_value(false)
.requires("pool_uuid"),
.requires("unlock_method"),
),
Command::new("stop").arg(Arg::new("pool_uuid").required(true)),
Command::new("create")
.arg(Arg::new("name").required(true))
.arg(
Expand Down Expand Up @@ -117,7 +122,7 @@ fn parse_args() -> Command<'static> {
),
Command::new("destroy").arg(Arg::new("name").required(true)),
Command::new("is-encrypted").arg(Arg::new("pool_uuid").required(true)),
Command::new("is-locked").arg(Arg::new("pool_uuid").required(true)),
Command::new("is-stopped").arg(Arg::new("pool_uuid").required(true)),
Command::new("is-bound").arg(Arg::new("pool_uuid").required(true)),
Command::new("has-passphrase").arg(Arg::new("pool_uuid").required(true)),
Command::new("clevis-pin").arg(Arg::new("pool_uuid").required(true)),
Expand Down Expand Up @@ -169,20 +174,23 @@ fn main() -> Result<(), String> {
Ok(())
}
} else if let Some(subcommand) = args.subcommand_matches("pool") {
if let Some(args) = subcommand.subcommand_matches("unlock") {
let unlock_method =
UnlockMethod::try_from(args.value_of("unlock_method").expect("required"))?;
let uuid = match args.value_of("pool_uuid") {
Some(u) => Some(PoolUuid::parse_str(u)?),
if let Some(args) = subcommand.subcommand_matches("start") {
let uuid = PoolUuid::parse_str(args.value_of("pool_uuid").expect("required"))?;
let unlock_method = match args.value_of("unlock_method") {
Some(um) => Some(UnlockMethod::try_from(um)?),
None => None,
};
let prompt = args.is_present("prompt");
if prompt && unlock_method == UnlockMethod::Clevis {
if prompt && unlock_method == Some(UnlockMethod::Clevis) {
return Err(Box::new(StratisError::Msg(
"--prompt and an unlock_method of clevis are mutally exclusive".to_string(),
)));
}
pool::pool_unlock(unlock_method, uuid, prompt)?;
pool::pool_start(uuid, unlock_method, prompt)?;
Ok(())
} else if let Some(args) = subcommand.subcommand_matches("stop") {
let uuid = PoolUuid::parse_str(args.value_of("pool_uuid").expect("required"))?;
pool::pool_stop(uuid)?;
Ok(())
} else if let Some(args) = subcommand.subcommand_matches("create") {
let paths = get_paths_from_args(args);
Expand Down Expand Up @@ -241,10 +249,10 @@ fn main() -> Result<(), String> {
let uuid = PoolUuid::parse_str(uuid_str)?;
println!("{}", pool::pool_is_encrypted(uuid)?,);
Ok(())
} else if let Some(args) = subcommand.subcommand_matches("is-locked") {
} else if let Some(args) = subcommand.subcommand_matches("is-stopped") {
let uuid_str = args.value_of("pool_uuid").expect("required");
let uuid = PoolUuid::parse_str(uuid_str)?;
println!("{}", pool::pool_is_locked(uuid)?,);
println!("{}", pool::pool_is_stopped(uuid)?,);
Ok(())
} else if let Some(args) = subcommand.subcommand_matches("is-bound") {
let uuid_str = args.value_of("pool_uuid").expect("required");
Expand Down
4 changes: 2 additions & 2 deletions src/dbus_api/api/manager_3_0/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
},
props::{get_locked_pools, get_version},
},
prop_conv::LockedPools,
prop_conv::StoppedOrLockedPools,
},
consts,
types::TData,
Expand Down Expand Up @@ -180,7 +180,7 @@ pub fn locked_pools_property<E>(
where
E: 'static + Engine,
{
f.property::<LockedPools, _>(consts::LOCKED_POOLS_PROP, ())
f.property::<StoppedOrLockedPools, _>(consts::LOCKED_POOLS_PROP, ())
.access(Access::Read)
.emits_changed(EmitsChangedSignal::True)
.on_get(get_locked_pools)
Expand Down
82 changes: 82 additions & 0 deletions src/dbus_api/api/manager_3_2/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// 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_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property};

use crate::{
dbus_api::{
api::{
manager_3_2::{
methods::{refresh_state, start_pool, stop_pool},
props::get_stopped_pools,
},
prop_conv::StoppedOrLockedPools,
},
consts,
types::TData,
},
engine::Engine,
};

pub fn start_pool_method<E>(
f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Method<MTSync<TData<E>>, TData<E>>
where
E: 'static + Engine,
{
f.method("StartPool", (), start_pool)
.in_arg(("pool_uuid", "s"))
.in_arg(("unlock_method", "(bs)"))
// In order from left to right:
// b: true if the pool was newly started
// o: pool path
// oa: block device paths
// oa: filesystem paths
//
// Rust representation: bool
.out_arg(("result", "(b(oaoao))"))
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn stop_pool_method<E>(
f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Method<MTSync<TData<E>>, TData<E>>
where
E: 'static + Engine,
{
f.method("StopPool", (), stop_pool)
.in_arg(("pool", "o"))
// In order from left to right:
// b: true if the pool was newly stopped
// s: string representation of UUID of stopped pool
//
// Rust representation: (bool, String)
.out_arg(("result", "(bs)"))
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn refresh_state_method<E>(
f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Method<MTSync<TData<E>>, TData<E>>
where
E: 'static + Engine,
{
f.method("RefreshState", (), refresh_state)
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn stopped_pools_property<E>(
f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
E: 'static + Engine,
{
f.property::<StoppedOrLockedPools, _>(consts::STOPPED_POOLS_PROP, ())
.access(Access::Read)
.emits_changed(EmitsChangedSignal::True)
.on_get(get_stopped_pools)
}
Loading

0 comments on commit ca53152

Please sign in to comment.