satrs_core/cfdp/
filestore.rs

1use alloc::string::{String, ToString};
2use core::fmt::Display;
3use crc::{Crc, CRC_32_CKSUM};
4use spacepackets::cfdp::ChecksumType;
5use spacepackets::ByteConversionError;
6#[cfg(feature = "std")]
7use std::error::Error;
8use std::path::Path;
9#[cfg(feature = "std")]
10pub use stdmod::*;
11
12pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
13
14#[derive(Debug, Clone)]
15pub enum FilestoreError {
16    FileDoesNotExist,
17    FileAlreadyExists,
18    DirDoesNotExist,
19    Permission,
20    IsNotFile,
21    IsNotDirectory,
22    ByteConversion(ByteConversionError),
23    Io {
24        raw_errno: Option<i32>,
25        string: String,
26    },
27    ChecksumTypeNotImplemented(ChecksumType),
28}
29
30impl From<ByteConversionError> for FilestoreError {
31    fn from(value: ByteConversionError) -> Self {
32        Self::ByteConversion(value)
33    }
34}
35
36impl Display for FilestoreError {
37    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        match self {
39            FilestoreError::FileDoesNotExist => {
40                write!(f, "file does not exist")
41            }
42            FilestoreError::FileAlreadyExists => {
43                write!(f, "file already exists")
44            }
45            FilestoreError::DirDoesNotExist => {
46                write!(f, "directory does not exist")
47            }
48            FilestoreError::Permission => {
49                write!(f, "permission error")
50            }
51            FilestoreError::IsNotFile => {
52                write!(f, "is not a file")
53            }
54            FilestoreError::IsNotDirectory => {
55                write!(f, "is not a directory")
56            }
57            FilestoreError::ByteConversion(e) => {
58                write!(f, "filestore error: {e}")
59            }
60            FilestoreError::Io { raw_errno, string } => {
61                write!(
62                    f,
63                    "filestore generic IO error with raw errno {:?}: {}",
64                    raw_errno, string
65                )
66            }
67            FilestoreError::ChecksumTypeNotImplemented(checksum_type) => {
68                write!(f, "checksum {:?} not implemented", checksum_type)
69            }
70        }
71    }
72}
73
74impl Error for FilestoreError {
75    fn source(&self) -> Option<&(dyn Error + 'static)> {
76        match self {
77            FilestoreError::ByteConversion(e) => Some(e),
78            _ => None,
79        }
80    }
81}
82
83#[cfg(feature = "std")]
84impl From<std::io::Error> for FilestoreError {
85    fn from(value: std::io::Error) -> Self {
86        Self::Io {
87            raw_errno: value.raw_os_error(),
88            string: value.to_string(),
89        }
90    }
91}
92
93pub trait VirtualFilestore {
94    fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
95
96    fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError>;
97
98    /// Truncating a file means deleting all its data so the resulting file is empty.
99    /// This can be more efficient than removing and re-creating a file.
100    fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError>;
101
102    fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError>;
103    fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError>;
104
105    fn read_data(
106        &self,
107        file_path: &str,
108        offset: u64,
109        read_len: u64,
110        buf: &mut [u8],
111    ) -> Result<(), FilestoreError>;
112
113    fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError>;
114
115    fn filename_from_full_path(path: &str) -> Option<&str>
116    where
117        Self: Sized,
118    {
119        // Convert the path string to a Path
120        let path = Path::new(path);
121
122        // Extract the file name using the file_name() method
123        path.file_name().and_then(|name| name.to_str())
124    }
125
126    fn is_file(&self, path: &str) -> bool;
127
128    fn is_dir(&self, path: &str) -> bool {
129        !self.is_file(path)
130    }
131
132    fn exists(&self, path: &str) -> bool;
133
134    /// This special function is the CFDP specific abstraction to verify the checksum of a file.
135    /// This allows to keep OS specific details like reading the whole file in the most efficient
136    /// manner inside the file system abstraction.
137    ///
138    /// The passed verification buffer argument will be used by the specific implementation as
139    /// a buffer to read the file into. It is recommended to use common buffer sizes like
140    /// 4096 or 8192 bytes.
141    fn checksum_verify(
142        &self,
143        file_path: &str,
144        checksum_type: ChecksumType,
145        expected_checksum: u32,
146        verification_buf: &mut [u8],
147    ) -> Result<bool, FilestoreError>;
148}
149
150#[cfg(feature = "std")]
151pub mod stdmod {
152    use super::*;
153    use std::{
154        fs::{self, File, OpenOptions},
155        io::{BufReader, Read, Seek, SeekFrom, Write},
156        path::Path,
157    };
158
159    #[derive(Default)]
160    pub struct NativeFilestore {}
161
162    impl VirtualFilestore for NativeFilestore {
163        fn create_file(&self, file_path: &str) -> Result<(), FilestoreError> {
164            if self.exists(file_path) {
165                return Err(FilestoreError::FileAlreadyExists);
166            }
167            File::create(file_path)?;
168            Ok(())
169        }
170
171        fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError> {
172            if !self.exists(file_path) {
173                return Err(FilestoreError::FileDoesNotExist);
174            }
175            if !self.is_file(file_path) {
176                return Err(FilestoreError::IsNotFile);
177            }
178            fs::remove_file(file_path)?;
179            Ok(())
180        }
181
182        fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError> {
183            if !self.exists(file_path) {
184                return Err(FilestoreError::FileDoesNotExist);
185            }
186            if !self.is_file(file_path) {
187                return Err(FilestoreError::IsNotFile);
188            }
189            OpenOptions::new()
190                .write(true)
191                .truncate(true)
192                .open(file_path)?;
193            Ok(())
194        }
195
196        fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> {
197            fs::create_dir(dir_path).map_err(|e| FilestoreError::Io {
198                raw_errno: e.raw_os_error(),
199                string: e.to_string(),
200            })?;
201            Ok(())
202        }
203
204        fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError> {
205            if !self.exists(dir_path) {
206                return Err(FilestoreError::DirDoesNotExist);
207            }
208            if !self.is_dir(dir_path) {
209                return Err(FilestoreError::IsNotDirectory);
210            }
211            if !all {
212                fs::remove_dir(dir_path)?;
213                return Ok(());
214            }
215            fs::remove_dir_all(dir_path)?;
216            Ok(())
217        }
218
219        fn read_data(
220            &self,
221            file_name: &str,
222            offset: u64,
223            read_len: u64,
224            buf: &mut [u8],
225        ) -> Result<(), FilestoreError> {
226            if buf.len() < read_len as usize {
227                return Err(ByteConversionError::ToSliceTooSmall {
228                    found: buf.len(),
229                    expected: read_len as usize,
230                }
231                .into());
232            }
233            if !self.exists(file_name) {
234                return Err(FilestoreError::FileDoesNotExist);
235            }
236            if !self.is_file(file_name) {
237                return Err(FilestoreError::IsNotFile);
238            }
239            let mut file = File::open(file_name)?;
240            file.seek(SeekFrom::Start(offset))?;
241            file.read_exact(&mut buf[0..read_len as usize])?;
242            Ok(())
243        }
244
245        fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError> {
246            if !self.exists(file) {
247                return Err(FilestoreError::FileDoesNotExist);
248            }
249            if !self.is_file(file) {
250                return Err(FilestoreError::IsNotFile);
251            }
252            let mut file = OpenOptions::new().write(true).open(file)?;
253            file.seek(SeekFrom::Start(offset))?;
254            file.write_all(buf)?;
255            Ok(())
256        }
257
258        fn is_file(&self, path: &str) -> bool {
259            let path = Path::new(path);
260            path.is_file()
261        }
262
263        fn exists(&self, path: &str) -> bool {
264            let path = Path::new(path);
265            if !path.exists() {
266                return false;
267            }
268            true
269        }
270
271        fn checksum_verify(
272            &self,
273            file_path: &str,
274            checksum_type: ChecksumType,
275            expected_checksum: u32,
276            verification_buf: &mut [u8],
277        ) -> Result<bool, FilestoreError> {
278            match checksum_type {
279                ChecksumType::Modular => {
280                    if self.calc_modular_checksum(file_path)? == expected_checksum {
281                        return Ok(true);
282                    }
283                    Ok(false)
284                }
285                ChecksumType::Crc32 => {
286                    let mut digest = CRC_32.digest();
287                    let file_to_check = File::open(file_path)?;
288                    let mut buf_reader = BufReader::new(file_to_check);
289                    loop {
290                        let bytes_read = buf_reader.read(verification_buf)?;
291                        if bytes_read == 0 {
292                            break;
293                        }
294                        digest.update(&verification_buf[0..bytes_read]);
295                    }
296                    if digest.finalize() == expected_checksum {
297                        return Ok(true);
298                    }
299                    Ok(false)
300                }
301                ChecksumType::NullChecksum => Ok(true),
302                _ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
303            }
304        }
305    }
306
307    impl NativeFilestore {
308        pub fn calc_modular_checksum(&self, file_path: &str) -> Result<u32, FilestoreError> {
309            let mut checksum: u32 = 0;
310            let file = File::open(file_path)?;
311            let mut buf_reader = BufReader::new(file);
312            let mut buffer = [0; 4];
313
314            loop {
315                let bytes_read = buf_reader.read(&mut buffer)?;
316                if bytes_read == 0 {
317                    break;
318                }
319                // Perform padding directly in the buffer
320                (bytes_read..4).for_each(|i| {
321                    buffer[i] = 0;
322                });
323
324                checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
325            }
326            Ok(checksum)
327        }
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use std::{fs, path::Path, println};
334
335    use super::*;
336    use alloc::format;
337    use tempfile::tempdir;
338
339    const EXAMPLE_DATA_CFDP: [u8; 15] = [
340        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
341    ];
342
343    const NATIVE_FS: NativeFilestore = NativeFilestore {};
344
345    #[test]
346    fn test_basic_native_filestore_create() {
347        let tmpdir = tempdir().expect("creating tmpdir failed");
348        let file_path = tmpdir.path().join("test.txt");
349        let result =
350            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
351        assert!(result.is_ok());
352        let path = Path::new(&file_path);
353        assert!(path.exists());
354        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
355        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
356    }
357
358    #[test]
359    fn test_basic_native_fs_file_exists() {
360        let tmpdir = tempdir().expect("creating tmpdir failed");
361        let file_path = tmpdir.path().join("test.txt");
362        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
363        NATIVE_FS
364            .create_file(file_path.to_str().expect("getting str for file failed"))
365            .unwrap();
366        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
367        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
368    }
369
370    #[test]
371    fn test_basic_native_fs_dir_exists() {
372        let tmpdir = tempdir().expect("creating tmpdir failed");
373        let dir_path = tmpdir.path().join("testdir");
374        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
375        NATIVE_FS
376            .create_dir(dir_path.to_str().expect("getting str for file failed"))
377            .unwrap();
378        assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()));
379        assert!(NATIVE_FS.is_dir(dir_path.as_path().to_str().unwrap()));
380    }
381
382    #[test]
383    fn test_basic_native_fs_remove_file() {
384        let tmpdir = tempdir().expect("creating tmpdir failed");
385        let file_path = tmpdir.path().join("test.txt");
386        NATIVE_FS
387            .create_file(file_path.to_str().expect("getting str for file failed"))
388            .expect("creating file failed");
389        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
390        NATIVE_FS
391            .remove_file(file_path.to_str().unwrap())
392            .expect("removing file failed");
393        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
394    }
395
396    #[test]
397    fn test_basic_native_fs_write() {
398        let tmpdir = tempdir().expect("creating tmpdir failed");
399        let file_path = tmpdir.path().join("test.txt");
400        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
401        NATIVE_FS
402            .create_file(file_path.to_str().expect("getting str for file failed"))
403            .unwrap();
404        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
405        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
406        println!("{}", file_path.to_str().unwrap());
407        let write_data = "hello world\n";
408        NATIVE_FS
409            .write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
410            .expect("writing to file failed");
411        let read_back = fs::read_to_string(file_path).expect("reading back data failed");
412        assert_eq!(read_back, write_data);
413    }
414
415    #[test]
416    fn test_basic_native_fs_read() {
417        let tmpdir = tempdir().expect("creating tmpdir failed");
418        let file_path = tmpdir.path().join("test.txt");
419        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
420        NATIVE_FS
421            .create_file(file_path.to_str().expect("getting str for file failed"))
422            .unwrap();
423        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
424        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
425        println!("{}", file_path.to_str().unwrap());
426        let write_data = "hello world\n";
427        NATIVE_FS
428            .write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
429            .expect("writing to file failed");
430        let read_back = fs::read_to_string(file_path).expect("reading back data failed");
431        assert_eq!(read_back, write_data);
432    }
433
434    #[test]
435    fn test_truncate_file() {
436        let tmpdir = tempdir().expect("creating tmpdir failed");
437        let file_path = tmpdir.path().join("test.txt");
438        NATIVE_FS
439            .create_file(file_path.to_str().expect("getting str for file failed"))
440            .expect("creating file failed");
441        fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
442        assert_eq!(fs::read(file_path.clone()).unwrap(), [1, 2, 3, 4]);
443        NATIVE_FS
444            .truncate_file(file_path.to_str().unwrap())
445            .unwrap();
446        assert_eq!(fs::read(file_path.clone()).unwrap(), []);
447    }
448
449    #[test]
450    fn test_remove_dir() {
451        let tmpdir = tempdir().expect("creating tmpdir failed");
452        let dir_path = tmpdir.path().join("testdir");
453        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
454        NATIVE_FS
455            .create_dir(dir_path.to_str().expect("getting str for file failed"))
456            .unwrap();
457        assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()));
458        NATIVE_FS
459            .remove_dir(dir_path.to_str().unwrap(), false)
460            .unwrap();
461        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
462    }
463
464    #[test]
465    fn test_read_file() {
466        let tmpdir = tempdir().expect("creating tmpdir failed");
467        let file_path = tmpdir.path().join("test.txt");
468        NATIVE_FS
469            .create_file(file_path.to_str().expect("getting str for file failed"))
470            .expect("creating file failed");
471        fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
472        let read_buf: &mut [u8] = &mut [0; 4];
473        NATIVE_FS
474            .read_data(file_path.to_str().unwrap(), 0, 4, read_buf)
475            .unwrap();
476        assert_eq!([1, 2, 3, 4], read_buf);
477        NATIVE_FS
478            .write_data(file_path.to_str().unwrap(), 4, &[5, 6, 7, 8])
479            .expect("writing to file failed");
480        NATIVE_FS
481            .read_data(file_path.to_str().unwrap(), 2, 4, read_buf)
482            .unwrap();
483        assert_eq!([3, 4, 5, 6], read_buf);
484    }
485
486    #[test]
487    fn test_remove_which_does_not_exist() {
488        let tmpdir = tempdir().expect("creating tmpdir failed");
489        let file_path = tmpdir.path().join("test.txt");
490        let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
491        assert!(result.is_err());
492        let error = result.unwrap_err();
493        if let FilestoreError::FileDoesNotExist = error {
494            assert_eq!(error.to_string(), "file does not exist");
495        } else {
496            panic!("unexpected error");
497        }
498    }
499
500    #[test]
501    fn test_file_already_exists() {
502        let tmpdir = tempdir().expect("creating tmpdir failed");
503        let file_path = tmpdir.path().join("test.txt");
504        let result =
505            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
506        assert!(result.is_ok());
507        let result =
508            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
509        assert!(result.is_err());
510        let error = result.unwrap_err();
511        if let FilestoreError::FileAlreadyExists = error {
512            assert_eq!(error.to_string(), "file already exists");
513        } else {
514            panic!("unexpected error");
515        }
516    }
517
518    #[test]
519    fn test_remove_file_with_dir_api() {
520        let tmpdir = tempdir().expect("creating tmpdir failed");
521        let file_path = tmpdir.path().join("test.txt");
522        NATIVE_FS
523            .create_file(file_path.to_str().expect("getting str for file failed"))
524            .unwrap();
525        let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
526        assert!(result.is_err());
527        let error = result.unwrap_err();
528        if let FilestoreError::IsNotDirectory = error {
529            assert_eq!(error.to_string(), "is not a directory");
530        } else {
531            panic!("unexpected error");
532        }
533    }
534
535    #[test]
536    fn test_remove_dir_remove_all() {
537        let tmpdir = tempdir().expect("creating tmpdir failed");
538        let dir_path = tmpdir.path().join("test");
539        NATIVE_FS
540            .create_dir(dir_path.to_str().expect("getting str for file failed"))
541            .unwrap();
542        let file_path = dir_path.as_path().join("test.txt");
543        NATIVE_FS
544            .create_file(file_path.to_str().expect("getting str for file failed"))
545            .unwrap();
546        let result = NATIVE_FS.remove_dir(dir_path.to_str().unwrap(), true);
547        assert!(result.is_ok());
548        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
549    }
550
551    #[test]
552    fn test_remove_dir_with_file_api() {
553        let tmpdir = tempdir().expect("creating tmpdir failed");
554        let file_path = tmpdir.path().join("test");
555        NATIVE_FS
556            .create_dir(file_path.to_str().expect("getting str for file failed"))
557            .unwrap();
558        let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
559        assert!(result.is_err());
560        let error = result.unwrap_err();
561        if let FilestoreError::IsNotFile = error {
562            assert_eq!(error.to_string(), "is not a file");
563        } else {
564            panic!("unexpected error");
565        }
566    }
567
568    #[test]
569    fn test_remove_dir_which_does_not_exist() {
570        let tmpdir = tempdir().expect("creating tmpdir failed");
571        let file_path = tmpdir.path().join("test");
572        let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
573        assert!(result.is_err());
574        let error = result.unwrap_err();
575        if let FilestoreError::DirDoesNotExist = error {
576            assert_eq!(error.to_string(), "directory does not exist");
577        } else {
578            panic!("unexpected error");
579        }
580    }
581
582    #[test]
583    fn test_remove_file_which_does_not_exist() {
584        let tmpdir = tempdir().expect("creating tmpdir failed");
585        let file_path = tmpdir.path().join("test.txt");
586        let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
587        assert!(result.is_err());
588        let error = result.unwrap_err();
589        if let FilestoreError::FileDoesNotExist = error {
590            assert_eq!(error.to_string(), "file does not exist");
591        } else {
592            panic!("unexpected error");
593        }
594    }
595
596    #[test]
597    fn test_truncate_file_which_does_not_exist() {
598        let tmpdir = tempdir().expect("creating tmpdir failed");
599        let file_path = tmpdir.path().join("test.txt");
600        let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
601        assert!(result.is_err());
602        let error = result.unwrap_err();
603        if let FilestoreError::FileDoesNotExist = error {
604            assert_eq!(error.to_string(), "file does not exist");
605        } else {
606            panic!("unexpected error");
607        }
608    }
609
610    #[test]
611    fn test_truncate_file_on_directory() {
612        let tmpdir = tempdir().expect("creating tmpdir failed");
613        let file_path = tmpdir.path().join("test");
614        NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
615        let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
616        assert!(result.is_err());
617        let error = result.unwrap_err();
618        if let FilestoreError::IsNotFile = error {
619            assert_eq!(error.to_string(), "is not a file");
620        } else {
621            panic!("unexpected error");
622        }
623    }
624
625    #[test]
626    fn test_byte_conversion_error_when_reading() {
627        let tmpdir = tempdir().expect("creating tmpdir failed");
628        let file_path = tmpdir.path().join("test.txt");
629        NATIVE_FS
630            .create_file(file_path.to_str().expect("getting str for file failed"))
631            .unwrap();
632        let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 2, &mut []);
633        assert!(result.is_err());
634        let error = result.unwrap_err();
635        if let FilestoreError::ByteConversion(byte_conv_error) = error {
636            if let ByteConversionError::ToSliceTooSmall { found, expected } = byte_conv_error {
637                assert_eq!(found, 0);
638                assert_eq!(expected, 2);
639            } else {
640                panic!("unexpected error");
641            }
642            assert_eq!(
643                error.to_string(),
644                format!("filestore error: {}", byte_conv_error)
645            );
646        } else {
647            panic!("unexpected error");
648        }
649    }
650
651    #[test]
652    fn test_read_file_on_dir() {
653        let tmpdir = tempdir().expect("creating tmpdir failed");
654        let dir_path = tmpdir.path().join("test");
655        NATIVE_FS
656            .create_dir(dir_path.to_str().expect("getting str for file failed"))
657            .unwrap();
658        let result = NATIVE_FS.read_data(dir_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
659        assert!(result.is_err());
660        let error = result.unwrap_err();
661        if let FilestoreError::IsNotFile = error {
662            assert_eq!(error.to_string(), "is not a file");
663        } else {
664            panic!("unexpected error");
665        }
666    }
667
668    #[test]
669    fn test_write_file_non_existing() {
670        let tmpdir = tempdir().expect("creating tmpdir failed");
671        let file_path = tmpdir.path().join("test.txt");
672        let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
673        assert!(result.is_err());
674        let error = result.unwrap_err();
675        if let FilestoreError::FileDoesNotExist = error {
676        } else {
677            panic!("unexpected error");
678        }
679    }
680
681    #[test]
682    fn test_write_file_on_dir() {
683        let tmpdir = tempdir().expect("creating tmpdir failed");
684        let file_path = tmpdir.path().join("test");
685        NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
686        let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
687        assert!(result.is_err());
688        let error = result.unwrap_err();
689        if let FilestoreError::IsNotFile = error {
690        } else {
691            panic!("unexpected error");
692        }
693    }
694
695    #[test]
696    fn test_filename_extraction() {
697        let tmpdir = tempdir().expect("creating tmpdir failed");
698        let file_path = tmpdir.path().join("test.txt");
699        NATIVE_FS
700            .create_file(file_path.to_str().expect("getting str for file failed"))
701            .unwrap();
702        NativeFilestore::filename_from_full_path(file_path.to_str().unwrap());
703    }
704
705    #[test]
706    fn test_modular_checksum() {
707        let tmpdir = tempdir().expect("creating tmpdir failed");
708        let file_path = tmpdir.path().join("mod-crc.bin");
709        fs::write(file_path.as_path(), EXAMPLE_DATA_CFDP).expect("writing test file failed");
710        // Kind of re-writing the modular checksum impl here which we are trying to test, but the
711        // numbers/correctness were verified manually using calculators, so this is okay.
712        let mut checksum: u32 = 0;
713        let mut buffer: [u8; 4] = [0; 4];
714        for i in 0..3 {
715            buffer = EXAMPLE_DATA_CFDP[i * 4..(i + 1) * 4].try_into().unwrap();
716            checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
717        }
718        buffer[0..3].copy_from_slice(&EXAMPLE_DATA_CFDP[12..15]);
719        buffer[3] = 0;
720        checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
721        let mut verif_buf: [u8; 32] = [0; 32];
722        let result = NATIVE_FS.checksum_verify(
723            file_path.to_str().unwrap(),
724            ChecksumType::Modular,
725            checksum,
726            &mut verif_buf,
727        );
728        assert!(result.is_ok());
729    }
730
731    #[test]
732    fn test_null_checksum_impl() {
733        let tmpdir = tempdir().expect("creating tmpdir failed");
734        let file_path = tmpdir.path().join("mod-crc.bin");
735        // The file to check does not even need to exist, and the verification buffer can be
736        // empty: the null checksum is always yields the same result.
737        let result = NATIVE_FS.checksum_verify(
738            file_path.to_str().unwrap(),
739            ChecksumType::NullChecksum,
740            0,
741            &mut [],
742        );
743        assert!(result.is_ok());
744        assert!(result.unwrap());
745    }
746
747    #[test]
748    fn test_checksum_not_implemented() {
749        let tmpdir = tempdir().expect("creating tmpdir failed");
750        let file_path = tmpdir.path().join("mod-crc.bin");
751        // The file to check does not even need to exist, and the verification buffer can be
752        // empty: the null checksum is always yields the same result.
753        let result = NATIVE_FS.checksum_verify(
754            file_path.to_str().unwrap(),
755            ChecksumType::Crc32Proximity1,
756            0,
757            &mut [],
758        );
759        assert!(result.is_err());
760        let error = result.unwrap_err();
761        if let FilestoreError::ChecksumTypeNotImplemented(cksum_type) = error {
762            assert_eq!(
763                error.to_string(),
764                format!("checksum {:?} not implemented", cksum_type)
765            );
766        } else {
767            panic!("unexpected error");
768        }
769    }
770}