Skip to content

Commit e3d4954

Browse files
committed
Refactor encryption to use a read lock during operation
1 parent b2582a1 commit e3d4954

File tree

8 files changed

+204
-74
lines changed

8 files changed

+204
-74
lines changed

src/dbus_api/pool/pool_3_9/methods.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,43 @@ pub fn encrypt_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
8686
return_message
8787
);
8888

89-
let mut guard = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message);
90-
let (name, _, pool) = guard.as_mut_tuple();
91-
92-
let result = handle_action!(
93-
pool.encrypt_pool(&name, pool_uuid, &ei),
89+
let read_guard = get_pool!(dbus_context.engine; pool_uuid; default_return; return_message);
90+
91+
// Check if operation is idempotent and complete all operations before handling result
92+
let create_result = handle_action!(
93+
read_guard.encrypt_pool_idem_check()
94+
.and_then(|action| match action {
95+
CreateAction::Identity => Ok(action),
96+
CreateAction::Created(_) => {
97+
let mut guard = block_on(dbus_context.engine.upgrade_pool(read_guard));
98+
let result = guard.start_encrypt_pool(pool_uuid, &ei);
99+
let result = result.and_then(|(sector_size, key_info)| {
100+
let guard = guard.downgrade();
101+
guard
102+
.do_encrypt_pool(pool_uuid, sector_size, key_info)
103+
.map(|_| guard)
104+
});
105+
let result = result.and_then(|guard| {
106+
let mut guard = block_on(dbus_context.engine.upgrade_pool(guard));
107+
let (name, _, _) = guard.as_mut_tuple();
108+
guard
109+
.finish_encrypt_pool(&name, pool_uuid)
110+
.map(|_| (guard, action))
111+
});
112+
result.map(|(_, action)| action)
113+
}
114+
}),
94115
dbus_context,
95116
pool_path.get_name()
96117
);
97-
let msg = match result {
118+
119+
let msg = match create_result {
120+
Ok(CreateAction::Identity) => {
121+
return_message.append3(default_return, DbusErrorEnum::OK as u16, OK_STRING.to_string())
122+
}
98123
Ok(CreateAction::Created(_)) => {
99-
let encryption_info = match pool.encryption_info().clone() {
124+
let guard = get_pool!(dbus_context.engine; pool_uuid; default_return; return_message);
125+
let encryption_info = match guard.encryption_info().clone() {
100126
Some(Either::Left(ei)) => ei,
101127
Some(Either::Right(_)) => {
102128
unreachable!("online reencryption disabled on metadata V1")
@@ -118,9 +144,6 @@ pub fn encrypt_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
118144
dbus_context.push_pool_encryption_status_change(pool_path.get_name(), true);
119145
return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string())
120146
}
121-
Ok(CreateAction::Identity) => {
122-
return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string())
123-
}
124147
Err(err) => {
125148
let (rc, rs) = engine_to_dbus_err_tuple(&err);
126149
return_message.append3(default_return, rc, rs)

src/engine/engine.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,26 @@ pub trait Pool: Debug + Send + Sync {
399399
limit: Option<Bytes>,
400400
) -> StratisResult<PropChangeAction<Option<Sectors>>>;
401401

402+
/// Check idempotence of pool encrypt operation.
403+
fn encrypt_pool_idem_check(&self) -> StratisResult<CreateAction<EncryptedDevice>>;
404+
402405
/// Encrypted an unencrypted pool.
403-
fn encrypt_pool(
406+
fn start_encrypt_pool(
404407
&mut self,
405-
name: &Name,
406408
pool_uuid: PoolUuid,
407409
encryption_info: &InputEncryptionInfo,
408-
) -> StratisResult<CreateAction<EncryptedDevice>>;
410+
) -> StratisResult<(u32, (u32, SizedKeyMemory))>;
411+
412+
/// Encrypted an unencrypted pool.
413+
fn do_encrypt_pool(
414+
&self,
415+
pool_uuid: PoolUuid,
416+
sector_size: u32,
417+
key_info: (u32, SizedKeyMemory),
418+
) -> StratisResult<()>;
419+
420+
/// Update internal data structures with the result of the encryption operation.
421+
fn finish_encrypt_pool(&mut self, name: &Name, pool_uuid: PoolUuid) -> StratisResult<()>;
409422

410423
/// Start reencryption of an encrypted pool.
411424
///

src/engine/sim_engine/pool.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use itertools::Itertools;
1414
use serde_json::{Map, Value};
1515

1616
use devicemapper::{Bytes, Sectors, IEC};
17+
use libcryptsetup_rs::SafeMemHandle;
1718

1819
use crate::{
1920
engine::{
@@ -930,20 +931,31 @@ impl Pool for SimPool {
930931
}
931932
}
932933

933-
fn encrypt_pool(
934-
&mut self,
935-
_: &Name,
936-
_: PoolUuid,
937-
enc: &InputEncryptionInfo,
938-
) -> StratisResult<CreateAction<EncryptedDevice>> {
934+
fn encrypt_pool_idem_check(&self) -> StratisResult<CreateAction<EncryptedDevice>> {
939935
if self.encryption_info.is_some() {
940936
Ok(CreateAction::Identity)
941937
} else {
942-
self.encryption_info = convert_encryption_info(Some(enc), None)?;
943938
Ok(CreateAction::Created(EncryptedDevice))
944939
}
945940
}
946941

942+
fn start_encrypt_pool(
943+
&mut self,
944+
_: PoolUuid,
945+
enc: &InputEncryptionInfo,
946+
) -> StratisResult<(u32, (u32, SizedKeyMemory))> {
947+
self.encryption_info = convert_encryption_info(Some(enc), None)?;
948+
Ok((0, (0, SizedKeyMemory::new(SafeMemHandle::alloc(1)?, 0))))
949+
}
950+
951+
fn do_encrypt_pool(&self, _: PoolUuid, _: u32, _: (u32, SizedKeyMemory)) -> StratisResult<()> {
952+
Ok(())
953+
}
954+
955+
fn finish_encrypt_pool(&mut self, _: &Name, _: PoolUuid) -> StratisResult<()> {
956+
Ok(())
957+
}
958+
947959
fn start_reencrypt_pool(&mut self) -> StratisResult<Vec<(u32, SizedKeyMemory, u32)>> {
948960
if self.encryption_info.is_none() {
949961
Err(StratisError::Msg(

src/engine/strat_engine/backstore/backstore/v2.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44

55
// Code to handle the backing store of a pool.
66

7-
use std::{
8-
cmp,
9-
iter::once,
10-
path::{Path, PathBuf},
11-
};
7+
use std::{cmp, iter::once, path::PathBuf};
128

139
use chrono::{DateTime, Utc};
1410
use either::Either;
@@ -1295,37 +1291,46 @@ impl Backstore {
12951291
self.data_tier.grow(dev)
12961292
}
12971293

1298-
pub fn encrypt(
1294+
pub fn prepare_encrypt(
12991295
&mut self,
13001296
pool_uuid: PoolUuid,
13011297
thinpool: &mut ThinPool<Self>,
13021298
offset: Sectors,
13031299
offset_direction: OffsetDirection,
13041300
encryption_info: &InputEncryptionInfo,
1305-
) -> StratisResult<()> {
1301+
) -> StratisResult<(u32, (u32, SizedKeyMemory))> {
13061302
let (dm_name, _) = format_backstore_ids(pool_uuid, CacheRole::Cache);
1307-
let unencrypted_path = PathBuf::from(format!("/dev/mapper/{}", &dm_name.to_string()));
1308-
let (mut device, sector_size, key_info) =
1303+
let unencrypted_path = PathBuf::from(DEVICEMAPPER_PATH).join(dm_name.to_string());
1304+
let (sector_size, key_info) =
13091305
CryptHandle::setup_encrypt(pool_uuid, thinpool, &unencrypted_path, encryption_info)?;
13101306

13111307
thinpool.suspend()?;
13121308
self.shift_alloc_offset(offset, offset_direction);
1313-
let set_device_res = get_devno_from_path(Path::new(&format!(
1314-
"/dev/mapper/{}",
1315-
&*format_crypt_backstore_name(&pool_uuid)
1316-
)))
1309+
let set_device_res = get_devno_from_path(
1310+
&PathBuf::from(DEVICEMAPPER_PATH)
1311+
.join(format_crypt_backstore_name(&pool_uuid).to_string()),
1312+
)
13171313
.and_then(|devno| thinpool.set_device(devno, offset, offset_direction));
13181314
thinpool.resume()?;
13191315
set_device_res?;
13201316

1321-
let handle = CryptHandle::do_encrypt(
1322-
&mut device,
1323-
&unencrypted_path,
1324-
pool_uuid,
1325-
sector_size,
1326-
key_info,
1327-
)?;
1317+
Ok((sector_size, key_info))
1318+
}
13281319

1320+
pub fn do_encrypt(
1321+
pool_uuid: PoolUuid,
1322+
sector_size: u32,
1323+
key_info: (u32, SizedKeyMemory),
1324+
) -> StratisResult<()> {
1325+
let (dm_name, _) = format_backstore_ids(pool_uuid, CacheRole::Cache);
1326+
let unencrypted_path = PathBuf::from(DEVICEMAPPER_PATH).join(dm_name.to_string());
1327+
CryptHandle::do_encrypt(&unencrypted_path, pool_uuid, sector_size, key_info)
1328+
}
1329+
1330+
pub fn finish_encrypt(&mut self, pool_uuid: PoolUuid) -> StratisResult<()> {
1331+
let (dm_name, _) = format_backstore_ids(pool_uuid, CacheRole::Cache);
1332+
let unencrypted_path = PathBuf::from(DEVICEMAPPER_PATH).join(dm_name.to_string());
1333+
let handle = CryptHandle::finish_encrypt(&unencrypted_path, pool_uuid)?;
13291334
self.cap_device.enc = Some(Either::Right(handle));
13301335
Ok(())
13311336
}

src/engine/strat_engine/crypt/handle/v2.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ impl CryptHandle {
726726
thinpool: &ThinPool<v2::Backstore>,
727727
unencrypted_path: &Path,
728728
encryption_info: &InputEncryptionInfo,
729-
) -> StratisResult<(CryptDevice, u32, (u32, SizedKeyMemory))> {
729+
) -> StratisResult<(u32, (u32, SizedKeyMemory))> {
730730
let mut tmp_file = tempfile::NamedTempFile::new()?;
731731
tmp_file
732732
.as_file_mut()
@@ -804,7 +804,7 @@ impl CryptHandle {
804804
CryptActivate::SHARED,
805805
)?;
806806

807-
Ok((device, sector_size, (keyslot, key)))
807+
Ok((sector_size, (keyslot, key)))
808808
}
809809

810810
/// Perform the online encryption operation that was set up.
@@ -813,12 +813,12 @@ impl CryptHandle {
813813
/// Precondition: crypt device was already added as the backing device for the thin pool
814814
/// failure to do so will result in corruption
815815
pub fn do_encrypt(
816-
device: &mut CryptDevice,
817816
unencrypted_path: &Path,
818817
pool_uuid: PoolUuid,
819818
sector_size: u32,
820819
key_info: (u32, SizedKeyMemory),
821-
) -> StratisResult<Self> {
820+
) -> StratisResult<()> {
821+
let mut device = acquire_crypt_device(unencrypted_path)?;
822822
let activation_name = &format_crypt_backstore_name(&pool_uuid).to_string();
823823
let (keyslot, key) = key_info;
824824
device.reencrypt_handle().reencrypt_init_by_passphrase(
@@ -849,7 +849,16 @@ impl CryptHandle {
849849
},
850850
)?;
851851
device.reencrypt_handle().reencrypt2::<()>(None, None)?;
852+
Ok(())
853+
}
852854

855+
/// Generate the CryptHandle from the LUKS2 device path.
856+
///
857+
/// Precondition: LUKS2 device path was fully encrypted successfully
858+
pub fn finish_encrypt(
859+
unencrypted_path: &Path,
860+
pool_uuid: PoolUuid,
861+
) -> StratisResult<CryptHandle> {
853862
CryptHandle::setup(unencrypted_path, pool_uuid, TokenUnlockMethod::Any, None)
854863
.map(|h| h.expect("should have crypt device after online encrypt"))
855864
}

src/engine/strat_engine/pool/dispatch.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,15 +351,40 @@ impl Pool for AnyPool {
351351
}
352352
}
353353

354-
fn encrypt_pool(
354+
fn encrypt_pool_idem_check(&self) -> StratisResult<CreateAction<EncryptedDevice>> {
355+
match self {
356+
AnyPool::V1(p) => p.encrypt_pool_idem_check(),
357+
AnyPool::V2(p) => p.encrypt_pool_idem_check(),
358+
}
359+
}
360+
361+
fn start_encrypt_pool(
355362
&mut self,
356-
name: &Name,
357363
pool_uuid: PoolUuid,
358364
encryption_info: &InputEncryptionInfo,
359-
) -> StratisResult<CreateAction<EncryptedDevice>> {
365+
) -> StratisResult<(u32, (u32, SizedKeyMemory))> {
366+
match self {
367+
AnyPool::V1(p) => p.start_encrypt_pool(pool_uuid, encryption_info),
368+
AnyPool::V2(p) => p.start_encrypt_pool(pool_uuid, encryption_info),
369+
}
370+
}
371+
372+
fn do_encrypt_pool(
373+
&self,
374+
pool_uuid: PoolUuid,
375+
sector_size: u32,
376+
key_info: (u32, SizedKeyMemory),
377+
) -> StratisResult<()> {
378+
match self {
379+
AnyPool::V1(p) => p.do_encrypt_pool(pool_uuid, sector_size, key_info),
380+
AnyPool::V2(p) => p.do_encrypt_pool(pool_uuid, sector_size, key_info),
381+
}
382+
}
383+
384+
fn finish_encrypt_pool(&mut self, name: &Name, pool_uuid: PoolUuid) -> StratisResult<()> {
360385
match self {
361-
AnyPool::V1(p) => p.encrypt_pool(name, pool_uuid, encryption_info),
362-
AnyPool::V2(p) => p.encrypt_pool(name, pool_uuid, encryption_info),
386+
AnyPool::V1(p) => p.finish_encrypt_pool(name, pool_uuid),
387+
AnyPool::V2(p) => p.finish_encrypt_pool(name, pool_uuid),
363388
}
364389
}
365390

src/engine/strat_engine/pool/v1.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,12 +1340,33 @@ impl Pool for StratPool {
13401340
}
13411341
}
13421342

1343-
fn encrypt_pool(
1343+
#[pool_mutating_action("NoRequests")]
1344+
fn encrypt_pool_idem_check(&self) -> StratisResult<CreateAction<EncryptedDevice>> {
1345+
Err(StratisError::Msg(
1346+
"Encrypting an unencrypted device is only supported in V2 of the metadata".to_string(),
1347+
))
1348+
}
1349+
1350+
#[pool_mutating_action("NoRequests")]
1351+
fn start_encrypt_pool(
13441352
&mut self,
1345-
_: &Name,
13461353
_: PoolUuid,
13471354
_: &InputEncryptionInfo,
1348-
) -> StratisResult<CreateAction<EncryptedDevice>> {
1355+
) -> StratisResult<(u32, (u32, SizedKeyMemory))> {
1356+
Err(StratisError::Msg(
1357+
"Encrypting an unencrypted device is only supported in V2 of the metadata".to_string(),
1358+
))
1359+
}
1360+
1361+
#[pool_mutating_action("NoRequests")]
1362+
fn do_encrypt_pool(&self, _: PoolUuid, _: u32, _: (u32, SizedKeyMemory)) -> StratisResult<()> {
1363+
Err(StratisError::Msg(
1364+
"Encrypting an unencrypted device is only supported in V2 of the metadata".to_string(),
1365+
))
1366+
}
1367+
1368+
#[pool_mutating_action("NoRequests")]
1369+
fn finish_encrypt_pool(&mut self, _: &Name, _: PoolUuid) -> StratisResult<()> {
13491370
Err(StratisError::Msg(
13501371
"Encrypting an unencrypted device is only supported in V2 of the metadata".to_string(),
13511372
))

0 commit comments

Comments
 (0)