1#[cfg(all(not(feature = "std"), feature = "alloc", feature = "lfn"))]
2use alloc::vec::Vec;
3use core::num;
4use core::str;
5#[cfg(feature = "lfn")]
6use core::{iter, slice};
7
8use crate::dir_entry::{
9 DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes, ShortName, DIR_ENTRY_SIZE,
10};
11#[cfg(feature = "lfn")]
12use crate::dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN};
13use crate::dir_entry::{SFN_PADDING, SFN_SIZE};
14use crate::error::{Error, IoError};
15use crate::file::File;
16use crate::fs::{DiskSlice, FileSystem, FsIoAdapter, OemCpConverter, ReadWriteSeek};
17use crate::io::{self, IoBase, Read, Seek, SeekFrom, Write};
18use crate::time::TimeProvider;
19
20const LFN_PADDING: u16 = 0xFFFF;
21
22pub(crate) enum DirRawStream<'a, IO: ReadWriteSeek, TP, OCC> {
23 File(File<'a, IO, TP, OCC>),
24 Root(DiskSlice<FsIoAdapter<'a, IO, TP, OCC>, FsIoAdapter<'a, IO, TP, OCC>>),
25}
26
27impl<IO: ReadWriteSeek, TP, OCC> DirRawStream<'_, IO, TP, OCC> {
28 fn abs_pos(&self) -> Option<u64> {
29 match self {
30 DirRawStream::File(file) => file.abs_pos(),
31 DirRawStream::Root(slice) => Some(slice.abs_pos()),
32 }
33 }
34
35 fn first_cluster(&self) -> Option<u32> {
36 match self {
37 DirRawStream::File(file) => file.first_cluster(),
38 DirRawStream::Root(_) => None,
39 }
40 }
41
42 pub(crate) fn is_root_dir(&self) -> bool {
43 match self {
44 DirRawStream::File(file) => file.is_root_dir(),
45 DirRawStream::Root(_) => true,
46 }
47 }
48}
49
50impl<IO: ReadWriteSeek, TP, OCC> Clone for DirRawStream<'_, IO, TP, OCC> {
52 fn clone(&self) -> Self {
53 match self {
54 DirRawStream::File(file) => DirRawStream::File(file.clone()),
55 DirRawStream::Root(raw) => DirRawStream::Root(raw.clone()),
56 }
57 }
58}
59
60impl<IO: ReadWriteSeek, TP, OCC> IoBase for DirRawStream<'_, IO, TP, OCC> {
61 type Error = Error<IO::Error>;
62}
63
64impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Read for DirRawStream<'_, IO, TP, OCC> {
65 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
66 match self {
67 DirRawStream::File(file) => file.read(buf),
68 DirRawStream::Root(raw) => raw.read(buf),
69 }
70 }
71}
72
73impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Write for DirRawStream<'_, IO, TP, OCC> {
74 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
75 match self {
76 DirRawStream::File(file) => file.write(buf),
77 DirRawStream::Root(raw) => raw.write(buf),
78 }
79 }
80 fn flush(&mut self) -> Result<(), Self::Error> {
81 match self {
82 DirRawStream::File(file) => file.flush(),
83 DirRawStream::Root(raw) => raw.flush(),
84 }
85 }
86}
87
88impl<IO: ReadWriteSeek, TP, OCC> Seek for DirRawStream<'_, IO, TP, OCC> {
89 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
90 match self {
91 DirRawStream::File(file) => file.seek(pos),
92 DirRawStream::Root(raw) => raw.seek(pos),
93 }
94 }
95}
96
97fn split_path(path: &str) -> (&str, Option<&str>) {
98 let trimmed_path = path.trim_matches('/');
99 trimmed_path.find('/').map_or((trimmed_path, None), |n| {
100 (&trimmed_path[..n], Some(&trimmed_path[n + 1..]))
101 })
102}
103
104enum DirEntryOrShortName<'a, IO: ReadWriteSeek, TP, OCC> {
105 DirEntry(DirEntry<'a, IO, TP, OCC>),
106 ShortName([u8; SFN_SIZE]),
107}
108
109pub struct Dir<'a, IO: ReadWriteSeek, TP, OCC> {
114 stream: DirRawStream<'a, IO, TP, OCC>,
115 fs: &'a FileSystem<IO, TP, OCC>,
116}
117
118impl<'a, IO: ReadWriteSeek, TP, OCC> Dir<'a, IO, TP, OCC> {
119 pub(crate) fn new(stream: DirRawStream<'a, IO, TP, OCC>, fs: &'a FileSystem<IO, TP, OCC>) -> Self {
120 Dir { stream, fs }
121 }
122
123 #[must_use]
125 #[allow(clippy::iter_not_returning_iterator)]
126 pub fn iter(&self) -> DirIter<'a, IO, TP, OCC> {
127 DirIter::new(self.stream.clone(), self.fs, true)
128 }
129}
130
131impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'a, IO, TP, OCC> {
132 fn find_entry(
133 &self,
134 name: &str,
135 is_dir: Option<bool>,
136 mut short_name_gen: Option<&mut ShortNameGenerator>,
137 ) -> Result<DirEntry<'a, IO, TP, OCC>, Error<IO::Error>> {
138 for r in self.iter() {
139 let e = r?;
140 if e.eq_name(name) {
142 if is_dir.is_some() && Some(e.is_dir()) != is_dir {
144 if e.is_dir() {
145 error!("Is a directory");
146 } else {
147 error!("Not a directory");
148 }
149 return Err(Error::InvalidInput);
150 }
151 return Ok(e);
152 }
153 if let Some(ref mut gen) = short_name_gen {
155 gen.add_existing(e.raw_short_name());
156 }
157 }
158 Err(Error::NotFound) }
160
161 #[allow(clippy::type_complexity)]
162 pub(crate) fn find_volume_entry(&self) -> Result<Option<DirEntry<'a, IO, TP, OCC>>, Error<IO::Error>> {
163 for r in DirIter::new(self.stream.clone(), self.fs, false) {
164 let e = r?;
165 if e.data.is_volume() {
166 return Ok(Some(e));
167 }
168 }
169 Ok(None)
170 }
171
172 fn check_for_existence(
173 &self,
174 name: &str,
175 is_dir: Option<bool>,
176 ) -> Result<DirEntryOrShortName<'a, IO, TP, OCC>, Error<IO::Error>> {
177 let mut short_name_gen = ShortNameGenerator::new(name);
178 loop {
179 let r = self.find_entry(name, is_dir, Some(&mut short_name_gen));
181 match r {
182 Err(Error::NotFound) => {}
184 Err(err) => return Err(err),
186 Ok(e) => return Ok(DirEntryOrShortName::DirEntry(e)),
188 };
189 if let Ok(name) = short_name_gen.generate() {
191 return Ok(DirEntryOrShortName::ShortName(name));
192 }
193 short_name_gen.next_iteration();
196 }
197 }
198
199 pub fn as_file(&self) -> Option<&File<'a, IO, TP, OCC>> {
202 match &self.stream {
203 DirRawStream::File(file) => Some(file),
204 DirRawStream::Root(_) => None,
205 }
206 }
207
208 pub fn open_dir(&self, path: &str) -> Result<Self, Error<IO::Error>> {
220 trace!("Dir::open_dir {}", path);
221 let (name, rest_opt) = split_path(path);
222 let e = self.find_entry(name, Some(true), None)?;
223 match rest_opt {
224 Some(rest) => e.to_dir().open_dir(rest),
225 None => Ok(e.to_dir()),
226 }
227 }
228
229 pub fn open_file(&self, path: &str) -> Result<File<'a, IO, TP, OCC>, Error<IO::Error>> {
241 trace!("Dir::open_file {}", path);
242 let (name, rest_opt) = split_path(path);
244 if let Some(rest) = rest_opt {
245 let e = self.find_entry(name, Some(true), None)?;
246 return e.to_dir().open_file(rest);
247 }
248 let e = self.find_entry(name, Some(false), None)?;
250 Ok(e.to_file())
251 }
252
253 pub fn create_file(&self, path: &str) -> Result<File<'a, IO, TP, OCC>, Error<IO::Error>> {
268 trace!("Dir::create_file {}", path);
269 let (name, rest_opt) = split_path(path);
271 if let Some(rest) = rest_opt {
272 return self.find_entry(name, Some(true), None)?.to_dir().create_file(rest);
273 }
274 let r = self.check_for_existence(name, Some(false))?;
276 match r {
277 DirEntryOrShortName::ShortName(short_name) => {
279 let sfn_entry = self.create_sfn_entry(short_name, FileAttributes::from_bits_truncate(0), None);
280 Ok(self.write_entry(name, sfn_entry)?.to_file())
281 }
282 DirEntryOrShortName::DirEntry(e) => Ok(e.to_file()),
284 }
285 }
286
287 pub fn create_dir(&self, path: &str) -> Result<Self, Error<IO::Error>> {
301 trace!("Dir::create_dir {}", path);
302 let (name, rest_opt) = split_path(path);
304 if let Some(rest) = rest_opt {
305 return self.find_entry(name, Some(true), None)?.to_dir().create_dir(rest);
306 }
307 let r = self.check_for_existence(name, Some(true))?;
309 match r {
310 DirEntryOrShortName::ShortName(short_name) => {
312 let cluster = self.fs.alloc_cluster(None, true)?;
314 let sfn_entry = self.create_sfn_entry(short_name, FileAttributes::DIRECTORY, Some(cluster));
316 let entry = self.write_entry(name, sfn_entry)?;
317 let dir = entry.to_dir();
318 let dot_sfn = ShortNameGenerator::generate_dot();
320 let sfn_entry = self.create_sfn_entry(dot_sfn, FileAttributes::DIRECTORY, entry.first_cluster());
321 dir.write_entry(".", sfn_entry)?;
322 let dotdot_sfn = ShortNameGenerator::generate_dotdot();
323 let dotdot_cluster = if self.stream.is_root_dir() {
325 None
326 } else {
327 self.stream.first_cluster()
328 };
329 let sfn_entry = self.create_sfn_entry(dotdot_sfn, FileAttributes::DIRECTORY, dotdot_cluster);
330 dir.write_entry("..", sfn_entry)?;
331 Ok(dir)
332 }
333 DirEntryOrShortName::DirEntry(e) => Ok(e.to_dir()),
335 }
336 }
337
338 fn is_empty(&self) -> Result<bool, Error<IO::Error>> {
339 trace!("Dir::is_empty");
340 for r in self.iter() {
342 let e = r?;
343 let name = e.short_file_name_as_bytes();
344 if name != b"." && name != b".." {
346 return Ok(false);
347 }
348 }
349 Ok(true)
350 }
351
352 pub fn remove(&self, path: &str) -> Result<(), Error<IO::Error>> {
367 trace!("Dir::remove {}", path);
368 let (name, rest_opt) = split_path(path);
370 if let Some(rest) = rest_opt {
371 let e = self.find_entry(name, Some(true), None)?;
372 return e.to_dir().remove(rest);
373 }
374 let e = self.find_entry(name, None, None)?;
376 if e.is_dir() && !e.to_dir().is_empty()? {
377 return Err(Error::DirectoryIsNotEmpty);
378 }
379 if let Some(n) = e.first_cluster() {
381 self.fs.free_cluster_chain(n)?;
382 }
383 let mut stream = self.stream.clone();
385 stream.seek(SeekFrom::Start(e.offset_range.0))?;
386 let num = ((e.offset_range.1 - e.offset_range.0) / u64::from(DIR_ENTRY_SIZE)) as usize;
387 for _ in 0..num {
388 let mut data = DirEntryData::deserialize(&mut stream)?;
389 trace!("removing dir entry {:?}", data);
390 data.set_deleted();
391 stream.seek(SeekFrom::Current(-i64::from(DIR_ENTRY_SIZE)))?;
392 data.serialize(&mut stream)?;
393 }
394 Ok(())
395 }
396
397 pub fn rename(&self, src_path: &str, dst_dir: &Dir<IO, TP, OCC>, dst_path: &str) -> Result<(), Error<IO::Error>> {
414 trace!("Dir::rename {} {}", src_path, dst_path);
415 let (src_name, src_rest_opt) = split_path(src_path);
417 if let Some(rest) = src_rest_opt {
418 let e = self.find_entry(src_name, Some(true), None)?;
419 return e.to_dir().rename(rest, dst_dir, dst_path);
420 }
421 let (dst_name, dst_rest_opt) = split_path(dst_path);
423 if let Some(rest) = dst_rest_opt {
424 let e = dst_dir.find_entry(dst_name, Some(true), None)?;
425 return self.rename(src_path, &e.to_dir(), rest);
426 }
427 self.rename_internal(src_path, dst_dir, dst_path)
429 }
430
431 fn rename_internal(
432 &self,
433 src_name: &str,
434 dst_dir: &Dir<IO, TP, OCC>,
435 dst_name: &str,
436 ) -> Result<(), Error<IO::Error>> {
437 trace!("Dir::rename_internal {} {}", src_name, dst_name);
438 let e = self.find_entry(src_name, None, None)?;
440 let r = dst_dir.check_for_existence(dst_name, None)?;
442 let short_name = match r {
443 DirEntryOrShortName::DirEntry(ref dst_e) => {
445 if e.is_same_entry(dst_e) {
447 return Ok(());
449 }
450 return Err(Error::AlreadyExists);
452 }
453 DirEntryOrShortName::ShortName(short_name) => short_name,
455 };
456 let mut stream = self.stream.clone();
458 stream.seek(SeekFrom::Start(e.offset_range.0))?;
459 let num = ((e.offset_range.1 - e.offset_range.0) / u64::from(DIR_ENTRY_SIZE)) as usize;
460 for _ in 0..num {
461 let mut data = DirEntryData::deserialize(&mut stream)?;
462 trace!("removing LFN entry {:?}", data);
463 data.set_deleted();
464 stream.seek(SeekFrom::Current(-i64::from(DIR_ENTRY_SIZE)))?;
465 data.serialize(&mut stream)?;
466 }
467 let sfn_entry = e.data.renamed(short_name);
469 dst_dir.write_entry(dst_name, sfn_entry)?;
470 Ok(())
471 }
472
473 fn find_free_entries(&self, num_entries: u32) -> Result<DirRawStream<'a, IO, TP, OCC>, Error<IO::Error>> {
474 let mut stream = self.stream.clone();
475 let mut first_free: u32 = 0;
476 let mut num_free: u32 = 0;
477 let mut i: u32 = 0;
478 loop {
479 let raw_entry = DirEntryData::deserialize(&mut stream)?;
480 if raw_entry.is_end() {
481 if num_free == 0 {
483 first_free = i;
484 }
485 let pos = u64::from(first_free * DIR_ENTRY_SIZE);
486 stream.seek(io::SeekFrom::Start(pos))?;
487 return Ok(stream);
488 } else if raw_entry.is_deleted() {
489 if num_free == 0 {
491 first_free = i;
492 }
493 num_free += 1;
494 if num_free == num_entries {
495 let pos = u64::from(first_free * DIR_ENTRY_SIZE);
497 stream.seek(io::SeekFrom::Start(pos))?;
498 return Ok(stream);
499 }
500 } else {
501 num_free = 0;
503 }
504 i += 1;
505 }
506 }
507
508 fn create_sfn_entry(
509 &self,
510 short_name: [u8; SFN_SIZE],
511 attrs: FileAttributes,
512 first_cluster: Option<u32>,
513 ) -> DirFileEntryData {
514 let mut raw_entry = DirFileEntryData::new(short_name, attrs);
515 raw_entry.set_first_cluster(first_cluster, self.fs.fat_type());
516 let now = self.fs.options.time_provider.get_current_date_time();
517 raw_entry.set_created(now);
518 raw_entry.set_accessed(now.date);
519 raw_entry.set_modified(now);
520 raw_entry
521 }
522
523 #[cfg(feature = "lfn")]
524 fn encode_lfn_utf16(name: &str) -> LfnBuffer {
525 LfnBuffer::from_ucs2_units(name.encode_utf16())
526 }
527 #[cfg(not(feature = "lfn"))]
528 fn encode_lfn_utf16(_name: &str) -> LfnBuffer {
529 LfnBuffer {}
530 }
531
532 #[allow(clippy::type_complexity)]
533 fn alloc_and_write_lfn_entries(
534 &self,
535 lfn_utf16: &LfnBuffer,
536 short_name: &[u8; SFN_SIZE],
537 ) -> Result<(DirRawStream<'a, IO, TP, OCC>, u64), Error<IO::Error>> {
538 let lfn_chsum = lfn_checksum(short_name);
540 let lfn_iter = LfnEntriesGenerator::new(lfn_utf16.as_ucs2_units(), lfn_chsum);
542 let num_entries = lfn_iter.len() as u32 + 1;
544 let mut stream = self.find_free_entries(num_entries)?;
545 let start_pos = stream.seek(io::SeekFrom::Current(0))?;
546 for lfn_entry in lfn_iter {
548 lfn_entry.serialize(&mut stream)?;
549 }
550 Ok((stream, start_pos))
551 }
552
553 fn alloc_sfn_entry(&self) -> Result<(DirRawStream<'a, IO, TP, OCC>, u64), Error<IO::Error>> {
554 let mut stream = self.find_free_entries(1)?;
555 let start_pos = stream.seek(io::SeekFrom::Current(0))?;
556 Ok((stream, start_pos))
557 }
558
559 fn write_entry(
560 &self,
561 name: &str,
562 raw_entry: DirFileEntryData,
563 ) -> Result<DirEntry<'a, IO, TP, OCC>, Error<IO::Error>> {
564 trace!("Dir::write_entry {}", name);
565 validate_long_name(name)?;
567 let lfn_utf16 = Self::encode_lfn_utf16(name);
569 let (mut stream, start_pos) = if name == "." || name == ".." {
572 self.alloc_sfn_entry()?
573 } else {
574 self.alloc_and_write_lfn_entries(&lfn_utf16, raw_entry.name())?
575 };
576 raw_entry.serialize(&mut stream)?;
578 let end_pos = stream.seek(io::SeekFrom::Current(0))?;
580 let end_abs_pos = stream.abs_pos().unwrap();
586 let start_abs_pos = end_abs_pos - u64::from(DIR_ENTRY_SIZE);
588 let short_name = ShortName::new(raw_entry.name());
590 Ok(DirEntry {
591 data: raw_entry,
592 short_name,
593 #[cfg(feature = "lfn")]
594 lfn_utf16,
595 fs: self.fs,
596 entry_pos: start_abs_pos,
597 offset_range: (start_pos, end_pos),
598 })
599 }
600}
601
602impl<IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Clone for Dir<'_, IO, TP, OCC> {
604 fn clone(&self) -> Self {
605 Self {
606 stream: self.stream.clone(),
607 fs: self.fs,
608 }
609 }
610}
611
612pub struct DirIter<'a, IO: ReadWriteSeek, TP, OCC> {
616 stream: DirRawStream<'a, IO, TP, OCC>,
617 fs: &'a FileSystem<IO, TP, OCC>,
618 skip_volume: bool,
619 err: bool,
620}
621
622impl<'a, IO: ReadWriteSeek, TP, OCC> DirIter<'a, IO, TP, OCC> {
623 fn new(stream: DirRawStream<'a, IO, TP, OCC>, fs: &'a FileSystem<IO, TP, OCC>, skip_volume: bool) -> Self {
624 DirIter {
625 stream,
626 fs,
627 skip_volume,
628 err: false,
629 }
630 }
631}
632
633impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC> DirIter<'a, IO, TP, OCC> {
634 fn should_skip_entry(&self, raw_entry: &DirEntryData) -> bool {
635 if raw_entry.is_deleted() {
636 return true;
637 }
638 match raw_entry {
639 DirEntryData::File(sfn_entry) => self.skip_volume && sfn_entry.is_volume(),
640 DirEntryData::Lfn(_) => false,
641 }
642 }
643
644 #[allow(clippy::type_complexity)]
645 fn read_dir_entry(&mut self) -> Result<Option<DirEntry<'a, IO, TP, OCC>>, Error<IO::Error>> {
646 trace!("DirIter::read_dir_entry");
647 let mut lfn_builder = LongNameBuilder::new();
648 let mut offset = self.stream.seek(SeekFrom::Current(0))?;
649 let mut begin_offset = offset;
650 loop {
651 let raw_entry = DirEntryData::deserialize(&mut self.stream)?;
652 offset += u64::from(DIR_ENTRY_SIZE);
653 if raw_entry.is_end() {
655 return Ok(None);
656 }
657 if self.should_skip_entry(&raw_entry) {
659 trace!("skip entry");
660 lfn_builder.clear();
661 begin_offset = offset;
662 continue;
663 }
664 match raw_entry {
665 DirEntryData::File(data) => {
666 let end_abs_pos = self.stream.abs_pos().unwrap();
672 let abs_pos = end_abs_pos - u64::from(DIR_ENTRY_SIZE);
674 lfn_builder.validate_chksum(data.name());
676 let short_name = ShortName::new(data.name());
678 trace!("file entry {:?}", data.name());
679 return Ok(Some(DirEntry {
680 data,
681 short_name,
682 #[cfg(feature = "lfn")]
683 lfn_utf16: lfn_builder.into_buf(),
684 fs: self.fs,
685 entry_pos: abs_pos,
686 offset_range: (begin_offset, offset),
687 }));
688 }
689 DirEntryData::Lfn(data) => {
690 trace!("lfn entry");
692 lfn_builder.process(&data);
693 }
694 }
695 }
696 }
697}
698
699impl<IO: ReadWriteSeek, TP, OCC> Clone for DirIter<'_, IO, TP, OCC> {
701 fn clone(&self) -> Self {
702 Self {
703 stream: self.stream.clone(),
704 fs: self.fs,
705 err: self.err,
706 skip_volume: self.skip_volume,
707 }
708 }
709}
710
711impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC> Iterator for DirIter<'a, IO, TP, OCC> {
712 type Item = Result<DirEntry<'a, IO, TP, OCC>, Error<IO::Error>>;
713
714 fn next(&mut self) -> Option<Self::Item> {
715 if self.err {
716 return None;
717 }
718 let r = self.read_dir_entry();
719 match r {
720 Ok(Some(e)) => Some(Ok(e)),
721 Ok(None) => None,
722 Err(err) => {
723 self.err = true;
724 Some(Err(err))
725 }
726 }
727 }
728}
729
730#[rustfmt::skip]
731fn validate_long_name<E: IoError>(name: &str) -> Result<(), Error<E>> {
732 if name.is_empty() {
734 return Err(Error::InvalidFileNameLength);
735 }
736 if name.len() > MAX_LONG_NAME_LEN {
737 return Err(Error::InvalidFileNameLength);
738 }
739 for c in name.chars() {
741 match c {
742 'a'..='z' | 'A'..='Z' | '0'..='9'
743 | '\u{80}'..='\u{FFFF}'
744 | '$' | '%' | '\'' | '-' | '_' | '@' | '~' | '`' | '!' | '(' | ')' | '{' | '}' | '.' | ' ' | '+' | ','
745 | ';' | '=' | '[' | ']' | '^' | '#' | '&' => {},
746 _ => return Err(Error::UnsupportedFileNameCharacter),
747 }
748 }
749 Ok(())
750}
751
752fn lfn_checksum(short_name: &[u8; SFN_SIZE]) -> u8 {
753 let mut chksum = num::Wrapping(0_u8);
754 for b in short_name {
755 chksum = (chksum << 7) + (chksum >> 1) + num::Wrapping(*b);
756 }
757 chksum.0
758}
759
760#[cfg(all(feature = "lfn", feature = "alloc"))]
761#[derive(Clone)]
762pub(crate) struct LfnBuffer {
763 ucs2_units: Vec<u16>,
764}
765
766const MAX_LONG_NAME_LEN: usize = 255;
767
768#[cfg(feature = "lfn")]
769const MAX_LONG_DIR_ENTRIES: usize = (MAX_LONG_NAME_LEN + LFN_PART_LEN - 1) / LFN_PART_LEN;
770
771#[cfg(all(feature = "lfn", not(feature = "alloc")))]
772const LONG_NAME_BUFFER_LEN: usize = MAX_LONG_DIR_ENTRIES * LFN_PART_LEN;
773
774#[cfg(all(feature = "lfn", not(feature = "alloc")))]
775#[derive(Clone)]
776pub(crate) struct LfnBuffer {
777 ucs2_units: [u16; LONG_NAME_BUFFER_LEN],
778 len: usize,
779}
780
781#[cfg(all(feature = "lfn", feature = "alloc"))]
782impl LfnBuffer {
783 fn new() -> Self {
784 Self {
785 ucs2_units: Vec::<u16>::new(),
786 }
787 }
788
789 fn from_ucs2_units<I: Iterator<Item = u16>>(usc2_units: I) -> Self {
790 Self {
791 ucs2_units: usc2_units.collect(),
792 }
793 }
794
795 fn clear(&mut self) {
796 self.ucs2_units.clear();
797 }
798
799 pub(crate) fn len(&self) -> usize {
800 self.ucs2_units.len()
801 }
802
803 fn set_len(&mut self, len: usize) {
804 self.ucs2_units.resize(len, 0_u16);
805 }
806
807 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
808 &self.ucs2_units
809 }
810}
811
812#[cfg(all(feature = "lfn", not(feature = "alloc")))]
813impl LfnBuffer {
814 fn new() -> Self {
815 Self {
816 ucs2_units: [0_u16; LONG_NAME_BUFFER_LEN],
817 len: 0,
818 }
819 }
820
821 fn from_ucs2_units<I: Iterator<Item = u16>>(usc2_units: I) -> Self {
822 let mut lfn = Self {
823 ucs2_units: [0_u16; LONG_NAME_BUFFER_LEN],
824 len: 0,
825 };
826 for (i, usc2_unit) in usc2_units.enumerate() {
827 lfn.ucs2_units[i] = usc2_unit;
828 lfn.len += 1;
829 }
830 lfn
831 }
832
833 fn clear(&mut self) {
834 self.ucs2_units = [0_u16; LONG_NAME_BUFFER_LEN];
835 self.len = 0;
836 }
837
838 pub(crate) fn len(&self) -> usize {
839 self.len
840 }
841
842 fn set_len(&mut self, len: usize) {
843 self.len = len;
844 }
845
846 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
847 &self.ucs2_units[..self.len]
848 }
849}
850
851#[cfg(not(feature = "lfn"))]
852#[derive(Clone)]
853pub(crate) struct LfnBuffer {}
854
855#[cfg(not(feature = "lfn"))]
856impl LfnBuffer {
857 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
858 &[]
859 }
860}
861
862#[cfg(feature = "lfn")]
863struct LongNameBuilder {
864 buf: LfnBuffer,
865 chksum: u8,
866 index: u8,
867}
868
869#[cfg(feature = "lfn")]
870impl LongNameBuilder {
871 fn new() -> Self {
872 Self {
873 buf: LfnBuffer::new(),
874 chksum: 0,
875 index: 0,
876 }
877 }
878
879 fn clear(&mut self) {
880 self.buf.clear();
881 self.index = 0;
882 }
883
884 fn into_buf(mut self) -> LfnBuffer {
885 if self.index == 1 {
887 self.truncate();
888 } else if !self.is_empty() {
889 warn!("unfinished LFN sequence {}", self.index);
890 self.clear();
891 }
892 self.buf
893 }
894
895 fn truncate(&mut self) {
896 let ucs2_units = &self.buf.ucs2_units;
898 let new_len = ucs2_units
899 .iter()
900 .rposition(|c| *c != 0xFFFF && *c != 0)
901 .map_or(0, |n| n + 1);
902 self.buf.set_len(new_len);
903 }
904
905 fn is_empty(&self) -> bool {
906 self.index == 0
909 }
910
911 fn process(&mut self, data: &DirLfnEntryData) {
912 let is_last = (data.order() & LFN_ENTRY_LAST_FLAG) != 0;
913 let index = data.order() & 0x1F;
914 if index == 0 || usize::from(index) > MAX_LONG_DIR_ENTRIES {
915 warn!("currupted lfn entry! {:x}", data.order());
917 self.clear();
918 return;
919 }
920 if is_last {
921 self.index = index;
923 self.chksum = data.checksum();
924 self.buf.set_len(usize::from(index) * LFN_PART_LEN);
925 } else if self.index == 0 || index != self.index - 1 || data.checksum() != self.chksum {
926 warn!(
928 "currupted lfn entry! {:x} {:x} {:x} {:x}",
929 data.order(),
930 self.index,
931 data.checksum(),
932 self.chksum
933 );
934 self.clear();
935 return;
936 } else {
937 self.index -= 1;
939 }
940 let pos = LFN_PART_LEN * usize::from(index - 1);
941 data.copy_name_to_slice(&mut self.buf.ucs2_units[pos..pos + 13]);
943 }
944
945 fn validate_chksum(&mut self, short_name: &[u8; SFN_SIZE]) {
946 if self.is_empty() {
947 return;
949 }
950 let chksum = lfn_checksum(short_name);
951 if chksum != self.chksum {
952 warn!("checksum mismatch {:x} {:x} {:?}", chksum, self.chksum, short_name);
953 self.clear();
954 }
955 }
956}
957
958#[cfg(not(feature = "lfn"))]
960struct LongNameBuilder {}
961#[cfg(not(feature = "lfn"))]
962impl LongNameBuilder {
963 fn new() -> Self {
964 LongNameBuilder {}
965 }
966 fn clear(&mut self) {}
967 fn into_vec(self) {}
968 fn truncate(&mut self) {}
969 fn process(&mut self, _data: &DirLfnEntryData) {}
970 fn validate_chksum(&mut self, _short_name: &[u8; SFN_SIZE]) {}
971}
972
973#[cfg(feature = "lfn")]
974struct LfnEntriesGenerator<'a> {
975 name_parts_iter: iter::Rev<slice::Chunks<'a, u16>>,
976 checksum: u8,
977 index: usize,
978 num: usize,
979 ended: bool,
980}
981
982#[cfg(feature = "lfn")]
983impl<'a> LfnEntriesGenerator<'a> {
984 fn new(name_utf16: &'a [u16], checksum: u8) -> Self {
985 let num_entries = (name_utf16.len() + LFN_PART_LEN - 1) / LFN_PART_LEN;
986 LfnEntriesGenerator {
988 checksum,
989 name_parts_iter: name_utf16.chunks(LFN_PART_LEN).rev(),
990 index: 0,
991 num: num_entries,
992 ended: false,
993 }
994 }
995}
996
997#[cfg(feature = "lfn")]
998impl Iterator for LfnEntriesGenerator<'_> {
999 type Item = DirLfnEntryData;
1000
1001 fn next(&mut self) -> Option<Self::Item> {
1002 if self.ended {
1003 return None;
1004 }
1005
1006 if let Some(name_part) = self.name_parts_iter.next() {
1008 let lfn_index = self.num - self.index;
1009 let mut order = lfn_index as u8;
1010 if self.index == 0 {
1011 order |= LFN_ENTRY_LAST_FLAG;
1013 }
1014 debug_assert!(order > 0);
1015 let mut lfn_part = [LFN_PADDING; LFN_PART_LEN];
1016 lfn_part[..name_part.len()].copy_from_slice(name_part);
1017 if name_part.len() < LFN_PART_LEN {
1018 lfn_part[name_part.len()] = 0;
1020 }
1021 let mut lfn_entry = DirLfnEntryData::new(order, self.checksum);
1023 lfn_entry.copy_name_from_slice(&lfn_part);
1024 self.index += 1;
1025 Some(lfn_entry)
1026 } else {
1027 self.ended = true;
1029 None
1030 }
1031 }
1032
1033 fn size_hint(&self) -> (usize, Option<usize>) {
1034 self.name_parts_iter.size_hint()
1035 }
1036}
1037
1038#[cfg(feature = "lfn")]
1040impl ExactSizeIterator for LfnEntriesGenerator<'_> {}
1041
1042#[cfg(not(feature = "lfn"))]
1044struct LfnEntriesGenerator {}
1045#[cfg(not(feature = "lfn"))]
1046impl LfnEntriesGenerator {
1047 fn new(_name_utf16: &[u16], _checksum: u8) -> Self {
1048 LfnEntriesGenerator {}
1049 }
1050}
1051#[cfg(not(feature = "lfn"))]
1052impl Iterator for LfnEntriesGenerator {
1053 type Item = DirLfnEntryData;
1054
1055 fn next(&mut self) -> Option<Self::Item> {
1056 None
1057 }
1058
1059 fn size_hint(&self) -> (usize, Option<usize>) {
1060 (0, Some(0))
1061 }
1062}
1063#[cfg(not(feature = "lfn"))]
1064impl ExactSizeIterator for LfnEntriesGenerator {}
1065
1066#[derive(Default, Debug, Clone)]
1067struct ShortNameGenerator {
1068 chksum: u16,
1069 long_prefix_bitmap: u16,
1070 prefix_chksum_bitmap: u16,
1071 name_fits: bool,
1072 lossy_conv: bool,
1073 exact_match: bool,
1074 basename_len: usize,
1075 short_name: [u8; SFN_SIZE],
1076}
1077
1078impl ShortNameGenerator {
1079 fn new(name: &str) -> Self {
1080 let mut short_name = [SFN_PADDING; SFN_SIZE];
1082 let dot_index_opt = name[1..].rfind('.').map(|index| index + 1);
1085 let basename_src = dot_index_opt.map_or(name, |dot_index| &name[..dot_index]);
1087 let (basename_len, basename_fits, basename_lossy) =
1088 Self::copy_short_name_part(&mut short_name[0..8], basename_src);
1089 let (name_fits, lossy_conv) = dot_index_opt.map_or((basename_fits, basename_lossy), |dot_index| {
1091 let (_, ext_fits, ext_lossy) = Self::copy_short_name_part(&mut short_name[8..11], &name[dot_index + 1..]);
1092 (basename_fits && ext_fits, basename_lossy || ext_lossy)
1093 });
1094 let chksum = Self::checksum(name);
1095 Self {
1096 chksum,
1097 name_fits,
1098 lossy_conv,
1099 basename_len,
1100 short_name,
1101 ..Self::default()
1102 }
1103 }
1104
1105 fn generate_dot() -> [u8; SFN_SIZE] {
1106 let mut short_name = [SFN_PADDING; SFN_SIZE];
1107 short_name[0] = b'.';
1108 short_name
1109 }
1110
1111 fn generate_dotdot() -> [u8; SFN_SIZE] {
1112 let mut short_name = [SFN_PADDING; SFN_SIZE];
1113 short_name[0] = b'.';
1114 short_name[1] = b'.';
1115 short_name
1116 }
1117
1118 fn copy_short_name_part(dst: &mut [u8], src: &str) -> (usize, bool, bool) {
1119 let mut dst_pos = 0;
1120 let mut lossy_conv = false;
1121 for c in src.chars() {
1122 if dst_pos == dst.len() {
1123 return (dst_pos, false, lossy_conv);
1125 }
1126 #[rustfmt::skip]
1128 let fixed_c = match c {
1129 ' ' | '.' => {
1131 lossy_conv = true;
1132 continue;
1133 },
1134 'A'..='Z' | 'a'..='z' | '0'..='9'
1136 | '!' | '#' | '$' | '%' | '&' | '\'' | '(' | ')' | '-' | '@' | '^' | '_' | '`' | '{' | '}' | '~' => c,
1137 _ => '_',
1139 };
1140 lossy_conv = lossy_conv || (fixed_c != c);
1142 let upper = fixed_c.to_ascii_uppercase();
1144 dst[dst_pos] = upper as u8; dst_pos += 1;
1146 }
1147 (dst_pos, true, lossy_conv)
1148 }
1149
1150 fn add_existing(&mut self, short_name: &[u8; SFN_SIZE]) {
1151 if short_name == &self.short_name {
1153 self.exact_match = true;
1154 }
1155 self.check_for_long_prefix_collision(short_name);
1157
1158 self.check_for_short_prefix_collision(short_name);
1160 }
1161
1162 fn check_for_long_prefix_collision(&mut self, short_name: &[u8; SFN_SIZE]) {
1163 let long_prefix_len = 6.min(self.basename_len);
1165 if short_name[long_prefix_len] != b'~' {
1166 return;
1167 }
1168 if let Some(num_suffix) = char::from(short_name[long_prefix_len + 1]).to_digit(10) {
1169 let long_prefix_matches = short_name[..long_prefix_len] == self.short_name[..long_prefix_len];
1170 let ext_matches = short_name[8..] == self.short_name[8..];
1171 if long_prefix_matches && ext_matches {
1172 self.long_prefix_bitmap |= 1 << num_suffix;
1173 }
1174 }
1175 }
1176
1177 fn check_for_short_prefix_collision(&mut self, short_name: &[u8; SFN_SIZE]) {
1178 let short_prefix_len = 2.min(self.basename_len);
1180 if short_name[short_prefix_len + 4] != b'~' {
1181 return;
1182 }
1183 if let Some(num_suffix) = char::from(short_name[short_prefix_len + 4 + 1]).to_digit(10) {
1184 let short_prefix_matches = short_name[..short_prefix_len] == self.short_name[..short_prefix_len];
1185 let ext_matches = short_name[8..] == self.short_name[8..];
1186 if short_prefix_matches && ext_matches {
1187 let chksum_res = str::from_utf8(&short_name[short_prefix_len..short_prefix_len + 4])
1188 .map(|s| u16::from_str_radix(s, 16));
1189 if chksum_res == Ok(Ok(self.chksum)) {
1190 self.prefix_chksum_bitmap |= 1 << num_suffix;
1191 }
1192 }
1193 }
1194 }
1195
1196 fn checksum(name: &str) -> u16 {
1197 let mut chksum = num::Wrapping(0_u16);
1199 for c in name.chars() {
1200 chksum = (chksum >> 1) + (chksum << 15) + num::Wrapping(c as u16);
1201 }
1202 chksum.0
1203 }
1204
1205 fn generate(&self) -> Result<[u8; SFN_SIZE], Error<()>> {
1206 if !self.lossy_conv && self.name_fits && !self.exact_match {
1207 return Ok(self.short_name);
1210 }
1211 for i in 1..5 {
1213 if self.long_prefix_bitmap & (1 << i) == 0 {
1214 return Ok(self.build_prefixed_name(i, false));
1215 }
1216 }
1217 for i in 1..10 {
1219 if self.prefix_chksum_bitmap & (1 << i) == 0 {
1220 return Ok(self.build_prefixed_name(i, true));
1221 }
1222 }
1223 Err(Error::AlreadyExists)
1225 }
1226
1227 fn next_iteration(&mut self) {
1228 self.chksum = (num::Wrapping(self.chksum) + num::Wrapping(1)).0;
1230 self.long_prefix_bitmap = 0;
1232 self.prefix_chksum_bitmap = 0;
1233 }
1234
1235 fn build_prefixed_name(&self, num: u32, with_chksum: bool) -> [u8; SFN_SIZE] {
1236 let mut buf = [SFN_PADDING; SFN_SIZE];
1237 let prefix_len = if with_chksum {
1238 let prefix_len = 2.min(self.basename_len);
1239 buf[..prefix_len].copy_from_slice(&self.short_name[..prefix_len]);
1240 buf[prefix_len..prefix_len + 4].copy_from_slice(&Self::u16_to_hex(self.chksum));
1241 prefix_len + 4
1242 } else {
1243 let prefix_len = 6.min(self.basename_len);
1244 buf[..prefix_len].copy_from_slice(&self.short_name[..prefix_len]);
1245 prefix_len
1246 };
1247 buf[prefix_len] = b'~';
1248 buf[prefix_len + 1] = char::from_digit(num, 10).unwrap() as u8; buf[8..].copy_from_slice(&self.short_name[8..]);
1250 buf
1251 }
1252
1253 fn u16_to_hex(x: u16) -> [u8; 4] {
1254 let x_u32 = u32::from(x);
1257 let mut hex_bytes = [
1258 char::from_digit((x_u32 >> 12) & 0xF, 16).unwrap() as u8,
1259 char::from_digit((x_u32 >> 8) & 0xF, 16).unwrap() as u8,
1260 char::from_digit((x_u32 >> 4) & 0xF, 16).unwrap() as u8,
1261 char::from_digit(x_u32 & 0xF, 16).unwrap() as u8,
1262 ];
1263 hex_bytes.make_ascii_uppercase();
1264 hex_bytes
1265 }
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270 use super::*;
1271
1272 #[test]
1273 fn test_split_path() {
1274 assert_eq!(split_path("aaa/bbb/ccc"), ("aaa", Some("bbb/ccc")));
1275 assert_eq!(split_path("aaa/bbb"), ("aaa", Some("bbb")));
1276 assert_eq!(split_path("aaa"), ("aaa", None));
1277 }
1278
1279 #[test]
1280 fn test_generate_short_name() {
1281 assert_eq!(ShortNameGenerator::new("Foo").generate().ok(), Some(*b"FOO "));
1282 assert_eq!(ShortNameGenerator::new("Foo.b").generate().ok(), Some(*b"FOO B "));
1283 assert_eq!(
1284 ShortNameGenerator::new("Foo.baR").generate().ok(),
1285 Some(*b"FOO BAR")
1286 );
1287 assert_eq!(
1288 ShortNameGenerator::new("Foo+1.baR").generate().ok(),
1289 Some(*b"FOO_1~1 BAR")
1290 );
1291 assert_eq!(
1292 ShortNameGenerator::new("ver +1.2.text").generate().ok(),
1293 Some(*b"VER_12~1TEX")
1294 );
1295 assert_eq!(
1296 ShortNameGenerator::new(".bashrc.swp").generate().ok(),
1297 Some(*b"BASHRC~1SWP")
1298 );
1299 assert_eq!(ShortNameGenerator::new(".foo").generate().ok(), Some(*b"FOO~1 "));
1300 }
1301
1302 #[test]
1303 fn test_short_name_checksum_overflow() {
1304 ShortNameGenerator::checksum("\u{FF5A}\u{FF5A}\u{FF5A}\u{FF5A}");
1305 }
1306
1307 #[test]
1308 fn test_lfn_checksum_overflow() {
1309 lfn_checksum(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
1310 }
1311
1312 #[test]
1313 fn test_generate_short_name_collisions_long() {
1314 let mut buf: [u8; SFN_SIZE];
1315 let mut gen = ShortNameGenerator::new("TextFile.Mine.txt");
1316 buf = gen.generate().unwrap();
1317 assert_eq!(&buf, b"TEXTFI~1TXT");
1318 gen.add_existing(&buf);
1319 buf = gen.generate().unwrap();
1320 assert_eq!(&buf, b"TEXTFI~2TXT");
1321 gen.add_existing(&buf);
1322 buf = gen.generate().unwrap();
1323 assert_eq!(&buf, b"TEXTFI~3TXT");
1324 gen.add_existing(&buf);
1325 buf = gen.generate().unwrap();
1326 assert_eq!(&buf, b"TEXTFI~4TXT");
1327 gen.add_existing(&buf);
1328 buf = gen.generate().unwrap();
1329 assert_eq!(&buf, b"TE527D~1TXT");
1330 gen.add_existing(&buf);
1331 buf = gen.generate().unwrap();
1332 assert_eq!(&buf, b"TE527D~2TXT");
1333 for i in 3..10 {
1334 gen.add_existing(&buf);
1335 buf = gen.generate().unwrap();
1336 assert_eq!(&buf, format!("TE527D~{}TXT", i).as_bytes());
1337 }
1338 gen.add_existing(&buf);
1339 assert!(gen.generate().is_err());
1340 gen.next_iteration();
1341 for _i in 0..4 {
1342 buf = gen.generate().unwrap();
1343 gen.add_existing(&buf);
1344 }
1345 buf = gen.generate().unwrap();
1346 assert_eq!(&buf, b"TE527E~1TXT");
1347 }
1348
1349 #[test]
1350 fn test_generate_short_name_collisions_short() {
1351 let mut buf: [u8; SFN_SIZE];
1352 let mut gen = ShortNameGenerator::new("x.txt");
1353 buf = gen.generate().unwrap();
1354 assert_eq!(&buf, b"X TXT");
1355 gen.add_existing(&buf);
1356 buf = gen.generate().unwrap();
1357 assert_eq!(&buf, b"X~1 TXT");
1358 gen.add_existing(&buf);
1359 buf = gen.generate().unwrap();
1360 assert_eq!(&buf, b"X~2 TXT");
1361 gen.add_existing(&buf);
1362 buf = gen.generate().unwrap();
1363 assert_eq!(&buf, b"X~3 TXT");
1364 gen.add_existing(&buf);
1365 buf = gen.generate().unwrap();
1366 assert_eq!(&buf, b"X~4 TXT");
1367 gen.add_existing(&buf);
1368 buf = gen.generate().unwrap();
1369 assert_eq!(&buf, b"X40DA~1 TXT");
1370 gen.add_existing(&buf);
1371 buf = gen.generate().unwrap();
1372 assert_eq!(&buf, b"X40DA~2 TXT");
1373 }
1374}