leodos_protocols/transport/cfdp/pdu/file_directive/
metadata.rs1use crate::transport::cfdp::pdu::CfdpError;
2use crate::transport::cfdp::pdu::EntityId;
3use crate::transport::cfdp::pdu::Pdu;
4use crate::transport::cfdp::pdu::PduHeaderFixedPart;
5use crate::transport::cfdp::pdu::TransactionSeqNum;
6use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
7use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
8use crate::transport::cfdp::pdu::header::Direction;
9use crate::transport::cfdp::pdu::header::PduType;
10use crate::transport::cfdp::pdu::header::TransmissionMode;
11use crate::transport::cfdp::pdu::tlv::Tlv;
12use crate::transport::cfdp::pdu::tlv::TlvIterator;
13use crate::transport::cfdp::pdu::tlv::TlvType;
14use crate::transport::cfdp::pdu::tlv::fault_handler_override::FaultHandlerSet;
15use crate::transport::cfdp::pdu::tlv::fault_handler_override::TlvFaultHandlerOverride;
16use crate::utils::get_bits_u8;
17use crate::utils::set_bits_u8;
18
19use bon::bon;
20use zerocopy::FromBytes;
21use zerocopy::Immutable;
22use zerocopy::IntoBytes;
23use zerocopy::KnownLayout;
24use zerocopy::Unaligned;
25use zerocopy::network_endian::U32;
26use zerocopy::network_endian::U64;
27
28#[repr(C)]
52#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
53pub struct MetadataPdu {
54 packed_flags: u8,
56 rest: [u8],
58}
59
60#[rustfmt::skip]
61mod bitmasks {
63 pub const _META_RESERVED_MASK_1: u8 = 0b_10000000;
65 pub const META_CLOSURE_REQ_MASK: u8 = 0b_01000000;
67 pub const _META_RESERVED_MASK_2: u8 = 0b_00110000;
69 pub const META_CHECKSUM_TYPE_MASK: u8 = 0b_00001111;
71}
72
73use bitmasks::*;
74
75#[repr(u8)]
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ChecksumType {
79 Modular = 0,
81 Proximity1Crc32 = 1,
83 Crc32c = 2,
85 IeeeCrc32 = 3,
87 Null = 15,
89}
90
91impl TryFrom<u8> for ChecksumType {
92 type Error = CfdpError;
93 fn try_from(value: u8) -> Result<Self, Self::Error> {
94 match value {
95 0 => Ok(ChecksumType::Modular),
96 1 => Ok(ChecksumType::Proximity1Crc32),
97 2 => Ok(ChecksumType::Crc32c),
98 3 => Ok(ChecksumType::IeeeCrc32),
99 15 => Ok(ChecksumType::Null),
100 _ => Err(CfdpError::Custom("Unsupported checksum type")),
101 }
102 }
103}
104
105impl MetadataPdu {
106 pub fn closure_requested(&self) -> bool {
108 get_bits_u8(self.packed_flags, META_CLOSURE_REQ_MASK) == 1
109 }
110 pub fn set_closure_requested(&mut self, requested: bool) {
112 set_bits_u8(
113 &mut self.packed_flags,
114 META_CLOSURE_REQ_MASK,
115 if requested { 1 } else { 0 },
116 );
117 }
118
119 pub fn checksum_type(&self) -> Result<ChecksumType, CfdpError> {
121 get_bits_u8(self.packed_flags, META_CHECKSUM_TYPE_MASK).try_into()
122 }
123 pub fn set_checksum_type(&mut self, checksum_type: ChecksumType) {
125 set_bits_u8(
126 &mut self.packed_flags,
127 META_CHECKSUM_TYPE_MASK,
128 checksum_type as u8,
129 );
130 }
131
132 pub fn file_size(&self, large_file_flag: bool) -> Result<u64, CfdpError> {
134 if large_file_flag {
135 U64::ref_from_prefix(&self.rest)
136 .map_err(|_| CfdpError::Custom("Failed to parse 64-bit file size"))
137 .map(|(len, _)| len.get())
138 } else {
139 U32::ref_from_prefix(&self.rest)
140 .map_err(|_| CfdpError::Custom("Failed to parse 32-bit file size"))
141 .map(|(len, _)| len.get() as u64)
142 }
143 }
144 pub fn set_file_size(
146 &mut self,
147 large_file_flag: bool,
148 file_size: u64,
149 ) -> Result<(), CfdpError> {
150 if large_file_flag {
151 let file_size_field = U64::mut_from_prefix(&mut self.rest)
152 .map_err(|_| CfdpError::Custom("Failed to parse 64-bit file size"))?
153 .0;
154 file_size_field.set(file_size);
155 Ok(())
156 } else {
157 if file_size > u32::MAX as u64 {
158 return Err(CfdpError::Custom("File size exceeds 32-bit maximum"));
159 }
160 let file_size_field = U32::mut_from_prefix(&mut self.rest)
161 .map_err(|_| CfdpError::Custom("Failed to parse 32-bit file size"))?
162 .0;
163 file_size_field.set(file_size as u32);
164 Ok(())
165 }
166 }
167
168 pub fn variable_fields(
172 &self,
173 large_file_flag: bool,
174 ) -> Result<(&[u8], &[u8], TlvIterator<'_>), CfdpError> {
175 let file_size_len = if large_file_flag { 8 } else { 4 };
176 let mut remainder = self.rest.get(file_size_len..).ok_or_else(|| {
177 CfdpError::Custom("Invalid metadata: insufficient data for file size")
178 })?;
179
180 let source_len = *remainder
182 .first()
183 .ok_or_else(|| CfdpError::Custom("Invalid metadata: missing source name length"))?
184 as usize;
185
186 remainder = remainder
188 .get(1..)
189 .ok_or(CfdpError::Custom("Invalid metadata slice"))?;
190 if remainder.len() < source_len {
191 return Err(CfdpError::Custom(
192 "Invalid metadata: insufficient data for source name",
193 ));
194 }
195 let (source_name, mut remainder) = remainder.split_at(source_len);
196
197 let dest_len = *remainder
199 .first()
200 .ok_or_else(|| CfdpError::Custom("Invalid metadata: missing destination name length"))?
201 as usize;
202
203 remainder = remainder
205 .get(1..)
206 .ok_or(CfdpError::Custom("Invalid metadata slice"))?;
207 if remainder.len() < dest_len {
208 return Err(CfdpError::Custom(
209 "Invalid metadata: insufficient data for destination name",
210 ));
211 }
212 let (dest_name, options_tlvs) = remainder.split_at(dest_len);
213
214 Ok((
215 source_name,
216 dest_name,
217 TlvIterator {
218 buffer: options_tlvs,
219 },
220 ))
221 }
222
223 pub fn set_variable_fields(
225 &mut self,
226 large_file_flag: bool,
227 source_file_name: &[u8],
228 dest_file_name: &[u8],
229 options: Option<&[u8]>,
230 ) -> Result<(), CfdpError> {
231 let file_size_len = if large_file_flag { 8 } else { 4 };
232 let mut cursor = file_size_len;
233
234 if source_file_name.len() > 255 {
236 return Err(CfdpError::Custom("Source file name too long"));
237 }
238 if dest_file_name.len() > 255 {
240 return Err(CfdpError::Custom("Destination file name too long"));
241 }
242
243 *self
244 .rest
245 .get_mut(cursor)
246 .ok_or_else(|| CfdpError::Custom("Insufficient space for Source File Name length"))? =
247 source_file_name.len() as u8;
248 cursor += 1;
249 self.rest
250 .get_mut(cursor..cursor + source_file_name.len())
251 .ok_or_else(|| CfdpError::Custom("Insufficient space for Source File Name"))?
252 .copy_from_slice(source_file_name);
253 cursor += source_file_name.len();
254 *self.rest.get_mut(cursor).ok_or_else(|| {
255 CfdpError::Custom("Insufficient space for Destination File Name length")
256 })? = dest_file_name.len() as u8;
257 cursor += 1;
258 self.rest
259 .get_mut(cursor..cursor + dest_file_name.len())
260 .ok_or_else(|| CfdpError::Custom("Insufficient space for Destination File Name"))?
261 .copy_from_slice(dest_file_name);
262 cursor += dest_file_name.len();
263
264 if let Some(opts) = options {
266 self.rest
267 .get_mut(cursor..cursor + opts.len())
268 .ok_or_else(|| CfdpError::Custom("Insufficient space for Options TLVs"))?;
269 }
270
271 Ok(())
272 }
273
274 pub fn source_file_name(&self, large_file_flag: bool) -> Result<&[u8], CfdpError> {
276 self.variable_fields(large_file_flag).map(|(src, _, _)| src)
277 }
278
279 pub fn dest_file_name(&self, large_file_flag: bool) -> Result<&[u8], CfdpError> {
281 self.variable_fields(large_file_flag).map(|(_, dst, _)| dst)
282 }
283
284 pub fn options(&self, large_file_flag: bool) -> Result<TlvIterator<'_>, CfdpError> {
286 self.variable_fields(large_file_flag)
287 .map(|(_, _, tlv_iter)| tlv_iter)
288 }
289
290 pub fn fault_handler_overrides(
294 &self,
295 large_file_flag: bool,
296 ) -> Result<FaultHandlerSet, CfdpError> {
297 let mut handlers = FaultHandlerSet::default();
299 let tlv_iter = self.options(large_file_flag)?;
300
301 for tlv in tlv_iter {
302 if tlv.tlv_type()? == TlvType::FaultHandlerOverride {
303 if tlv.value().len() != 1 {
305 continue;
307 }
308 let fho_tlv = TlvFaultHandlerOverride::ref_from_bytes(tlv.value())
309 .map_err(|_| CfdpError::Custom("Failed to parse Fault Handler Override TLV"))?;
310
311 handlers.set_handler(fho_tlv.condition_code()?, fho_tlv.handler_code()?);
313 }
314 }
315 Ok(handlers)
316 }
317
318 pub fn filestore_requests(
320 &self,
321 large_file_flag: bool,
322 ) -> Result<impl Iterator<Item = &Tlv>, CfdpError> {
323 Ok(self
324 .options(large_file_flag)?
325 .filter(|tlv| tlv.tlv_type() == Ok(TlvType::FilestoreRequest)))
326 }
327
328 pub fn rest(&self) -> &[u8] {
330 &self.rest
331 }
332}
333
334#[bon]
335impl MetadataPdu {
336 #[builder]
338 pub fn new<'a>(
339 buffer: &'a mut [u8],
340 source_entity_id: EntityId,
341 dest_entity_id: EntityId,
342 transaction_seq_num: TransactionSeqNum,
343 transmission_mode: TransmissionMode,
344 large_file_flag: bool,
345 crc_flag: bool,
346 closure_requested: bool,
347 checksum_type: ChecksumType,
348 file_size: u64,
349 source_file_name: &'a [u8],
350 dest_file_name: &'a [u8],
351 options: Option<&'a [u8]>,
352 ) -> Result<&'a mut Pdu, CfdpError> {
353 let fixed_part_len = size_of::<u8>(); let file_size_len = if large_file_flag { 8 } else { 4 };
356 let src_name_lv_len = 1 + source_file_name.len(); let dst_name_lv_len = 1 + dest_file_name.len(); let options_len = options.map_or(0, |o| o.len());
359
360 let specific_data_len =
361 fixed_part_len + file_size_len + src_name_lv_len + dst_name_lv_len + options_len;
362 let data_field_len = (1 + specific_data_len) as u16;
363
364 let header = PduHeaderFixedPart::builder()
365 .version(1)
366 .pdu_type(PduType::FileDirective)
367 .direction(Direction::TowardReceiver)
368 .tx_mode(transmission_mode)
369 .crc_flag(crc_flag)
370 .large_file_flag(large_file_flag)
371 .data_field_len(data_field_len)
372 .seg_ctrl(false)
373 .seg_meta_flag(false)
374 .build()?;
375
376 let pdu = Pdu::builder()
377 .buffer(buffer)
378 .header_fixed(header)
379 .source_entity_id(source_entity_id)
380 .destination_entity_id(dest_entity_id)
381 .transaction_seq_num(transaction_seq_num)
382 .build()?;
383
384 let data_field = pdu
385 .data_field_mut()
386 .map_err(|_| CfdpError::Custom("Failed to get data field"))?;
387 let provided_len = data_field.len();
388 let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field).map_err(|_| {
389 CfdpError::BufferTooSmall {
390 required: data_field_len as usize,
391 provided: provided_len,
392 }
393 })?;
394 directive_pdu.set_directive_code(DirectiveCode::Metadata);
395
396 let provided_len = directive_pdu.rest.len();
397 let rest_len = file_size_len + src_name_lv_len + dst_name_lv_len + options_len;
398 let meta_pdu =
399 MetadataPdu::mut_from_bytes_with_elems(&mut directive_pdu.rest, rest_len)
400 .map_err(|_| CfdpError::BufferTooSmall {
401 required: specific_data_len,
402 provided: provided_len,
403 })?;
404 meta_pdu.set_closure_requested(closure_requested);
405 meta_pdu.set_checksum_type(checksum_type);
406
407 meta_pdu
408 .set_file_size(large_file_flag, file_size)
409 .map_err(|_| CfdpError::Custom("Failed to set file size in Metadata PDU"))?;
410 meta_pdu
411 .set_variable_fields(large_file_flag, source_file_name, dest_file_name, options)
412 .map_err(|_| CfdpError::Custom("Failed to set variable fields in Metadata PDU"))?;
413
414 Ok(pdu)
415 }
416}