1use core::convert::TryFrom;
6use core::ops::ControlFlow;
7
8use crate::fat::{self, BlockCache, RESERVED_ENTRIES};
9
10use crate::filesystem::{
11 Attributes, ClusterId, DirEntry, Directory, DirectoryInfo, File, FileInfo, Mode,
12 SearchIdGenerator, TimeSource, ToShortFileName, MAX_FILE_SIZE,
13};
14use crate::{debug, Block, BlockCount, BlockDevice, BlockIdx, Error, VolumeType};
15use heapless::Vec;
16
17pub struct Volume<D, T, const MAX_DIRS: usize = 4, const MAX_FILES: usize = 4>
20where
21 D: BlockDevice,
22 T: TimeSource,
23 <D as BlockDevice>::Error: core::fmt::Debug,
24{
25 pub(crate) block_device: D,
26 pub(crate) time_source: T,
27 id_generator: SearchIdGenerator,
28 volume_type: VolumeType,
29 open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
30 open_files: Vec<FileInfo, MAX_FILES>,
31}
32
33impl<D, T> Volume<D, T, 4, 4>
34where
35 D: BlockDevice,
36 T: TimeSource,
37 <D as BlockDevice>::Error: core::fmt::Debug,
38{
39 pub async fn new(
47 block_device: D,
48 time_source: T,
49 ) -> Result<Volume<D, T, 4, 4>, Error<D::Error>> {
50 Self::new_with_limits(block_device, time_source, 5000).await
53 }
54}
55
56impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize> Volume<D, T, MAX_DIRS, MAX_FILES>
57where
58 D: BlockDevice,
59 T: TimeSource,
60 <D as BlockDevice>::Error: core::fmt::Debug,
61{
62 pub async fn new_with_limits(
70 block_device: D,
71 time_source: T,
72 id_offset: u32,
73 ) -> Result<Volume<D, T, MAX_DIRS, MAX_FILES>, Error<D::Error>> {
74 debug!("Creating new embedded-sdmmc::VolumeManager");
75 let volume_type =
76 fat::parse_volume(&block_device, BlockIdx(0), block_device.num_blocks().await?).await?;
77 Ok(Volume {
78 block_device,
79 time_source,
80 id_generator: SearchIdGenerator::new(id_offset),
81 volume_type,
82 open_dirs: Vec::new(),
83 open_files: Vec::new(),
84 })
85 }
86
87 pub fn device(&mut self) -> &mut D {
89 &mut self.block_device
90 }
91
92 pub fn open_root_dir(&mut self) -> Result<Directory, Error<D::Error>> {
97 for dir in self.open_dirs.iter() {
98 if dir.cluster == ClusterId::ROOT_DIR {
99 return Err(Error::DirAlreadyOpen);
100 }
101 }
102
103 let directory_id = Directory(self.id_generator.get());
104 let dir_info = DirectoryInfo {
105 cluster: ClusterId::ROOT_DIR,
106 directory_id,
107 };
108
109 self.open_dirs
110 .push(dir_info)
111 .map_err(|_| Error::TooManyOpenDirs)?;
112
113 Ok(directory_id)
114 }
115
116 pub async fn open_dir<N>(
124 &mut self,
125 parent_dir: Directory,
126 name: N,
127 ) -> Result<Directory, Error<D::Error>>
128 where
129 N: ToShortFileName,
130 {
131 if self.open_dirs.is_full() {
132 return Err(Error::TooManyOpenDirs);
133 }
134
135 let parent_dir_idx = self.get_dir_by_id(parent_dir)?;
137 let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
138
139 let parent_dir_info = &self.open_dirs[parent_dir_idx];
141 let dir_entry = match &self.volume_type {
142 VolumeType::Fat(fat) => {
143 fat.find_directory_entry(&self.block_device, parent_dir_info, &short_file_name)
144 .await?
145 }
146 };
147
148 if !dir_entry.attributes.is_directory() {
149 return Err(Error::OpenedFileAsDir);
150 }
151
152 for d in self.open_dirs.iter() {
154 if d.cluster == dir_entry.cluster {
155 return Err(Error::DirAlreadyOpen);
156 }
157 }
158
159 let directory_id = Directory(self.id_generator.get());
161 let dir_info = DirectoryInfo {
162 directory_id,
163 cluster: dir_entry.cluster,
164 };
165
166 self.open_dirs
167 .push(dir_info)
168 .map_err(|_| Error::TooManyOpenDirs)?;
169
170 Ok(directory_id)
171 }
172
173 pub fn close_dir(&mut self, directory: Directory) -> Result<(), Error<D::Error>> {
176 for (idx, info) in self.open_dirs.iter().enumerate() {
177 if directory == info.directory_id {
178 self.open_dirs.swap_remove(idx);
179 return Ok(());
180 }
181 }
182 Err(Error::BadHandle)
183 }
184
185 pub async fn find_directory_entry<N>(
187 &mut self,
188 directory: Directory,
189 name: N,
190 ) -> Result<DirEntry, Error<D::Error>>
191 where
192 N: ToShortFileName,
193 {
194 let directory_idx = self.get_dir_by_id(directory)?;
195 match &self.volume_type {
196 VolumeType::Fat(fat) => {
197 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
198 fat.find_directory_entry(&self.block_device, &self.open_dirs[directory_idx], &sfn)
199 .await
200 }
201 }
202 }
203
204 #[allow(missing_docs)]
205 pub async fn find_lfn_directory_entry(
206 &mut self,
207 directory: Directory,
208 name: &str,
209 ) -> Result<DirEntry, Error<D::Error>> {
210 let directory_idx = self.get_dir_by_id(directory)?;
211 match &self.volume_type {
212 VolumeType::Fat(fat) => {
213 fat.find_lfn_directory_entry(
214 &self.block_device,
215 &self.open_dirs[directory_idx],
216 &name,
217 )
218 .await
219 }
220 }
221 }
222
223 pub async fn iterate_dir<F, U>(
225 &mut self,
226 directory: Directory,
227 func: F,
228 ) -> Result<Option<U>, Error<D::Error>>
229 where
230 F: FnMut(&DirEntry) -> ControlFlow<U>,
231 {
232 let directory_idx = self.get_dir_by_id(directory)?;
233 match &self.volume_type {
234 VolumeType::Fat(fat) => {
235 fat.iterate_dir(&self.block_device, &self.open_dirs[directory_idx], func)
236 .await
237 }
238 }
239 }
240
241 pub async fn iterate_lfn_dir<F, U>(
243 &mut self,
244 directory: Directory,
245 func: F,
246 ) -> Result<Option<U>, Error<D::Error>>
247 where
248 F: FnMut(Option<&str>, &DirEntry) -> ControlFlow<U>,
249 {
250 let directory_idx = self.get_dir_by_id(directory)?;
251 match &self.volume_type {
252 VolumeType::Fat(fat) => {
253 fat.iterate_lfn_dir(&self.block_device, &self.open_dirs[directory_idx], func)
254 .await
255 }
256 }
257 }
258
259 async unsafe fn open_dir_entry(
266 &mut self,
267 dir_entry: DirEntry,
268 mode: Mode,
269 ) -> Result<File, Error<D::Error>> {
270 if self.open_files.is_full() {
272 return Err(Error::TooManyOpenFiles);
273 }
274
275 if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
276 return Err(Error::ReadOnly);
277 }
278
279 if dir_entry.attributes.is_directory() {
280 return Err(Error::OpenedDirAsFile);
281 }
282
283 if self.file_is_open(&dir_entry) {
285 return Err(Error::FileAlreadyOpen);
286 }
287
288 let mode = solve_mode_variant(mode, true);
289 let file_id = File(self.id_generator.get());
290
291 let file = match mode {
292 Mode::ReadOnly => FileInfo {
293 file_id,
294 current_cluster: (0, dir_entry.cluster),
295 current_offset: 0,
296 mode,
297 entry: dir_entry,
298 dirty: false,
299 },
300 Mode::ReadWriteAppend => {
301 let mut file = FileInfo {
302 file_id,
303 current_cluster: (0, dir_entry.cluster),
304 current_offset: 0,
305 mode,
306 entry: dir_entry,
307 dirty: false,
308 };
309 file.seek_from_end(0).ok();
311 file
312 }
313 Mode::ReadWriteTruncate => {
314 let mut file = FileInfo {
315 file_id,
316 current_cluster: (0, dir_entry.cluster),
317 current_offset: 0,
318 mode,
319 entry: dir_entry,
320 dirty: false,
321 };
322 match &mut self.volume_type {
323 VolumeType::Fat(fat) => {
324 fat.truncate_cluster_chain(&self.block_device, file.entry.cluster)
325 .await?
326 }
327 };
328 file.update_length(0);
329 match &self.volume_type {
330 VolumeType::Fat(fat) => {
331 file.entry.mtime = self.time_source.get_timestamp();
332 let fat_type = fat.get_fat_type();
333 self.write_entry_to_disk(fat_type, &file.entry).await?;
334 }
335 };
336
337 file
338 }
339 _ => return Err(Error::Unsupported),
340 };
341
342 unsafe {
344 self.open_files.push_unchecked(file);
345 }
346
347 Ok(file_id)
348 }
349
350 pub async fn open_file_in_dir<N>(
352 &mut self,
353 directory: Directory,
354 name: N,
355 mode: Mode,
356 ) -> Result<File, Error<D::Error>>
357 where
358 N: ToShortFileName,
359 {
360 if self.open_files.is_full() {
362 return Err(Error::TooManyOpenFiles);
363 }
364
365 let directory_idx = self.get_dir_by_id(directory)?;
366 let directory_info = &self.open_dirs[directory_idx];
367 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
368
369 let dir_entry = match &self.volume_type {
370 VolumeType::Fat(fat) => {
371 fat.find_directory_entry(&self.block_device, directory_info, &sfn)
372 .await
373 }
374 };
375
376 let dir_entry = match dir_entry {
377 Ok(entry) => {
378 Some(entry)
380 }
381 Err(_)
382 if (mode == Mode::ReadWriteCreate)
383 | (mode == Mode::ReadWriteCreateOrTruncate)
384 | (mode == Mode::ReadWriteCreateOrAppend) =>
385 {
386 None
389 }
390 _ => {
391 return Err(Error::FileNotFound);
393 }
394 };
395
396 if let Some(dir_entry) = &dir_entry {
398 if self.file_is_open(&dir_entry) {
399 return Err(Error::FileAlreadyOpen);
400 }
401 }
402
403 let mode = solve_mode_variant(mode, dir_entry.is_some());
404
405 match mode {
406 Mode::ReadWriteCreate => {
407 if dir_entry.is_some() {
408 return Err(Error::FileAlreadyExists);
409 }
410 let att = Attributes::create_from_fat(0);
411 let entry = match &mut self.volume_type {
412 VolumeType::Fat(fat) => {
413 fat.write_new_directory_entry(
414 &self.block_device,
415 &self.time_source,
416 directory_info,
417 sfn,
418 att,
419 )
420 .await?
421 }
422 };
423
424 let file_id = File(self.id_generator.get());
425
426 let file = FileInfo {
427 file_id,
428 current_cluster: (0, entry.cluster),
429 current_offset: 0,
430 mode,
431 entry,
432 dirty: false,
433 };
434
435 unsafe {
437 self.open_files.push_unchecked(file);
438 }
439
440 Ok(file_id)
441 }
442 _ => {
443 let dir_entry = dir_entry.unwrap();
445 unsafe { self.open_dir_entry(dir_entry, mode).await }
447 }
448 }
449 }
450
451 pub async fn delete_file_in_dir<N>(
453 &mut self,
454 directory: Directory,
455 name: N,
456 ) -> Result<(), Error<D::Error>>
457 where
458 N: ToShortFileName,
459 {
460 let dir_idx = self.get_dir_by_id(directory)?;
461 let dir_info = &self.open_dirs[dir_idx];
462 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
463
464 let dir_entry = match &self.volume_type {
465 VolumeType::Fat(fat) => {
466 fat.find_directory_entry(&self.block_device, dir_info, &sfn)
467 .await
468 }
469 }?;
470
471 if dir_entry.attributes.is_directory() {
472 return Err(Error::DeleteDirAsFile);
473 }
474
475 if self.file_is_open(&dir_entry) {
476 return Err(Error::FileAlreadyOpen);
477 }
478
479 match &self.volume_type {
480 VolumeType::Fat(fat) => {
481 fat.delete_directory_entry(&self.block_device, dir_info, &sfn)
482 .await?
483 }
484 }
485
486 Ok(())
487 }
488
489 fn file_is_open(&self, dir_entry: &DirEntry) -> bool {
493 for f in self.open_files.iter() {
494 if f.entry.entry_block == dir_entry.entry_block
495 && f.entry.entry_offset == dir_entry.entry_offset
496 {
497 return true;
498 }
499 }
500 false
501 }
502
503 pub async fn read(&mut self, file: File, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
505 let file_idx = self.get_file_by_id(file)?;
506 let mut space = buffer.len();
510 let mut read = 0;
511 while space > 0 && !self.open_files[file_idx].eof() {
512 let mut current_cluster = self.open_files[file_idx].current_cluster;
513 let (block_idx, block_offset, block_avail) = self
514 .find_data_on_disk(
515 &mut current_cluster,
516 self.open_files[file_idx].current_offset,
517 )
518 .await?;
519 self.open_files[file_idx].current_cluster = current_cluster;
520 let mut blocks = [Block::new()];
521 self.block_device
522 .read(&mut blocks, block_idx, "read")
523 .await
524 .map_err(Error::DeviceError)?;
525 let block = &blocks[0];
526 let to_copy = block_avail
527 .min(space)
528 .min(self.open_files[file_idx].left() as usize);
529 assert!(to_copy != 0);
530 buffer[read..read + to_copy]
531 .copy_from_slice(&block[block_offset..block_offset + to_copy]);
532 read += to_copy;
533 space -= to_copy;
534 self.open_files[file_idx]
535 .seek_from_current(to_copy as i32)
536 .unwrap();
537 }
538 Ok(read)
539 }
540
541 pub async fn write(&mut self, file: File, buffer: &[u8]) -> Result<usize, Error<D::Error>> {
543 #[cfg(feature = "defmt-log")]
544 debug!("write(file={:?}, buffer={:x}", file, buffer);
545
546 #[cfg(feature = "log")]
547 debug!("write(file={:?}, buffer={:x?}", file, buffer);
548
549 let file_idx = self.get_file_by_id(file)?;
552
553 if self.open_files[file_idx].mode == Mode::ReadOnly {
554 return Err(Error::ReadOnly);
555 }
556
557 self.open_files[file_idx].dirty = true;
558
559 if self.open_files[file_idx].entry.cluster.0 < RESERVED_ENTRIES {
560 self.open_files[file_idx].entry.cluster = match self.volume_type {
562 VolumeType::Fat(ref mut fat) => {
563 fat.alloc_cluster(&self.block_device, None, false).await?
564 }
565 };
566 debug!(
567 "Alloc first cluster {:?}",
568 self.open_files[file_idx].entry.cluster
569 );
570 }
571
572 if (self.open_files[file_idx].current_cluster.1) < self.open_files[file_idx].entry.cluster {
573 debug!("Rewinding to start");
574 self.open_files[file_idx].current_cluster =
575 (0, self.open_files[file_idx].entry.cluster);
576 }
577 let bytes_until_max =
578 usize::try_from(MAX_FILE_SIZE - self.open_files[file_idx].current_offset)
579 .map_err(|_| Error::ConversionError)?;
580 let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
581 let mut written = 0;
582
583 while written < bytes_to_write {
584 let mut current_cluster = self.open_files[file_idx].current_cluster;
585 debug!(
586 "Have written bytes {}/{}, finding cluster {:?}",
587 written, bytes_to_write, current_cluster
588 );
589 let current_offset = self.open_files[file_idx].current_offset;
590 let (block_idx, block_offset, block_avail) = match self
591 .find_data_on_disk(&mut current_cluster, current_offset)
592 .await
593 {
594 Ok(vars) => {
595 debug!(
596 "Found block_idx={:?}, block_offset={:?}, block_avail={}",
597 vars.0, vars.1, vars.2
598 );
599 vars
600 }
601 Err(Error::EndOfFile) => {
602 debug!("Extending file");
603 match self.volume_type {
604 VolumeType::Fat(ref mut fat) => {
605 if fat
606 .alloc_cluster(&self.block_device, Some(current_cluster.1), false)
607 .await
608 .is_err()
609 {
610 return Ok(written);
611 }
612 debug!("Allocated new FAT cluster, finding offsets...");
613 let new_offset = self
614 .find_data_on_disk(
615 &mut current_cluster,
616 self.open_files[file_idx].current_offset,
617 )
618 .await
619 .map_err(|_| Error::AllocationError)?;
620 debug!("New offset {:?}", new_offset);
621 new_offset
622 }
623 }
624 }
625 Err(e) => return Err(e),
626 };
627 let mut blocks = [Block::new()];
628 let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
629 if block_offset != 0 {
630 debug!("Partial block write");
631 self.block_device
632 .read(&mut blocks, block_idx, "read")
633 .await
634 .map_err(Error::DeviceError)?;
635 }
636 let block = &mut blocks[0];
637 block[block_offset..block_offset + to_copy]
638 .copy_from_slice(&buffer[written..written + to_copy]);
639 debug!("Writing block {:?}", block_idx);
640 self.block_device
641 .write(&blocks, block_idx)
642 .await
643 .map_err(Error::DeviceError)?;
644 written += to_copy;
645 self.open_files[file_idx].current_cluster = current_cluster;
646
647 let to_copy = to_copy as u32;
648 let new_offset = self.open_files[file_idx].current_offset + to_copy;
649 if new_offset > self.open_files[file_idx].entry.size {
650 self.open_files[file_idx].update_length(new_offset);
652 }
653 self.open_files[file_idx]
654 .seek_from_start(new_offset)
655 .unwrap();
656 }
658 self.open_files[file_idx].entry.attributes.set_archive(true);
659 self.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
660 Ok(written)
661 }
662
663 pub async fn close_file(&mut self, file: File) -> Result<(), Error<D::Error>> {
665 let mut found_idx = None;
666 for (idx, info) in self.open_files.iter().enumerate() {
667 if file == info.file_id {
668 found_idx = Some((info, idx));
669 break;
670 }
671 }
672
673 let (file_info, file_idx) = found_idx.ok_or(Error::BadHandle)?;
674
675 if file_info.dirty {
676 match self.volume_type {
677 VolumeType::Fat(ref mut fat) => {
678 debug!("Updating FAT info sector");
679 fat.update_info_sector(&self.block_device).await?;
680 debug!("Updating dir entry {:?}", file_info.entry);
681 if file_info.entry.size != 0 {
682 assert!(file_info.entry.cluster.0 != 0);
684 }
685 let fat_type = fat.get_fat_type();
686 self.write_entry_to_disk(fat_type, &file_info.entry).await?;
687 }
688 };
689 }
690
691 self.open_files.swap_remove(file_idx);
692 Ok(())
693 }
694
695 pub fn has_open_handles(&self) -> bool {
697 !(self.open_dirs.is_empty() || self.open_files.is_empty())
698 }
699
700 pub fn free(self) -> (D, T) {
702 (self.block_device, self.time_source)
703 }
704
705 pub fn file_eof(&self, file: File) -> Result<bool, Error<D::Error>> {
707 let file_idx = self.get_file_by_id(file)?;
708 Ok(self.open_files[file_idx].eof())
709 }
710
711 pub fn file_seek_from_start(&mut self, file: File, offset: u32) -> Result<(), Error<D::Error>> {
713 let file_idx = self.get_file_by_id(file)?;
714 self.open_files[file_idx]
715 .seek_from_start(offset)
716 .map_err(|_| Error::InvalidOffset)?;
717 Ok(())
718 }
719
720 pub fn file_seek_from_current(
722 &mut self,
723 file: File,
724 offset: i32,
725 ) -> Result<(), Error<D::Error>> {
726 let file_idx = self.get_file_by_id(file)?;
727 self.open_files[file_idx]
728 .seek_from_current(offset)
729 .map_err(|_| Error::InvalidOffset)?;
730 Ok(())
731 }
732
733 pub fn file_seek_from_end(&mut self, file: File, offset: u32) -> Result<(), Error<D::Error>> {
735 let file_idx = self.get_file_by_id(file)?;
736 self.open_files[file_idx]
737 .seek_from_end(offset)
738 .map_err(|_| Error::InvalidOffset)?;
739 Ok(())
740 }
741
742 pub fn file_length(&self, file: File) -> Result<u32, Error<D::Error>> {
744 let file_idx = self.get_file_by_id(file)?;
745 Ok(self.open_files[file_idx].length())
746 }
747
748 pub fn file_offset(&self, file: File) -> Result<u32, Error<D::Error>> {
750 let file_idx = self.get_file_by_id(file)?;
751 Ok(self.open_files[file_idx].current_offset)
752 }
753
754 fn get_dir_by_id(&self, directory: Directory) -> Result<usize, Error<D::Error>> {
755 for (idx, d) in self.open_dirs.iter().enumerate() {
756 if d.directory_id == directory {
757 return Ok(idx);
758 }
759 }
760 Err(Error::BadHandle)
761 }
762
763 fn get_file_by_id(&self, file: File) -> Result<usize, Error<D::Error>> {
764 for (idx, f) in self.open_files.iter().enumerate() {
765 if f.file_id == file {
766 return Ok(idx);
767 }
768 }
769 Err(Error::BadHandle)
770 }
771
772 async fn find_data_on_disk(
776 &self,
777 start: &mut (u32, ClusterId),
778 desired_offset: u32,
779 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>> {
780 let bytes_per_cluster = match &self.volume_type {
781 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
782 };
783 let offset_from_cluster = desired_offset - start.0;
785 let num_clusters = offset_from_cluster / bytes_per_cluster;
786 let mut block_cache = BlockCache::empty();
787 for _ in 0..num_clusters {
788 start.1 = match &self.volume_type {
789 VolumeType::Fat(fat) => {
790 fat.next_cluster(&self.block_device, start.1, &mut block_cache)
791 .await?
792 }
793 };
794 start.0 += bytes_per_cluster;
795 }
796 let offset_from_cluster = desired_offset - start.0;
798 assert!(offset_from_cluster < bytes_per_cluster);
799 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
800 let block_idx = match &self.volume_type {
801 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
802 } + num_blocks;
803 let block_offset = (desired_offset % Block::LEN_U32) as usize;
804 let available = Block::LEN - block_offset;
805 Ok((block_idx, block_offset, available))
806 }
807
808 async fn write_entry_to_disk(
810 &self,
811 fat_type: fat::FatType,
812 entry: &DirEntry,
813 ) -> Result<(), Error<D::Error>> {
814 let mut blocks = [Block::new()];
815 self.block_device
816 .read(&mut blocks, entry.entry_block, "read")
817 .await
818 .map_err(Error::DeviceError)?;
819 let block = &mut blocks[0];
820
821 let start = usize::try_from(entry.entry_offset).map_err(|_| Error::ConversionError)?;
822 block[start..start + 32].copy_from_slice(&entry.serialize(fat_type)[..]);
823
824 self.block_device
825 .write(&blocks, entry.entry_block)
826 .await
827 .map_err(Error::DeviceError)?;
828 Ok(())
829 }
830}
831
832fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
835 let mut mode = mode;
836 if mode == Mode::ReadWriteCreateOrAppend {
837 if dir_entry_is_some {
838 mode = Mode::ReadWriteAppend;
839 } else {
840 mode = Mode::ReadWriteCreate;
841 }
842 } else if mode == Mode::ReadWriteCreateOrTruncate {
843 if dir_entry_is_some {
844 mode = Mode::ReadWriteTruncate;
845 } else {
846 mode = Mode::ReadWriteCreate;
847 }
848 }
849 mode
850}
851
852