diff --git a/src/cfdp/lv.rs b/src/cfdp/lv.rs new file mode 100644 index 0000000..279a313 --- /dev/null +++ b/src/cfdp/lv.rs @@ -0,0 +1,98 @@ +use crate::cfdp::TlvLvError; +use crate::{ByteConversionError, SizeMissmatch}; +use std::prelude::v1::String; + +pub const MIN_LV_LEN: usize = 1; + +/// Generic CFDP length-value (LV) abstraction. +/// +/// This is just a thin wrapper around a raw slice which performs some additional error handling. +pub struct Lv<'a> { + data: &'a [u8], +} + +pub(crate) fn generic_len_check_data_serialization( + buf: &[u8], + data: &[u8], + min_overhead: usize, +) -> Result<(), ByteConversionError> { + if buf.len() < data.len() + min_overhead { + return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: data.len() + min_overhead, + })); + } + Ok(()) +} + +pub(crate) fn generic_len_check_deserialization( + buf: &[u8], + min_overheader: usize, +) -> Result<(), ByteConversionError> { + if buf.len() < min_overheader { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: MIN_LV_LEN, + })); + } + Ok(()) +} +impl<'a> Lv<'a> { + pub fn new(data: &[u8]) -> Result { + if data.len() > u8::MAX as usize { + return Err(TlvLvError::DataTooLarge(data.len())); + } + Ok(Lv { data }) + } + + /// Helper function to build a string LV. This is especially useful for the file or directory + /// path LVs + pub fn new_from_str(str_slice: &str) -> Result { + Self::new(str_slice.as_bytes()) + } + + /// Helper function to build a string LV. This is especially useful for the file or directory + /// path LVs + #[cfg(feature = "std")] + pub fn new_from_string(string: &'a String) -> Result, TlvLvError> { + Self::new(string.as_bytes()) + } + + pub fn value(&self) -> &[u8] { + self.data + } + + /// Writes the LV to a raw buffer. Please note that the first byte will contain the length + /// of the value, but the values may not exceed a length of [u8::MAX]. + pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { + generic_len_check_data_serialization(buf, self.data, MIN_LV_LEN)?; + Ok(self.write_to_be_bytes_no_len_check(buf)) + } + + /// Reads a LV from a raw buffer. + pub fn from_be_bytes(buf: &'a [u8]) -> Result, TlvLvError> { + generic_len_check_deserialization(buf, MIN_LV_LEN)?; + Self::from_be_bytes_no_len_check(buf) + } + + pub(crate) fn write_to_be_bytes_no_len_check(&self, buf: &mut [u8]) -> usize { + // Length check in constructor ensures the length always has a valid value. + buf[0] = self.data.len() as u8; + buf[MIN_LV_LEN..self.data.len() + MIN_LV_LEN].copy_from_slice(self.data); + MIN_LV_LEN + self.data.len() + } + + pub(crate) fn from_be_bytes_no_len_check(buf: &'a [u8]) -> Result, TlvLvError> { + let value_len = buf[0] as usize; + if buf.len() < value_len + MIN_LV_LEN { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: MIN_LV_LEN + value_len, + }) + .into()); + } + Ok(Self { + data: &buf[MIN_LV_LEN..MIN_LV_LEN + value_len], + }) + } +} diff --git a/src/cfdp/mod.rs b/src/cfdp/mod.rs index 2704bb6..c51cdaf 100644 --- a/src/cfdp/mod.rs +++ b/src/cfdp/mod.rs @@ -1,7 +1,12 @@ +use crate::ByteConversionError; +use core::fmt::{Display, Formatter}; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use std::error::Error; +pub mod lv; pub mod pdu; pub mod tlv; @@ -127,3 +132,49 @@ pub enum ChecksumType { } pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4]; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TlvLvError { + DataTooLarge(usize), + ByteConversionError(ByteConversionError), + /// Only relevant for TLV de-serialization. + UnknownTlvType(u8), +} + +impl From for TlvLvError { + fn from(value: ByteConversionError) -> Self { + Self::ByteConversionError(value) + } +} + +impl Display for TlvLvError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + TlvLvError::UnknownTlvType(raw_tlv_id) => { + write!(f, "unknown TLV type {raw_tlv_id}") + } + TlvLvError::DataTooLarge(data_len) => { + write!( + f, + "data with size {} larger than allowed {} bytes", + data_len, + u8::MAX + ) + } + TlvLvError::ByteConversionError(e) => { + write!(f, "{}", e) + } + } + } +} + +#[cfg(feature = "std")] +impl Error for TlvLvError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + TlvLvError::ByteConversionError(e) => Some(e), + _ => None, + } + } +} diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index 2883478..2bdea62 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -1,15 +1,17 @@ +use crate::cfdp::lv::Lv; use crate::cfdp::pdu::{FileDirectiveType, PduHeader}; use crate::cfdp::ChecksumType; -pub struct MetadataParams { +pub struct MetadataGenericParams { closure_requested: bool, checksum_type: ChecksumType, file_size: u64, - //src_file_name: } -pub struct MetadataPdu { +pub struct MetadataPdu<'src_name, 'dest_name> { pdu_header: PduHeader, file_directive: FileDirectiveType, - metadata_params: MetadataParams, + metadata_params: MetadataGenericParams, + src_file_name: Option>, + dest_file_name: Option>, } diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index 81bcc4a..a68b230 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -63,14 +63,21 @@ impl Display for PduError { ) } PduError::ByteConversionError(e) => { - write!(f, "low level byte conversion error: {e}") + write!(f, "{}", e) } } } } #[cfg(feature = "std")] -impl Error for PduError {} +impl Error for PduError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + PduError::ByteConversionError(e) => Some(e), + _ => None, + } + } +} impl From for PduError { fn from(value: ByteConversionError) -> Self { diff --git a/src/cfdp/tlv.rs b/src/cfdp/tlv.rs index b23e938..60dcab6 100644 --- a/src/cfdp/tlv.rs +++ b/src/cfdp/tlv.rs @@ -1,4 +1,8 @@ -use crate::{ByteConversionError, SizeMissmatch}; +use crate::cfdp::lv::{ + generic_len_check_data_serialization, generic_len_check_deserialization, Lv, MIN_LV_LEN, +}; +use crate::cfdp::TlvLvError; +use crate::ByteConversionError; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -19,68 +23,40 @@ pub enum TlvType { pub struct Tlv<'a> { tlv_type: TlvType, - data: &'a [u8], -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum TlvError { - DataTooLarge(usize), - ByteConversionError(ByteConversionError), - UnknownTlvType(u8), -} - -impl From for TlvError { - fn from(value: ByteConversionError) -> Self { - Self::ByteConversionError(value) - } + lv: Lv<'a>, } impl<'a> Tlv<'a> { - pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { + pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { if data.len() > u8::MAX as usize { - return Err(TlvError::DataTooLarge(data.len())); + return Err(TlvLvError::DataTooLarge(data.len())); } - Ok(Tlv { tlv_type, data }) + Ok(Tlv { + tlv_type, + lv: Lv::new(data)?, + }) } pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - if buf.len() < self.data.len() + MIN_TLV_LEN { - return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: self.data.len() + MIN_TLV_LEN, - })); - } + generic_len_check_data_serialization(buf, self.value(), MIN_TLV_LEN)?; buf[0] = self.tlv_type as u8; - // Length check in constructor ensures the length always has a valid value. - buf[1] = self.data.len() as u8; - buf[MIN_TLV_LEN..self.data.len() + MIN_TLV_LEN].copy_from_slice(self.data); - Ok(MIN_TLV_LEN + self.data.len()) + self.lv.write_to_be_bytes_no_len_check(&mut buf[1..]); + Ok(MIN_TLV_LEN + self.value().len()) } - pub fn from_be_bytes(buf: &'a [u8]) -> Result, TlvError> { - if buf.len() < MIN_TLV_LEN { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: MIN_TLV_LEN, - }) - .into()); - } + pub fn value(&self) -> &[u8] { + self.lv.value() + } + + pub fn from_be_bytes(buf: &'a [u8]) -> Result, TlvLvError> { + generic_len_check_deserialization(buf, MIN_TLV_LEN)?; let tlv_type_res = TlvType::try_from(buf[0]); if tlv_type_res.is_err() { - return Err(TlvError::UnknownTlvType(buf[1])); - } - let value_len = buf[1] as usize; - if buf.len() < value_len { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: MIN_TLV_LEN + value_len, - }) - .into()); + return Err(TlvLvError::UnknownTlvType(buf[1])); } Ok(Self { tlv_type: tlv_type_res.unwrap(), - data: &buf[MIN_TLV_LEN..MIN_TLV_LEN + value_len], + lv: Lv::from_be_bytes(&buf[MIN_LV_LEN..])?, }) } }