extern crate alloc;
use alloc::vec::Vec;
use super::{
apply_script::{self, TransactionJournalApplyWritesScriptIterator, TransactionJournalTrimsScriptIterator},
extents_covering_auth_digests::ExtentsCoveringAuthDigests,
staging_copy_disguise::JournalStagingCopyUndisguise,
};
use crate::{
blkdev::{self, ChunkedIoRegion, ChunkedIoRegionChunkRange, ChunkedIoRegionError},
crypto::{CryptoError, hash, symcipher},
fs::{
NvFsError, NvFsIoError,
cocoonfs::{
FormatError, alloc_bitmap,
auth_subject_ids::AuthSubjectDataSuffix,
encryption_entities::{
EncryptedChainedExtentsAssociatedDataAuthSubjectDataSuffix, EncryptedChainedExtentsDecryptionInstance,
EncryptedChainedExtentsEncryptionInstance, EncryptedChainedExtentsLayout, check_cbc_padding,
},
extents,
fs::CocoonFsConfig,
image_header, inode_extents_list, inode_index, keys,
layout::{self, BlockIndex as _},
leb128,
transaction::{Transaction, TransactionJournalUpdateAuthDigestsScriptIterator},
},
},
nvfs_err_internal, tpm2_interface,
utils_async::sync_types,
utils_common::{
alloc::try_alloc_zeroizing_vec,
bitmanip::BitManip as _,
fixed_vec::FixedVec,
io_slices::{
self, IoSlicesIter as _, IoSlicesIterCommon as _, IoSlicesMutIter as _, WalkableIoSlicesIter as _,
},
zeroize,
},
};
use core::{convert, mem, num, pin, task};
const JOURNAL_LOG_FIELD_TAG_AUTH_TREE_EXTENTS_VALUE: u8 = 1u8;
const JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_EXTENTS_VALUE: u8 = 2u8;
const JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_FRAGMENTS_AUTH_DIGESTS_VALUE: u8 = 3u8;
const JOURNAL_LOG_FIELD_TAG_APPLY_WRITES_SCRIPT_VALUE: u8 = 4u8;
const JOURNAL_LOG_FIELD_TAG_UPDATE_AUTH_DIGESTS_SCRIPT_VALUE: u8 = 5u8;
const JOURNAL_LOG_FIELD_TAG_TRIM_SCRIPT_VALUE: u8 = 6u8;
const JOURNAL_LOG_FIELD_TAG_JOURNAL_STAGING_COPY_DISGUISE_VALUE: u8 = 7u8;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum JournalLogFieldTag {
AuthTreeExtents = JOURNAL_LOG_FIELD_TAG_AUTH_TREE_EXTENTS_VALUE,
AllocBitmapFileExtents = JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_EXTENTS_VALUE,
AllocBitmapFileFragmentsAuthDigests = JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_FRAGMENTS_AUTH_DIGESTS_VALUE,
ApplyWritesScript = JOURNAL_LOG_FIELD_TAG_APPLY_WRITES_SCRIPT_VALUE,
UpdateAuthDigestsScript = JOURNAL_LOG_FIELD_TAG_UPDATE_AUTH_DIGESTS_SCRIPT_VALUE,
TrimScript = JOURNAL_LOG_FIELD_TAG_TRIM_SCRIPT_VALUE,
JournalStagingCopyDisguise = JOURNAL_LOG_FIELD_TAG_JOURNAL_STAGING_COPY_DISGUISE_VALUE,
}
fn encoded_field_tag_len(tag: JournalLogFieldTag) -> usize {
debug_assert!((tag as u32) < 0x80);
1
}
fn encode_field_tag(dst: &mut [u8], tag: JournalLogFieldTag) -> &mut [u8] {
debug_assert!((tag as u32) < 0x80);
dst[0] = tag as u8;
&mut dst[1..]
}
fn decode_field_tag<'a, SI: io_slices::IoSlicesIter<'a, BackendIteratorError = convert::Infallible>>(
mut src: SI,
) -> Result<Option<JournalLogFieldTag>, NvFsError> {
if src.is_empty()? {
return Ok(None);
}
let mut tag = [0u8; 1];
io_slices::SingletonIoSliceMut::new(&mut tag)
.map_infallible_err()
.copy_from_iter(&mut src)?;
let tag = tag[0];
if tag & 0x80 != 0 {
return Err(NvFsError::from(FormatError::InvalidJournalLogFieldTagEncoding));
}
let tag = match tag {
JOURNAL_LOG_FIELD_TAG_AUTH_TREE_EXTENTS_VALUE => JournalLogFieldTag::AuthTreeExtents,
JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_EXTENTS_VALUE => JournalLogFieldTag::AllocBitmapFileExtents,
JOURNAL_LOG_FIELD_TAG_ALLOC_BITMAP_FILE_FRAGMENTS_AUTH_DIGESTS_VALUE => {
JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests
}
JOURNAL_LOG_FIELD_TAG_APPLY_WRITES_SCRIPT_VALUE => JournalLogFieldTag::ApplyWritesScript,
JOURNAL_LOG_FIELD_TAG_UPDATE_AUTH_DIGESTS_SCRIPT_VALUE => JournalLogFieldTag::UpdateAuthDigestsScript,
JOURNAL_LOG_FIELD_TAG_TRIM_SCRIPT_VALUE => JournalLogFieldTag::TrimScript,
JOURNAL_LOG_FIELD_TAG_JOURNAL_STAGING_COPY_DISGUISE_VALUE => JournalLogFieldTag::JournalStagingCopyDisguise,
_ => return Err(NvFsError::from(FormatError::InvalidJournalLogFieldTag)),
};
Ok(Some(tag))
}
fn encoded_field_tag_and_len_len(tag: JournalLogFieldTag, value_len: usize) -> Result<usize, NvFsError> {
let encoded_tag_len = encoded_field_tag_len(tag);
let encoded_len_len = leb128::leb128u_u64_encoded_len(
u64::try_from(value_len).map_err(|_| NvFsError::from(FormatError::JournalLogFieldLengthOverflow))?,
);
Ok(encoded_tag_len + encoded_len_len)
}
fn encode_field_tag_and_len(
mut dst: &mut [u8],
tag: JournalLogFieldTag,
value_len: usize,
) -> Result<&mut [u8], NvFsError> {
let value_len =
u64::try_from(value_len).map_err(|_| NvFsError::from(FormatError::JournalLogFieldLengthOverflow))?;
dst = encode_field_tag(dst, tag);
dst = leb128::leb128u_u64_encode(dst, value_len);
Ok(dst)
}
fn decode_field_tag_and_len<'a, SI: io_slices::PeekableIoSlicesIter<'a, BackendIteratorError = convert::Infallible>>(
mut src: SI,
) -> Result<Option<(JournalLogFieldTag, usize)>, NvFsError> {
let tag = decode_field_tag(&mut src)?;
let tag = match tag {
Some(tag) => tag,
None => return Ok(None),
};
let mut decode_buf: [u8; 10] = [0u8; 10];
let decode_buf_len = decode_buf.len();
let mut decode_buf_io_slice = io_slices::SingletonIoSliceMut::new(&mut decode_buf);
(&mut decode_buf_io_slice)
.map_infallible_err()
.copy_from_iter(&mut src.decoupled_borrow())?;
let decode_buf_len = decode_buf_len - decode_buf_io_slice.total_len()?;
let decode_buf = &decode_buf[..decode_buf_len];
let (value_len, decode_buf_remainder) = leb128::leb128u_u64_decode(decode_buf)
.map_err(|_| NvFsError::from(FormatError::InvalidJournalLogFieldLengthEncoding))?;
src.skip(decode_buf.len() - decode_buf_remainder.len())
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => nvfs_err_internal!(),
},
})?;
let value_len = usize::try_from(value_len).map_err(|_| NvFsError::DimensionsNotSupported)?;
Ok(Some((tag, value_len)))
}
#[derive(Clone)]
pub struct JournalLogEncodeBufferLayout {
encoded_auth_tree_extents_value_len: usize,
encoded_alloc_bitmap_file_extents_value_len: usize,
encoded_alloc_bitmap_file_fragments_auth_digests_value_len: usize,
encoded_apply_writes_script_value_len: usize,
encoded_update_auth_digests_script_value_len: usize,
encoded_trim_script_value_len: Option<num::NonZeroUsize>,
encoded_journal_staging_copy_disguise_value_len: Option<num::NonZeroUsize>,
encoded_total_len: usize,
}
impl JournalLogEncodeBufferLayout {
pub fn new(
fs_config: &CocoonFsConfig,
fs_sync_state_alloc_bitmap: &alloc_bitmap::AllocBitmap,
transaction: &Transaction,
auth_tree_extents: &extents::LogicalExtents,
alloc_bitmap_file_extents: &extents::LogicalExtents,
encoded_alloc_bitmap_file_fragments_auth_digests_len: usize,
) -> Result<Self, NvFsError> {
let image_layout = &fs_config.image_layout;
let encoded_auth_tree_extents_value_len = inode_extents_list::indirect_extents_list_encoded_len(
auth_tree_extents
.iter()
.map(|logical_extent| logical_extent.physical_range()),
)?;
let encoded_auth_tree_extents_tag_and_len_len =
encoded_field_tag_and_len_len(JournalLogFieldTag::AuthTreeExtents, encoded_auth_tree_extents_value_len)?;
let encoded_alloc_bitmap_file_extents_value_len = inode_extents_list::indirect_extents_list_encoded_len(
alloc_bitmap_file_extents
.iter()
.map(|logical_extent| logical_extent.physical_range()),
)?;
let encoded_alloc_bitmap_file_extents_tag_and_len_len = encoded_field_tag_and_len_len(
JournalLogFieldTag::AllocBitmapFileExtents,
encoded_alloc_bitmap_file_extents_value_len,
)?;
let encoded_alloc_bitmap_file_fragments_auth_digests_value_len =
encoded_alloc_bitmap_file_fragments_auth_digests_len
.checked_add(hash::hash_alg_digest_len(image_layout.preauth_cca_protection_hmac_hash_alg) as usize)
.ok_or(NvFsError::DimensionsNotSupported)?;
let encoded_alloc_bitmap_file_fragments_auth_digests_tag_and_len_len = encoded_field_tag_and_len_len(
JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests,
encoded_alloc_bitmap_file_fragments_auth_digests_value_len,
)?;
let salt_len =
u8::try_from(fs_config.salt.len()).map_err(|_| NvFsError::from(FormatError::InvalidSaltLength))?;
let encoded_apply_writes_script_value_len = apply_script::JournalApplyWritesScript::encoded_len(
TransactionJournalApplyWritesScriptIterator::new(
&transaction.auth_tree_data_blocks_update_states,
&fs_config.image_layout,
salt_len,
),
image_layout.io_block_allocation_blocks_log2 as u32,
)?;
let encoded_apply_writes_script_tag_and_len_len = encoded_field_tag_and_len_len(
JournalLogFieldTag::ApplyWritesScript,
encoded_apply_writes_script_value_len,
)?;
let encoded_update_auth_digests_script_value_len = apply_script::JournalUpdateAuthDigestsScript::encoded_len(
TransactionJournalUpdateAuthDigestsScriptIterator::new(
&transaction.auth_tree_data_blocks_update_states,
&transaction.allocs.pending_frees,
fs_config.image_header_end,
image_layout.auth_tree_data_block_allocation_blocks_log2,
),
image_layout.auth_tree_data_block_allocation_blocks_log2 as u32,
)?;
let encoded_update_auth_digests_script_tag_and_len_len = encoded_field_tag_and_len_len(
JournalLogFieldTag::UpdateAuthDigestsScript,
encoded_update_auth_digests_script_value_len,
)?;
let mut encoded_total_len = encoded_auth_tree_extents_tag_and_len_len
.checked_add(encoded_auth_tree_extents_value_len)
.and_then(|acc| acc.checked_add(encoded_alloc_bitmap_file_extents_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_alloc_bitmap_file_extents_value_len))
.and_then(|acc| acc.checked_add(encoded_alloc_bitmap_file_fragments_auth_digests_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_alloc_bitmap_file_fragments_auth_digests_value_len))
.and_then(|acc| acc.checked_add(encoded_apply_writes_script_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_apply_writes_script_value_len))
.and_then(|acc| acc.checked_add(encoded_update_auth_digests_script_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_update_auth_digests_script_value_len));
let encoded_trim_script_value_len = if fs_config.enable_trimming {
let encoded_trim_script_value_len = num::NonZeroUsize::new(apply_script::JournalTrimsScript::encoded_len(
TransactionJournalTrimsScriptIterator::new(
fs_sync_state_alloc_bitmap,
&transaction.allocs.pending_frees,
image_layout.io_block_allocation_blocks_log2,
),
image_layout.io_block_allocation_blocks_log2 as u32,
)?);
if let Some(encoded_trim_script_value_len) = encoded_trim_script_value_len {
let encoded_trim_script_tag_and_len_len =
encoded_field_tag_and_len_len(JournalLogFieldTag::TrimScript, encoded_trim_script_value_len.get())?;
encoded_total_len = encoded_total_len
.and_then(|acc| acc.checked_add(encoded_trim_script_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_trim_script_value_len.get()));
}
encoded_trim_script_value_len
} else {
None
};
let encoded_journal_staging_copy_disguise_value_len = if let Some(transaction_journal_staging_copy_disguise) =
transaction
.journal_staging_copy_disguise
.as_ref()
.map(|journal_staging_copy_disguise| &journal_staging_copy_disguise.0)
{
let encoded_journal_staging_copy_disguise_value_len =
num::NonZeroUsize::new(transaction_journal_staging_copy_disguise.encoded_len())
.ok_or_else(|| nvfs_err_internal!())?;
let encoded_journal_staging_copy_disguise_tag_and_len_len = encoded_field_tag_and_len_len(
JournalLogFieldTag::JournalStagingCopyDisguise,
encoded_journal_staging_copy_disguise_value_len.get(),
)?;
encoded_total_len = encoded_total_len
.and_then(|acc| acc.checked_add(encoded_journal_staging_copy_disguise_tag_and_len_len))
.and_then(|acc| acc.checked_add(encoded_journal_staging_copy_disguise_value_len.get()));
Some(encoded_journal_staging_copy_disguise_value_len)
} else {
None
};
let encoded_total_len = encoded_total_len.ok_or(NvFsError::DimensionsNotSupported)?;
Ok(Self {
encoded_auth_tree_extents_value_len,
encoded_alloc_bitmap_file_extents_value_len,
encoded_alloc_bitmap_file_fragments_auth_digests_value_len,
encoded_apply_writes_script_value_len,
encoded_update_auth_digests_script_value_len,
encoded_trim_script_value_len,
encoded_journal_staging_copy_disguise_value_len,
encoded_total_len,
})
}
pub fn get_encoded_total_len(&self) -> usize {
self.encoded_total_len
}
}
pub struct JournalLog {
pub log_extents: extents::PhysicalExtents,
pub auth_tree_extents: extents::PhysicalExtents,
pub alloc_bitmap_file_extents: extents::PhysicalExtents,
pub alloc_bitmap_file_fragments_auth_digests: ExtentsCoveringAuthDigests,
pub apply_writes_script: apply_script::JournalApplyWritesScript,
pub update_auth_digests_script: apply_script::JournalUpdateAuthDigestsScript,
pub trim_script: Option<apply_script::JournalTrimsScript>,
pub journal_staging_copy_undisguise: Option<JournalStagingCopyUndisguise>,
}
impl JournalLog {
pub fn extents_encryption_layout(
image_layout: &layout::ImageLayout,
) -> Result<EncryptedChainedExtentsLayout, NvFsError> {
let auth_tree_data_block_allocation_blocks_log2 = image_layout.auth_tree_data_block_allocation_blocks_log2;
let io_block_allocation_blocks_log2 = image_layout.io_block_allocation_blocks_log2;
let journal_block_allocation_blocks_log2 =
auth_tree_data_block_allocation_blocks_log2.max(io_block_allocation_blocks_log2);
EncryptedChainedExtentsLayout::new(
8, image_layout.block_cipher_alg,
Some(image_layout.preauth_cca_protection_hmac_hash_alg),
journal_block_allocation_blocks_log2,
image_layout.allocation_block_size_128b_log2,
)
}
pub fn extents_encryption_instance<ST: sync_types::SyncTypes>(
image_layout: &layout::ImageLayout,
fs_root_key: &keys::RootKey,
fs_sync_state_keys_cache: &mut keys::KeyCacheRef<'_, ST>,
) -> Result<EncryptedChainedExtentsEncryptionInstance, NvFsError> {
let encryption_key = keys::KeyCache::get_key(
fs_sync_state_keys_cache,
fs_root_key,
&keys::KeyId::new(
inode_index::SpecialInode::JournalLog as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::Encryption,
),
)?;
let block_cipher_instance = symcipher::SymBlockCipherModeEncryptionInstance::new(
tpm2_interface::TpmiAlgCipherMode::Cbc,
&image_layout.block_cipher_alg,
&encryption_key,
)?;
drop(encryption_key);
let inline_authentication_key = keys::KeyCache::get_key(
fs_sync_state_keys_cache,
fs_root_key,
&keys::KeyId::new(
inode_index::SpecialInode::JournalLog as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::PreAuthCcaProtectionAuthentication,
),
)?;
let inline_authentication_hmac_instance = hash::HmacInstance::new(
image_layout.preauth_cca_protection_hmac_hash_alg,
&inline_authentication_key,
)?;
drop(inline_authentication_key);
let extents_encryption_layout = Self::extents_encryption_layout(image_layout)?;
EncryptedChainedExtentsEncryptionInstance::new(
&extents_encryption_layout,
block_cipher_instance,
Some(inline_authentication_hmac_instance),
)
}
pub fn extents_decryption_instance<ST: sync_types::SyncTypes>(
image_layout: &layout::ImageLayout,
fs_root_key: &keys::RootKey,
fs_sync_state_keys_cache: &mut keys::KeyCacheRef<'_, ST>,
) -> Result<EncryptedChainedExtentsDecryptionInstance, NvFsError> {
let encryption_key = keys::KeyCache::get_key(
fs_sync_state_keys_cache,
fs_root_key,
&keys::KeyId::new(
inode_index::SpecialInode::JournalLog as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::Encryption,
),
)?;
let block_cipher_instance = symcipher::SymBlockCipherModeDecryptionInstance::new(
tpm2_interface::TpmiAlgCipherMode::Cbc,
&image_layout.block_cipher_alg,
&encryption_key,
)?;
drop(encryption_key);
let inline_authentication_key = keys::KeyCache::get_key(
fs_sync_state_keys_cache,
fs_root_key,
&keys::KeyId::new(
inode_index::SpecialInode::JournalLog as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::PreAuthCcaProtectionAuthentication,
),
)?;
let inline_authentication_hmac_instance = hash::HmacInstance::new(
image_layout.preauth_cca_protection_hmac_hash_alg,
&inline_authentication_key,
)?;
drop(inline_authentication_key);
let extents_encryption_layout = Self::extents_encryption_layout(image_layout)?;
EncryptedChainedExtentsDecryptionInstance::new(
&extents_encryption_layout,
block_cipher_instance,
Some(inline_authentication_hmac_instance),
)
}
pub fn head_extent_physical_location(
image_layout: &layout::ImageLayout,
image_header_end: layout::PhysicalAllocBlockIndex,
) -> Result<(layout::PhysicalAllocBlockRange, u64), NvFsError> {
let auth_tree_data_block_allocation_blocks_log2 = image_layout.auth_tree_data_block_allocation_blocks_log2;
let io_block_allocation_blocks_log2 = image_layout.io_block_allocation_blocks_log2;
let journal_block_allocation_blocks_log2 =
auth_tree_data_block_allocation_blocks_log2.max(io_block_allocation_blocks_log2);
debug_assert!((journal_block_allocation_blocks_log2 as u32) < u64::BITS - 7);
let head_extent_allocation_blocks_begin = image_header_end
.align_up(journal_block_allocation_blocks_log2 as u32)
.ok_or_else(|| nvfs_err_internal!())?;
let journal_extents_layout = Self::extents_encryption_layout(image_layout)?.get_extents_layout()?;
let head_extent_allocation_blocks_count = journal_extents_layout.min_extents_allocation_blocks().0;
let head_extent_payload_len =
journal_extents_layout.extent_effective_payload_len(head_extent_allocation_blocks_count, true);
let head_extent_allocation_blocks_end =
head_extent_allocation_blocks_begin + head_extent_allocation_blocks_count;
if u64::from(head_extent_allocation_blocks_end)
>> (u64::BITS - 7 - image_layout.allocation_block_size_128b_log2 as u32)
> 1
{
return Err(NvFsError::from(FormatError::InvalidImageLayoutConfig));
}
Ok((
layout::PhysicalAllocBlockRange::new(
head_extent_allocation_blocks_begin,
head_extent_allocation_blocks_end,
),
head_extent_payload_len,
))
}
#[allow(clippy::too_many_arguments)]
pub fn encode<'a, ST: sync_types::SyncTypes>(
mut dst: &'a mut [u8],
encode_buf_layout: &JournalLogEncodeBufferLayout,
fs_config: &CocoonFsConfig,
fs_sync_state_alloc_bitmap: &alloc_bitmap::AllocBitmap,
fs_sync_state_keys_cache: &mut keys::KeyCacheRef<'_, ST>,
transaction: &Transaction,
auth_tree_extents: &extents::LogicalExtents,
alloc_bitmap_file_extents: &extents::LogicalExtents,
encoded_alloc_bitmap_file_fragments_auth_digests: &[u8],
) -> Result<&'a mut [u8], NvFsError> {
let image_layout = &fs_config.image_layout;
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::AuthTreeExtents,
encode_buf_layout.encoded_auth_tree_extents_value_len,
)?;
dst = inode_extents_list::indirect_extents_list_encode_into(
dst,
auth_tree_extents
.iter()
.map(|logical_extent| logical_extent.physical_range()),
);
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::AllocBitmapFileExtents,
encode_buf_layout.encoded_alloc_bitmap_file_extents_value_len,
)?;
let dst_alloc_bitmap_file_extents;
(dst_alloc_bitmap_file_extents, dst) =
dst.split_at_mut(encode_buf_layout.encoded_alloc_bitmap_file_extents_value_len);
if !inode_extents_list::indirect_extents_list_encode_into(
dst_alloc_bitmap_file_extents,
alloc_bitmap_file_extents
.iter()
.map(|logical_extent| logical_extent.physical_range()),
)
.is_empty()
{
return Err(nvfs_err_internal!());
}
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests,
encode_buf_layout.encoded_alloc_bitmap_file_fragments_auth_digests_value_len,
)?;
debug_assert_eq!(
encode_buf_layout.encoded_alloc_bitmap_file_fragments_auth_digests_value_len,
encoded_alloc_bitmap_file_fragments_auth_digests.len()
+ hash::hash_alg_digest_len(image_layout.preauth_cca_protection_hmac_hash_alg) as usize
);
let dst_encoded_alloc_bitmap_file_fragments_auth_digests;
(dst_encoded_alloc_bitmap_file_fragments_auth_digests, dst) =
dst.split_at_mut(encoded_alloc_bitmap_file_fragments_auth_digests.len());
dst_encoded_alloc_bitmap_file_fragments_auth_digests
.copy_from_slice(encoded_alloc_bitmap_file_fragments_auth_digests);
let dst_alloc_bitmap_file_fragments_auth_digests_cca_protection_hmac_digest;
(
dst_alloc_bitmap_file_fragments_auth_digests_cca_protection_hmac_digest,
dst,
) = dst.split_at_mut(hash::hash_alg_digest_len(image_layout.preauth_cca_protection_hmac_hash_alg) as usize);
let alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key = keys::KeyCache::get_key(
fs_sync_state_keys_cache,
&fs_config.root_key,
&keys::KeyId::new(
inode_index::SpecialInode::AllocBitmap as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::PreAuthCcaProtectionAuthentication,
),
)?;
let mut alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance =
hash::HmacInstance::new(
image_layout.preauth_cca_protection_hmac_hash_alg,
&alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key,
)?;
drop(alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key);
debug_assert!((JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests as u32) < 0x80);
let auth_context_subject_id_suffix = [
0u8, JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests as u8,
0u8, AuthSubjectDataSuffix::JournalLogField as u8,
];
let encoded_image_layout = image_layout.encode()?;
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance.update(
io_slices::BuffersSliceIoSlicesIter::new(&[
encoded_image_layout.as_slice(),
dst_alloc_bitmap_file_extents,
dst_encoded_alloc_bitmap_file_fragments_auth_digests,
auth_context_subject_id_suffix.as_slice(),
])
.map_infallible_err(),
)?;
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance
.finalize_into(dst_alloc_bitmap_file_fragments_auth_digests_cca_protection_hmac_digest)?;
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::ApplyWritesScript,
encode_buf_layout.encoded_apply_writes_script_value_len,
)?;
let salt_len =
u8::try_from(fs_config.salt.len()).map_err(|_| NvFsError::from(FormatError::InvalidSaltLength))?;
dst = apply_script::JournalApplyWritesScript::encode(
dst,
TransactionJournalApplyWritesScriptIterator::new(
&transaction.auth_tree_data_blocks_update_states,
&fs_config.image_layout,
salt_len,
),
image_layout.io_block_allocation_blocks_log2 as u32,
)?;
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::UpdateAuthDigestsScript,
encode_buf_layout.encoded_update_auth_digests_script_value_len,
)?;
dst = apply_script::JournalUpdateAuthDigestsScript::encode(
dst,
TransactionJournalUpdateAuthDigestsScriptIterator::new(
&transaction.auth_tree_data_blocks_update_states,
&transaction.allocs.pending_frees,
fs_config.image_header_end,
image_layout.auth_tree_data_block_allocation_blocks_log2,
),
image_layout.auth_tree_data_block_allocation_blocks_log2 as u32,
)?;
if let Some(encoded_trim_script_value_len) = encode_buf_layout.encoded_trim_script_value_len {
debug_assert!(fs_config.enable_trimming);
dst = encode_field_tag_and_len(dst, JournalLogFieldTag::TrimScript, encoded_trim_script_value_len.get())?;
dst = apply_script::JournalTrimsScript::encode(
dst,
TransactionJournalTrimsScriptIterator::new(
fs_sync_state_alloc_bitmap,
&transaction.allocs.pending_frees,
image_layout.io_block_allocation_blocks_log2,
),
image_layout.io_block_allocation_blocks_log2 as u32,
)?;
}
if let Some(transaction_journal_staging_copy_disguise) = transaction
.journal_staging_copy_disguise
.as_ref()
.map(|journal_staging_copy_disguise| &journal_staging_copy_disguise.0)
{
let encoded_journal_staging_copy_disguise_value_len = encode_buf_layout
.encoded_journal_staging_copy_disguise_value_len
.ok_or_else(|| nvfs_err_internal!())?;
dst = encode_field_tag_and_len(
dst,
JournalLogFieldTag::JournalStagingCopyDisguise,
encoded_journal_staging_copy_disguise_value_len.get(),
)?;
dst = transaction_journal_staging_copy_disguise.encode(dst)?;
}
Ok(dst)
}
pub fn decode<
'a,
ST: sync_types::SyncTypes,
SI: io_slices::PeekableIoSlicesIter<'a, BackendIteratorError = convert::Infallible>,
>(
mut src: SI,
log_extents: extents::PhysicalExtents,
image_layout: &layout::ImageLayout,
root_key: &keys::RootKey,
keys_cache: &mut keys::KeyCacheRef<'_, ST>,
) -> Result<Self, NvFsError> {
let io_block_allocation_blocks_log2 = image_layout.io_block_allocation_blocks_log2 as u32;
let auth_tree_data_block_allocation_blocks_log2 =
image_layout.auth_tree_data_block_allocation_blocks_log2 as u32;
let journal_block_allocation_blocks_log2 =
io_block_allocation_blocks_log2.max(auth_tree_data_block_allocation_blocks_log2);
let (tag, encoded_auth_tree_extents_len) =
decode_field_tag_and_len(src.as_ref())?.ok_or(NvFsError::from(FormatError::IncompleteJournalLog))?;
if tag != JournalLogFieldTag::AuthTreeExtents {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let mut encoded_auth_tree_extents =
src.as_ref()
.take_exact(encoded_auth_tree_extents_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds)
}
},
});
let auth_tree_extents = inode_extents_list::indirect_extents_list_decode(&mut encoded_auth_tree_extents)?;
if !encoded_auth_tree_extents.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
let mut extents_end_high_watermark = layout::PhysicalAllocBlockIndex::from(0u64);
for (i, cur_extent) in auth_tree_extents.iter().enumerate() {
if !(u64::from(cur_extent.begin()) | u64::from(cur_extent.end()))
.is_aligned_pow2(journal_block_allocation_blocks_log2)
{
return Err(NvFsError::from(FormatError::UnalignedAuthTreeExtents));
}
if cur_extent.begin() >= extents_end_high_watermark {
extents_end_high_watermark = cur_extent.end();
continue;
}
for j in 0..i {
if auth_tree_extents.get_extent_range(j).overlaps_with(&cur_extent) {
return Err(NvFsError::from(FormatError::InvalidExtents));
}
}
}
let alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key = keys::KeyCache::get_key(
keys_cache,
root_key,
&keys::KeyId::new(
inode_index::SpecialInode::AllocBitmap as u32,
inode_index::InodeKeySubdomain::InodeData as u32,
keys::KeyPurpose::PreAuthCcaProtectionAuthentication,
),
)?;
let mut alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance =
hash::HmacInstance::new(
image_layout.preauth_cca_protection_hmac_hash_alg,
&alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key,
)?;
drop(alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_key);
let encoded_image_layout = image_layout.encode()?;
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance
.update(io_slices::SingletonIoSlice::new(&encoded_image_layout).map_infallible_err())?;
let (tag, encoded_alloc_bitmap_file_extents_len) =
decode_field_tag_and_len(src.as_ref())?.ok_or(NvFsError::from(FormatError::IncompleteJournalLog))?;
if tag != JournalLogFieldTag::AllocBitmapFileExtents {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
if src.total_len()? < encoded_alloc_bitmap_file_extents_len {
return Err(NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds));
}
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance.update(
src.decoupled_borrow()
.take_exact(encoded_alloc_bitmap_file_extents_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => CryptoError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => CryptoError::Internal,
},
}),
)?;
let mut encoded_alloc_bitmap_file_extents = src
.as_ref()
.take_exact(encoded_alloc_bitmap_file_extents_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
nvfs_err_internal!()
}
},
});
let alloc_bitmap_file_extents =
inode_extents_list::indirect_extents_list_decode(&mut encoded_alloc_bitmap_file_extents)?;
if !encoded_alloc_bitmap_file_extents.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
let mut extents_end_high_watermark = layout::PhysicalAllocBlockIndex::from(0u64);
for (i, cur_extent) in alloc_bitmap_file_extents.iter().enumerate() {
if cur_extent.begin() >= extents_end_high_watermark {
extents_end_high_watermark = cur_extent.end();
continue;
}
for j in 0..i {
if alloc_bitmap_file_extents.get_extent_range(j).overlaps_with(&cur_extent) {
return Err(NvFsError::from(FormatError::InvalidExtents));
}
}
}
let (tag, encoded_alloc_bitmap_file_fragments_auth_digests_len) =
decode_field_tag_and_len(src.as_ref())?.ok_or(NvFsError::from(FormatError::IncompleteJournalLog))?;
if tag != JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let preauth_cca_protection_digest_len =
hash::hash_alg_digest_len(image_layout.preauth_cca_protection_hmac_hash_alg) as usize;
if encoded_alloc_bitmap_file_fragments_auth_digests_len < preauth_cca_protection_digest_len {
return Err(NvFsError::from(
FormatError::InvalidJournalExtentsCoveringAuthDigestsFormat,
));
}
if src.total_len()? < encoded_alloc_bitmap_file_extents_len {
return Err(NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds));
}
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance.update(
src.decoupled_borrow()
.take_exact(encoded_alloc_bitmap_file_fragments_auth_digests_len - preauth_cca_protection_digest_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => CryptoError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => CryptoError::Internal,
},
}),
)?;
let mut encoded_alloc_bitmap_file_fragments_auth_digests = src
.as_ref()
.take_exact(encoded_alloc_bitmap_file_fragments_auth_digests_len - preauth_cca_protection_digest_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
nvfs_err_internal!()
}
},
});
let alloc_bitmap_file_fragments_auth_digests = ExtentsCoveringAuthDigests::decode(
encoded_alloc_bitmap_file_fragments_auth_digests.as_ref(),
image_layout.auth_tree_data_block_allocation_blocks_log2,
image_layout.allocation_block_size_128b_log2,
hash::hash_alg_digest_len(image_layout.preauth_cca_protection_hmac_hash_alg) as usize,
)?;
if !encoded_alloc_bitmap_file_fragments_auth_digests.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
debug_assert!((JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests as u32) < 0x80);
let auth_context_subject_id_suffix = [
0u8, JournalLogFieldTag::AllocBitmapFileFragmentsAuthDigests as u8,
0u8, AuthSubjectDataSuffix::JournalLogField as u8,
];
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance
.update(io_slices::SingletonIoSlice::new(&auth_context_subject_id_suffix).map_infallible_err())?;
let mut alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_digest =
zeroize::Zeroizing::new(FixedVec::<u8, 5>::new_with_default(preauth_cca_protection_digest_len)?);
alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_hmac_instance
.finalize_into(&mut alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_digest)?;
if io_slices::SingletonIoSlice::new(&alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_digest)
.map_infallible_err()
.ct_eq_with_iter(
src.as_ref()
.take_exact(preauth_cca_protection_digest_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
nvfs_err_internal!()
}
},
}),
)?
.unwrap()
== 0
{
return Err(NvFsError::AuthenticationFailure);
}
drop(alloc_bitmap_file_fragments_auth_digests_preauth_cca_protection_digest);
let (tag, encoded_apply_writes_script_len) =
decode_field_tag_and_len(src.as_ref())?.ok_or(NvFsError::from(FormatError::IncompleteJournalLog))?;
if tag != JournalLogFieldTag::ApplyWritesScript {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let mut encoded_apply_writes_script =
src.as_ref()
.take_exact(encoded_apply_writes_script_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds)
}
},
});
let apply_writes_script = apply_script::JournalApplyWritesScript::decode(
encoded_apply_writes_script.as_ref(),
image_layout.io_block_allocation_blocks_log2 as u32,
image_layout.allocation_bitmap_file_block_allocation_blocks_log2 as u32,
)?;
if !encoded_apply_writes_script.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
let (tag, encoded_update_auth_digests_script_len) =
decode_field_tag_and_len(src.as_ref())?.ok_or(NvFsError::from(FormatError::IncompleteJournalLog))?;
if tag != JournalLogFieldTag::UpdateAuthDigestsScript {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let mut encoded_update_auth_digests_script = src
.as_ref()
.take_exact(encoded_update_auth_digests_script_len)
.map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds)
}
},
});
let update_auth_digests_script = apply_script::JournalUpdateAuthDigestsScript::decode(
encoded_update_auth_digests_script.as_ref(),
image_layout.auth_tree_data_block_allocation_blocks_log2 as u32,
image_layout.allocation_bitmap_file_block_allocation_blocks_log2 as u32,
)?;
if !encoded_update_auth_digests_script.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
let mut trim_script = None;
let mut journal_staging_copy_undisguise = None;
while let Some((tag, encoded_field_len)) = decode_field_tag_and_len(src.as_ref())? {
if tag == JournalLogFieldTag::TrimScript {
if trim_script.is_some() {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let mut encoded_trim_script = src.as_ref().take_exact(encoded_field_len).map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds)
}
},
});
trim_script = Some(apply_script::JournalTrimsScript::decode(
encoded_trim_script.as_ref(),
image_layout.io_block_allocation_blocks_log2 as u32,
image_layout.allocation_bitmap_file_block_allocation_blocks_log2 as u32,
)?);
if !encoded_trim_script.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
} else if tag == JournalLogFieldTag::JournalStagingCopyDisguise {
if journal_staging_copy_undisguise.is_some() {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
let mut encoded_journal_staging_copy_disguise =
src.as_ref().take_exact(encoded_field_len).map_err(|e| match e {
io_slices::IoSlicesIterError::BackendIteratorError(e) => NvFsError::from(e),
io_slices::IoSlicesIterError::IoSlicesError(e) => match e {
io_slices::IoSlicesError::BuffersExhausted => {
NvFsError::from(FormatError::JournalLogFieldLengthOutOfBounds)
}
},
});
journal_staging_copy_undisguise = Some(JournalStagingCopyUndisguise::decode(
encoded_journal_staging_copy_disguise.as_ref(),
)?);
if !encoded_journal_staging_copy_disguise.is_empty()? {
return Err(NvFsError::from(FormatError::ExcessJournalLogFieldLength));
}
} else {
return Err(NvFsError::from(FormatError::UnexpectedJournalLogField));
}
}
Ok(Self {
log_extents,
auth_tree_extents,
alloc_bitmap_file_extents,
alloc_bitmap_file_fragments_auth_digests,
apply_writes_script,
update_auth_digests_script,
trim_script,
journal_staging_copy_undisguise,
})
}
}
pub struct JournalLogInvalidateFuture<B: blkdev::NvBlkDev> {
fut_state: JournalLogInvalidateFutureState<B>,
issue_sync: bool,
}
enum JournalLogInvalidateFutureState<B: blkdev::NvBlkDev> {
Init,
WriteBarrierBeforeInvalidate {
write_barrier_fut: B::WriteBarrierFuture,
},
InvalidateJournalLogHead {
overwrite_journal_log_head_fut: B::WriteFuture<JournalLogInvalidateNvBlkDevWriteRequest>,
},
WriteSyncAfterInvalidate {
write_sync_fut: B::WriteSyncFuture,
},
Done,
}
impl<B: blkdev::NvBlkDev> JournalLogInvalidateFuture<B> {
pub fn new(issue_sync: bool) -> Self {
Self {
fut_state: JournalLogInvalidateFutureState::Init,
issue_sync,
}
}
pub fn poll(
self: pin::Pin<&mut Self>,
blkdev: &B,
image_layout: &layout::ImageLayout,
image_header_end: layout::PhysicalAllocBlockIndex,
cx: &mut task::Context<'_>,
) -> task::Poll<Result<(), NvFsError>> {
let this = pin::Pin::into_inner(self);
loop {
match &mut this.fut_state {
JournalLogInvalidateFutureState::Init => {
let write_barrier_fut = match blkdev.write_barrier() {
Ok(write_barrier_fut) => write_barrier_fut,
Err(e) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
this.fut_state =
JournalLogInvalidateFutureState::WriteBarrierBeforeInvalidate { write_barrier_fut };
}
JournalLogInvalidateFutureState::WriteBarrierBeforeInvalidate { write_barrier_fut } => {
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(write_barrier_fut), blkdev, cx) {
task::Poll::Ready(Ok(())) => (),
task::Poll::Ready(Err(e)) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
let journal_log_head_extent =
match JournalLog::head_extent_physical_location(image_layout, image_header_end) {
Ok((journal_log_head_extent, _)) => journal_log_head_extent,
Err(e) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let overwrite_journal_log_head_request = match JournalLogInvalidateNvBlkDevWriteRequest::new(
&journal_log_head_extent,
image_layout.allocation_block_size_128b_log2 as u32,
blkdev,
) {
Ok(overwrite_journal_log_head_request) => overwrite_journal_log_head_request,
Err(e) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let overwrite_journal_log_head_fut = match blkdev
.write(overwrite_journal_log_head_request)
.and_then(|r| r.map_err(|(_, e)| e))
{
Ok(overwrite_journal_log_head_fut) => overwrite_journal_log_head_fut,
Err(e) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
this.fut_state = JournalLogInvalidateFutureState::InvalidateJournalLogHead {
overwrite_journal_log_head_fut,
};
}
JournalLogInvalidateFutureState::InvalidateJournalLogHead {
overwrite_journal_log_head_fut,
} => {
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(overwrite_journal_log_head_fut), blkdev, cx) {
task::Poll::Ready(Ok((_, Ok(())))) => (),
task::Poll::Ready(Ok((_, Err(e))) | Err(e)) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
let write_sync_fut = match blkdev.write_sync() {
Ok(write_sync_fut) => write_sync_fut,
Err(e) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
if this.issue_sync {
this.fut_state = JournalLogInvalidateFutureState::WriteSyncAfterInvalidate { write_sync_fut };
} else {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Ok(()));
}
}
JournalLogInvalidateFutureState::WriteSyncAfterInvalidate { write_sync_fut } => {
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(write_sync_fut), blkdev, cx) {
task::Poll::Ready(Ok(())) => (),
task::Poll::Ready(Err(e)) => {
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
this.fut_state = JournalLogInvalidateFutureState::Done;
return task::Poll::Ready(Ok(()));
}
JournalLogInvalidateFutureState::Done => unreachable!(),
}
}
}
}
struct JournalLogInvalidateNvBlkDevWriteRequest {
region: ChunkedIoRegion,
overwrite_buf: FixedVec<u8, 7>,
}
impl JournalLogInvalidateNvBlkDevWriteRequest {
fn new<B: blkdev::NvBlkDev>(
journal_log_head_extent: &layout::PhysicalAllocBlockRange,
allocation_block_size_128b_log2: u32,
blkdev: &B,
) -> Result<Self, NvFsError> {
let overwrite_buf = FixedVec::new_with_value(128, 0u8)?;
let blkdev_io_block_size_128b_log2 = blkdev.io_block_size_128b_log2();
let journal_log_head_extent_begin_128b =
u64::from(journal_log_head_extent.begin()) << allocation_block_size_128b_log2;
let journal_log_head_extent_overwrite_end_128b = journal_log_head_extent_begin_128b
.checked_add(1u64 << blkdev_io_block_size_128b_log2)
.ok_or(NvFsError::from(blkdev::NvBlkDevIoError::IoBlockOutOfRange))?;
let region = ChunkedIoRegion::new(
journal_log_head_extent_begin_128b,
journal_log_head_extent_overwrite_end_128b,
0,
)
.map_err(|_| nvfs_err_internal!())?;
Ok(Self { region, overwrite_buf })
}
}
impl blkdev::NvBlkDevWriteRequest for JournalLogInvalidateNvBlkDevWriteRequest {
fn region(&self) -> &ChunkedIoRegion {
&self.region
}
fn get_source_buffer(&self, range: &ChunkedIoRegionChunkRange) -> Result<&[u8], blkdev::NvBlkDevIoError> {
Ok(&self.overwrite_buf[range.range_in_chunk().clone()])
}
}
pub struct JournalLogReadFuture<B: blkdev::NvBlkDev> {
fut_state: JournalLogReadFutureState<B>,
log_extents: extents::PhysicalExtents,
extents_decryption_instance: Option<EncryptedChainedExtentsDecryptionInstance>,
decrypted_journal_log_extents: Vec<zeroize::Zeroizing<Vec<u8>>>,
}
enum JournalLogReadFutureState<B: blkdev::NvBlkDev> {
Init,
ReadJournalLogHeadExtentHead {
read_fut: B::ReadFuture<JournalLogReadExtentNvBlkDevReadRequest>,
journal_log_head_extent: layout::PhysicalAllocBlockRange,
journal_log_head_extent_effective_payload_len: usize,
},
ReadJournalLogHeadExtentTail {
read_fut: B::ReadFuture<JournalLogReadExtentNvBlkDevReadRequest>,
journal_log_head_extent_head: FixedVec<u8, 7>,
journal_log_head_extent_allocation_blocks: layout::AllocBlockCount,
journal_log_head_extent_effective_payload_len: usize,
},
DecryptJournalLogHeadExtent {
journal_log_head_extent_head: FixedVec<u8, 7>,
journal_log_head_extent_tail: FixedVec<u8, 7>,
journal_log_head_extent_allocation_blocks: layout::AllocBlockCount,
journal_log_head_extent_effective_payload_len: usize,
},
ReadNextJournalLogTailExtentPrepare {
next_journal_log_tail_extent: layout::PhysicalAllocBlockRange,
},
ReadNextJournalLogTailExtent {
read_fut: B::ReadFuture<JournalLogReadExtentNvBlkDevReadRequest>,
next_journal_log_tail_extent_allocation_blocks: layout::AllocBlockCount,
},
DecodeJournalLog,
Done,
}
impl<B: blkdev::NvBlkDev> JournalLogReadFuture<B> {
pub fn new() -> Self {
Self {
fut_state: JournalLogReadFutureState::Init,
log_extents: extents::PhysicalExtents::new(),
extents_decryption_instance: None,
decrypted_journal_log_extents: Vec::new(),
}
}
pub fn poll<ST: sync_types::SyncTypes>(
self: pin::Pin<&mut Self>,
blkdev: &B,
image_layout: &layout::ImageLayout,
salt_len: u8,
root_key: &keys::RootKey,
keys_cache: &mut keys::KeyCacheRef<'_, ST>,
cx: &mut task::Context<'_>,
) -> task::Poll<Result<Option<JournalLog>, NvFsError>> {
let this = pin::Pin::into_inner(self);
loop {
match &mut this.fut_state {
JournalLogReadFutureState::Init => {
let image_header_end =
image_header::MutableImageHeader::physical_location(image_layout, salt_len).end();
let (journal_log_head_extent, journal_log_head_extent_effective_payload_len) =
match JournalLog::head_extent_physical_location(image_layout, image_header_end) {
Ok((journal_log_head_extent, journal_log_head_extent_effective_payload_len)) => {
(journal_log_head_extent, journal_log_head_extent_effective_payload_len)
}
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let journal_log_head_extent_effective_payload_len =
match usize::try_from(journal_log_head_extent_effective_payload_len) {
Ok(journal_log_head_extent_effective_payload_len) => {
journal_log_head_extent_effective_payload_len
}
Err(_) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::DimensionsNotSupported));
}
};
if let Err(e) = this.log_extents.push_extent(&journal_log_head_extent, false) {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
let blkdev_io_block_size_128b_log2 = blkdev.io_block_size_128b_log2();
let allocation_block_size_128b_log2 = image_layout.allocation_block_size_128b_log2 as u32;
let journal_log_head_extent_begin_128b =
u64::from(journal_log_head_extent.begin()) << allocation_block_size_128b_log2;
let journal_log_head_extent_head_read_request = match JournalLogReadExtentNvBlkDevReadRequest::new(
journal_log_head_extent_begin_128b,
journal_log_head_extent_begin_128b + (1 << blkdev_io_block_size_128b_log2),
blkdev_io_block_size_128b_log2,
) {
Ok(journal_log_head_extent_head_read_request) => journal_log_head_extent_head_read_request,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let read_fut = match blkdev.read(journal_log_head_extent_head_read_request) {
Ok(Ok(read_fut)) => read_fut,
Err(e) | Ok(Err((_, e))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
this.fut_state = JournalLogReadFutureState::ReadJournalLogHeadExtentHead {
read_fut,
journal_log_head_extent,
journal_log_head_extent_effective_payload_len,
};
}
JournalLogReadFutureState::ReadJournalLogHeadExtentHead {
read_fut,
journal_log_head_extent,
journal_log_head_extent_effective_payload_len,
} => {
let journal_log_head_extent_head_read_request =
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(read_fut), blkdev, cx) {
task::Poll::Ready(Ok((journal_log_head_extent_head_read_request, Ok(())))) => {
journal_log_head_extent_head_read_request
}
task::Poll::Ready(Err(e) | Ok((_, Err(e)))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
let journal_log_head_extent_head = journal_log_head_extent_head_read_request.into_dst_buf();
if &journal_log_head_extent_head[..8] != b"CCFSJRNL".as_slice() {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Ok(None));
}
let blkdev_io_block_size_128b_log2 = blkdev.io_block_size_128b_log2();
let allocation_block_size_128b_log2 = image_layout.allocation_block_size_128b_log2 as u32;
let journal_log_head_extent_begin_128b =
u64::from(journal_log_head_extent.begin()) << allocation_block_size_128b_log2;
let journal_log_head_extent_tail_begin_128b =
journal_log_head_extent_begin_128b + (1 << blkdev_io_block_size_128b_log2);
let journal_log_head_extent_end_128b =
u64::from(journal_log_head_extent.end()) << allocation_block_size_128b_log2;
if journal_log_head_extent_tail_begin_128b == journal_log_head_extent_end_128b {
this.fut_state = JournalLogReadFutureState::DecryptJournalLogHeadExtent {
journal_log_head_extent_head,
journal_log_head_extent_tail: FixedVec::new_empty(),
journal_log_head_extent_allocation_blocks: journal_log_head_extent.block_count(),
journal_log_head_extent_effective_payload_len:
*journal_log_head_extent_effective_payload_len,
};
continue;
}
let journal_log_head_extent_tail_read_request = match JournalLogReadExtentNvBlkDevReadRequest::new(
journal_log_head_extent_tail_begin_128b,
journal_log_head_extent_end_128b,
blkdev_io_block_size_128b_log2,
) {
Ok(journal_log_head_extent_tail_read_request) => journal_log_head_extent_tail_read_request,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let read_fut = match blkdev.read(journal_log_head_extent_tail_read_request) {
Ok(Ok(read_fut)) => read_fut,
Err(e) | Ok(Err((_, e))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
this.fut_state = JournalLogReadFutureState::ReadJournalLogHeadExtentTail {
read_fut,
journal_log_head_extent_head,
journal_log_head_extent_allocation_blocks: journal_log_head_extent.block_count(),
journal_log_head_extent_effective_payload_len: *journal_log_head_extent_effective_payload_len,
};
}
JournalLogReadFutureState::ReadJournalLogHeadExtentTail {
read_fut,
journal_log_head_extent_head,
journal_log_head_extent_allocation_blocks,
journal_log_head_extent_effective_payload_len,
} => {
let journal_log_head_extent_tail_read_request =
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(read_fut), blkdev, cx) {
task::Poll::Ready(Ok((journal_log_head_extent_tail_read_request, Ok(())))) => {
journal_log_head_extent_tail_read_request
}
task::Poll::Ready(Err(e) | Ok((_, Err(e)))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
let journal_log_head_extent_tail = journal_log_head_extent_tail_read_request.into_dst_buf();
this.fut_state = JournalLogReadFutureState::DecryptJournalLogHeadExtent {
journal_log_head_extent_head: mem::take(journal_log_head_extent_head),
journal_log_head_extent_tail,
journal_log_head_extent_allocation_blocks: *journal_log_head_extent_allocation_blocks,
journal_log_head_extent_effective_payload_len: *journal_log_head_extent_effective_payload_len,
};
}
JournalLogReadFutureState::DecryptJournalLogHeadExtent {
journal_log_head_extent_head,
journal_log_head_extent_tail,
journal_log_head_extent_allocation_blocks,
journal_log_head_extent_effective_payload_len,
} => {
let extents_decryption_instance =
match JournalLog::extents_decryption_instance(image_layout, root_key, keys_cache) {
Ok(extents_decryption_instance) => extents_decryption_instance,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let extents_decryption_instance =
this.extents_decryption_instance.insert(extents_decryption_instance);
let mut decrypted_journal_log_head_extent =
match try_alloc_zeroizing_vec(*journal_log_head_extent_effective_payload_len) {
Ok(decrypted_journal_log_head_extent) => decrypted_journal_log_head_extent,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
let encoded_image_layout = match image_layout.encode() {
Ok(encoded_image_layout) => encoded_image_layout,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let auth_context_subject_id_suffix = [
0u8, EncryptedChainedExtentsAssociatedDataAuthSubjectDataSuffix::JournalLog as u8,
];
let authenticated_associated_data = [
encoded_image_layout.as_slice(),
auth_context_subject_id_suffix.as_slice(),
];
let authenticated_associated_data =
io_slices::BuffersSliceIoSlicesIter::new(&authenticated_associated_data).map_infallible_err();
let next_chained_extent = match extents_decryption_instance.decrypt_one_extent(
io_slices::SingletonIoSliceMut::new(decrypted_journal_log_head_extent.as_mut_slice())
.map_infallible_err(),
io_slices::SingletonIoSlice::new(journal_log_head_extent_head)
.chain(io_slices::SingletonIoSlice::new(journal_log_head_extent_tail))
.map_infallible_err(),
authenticated_associated_data,
*journal_log_head_extent_allocation_blocks,
) {
Ok(next_chained_extent) => next_chained_extent,
Err(NvFsError::AuthenticationFailure) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Ok(None));
}
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
if let Err(e) = this.decrypted_journal_log_extents.try_reserve(1) {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
};
this.decrypted_journal_log_extents
.push(decrypted_journal_log_head_extent);
this.fut_state = match next_chained_extent {
Some(next_journal_log_tail_extent) => {
JournalLogReadFutureState::ReadNextJournalLogTailExtentPrepare {
next_journal_log_tail_extent,
}
}
None => JournalLogReadFutureState::DecodeJournalLog,
};
}
JournalLogReadFutureState::ReadNextJournalLogTailExtentPrepare {
next_journal_log_tail_extent,
} => {
if let Err(e) = this.log_extents.push_extent(next_journal_log_tail_extent, false) {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
let allocation_block_size_128b_log2 = image_layout.allocation_block_size_128b_log2 as u32;
let io_block_allocation_blocks_log2 = image_layout.io_block_allocation_blocks_log2 as u32;
let auth_tree_data_block_allocation_blocks_log2 =
image_layout.auth_tree_data_block_allocation_blocks_log2 as u32;
let journal_block_allocation_blocks_log2 =
io_block_allocation_blocks_log2.max(auth_tree_data_block_allocation_blocks_log2);
let next_journal_log_tail_extent_begin_128b =
u64::from(next_journal_log_tail_extent.begin()) << allocation_block_size_128b_log2;
let next_journal_log_tail_extent_end_128b =
u64::from(next_journal_log_tail_extent.end()) << allocation_block_size_128b_log2;
let next_journal_log_tail_extent_read_request = match JournalLogReadExtentNvBlkDevReadRequest::new(
next_journal_log_tail_extent_begin_128b,
next_journal_log_tail_extent_end_128b,
journal_block_allocation_blocks_log2,
) {
Ok(next_journal_log_tail_extent_read_request) => next_journal_log_tail_extent_read_request,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let read_fut = match blkdev.read(next_journal_log_tail_extent_read_request) {
Ok(Ok(read_fut)) => read_fut,
Err(e) | Ok(Err((_, e))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
this.fut_state = JournalLogReadFutureState::ReadNextJournalLogTailExtent {
read_fut,
next_journal_log_tail_extent_allocation_blocks: next_journal_log_tail_extent.block_count(),
};
}
JournalLogReadFutureState::ReadNextJournalLogTailExtent {
read_fut,
next_journal_log_tail_extent_allocation_blocks,
} => {
let next_journal_log_tail_extent_read_request =
match blkdev::NvBlkDevFuture::poll(pin::Pin::new(read_fut), blkdev, cx) {
task::Poll::Ready(Ok((journal_log_read_next_tail_extent_request, Ok(())))) => {
journal_log_read_next_tail_extent_request
}
task::Poll::Ready(Err(e) | Ok((_, Err(e)))) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
task::Poll::Pending => return task::Poll::Pending,
};
let next_journal_log_tail_extent = next_journal_log_tail_extent_read_request.into_dst_buf();
let extents_decryption_instance = match this.extents_decryption_instance.as_mut() {
Some(extents_decryption_instance) => extents_decryption_instance,
None => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(nvfs_err_internal!()));
}
};
let next_journal_log_tail_extent_effective_payload_len = match extents_decryption_instance
.max_extent_decrypted_len(*next_journal_log_tail_extent_allocation_blocks, false)
{
Ok(next_journal_log_tail_extent_effective_payload_len) => {
next_journal_log_tail_extent_effective_payload_len
}
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let mut decrypted_next_journal_log_tail_extent =
match try_alloc_zeroizing_vec(next_journal_log_tail_extent_effective_payload_len) {
Ok(decrypted_next_journal_log_tail_extent) => decrypted_next_journal_log_tail_extent,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
}
};
let encoded_image_layout = match image_layout.encode() {
Ok(encoded_image_layout) => encoded_image_layout,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
let auth_context_subject_id_suffix = [
0u8, EncryptedChainedExtentsAssociatedDataAuthSubjectDataSuffix::JournalLog as u8,
];
let authenticated_associated_data = [
encoded_image_layout.as_slice(),
auth_context_subject_id_suffix.as_slice(),
];
let authenticated_associated_data =
io_slices::BuffersSliceIoSlicesIter::new(&authenticated_associated_data).map_infallible_err();
let next_chained_extent = match extents_decryption_instance.decrypt_one_extent(
io_slices::SingletonIoSliceMut::new(decrypted_next_journal_log_tail_extent.as_mut_slice())
.map_infallible_err(),
io_slices::SingletonIoSlice::new(&next_journal_log_tail_extent).map_infallible_err(),
authenticated_associated_data,
*next_journal_log_tail_extent_allocation_blocks,
) {
Ok(next_chained_extent) => next_chained_extent,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
if let Err(e) = this.decrypted_journal_log_extents.try_reserve(1) {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(NvFsError::from(e)));
};
this.decrypted_journal_log_extents
.push(decrypted_next_journal_log_tail_extent);
this.fut_state = match next_chained_extent {
Some(next_journal_log_tail_extent) => {
JournalLogReadFutureState::ReadNextJournalLogTailExtentPrepare {
next_journal_log_tail_extent,
}
}
None => JournalLogReadFutureState::DecodeJournalLog,
};
}
JournalLogReadFutureState::DecodeJournalLog => {
this.extents_decryption_instance = None;
let mut padding_len = match check_cbc_padding(
io_slices::BuffersSliceIoSlicesIter::new(&this.decrypted_journal_log_extents)
.map_infallible_err(),
) {
Ok(padding_len) => padding_len,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
while padding_len != 0 {
let last_decrypted_extent = match this.decrypted_journal_log_extents.last_mut() {
Some(last_decrypted_extent) => last_decrypted_extent,
None => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(nvfs_err_internal!()));
}
};
let last_decrypted_extent_len = last_decrypted_extent.len();
if last_decrypted_extent_len > padding_len {
last_decrypted_extent.truncate(last_decrypted_extent_len - padding_len);
padding_len = 0
} else {
padding_len -= last_decrypted_extent_len;
this.decrypted_journal_log_extents.pop();
}
}
let journal_log = match JournalLog::decode(
io_slices::BuffersSliceIoSlicesIter::new(&this.decrypted_journal_log_extents)
.map_infallible_err(),
mem::take(&mut this.log_extents),
image_layout,
root_key,
keys_cache,
) {
Ok(journal_log) => journal_log,
Err(e) => {
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Err(e));
}
};
this.decrypted_journal_log_extents = Vec::new();
this.fut_state = JournalLogReadFutureState::Done;
return task::Poll::Ready(Ok(Some(journal_log)));
}
JournalLogReadFutureState::Done => unreachable!(),
}
}
}
}
struct JournalLogReadExtentNvBlkDevReadRequest {
region: ChunkedIoRegion,
dst: FixedVec<u8, 7>,
}
impl JournalLogReadExtentNvBlkDevReadRequest {
fn new(physical_begin_128b: u64, physical_end_128b: u64, chunk_size_128b_log2: u32) -> Result<Self, NvFsError> {
debug_assert!(chunk_size_128b_log2 < u64::BITS - 7);
let region_len_128b = physical_end_128b - physical_begin_128b;
let region_len = region_len_128b << 7;
if region_len >> 7 != region_len_128b {
return Err(NvFsError::IoError(NvFsIoError::RegionOutOfRange));
}
let region_len = usize::try_from(region_len).map_err(|_| NvFsError::DimensionsNotSupported)?;
let dst = FixedVec::new_with_default(region_len)?;
let region = blkdev::ChunkedIoRegion::new(physical_begin_128b, physical_end_128b, chunk_size_128b_log2)
.map_err(|e| match e {
ChunkedIoRegionError::ChunkSizeOverflow => nvfs_err_internal!(),
ChunkedIoRegionError::InvalidBounds => nvfs_err_internal!(),
ChunkedIoRegionError::ChunkIndexOverflow => NvFsError::DimensionsNotSupported,
ChunkedIoRegionError::RegionUnaligned => {
NvFsError::FsFormatError(FormatError::UnalignedJournalExtents as isize)
}
})?;
Ok(Self { region, dst })
}
pub fn into_dst_buf(self) -> FixedVec<u8, 7> {
let Self { region: _, dst } = self;
dst
}
}
impl blkdev::NvBlkDevReadRequest for JournalLogReadExtentNvBlkDevReadRequest {
fn region(&self) -> &ChunkedIoRegion {
&self.region
}
fn get_destination_buffer(
&mut self,
range: &ChunkedIoRegionChunkRange,
) -> Result<Option<&mut [u8]>, blkdev::NvBlkDevIoError> {
let chunk_size_128b_log2 = self.region.chunk_size_128b_log2();
let (chunk_index, _) = range.chunk().decompose_to_hierarchic_indices([]);
let dst_chunk =
&mut self.dst[chunk_index << (chunk_size_128b_log2 + 7)..(chunk_index + 1) << (chunk_size_128b_log2 + 7)];
Ok(Some(&mut dst_chunk[range.range_in_chunk().clone()]))
}
}