}
}
- fn get_dest_dir_path(&self, primary_namespace: &str, secondary_namespace: &str) -> std::io::Result<PathBuf> {
+ fn get_dest_dir_path(
+ &self, primary_namespace: &str, secondary_namespace: &str,
+ ) -> std::io::Result<PathBuf> {
let mut dest_dir_path = {
#[cfg(target_os = "windows")]
{
}
impl KVStore for FilesystemStore {
- fn read(&self, primary_namespace: &str, secondary_namespace: &str, key: &str) -> lightning::io::Result<Vec<u8>> {
+ fn read(
+ &self, primary_namespace: &str, secondary_namespace: &str, key: &str,
+ ) -> lightning::io::Result<Vec<u8>> {
check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "read")?;
let mut dest_file_path = self.get_dest_dir_path(primary_namespace, secondary_namespace)?;
Ok(buf)
}
- fn write(&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: &[u8]) -> lightning::io::Result<()> {
+ fn write(
+ &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: &[u8],
+ ) -> lightning::io::Result<()> {
check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "write")?;
let mut dest_file_path = self.get_dest_dir_path(primary_namespace, secondary_namespace)?;
dest_file_path.push(key);
- let parent_directory = dest_file_path
- .parent()
- .ok_or_else(|| {
- let msg =
- format!("Could not retrieve parent directory of {}.", dest_file_path.display());
- std::io::Error::new(std::io::ErrorKind::InvalidInput, msg)
- })?;
+ let parent_directory = dest_file_path.parent().ok_or_else(|| {
+ let msg =
+ format!("Could not retrieve parent directory of {}.", dest_file_path.display());
+ std::io::Error::new(std::io::ErrorKind::InvalidInput, msg)
+ })?;
fs::create_dir_all(&parent_directory)?;
// Do a crazy dance with lots of fsync()s to be overly cautious here...
match res {
Ok(()) => {
// We fsync the dest file in hopes this will also flush the metadata to disk.
- let dest_file = fs::OpenOptions::new().read(true).write(true)
- .open(&dest_file_path)?;
+ let dest_file =
+ fs::OpenOptions::new().read(true).write(true).open(&dest_file_path)?;
dest_file.sync_all()?;
Ok(())
- }
+ },
Err(e) => Err(e.into()),
}
}
res
}
- fn remove(&self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool) -> lightning::io::Result<()> {
+ fn remove(
+ &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool,
+ ) -> lightning::io::Result<()> {
check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "remove")?;
let mut dest_file_path = self.get_dest_dir_path(primary_namespace, secondary_namespace)?;
fs::remove_file(&dest_file_path)?;
let parent_directory = dest_file_path.parent().ok_or_else(|| {
- let msg =
- format!("Could not retrieve parent directory of {}.", dest_file_path.display());
+ let msg = format!(
+ "Could not retrieve parent directory of {}.",
+ dest_file_path.display()
+ );
std::io::Error::new(std::io::ErrorKind::InvalidInput, msg)
})?;
let dir_file = fs::OpenOptions::new().read(true).open(parent_directory)?;
// However, all this is partially based on assumptions and local experiments, as
// Windows API is horribly underdocumented.
let mut trash_file_path = dest_file_path.clone();
- let trash_file_ext = format!("{}.trash",
- self.tmp_file_counter.fetch_add(1, Ordering::AcqRel));
+ let trash_file_ext =
+ format!("{}.trash", self.tmp_file_counter.fetch_add(1, Ordering::AcqRel));
trash_file_path.set_extension(trash_file_ext);
call!(unsafe {
{
// We fsync the trash file in hopes this will also flush the original's file
// metadata to disk.
- let trash_file = fs::OpenOptions::new().read(true).write(true)
+ let trash_file = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
.open(&trash_file_path.clone())?;
trash_file.sync_all()?;
}
Ok(())
}
- fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> lightning::io::Result<Vec<String>> {
+ fn list(
+ &self, primary_namespace: &str, secondary_namespace: &str,
+ ) -> lightning::io::Result<Vec<String>> {
check_namespace_key_validity(primary_namespace, secondary_namespace, None, "list")?;
let prefixed_dest = self.get_dest_dir_path(primary_namespace, secondary_namespace)?;
// If we otherwise don't find a file at the given path something went wrong.
if !metadata.is_file() {
- debug_assert!(false, "Failed to list keys of {}/{}: file couldn't be accessed.",
- PrintableString(primary_namespace), PrintableString(secondary_namespace));
- let msg = format!("Failed to list keys of {}/{}: file couldn't be accessed.",
- PrintableString(primary_namespace), PrintableString(secondary_namespace));
+ debug_assert!(
+ false,
+ "Failed to list keys of {}/{}: file couldn't be accessed.",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
+ let msg = format!(
+ "Failed to list keys of {}/{}: file couldn't be accessed.",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
return Err(lightning::io::Error::new(lightning::io::ErrorKind::Other, msg));
}
keys.push(relative_path.to_string())
}
} else {
- debug_assert!(false, "Failed to list keys of {}/{}: file path is not valid UTF-8",
- PrintableString(primary_namespace), PrintableString(secondary_namespace));
- let msg = format!("Failed to list keys of {}/{}: file path is not valid UTF-8",
- PrintableString(primary_namespace), PrintableString(secondary_namespace));
- return Err(lightning::io::Error::new(lightning::io::ErrorKind::Other, msg));
+ debug_assert!(
+ false,
+ "Failed to list keys of {}/{}: file path is not valid UTF-8",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
+ let msg = format!(
+ "Failed to list keys of {}/{}: file path is not valid UTF-8",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
+ return Err(lightning::io::Error::new(
+ lightning::io::ErrorKind::Other,
+ msg,
+ ));
}
- }
+ },
Err(e) => {
- debug_assert!(false, "Failed to list keys of {}/{}: {}",
- PrintableString(primary_namespace), PrintableString(secondary_namespace), e);
- let msg = format!("Failed to list keys of {}/{}: {}",
- PrintableString(primary_namespace), PrintableString(secondary_namespace), e);
+ debug_assert!(
+ false,
+ "Failed to list keys of {}/{}: {}",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace),
+ e
+ );
+ let msg = format!(
+ "Failed to list keys of {}/{}: {}",
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace),
+ e
+ );
return Err(lightning::io::Error::new(lightning::io::ErrorKind::Other, msg));
- }
+ },
}
}
use bitcoin::Txid;
- use lightning::chain::ChannelMonitorUpdateStatus;
use lightning::chain::chainmonitor::Persist;
use lightning::chain::transaction::OutPoint;
+ use lightning::chain::ChannelMonitorUpdateStatus;
use lightning::check_closed_event;
use lightning::events::{ClosureReason, MessageSendEventsProvider};
use lightning::ln::functional_test_utils::*;
- use lightning::util::test_utils;
use lightning::util::persist::read_channel_monitors;
+ use lightning::util::test_utils;
use std::str::FromStr;
impl Drop for FilesystemStore {
// fails.
match fs::remove_dir_all(&self.data_dir) {
Err(e) => println!("Failed to remove test persister directory: {}", e),
- _ => {}
+ _ => {},
}
}
}
let chanmon_cfgs = create_chanmon_cfgs(1);
let mut node_cfgs = create_node_cfgs(1, &chanmon_cfgs);
- let chain_mon_0 = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, &store, node_cfgs[0].keys_manager);
+ let chain_mon_0 = test_utils::TestChainMonitor::new(
+ Some(&chanmon_cfgs[0].chain_source),
+ &chanmon_cfgs[0].tx_broadcaster,
+ &chanmon_cfgs[0].logger,
+ &chanmon_cfgs[0].fee_estimator,
+ &store,
+ node_cfgs[0].keys_manager,
+ );
node_cfgs[0].chain_monitor = chain_mon_0;
let node_chanmgrs = create_node_chanmgrs(1, &node_cfgs, &[None]);
let nodes = create_network(1, &node_cfgs, &node_chanmgrs);
// Check that read_channel_monitors() returns error if monitors/ is not a
// directory.
- assert!(read_channel_monitors(&store, nodes[0].keys_manager, nodes[0].keys_manager).is_err());
+ assert!(
+ read_channel_monitors(&store, nodes[0].keys_manager, nodes[0].keys_manager).is_err()
+ );
}
#[test]
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
let error_message = "Channel force-closed";
- nodes[1].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[0].node.get_our_node_id(), error_message.to_string()).unwrap();
- check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) }, [nodes[0].node.get_our_node_id()], 100000);
+ nodes[1]
+ .node
+ .force_close_broadcasting_latest_txn(
+ &chan.2,
+ &nodes[0].node.get_our_node_id(),
+ error_message.to_string(),
+ )
+ .unwrap();
+ check_closed_event!(
+ nodes[1],
+ 1,
+ ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) },
+ [nodes[0].node.get_our_node_id()],
+ 100000
+ );
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
// Set the store's directory to read-only, which should result in
fs::set_permissions(path, perms).unwrap();
let test_txo = OutPoint {
- txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
- index: 0
+ txid: Txid::from_str(
+ "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be",
+ )
+ .unwrap(),
+ index: 0,
};
match store.persist_new_channel(test_txo, &added_monitors[0].1) {
ChannelMonitorUpdateStatus::UnrecoverableError => {},
- _ => panic!("unexpected result from persisting new channel")
+ _ => panic!("unexpected result from persisting new channel"),
}
nodes[1].node.get_and_clear_pending_msg_events();
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
let error_message = "Channel force-closed";
- nodes[1].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[0].node.get_our_node_id(), error_message.to_string()).unwrap();
- check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) }, [nodes[0].node.get_our_node_id()], 100000);
+ nodes[1]
+ .node
+ .force_close_broadcasting_latest_txn(
+ &chan.2,
+ &nodes[0].node.get_our_node_id(),
+ error_message.to_string(),
+ )
+ .unwrap();
+ check_closed_event!(
+ nodes[1],
+ 1,
+ ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) },
+ [nodes[0].node.get_our_node_id()],
+ 100000
+ );
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
let update_id = update_map.get(&added_monitors[0].1.channel_id()).unwrap();
let store = FilesystemStore::new(":<>/".into());
let test_txo = OutPoint {
- txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
- index: 0
+ txid: Txid::from_str(
+ "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be",
+ )
+ .unwrap(),
+ index: 0,
};
match store.persist_new_channel(test_txo, &added_monitors[0].1) {
ChannelMonitorUpdateStatus::UnrecoverableError => {},
- _ => panic!("unexpected result from persisting new channel")
+ _ => panic!("unexpected result from persisting new channel"),
}
nodes[1].node.get_and_clear_pending_msg_events();
let store_a = super::FilesystemStore::new("bench_filesystem_store_a".into());
let store_b = super::FilesystemStore::new("bench_filesystem_store_b".into());
lightning::ln::channelmanager::bench::bench_two_sends(
- bench, "bench_filesystem_persisted_sends", store_a, store_b);
+ bench,
+ "bench_filesystem_persisted_sends",
+ store_a,
+ store_b,
+ );
}
}
-use lightning::util::persist::{KVStore, KVSTORE_NAMESPACE_KEY_MAX_LEN, read_channel_monitors};
-use lightning::ln::functional_test_utils::{connect_block, create_announced_chan_between_nodes,
- create_chanmon_cfgs, create_dummy_block, create_network, create_node_cfgs, create_node_chanmgrs,
- send_payment};
use lightning::chain::channelmonitor::CLOSED_CHANNEL_UPDATE_ID;
-use lightning::util::test_utils;
-use lightning::{check_closed_broadcast, check_closed_event, check_added_monitors};
use lightning::events::ClosureReason;
+use lightning::ln::functional_test_utils::{
+ connect_block, create_announced_chan_between_nodes, create_chanmon_cfgs, create_dummy_block,
+ create_network, create_node_cfgs, create_node_chanmgrs, send_payment,
+};
+use lightning::util::persist::{read_channel_monitors, KVStore, KVSTORE_NAMESPACE_KEY_MAX_LEN};
+use lightning::util::test_utils;
+use lightning::{check_added_monitors, check_closed_broadcast, check_closed_event};
use std::panic::RefUnwindSafe;
kv_store.write("", "", key, &data).unwrap();
let res = std::panic::catch_unwind(|| kv_store.write("", secondary_namespace, key, &data));
assert!(res.is_err());
- let res = std::panic::catch_unwind(|| kv_store.write(primary_namespace, secondary_namespace, "", &data));
+ let res = std::panic::catch_unwind(|| {
+ kv_store.write(primary_namespace, secondary_namespace, "", &data)
+ });
assert!(res.is_err());
let listed_keys = kv_store.list(primary_namespace, secondary_namespace).unwrap();
pub(crate) fn do_test_store<K: KVStore>(store_0: &K, store_1: &K) {
let chanmon_cfgs = create_chanmon_cfgs(2);
let mut node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
- let chain_mon_0 = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, store_0, node_cfgs[0].keys_manager);
- let chain_mon_1 = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[1].chain_source), &chanmon_cfgs[1].tx_broadcaster, &chanmon_cfgs[1].logger, &chanmon_cfgs[1].fee_estimator, store_1, node_cfgs[1].keys_manager);
+ let chain_mon_0 = test_utils::TestChainMonitor::new(
+ Some(&chanmon_cfgs[0].chain_source),
+ &chanmon_cfgs[0].tx_broadcaster,
+ &chanmon_cfgs[0].logger,
+ &chanmon_cfgs[0].fee_estimator,
+ store_0,
+ node_cfgs[0].keys_manager,
+ );
+ let chain_mon_1 = test_utils::TestChainMonitor::new(
+ Some(&chanmon_cfgs[1].chain_source),
+ &chanmon_cfgs[1].tx_broadcaster,
+ &chanmon_cfgs[1].logger,
+ &chanmon_cfgs[1].fee_estimator,
+ store_1,
+ node_cfgs[1].keys_manager,
+ );
node_cfgs[0].chain_monitor = chain_mon_0;
node_cfgs[1].chain_monitor = chain_mon_1;
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
// Check that the persisted channel data is empty before any channels are
// open.
- let mut persisted_chan_data_0 = read_channel_monitors(store_0, nodes[0].keys_manager, nodes[0].keys_manager).unwrap();
+ let mut persisted_chan_data_0 =
+ read_channel_monitors(store_0, nodes[0].keys_manager, nodes[0].keys_manager).unwrap();
assert_eq!(persisted_chan_data_0.len(), 0);
- let mut persisted_chan_data_1 = read_channel_monitors(store_1, nodes[1].keys_manager, nodes[1].keys_manager).unwrap();
+ let mut persisted_chan_data_1 =
+ read_channel_monitors(store_1, nodes[1].keys_manager, nodes[1].keys_manager).unwrap();
assert_eq!(persisted_chan_data_1.len(), 0);
// Helper to make sure the channel is on the expected update ID.
macro_rules! check_persisted_data {
($expected_update_id: expr) => {
- persisted_chan_data_0 = read_channel_monitors(store_0, nodes[0].keys_manager, nodes[0].keys_manager).unwrap();
+ persisted_chan_data_0 =
+ read_channel_monitors(store_0, nodes[0].keys_manager, nodes[0].keys_manager)
+ .unwrap();
assert_eq!(persisted_chan_data_0.len(), 1);
for (_, mon) in persisted_chan_data_0.iter() {
assert_eq!(mon.get_latest_update_id(), $expected_update_id);
}
- persisted_chan_data_1 = read_channel_monitors(store_1, nodes[1].keys_manager, nodes[1].keys_manager).unwrap();
+ persisted_chan_data_1 =
+ read_channel_monitors(store_1, nodes[1].keys_manager, nodes[1].keys_manager)
+ .unwrap();
assert_eq!(persisted_chan_data_1.len(), 1);
for (_, mon) in persisted_chan_data_1.iter() {
assert_eq!(mon.get_latest_update_id(), $expected_update_id);
}
- }
+ };
}
// Create some initial channel and check that a channel was persisted.
check_persisted_data!(0);
// Send a few payments and make sure the monitors are updated to the latest.
- send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
+ send_payment(&nodes[0], &vec![&nodes[1]][..], 8000000);
check_persisted_data!(5);
- send_payment(&nodes[1], &vec!(&nodes[0])[..], 4000000);
+ send_payment(&nodes[1], &vec![&nodes[0]][..], 4000000);
check_persisted_data!(10);
// Force close because cooperative close doesn't result in any persisted
// updates.
let error_message = "Channel force-closed";
- nodes[0].node.force_close_broadcasting_latest_txn(&nodes[0].node.list_channels()[0].channel_id, &nodes[1].node.get_our_node_id(), error_message.to_string()).unwrap();
- check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) }, [nodes[1].node.get_our_node_id()], 100000);
+ nodes[0]
+ .node
+ .force_close_broadcasting_latest_txn(
+ &nodes[0].node.list_channels()[0].channel_id,
+ &nodes[1].node.get_our_node_id(),
+ error_message.to_string(),
+ )
+ .unwrap();
+ check_closed_event!(
+ nodes[0],
+ 1,
+ ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) },
+ [nodes[1].node.get_our_node_id()],
+ 100000
+ );
check_closed_broadcast!(nodes[0], true);
check_added_monitors!(nodes[0], 1);
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- connect_block(&nodes[1], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![node_txn[0].clone(), node_txn[0].clone()]));
+ connect_block(
+ &nodes[1],
+ &create_dummy_block(
+ nodes[0].best_block_hash(),
+ 42,
+ vec![node_txn[0].clone(), node_txn[0].clone()],
+ ),
+ );
check_closed_broadcast!(nodes[1], true);
- check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [nodes[0].node.get_our_node_id()], 100000);
+ check_closed_event!(
+ nodes[1],
+ 1,
+ ClosureReason::CommitmentTxConfirmed,
+ [nodes[0].node.get_our_node_id()],
+ 100000
+ );
check_added_monitors!(nodes[1], 1);
// Make sure everything is persisted as expected after close.
use lightning::util::persist::{KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN};
use lightning::util::string::PrintableString;
-
pub(crate) fn is_valid_kvstore_str(key: &str) -> bool {
- key.len() <= KVSTORE_NAMESPACE_KEY_MAX_LEN && key.chars().all(|c| KVSTORE_NAMESPACE_KEY_ALPHABET.contains(c))
+ key.len() <= KVSTORE_NAMESPACE_KEY_MAX_LEN
+ && key.chars().all(|c| KVSTORE_NAMESPACE_KEY_ALPHABET.contains(c))
}
pub(crate) fn check_namespace_key_validity(
- primary_namespace: &str, secondary_namespace: &str, key: Option<&str>, operation: &str)
--> Result<(), std::io::Error> {
+ primary_namespace: &str, secondary_namespace: &str, key: Option<&str>, operation: &str,
+) -> Result<(), std::io::Error> {
if let Some(key) = key {
if key.is_empty() {
- debug_assert!(false, "Failed to {} {}/{}/{}: key may not be empty.", operation,
- PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key));
- let msg = format!("Failed to {} {}/{}/{}: key may not be empty.", operation,
- PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key));
+ debug_assert!(
+ false,
+ "Failed to {} {}/{}/{}: key may not be empty.",
+ operation,
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace),
+ PrintableString(key)
+ );
+ let msg = format!(
+ "Failed to {} {}/{}/{}: key may not be empty.",
+ operation,
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace),
+ PrintableString(key)
+ );
return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
}
return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
}
- if !is_valid_kvstore_str(primary_namespace) || !is_valid_kvstore_str(secondary_namespace) || !is_valid_kvstore_str(key) {
+ if !is_valid_kvstore_str(primary_namespace)
+ || !is_valid_kvstore_str(secondary_namespace)
+ || !is_valid_kvstore_str(key)
+ {
debug_assert!(false, "Failed to {} {}/{}/{}: primary namespace, secondary namespace, and key must be valid.",
operation,
PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key));
return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
}
if !is_valid_kvstore_str(primary_namespace) || !is_valid_kvstore_str(secondary_namespace) {
- debug_assert!(false, "Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
- operation, PrintableString(primary_namespace), PrintableString(secondary_namespace));
- let msg = format!("Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
- operation, PrintableString(primary_namespace), PrintableString(secondary_namespace));
+ debug_assert!(
+ false,
+ "Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
+ operation,
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
+ let msg = format!(
+ "Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
+ operation,
+ PrintableString(primary_namespace),
+ PrintableString(secondary_namespace)
+ );
return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
}
}