cfdp/
dest.rs

1//! # CFDP Destination Entity Module
2//!
3//! The [DestinationHandler] is the primary component of this module which converts the PDUs sent
4//! from a remote source entity back to a file. A file copy operation on the receiver side
5//! is started with the reception of a Metadata PDU, for example one generated by the
6//! [spacepackets::cfdp::pdu::metadata::MetadataPduCreator]. After that, file packet PDUs, for
7//! example generated with the [spacepackets::cfdp::pdu::file_data] module, can be inserted into
8//! the destination handler and will be assembled into a file.
9//!
10//! A destination entity might still generate packets which need to be sent back to the source
11//! entity of the file transfer. However, this handler allows freedom of communication like the
12//! source entity by using a user-provided [PduSender] instance to send all generated PDUs.
13//!
14//! The transaction will be finished when following conditions are met:
15//!
16//!  1. A valid EOF PDU, for example one generated by the [spacepackets::cfdp::pdu::eof::EofPdu]
17//!     helper, has been inserted into the class.
18//!  2. The file checksum verification has been successful. If this is not the case for the
19//!     unacknowledged mode, the handler will re-attempt the checksum calculation up to a certain
20//!     threshold called the check limit. If the threshold is reached, the transaction will
21//!     finish with a failure.
22//!
23//! ### Unacknowledged mode with closure
24//!
25//!  3. Finished PDU has been sent back to the remote side.
26//!
27//! ### Acknowledged mode
28//!
29//!  3. An EOF ACK PDU has been sent back to the remote side.
30//!  4. A Finished PDU has been sent back to the remote side.
31//!  5. A Finished PDU ACK was received.
32use crate::{
33    DummyPduProvider, FaultInfo, GenericSendError, IndicationConfig, PduProvider,
34    PositiveAckParams,
35    lost_segments::{LostSegmentError, LostSegmentStore},
36    user::TransactionFinishedParams,
37};
38use core::{
39    cell::{Cell, RefCell},
40    str::{Utf8Error, from_utf8, from_utf8_unchecked},
41};
42
43use super::{
44    Countdown, EntityType, LocalEntityConfig, PacketTarget, PduSender, RemoteConfigStore,
45    RemoteEntityConfig, State, TimerContext, TimerCreator, TransactionId, UserFaultHook,
46    filestore::{FilestoreError, VirtualFilestore},
47    user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams},
48};
49use smallvec::SmallVec;
50use spacepackets::{
51    cfdp::{
52        ChecksumType, ConditionCode, FaultHandlerCode, LargeFileFlag, PduType, TransactionStatus,
53        TransmissionMode,
54        pdu::{
55            CfdpPdu, CommonPduConfig, FileDirectiveType, PduError, PduHeader,
56            ack::AckPdu,
57            eof::EofPdu,
58            file_data::FileDataPdu,
59            finished::{DeliveryCode, FileStatus, FinishedPduCreator},
60            metadata::{MetadataGenericParams, MetadataPduReader},
61            nak::{NakPduCreator, NakPduCreatorWithReservedSeqReqsBuf},
62        },
63        tlv::{EntityIdTlv, GenericTlv, ReadableTlv, TlvType, msg_to_user::MsgToUserTlv},
64    },
65    util::{UnsignedByteField, UnsignedEnum},
66};
67
68#[derive(Debug)]
69struct FileNames {
70    src_file_name: [u8; u8::MAX as usize],
71    src_file_name_len: usize,
72    dest_file_name: [u8; u8::MAX as usize],
73    dest_file_name_len: usize,
74    dest_path_buf: [u8; u8::MAX as usize * 2],
75    dest_file_path_len: usize,
76}
77
78#[derive(Debug, Default, Clone, Copy)]
79pub struct AnomalyTracker {
80    invalid_ack_directive_code: u8,
81    lost_segment_errors: u8,
82}
83
84impl AnomalyTracker {
85    #[inline]
86    pub fn invalid_ack_directive_code(&mut self) -> u8 {
87        self.invalid_ack_directive_code
88    }
89
90    #[inline]
91    pub fn lost_segment_errors(&mut self) -> u8 {
92        self.lost_segment_errors
93    }
94
95    #[inline]
96    pub fn increment_lost_segment_errors(&mut self) {
97        self.lost_segment_errors = self.lost_segment_errors.wrapping_add(1);
98    }
99
100    #[inline]
101    pub fn increment_invalid_ack_directive_code(&mut self) {
102        self.invalid_ack_directive_code = self.invalid_ack_directive_code.wrapping_add(1);
103    }
104
105    #[inline]
106    pub fn reset(&mut self) {
107        *self = Default::default();
108    }
109}
110
111#[derive(Debug, PartialEq, Eq, Copy, Clone)]
112enum CompletionDisposition {
113    Completed = 0,
114    Cancelled = 1,
115}
116
117/// This enumeration models the different transaction steps of the destination entity handler.
118#[derive(Debug, Copy, Clone, PartialEq, Eq)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120#[cfg_attr(feature = "defmt", derive(defmt::Format))]
121pub enum TransactionStep {
122    Idle,
123    TransactionStart,
124    /// Special state which is only used for acknowledged mode. The CFDP entity is still waiting
125    /// for a missing metadata PDU to be re-sent. Until then, all arriving file data PDUs will only
126    /// update the internal lost segment tracker. When the EOF PDU arrives, the state will be left.
127    /// Please note that deferred lost segment handling might also be active when this state is set.
128    WaitingForMetadata,
129    ReceivingFileDataPdus,
130    /// This is the check timer step as specified in chapter 4.6.3.3 b) of the standard.
131    /// The destination handler will still check for file data PDUs which might lead to a full
132    /// file transfer completion.
133    ReceivingFileDataPdusWithCheckLimitHandling,
134    WaitingForMissingData,
135    //SendingAckPdu,
136    TransferCompletion,
137    WaitingForFinishedAck,
138}
139
140#[derive(Debug)]
141struct FinishedParams {
142    condition_code: Cell<ConditionCode>,
143    delivery_code: Cell<DeliveryCode>,
144    fault_location_finished: Cell<Option<EntityIdTlv>>,
145    file_status: FileStatus,
146}
147
148impl Default for FinishedParams {
149    fn default() -> Self {
150        Self {
151            condition_code: Cell::new(ConditionCode::NoError),
152            delivery_code: Cell::new(DeliveryCode::Incomplete),
153            fault_location_finished: Cell::new(None),
154            file_status: FileStatus::Unreported,
155        }
156    }
157}
158
159impl FinishedParams {
160    pub fn reset(&mut self) {
161        self.condition_code.set(ConditionCode::NoError);
162        self.delivery_code.set(DeliveryCode::Incomplete);
163        self.file_status = FileStatus::Unreported;
164    }
165}
166
167#[derive(Debug, Copy, Clone)]
168pub struct AcknowledgedModeParams {
169    last_start_offset: u64,
170    last_end_offset: u64,
171    metadata_missing: bool,
172    nak_activity_counter: u32,
173    deferred_procedure_active: bool,
174}
175
176// This contains parameters for destination transaction.
177#[derive(Debug)]
178struct TransactionParams<CountdownInstance: Countdown> {
179    pdu_conf: CommonPduConfig,
180    file_names: FileNames,
181    msgs_to_user_size: usize,
182    // TODO: Should we make this configurable?
183    msgs_to_user_buf: [u8; 1024],
184    remote_cfg: Option<RemoteEntityConfig>,
185    transaction_id: Option<TransactionId>,
186    metadata_params: MetadataGenericParams,
187    file_size: u64,
188    progress: u64,
189    acked_params: Option<AcknowledgedModeParams>,
190    deferred_procedure_timer: Option<CountdownInstance>,
191    finished_params: FinishedParams,
192    positive_ack_params: Option<PositiveAckParams>,
193    ack_timer: Option<CountdownInstance>,
194    metadata_only: bool,
195    completion_disposition: Cell<CompletionDisposition>,
196    checksum: u32,
197    anomaly_tracker: AnomalyTracker,
198    current_check_count: u32,
199    current_check_timer: Option<CountdownInstance>,
200}
201
202impl<CheckTimer: Countdown> TransactionParams<CheckTimer> {
203    fn transmission_mode(&self) -> TransmissionMode {
204        self.pdu_conf.trans_mode
205    }
206}
207
208impl Default for FileNames {
209    fn default() -> Self {
210        Self {
211            src_file_name: [0; u8::MAX as usize],
212            src_file_name_len: Default::default(),
213            dest_file_name: [0; u8::MAX as usize],
214            dest_file_name_len: Default::default(),
215            dest_path_buf: [0; u8::MAX as usize * 2],
216            dest_file_path_len: Default::default(),
217        }
218    }
219}
220
221impl<CheckTimer: Countdown> TransactionParams<CheckTimer> {
222    fn file_size(&self) -> u64 {
223        self.metadata_params.file_size
224    }
225
226    fn metadata_params(&self) -> &MetadataGenericParams {
227        &self.metadata_params
228    }
229}
230
231impl<CheckTimer: Countdown> Default for TransactionParams<CheckTimer> {
232    fn default() -> Self {
233        Self {
234            pdu_conf: Default::default(),
235            msgs_to_user_size: 0,
236            file_size: 0,
237            msgs_to_user_buf: [0; 1024],
238            file_names: Default::default(),
239            remote_cfg: None,
240            transaction_id: None,
241            metadata_params: Default::default(),
242            progress: Default::default(),
243            deferred_procedure_timer: None,
244            acked_params: None,
245            metadata_only: false,
246            positive_ack_params: None,
247            ack_timer: None,
248            finished_params: FinishedParams::default(),
249            completion_disposition: Cell::new(CompletionDisposition::Completed),
250            checksum: 0,
251            current_check_count: 0,
252            anomaly_tracker: AnomalyTracker::default(),
253            current_check_timer: None,
254        }
255    }
256}
257
258impl<CheckTimer: Countdown> TransactionParams<CheckTimer> {
259    fn reset(&mut self) {
260        self.finished_params.reset();
261        self.anomaly_tracker.reset();
262    }
263}
264
265#[derive(Debug, thiserror::Error)]
266#[non_exhaustive]
267pub enum DestError {
268    /// File directive expected, but none specified
269    #[error("expected file directive")]
270    DirectiveFieldEmpty,
271    #[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")]
272    CantProcessPacketType {
273        pdu_type: PduType,
274        directive_type: Option<FileDirectiveType>,
275    },
276    #[error("first packet must be metadata PDU for unacknowledged transfers")]
277    FirstPacketNotMetadata,
278    #[error(
279        "can not process PDU with type {pdu_type:?} and directive field {file_directive_type:?} in \
280        current transaction step {step:?}"
281    )]
282    WrongStepForPdu {
283        step: TransactionStep,
284        pdu_type: PduType,
285        file_directive_type: Option<FileDirectiveType>,
286    },
287    // Received new metadata PDU while being already being busy with a file transfer.
288    #[error("busy with transfer")]
289    RecvdMetadataButIsBusy,
290    #[error("empty source file field")]
291    EmptySrcFileField,
292    #[error("empty dest file field")]
293    EmptyDestFileField,
294    #[error("pdu error {0}")]
295    Pdu(#[from] PduError),
296    #[error("io error {0}")]
297    #[cfg(feature = "std")]
298    Io(#[from] std::io::Error),
299    #[error("file store error: {0}")]
300    Filestore(#[from] FilestoreError),
301    #[error("unexpected large file size which is not supported for current transfer")]
302    UnexpectedLargeFileSize,
303    #[error("lost segment error: {0}")]
304    LostSegmentError(#[from] LostSegmentError),
305    #[error("path conversion error {0}")]
306    PathConversion(#[from] Utf8Error),
307    #[error("error building dest path from source file name and dest folder")]
308    PathConcat,
309    #[error("no remote entity configuration found for {0:?}")]
310    NoRemoteConfigFound(UnsignedByteField),
311    #[error("issue sending PDU: {0}")]
312    SendError(#[from] GenericSendError),
313    #[error("invalid remote entity configuration: {0:?}")]
314    InvalidRemoteConfig(RemoteEntityConfig),
315    #[error("cfdp feature not implemented")]
316    NotImplemented,
317}
318
319/// This is the primary CFDP destination handler. It models the CFDP destination entity, which is
320/// primarily responsible for receiving files sent from another CFDP entity. It performs the
321/// reception side of File Copy Operations.
322///
323/// The [DestinationHandler::state_machine] function is the primary function to drive the
324/// destination handler. It can be used to insert packets into the destination
325/// handler and driving the state machine, which might generate new packets to be sent to the
326/// remote entity. Please note that the destination handler can also only process Metadata, EOF and
327/// Prompt PDUs in addition to ACK PDUs where the acknowledged PDU is the Finished PDU.
328/// All generated packets are sent using the user provided [PduSender].
329///
330///
331/// The handler has an internal buffer for PDU generation and checksum generation. The size of this
332/// buffer is select via Cargo features and defaults to 2048 bytes. It does not allocate
333/// memory during run-time and thus is suitable for embedded systems where  allocation is
334/// not possible.  Furthermore, it uses the [VirtualFilestore] abstraction to allow
335/// usage on systems without a [std] filesystem.
336///
337/// This handler is able to deal with file copy operations to directories, similarly to how the
338/// UNIX tool `cp` works. If the destination path is a directory instead of a regular  full path,
339/// the source path base file name will be appended to the destination path to form the resulting
340/// new full path.
341///
342// This handler also does not support concurrency out of the box but is flexible enough to be used
343/// in different concurrent contexts. For example, you can dynamically create new handlers and
344/// run them inside a thread pool, or move the newly created handler to a new thread."""
345pub struct DestinationHandler<
346    PduSenderInstance: PduSender,
347    UserFaultHookInstance: UserFaultHook,
348    VirtualFileStoreInstance: VirtualFilestore,
349    RemoteConfigStoreInstance: RemoteConfigStore,
350    TimerCreatorInstance: TimerCreator<Countdown = CountdownInstance>,
351    CountdownInstance: Countdown,
352    LostSegmentTracker: LostSegmentStore,
353> {
354    local_cfg: LocalEntityConfig<UserFaultHookInstance>,
355    step: core::cell::Cell<TransactionStep>,
356    state: State,
357    transaction_params: TransactionParams<CountdownInstance>,
358    pdu_and_cksum_buffer: RefCell<[u8; crate::buf_len::PACKET_BUF_LEN]>,
359    pub pdu_sender: PduSenderInstance,
360    pub vfs: VirtualFileStoreInstance,
361    pub remote_cfg_table: RemoteConfigStoreInstance,
362    pub check_timer_creator: TimerCreatorInstance,
363    lost_segment_tracker: LostSegmentTracker,
364}
365
366#[cfg(feature = "std")]
367pub type StdDestinationHandler<PduSender, UserFaultHook> = DestinationHandler<
368    PduSender,
369    UserFaultHook,
370    crate::filestore::NativeFilestore,
371    crate::RemoteConfigStoreStd,
372    crate::StdTimerCreator,
373    crate::StdCountdown,
374    crate::lost_segments::LostSegmentsList,
375>;
376
377#[cfg(feature = "std")]
378impl<PduSenderInstance: PduSender, UserFaultHookInstance: UserFaultHook>
379    StdDestinationHandler<PduSenderInstance, UserFaultHookInstance>
380{
381    #[cfg(feature = "std")]
382    pub fn new_std(
383        local_cfg: LocalEntityConfig<UserFaultHookInstance>,
384        pdu_sender: PduSenderInstance,
385    ) -> Self {
386        Self::new(
387            local_cfg,
388            pdu_sender,
389            crate::filestore::NativeFilestore::default(),
390            crate::RemoteConfigStoreStd::default(),
391            crate::StdTimerCreator::default(),
392            crate::lost_segments::LostSegmentsList::default(),
393        )
394    }
395}
396
397impl<
398    PduSenderInstance: PduSender,
399    UserFaultHookInstance: UserFaultHook,
400    VirtualFilestoreInstance: VirtualFilestore,
401    RemoteConfigStoreInstance: RemoteConfigStore,
402    TimerCreatorInstance: TimerCreator<Countdown = CountdownInstance>,
403    CountdownInstance: Countdown,
404    LostSegmentTracker: LostSegmentStore,
405>
406    DestinationHandler<
407        PduSenderInstance,
408        UserFaultHookInstance,
409        VirtualFilestoreInstance,
410        RemoteConfigStoreInstance,
411        TimerCreatorInstance,
412        CountdownInstance,
413        LostSegmentTracker,
414    >
415{
416    /// Constructs a new destination handler.
417    ///
418    /// # Arguments
419    ///
420    /// * `local_cfg` - The local CFDP entity configuration.
421    ///
422    /// * `pdu_and_cksum_buf_size` - The handler requires a buffer to generate PDUs and perform
423    ///   checksum calculations. This parameter can either be a known upper bound for the packet
424    ///   size, for example 2048 or 4096 bytes.
425    ///   It can also specifically be determined by the largest packet size parameter of all
426    ///   remote entity configurations in the passed `remote_cfg_table`.
427    /// * `pdu_sender` - [PduSender] used to send generated PDU packets.
428    /// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
429    ///   implementation from the underlying filestore/filesystem. This allows to use this handler
430    ///   for embedded systems where a standard runtime might not be available.
431    /// * `remote_cfg_table` - The [RemoteConfigStore] used to look up remote
432    ///   entities and target specific configuration for file copy operations.
433    /// * `check_timer_creator` - [TimerCreator] used by the CFDP handler to generate
434    ///   timers required by various tasks. This allows to use this handler for embedded systems
435    ///   where the standard time APIs might not be available.
436    pub fn new(
437        local_cfg: LocalEntityConfig<UserFaultHookInstance>,
438        pdu_sender: PduSenderInstance,
439        vfs: VirtualFilestoreInstance,
440        remote_cfg_table: RemoteConfigStoreInstance,
441        timer_creator: TimerCreatorInstance,
442        lost_segment_tracker: LostSegmentTracker,
443    ) -> Self {
444        Self {
445            local_cfg,
446            step: Cell::new(TransactionStep::Idle),
447            state: State::Idle,
448            transaction_params: Default::default(),
449            pdu_and_cksum_buffer: core::cell::RefCell::new([0; crate::buf_len::PACKET_BUF_LEN]),
450            pdu_sender,
451            vfs,
452            remote_cfg_table,
453            check_timer_creator: timer_creator,
454            lost_segment_tracker,
455        }
456    }
457
458    pub fn state_machine_no_packet(
459        &mut self,
460        cfdp_user: &mut impl CfdpUser,
461    ) -> Result<u32, DestError> {
462        self.state_machine(cfdp_user, None::<&DummyPduProvider>)
463    }
464
465    /// This is the core function to drive the destination handler. It is also used to insert
466    /// packets into the destination handler.
467    ///
468    /// The state machine should either be called if a packet with the appropriate destination ID
469    /// is received and periodically to perform all CFDP related tasks, for example
470    /// checking for timeouts or missed file segments.
471    ///
472    /// The function returns the number of sent PDU packets on success.
473    pub fn state_machine(
474        &mut self,
475        cfdp_user: &mut impl CfdpUser,
476        packet_to_insert: Option<&impl PduProvider>,
477    ) -> Result<u32, DestError> {
478        let mut sent_packets = 0;
479        if let Some(packet) = packet_to_insert {
480            sent_packets += self.insert_packet(cfdp_user, packet)?;
481        }
482        match self.state {
483            State::Idle => {
484                // TODO: In acknowledged mode, add timer handling.
485            }
486            State::Busy => {
487                sent_packets += self.fsm_busy(cfdp_user)?;
488            }
489            State::Suspended => {
490                // There is now way to suspend the handler currently anyway.
491            }
492        }
493        Ok(sent_packets)
494    }
495
496    /// This function models the Cancel.request CFDP primitive and is the recommended way
497    /// to cancel a transaction. It will cause a Notice Of Cancellation at this entity.
498    /// Please note that the state machine might still be active because a cancelled transfer
499    /// might still require some packets to be sent to the remote sender entity.
500    ///
501    /// If no unexpected errors occur, this function returns whether the current transfer
502    /// was cancelled. It returns [false] if the state machine is currently idle or if there
503    /// is a transaction ID missmatch.
504    pub fn cancel_request(&mut self, transaction_id: &TransactionId) -> bool {
505        if self.state() == super::State::Idle {
506            return false;
507        }
508        if let Some(active_id) = self.transaction_id() {
509            if active_id == *transaction_id {
510                self.trigger_notice_of_completion_cancelled(
511                    ConditionCode::CancelRequestReceived,
512                    EntityIdTlv::new(self.local_cfg.id),
513                );
514
515                self.set_step(TransactionStep::TransferCompletion);
516                return true;
517            }
518        }
519        false
520    }
521
522    /// Returns [None] if the state machine is IDLE, and the transmission mode of the current
523    /// request otherwise.
524    #[inline]
525    pub fn transmission_mode(&self) -> Option<TransmissionMode> {
526        if self.state == State::Idle {
527            return None;
528        }
529        Some(self.transaction_params.transmission_mode())
530    }
531
532    #[inline]
533    pub fn transaction_id(&self) -> Option<TransactionId> {
534        self.transaction_params.transaction_id
535    }
536
537    /// Get the step, which denotes the exact step of a pending CFDP transaction when applicable.
538    #[inline]
539    pub fn step(&self) -> TransactionStep {
540        self.step.get()
541    }
542
543    /// Get the step, which denotes whether the CFDP handler is active, and which CFDP class
544    /// is used if it is active.
545    #[inline]
546    pub fn state(&self) -> State {
547        self.state
548    }
549
550    #[inline]
551    pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHookInstance> {
552        &self.local_cfg
553    }
554
555    #[inline]
556    pub fn local_cfg_mut(&mut self) -> &mut LocalEntityConfig<UserFaultHookInstance> {
557        &mut self.local_cfg
558    }
559
560    /// This function is public to allow completely resetting the handler, but it is explicitely
561    /// discouraged to do this. CFDP has mechanism to detect issues and errors on itself.
562    /// Resetting the handler might interfere with these mechanisms and lead to unexpected
563    /// behaviour.
564    pub fn reset(&mut self) {
565        self.set_step(TransactionStep::Idle);
566        self.state = State::Idle;
567        self.transaction_params.reset();
568    }
569
570    fn insert_packet(
571        &mut self,
572        cfdp_user: &mut impl CfdpUser,
573        packet_to_insert: &impl PduProvider,
574    ) -> Result<u32, DestError> {
575        let mut sent_packets = 0;
576        if packet_to_insert.packet_target()? != PacketTarget::DestEntity {
577            // Unwrap is okay here, a PacketInfo for a file data PDU should always have the
578            // destination as the target.
579            return Err(DestError::CantProcessPacketType {
580                pdu_type: packet_to_insert.pdu_type(),
581                directive_type: packet_to_insert.file_directive_type(),
582            });
583        }
584        match packet_to_insert.pdu_type() {
585            PduType::FileDirective => {
586                if packet_to_insert.file_directive_type().is_none() {
587                    return Err(DestError::DirectiveFieldEmpty);
588                }
589                sent_packets += self.handle_file_directive(
590                    cfdp_user,
591                    packet_to_insert.file_directive_type().unwrap(),
592                    packet_to_insert.raw_pdu(),
593                )?;
594            }
595            PduType::FileData => {
596                let fd_pdu = FileDataPdu::from_bytes(packet_to_insert.raw_pdu())?;
597                sent_packets += self.handle_file_data(cfdp_user, fd_pdu)?;
598            }
599        }
600        Ok(sent_packets)
601    }
602
603    fn handle_file_directive(
604        &mut self,
605        cfdp_user: &mut impl CfdpUser,
606        pdu_directive: FileDirectiveType,
607        raw_packet: &[u8],
608    ) -> Result<u32, DestError> {
609        let mut sent_packets = 0;
610        match pdu_directive {
611            FileDirectiveType::EofPdu => {
612                let eof_pdu = EofPdu::from_bytes(raw_packet)?;
613                sent_packets += self.handle_eof_pdu(cfdp_user, eof_pdu)?
614            }
615            FileDirectiveType::FinishedPdu
616            | FileDirectiveType::NakPdu
617            | FileDirectiveType::KeepAlivePdu => {
618                return Err(DestError::CantProcessPacketType {
619                    pdu_type: PduType::FileDirective,
620                    directive_type: Some(pdu_directive),
621                });
622            }
623            FileDirectiveType::AckPdu => {
624                let ack_pdu = AckPdu::from_bytes(raw_packet)?;
625                self.handle_ack_pdu(ack_pdu)?;
626            }
627            FileDirectiveType::MetadataPdu => {
628                let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?;
629                self.handle_metadata_pdu(metadata_pdu)?
630            }
631            FileDirectiveType::PromptPdu => self.handle_prompt_pdu(raw_packet)?,
632        };
633        Ok(sent_packets)
634    }
635
636    fn handle_ack_pdu(&mut self, ack_pdu: AckPdu) -> Result<(), DestError> {
637        if ack_pdu.directive_code_of_acked_pdu() != FileDirectiveType::FinishedPdu {
638            self.transaction_params
639                .anomaly_tracker
640                .increment_invalid_ack_directive_code();
641        }
642        // We are done.
643        self.reset();
644        Ok(())
645    }
646
647    fn first_packet_handling(&mut self, pdu_config: &CommonPduConfig) -> Result<(), DestError> {
648        self.transaction_params.reset();
649        let remote_cfg = self.remote_cfg_table.get(pdu_config.source_id().value());
650        if remote_cfg.is_none() {
651            return Err(DestError::NoRemoteConfigFound(pdu_config.source_id()));
652        }
653        self.transaction_params.remote_cfg = Some(*remote_cfg.unwrap());
654        self.transaction_params.transaction_id = Some(TransactionId::new(
655            pdu_config.source_id(),
656            pdu_config.transaction_seq_num,
657        ));
658        self.transaction_params.pdu_conf = *pdu_config;
659        self.transaction_params.pdu_conf.direction = spacepackets::cfdp::Direction::TowardsSender;
660        self.state = State::Busy;
661        Ok(())
662    }
663
664    fn handle_metadata_pdu(&mut self, metadata_pdu: MetadataPduReader) -> Result<(), DestError> {
665        let first_packet = self.step() == TransactionStep::Idle;
666        if first_packet {
667            self.first_packet_handling(metadata_pdu.pdu_header().common_pdu_conf())?;
668        }
669        match self.transmission_mode() {
670            Some(transmission_mode) => {
671                if !first_packet && transmission_mode == TransmissionMode::Unacknowledged {
672                    return Err(DestError::RecvdMetadataButIsBusy);
673                }
674                match self.transaction_params.acked_params.as_mut() {
675                    Some(acked_params) => {
676                        if acked_params.metadata_missing {
677                            if let Some(timer) =
678                                self.transaction_params.deferred_procedure_timer.as_mut()
679                            {
680                                acked_params.nak_activity_counter = 0;
681                                timer.reset();
682                            }
683                            acked_params.metadata_missing = false;
684                        } else {
685                            // Quietly ignore.
686                            return Ok(());
687                        }
688                    }
689                    None => {
690                        self.transaction_params.acked_params = Some(AcknowledgedModeParams {
691                            last_start_offset: 0,
692                            last_end_offset: 0,
693                            metadata_missing: false,
694                            nak_activity_counter: 0,
695                            deferred_procedure_active: false,
696                        });
697                    }
698                }
699            }
700            None => {
701                return Err(DestError::RecvdMetadataButIsBusy);
702            }
703        }
704
705        self.transaction_params.metadata_params = *metadata_pdu.metadata_params();
706        let remote_cfg = self.remote_cfg_table.get(metadata_pdu.source_id().value());
707        if remote_cfg.is_none() {
708            return Err(DestError::NoRemoteConfigFound(metadata_pdu.dest_id()));
709        }
710
711        let src_name = metadata_pdu.src_file_name();
712        let dest_name = metadata_pdu.dest_file_name();
713        if src_name.is_empty() && dest_name.is_empty() {
714            self.transaction_params.metadata_only = true;
715        }
716        if !self.transaction_params.metadata_only && src_name.is_empty() {
717            return Err(DestError::EmptySrcFileField);
718        }
719        if !self.transaction_params.metadata_only && dest_name.is_empty() {
720            return Err(DestError::EmptyDestFileField);
721        }
722        if !self.transaction_params.metadata_only {
723            self.transaction_params.file_names.src_file_name[..src_name.len_value()]
724                .copy_from_slice(src_name.value());
725            self.transaction_params.file_names.src_file_name_len = src_name.len_value();
726            if dest_name.is_empty() {
727                return Err(DestError::EmptyDestFileField);
728            }
729            self.transaction_params.file_names.dest_file_name[..dest_name.len_value()]
730                .copy_from_slice(dest_name.value());
731            self.transaction_params.file_names.dest_file_name_len = dest_name.len_value();
732            self.transaction_params.msgs_to_user_size = 0;
733        }
734        if !metadata_pdu.options().is_empty() {
735            for option_tlv in metadata_pdu.options_iter().unwrap() {
736                if option_tlv.is_standard_tlv()
737                    && option_tlv.tlv_type().unwrap() == TlvType::MsgToUser
738                {
739                    self.transaction_params
740                        .msgs_to_user_buf
741                        .copy_from_slice(option_tlv.raw_data().unwrap());
742                    self.transaction_params.msgs_to_user_size += option_tlv.len_full();
743                }
744            }
745        }
746        self.set_step(TransactionStep::TransactionStart);
747        Ok(())
748    }
749
750    fn reset_nak_activity_parameters_if_active(&mut self) {
751        if let Some(acked_params) = self.transaction_params.acked_params.as_mut() {
752            if acked_params.metadata_missing {
753                if let Some(timer) = self.transaction_params.deferred_procedure_timer.as_mut() {
754                    acked_params.nak_activity_counter = 0;
755                    timer.reset();
756                }
757            }
758        }
759    }
760
761    fn handle_file_data_without_previous_metadata(
762        &mut self,
763        fd_pdu: &FileDataPdu,
764    ) -> Result<u32, DestError> {
765        let mut packets_sent = 0;
766        if fd_pdu.transmission_mode() == TransmissionMode::Unacknowledged {
767            // In acknowledged mode, we need to wait for the metadata PDU first.
768            return Err(DestError::FirstPacketNotMetadata);
769        }
770        self.first_packet_handling(fd_pdu.pdu_header().common_pdu_conf())?;
771        self.transaction_params.progress = fd_pdu.offset() + fd_pdu.file_data().len() as u64;
772        self.transaction_params.acked_params = Some(AcknowledgedModeParams {
773            last_start_offset: 0,
774            last_end_offset: 0,
775            nak_activity_counter: 0,
776            metadata_missing: true,
777            deferred_procedure_active: false,
778        });
779        if fd_pdu.file_data().len() as u64 > 0 {
780            self.lost_segment_tracker
781                .add_lost_segment((0, self.transaction_params.progress))?;
782            let ack_params = self.transaction_params.acked_params.as_mut().unwrap();
783            ack_params.last_start_offset = self.transaction_params.progress;
784            ack_params.last_end_offset = self.transaction_params.progress;
785        }
786        if self
787            .transaction_params
788            .remote_cfg
789            .as_ref()
790            .unwrap()
791            .immediate_nak_mode
792        {
793            let mut num_seg_reqs = 1;
794            if fd_pdu.file_data().len() as u64 > 0 {
795                num_seg_reqs += 1;
796            }
797            let pdu_header = PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
798            let mut nak_pdu = NakPduCreatorWithReservedSeqReqsBuf::new(
799                self.pdu_and_cksum_buffer.get_mut(),
800                pdu_header,
801                num_seg_reqs,
802            )
803            .map_err(PduError::from)?;
804            let buf_mut = nak_pdu.segment_request_buffer_mut();
805            let mut current_offset = 0;
806            let increment = if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
807                8
808            } else {
809                4
810            };
811            buf_mut[0..current_offset].fill(0);
812            current_offset += increment;
813            buf_mut[current_offset..current_offset + increment].fill(0);
814            current_offset += increment;
815            if fd_pdu.file_data().len() as u64 > 0 {
816                buf_mut[0..current_offset].fill(0);
817                current_offset += increment;
818                if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
819                    buf_mut[current_offset..current_offset + increment]
820                        .copy_from_slice(&self.transaction_params.progress.to_be_bytes());
821                } else {
822                    buf_mut[current_offset..current_offset + increment]
823                        .copy_from_slice(&(self.transaction_params.progress as u32).to_be_bytes());
824                }
825            }
826            let written_size = nak_pdu
827                .finish(0, self.transaction_params.progress)
828                .map_err(PduError::from)?;
829            self.pdu_sender.send_file_directive_pdu(
830                FileDirectiveType::NakPdu,
831                &self.pdu_and_cksum_buffer.borrow()[0..written_size],
832            )?;
833            packets_sent += 1;
834        }
835        Ok(packets_sent)
836    }
837
838    fn handle_file_data(
839        &mut self,
840        user: &mut impl CfdpUser,
841        fd_pdu: FileDataPdu,
842    ) -> Result<u32, DestError> {
843        let mut sent_packets = 0;
844        let mut handle_indication = |id: TransactionId, indication_config: &IndicationConfig| {
845            if indication_config.file_segment_recv {
846                user.file_segment_recvd_indication(&FileSegmentRecvdParams {
847                    id,
848                    offset: fd_pdu.offset(),
849                    length: fd_pdu.file_data().len(),
850                    segment_metadata: fd_pdu.segment_metadata(),
851                });
852            }
853        };
854        let step = self.step.get();
855        if self.state == State::Idle {
856            if step == TransactionStep::Idle {
857                sent_packets += self.handle_file_data_without_previous_metadata(&fd_pdu)?;
858                self.set_step(TransactionStep::WaitingForMetadata);
859                handle_indication(
860                    self.transaction_id().unwrap(),
861                    &self.local_cfg.indication_cfg,
862                );
863                return Ok(sent_packets);
864            }
865            if step != TransactionStep::ReceivingFileDataPdus
866                && step != TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling
867            {
868                return Err(DestError::WrongStepForPdu {
869                    pdu_type: PduType::FileData,
870                    file_directive_type: None,
871                    step,
872                });
873            }
874        }
875        handle_indication(
876            self.transaction_id().unwrap(),
877            &self.local_cfg.indication_cfg,
878        );
879        if let Err(e) = self.vfs.write_data(
880            // Safety: It was already verified that the path is valid during the transaction start.
881            unsafe {
882                from_utf8_unchecked(
883                    //from_utf8(
884                    &self.transaction_params.file_names.dest_path_buf
885                        [0..self.transaction_params.file_names.dest_file_path_len],
886                )
887            },
888            fd_pdu.offset(),
889            fd_pdu.file_data(),
890        ) {
891            if self.declare_fault(ConditionCode::FilestoreRejection)
892                == FaultHandlerCode::AbandonTransaction
893            {
894                self.abandon_transaction();
895            }
896            return Err(e.into());
897        }
898        self.transaction_params.progress = core::cmp::max(
899            self.transaction_params.progress,
900            fd_pdu.offset() + fd_pdu.file_data().len() as u64,
901        );
902        if self.transmission_mode().unwrap() == TransmissionMode::Acknowledged {
903            sent_packets += self.lost_segment_handling(&fd_pdu)?;
904        }
905        Ok(sent_packets)
906    }
907
908    fn lost_segment_handling(&mut self, fd_pdu: &FileDataPdu) -> Result<u32, DestError> {
909        // Lost segment detection: 4.6.4.3.1 a) and b) are covered by this code. c) is covered
910        //by dedicated code which is run when the EOF PDU is handled.
911        if self.transaction_params.acked_params.is_none() {
912            return Ok(0);
913        }
914
915        let mut sent_packets = 0;
916        let acked_params = self.transaction_params.acked_params.as_mut().unwrap();
917        if fd_pdu.offset() > acked_params.last_end_offset {
918            let lost_segment = (acked_params.last_end_offset, fd_pdu.offset());
919            self.lost_segment_tracker.add_lost_segment(lost_segment)?;
920            if self
921                .transaction_params
922                .remote_cfg
923                .as_ref()
924                .unwrap()
925                .immediate_nak_mode
926            {
927                let pdu_header =
928                    PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
929                if self.transaction_params.pdu_conf.file_flag == LargeFileFlag::Normal
930                    && lost_segment.0 > u32::MAX as u64
931                    || lost_segment.1 > u32::MAX as u64
932                {
933                    return Err(DestError::UnexpectedLargeFileSize);
934                }
935                let seg32 = [(lost_segment.0 as u32, lost_segment.1 as u32)];
936                let seg64 = [lost_segment];
937                let nak_pdu = if self.transaction_params.pdu_conf.file_flag == LargeFileFlag::Normal
938                {
939                    NakPduCreator::new_normal_file_size(
940                        pdu_header,
941                        0,
942                        self.transaction_params.progress as u32,
943                        &seg32,
944                    )
945                    .unwrap()
946                } else {
947                    NakPduCreator::new_large_file_size(
948                        pdu_header,
949                        0,
950                        self.transaction_params.progress,
951                        &seg64,
952                    )
953                    .unwrap()
954                };
955                let written_len = nak_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
956                self.pdu_sender.send_file_directive_pdu(
957                    FileDirectiveType::NakPdu,
958                    &self.pdu_and_cksum_buffer.borrow()[0..written_len],
959                )?;
960                sent_packets += 1;
961            }
962        }
963        if fd_pdu.offset() >= acked_params.last_end_offset {
964            acked_params.last_start_offset = fd_pdu.offset();
965            acked_params.last_end_offset = fd_pdu.offset() + fd_pdu.file_data().len() as u64;
966        }
967        if fd_pdu.offset() + fd_pdu.file_data().len() as u64 <= acked_params.last_start_offset {
968            // Might be a re-requested FD PDU.
969            let removed = self.lost_segment_tracker.remove_lost_segment((
970                fd_pdu.offset(),
971                fd_pdu.offset() + fd_pdu.file_data().len() as u64,
972            ))?;
973            // Reception of missing segments resets the NAK activity parameters.
974            // See CFDP 4.6.4.7.
975            if removed && acked_params.deferred_procedure_active {
976                self.reset_nak_activity_parameters_if_active();
977            }
978        }
979        Ok(sent_packets)
980    }
981
982    fn handle_eof_pdu(
983        &mut self,
984        cfdp_user: &mut impl CfdpUser,
985        eof_pdu: EofPdu,
986    ) -> Result<u32, DestError> {
987        let sent_packets = 0;
988        let first_packet = self.step() == TransactionStep::Idle;
989        if first_packet {
990            self.first_packet_handling(eof_pdu.pdu_header().common_pdu_conf())?;
991        }
992        if self.local_cfg.indication_cfg.eof_recv {
993            // Unwrap is okay here, application logic ensures that transaction ID is valid here.
994            cfdp_user
995                .eof_recvd_indication(self.transaction_params.transaction_id.as_ref().unwrap());
996        }
997        if first_packet {
998            if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged {
999                return Err(DestError::WrongStepForPdu {
1000                    pdu_type: PduType::FileDirective,
1001                    file_directive_type: Some(FileDirectiveType::EofPdu),
1002                    step: self.step(),
1003                });
1004            }
1005            return self.handle_eof_without_previous_metadata_in_acked_mode(&eof_pdu);
1006        }
1007        if eof_pdu.condition_code() == ConditionCode::NoError {
1008            if !self.handle_eof_no_error(&eof_pdu)? {
1009                return Ok(sent_packets);
1010            }
1011        } else {
1012            self.handle_eof_cancel(&eof_pdu);
1013        };
1014        let mut sent_packets = 0;
1015        match self.transaction_params.transmission_mode() {
1016            TransmissionMode::Acknowledged => {
1017                self.acknowledge_eof_pdu(&eof_pdu)?;
1018                sent_packets += 1;
1019                match self.transaction_params.acked_params.as_ref() {
1020                    Some(acked_params) => {
1021                        if acked_params.metadata_missing || !self.lost_segment_tracker.is_empty() {
1022                            self.start_deferred_lost_segment_handling();
1023                        } else {
1024                            self.set_step(TransactionStep::TransferCompletion);
1025                        }
1026                    }
1027                    // This can happen when the EOF is the first packet to arrive.
1028                    None => {
1029                        self.start_deferred_lost_segment_handling();
1030                    }
1031                }
1032            }
1033            TransmissionMode::Unacknowledged => {
1034                self.set_step(TransactionStep::TransferCompletion);
1035            }
1036        }
1037        Ok(sent_packets)
1038    }
1039
1040    fn handle_eof_without_previous_metadata_in_acked_mode(
1041        &mut self,
1042        eof_pdu: &EofPdu,
1043    ) -> Result<u32, DestError> {
1044        let mut sent_packets = 0;
1045        self.transaction_params.file_size = eof_pdu.file_size();
1046        self.transaction_params.checksum = eof_pdu.file_checksum();
1047        self.transaction_params.acked_params = Some(AcknowledgedModeParams {
1048            last_start_offset: 0,
1049            last_end_offset: 0,
1050            nak_activity_counter: 0,
1051            metadata_missing: true,
1052            deferred_procedure_active: false,
1053        });
1054        if self.transaction_params.file_size > 0 {
1055            self.lost_segment_tracker.reset();
1056            // Add the whole file to the lost segments map for now.
1057            self.lost_segment_tracker
1058                .add_lost_segment((0, eof_pdu.file_size()))?;
1059        }
1060        if eof_pdu.condition_code() != ConditionCode::NoError {
1061            self.handle_eof_cancel(eof_pdu);
1062        }
1063        self.acknowledge_eof_pdu(eof_pdu)?;
1064        sent_packets += 1;
1065        if self.transaction_params.completion_disposition.get() == CompletionDisposition::Cancelled
1066        {
1067            self.set_step(TransactionStep::TransferCompletion);
1068            return Ok(sent_packets);
1069        }
1070        self.set_step(TransactionStep::WaitingForMetadata);
1071        self.start_deferred_lost_segment_handling();
1072        Ok(sent_packets)
1073    }
1074
1075    fn handle_eof_cancel(&mut self, eof_pdu: &EofPdu) {
1076        // This is an EOF (Cancel), perform Cancel Response Procedures according to chapter
1077        // 4.6.6 of the standard. Set remote ID as fault location.
1078        self.trigger_notice_of_completion_cancelled(
1079            eof_pdu.condition_code(),
1080            EntityIdTlv::new(self.transaction_params.remote_cfg.unwrap().entity_id),
1081        );
1082        // Store this as progress for the checksum calculation as well.
1083        self.transaction_params.progress = eof_pdu.file_size();
1084        if let Some(ack_params) = &self.transaction_params.acked_params {
1085            if ack_params.metadata_missing {
1086                return;
1087            }
1088        }
1089        if self.transaction_params.progress == 0 {
1090            // Empty file, no file data PDU.
1091            self.transaction_params
1092                .finished_params
1093                .delivery_code
1094                .set(DeliveryCode::Complete);
1095            return;
1096        }
1097        if self.checksum_verify(self.transaction_params.progress, eof_pdu.file_checksum()) {
1098            self.transaction_params
1099                .finished_params
1100                .delivery_code
1101                .set(DeliveryCode::Complete);
1102            return;
1103        }
1104        self.transaction_params
1105            .finished_params
1106            .delivery_code
1107            .set(DeliveryCode::Incomplete);
1108    }
1109
1110    fn acknowledge_eof_pdu(&mut self, eof_pdu: &EofPdu) -> Result<(), DestError> {
1111        let pdu_header = PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
1112        let ack_pdu = AckPdu::new_for_eof_pdu(
1113            pdu_header,
1114            eof_pdu.condition_code(),
1115            TransactionStatus::Active,
1116        );
1117        let written_len = ack_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
1118        self.pdu_sender.send_file_directive_pdu(
1119            FileDirectiveType::AckPdu,
1120            &self.pdu_and_cksum_buffer.borrow()[0..written_len],
1121        )?;
1122        Ok(())
1123    }
1124
1125    fn start_deferred_lost_segment_handling(&mut self) {
1126        match &mut self.transaction_params.acked_params {
1127            Some(params) => {
1128                params.last_start_offset = self.transaction_params.file_size;
1129                params.last_end_offset = self.transaction_params.file_size;
1130                params.deferred_procedure_active = true;
1131                params.nak_activity_counter = 0;
1132            }
1133            None => {
1134                self.transaction_params.acked_params = Some(AcknowledgedModeParams {
1135                    last_start_offset: self.transaction_params.file_size,
1136                    last_end_offset: self.transaction_params.file_size,
1137                    metadata_missing: false,
1138                    nak_activity_counter: 0,
1139                    deferred_procedure_active: true,
1140                })
1141            }
1142        }
1143    }
1144
1145    fn check_for_deferred_lost_segment_completion(&mut self, metadata_missing: bool) -> bool {
1146        if self.lost_segment_tracker.is_empty() && !metadata_missing {
1147            // We are done and have received everything.
1148            match self.checksum_verify(
1149                self.transaction_params.progress,
1150                self.transaction_params.checksum,
1151            ) {
1152                true => {
1153                    self.transaction_params
1154                        .finished_params
1155                        .condition_code
1156                        .set(ConditionCode::NoError);
1157                    self.transaction_params
1158                        .finished_params
1159                        .delivery_code
1160                        .set(DeliveryCode::Complete);
1161                }
1162                false => {
1163                    self.transaction_params
1164                        .finished_params
1165                        .condition_code
1166                        .set(ConditionCode::FileChecksumFailure);
1167                    self.transaction_params
1168                        .finished_params
1169                        .delivery_code
1170                        .set(DeliveryCode::Incomplete);
1171                }
1172            }
1173            self.transaction_params
1174                .acked_params
1175                .as_mut()
1176                .unwrap()
1177                .deferred_procedure_active = false;
1178            self.set_step(TransactionStep::TransferCompletion);
1179            return true;
1180        }
1181        false
1182    }
1183
1184    fn deferred_lost_segment_handling(&mut self) -> Result<u32, DestError> {
1185        assert!(
1186            self.transaction_params.acked_params.is_some(),
1187            "acknowledged parameters unexpectedly None"
1188        );
1189        let acked_params = self.transaction_params.acked_params.unwrap();
1190        if self.check_for_deferred_lost_segment_completion(acked_params.metadata_missing) {
1191            return Ok(0);
1192        }
1193        let mut sent_packets = 0;
1194        let first_nak_issuance = self.transaction_params.deferred_procedure_timer.is_none();
1195        if first_nak_issuance {
1196            self.transaction_params.deferred_procedure_timer = Some(
1197                self.check_timer_creator
1198                    .create_countdown(TimerContext::NakActivity {
1199                        expiry_time: self
1200                            .transaction_params
1201                            .remote_cfg
1202                            .as_ref()
1203                            .unwrap()
1204                            .nak_timer_interval,
1205                    }),
1206            );
1207        } else if !self
1208            .transaction_params
1209            .deferred_procedure_timer
1210            .as_ref()
1211            .unwrap()
1212            .has_expired()
1213        {
1214            return Ok(sent_packets);
1215        }
1216        if !first_nak_issuance
1217            && acked_params.nak_activity_counter + 1
1218                == self
1219                    .transaction_params
1220                    .remote_cfg
1221                    .as_ref()
1222                    .unwrap()
1223                    .nak_timer_expiration_limit
1224        {
1225            self.transaction_params
1226                .finished_params
1227                .delivery_code
1228                .set(DeliveryCode::Incomplete);
1229            if self.declare_fault(ConditionCode::NakLimitReached)
1230                == FaultHandlerCode::AbandonTransaction
1231            {
1232                self.abandon_transaction();
1233            }
1234            return Ok(sent_packets);
1235        }
1236        if !first_nak_issuance {
1237            self.transaction_params
1238                .acked_params
1239                .as_mut()
1240                .unwrap()
1241                .nak_activity_counter += 1;
1242            self.transaction_params
1243                .deferred_procedure_timer
1244                .as_mut()
1245                .unwrap()
1246                .reset();
1247        }
1248        let pdu_header = PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
1249        let max_segment_reqs = NakPduCreatorWithReservedSeqReqsBuf::calculate_max_segment_requests(
1250            self.transaction_params
1251                .remote_cfg
1252                .as_ref()
1253                .unwrap()
1254                .max_packet_len,
1255            &pdu_header,
1256        )
1257        .map_err(|_| DestError::InvalidRemoteConfig(self.transaction_params.remote_cfg.unwrap()))?;
1258        let mut segments_to_send = self.lost_segment_tracker.number_of_segments();
1259        if acked_params.metadata_missing {
1260            segments_to_send += 1;
1261        }
1262        if segments_to_send <= max_segment_reqs {
1263            // Should never fail because we calculcated the allowed maximum number of segment
1264            // requests for the buffer.
1265            let mut nak_pdu_creator = NakPduCreatorWithReservedSeqReqsBuf::new(
1266                self.pdu_and_cksum_buffer.get_mut(),
1267                pdu_header,
1268                segments_to_send,
1269            )
1270            .unwrap();
1271            // All error conditions were checked so this should never fail.
1272            //
1273            // 1. Number of segments was calculated.
1274            // 2. Buffer size was calculated based on PDU header and maximum packet size.
1275            // 3. Large file flag is based on PDU header, so there should never be a missmatch.
1276            self.lost_segment_tracker
1277                .write_to_nak_segment_list(&mut nak_pdu_creator, acked_params.metadata_missing)
1278                .expect("unexpected lost segment write error");
1279            let written_len = nak_pdu_creator
1280                .finish(0, self.transaction_params.file_size)
1281                .map_err(PduError::from)?;
1282            self.pdu_sender.send_file_directive_pdu(
1283                FileDirectiveType::NakPdu,
1284                &self.pdu_and_cksum_buffer.borrow()[0..written_len],
1285            )?;
1286            sent_packets += 1;
1287        } else {
1288            sent_packets += self.write_multi_packet_nak_sequence(
1289                segments_to_send,
1290                max_segment_reqs,
1291                acked_params.metadata_missing,
1292            )?;
1293        }
1294
1295        Ok(sent_packets)
1296    }
1297
1298    #[cold]
1299    fn write_multi_packet_nak_sequence(
1300        &mut self,
1301        mut segments_to_send: usize,
1302        max_segments: usize,
1303        first_segment_metadata: bool,
1304    ) -> Result<u32, DestError> {
1305        let mut sent_packets = 0;
1306        let pdu_header = PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
1307        let mut nak_pdu_creator = NakPduCreatorWithReservedSeqReqsBuf::new(
1308            self.pdu_and_cksum_buffer.get_mut(),
1309            pdu_header,
1310            max_segments,
1311        )
1312        .unwrap();
1313
1314        let mut segment_index = 0;
1315        let mut buf_index = 0;
1316        let mut current_start_of_scope = 0;
1317        let mut current_end_of_scope = 0;
1318        let mut seg_buf = nak_pdu_creator.segment_request_buffer_mut();
1319
1320        // First segment re-requests metadata PDU.
1321        if first_segment_metadata {
1322            if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
1323                seg_buf[0..16].fill(0);
1324                buf_index = 16;
1325            } else {
1326                seg_buf[0..8].fill(0);
1327                buf_index = 8;
1328            }
1329            segment_index = 1; // account for the header gap entry
1330        }
1331
1332        for (idx, (start, end)) in self.lost_segment_tracker.iter().enumerate() {
1333            // flush full PDU *before* writing the new entry
1334            if segment_index == max_segments {
1335                let written_len = nak_pdu_creator
1336                    .finish(current_start_of_scope, current_end_of_scope)
1337                    .map_err(PduError::from)?;
1338                self.pdu_sender.send_file_directive_pdu(
1339                    FileDirectiveType::NakPdu,
1340                    &self.pdu_and_cksum_buffer.borrow()[..written_len],
1341                )?;
1342                sent_packets += 1;
1343                segments_to_send = segments_to_send.saturating_sub(max_segments);
1344
1345                // new PDU with the same capacity
1346                nak_pdu_creator = NakPduCreatorWithReservedSeqReqsBuf::new(
1347                    self.pdu_and_cksum_buffer.get_mut(),
1348                    pdu_header,
1349                    core::cmp::min(segments_to_send, max_segments),
1350                )
1351                .unwrap();
1352                seg_buf = nak_pdu_creator.segment_request_buffer_mut();
1353                segment_index = 0;
1354                current_start_of_scope = current_end_of_scope;
1355                buf_index = 0;
1356            }
1357
1358            // write entry
1359            if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
1360                seg_buf[buf_index..buf_index + 8].copy_from_slice(&start.to_be_bytes());
1361                buf_index += 8;
1362                seg_buf[buf_index..buf_index + 8].copy_from_slice(&end.to_be_bytes());
1363                buf_index += 8;
1364            } else {
1365                seg_buf[buf_index..buf_index + 4].copy_from_slice(&(start as u32).to_be_bytes());
1366                buf_index += 4;
1367                seg_buf[buf_index..buf_index + 4].copy_from_slice(&(end as u32).to_be_bytes());
1368                buf_index += 4;
1369            }
1370            if idx == max_segments {
1371                // Clamp the end of scope to the progress for the last NAK in the sequence.
1372                current_end_of_scope = self.transaction_params.progress;
1373            } else {
1374                current_end_of_scope = end;
1375            }
1376            segment_index += 1;
1377        }
1378
1379        // send trailing PDU if anything was written
1380        if segment_index > 0 {
1381            let written_len = nak_pdu_creator
1382                .finish(current_start_of_scope, current_end_of_scope)
1383                .map_err(PduError::from)?;
1384            self.pdu_sender.send_file_directive_pdu(
1385                FileDirectiveType::NakPdu,
1386                &self.pdu_and_cksum_buffer.borrow()[..written_len],
1387            )?;
1388            sent_packets += 1;
1389        }
1390        Ok(sent_packets)
1391    }
1392
1393    fn trigger_notice_of_completion_cancelled(
1394        &self,
1395        cond_code: ConditionCode,
1396        fault_location: EntityIdTlv,
1397    ) {
1398        self.transaction_params
1399            .completion_disposition
1400            .set(CompletionDisposition::Cancelled);
1401        self.transaction_params
1402            .finished_params
1403            .condition_code
1404            .set(cond_code);
1405        self.transaction_params
1406            .finished_params
1407            .fault_location_finished
1408            .set(Some(fault_location));
1409        // For anything larger than 0, we'd have to do a checksum check to verify whether
1410        // the delivery is really complete, and we need the EOF checksum for that..
1411        if self.transaction_params.progress == 0 {
1412            self.transaction_params
1413                .finished_params
1414                .delivery_code
1415                .set(DeliveryCode::Complete);
1416        }
1417    }
1418
1419    /// Returns whether the transfer can be completed regularly.
1420    fn handle_eof_no_error(&mut self, eof_pdu: &EofPdu) -> Result<bool, DestError> {
1421        // CFDP 4.6.1.2.9: Declare file size error if progress exceeds file size
1422        if self.transaction_params.progress > eof_pdu.file_size() {
1423            match self.declare_fault(ConditionCode::FileSizeError) {
1424                FaultHandlerCode::IgnoreError => (),
1425                FaultHandlerCode::AbandonTransaction => {
1426                    self.abandon_transaction();
1427                    return Ok(false);
1428                }
1429                FaultHandlerCode::NoticeOfCancellation | FaultHandlerCode::NoticeOfSuspension => {
1430                    return Ok(false);
1431                }
1432            }
1433        } else if (self.transaction_params.progress < eof_pdu.file_size())
1434            && self.transaction_params.transmission_mode() == TransmissionMode::Acknowledged
1435        {
1436            // CFDP 4.6.4.3.1: The end offset of the last received file segment and the file
1437            // size as stated in the EOF PDU is not the same, so we need to add that segment to
1438            // the lost segments for the deferred lost segment detection procedure.
1439            self.lost_segment_tracker
1440                .add_lost_segment((self.transaction_params.progress, eof_pdu.file_size()))?;
1441        }
1442
1443        self.transaction_params.file_size = eof_pdu.file_size();
1444        self.transaction_params.checksum = eof_pdu.file_checksum();
1445        if self.transaction_params.transmission_mode() == TransmissionMode::Unacknowledged
1446            && !self.checksum_verify(
1447                self.transaction_params.progress,
1448                self.transaction_params.checksum,
1449            )
1450        {
1451            self.start_check_limit_handling();
1452            return Ok(false);
1453        }
1454        self.transaction_params
1455            .finished_params
1456            .delivery_code
1457            .set(DeliveryCode::Complete);
1458        self.transaction_params
1459            .finished_params
1460            .condition_code
1461            .set(ConditionCode::NoError);
1462        Ok(true)
1463    }
1464
1465    fn checksum_verify(&mut self, verify_len: u64, checksum: u32) -> bool {
1466        if self.transaction_params.metadata_params().checksum_type == ChecksumType::NullChecksum
1467            || self.transaction_params.metadata_only
1468        {
1469            return true;
1470        }
1471        match self.vfs.checksum_verify(
1472            checksum,
1473            // Safety: It was already verified that the path is valid during the transaction start.
1474            unsafe {
1475                from_utf8_unchecked(
1476                    &self.transaction_params.file_names.dest_path_buf
1477                        [0..self.transaction_params.file_names.dest_file_path_len],
1478                )
1479            },
1480            self.transaction_params.metadata_params().checksum_type,
1481            verify_len,
1482            self.pdu_and_cksum_buffer.get_mut(),
1483        ) {
1484            Ok(false) => {
1485                if self.declare_fault(ConditionCode::FileChecksumFailure)
1486                    == FaultHandlerCode::AbandonTransaction
1487                {
1488                    self.abandon_transaction();
1489                }
1490                false
1491            }
1492            Ok(true) => true,
1493            Err(e) => match e {
1494                FilestoreError::ChecksumTypeNotImplemented(_) => {
1495                    if self.declare_fault(ConditionCode::UnsupportedChecksumType)
1496                        == FaultHandlerCode::AbandonTransaction
1497                    {
1498                        self.abandon_transaction();
1499                    }
1500                    // For this case, the applicable algorithm shall be the the null checksum,
1501                    // which is always succesful.
1502                    true
1503                }
1504                _ => {
1505                    if self.declare_fault(ConditionCode::FilestoreRejection)
1506                        == FaultHandlerCode::AbandonTransaction
1507                    {
1508                        self.abandon_transaction();
1509                    }
1510                    // Treat this equivalent to a failed checksum procedure.
1511                    false
1512                }
1513            },
1514        }
1515    }
1516
1517    fn start_check_limit_handling(&mut self) {
1518        self.set_step(TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling);
1519        self.transaction_params.current_check_timer = Some(
1520            self.check_timer_creator
1521                .create_countdown(TimerContext::CheckLimit {
1522                    local_id: self.local_cfg.id,
1523                    remote_id: self.transaction_params.remote_cfg.unwrap().entity_id,
1524                    entity_type: EntityType::Receiving,
1525                }),
1526        );
1527        self.transaction_params.current_check_count = 0;
1528    }
1529
1530    fn check_limit_handling(&mut self) -> Result<(), DestError> {
1531        if self.transaction_params.current_check_timer.is_none() {
1532            return Ok(());
1533        }
1534        let check_timer = self
1535            .transaction_params
1536            .current_check_timer
1537            .as_ref()
1538            .unwrap();
1539        if check_timer.has_expired() {
1540            if self.checksum_verify(
1541                self.transaction_params.progress,
1542                self.transaction_params.checksum,
1543            ) {
1544                self.transaction_params
1545                    .finished_params
1546                    .condition_code
1547                    .set(ConditionCode::NoError);
1548                self.transaction_params
1549                    .finished_params
1550                    .delivery_code
1551                    .set(DeliveryCode::Complete);
1552                self.set_step(TransactionStep::TransferCompletion);
1553                return Ok(());
1554            }
1555            if self.transaction_params.current_check_count + 1
1556                >= self.transaction_params.remote_cfg.unwrap().check_limit
1557            {
1558                if self.declare_fault(ConditionCode::CheckLimitReached)
1559                    == FaultHandlerCode::AbandonTransaction
1560                {
1561                    self.abandon_transaction();
1562                }
1563            } else {
1564                self.transaction_params.current_check_count += 1;
1565                self.transaction_params
1566                    .current_check_timer
1567                    .as_mut()
1568                    .unwrap()
1569                    .reset();
1570            }
1571        }
1572        Ok(())
1573    }
1574
1575    fn handle_prompt_pdu(&mut self, _raw_packet: &[u8]) -> Result<(), DestError> {
1576        Err(DestError::NotImplemented)
1577    }
1578
1579    fn fsm_busy(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<u32, DestError> {
1580        let mut sent_packets = 0;
1581        if self.step() == TransactionStep::TransactionStart {
1582            let result = self.transaction_start(cfdp_user);
1583            if let Err(e) = result {
1584                // If we can not even start the transaction properly, reset the handler.
1585                // We could later let the user do this optionally, but for now this is the safer
1586                // approach to prevent inconsistent states of the handler.
1587                self.reset();
1588                return Err(e);
1589            }
1590        }
1591        if self.step() == TransactionStep::WaitingForMetadata
1592            || self.step() == TransactionStep::ReceivingFileDataPdus
1593        {
1594            if let Some(ack_params) = &mut self.transaction_params.acked_params {
1595                if ack_params.deferred_procedure_active {
1596                    sent_packets += self.deferred_lost_segment_handling()?;
1597                }
1598            }
1599        }
1600        if self.step() == TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling {
1601            self.check_limit_handling()?;
1602        }
1603        if self.step() == TransactionStep::TransferCompletion {
1604            sent_packets += self.transfer_completion(cfdp_user)?;
1605        }
1606        if self.step() == TransactionStep::WaitingForFinishedAck {
1607            sent_packets += self.handle_positive_ack_procedures()?;
1608            // Little hack because of the state machine handling order to ensure the transfer
1609            // is completed in one go.
1610            if self.step() == TransactionStep::TransferCompletion {
1611                sent_packets += self.transfer_completion(cfdp_user)?;
1612            }
1613        }
1614        Ok(sent_packets)
1615    }
1616
1617    fn transaction_start(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), DestError> {
1618        let id = self.transaction_id().unwrap();
1619        let dest_name = from_utf8(
1620            &self.transaction_params.file_names.dest_file_name
1621                [..self.transaction_params.file_names.dest_file_name_len],
1622        )?;
1623        self.transaction_params.file_names.dest_path_buf[0..dest_name.len()]
1624            .copy_from_slice(dest_name.as_bytes());
1625        self.transaction_params.file_names.dest_file_path_len = dest_name.len();
1626        let source_id = self.transaction_params.pdu_conf.source_id();
1627        let src_name = from_utf8(
1628            &self.transaction_params.file_names.src_file_name
1629                [0..self.transaction_params.file_names.src_file_name_len],
1630        )?;
1631        let mut msgs_to_user = SmallVec::<[MsgToUserTlv<'_>; 16]>::new();
1632        let mut num_msgs_to_user = 0;
1633        if self.transaction_params.msgs_to_user_size > 0 {
1634            let mut index = 0;
1635            while index < self.transaction_params.msgs_to_user_size {
1636                // This should never panic as the validity of the options was checked beforehand.
1637                let msgs_to_user_tlv =
1638                    MsgToUserTlv::from_bytes(&self.transaction_params.msgs_to_user_buf[index..])
1639                        .expect("message to user creation failed unexpectedly");
1640                msgs_to_user.push(msgs_to_user_tlv);
1641                index += msgs_to_user_tlv.len_full();
1642                num_msgs_to_user += 1;
1643            }
1644        }
1645        let metadata_recvd_params = MetadataReceivedParams {
1646            id,
1647            source_id,
1648            file_size: self.transaction_params.file_size(),
1649            src_file_name: src_name,
1650            dest_file_name: dest_name,
1651            msgs_to_user: &msgs_to_user[..num_msgs_to_user],
1652        };
1653        cfdp_user.metadata_recvd_indication(&metadata_recvd_params);
1654
1655        if self.vfs.exists(dest_name)? && self.vfs.is_dir(dest_name)? {
1656            // Create new destination path by concatenating the last part of the source source
1657            // name and the destination folder. For example, for a source file of /tmp/hello.txt
1658            // and a destination name of /home/test, the resulting file name should be
1659            // /home/test/hello.txt
1660            // Safety: It was already verified that the path is valid during the transaction start.
1661            let source_file_name = self.vfs.file_name(src_name)?;
1662            if source_file_name.is_none() {
1663                return Err(DestError::PathConcat);
1664            }
1665            let source_name = source_file_name.unwrap();
1666            self.transaction_params.file_names.dest_path_buf[dest_name.len()] = b'/';
1667            self.transaction_params.file_names.dest_path_buf
1668                [dest_name.len() + 1..dest_name.len() + 1 + source_name.len()]
1669                .copy_from_slice(source_name.as_bytes());
1670            self.transaction_params.file_names.dest_file_path_len += 1 + source_name.len();
1671        }
1672        let dest_path_str = from_utf8(
1673            &self.transaction_params.file_names.dest_path_buf
1674                [0..self.transaction_params.file_names.dest_file_path_len],
1675        )?;
1676        if self.vfs.exists(dest_path_str)? {
1677            self.vfs.truncate_file(dest_path_str)?;
1678        } else {
1679            self.vfs.create_file(dest_path_str)?;
1680        }
1681        self.transaction_params.finished_params.file_status = FileStatus::Retained;
1682        drop(msgs_to_user);
1683        self.set_step(TransactionStep::ReceivingFileDataPdus);
1684        Ok(())
1685    }
1686
1687    fn transfer_completion(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<u32, DestError> {
1688        let mut sent_packets = 0;
1689        self.notice_of_completion(cfdp_user)?;
1690        if self.transaction_params.transmission_mode() == TransmissionMode::Acknowledged
1691            || self.transaction_params.metadata_params().closure_requested
1692        {
1693            sent_packets += self.send_finished_pdu()?;
1694            self.start_positive_ack_procedure();
1695            if self.transaction_params.transmission_mode() == TransmissionMode::Acknowledged {
1696                self.set_step(TransactionStep::WaitingForFinishedAck);
1697                return Ok(sent_packets);
1698            }
1699        }
1700        self.reset();
1701        Ok(sent_packets)
1702    }
1703
1704    fn start_positive_ack_procedure(&mut self) {
1705        match &mut self.transaction_params.positive_ack_params {
1706            Some(current) => {
1707                current.ack_counter = 0;
1708            }
1709            None => {
1710                self.transaction_params.positive_ack_params = Some(PositiveAckParams {
1711                    ack_counter: 0,
1712                    positive_ack_of_cancellation: false,
1713                })
1714            }
1715        }
1716        self.transaction_params.ack_timer = Some(
1717            self.check_timer_creator
1718                .create_countdown(TimerContext::PositiveAck {
1719                    expiry_time: self
1720                        .transaction_params
1721                        .remote_cfg
1722                        .as_ref()
1723                        .unwrap()
1724                        .positive_ack_timer_interval,
1725                }),
1726        );
1727    }
1728
1729    fn handle_positive_ack_procedures(&mut self) -> Result<u32, DestError> {
1730        // Do we have positive-ack params?
1731        if self.transaction_params.positive_ack_params.is_none() {
1732            return Ok(0);
1733        }
1734        let params = self.transaction_params.positive_ack_params.unwrap();
1735
1736        // Has the timer expired?
1737        if !self
1738            .transaction_params
1739            .ack_timer
1740            .as_mut()
1741            .unwrap()
1742            .has_expired()
1743        {
1744            return Ok(0);
1745        }
1746
1747        let expiration_limit = self
1748            .transaction_params
1749            .remote_cfg
1750            .as_ref()
1751            .unwrap()
1752            .positive_ack_timer_expiration_limit;
1753        // If bumping the counter would exceed the limit, fault & maybe abandon
1754        if params.ack_counter + 1 >= expiration_limit {
1755            if self.declare_fault(ConditionCode::PositiveAckLimitReached)
1756                == FaultHandlerCode::AbandonTransaction
1757            {
1758                self.abandon_transaction();
1759                return Ok(0);
1760            }
1761            self.transaction_params
1762                .positive_ack_params
1763                .as_mut()
1764                .unwrap()
1765                .positive_ack_of_cancellation = true;
1766            return Ok(0);
1767        }
1768
1769        let params_mut = self
1770            .transaction_params
1771            .positive_ack_params
1772            .as_mut()
1773            .unwrap();
1774        params_mut.ack_counter += 1;
1775        self.transaction_params.ack_timer.as_mut().unwrap().reset();
1776        self.send_finished_pdu()
1777    }
1778
1779    fn notice_of_completion(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), DestError> {
1780        if self.transaction_params.completion_disposition.get() == CompletionDisposition::Completed
1781        {
1782            // TODO: Execute any filestore requests
1783        } else if self
1784            .transaction_params
1785            .remote_cfg
1786            .as_ref()
1787            .unwrap()
1788            .disposition_on_cancellation
1789            && self.transaction_params.finished_params.delivery_code.get()
1790                == DeliveryCode::Incomplete
1791        {
1792            // Safety: We already verified that the path is valid during the transaction start.
1793            let dest_path = unsafe {
1794                from_utf8_unchecked(
1795                    &self.transaction_params.file_names.dest_path_buf
1796                        [0..self.transaction_params.file_names.dest_file_path_len],
1797                )
1798            };
1799            if self.vfs.exists(dest_path)? && self.vfs.is_file(dest_path)? {
1800                self.vfs.remove_file(dest_path)?;
1801            }
1802            self.transaction_params.finished_params.file_status = FileStatus::DiscardDeliberately;
1803        }
1804        let tstate = &self.transaction_params;
1805        let transaction_finished_params = TransactionFinishedParams {
1806            id: tstate.transaction_id.unwrap(),
1807            condition_code: tstate.finished_params.condition_code.get(),
1808            delivery_code: tstate.finished_params.delivery_code.get(),
1809            file_status: tstate.finished_params.file_status,
1810        };
1811        cfdp_user.transaction_finished_indication(&transaction_finished_params);
1812        Ok(())
1813    }
1814
1815    // When the fault handler code [FaultHandlerCode::AbandonTransaction] is returned, the caller
1816    // must call [Self::abandon_transaction] as soon as it is possible.
1817    fn declare_fault(&self, condition_code: ConditionCode) -> FaultHandlerCode {
1818        // Cache those, because they might be reset when abandoning the transaction.
1819        let transaction_id = self.transaction_id().unwrap();
1820        let progress = self.transaction_params.progress;
1821        let mut fh_code = self
1822            .local_cfg
1823            .fault_handler
1824            .get_fault_handler(condition_code);
1825        // CFDP 4.11.2.3.2: Any fault declared in the course of transferring the Finished (cancel)
1826        // PDU must result in abadonment of the transaction.
1827        if let Some(positive_ack) = &self.transaction_params.positive_ack_params {
1828            if positive_ack.positive_ack_of_cancellation {
1829                fh_code = FaultHandlerCode::AbandonTransaction;
1830            }
1831        }
1832        match fh_code {
1833            FaultHandlerCode::NoticeOfCancellation => {
1834                self.notice_of_cancellation(condition_code, EntityIdTlv::new(self.local_cfg().id));
1835            }
1836            FaultHandlerCode::NoticeOfSuspension => self.notice_of_suspension(),
1837            FaultHandlerCode::IgnoreError => (),
1838            FaultHandlerCode::AbandonTransaction => (),
1839        }
1840        self.local_cfg.fault_handler.report_fault(
1841            fh_code,
1842            FaultInfo::new(transaction_id, condition_code, progress),
1843        )
1844    }
1845
1846    fn notice_of_cancellation(&self, condition_code: ConditionCode, fault_location: EntityIdTlv) {
1847        self.set_step_internal(TransactionStep::TransferCompletion);
1848        let tstate = &self.transaction_params;
1849        tstate.finished_params.condition_code.set(condition_code);
1850        tstate
1851            .completion_disposition
1852            .set(CompletionDisposition::Cancelled);
1853        tstate
1854            .finished_params
1855            .fault_location_finished
1856            .set(Some(fault_location));
1857    }
1858
1859    fn notice_of_suspension(&self) {
1860        // TODO: Implement suspension handling.
1861    }
1862
1863    fn abandon_transaction(&mut self) {
1864        self.reset();
1865    }
1866
1867    #[inline]
1868    fn set_step_internal(&self, step: TransactionStep) {
1869        self.step.set(step);
1870    }
1871
1872    #[inline]
1873    fn set_step(&mut self, step: TransactionStep) {
1874        self.set_step_internal(step);
1875    }
1876
1877    fn send_finished_pdu(&mut self) -> Result<u32, DestError> {
1878        let tstate = &self.transaction_params;
1879
1880        let pdu_header = PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0);
1881        let finished_pdu = if tstate.finished_params.condition_code.get() == ConditionCode::NoError
1882            || tstate.finished_params.condition_code.get() == ConditionCode::UnsupportedChecksumType
1883        {
1884            FinishedPduCreator::new_no_error(
1885                pdu_header,
1886                tstate.finished_params.delivery_code.get(),
1887                tstate.finished_params.file_status,
1888            )
1889        } else {
1890            FinishedPduCreator::new(
1891                pdu_header,
1892                tstate.finished_params.condition_code.get(),
1893                tstate.finished_params.delivery_code.get(),
1894                tstate.finished_params.file_status,
1895                &[],
1896                tstate.finished_params.fault_location_finished.get(),
1897            )
1898        };
1899        finished_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
1900        self.pdu_sender.send_file_directive_pdu(
1901            FileDirectiveType::FinishedPdu,
1902            &self.pdu_and_cksum_buffer.borrow()[0..finished_pdu.len_written()],
1903        )?;
1904        Ok(1)
1905    }
1906}
1907
1908#[cfg(test)]
1909mod tests {
1910    #[allow(unused_imports)]
1911    use std::println;
1912    use std::{
1913        fs,
1914        path::{Path, PathBuf},
1915        string::String,
1916    };
1917
1918    use alloc::vec::Vec;
1919    use rand::Rng;
1920    use spacepackets::{
1921        cfdp::{
1922            ChecksumType, TransmissionMode,
1923            lv::Lv,
1924            pdu::{
1925                WritablePduPacket, finished::FinishedPduReader, metadata::MetadataPduCreator,
1926                nak::NakPduReader,
1927            },
1928        },
1929        util::UnsignedByteFieldU8,
1930    };
1931
1932    use crate::{
1933        CRC_32, FaultHandler, IndicationConfig, PduRawWithInfo, RemoteConfigStoreStd,
1934        filestore::NativeFilestore,
1935        lost_segments::LostSegmentsList,
1936        tests::{
1937            LOCAL_ID, REMOTE_ID, SentPdu, TestCfdpSender, TestCfdpUser, TestCheckTimer,
1938            TestCheckTimerCreator, TestFaultHandler, TimerExpiryControl, basic_remote_cfg_table,
1939        },
1940    };
1941
1942    use super::*;
1943
1944    #[derive(Debug, Clone, Copy)]
1945    pub struct TransferInfo {
1946        id: TransactionId,
1947        header: PduHeader,
1948    }
1949
1950    type TestDestHandler = DestinationHandler<
1951        TestCfdpSender,
1952        TestFaultHandler,
1953        NativeFilestore,
1954        RemoteConfigStoreStd,
1955        TestCheckTimerCreator,
1956        TestCheckTimer,
1957        LostSegmentsList,
1958    >;
1959
1960    struct DestHandlerTestbench {
1961        expiry_control: TimerExpiryControl,
1962        handler: TestDestHandler,
1963        src_path: PathBuf,
1964        dest_path: PathBuf,
1965        check_dest_file: bool,
1966        check_handler_idle_at_drop: bool,
1967        closure_requested: bool,
1968        pdu_conf: CommonPduConfig,
1969        expected_full_data: Vec<u8>,
1970        expected_file_size: u64,
1971        buf: [u8; 512],
1972    }
1973
1974    impl DestHandlerTestbench {
1975        fn new_with_fixed_paths(
1976            fault_handler: TestFaultHandler,
1977
1978            transmission_mode: TransmissionMode,
1979            closure_requested: bool,
1980        ) -> Self {
1981            let (src_path, dest_path) = init_full_filepaths_textfile();
1982            assert!(!Path::exists(&dest_path));
1983            Self::new(
1984                fault_handler,
1985                transmission_mode,
1986                closure_requested,
1987                true,
1988                src_path,
1989                dest_path,
1990            )
1991        }
1992
1993        fn new(
1994            fault_handler: TestFaultHandler,
1995            transmission_mode: TransmissionMode,
1996            closure_requested: bool,
1997            check_dest_file: bool,
1998            src_path: PathBuf,
1999            dest_path: PathBuf,
2000        ) -> Self {
2001            let expiry_control = TimerExpiryControl::default();
2002            let test_sender = TestCfdpSender::default();
2003            let dest_handler = default_dest_handler(fault_handler, test_sender, &expiry_control);
2004            let mut handler = Self {
2005                expiry_control,
2006                handler: dest_handler,
2007                src_path,
2008                closure_requested,
2009                dest_path,
2010                check_dest_file,
2011                check_handler_idle_at_drop: true,
2012                expected_file_size: 0,
2013                pdu_conf: CommonPduConfig::new_with_byte_fields(
2014                    LOCAL_ID,
2015                    REMOTE_ID,
2016                    UnsignedByteFieldU8::new(0),
2017                )
2018                .unwrap(),
2019                expected_full_data: Vec::new(),
2020                buf: [0; 512],
2021            };
2022            handler.pdu_conf.trans_mode = transmission_mode;
2023            handler.state_check(State::Idle, TransactionStep::Idle);
2024            handler
2025        }
2026
2027        pub fn fault_handler(&self) -> &FaultHandler<TestFaultHandler> {
2028            &self.handler.local_cfg.fault_handler
2029        }
2030
2031        #[inline]
2032        fn set_large_file_flag(&mut self, large_file: LargeFileFlag) {
2033            self.pdu_conf.file_flag = large_file;
2034        }
2035
2036        fn dest_path(&self) -> &PathBuf {
2037            &self.dest_path
2038        }
2039
2040        fn all_fault_queues_empty(&self) -> bool {
2041            self.handler
2042                .local_cfg
2043                .user_fault_hook()
2044                .borrow()
2045                .all_queues_empty()
2046        }
2047
2048        #[allow(dead_code)]
2049        fn indication_cfg_mut(&mut self) -> &mut IndicationConfig {
2050            &mut self.handler.local_cfg.indication_cfg
2051        }
2052
2053        fn remote_cfg_mut(&mut self) -> &mut RemoteEntityConfig {
2054            self.handler
2055                .remote_cfg_table
2056                .get_mut(LOCAL_ID.value())
2057                .unwrap()
2058        }
2059
2060        fn pdu_queue_empty(&mut self) -> bool {
2061            self.handler.pdu_sender.queue_empty()
2062        }
2063
2064        fn pdu_queue_len(&mut self) -> usize {
2065            self.handler.pdu_sender.queue_len()
2066        }
2067
2068        fn get_next_pdu(&mut self) -> Option<SentPdu> {
2069            self.handler.pdu_sender.retrieve_next_pdu()
2070        }
2071
2072        fn indication_cfg(&mut self) -> &IndicationConfig {
2073            &self.handler.local_cfg.indication_cfg
2074        }
2075
2076        fn set_check_timer_expired(&mut self) {
2077            self.expiry_control.set_check_limit_expired();
2078        }
2079
2080        fn set_nak_activity_timer_expired(&mut self) {
2081            self.expiry_control.set_nak_activity_expired();
2082        }
2083
2084        fn set_positive_ack_expired(&mut self) {
2085            self.expiry_control.set_positive_ack_expired();
2086        }
2087
2088        fn test_user_from_cached_paths(&self, expected_file_size: u64) -> TestCfdpUser {
2089            TestCfdpUser::new(
2090                0,
2091                self.src_path.to_string_lossy().into(),
2092                self.dest_path.to_string_lossy().into(),
2093                expected_file_size,
2094            )
2095        }
2096
2097        fn generic_transfer_init(
2098            &mut self,
2099            user: &mut TestCfdpUser,
2100            file_size: u64,
2101        ) -> Result<TransferInfo, DestError> {
2102            self.expected_file_size = file_size;
2103            assert_eq!(user.transaction_indication_call_count, 0);
2104            assert_eq!(user.metadata_recv_queue.len(), 0);
2105            let pdu_header = PduHeader::new_for_file_directive(self.pdu_conf, 0);
2106            let metadata_pdu = create_metadata_pdu(
2107                &pdu_header,
2108                self.src_path.as_path(),
2109                self.dest_path.as_path(),
2110                file_size,
2111                self.closure_requested,
2112            );
2113            let packet_info = create_packet_info(&metadata_pdu, &mut self.buf);
2114            self.handler.state_machine(user, Some(&packet_info))?;
2115            assert_eq!(user.metadata_recv_queue.len(), 1);
2116            assert_eq!(
2117                self.handler.transmission_mode().unwrap(),
2118                self.pdu_conf.trans_mode
2119            );
2120            assert_eq!(user.transaction_indication_call_count, 0);
2121            assert_eq!(user.metadata_recv_queue.len(), 1);
2122            let metadata_recvd = user.metadata_recv_queue.pop_front().unwrap();
2123            assert_eq!(metadata_recvd.source_id, LOCAL_ID.into());
2124            assert_eq!(
2125                metadata_recvd.src_file_name,
2126                String::from(self.src_path.to_str().unwrap())
2127            );
2128            assert_eq!(
2129                metadata_recvd.dest_file_name,
2130                String::from(self.dest_path().to_str().unwrap())
2131            );
2132            assert_eq!(metadata_recvd.id, self.handler.transaction_id().unwrap());
2133            assert_eq!(metadata_recvd.file_size, file_size);
2134            assert!(metadata_recvd.msgs_to_user.is_empty());
2135            Ok(TransferInfo {
2136                id: self.handler.transaction_id().unwrap(),
2137                header: pdu_header,
2138            })
2139        }
2140
2141        fn generic_file_data_insert(
2142            &mut self,
2143            user: &mut TestCfdpUser,
2144            offset: u64,
2145            file_data_chunk: &[u8],
2146        ) -> Result<u32, DestError> {
2147            let pdu_header = PduHeader::new_for_file_data_default(self.pdu_conf, 0);
2148            let filedata_pdu =
2149                FileDataPdu::new_no_seg_metadata(pdu_header, offset, file_data_chunk);
2150            filedata_pdu
2151                .write_to_bytes(&mut self.buf)
2152                .expect("writing file data PDU failed");
2153            let packet_info = PduRawWithInfo::new(&self.buf).expect("creating packet info failed");
2154            let result = self.handler.state_machine(user, Some(&packet_info));
2155            if self.indication_cfg().file_segment_recv {
2156                assert!(!user.file_seg_recvd_queue.is_empty());
2157                let file_seg = user.file_seg_recvd_queue.pop_front().unwrap();
2158                assert_eq!(file_seg.offset, offset);
2159                assert_eq!(file_seg.length, file_data_chunk.len());
2160            }
2161            result
2162        }
2163
2164        fn generic_eof_no_error(
2165            &mut self,
2166            user: &mut TestCfdpUser,
2167            expected_full_data: Vec<u8>,
2168        ) -> Result<u32, DestError> {
2169            self.expected_full_data = expected_full_data;
2170            assert_eq!(user.finished_indic_queue.len(), 0);
2171            let pdu_header = PduHeader::new_for_file_directive(self.pdu_conf, 0);
2172            let eof_pdu = create_no_error_eof(&self.expected_full_data, &pdu_header);
2173            let packet_info = create_packet_info(&eof_pdu, &mut self.buf);
2174            self.check_handler_idle_at_drop = true;
2175            self.check_dest_file = true;
2176            let result = self.handler.state_machine(user, Some(&packet_info));
2177            if self.indication_cfg().eof_recv {
2178                assert_eq!(user.eof_recvd_call_count, 1);
2179            }
2180            result
2181        }
2182
2183        fn check_completion_indication_success(&mut self, user: &mut TestCfdpUser) {
2184            assert_eq!(user.finished_indic_queue.len(), 1);
2185            let finished_indication = user.finished_indic_queue.pop_front().unwrap();
2186            assert_eq!(
2187                finished_indication.id,
2188                self.handler.transaction_id().unwrap()
2189            );
2190            assert_eq!(finished_indication.file_status, FileStatus::Retained);
2191            assert_eq!(finished_indication.delivery_code, DeliveryCode::Complete);
2192            assert_eq!(finished_indication.condition_code, ConditionCode::NoError);
2193        }
2194
2195        fn check_completion_indication_failure(
2196            &mut self,
2197            user: &mut TestCfdpUser,
2198            cond_code: ConditionCode,
2199            file_status: FileStatus,
2200            delivery_code: DeliveryCode,
2201        ) {
2202            assert_eq!(user.finished_indic_queue.len(), 1);
2203            let finished_indication = user.finished_indic_queue.pop_front().unwrap();
2204            assert_eq!(
2205                finished_indication.id,
2206                self.handler.transaction_id().unwrap()
2207            );
2208            assert_eq!(finished_indication.file_status, file_status);
2209            assert_eq!(finished_indication.delivery_code, delivery_code);
2210            assert_eq!(finished_indication.condition_code, cond_code);
2211        }
2212
2213        fn check_eof_ack_pdu(&mut self, cond_code: ConditionCode) {
2214            assert!(!self.pdu_queue_empty());
2215            let pdu = self.get_next_pdu().unwrap();
2216            assert_eq!(pdu.pdu_type, PduType::FileDirective);
2217            assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::AckPdu);
2218            let ack_pdu = AckPdu::from_bytes(&pdu.raw_pdu).unwrap();
2219            assert_eq!(ack_pdu.condition_code(), cond_code);
2220            assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
2221            assert_eq!(
2222                ack_pdu.directive_code_of_acked_pdu(),
2223                FileDirectiveType::EofPdu
2224            );
2225        }
2226
2227        fn check_finished_pdu_success(&mut self) {
2228            let pdu = self.get_next_pdu().unwrap();
2229            assert_eq!(pdu.pdu_type, PduType::FileDirective);
2230            assert_eq!(
2231                pdu.file_directive_type.unwrap(),
2232                FileDirectiveType::FinishedPdu
2233            );
2234            let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
2235            assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
2236            assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
2237            assert_eq!(finished_pdu.condition_code(), ConditionCode::NoError);
2238            assert!(finished_pdu.fault_location().is_none());
2239        }
2240
2241        fn check_finished_pdu_failure(
2242            &mut self,
2243            cond_code: ConditionCode,
2244            file_status: FileStatus,
2245            delivery_code: DeliveryCode,
2246        ) {
2247            let pdu = self.get_next_pdu().unwrap();
2248            assert_eq!(pdu.pdu_type, PduType::FileDirective);
2249            assert_eq!(
2250                pdu.file_directive_type.unwrap(),
2251                FileDirectiveType::FinishedPdu
2252            );
2253            let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
2254            assert_eq!(finished_pdu.delivery_code(), delivery_code);
2255            assert_eq!(finished_pdu.file_status(), file_status);
2256            assert_eq!(finished_pdu.condition_code(), cond_code);
2257        }
2258
2259        fn acknowledge_finished_pdu(
2260            &mut self,
2261            user: &mut impl CfdpUser,
2262            transfer_info: &TransferInfo,
2263        ) {
2264            let ack_pdu = AckPdu::new_for_finished_pdu(
2265                transfer_info.header,
2266                ConditionCode::NoError,
2267                TransactionStatus::Active,
2268            );
2269            self.handler
2270                .state_machine(user, Some(&create_packet_info(&ack_pdu, &mut self.buf)))
2271                .expect("handling ack PDU failed");
2272        }
2273
2274        fn state_check(&self, state: State, step: TransactionStep) {
2275            assert_eq!(self.handler.state(), state);
2276            assert_eq!(self.handler.step(), step);
2277        }
2278    }
2279
2280    // Specifying some checks in the drop method avoids some boilerplate.
2281    impl Drop for DestHandlerTestbench {
2282        fn drop(&mut self) {
2283            if !self.all_fault_queues_empty() {
2284                let fh_queues = self.handler.local_cfg.user_fault_hook().borrow();
2285                println!(
2286                    "fault queues not empty. cancellation {}, suspension {}, ignored {}, abandon {}",
2287                    fh_queues.notice_of_cancellation_queue.len(),
2288                    fh_queues.notice_of_suspension_queue.len(),
2289                    fh_queues.ignored_queue.len(),
2290                    fh_queues.abandoned_queue.len()
2291                );
2292            }
2293            assert!(self.all_fault_queues_empty(), "fault queues not empty, ");
2294            assert!(self.pdu_queue_empty());
2295            if self.check_handler_idle_at_drop {
2296                self.state_check(State::Idle, TransactionStep::Idle);
2297            }
2298            if self.check_dest_file {
2299                assert!(Path::exists(&self.dest_path));
2300                let read_content = fs::read(&self.dest_path).expect("reading back string failed");
2301                assert_eq!(read_content.len() as u64, self.expected_file_size);
2302                assert_eq!(read_content, self.expected_full_data);
2303                assert!(fs::remove_file(self.dest_path.as_path()).is_ok());
2304            }
2305        }
2306    }
2307
2308    fn init_full_filepaths_textfile() -> (PathBuf, PathBuf) {
2309        (
2310            tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf(),
2311            tempfile::NamedTempFile::new()
2312                .unwrap()
2313                .into_temp_path()
2314                .to_path_buf(),
2315        )
2316    }
2317
2318    fn default_dest_handler(
2319        test_fault_handler: TestFaultHandler,
2320        test_packet_sender: TestCfdpSender,
2321        expiry_control: &TimerExpiryControl,
2322    ) -> TestDestHandler {
2323        let local_entity_cfg = LocalEntityConfig {
2324            id: REMOTE_ID.into(),
2325            indication_cfg: IndicationConfig::default(),
2326            fault_handler: FaultHandler::new(test_fault_handler),
2327        };
2328        DestinationHandler::new(
2329            local_entity_cfg,
2330            test_packet_sender,
2331            NativeFilestore::default(),
2332            basic_remote_cfg_table(LOCAL_ID, 1024, true),
2333            TestCheckTimerCreator::new(expiry_control),
2334            LostSegmentsList::default(),
2335        )
2336    }
2337
2338    fn create_metadata_pdu<'filename>(
2339        pdu_header: &PduHeader,
2340        src_name: &'filename Path,
2341        dest_name: &'filename Path,
2342        file_size: u64,
2343        closure_requested: bool,
2344    ) -> MetadataPduCreator<'filename, 'filename, 'static> {
2345        let checksum_type = if file_size == 0 {
2346            ChecksumType::NullChecksum
2347        } else {
2348            ChecksumType::Crc32
2349        };
2350        let metadata_params =
2351            MetadataGenericParams::new(closure_requested, checksum_type, file_size);
2352        MetadataPduCreator::new_no_opts(
2353            *pdu_header,
2354            metadata_params,
2355            Lv::new_from_str(src_name.as_os_str().to_str().unwrap()).unwrap(),
2356            Lv::new_from_str(dest_name.as_os_str().to_str().unwrap()).unwrap(),
2357        )
2358    }
2359
2360    fn create_packet_info<'a>(
2361        pdu: &'a impl WritablePduPacket,
2362        buf: &'a mut [u8],
2363    ) -> PduRawWithInfo<'a> {
2364        let written_len = pdu
2365            .write_to_bytes(buf)
2366            .expect("writing metadata PDU failed");
2367        PduRawWithInfo::new(&buf[..written_len]).expect("generating packet info failed")
2368    }
2369
2370    fn create_no_error_eof(file_data: &[u8], pdu_header: &PduHeader) -> EofPdu {
2371        let crc32 = if !file_data.is_empty() {
2372            let mut digest = CRC_32.digest();
2373            digest.update(file_data);
2374            digest.finalize()
2375        } else {
2376            0
2377        };
2378        EofPdu::new_no_error(*pdu_header, crc32, file_data.len() as u64)
2379    }
2380
2381    #[test]
2382    fn test_basic() {
2383        let fault_handler = TestFaultHandler::default();
2384        let test_sender = TestCfdpSender::default();
2385        let dest_handler =
2386            default_dest_handler(fault_handler, test_sender, &TimerExpiryControl::default());
2387        assert!(dest_handler.transmission_mode().is_none());
2388        assert!(
2389            dest_handler
2390                .local_cfg
2391                .fault_handler
2392                .user_hook
2393                .borrow()
2394                .all_queues_empty()
2395        );
2396        assert!(dest_handler.pdu_sender.queue_empty());
2397        assert_eq!(dest_handler.state(), State::Idle);
2398        assert_eq!(dest_handler.step(), TransactionStep::Idle);
2399    }
2400
2401    #[test]
2402    fn test_cancelling_idle_fsm() {
2403        let fault_handler = TestFaultHandler::default();
2404        let test_sender = TestCfdpSender::default();
2405        let mut dest_handler =
2406            default_dest_handler(fault_handler, test_sender, &TimerExpiryControl::default());
2407        assert!(!dest_handler.cancel_request(&TransactionId::new(
2408            UnsignedByteFieldU8::new(0).into(),
2409            UnsignedByteFieldU8::new(0).into()
2410        )));
2411    }
2412
2413    #[test]
2414    fn test_empty_file_transfer_not_acked_no_closure() {
2415        let fault_handler = TestFaultHandler::default();
2416        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2417            fault_handler,
2418            TransmissionMode::Unacknowledged,
2419            false,
2420        );
2421        let mut test_user = tb.test_user_from_cached_paths(0);
2422        tb.generic_transfer_init(&mut test_user, 0)
2423            .expect("transfer init failed");
2424        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2425        tb.generic_eof_no_error(&mut test_user, Vec::new())
2426            .expect("EOF no error insertion failed");
2427        tb.check_completion_indication_success(&mut test_user);
2428    }
2429
2430    #[test]
2431    fn test_empty_file_transfer_invalid_remote_id() {
2432        let fault_handler = TestFaultHandler::default();
2433        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2434            fault_handler,
2435            TransmissionMode::Unacknowledged,
2436            false,
2437        );
2438        let mut test_user = tb.test_user_from_cached_paths(0);
2439        let mut conf = tb.pdu_conf;
2440        // Just swap them..
2441        conf.set_source_and_dest_id(REMOTE_ID, LOCAL_ID).unwrap();
2442        let pdu_header = PduHeader::new_for_file_directive(conf, 0);
2443        let metadata_pdu = create_metadata_pdu(
2444            &pdu_header,
2445            tb.src_path.as_path(),
2446            tb.dest_path.as_path(),
2447            0,
2448            false,
2449        );
2450        let packet_info = create_packet_info(&metadata_pdu, &mut tb.buf);
2451        if let Err(DestError::NoRemoteConfigFound(id)) =
2452            tb.handler.state_machine(&mut test_user, Some(&packet_info))
2453        {
2454            assert_eq!(id, REMOTE_ID.into());
2455        } else {
2456            panic!("expected no remote config found error");
2457        }
2458        tb.check_dest_file = false;
2459    }
2460
2461    #[test]
2462    fn test_empty_file_transfer_acked() {
2463        let fault_handler = TestFaultHandler::default();
2464        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2465            fault_handler,
2466            TransmissionMode::Acknowledged,
2467            false,
2468        );
2469        let mut user = tb.test_user_from_cached_paths(0);
2470        let transfer_info = tb
2471            .generic_transfer_init(&mut user, 0)
2472            .expect("transfer init failed");
2473        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2474        tb.generic_eof_no_error(&mut user, Vec::new())
2475            .expect("EOF no error insertion failed");
2476        tb.check_completion_indication_success(&mut user);
2477        assert_eq!(tb.pdu_queue_len(), 2);
2478        tb.check_eof_ack_pdu(ConditionCode::NoError);
2479        tb.check_finished_pdu_success();
2480        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
2481    }
2482
2483    #[test]
2484    fn test_small_file_transfer_not_acked() {
2485        let file_data_str = "Hello World!";
2486        let file_data = file_data_str.as_bytes();
2487        let file_size = file_data.len() as u64;
2488        let fault_handler = TestFaultHandler::default();
2489
2490        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2491            fault_handler,
2492            TransmissionMode::Unacknowledged,
2493            false,
2494        );
2495        let mut user = tb.test_user_from_cached_paths(file_size);
2496        let _transfer_info = tb
2497            .generic_transfer_init(&mut user, file_size)
2498            .expect("transfer init failed");
2499        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2500        assert_eq!(
2501            tb.generic_file_data_insert(&mut user, 0, file_data)
2502                .expect("file data insertion failed"),
2503            0
2504        );
2505        assert_eq!(
2506            tb.generic_eof_no_error(&mut user, file_data.to_vec())
2507                .expect("EOF no error insertion failed"),
2508            0
2509        );
2510        tb.check_completion_indication_success(&mut user);
2511    }
2512
2513    #[test]
2514    fn test_small_file_transfer_acked() {
2515        let file_data_str = "Hello World!";
2516        let file_data = file_data_str.as_bytes();
2517        let file_size = file_data.len() as u64;
2518        let fault_handler = TestFaultHandler::default();
2519
2520        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2521            fault_handler,
2522            TransmissionMode::Acknowledged,
2523            false,
2524        );
2525        let mut user = tb.test_user_from_cached_paths(file_size);
2526        let transfer_info = tb
2527            .generic_transfer_init(&mut user, file_size)
2528            .expect("transfer init failed");
2529        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2530        assert_eq!(
2531            tb.generic_file_data_insert(&mut user, 0, file_data)
2532                .expect("file data insertion failed"),
2533            0
2534        );
2535        assert_eq!(
2536            tb.generic_eof_no_error(&mut user, file_data.to_vec())
2537                .expect("EOF no error insertion failed"),
2538            2
2539        );
2540        tb.check_completion_indication_success(&mut user);
2541        assert_eq!(tb.pdu_queue_len(), 2);
2542        tb.check_eof_ack_pdu(ConditionCode::NoError);
2543        tb.check_finished_pdu_success();
2544        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
2545    }
2546
2547    #[test]
2548    fn test_segmented_file_transfer_not_acked() {
2549        let mut rng = rand::rng();
2550        let mut random_data = [0u8; 512];
2551        rng.fill(&mut random_data);
2552        let file_size = random_data.len() as u64;
2553        let segment_len = 256;
2554        let fault_handler = TestFaultHandler::default();
2555
2556        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2557            fault_handler,
2558            TransmissionMode::Unacknowledged,
2559            false,
2560        );
2561        let mut test_user = tb.test_user_from_cached_paths(file_size);
2562        tb.generic_transfer_init(&mut test_user, file_size)
2563            .expect("transfer init failed");
2564        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2565        tb.generic_file_data_insert(&mut test_user, 0, &random_data[0..segment_len])
2566            .expect("file data insertion failed");
2567        tb.generic_file_data_insert(
2568            &mut test_user,
2569            segment_len as u64,
2570            &random_data[segment_len..],
2571        )
2572        .expect("file data insertion failed");
2573        tb.generic_eof_no_error(&mut test_user, random_data.to_vec())
2574            .expect("EOF no error insertion failed");
2575        tb.check_completion_indication_success(&mut test_user);
2576    }
2577
2578    #[test]
2579    fn test_segmented_file_transfer_acked() {
2580        let mut rng = rand::rng();
2581        let mut random_data = [0u8; 512];
2582        rng.fill(&mut random_data);
2583        let file_size = random_data.len() as u64;
2584        let segment_len = 256;
2585        let fault_handler = TestFaultHandler::default();
2586
2587        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2588            fault_handler,
2589            TransmissionMode::Acknowledged,
2590            false,
2591        );
2592        let mut user = tb.test_user_from_cached_paths(file_size);
2593        let transfer_info = tb
2594            .generic_transfer_init(&mut user, file_size)
2595            .expect("transfer init failed");
2596        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2597        tb.generic_file_data_insert(&mut user, 0, &random_data[0..segment_len])
2598            .expect("file data insertion failed");
2599        tb.generic_file_data_insert(&mut user, segment_len as u64, &random_data[segment_len..])
2600            .expect("file data insertion failed");
2601        tb.generic_eof_no_error(&mut user, random_data.to_vec())
2602            .expect("EOF no error insertion failed");
2603        tb.check_completion_indication_success(&mut user);
2604        assert_eq!(tb.pdu_queue_len(), 2);
2605        tb.check_eof_ack_pdu(ConditionCode::NoError);
2606        tb.check_finished_pdu_success();
2607        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
2608    }
2609
2610    #[test]
2611    fn test_check_limit_handling_transfer_success() {
2612        let mut rng = rand::rng();
2613        let mut random_data = [0u8; 512];
2614        rng.fill(&mut random_data);
2615        let file_size = random_data.len() as u64;
2616        let segment_len = 256;
2617        let fault_handler = TestFaultHandler::default();
2618
2619        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2620            fault_handler,
2621            TransmissionMode::Unacknowledged,
2622            false,
2623        );
2624        let mut test_user = tb.test_user_from_cached_paths(file_size);
2625        let transfer_info = tb
2626            .generic_transfer_init(&mut test_user, file_size)
2627            .expect("transfer init failed");
2628
2629        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2630        tb.generic_file_data_insert(&mut test_user, 0, &random_data[0..segment_len])
2631            .expect("file data insertion 0 failed");
2632        tb.generic_eof_no_error(&mut test_user, random_data.to_vec())
2633            .expect("EOF no error insertion failed");
2634
2635        // Checksum failure.
2636        let mut fault_handler = tb.handler.local_cfg.fault_handler.user_hook.borrow_mut();
2637        assert_eq!(fault_handler.ignored_queue.len(), 1);
2638        let cancelled = fault_handler.ignored_queue.pop_front().unwrap();
2639        assert_eq!(cancelled.transaction_id(), transfer_info.id);
2640        assert_eq!(
2641            cancelled.condition_code(),
2642            ConditionCode::FileChecksumFailure
2643        );
2644        assert_eq!(cancelled.progress(), segment_len as u64);
2645
2646        drop(fault_handler);
2647        tb.state_check(
2648            State::Busy,
2649            TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling,
2650        );
2651        tb.set_check_timer_expired();
2652
2653        tb.generic_file_data_insert(
2654            &mut test_user,
2655            segment_len as u64,
2656            &random_data[segment_len..],
2657        )
2658        .expect("file data insertion 1 failed");
2659        tb.handler
2660            .state_machine_no_packet(&mut test_user)
2661            .expect("fsm failure");
2662
2663        tb.check_completion_indication_success(&mut test_user);
2664        assert!(test_user.indication_queues_empty());
2665    }
2666
2667    #[test]
2668    fn test_check_limit_handling_limit_reached() {
2669        let mut rng = rand::rng();
2670        let mut random_data = [0u8; 512];
2671        rng.fill(&mut random_data);
2672        let file_size = random_data.len() as u64;
2673        let segment_len: usize = 256;
2674        let check_checksum_failure =
2675            |testbench: &mut DestHandlerTestbench, transaction_id: TransactionId| {
2676                let mut fault_hook = testbench.fault_handler().user_hook.borrow_mut();
2677                assert!(fault_hook.notice_of_suspension_queue.is_empty());
2678                let ignored_queue = &mut fault_hook.ignored_queue;
2679                assert_eq!(ignored_queue.len(), 1);
2680                let ignored = ignored_queue.pop_front().unwrap();
2681                assert_eq!(ignored.transaction_id(), transaction_id);
2682                assert_eq!(ignored.condition_code(), ConditionCode::FileChecksumFailure);
2683                assert_eq!(ignored.progress(), segment_len as u64);
2684            };
2685
2686        let fault_handler = TestFaultHandler::default();
2687        let mut testbench = DestHandlerTestbench::new_with_fixed_paths(
2688            fault_handler,
2689            TransmissionMode::Unacknowledged,
2690            false,
2691        );
2692        let mut test_user = testbench.test_user_from_cached_paths(file_size);
2693        let transfer_info = testbench
2694            .generic_transfer_init(&mut test_user, file_size)
2695            .expect("transfer init failed");
2696
2697        testbench.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2698        testbench
2699            .generic_file_data_insert(&mut test_user, 0, &random_data[0..segment_len])
2700            .expect("file data insertion 0 failed");
2701        testbench
2702            .generic_eof_no_error(&mut test_user, random_data.to_vec())
2703            .expect("EOF no error insertion failed");
2704        check_checksum_failure(&mut testbench, transfer_info.id);
2705
2706        testbench.state_check(
2707            State::Busy,
2708            TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling,
2709        );
2710
2711        testbench.set_check_timer_expired();
2712        testbench
2713            .handler
2714            .state_machine_no_packet(&mut test_user)
2715            .expect("fsm error");
2716        testbench.state_check(
2717            State::Busy,
2718            TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling,
2719        );
2720        check_checksum_failure(&mut testbench, transfer_info.id);
2721
2722        testbench.set_check_timer_expired();
2723        testbench
2724            .handler
2725            .state_machine_no_packet(&mut test_user)
2726            .expect("fsm error");
2727        check_checksum_failure(&mut testbench, transfer_info.id);
2728
2729        testbench.state_check(State::Idle, TransactionStep::Idle);
2730
2731        {
2732            let mut fault_hook = testbench.fault_handler().user_hook.borrow_mut();
2733            let cancelled_queue = &mut fault_hook.notice_of_cancellation_queue;
2734            assert_eq!(cancelled_queue.len(), 1);
2735            let cancelled = cancelled_queue.pop_front().unwrap();
2736            assert_eq!(cancelled.transaction_id(), transfer_info.id);
2737            assert_eq!(cancelled.condition_code(), ConditionCode::CheckLimitReached);
2738            assert_eq!(cancelled.progress(), segment_len as u64);
2739        }
2740
2741        assert_eq!(test_user.finished_indic_queue.len(), 1);
2742        let finished_indication = test_user.finished_indic_queue.pop_front().unwrap();
2743        assert_eq!(finished_indication.id, transfer_info.id);
2744        assert_eq!(
2745            finished_indication.condition_code,
2746            ConditionCode::CheckLimitReached
2747        );
2748        assert_eq!(finished_indication.delivery_code, DeliveryCode::Incomplete);
2749        assert_eq!(finished_indication.file_status, FileStatus::Retained);
2750
2751        assert!(testbench.handler.pdu_sender.queue_empty());
2752
2753        // Check that the broken file exists.
2754        testbench.check_dest_file = false;
2755        assert!(Path::exists(testbench.dest_path()));
2756        let read_content = fs::read(testbench.dest_path()).expect("reading back string failed");
2757        assert_eq!(read_content.len(), segment_len);
2758        assert_eq!(read_content, &random_data[0..segment_len]);
2759        assert!(fs::remove_file(testbench.dest_path().as_path()).is_ok());
2760        assert!(test_user.indication_queues_empty());
2761    }
2762
2763    fn check_finished_pdu_success(sent_pdu: &SentPdu) {
2764        assert_eq!(sent_pdu.pdu_type, PduType::FileDirective);
2765        assert_eq!(
2766            sent_pdu.file_directive_type,
2767            Some(FileDirectiveType::FinishedPdu)
2768        );
2769        let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
2770        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
2771        assert_eq!(finished_pdu.condition_code(), ConditionCode::NoError);
2772        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
2773        assert!(finished_pdu.fault_location().is_none());
2774        assert_eq!(finished_pdu.fs_responses_raw(), &[]);
2775    }
2776
2777    #[test]
2778    fn test_file_transfer_with_closure() {
2779        let fault_handler = TestFaultHandler::default();
2780        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2781            fault_handler,
2782            TransmissionMode::Unacknowledged,
2783            true,
2784        );
2785        let mut test_user = tb.test_user_from_cached_paths(0);
2786        tb.generic_transfer_init(&mut test_user, 0)
2787            .expect("transfer init failed");
2788        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2789        let sent_packets = tb
2790            .generic_eof_no_error(&mut test_user, Vec::new())
2791            .expect("EOF no error insertion failed");
2792        assert_eq!(sent_packets, 1);
2793        assert!(tb.all_fault_queues_empty());
2794        // The Finished PDU was sent, so the state machine is done.
2795        tb.state_check(State::Idle, TransactionStep::Idle);
2796        assert!(!tb.handler.pdu_sender.queue_empty());
2797        let sent_pdu = tb.handler.pdu_sender.retrieve_next_pdu().unwrap();
2798        check_finished_pdu_success(&sent_pdu);
2799        tb.check_completion_indication_success(&mut test_user);
2800        assert!(test_user.indication_queues_empty());
2801    }
2802
2803    #[test]
2804    fn test_finished_pdu_insertion_rejected() {
2805        let fault_handler = TestFaultHandler::default();
2806        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2807            fault_handler,
2808            TransmissionMode::Unacknowledged,
2809            false,
2810        );
2811        tb.check_dest_file = false;
2812        let mut user = tb.test_user_from_cached_paths(0);
2813        let finished_pdu = FinishedPduCreator::new_no_error(
2814            PduHeader::new_for_file_directive(CommonPduConfig::default(), 0),
2815            DeliveryCode::Complete,
2816            FileStatus::Retained,
2817        );
2818        let finished_pdu_raw = finished_pdu.to_vec().unwrap();
2819        let packet_info = PduRawWithInfo::new(&finished_pdu_raw).unwrap();
2820        let error = tb.handler.state_machine(&mut user, Some(&packet_info));
2821        assert!(error.is_err());
2822        let error = error.unwrap_err();
2823        if let DestError::CantProcessPacketType {
2824            pdu_type,
2825            directive_type,
2826        } = error
2827        {
2828            assert_eq!(pdu_type, PduType::FileDirective);
2829            assert_eq!(directive_type, Some(FileDirectiveType::FinishedPdu));
2830        }
2831    }
2832
2833    #[test]
2834    fn test_metadata_insertion_twice_fails() {
2835        let fault_handler = TestFaultHandler::default();
2836        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2837            fault_handler,
2838            TransmissionMode::Unacknowledged,
2839            true,
2840        );
2841        let mut user = tb.test_user_from_cached_paths(0);
2842        tb.generic_transfer_init(&mut user, 0)
2843            .expect("transfer init failed");
2844        tb.check_handler_idle_at_drop = false;
2845        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2846        let pdu_header = PduHeader::new_for_file_directive(tb.pdu_conf, 0);
2847        let metadata_pdu = create_metadata_pdu(
2848            &pdu_header,
2849            tb.src_path.as_path(),
2850            tb.dest_path.as_path(),
2851            0,
2852            tb.closure_requested,
2853        );
2854        let packet_info = create_packet_info(&metadata_pdu, &mut tb.buf);
2855        let error = tb.handler.state_machine(&mut user, Some(&packet_info));
2856        assert!(error.is_err());
2857        let error = error.unwrap_err();
2858        if let DestError::RecvdMetadataButIsBusy = error {
2859        } else {
2860            panic!("unexpected error: {:?}", error);
2861        }
2862    }
2863
2864    #[test]
2865    fn test_checksum_failure_not_acked() {
2866        let file_data_str = "Hello World!";
2867        let file_data = file_data_str.as_bytes();
2868        let file_size = file_data.len() as u64;
2869        let fault_handler = TestFaultHandler::default();
2870        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
2871            fault_handler,
2872            TransmissionMode::Unacknowledged,
2873            true,
2874        );
2875        let mut user = tb.test_user_from_cached_paths(file_size);
2876        tb.generic_transfer_init(&mut user, file_size)
2877            .expect("transfer init failed");
2878        let faulty_file_data = b"Hemlo World!";
2879        assert_eq!(
2880            tb.generic_file_data_insert(&mut user, 0, faulty_file_data)
2881                .expect("file data insertion failed"),
2882            0
2883        );
2884        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2885        let sent_packets = tb
2886            .generic_eof_no_error(&mut user, file_data.into())
2887            .expect("EOF no error insertion failed");
2888        // FSM enters check limit algorithm here, so no finished PDU is created.
2889        assert_eq!(sent_packets, 0);
2890
2891        let transaction_id = tb.handler.transaction_id().unwrap();
2892        let mut fault_hook = tb.handler.local_cfg.user_fault_hook().borrow_mut();
2893        assert!(fault_hook.notice_of_suspension_queue.is_empty());
2894
2895        // The file checksum failure is ignored by default and check limit handling is now
2896        // performed.
2897        let ignored_queue = &mut fault_hook.ignored_queue;
2898        assert_eq!(ignored_queue.len(), 1);
2899        let cancelled = ignored_queue.pop_front().unwrap();
2900        assert_eq!(cancelled.transaction_id(), transaction_id);
2901        assert_eq!(
2902            cancelled.condition_code(),
2903            ConditionCode::FileChecksumFailure
2904        );
2905        assert_eq!(cancelled.progress(), file_size);
2906        drop(fault_hook);
2907
2908        tb.state_check(
2909            State::Busy,
2910            TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling,
2911        );
2912        tb.set_check_timer_expired();
2913        tb.handler
2914            .state_machine_no_packet(&mut user)
2915            .expect("fsm error");
2916        tb.state_check(
2917            State::Busy,
2918            TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling,
2919        );
2920        tb.set_check_timer_expired();
2921        tb.handler
2922            .state_machine_no_packet(&mut user)
2923            .expect("fsm error");
2924        tb.state_check(State::Idle, TransactionStep::Idle);
2925
2926        // Transaction is cancelled because the check limit is reached.
2927        let mut fault_hook = tb.handler.local_cfg.user_fault_hook().borrow_mut();
2928        let cancelled_queue = &mut fault_hook.notice_of_cancellation_queue;
2929        assert_eq!(cancelled_queue.len(), 1);
2930        let cancelled = cancelled_queue.pop_front().unwrap();
2931        assert_eq!(cancelled.transaction_id(), transaction_id);
2932        assert_eq!(cancelled.condition_code(), ConditionCode::CheckLimitReached);
2933        assert_eq!(cancelled.progress(), file_size);
2934
2935        drop(fault_hook);
2936
2937        let sent_pdu = tb.handler.pdu_sender.retrieve_next_pdu().unwrap();
2938        assert_eq!(sent_pdu.pdu_type, PduType::FileDirective);
2939        assert_eq!(
2940            sent_pdu.file_directive_type,
2941            Some(FileDirectiveType::FinishedPdu)
2942        );
2943        let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
2944        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
2945        assert_eq!(
2946            finished_pdu.condition_code(),
2947            ConditionCode::CheckLimitReached
2948        );
2949
2950        let mut fault_hook = tb.handler.local_cfg.user_fault_hook().borrow_mut();
2951        let ignored_queue = &mut fault_hook.ignored_queue;
2952        assert_eq!(ignored_queue.len(), 2);
2953        let mut ignored = ignored_queue.pop_front().unwrap();
2954        assert_eq!(ignored.transaction_id(), transaction_id);
2955        assert_eq!(ignored.condition_code(), ConditionCode::FileChecksumFailure);
2956        assert_eq!(ignored.progress(), file_size);
2957        ignored = ignored_queue.pop_front().unwrap();
2958        assert_eq!(ignored.transaction_id(), transaction_id);
2959        assert_eq!(ignored.condition_code(), ConditionCode::FileChecksumFailure);
2960        assert_eq!(ignored.progress(), file_size);
2961
2962        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Incomplete);
2963        assert!(finished_pdu.fault_location().is_some());
2964        assert_eq!(
2965            *finished_pdu.fault_location().unwrap().entity_id(),
2966            REMOTE_ID.into()
2967        );
2968        assert_eq!(finished_pdu.fs_responses_raw(), &[]);
2969        assert!(tb.handler.pdu_sender.queue_empty());
2970        user.verify_finished_indication_retained(
2971            DeliveryCode::Incomplete,
2972            ConditionCode::CheckLimitReached,
2973            transaction_id,
2974        );
2975        tb.expected_full_data = faulty_file_data.to_vec();
2976    }
2977
2978    #[test]
2979    fn test_file_copy_to_directory() {
2980        let fault_handler = TestFaultHandler::default();
2981        let src_path = tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf();
2982        let dest_path = tempfile::TempDir::new().unwrap();
2983        let mut dest_path_buf = dest_path.keep();
2984        let mut tb = DestHandlerTestbench::new(
2985            fault_handler,
2986            TransmissionMode::Unacknowledged,
2987            false,
2988            false,
2989            src_path.clone(),
2990            dest_path_buf.clone(),
2991        );
2992        dest_path_buf.push(src_path.file_name().unwrap());
2993        tb.dest_path = dest_path_buf;
2994        let mut user = tb.test_user_from_cached_paths(0);
2995        tb.generic_transfer_init(&mut user, 0)
2996            .expect("transfer init failed");
2997        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
2998        tb.generic_eof_no_error(&mut user, Vec::new())
2999            .expect("EOF no error insertion failed");
3000        tb.check_completion_indication_success(&mut user);
3001    }
3002
3003    #[test]
3004    fn test_tranfer_cancellation_empty_file_with_eof_pdu() {
3005        let fault_handler = TestFaultHandler::default();
3006        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3007            fault_handler,
3008            TransmissionMode::Unacknowledged,
3009            false,
3010        );
3011        let mut user = tb.test_user_from_cached_paths(0);
3012        let transfer_info = tb
3013            .generic_transfer_init(&mut user, 0)
3014            .expect("transfer init failed");
3015        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3016        let pdu_header = PduHeader::new_for_file_directive(tb.pdu_conf, 0);
3017        let cancel_eof = EofPdu::new(
3018            pdu_header,
3019            ConditionCode::CancelRequestReceived,
3020            0,
3021            0,
3022            Some(EntityIdTlv::new(LOCAL_ID.into())),
3023        );
3024        let packets = tb
3025            .handler
3026            .state_machine(
3027                &mut user,
3028                Some(&PduRawWithInfo::new(&cancel_eof.to_vec().unwrap()).unwrap()),
3029            )
3030            .expect("state machine call with EOF insertion failed");
3031        assert_eq!(packets, 0);
3032        user.verify_finished_indication_retained(
3033            DeliveryCode::Complete,
3034            ConditionCode::CancelRequestReceived,
3035            transfer_info.id,
3036        );
3037    }
3038
3039    fn generic_tranfer_cancellation_partial_file_with_eof_pdu(
3040        with_closure: bool,
3041        insert_packet: bool,
3042    ) {
3043        let file_data_str = "Hello World!";
3044        let file_data = file_data_str.as_bytes();
3045        let file_size = 5;
3046
3047        let fault_handler = TestFaultHandler::default();
3048        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3049            fault_handler,
3050            TransmissionMode::Unacknowledged,
3051            with_closure,
3052        );
3053        let mut user = tb.test_user_from_cached_paths(file_size);
3054        let transfer_info = tb
3055            .generic_transfer_init(&mut user, file_size)
3056            .expect("transfer init failed");
3057        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3058        if insert_packet {
3059            tb.generic_file_data_insert(&mut user, 0, &file_data[0..5])
3060                .expect("file data insertion failed");
3061        }
3062        let mut digest = CRC_32.digest();
3063        digest.update(&file_data[0..5]);
3064        let checksum = digest.finalize();
3065        let pdu_header = PduHeader::new_for_file_directive(tb.pdu_conf, 0);
3066        let cancel_eof = EofPdu::new(
3067            pdu_header,
3068            ConditionCode::CancelRequestReceived,
3069            checksum,
3070            5,
3071            Some(EntityIdTlv::new(LOCAL_ID.into())),
3072        );
3073        let packets = tb
3074            .handler
3075            .state_machine(
3076                &mut user,
3077                Some(&PduRawWithInfo::new(&cancel_eof.to_vec().unwrap()).unwrap()),
3078            )
3079            .expect("state machine call with EOF insertion failed");
3080        if with_closure {
3081            assert_eq!(packets, 1);
3082            let finished_pdu = tb.handler.pdu_sender.retrieve_next_pdu().unwrap();
3083            assert_eq!(finished_pdu.pdu_type, PduType::FileDirective);
3084            assert_eq!(
3085                finished_pdu.file_directive_type.unwrap(),
3086                FileDirectiveType::FinishedPdu
3087            );
3088            let finished_pdu = FinishedPduReader::from_bytes(&finished_pdu.raw_pdu).unwrap();
3089            assert_eq!(
3090                finished_pdu.condition_code(),
3091                ConditionCode::CancelRequestReceived
3092            );
3093            if insert_packet {
3094                // Checksum success, so data is complete.
3095                assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
3096                user.verify_finished_indication_retained(
3097                    DeliveryCode::Complete,
3098                    ConditionCode::CancelRequestReceived,
3099                    transfer_info.id,
3100                );
3101            } else {
3102                // Checksum failure, so data is incomplete.
3103                assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Incomplete);
3104                tb.check_dest_file = false;
3105                let mut fault_hook = tb.handler.local_cfg.user_fault_hook().borrow_mut();
3106                let ignored_queue = &mut fault_hook.ignored_queue;
3107                let ignored = ignored_queue.pop_front().unwrap();
3108                assert_eq!(ignored.transaction_id(), transfer_info.id);
3109                assert_eq!(ignored.condition_code(), ConditionCode::FileChecksumFailure);
3110                assert_eq!(ignored.progress(), 5);
3111                user.verify_finished_indication_retained(
3112                    DeliveryCode::Incomplete,
3113                    ConditionCode::CancelRequestReceived,
3114                    transfer_info.id,
3115                );
3116            }
3117            assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
3118            assert_eq!(
3119                finished_pdu
3120                    .fault_location()
3121                    .expect("no fault location set"),
3122                EntityIdTlv::new(LOCAL_ID.into())
3123            );
3124        } else {
3125            user.verify_finished_indication_retained(
3126                DeliveryCode::Complete,
3127                ConditionCode::CancelRequestReceived,
3128                transfer_info.id,
3129            );
3130            assert_eq!(packets, 0);
3131        }
3132        tb.expected_file_size = file_size;
3133        tb.expected_full_data = file_data[0..file_size as usize].to_vec();
3134    }
3135
3136    #[test]
3137    fn test_tranfer_cancellation_partial_file_with_eof_pdu_no_closure_complete() {
3138        generic_tranfer_cancellation_partial_file_with_eof_pdu(false, true);
3139    }
3140
3141    #[test]
3142    fn test_tranfer_cancellation_partial_file_with_eof_pdu_with_closure_complete() {
3143        generic_tranfer_cancellation_partial_file_with_eof_pdu(true, true);
3144    }
3145
3146    #[test]
3147    fn test_tranfer_cancellation_partial_file_with_eof_pdu_with_closure_incomplete() {
3148        generic_tranfer_cancellation_partial_file_with_eof_pdu(true, false);
3149    }
3150
3151    #[test]
3152    fn test_tranfer_cancellation_empty_file_with_cancel_api() {
3153        let fault_handler = TestFaultHandler::default();
3154        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3155            fault_handler,
3156            TransmissionMode::Unacknowledged,
3157            false,
3158        );
3159        let mut user = tb.test_user_from_cached_paths(0);
3160        let transfer_info = tb
3161            .generic_transfer_init(&mut user, 0)
3162            .expect("transfer init failed");
3163        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3164        tb.handler.cancel_request(&transfer_info.id);
3165        let packets = tb
3166            .handler
3167            .state_machine_no_packet(&mut user)
3168            .expect("state machine call with EOF insertion failed");
3169        user.verify_finished_indication_retained(
3170            DeliveryCode::Complete,
3171            ConditionCode::CancelRequestReceived,
3172            transfer_info.id,
3173        );
3174        assert_eq!(packets, 0);
3175    }
3176
3177    #[test]
3178    fn test_tranfer_cancellation_empty_file_with_cancel_api_and_closure() {
3179        let fault_handler = TestFaultHandler::default();
3180        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3181            fault_handler,
3182            TransmissionMode::Unacknowledged,
3183            true,
3184        );
3185        let mut user = tb.test_user_from_cached_paths(0);
3186        let transfer_info = tb
3187            .generic_transfer_init(&mut user, 0)
3188            .expect("transfer init failed");
3189        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3190        tb.handler.cancel_request(&transfer_info.id);
3191        let packets = tb
3192            .handler
3193            .state_machine_no_packet(&mut user)
3194            .expect("state machine call with EOF insertion failed");
3195        assert_eq!(packets, 1);
3196        let next_pdu = tb.get_next_pdu().unwrap();
3197        assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
3198        assert_eq!(
3199            next_pdu.file_directive_type.unwrap(),
3200            FileDirectiveType::FinishedPdu
3201        );
3202        let finished_pdu =
3203            FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed");
3204        assert_eq!(
3205            finished_pdu.condition_code(),
3206            ConditionCode::CancelRequestReceived
3207        );
3208        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
3209        // Empty file, so still complete.
3210        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
3211        assert_eq!(
3212            finished_pdu.fault_location(),
3213            Some(EntityIdTlv::new(REMOTE_ID.into()))
3214        );
3215        user.verify_finished_indication_retained(
3216            DeliveryCode::Complete,
3217            ConditionCode::CancelRequestReceived,
3218            transfer_info.id,
3219        );
3220    }
3221
3222    #[test]
3223    fn test_tranfer_cancellation_partial_file_with_cancel_api_and_closure() {
3224        let file_data_str = "Hello World!";
3225        let file_data = file_data_str.as_bytes();
3226        let file_size = 5;
3227
3228        let fault_handler = TestFaultHandler::default();
3229        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3230            fault_handler,
3231            TransmissionMode::Unacknowledged,
3232            true,
3233        );
3234        let mut user = tb.test_user_from_cached_paths(file_size);
3235        let transfer_info = tb
3236            .generic_transfer_init(&mut user, file_size)
3237            .expect("transfer init failed");
3238        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3239        tb.generic_file_data_insert(&mut user, 0, &file_data[0..5])
3240            .expect("file data insertion failed");
3241
3242        tb.handler.cancel_request(&transfer_info.id);
3243        let packets = tb
3244            .handler
3245            .state_machine_no_packet(&mut user)
3246            .expect("state machine call with EOF insertion failed");
3247        assert_eq!(packets, 1);
3248        let next_pdu = tb.get_next_pdu().unwrap();
3249        assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
3250        assert_eq!(
3251            next_pdu.file_directive_type.unwrap(),
3252            FileDirectiveType::FinishedPdu
3253        );
3254        let finished_pdu =
3255            FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed");
3256        assert_eq!(
3257            finished_pdu.condition_code(),
3258            ConditionCode::CancelRequestReceived
3259        );
3260        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
3261        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Incomplete);
3262        assert_eq!(
3263            finished_pdu.fault_location(),
3264            Some(EntityIdTlv::new(REMOTE_ID.into()))
3265        );
3266        tb.expected_file_size = file_size;
3267        tb.expected_full_data = file_data[0..file_size as usize].to_vec();
3268        user.verify_finished_indication_retained(
3269            DeliveryCode::Incomplete,
3270            ConditionCode::CancelRequestReceived,
3271            transfer_info.id,
3272        );
3273    }
3274
3275    // Only incomplete received files will be removed.
3276    #[test]
3277    fn test_tranfer_cancellation_file_disposition_not_done_for_empty_file() {
3278        let fault_handler = TestFaultHandler::default();
3279        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3280            fault_handler,
3281            TransmissionMode::Unacknowledged,
3282            false,
3283        );
3284        let remote_cfg_mut = tb
3285            .handler
3286            .remote_cfg_table
3287            .get_mut(LOCAL_ID.value())
3288            .unwrap();
3289        remote_cfg_mut.disposition_on_cancellation = true;
3290        let mut user = tb.test_user_from_cached_paths(0);
3291        let transfer_info = tb
3292            .generic_transfer_init(&mut user, 0)
3293            .expect("transfer init failed");
3294        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3295
3296        tb.handler.cancel_request(&transfer_info.id);
3297        let packets = tb
3298            .handler
3299            .state_machine_no_packet(&mut user)
3300            .expect("state machine call with EOF insertion failed");
3301        assert_eq!(packets, 0);
3302        user.verify_finished_indication_retained(
3303            DeliveryCode::Complete,
3304            ConditionCode::CancelRequestReceived,
3305            transfer_info.id,
3306        );
3307    }
3308
3309    #[test]
3310    fn test_tranfer_cancellation_file_disposition_not_done_for_incomplete_file() {
3311        let file_data_str = "Hello World!";
3312        let file_data = file_data_str.as_bytes();
3313        let file_size = file_data.len() as u64;
3314
3315        let fault_handler = TestFaultHandler::default();
3316        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3317            fault_handler,
3318            TransmissionMode::Unacknowledged,
3319            false,
3320        );
3321        tb.check_dest_file = false;
3322        let remote_cfg_mut = tb
3323            .handler
3324            .remote_cfg_table
3325            .get_mut(LOCAL_ID.value())
3326            .unwrap();
3327        remote_cfg_mut.disposition_on_cancellation = true;
3328        let mut user = tb.test_user_from_cached_paths(file_size);
3329        let transfer_info = tb
3330            .generic_transfer_init(&mut user, file_size)
3331            .expect("transfer init failed");
3332        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3333        tb.generic_file_data_insert(&mut user, 0, &file_data[0..5])
3334            .expect("file data insertion failed");
3335
3336        tb.handler.cancel_request(&transfer_info.id);
3337        let packets = tb
3338            .handler
3339            .state_machine_no_packet(&mut user)
3340            .expect("state machine call with EOF insertion failed");
3341        assert_eq!(packets, 0);
3342        // File was disposed.
3343        assert!(!Path::exists(tb.dest_path()));
3344        assert_eq!(user.finished_indic_queue.len(), 1);
3345        user.verify_finished_indication(
3346            DeliveryCode::Incomplete,
3347            ConditionCode::CancelRequestReceived,
3348            transfer_info.id,
3349            FileStatus::DiscardDeliberately,
3350        );
3351    }
3352
3353    fn generic_test_immediate_nak_request(file_flag: LargeFileFlag) {
3354        let file_data_str = "Hello World!";
3355        let file_data = file_data_str.as_bytes();
3356        let file_size = file_data.len() as u64;
3357        let fault_handler = TestFaultHandler::default();
3358
3359        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3360            fault_handler,
3361            TransmissionMode::Acknowledged,
3362            false,
3363        );
3364        tb.set_large_file_flag(file_flag);
3365        tb.remote_cfg_mut().immediate_nak_mode = true;
3366        let mut user = tb.test_user_from_cached_paths(file_size);
3367        let transfer_info = tb
3368            .generic_transfer_init(&mut user, file_size)
3369            .expect("transfer init failed");
3370        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3371
3372        // This should immediately trigger a NAK request for file segment 0..4
3373        assert_eq!(
3374            tb.generic_file_data_insert(&mut user, 4, &file_data[4..])
3375                .expect("file data insertion failed"),
3376            1
3377        );
3378        assert_eq!(tb.pdu_queue_len(), 1);
3379        let pdu = tb.get_next_pdu().unwrap();
3380        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3381        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3382        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3383        assert_eq!(nak_pdu.pdu_header().common_pdu_conf().file_flag, file_flag);
3384        assert_eq!(nak_pdu.start_of_scope(), 0);
3385        assert_eq!(nak_pdu.end_of_scope(), file_size);
3386        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3387        assert_eq!(seg_reqs.len(), 1);
3388        assert_eq!(seg_reqs[0], (0, 4));
3389        // We simulate the reply by re-inserting the missing file segment.
3390        tb.generic_file_data_insert(&mut user, 0, &file_data[0..4])
3391            .expect("file data insertion failed");
3392
3393        tb.generic_eof_no_error(&mut user, file_data.to_vec())
3394            .expect("EOF no error insertion failed");
3395        tb.check_completion_indication_success(&mut user);
3396        assert_eq!(tb.pdu_queue_len(), 2);
3397        tb.check_eof_ack_pdu(ConditionCode::NoError);
3398        tb.check_finished_pdu_success();
3399        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3400    }
3401
3402    #[test]
3403    fn test_immediate_nak_request() {
3404        generic_test_immediate_nak_request(LargeFileFlag::Normal);
3405    }
3406
3407    #[test]
3408    fn test_immediate_nak_request_large_file() {
3409        generic_test_immediate_nak_request(LargeFileFlag::Large);
3410    }
3411
3412    fn generic_test_deferred_nak_request(file_flag: LargeFileFlag) {
3413        let file_data_str = "Hello World!";
3414        let file_data = file_data_str.as_bytes();
3415        let file_size = file_data.len() as u64;
3416        let fault_handler = TestFaultHandler::default();
3417
3418        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3419            fault_handler,
3420            TransmissionMode::Acknowledged,
3421            false,
3422        );
3423        tb.set_large_file_flag(file_flag);
3424        // Disable this, we only want to check the deferred procedure.
3425        tb.remote_cfg_mut().immediate_nak_mode = false;
3426        let mut user = tb.test_user_from_cached_paths(file_size);
3427        let transfer_info = tb
3428            .generic_transfer_init(&mut user, file_size)
3429            .expect("transfer init failed");
3430        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3431
3432        assert_eq!(
3433            tb.generic_file_data_insert(&mut user, 4, &file_data[4..])
3434                .expect("file data insertion failed"),
3435            0
3436        );
3437        assert_eq!(
3438            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3439                .expect("EOF no error insertion failed"),
3440            2
3441        );
3442        assert_eq!(tb.pdu_queue_len(), 2);
3443        tb.check_eof_ack_pdu(ConditionCode::NoError);
3444        assert_eq!(tb.pdu_queue_len(), 1);
3445        let pdu = tb.get_next_pdu().unwrap();
3446        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3447        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3448        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3449        assert_eq!(nak_pdu.start_of_scope(), 0);
3450        assert_eq!(nak_pdu.end_of_scope(), file_size);
3451        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3452        assert_eq!(seg_reqs.len(), 1);
3453        assert_eq!(seg_reqs[0], (0, 4));
3454
3455        // We simulate the reply by re-inserting the missing file segment.
3456        tb.generic_file_data_insert(&mut user, 0, &file_data[0..4])
3457            .expect("file data insertion failed");
3458        tb.check_completion_indication_success(&mut user);
3459        assert_eq!(tb.pdu_queue_len(), 1);
3460        tb.check_finished_pdu_success();
3461        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3462    }
3463
3464    #[test]
3465    fn test_deferred_nak_request() {
3466        generic_test_deferred_nak_request(LargeFileFlag::Normal);
3467    }
3468
3469    #[test]
3470    fn test_deferred_nak_request_large() {
3471        generic_test_deferred_nak_request(LargeFileFlag::Large);
3472    }
3473
3474    #[test]
3475    fn test_file_data_before_metadata() {
3476        let file_data_str = "Hello World!";
3477        let file_data = file_data_str.as_bytes();
3478        let file_size = file_data.len() as u64;
3479        let fault_handler = TestFaultHandler::default();
3480
3481        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3482            fault_handler,
3483            TransmissionMode::Acknowledged,
3484            false,
3485        );
3486        tb.remote_cfg_mut().immediate_nak_mode = true;
3487        let mut user = tb.test_user_from_cached_paths(file_size);
3488        assert_eq!(
3489            tb.generic_file_data_insert(&mut user, 0, file_data)
3490                .expect("file data insertion failed"),
3491            1
3492        );
3493        tb.state_check(State::Busy, TransactionStep::WaitingForMetadata);
3494        assert_eq!(tb.pdu_queue_len(), 1);
3495        let pdu = tb.get_next_pdu().unwrap();
3496        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3497        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3498        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3499        assert_eq!(nak_pdu.start_of_scope(), 0);
3500        assert_eq!(nak_pdu.end_of_scope(), file_size);
3501        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3502        assert_eq!(seg_reqs.len(), 2);
3503        // Metadata is re-requested.
3504        assert_eq!(seg_reqs[0], (0, 0));
3505        // File data is re-requested. The destination handler can not do anything with the file
3506        // data as long as no metadata has been received. Buffering of FD PDUs is not supported.
3507        assert_eq!(seg_reqs[1], (0, 12));
3508
3509        let transfer_info = tb
3510            .generic_transfer_init(&mut user, file_size)
3511            .expect("transfer init failed");
3512        // Re-insert missing file data.
3513        assert_eq!(
3514            tb.generic_file_data_insert(&mut user, 0, file_data)
3515                .expect("file data insertion failed"),
3516            0
3517        );
3518        assert_eq!(
3519            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3520                .expect("EOF no error insertion failed"),
3521            2
3522        );
3523        tb.check_completion_indication_success(&mut user);
3524        assert_eq!(tb.pdu_queue_len(), 2);
3525        tb.check_eof_ack_pdu(ConditionCode::NoError);
3526        tb.check_finished_pdu_success();
3527        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3528    }
3529
3530    #[test]
3531    fn test_eof_before_metadata() {
3532        let file_data_str = "Hello World!";
3533        let file_data = file_data_str.as_bytes();
3534        let file_size = file_data.len() as u64;
3535        let fault_handler = TestFaultHandler::default();
3536
3537        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3538            fault_handler,
3539            TransmissionMode::Acknowledged,
3540            false,
3541        );
3542        tb.remote_cfg_mut().immediate_nak_mode = true;
3543        let mut user = tb.test_user_from_cached_paths(file_size);
3544        // We expect the ACK for the EOF and a NAK packet re-requesting the metadata and file data.
3545        assert_eq!(
3546            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3547                .expect("file data insertion failed"),
3548            2
3549        );
3550        tb.state_check(State::Busy, TransactionStep::WaitingForMetadata);
3551        assert_eq!(tb.pdu_queue_len(), 2);
3552        tb.check_eof_ack_pdu(ConditionCode::NoError);
3553        let pdu = tb.get_next_pdu().unwrap();
3554        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3555        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3556        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3557        assert_eq!(nak_pdu.start_of_scope(), 0);
3558        assert_eq!(nak_pdu.end_of_scope(), file_size);
3559        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3560        assert_eq!(seg_reqs.len(), 2);
3561        // Metadata is re-requested.
3562        assert_eq!(seg_reqs[0], (0, 0));
3563        // File data is re-requested. The destination handler can not do anything with the file
3564        // data as long as no metadata has been received. Buffering of FD PDUs is not supported.
3565        assert_eq!(seg_reqs[1], (0, 12));
3566
3567        let transfer_info = tb
3568            .generic_transfer_init(&mut user, file_size)
3569            .expect("transfer init failed");
3570        // Re-insert missing file data.
3571        assert_eq!(
3572            tb.generic_file_data_insert(&mut user, 0, file_data)
3573                .expect("file data insertion failed"),
3574            1
3575        );
3576        tb.check_completion_indication_success(&mut user);
3577        assert_eq!(tb.pdu_queue_len(), 1);
3578        tb.check_finished_pdu_success();
3579        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3580    }
3581
3582    #[test]
3583    fn test_nak_limit_reached() {
3584        let file_data_str = "Hello World!";
3585        let file_data = file_data_str.as_bytes();
3586        let file_size = file_data.len() as u64;
3587        let fault_handler = TestFaultHandler::default();
3588
3589        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3590            fault_handler,
3591            TransmissionMode::Acknowledged,
3592            false,
3593        );
3594        // Disable this, we only want to check the deferred procedure.
3595        tb.remote_cfg_mut().immediate_nak_mode = false;
3596        let mut user = tb.test_user_from_cached_paths(file_size);
3597        let transfer_info = tb
3598            .generic_transfer_init(&mut user, file_size)
3599            .expect("transfer init failed");
3600        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3601
3602        assert_eq!(
3603            tb.generic_file_data_insert(&mut user, 4, &file_data[4..])
3604                .expect("file data insertion failed"),
3605            0
3606        );
3607        assert_eq!(
3608            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3609                .expect("EOF no error insertion failed"),
3610            2
3611        );
3612        assert_eq!(tb.pdu_queue_len(), 2);
3613        tb.check_eof_ack_pdu(ConditionCode::NoError);
3614        assert_eq!(tb.pdu_queue_len(), 1);
3615        let pdu = tb.get_next_pdu().unwrap();
3616        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3617        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3618        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3619        assert_eq!(nak_pdu.start_of_scope(), 0);
3620        assert_eq!(nak_pdu.end_of_scope(), file_size);
3621        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3622        assert_eq!(seg_reqs.len(), 1);
3623        assert_eq!(seg_reqs[0], (0, 4));
3624
3625        // Let the NAK timer expire
3626        tb.set_nak_activity_timer_expired();
3627        assert_eq!(tb.handler.state_machine_no_packet(&mut user).unwrap(), 1);
3628        assert_eq!(tb.pdu_queue_len(), 1);
3629        let pdu = tb.get_next_pdu().unwrap();
3630        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3631        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3632        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3633        assert_eq!(nak_pdu.start_of_scope(), 0);
3634        assert_eq!(nak_pdu.end_of_scope(), file_size);
3635        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3636        assert_eq!(seg_reqs.len(), 1);
3637        assert_eq!(seg_reqs[0], (0, 4));
3638
3639        // Let the NAK timer expire again.
3640        tb.set_nak_activity_timer_expired();
3641        assert_eq!(tb.handler.state_machine_no_packet(&mut user).unwrap(), 1);
3642        assert_eq!(tb.pdu_queue_len(), 1);
3643        tb.check_completion_indication_failure(
3644            &mut user,
3645            ConditionCode::NakLimitReached,
3646            FileStatus::Retained,
3647            DeliveryCode::Incomplete,
3648        );
3649        tb.check_finished_pdu_failure(
3650            ConditionCode::NakLimitReached,
3651            FileStatus::Retained,
3652            DeliveryCode::Incomplete,
3653        );
3654
3655        {
3656            let mut fault_hook = tb.fault_handler().user_hook.borrow_mut();
3657            assert_eq!(fault_hook.notice_of_cancellation_queue.len(), 1);
3658            let cancellation = fault_hook.notice_of_cancellation_queue.pop_front().unwrap();
3659            assert_eq!(cancellation.transaction_id(), transfer_info.id);
3660            assert_eq!(
3661                cancellation.condition_code(),
3662                ConditionCode::NakLimitReached
3663            );
3664            assert_eq!(cancellation.progress(), file_size);
3665        }
3666        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3667        tb.check_dest_file = false;
3668    }
3669
3670    #[test]
3671    fn test_positive_ack_procedure() {
3672        let fault_handler = TestFaultHandler::default();
3673        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3674            fault_handler,
3675            TransmissionMode::Acknowledged,
3676            false,
3677        );
3678        let mut user = tb.test_user_from_cached_paths(0);
3679        let transfer_info = tb
3680            .generic_transfer_init(&mut user, 0)
3681            .expect("transfer init failed");
3682        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3683        tb.generic_eof_no_error(&mut user, Vec::new())
3684            .expect("EOF no error insertion failed");
3685        tb.check_completion_indication_success(&mut user);
3686        assert_eq!(tb.pdu_queue_len(), 2);
3687        tb.check_eof_ack_pdu(ConditionCode::NoError);
3688        tb.check_finished_pdu_success();
3689
3690        tb.set_positive_ack_expired();
3691        // This should cause the PDU to be sent again.
3692        assert_eq!(tb.handler.state_machine_no_packet(&mut user).unwrap(), 1);
3693        tb.check_finished_pdu_success();
3694        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3695    }
3696
3697    fn generic_positive_ack_test(
3698        tb: &mut DestHandlerTestbench,
3699        user: &mut TestCfdpUser,
3700    ) -> TransferInfo {
3701        let transfer_info = tb
3702            .generic_transfer_init(user, 0)
3703            .expect("transfer init failed");
3704        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3705        tb.generic_eof_no_error(user, Vec::new())
3706            .expect("EOF no error insertion failed");
3707        tb.check_completion_indication_success(user);
3708        assert_eq!(tb.pdu_queue_len(), 2);
3709        tb.check_eof_ack_pdu(ConditionCode::NoError);
3710        tb.check_finished_pdu_success();
3711
3712        // This should cause the PDU to be sent again.
3713        tb.set_positive_ack_expired();
3714        assert_eq!(tb.handler.state_machine_no_packet(user).unwrap(), 1);
3715        tb.check_finished_pdu_success();
3716
3717        // Positive ACK limit reached.
3718        tb.set_positive_ack_expired();
3719        assert_eq!(tb.handler.state_machine_no_packet(user).unwrap(), 1);
3720
3721        tb.check_finished_pdu_failure(
3722            ConditionCode::PositiveAckLimitReached,
3723            FileStatus::Retained,
3724            DeliveryCode::Complete,
3725        );
3726        tb.check_completion_indication_failure(
3727            user,
3728            ConditionCode::PositiveAckLimitReached,
3729            FileStatus::Retained,
3730            DeliveryCode::Complete,
3731        );
3732        {
3733            let mut fault_handler = tb.fault_handler().user_hook.borrow_mut();
3734            assert!(!fault_handler.cancellation_queue_empty());
3735            let cancellation = fault_handler
3736                .notice_of_cancellation_queue
3737                .pop_front()
3738                .unwrap();
3739            assert_eq!(cancellation.transaction_id(), transfer_info.id);
3740            assert_eq!(
3741                cancellation.condition_code(),
3742                ConditionCode::PositiveAckLimitReached
3743            );
3744            assert_eq!(cancellation.progress(), 0);
3745        }
3746        transfer_info
3747    }
3748
3749    #[test]
3750    fn test_positive_ack_limit_reached() {
3751        let fault_handler = TestFaultHandler::default();
3752        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3753            fault_handler,
3754            TransmissionMode::Acknowledged,
3755            false,
3756        );
3757        let mut user = tb.test_user_from_cached_paths(0);
3758        let transfer_info = generic_positive_ack_test(&mut tb, &mut user);
3759        // Chances are that this one won't work either leading to transfer abandonment, but we
3760        // acknowledge it here
3761        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3762    }
3763
3764    #[test]
3765    fn test_positive_ack_limit_reached_with_subsequent_abandonment() {
3766        let fault_handler = TestFaultHandler::default();
3767        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3768            fault_handler,
3769            TransmissionMode::Acknowledged,
3770            false,
3771        );
3772        let mut user = tb.test_user_from_cached_paths(0);
3773        let transfer_info = generic_positive_ack_test(&mut tb, &mut user);
3774
3775        // This should cause the PDU to be sent again.
3776        tb.set_positive_ack_expired();
3777        assert_eq!(tb.handler.state_machine_no_packet(&mut user).unwrap(), 1);
3778        tb.check_finished_pdu_failure(
3779            ConditionCode::PositiveAckLimitReached,
3780            FileStatus::Retained,
3781            DeliveryCode::Complete,
3782        );
3783
3784        // Postive ACK limit reached which leads to abandonment.
3785        tb.set_positive_ack_expired();
3786        assert_eq!(tb.handler.state_machine_no_packet(&mut user).unwrap(), 0);
3787        {
3788            let mut fault_handler = tb.fault_handler().user_hook.borrow_mut();
3789            assert!(!fault_handler.abandoned_queue_empty());
3790            let cancellation = fault_handler.abandoned_queue.pop_front().unwrap();
3791            assert_eq!(cancellation.transaction_id(), transfer_info.id);
3792            assert_eq!(
3793                cancellation.condition_code(),
3794                ConditionCode::PositiveAckLimitReached
3795            );
3796            assert_eq!(cancellation.progress(), 0);
3797        }
3798    }
3799
3800    #[test]
3801    fn test_multi_segment_nak() {
3802        let file_data_str = "Hello Wooorld!";
3803        let file_data = file_data_str.as_bytes();
3804        let file_size = file_data.len() as u64;
3805        let fault_handler = TestFaultHandler::default();
3806
3807        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3808            fault_handler,
3809            TransmissionMode::Acknowledged,
3810            false,
3811        );
3812        // Disable this, we only want to check the deferred procedure.
3813        tb.remote_cfg_mut().immediate_nak_mode = false;
3814        let mut user = tb.test_user_from_cached_paths(file_size);
3815        let transfer_info = tb
3816            .generic_transfer_init(&mut user, file_size)
3817            .expect("transfer init failed");
3818        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3819
3820        assert_eq!(
3821            tb.generic_file_data_insert(&mut user, 2, &file_data[2..4])
3822                .expect("file data insertion failed"),
3823            0
3824        );
3825        assert_eq!(
3826            tb.generic_file_data_insert(&mut user, 6, &file_data[6..8])
3827                .expect("file data insertion failed"),
3828            0
3829        );
3830        assert_eq!(
3831            tb.generic_file_data_insert(&mut user, 10, &file_data[10..])
3832                .expect("file data insertion failed"),
3833            0
3834        );
3835        assert_eq!(
3836            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3837                .expect("EOF no error insertion failed"),
3838            2
3839        );
3840        assert_eq!(tb.pdu_queue_len(), 2);
3841        tb.check_eof_ack_pdu(ConditionCode::NoError);
3842        assert_eq!(tb.pdu_queue_len(), 1);
3843        let pdu = tb.get_next_pdu().unwrap();
3844        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3845        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3846        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3847        assert_eq!(nak_pdu.start_of_scope(), 0);
3848        assert_eq!(nak_pdu.end_of_scope(), file_size);
3849        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3850        assert_eq!(seg_reqs.len(), 3);
3851        assert_eq!(seg_reqs[0], (0, 2));
3852        assert_eq!(seg_reqs[1], (4, 6));
3853        assert_eq!(seg_reqs[2], (8, 10));
3854
3855        // We simulate the reply by re-inserting the missing file segment.
3856        tb.generic_file_data_insert(&mut user, 0, &file_data[0..2])
3857            .expect("file data insertion failed");
3858        tb.generic_file_data_insert(&mut user, 4, &file_data[4..6])
3859            .expect("file data insertion failed");
3860        tb.generic_file_data_insert(&mut user, 8, &file_data[8..10])
3861            .expect("file data insertion failed");
3862        tb.check_completion_indication_success(&mut user);
3863        assert_eq!(tb.pdu_queue_len(), 1);
3864        tb.check_finished_pdu_success();
3865        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3866    }
3867
3868    #[test]
3869    fn test_multi_packet_nak_sequence_large_file_flag() {
3870        let file_data = [0_u8; 64];
3871        let file_size = file_data.len() as u64;
3872        let fault_handler = TestFaultHandler::default();
3873
3874        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3875            fault_handler,
3876            TransmissionMode::Acknowledged,
3877            false,
3878        );
3879        // Disable this, we only want to check the deferred procedure.
3880        tb.remote_cfg_mut().immediate_nak_mode = false;
3881        let max_packet_len = 80;
3882        tb.remote_cfg_mut().max_packet_len = max_packet_len;
3883        tb.set_large_file_flag(LargeFileFlag::Large);
3884        let mut user = tb.test_user_from_cached_paths(file_size);
3885        let transfer_info = tb
3886            .generic_transfer_init(&mut user, file_size)
3887            .expect("transfer init failed");
3888        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3889
3890        assert_eq!(
3891            NakPduCreatorWithReservedSeqReqsBuf::calculate_max_segment_requests(
3892                max_packet_len,
3893                &PduHeader::new_for_file_directive(tb.pdu_conf, 0),
3894            )
3895            .unwrap(),
3896            3
3897        );
3898        let missing_segs_0: &[(u64, u64)] = &[(0, 2), (4, 6), (8, 10)];
3899        let missing_segs_1: &[(u64, u64)] = &[(12, 14)];
3900        assert_eq!(
3901            tb.generic_file_data_insert(&mut user, 2, &file_data[2..4])
3902                .expect("file data insertion failed"),
3903            0
3904        );
3905        assert_eq!(
3906            tb.generic_file_data_insert(&mut user, 6, &file_data[6..8])
3907                .expect("file data insertion failed"),
3908            0
3909        );
3910        assert_eq!(
3911            tb.generic_file_data_insert(&mut user, 10, &file_data[10..12])
3912                .expect("file data insertion failed"),
3913            0
3914        );
3915        assert_eq!(
3916            tb.generic_file_data_insert(&mut user, 14, &file_data[14..file_size as usize])
3917                .expect("file data insertion failed"),
3918            0
3919        );
3920        assert_eq!(
3921            tb.generic_eof_no_error(&mut user, file_data.to_vec())
3922                .expect("EOF no error insertion failed"),
3923            3
3924        );
3925        assert_eq!(tb.pdu_queue_len(), 3);
3926        tb.check_eof_ack_pdu(ConditionCode::NoError);
3927        let pdu = tb.get_next_pdu().unwrap();
3928        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3929        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3930        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3931        assert_eq!(nak_pdu.start_of_scope(), 0);
3932        assert_eq!(nak_pdu.end_of_scope(), 10);
3933        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3934        assert_eq!(seg_reqs, missing_segs_0);
3935
3936        let pdu = tb.get_next_pdu().unwrap();
3937        assert_eq!(pdu.pdu_type, PduType::FileDirective);
3938        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
3939        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
3940        assert_eq!(nak_pdu.start_of_scope(), 10);
3941        assert_eq!(nak_pdu.end_of_scope(), file_size);
3942        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
3943        assert_eq!(seg_reqs, missing_segs_1);
3944
3945        for missing_seg in missing_segs_0 {
3946            assert_eq!(
3947                tb.generic_file_data_insert(
3948                    &mut user,
3949                    missing_seg.0,
3950                    &file_data[missing_seg.0 as usize..missing_seg.1 as usize]
3951                )
3952                .expect("file data insertion failed"),
3953                0
3954            );
3955        }
3956        for missing_seg in missing_segs_1 {
3957            assert_eq!(
3958                tb.generic_file_data_insert(
3959                    &mut user,
3960                    missing_seg.0,
3961                    &file_data[missing_seg.0 as usize..missing_seg.1 as usize]
3962                )
3963                .expect("file data insertion failed"),
3964                1
3965            );
3966        }
3967
3968        tb.check_completion_indication_success(&mut user);
3969        assert_eq!(tb.pdu_queue_len(), 1);
3970        tb.check_finished_pdu_success();
3971        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
3972    }
3973
3974    #[test]
3975    fn test_multi_packet_nak_sequence_normal_file_flag() {
3976        let file_data = [0_u8; 64];
3977        let file_size = file_data.len() as u64;
3978        let fault_handler = TestFaultHandler::default();
3979
3980        let mut tb = DestHandlerTestbench::new_with_fixed_paths(
3981            fault_handler,
3982            TransmissionMode::Acknowledged,
3983            false,
3984        );
3985        // Disable this, we only want to check the deferred procedure.
3986        tb.remote_cfg_mut().immediate_nak_mode = false;
3987        let max_packet_len = 50;
3988        tb.remote_cfg_mut().max_packet_len = max_packet_len;
3989        let mut user = tb.test_user_from_cached_paths(file_size);
3990        let transfer_info = tb
3991            .generic_transfer_init(&mut user, file_size)
3992            .expect("transfer init failed");
3993        tb.state_check(State::Busy, TransactionStep::ReceivingFileDataPdus);
3994
3995        assert_eq!(
3996            NakPduCreatorWithReservedSeqReqsBuf::calculate_max_segment_requests(
3997                max_packet_len,
3998                &PduHeader::new_for_file_directive(tb.pdu_conf, 0),
3999            )
4000            .unwrap(),
4001            4
4002        );
4003        let missing_segs_0: &[(u64, u64)] = &[(0, 2), (4, 6), (8, 10), (12, 14)];
4004        let missing_segs_1: &[(u64, u64)] = &[(16, 18)];
4005        assert_eq!(
4006            tb.generic_file_data_insert(&mut user, 2, &file_data[2..4])
4007                .expect("file data insertion failed"),
4008            0
4009        );
4010        assert_eq!(
4011            tb.generic_file_data_insert(&mut user, 6, &file_data[6..8])
4012                .expect("file data insertion failed"),
4013            0
4014        );
4015        assert_eq!(
4016            tb.generic_file_data_insert(&mut user, 10, &file_data[10..12])
4017                .expect("file data insertion failed"),
4018            0
4019        );
4020        assert_eq!(
4021            tb.generic_file_data_insert(&mut user, 14, &file_data[14..16])
4022                .expect("file data insertion failed"),
4023            0
4024        );
4025        assert_eq!(
4026            tb.generic_file_data_insert(&mut user, 18, &file_data[18..file_size as usize])
4027                .expect("file data insertion failed"),
4028            0
4029        );
4030        assert_eq!(
4031            tb.generic_eof_no_error(&mut user, file_data.to_vec())
4032                .expect("EOF no error insertion failed"),
4033            3
4034        );
4035        assert_eq!(tb.pdu_queue_len(), 3);
4036        tb.check_eof_ack_pdu(ConditionCode::NoError);
4037        let pdu = tb.get_next_pdu().unwrap();
4038        assert_eq!(pdu.pdu_type, PduType::FileDirective);
4039        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
4040        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
4041        assert_eq!(nak_pdu.start_of_scope(), 0);
4042        assert_eq!(nak_pdu.end_of_scope(), 14);
4043        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
4044        assert_eq!(seg_reqs, missing_segs_0);
4045
4046        let pdu = tb.get_next_pdu().unwrap();
4047        assert_eq!(pdu.pdu_type, PduType::FileDirective);
4048        assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu);
4049        let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
4050        assert_eq!(nak_pdu.start_of_scope(), 14);
4051        assert_eq!(nak_pdu.end_of_scope(), file_size);
4052        let seg_reqs: Vec<(u64, u64)> = nak_pdu.get_segment_requests_iterator().unwrap().collect();
4053        assert_eq!(seg_reqs, missing_segs_1);
4054
4055        for missing_seg in missing_segs_0 {
4056            assert_eq!(
4057                tb.generic_file_data_insert(
4058                    &mut user,
4059                    missing_seg.0,
4060                    &file_data[missing_seg.0 as usize..missing_seg.1 as usize]
4061                )
4062                .expect("file data insertion failed"),
4063                0
4064            );
4065        }
4066        for missing_seg in missing_segs_1 {
4067            assert_eq!(
4068                tb.generic_file_data_insert(
4069                    &mut user,
4070                    missing_seg.0,
4071                    &file_data[missing_seg.0 as usize..missing_seg.1 as usize]
4072                )
4073                .expect("file data insertion failed"),
4074                1
4075            );
4076        }
4077
4078        tb.check_completion_indication_success(&mut user);
4079        assert_eq!(tb.pdu_queue_len(), 1);
4080        tb.check_finished_pdu_success();
4081        tb.acknowledge_finished_pdu(&mut user, &transfer_info);
4082    }
4083}