1use crate::core::{char, cmp, num, str};
2#[cfg(feature = "lfn")]
3use crate::core::{iter, slice};
4use crate::io;
5use crate::io::prelude::*;
6use crate::io::{ErrorKind, SeekFrom};
7#[cfg(all(not(feature = "std"), feature = "alloc", feature = "lfn"))]
8use alloc::vec::Vec;
9
10use crate::dir_entry::{
11 DirEntry, DirEntryData, DirEntryEditor, DirFileEntryData, DirLfnEntryData, FileAttributes,
12 ShortName, DIR_ENTRY_SIZE,
13};
14#[cfg(feature = "lfn")]
15use crate::dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN};
16use crate::dir_entry::{SFN_PADDING, SFN_SIZE};
17use crate::error::FatfsError;
18use crate::file::File;
19use crate::fs::{DiskSlice, FileSystem, FsIoAdapter, OemCpConverter, ReadWriteSeek};
20use crate::time::{Date, DateTime, TimeProvider};
21use std::cell::{RefCell, RefMut};
22
23pub(crate) enum DirRawStream<'fs, IO: ReadWriteSeek, TP, OCC> {
24 File(Option<File<'fs, IO, TP, OCC>>),
25 Root(DiskSlice<FsIoAdapter<'fs, IO, TP, OCC>>),
26}
27
28impl<IO: ReadWriteSeek, TP, OCC> DirRawStream<'_, IO, TP, OCC> {
29 fn abs_pos(&self) -> Result<Option<u64>, FatfsError> {
30 match self {
31 DirRawStream::File(file) => {
32 if let Some(ref file) = file {
33 Ok(file.abs_pos()?)
34 } else {
35 Ok(None)
36 }
37 }
38 DirRawStream::Root(slice) => Ok(Some(slice.abs_pos())),
39 }
40 }
41
42 fn first_cluster(&self) -> Option<u32> {
43 match self {
44 DirRawStream::File(file) => file.as_ref().and_then(|f| f.first_cluster()),
45 DirRawStream::Root(_) => None,
46 }
47 }
48
49 fn entry_mut(&mut self) -> Option<&mut DirEntryEditor> {
50 match self {
51 DirRawStream::File(file) => file.as_mut().and_then(|f| f.editor_mut()),
52 DirRawStream::Root(_) => None,
53 }
54 }
55
56 fn entry(&self) -> Option<&DirEntryEditor> {
57 match self {
58 DirRawStream::File(file) => file.as_ref().and_then(|f| f.editor()),
59 DirRawStream::Root(_) => None,
60 }
61 }
62
63 fn is_deleted(&self) -> bool {
64 match self {
65 DirRawStream::File(file) => file.as_ref().map_or(true, |f| f.is_deleted()),
66 DirRawStream::Root(_) => false,
67 }
68 }
69}
70
71impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Read for DirRawStream<'_, IO, TP, OCC> {
72 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
73 match self {
74 DirRawStream::File(Some(file)) => file.read(buf),
75 DirRawStream::File(None) => {
76 Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"))
77 }
78 DirRawStream::Root(raw) => raw.read(buf),
79 }
80 }
81}
82
83impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Write for DirRawStream<'_, IO, TP, OCC> {
84 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
85 match self {
86 DirRawStream::File(Some(file)) => file.write(buf),
87 DirRawStream::File(None) => {
88 Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"))
89 }
90 DirRawStream::Root(raw) => raw.write(buf),
91 }
92 }
93 fn flush(&mut self) -> io::Result<()> {
94 match self {
95 DirRawStream::File(Some(file)) => file.flush(),
96 DirRawStream::File(None) => {
97 Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"))
98 }
99 DirRawStream::Root(raw) => raw.flush(),
100 }
101 }
102}
103
104impl<IO: ReadWriteSeek, TP, OCC> Seek for DirRawStream<'_, IO, TP, OCC> {
105 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
106 match self {
107 DirRawStream::File(Some(file)) => file.seek(pos),
108 DirRawStream::File(None) => {
109 Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"))
110 }
111 DirRawStream::Root(raw) => raw.seek(pos),
112 }
113 }
114}
115
116pub fn validate_filename<'a>(name: &'a str) -> io::Result<()> {
118 if name == "." || name == ".." {
119 return Ok(());
120 }
121
122 if name.ends_with(&['.', ' '][..]) || name.is_empty() {
128 return Err(io::Error::new(ErrorKind::InvalidInput, "Illegal filename"));
129 }
130 Ok(())
131}
132
133fn split_path<'a>(path: &'a str) -> io::Result<(&'a str, Option<&'a str>)> {
134 let mut path_split = path.trim_matches('/').splitn(2, '/');
136 let comp = path_split.next().unwrap(); validate_filename(comp)?;
138 let rest_opt = path_split.next();
139 Ok((comp, rest_opt))
140}
141
142enum DirEntryOrShortName<'fs, IO: ReadWriteSeek, TP, OCC> {
143 DirEntry(DirEntry<'fs, IO, TP, OCC>),
144 ShortName([u8; SFN_SIZE]),
145}
146
147pub struct Dir<'fs, IO: ReadWriteSeek, TP, OCC> {
152 stream: RefCell<DirRawStream<'fs, IO, TP, OCC>>,
153 fs: &'fs FileSystem<IO, TP, OCC>,
154 is_root: bool,
155}
156
157impl<'fs, IO: ReadWriteSeek, TP, OCC> Dir<'fs, IO, TP, OCC> {
158 pub(crate) fn new(
159 stream: DirRawStream<'fs, IO, TP, OCC>,
160 fs: &'fs FileSystem<IO, TP, OCC>,
161 is_root: bool,
162 ) -> Self {
163 Dir { stream: RefCell::new(stream), fs, is_root }
164 }
165
166 pub fn iter<'a>(&'a self) -> DirIter<'a, 'fs, IO, TP, OCC> {
168 DirIter::new(&self.stream, self.fs, true)
169 }
170}
171
172impl<'fs, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'fs, IO, TP, OCC> {
173 fn find_entry(
174 &self,
175 name: &str,
176 is_dir: Option<bool>,
177 mut short_name_gen: Option<&mut ShortNameGenerator>,
178 ) -> io::Result<DirEntry<'fs, IO, TP, OCC>> {
179 for r in self.iter() {
180 let e = r?;
181 if e.eq_name(name) {
183 if is_dir.is_some() && Some(e.is_dir()) != is_dir {
185 let error =
186 if e.is_dir() { FatfsError::IsDirectory } else { FatfsError::NotDirectory };
187 return Err(io::Error::new(ErrorKind::Other, error));
188 }
189 return Ok(e);
190 }
191 if let Some(ref mut gen) = short_name_gen {
193 gen.add_existing(e.raw_short_name());
194 }
195 }
196 Err(io::Error::new(ErrorKind::NotFound, "No such file or directory"))
197 }
198
199 pub(crate) fn find_volume_entry(&self) -> io::Result<Option<DirEntry<'fs, IO, TP, OCC>>> {
200 for r in DirIter::new(&self.stream, self.fs, false) {
201 let e = r?;
202 if e.data.is_volume() {
203 return Ok(Some(e));
204 }
205 }
206 Ok(None)
207 }
208
209 fn check_for_existence(
210 &self,
211 name: &str,
212 is_dir: Option<bool>,
213 ) -> io::Result<DirEntryOrShortName<'fs, IO, TP, OCC>> {
214 let mut short_name_gen = ShortNameGenerator::new(name);
215 loop {
216 let r = self.find_entry(name, is_dir, Some(&mut short_name_gen));
217 match r {
218 Err(ref err) if err.kind() == ErrorKind::NotFound => {}
219 Err(err) => return Err(err),
221 Ok(e) => return Ok(DirEntryOrShortName::DirEntry(e)),
223 };
224 if let Ok(name) = short_name_gen.generate() {
225 return Ok(DirEntryOrShortName::ShortName(name));
226 }
227 short_name_gen.next_iteration();
228 }
229 }
230
231 pub fn set_created(&mut self, date_time: DateTime) {
233 match self.stream.borrow_mut().entry_mut() {
234 Some(e) => e.set_created(date_time),
235 None => {}
236 }
237 }
238
239 pub fn set_accessed(&mut self, date: Date) {
241 match self.stream.borrow_mut().entry_mut() {
242 Some(e) => e.set_accessed(date),
243 None => {}
244 }
245 }
246
247 pub fn set_modified(&mut self, date_time: DateTime) {
249 match self.stream.borrow_mut().entry_mut() {
250 Some(e) => e.set_modified(date_time),
251 None => {}
252 }
253 }
254
255 pub fn accessed(&self) -> Date {
257 match self.stream.borrow().entry() {
258 Some(ref e) => e.inner().accessed(),
259 None => Date::epoch(),
260 }
261 }
262
263 pub fn created(&self) -> DateTime {
265 match self.stream.borrow().entry() {
266 Some(ref e) => e.inner().created(),
267 None => DateTime::epoch(),
268 }
269 }
270
271 pub fn modified(&self) -> DateTime {
273 match self.stream.borrow().entry() {
274 Some(ref e) => e.inner().modified(),
275 None => DateTime::epoch(),
276 }
277 }
278
279 pub fn open_dir(&self, path: &str) -> io::Result<Self> {
283 trace!("open_dir {}", path);
284 let (name, rest_opt) = split_path(path)?;
285 let e = self.find_entry(name, Some(true), None)?;
286 match rest_opt {
287 Some(rest) => e.to_dir().open_dir(rest),
288 None => Ok(e.to_dir()),
289 }
290 }
291
292 pub fn open_file(&self, path: &str) -> io::Result<File<'fs, IO, TP, OCC>> {
296 trace!("open_file {}", path);
297 let (name, rest_opt) = split_path(path)?;
299 if let Some(rest) = rest_opt {
300 let e = self.find_entry(name, Some(true), None)?;
301 return e.to_dir().open_file(rest);
302 }
303 let e = self.find_entry(name, Some(false), None)?;
305 Ok(e.to_file())
306 }
307
308 pub fn create_file(&self, path: &str) -> io::Result<File<'fs, IO, TP, OCC>> {
313 trace!("create_file {}", path);
314 let (name, rest_opt) = split_path(path)?;
316 if let Some(rest) = rest_opt {
317 return self.find_entry(name, Some(true), None)?.to_dir().create_file(rest);
318 }
319 if self.stream.borrow().is_deleted() {
320 return Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"));
321 }
322 let r = self.check_for_existence(name, Some(false))?;
324 match r {
325 DirEntryOrShortName::ShortName(short_name) => {
327 let sfn_entry =
328 self.create_sfn_entry(short_name, FileAttributes::from_bits_truncate(0), None);
329 Ok(self.write_entry(name, sfn_entry)?.to_file())
330 }
331 DirEntryOrShortName::DirEntry(e) => Ok(e.to_file()),
333 }
334 }
335
336 pub fn create_dir(&self, path: &str) -> io::Result<Self> {
340 trace!("create_dir {}", path);
341 let (name, rest_opt) = split_path(path)?;
343 if let Some(rest) = rest_opt {
344 return self.find_entry(name, Some(true), None)?.to_dir().create_dir(rest);
345 }
346 if self.stream.borrow().is_deleted() {
347 return Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"));
348 }
349 let r = self.check_for_existence(name, Some(true))?;
351 match r {
352 DirEntryOrShortName::ShortName(short_name) => {
354 let transaction = self.fs.begin_transaction().unwrap();
355 let cluster = self.fs.alloc_cluster(None, true)?;
357 let sfn_entry =
359 self.create_sfn_entry(short_name, FileAttributes::DIRECTORY, Some(cluster));
360 let entry = self.write_entry(name, sfn_entry)?;
361 let dir = entry.to_dir();
362 let dot_sfn = ShortNameGenerator::generate_dot();
364 let sfn_entry = self.create_sfn_entry(
365 dot_sfn,
366 FileAttributes::DIRECTORY,
367 entry.first_cluster(),
368 );
369 dir.write_entry(".", sfn_entry)?;
370 let dotdot_sfn = ShortNameGenerator::generate_dotdot();
371 let sfn_entry = self.create_sfn_entry(
372 dotdot_sfn,
373 FileAttributes::DIRECTORY,
374 self.cluster_for_child_dotdot_entry(),
375 );
376 dir.write_entry("..", sfn_entry)?;
377 self.fs.commit(transaction)?;
378 Ok(dir)
379 }
380 DirEntryOrShortName::DirEntry(e) => Ok(e.to_dir()),
382 }
383 }
384
385 pub fn is_empty(&self) -> io::Result<bool> {
386 trace!("is_empty");
387 for r in self.iter() {
389 let e = r?;
390 let name = e.short_file_name_as_bytes();
391 if name != b"." && name != b".." {
393 return Ok(false);
394 }
395 }
396 Ok(true)
397 }
398
399 pub fn remove(&self, path: &str) -> io::Result<()> {
405 trace!("remove {}", path);
406 let (name, rest_opt) = split_path(path)?;
408 if let Some(rest) = rest_opt {
409 let e = self.find_entry(name, Some(true), None)?;
410 return e.to_dir().remove(rest);
411 }
412 let e = self.find_entry(name, None, None)?;
414 if e.is_dir() && !e.to_dir().is_empty()? {
415 return Err(io::Error::new(ErrorKind::Other, FatfsError::DirectoryNotEmpty));
416 }
417 if let Some(n) = e.first_cluster() {
419 self.fs.free_cluster_chain(n)?;
420 }
421 let mut stream = self.stream.borrow_mut();
423 stream.seek(SeekFrom::Start(e.offset_range.0 as u64))?;
424 let num = (e.offset_range.1 - e.offset_range.0) as usize / DIR_ENTRY_SIZE as usize;
425 for _ in 0..num {
426 let mut data = DirEntryData::deserialize(&mut *stream)?;
427 trace!("removing dir entry {:?}", data);
428 data.set_deleted();
429 stream.seek(SeekFrom::Current(-(DIR_ENTRY_SIZE as i64)))?;
430 data.serialize(&mut *stream)?;
431 }
432 Ok(())
433 }
434
435 fn unlink_file_on_disk(&self, offset_range: (u64, u64)) -> io::Result<()> {
436 let mut stream = self.stream.borrow_mut();
437 stream.seek(io::SeekFrom::Start(offset_range.0))?;
438 let num = (offset_range.1 - offset_range.0) as usize / DIR_ENTRY_SIZE as usize;
439 for _ in 0..num {
440 let mut data = DirEntryData::deserialize(&mut *stream)?;
441 trace!("removing dir entry {:?}", data);
442 data.set_deleted();
443 stream.seek(SeekFrom::Current(-(DIR_ENTRY_SIZE as i64)))?;
444 data.serialize(&mut *stream)?;
445 }
446 Ok(())
447 }
448
449 pub fn unlink_file(&self, file: &mut File<IO, TP, OCC>) -> io::Result<()> {
455 let entry = file
456 .editor()
457 .ok_or(io::Error::new(ErrorKind::InvalidInput, "Can't delete file with no dirent"))?;
458 self.unlink_file_on_disk(entry.offset_range)?;
459 file.mark_deleted();
460 Ok(())
461 }
462
463 pub fn unlink_dir(&self, dir: &mut Dir<IO, TP, OCC>) -> io::Result<()> {
468 if !dir.is_empty()? {
469 return Err(io::Error::new(ErrorKind::Other, FatfsError::DirectoryNotEmpty));
470 }
471 match &mut *dir.stream.borrow_mut() {
472 DirRawStream::File(ref mut option) => {
473 if let Some(mut file) = option.take() {
474 self.unlink_file(&mut file)?;
475 file.purge()
476 } else {
477 return Err(io::Error::new(ErrorKind::NotFound, "Not found"));
479 }
480 }
481 DirRawStream::Root(_) => Err(io::Error::new(
482 ErrorKind::InvalidInput,
483 "Root directory is not child of any directory",
484 )),
485 }
486 }
487
488 pub fn rename(
496 &self,
497 src_path: &str,
498 dst_dir: &Dir<IO, TP, OCC>,
499 dst_path: &str,
500 ) -> io::Result<()> {
501 trace!("rename {} {}", src_path, dst_path);
502 let (name, rest_opt) = split_path(src_path)?;
504 if let Some(rest) = rest_opt {
505 let e = self.find_entry(name, Some(true), None)?;
506 return e.to_dir().rename(rest, dst_dir, dst_path);
507 }
508 let (name, rest_opt) = split_path(dst_path)?;
510 if let Some(rest) = rest_opt {
511 let e = dst_dir.find_entry(name, Some(true), None)?;
512 return self.rename(src_path, &e.to_dir(), rest);
513 }
514
515 self.rename_internal(src_path, dst_dir, dst_path)
517 }
518
519 pub fn rename_over_file(
521 &self,
522 src_path: &str,
523 dst_dir: &Dir<IO, TP, OCC>,
524 dst_path: &str,
525 file: &mut File<IO, TP, OCC>,
526 ) -> io::Result<()> {
527 let transaction = self.fs.begin_transaction().unwrap();
528 let entry = file
529 .editor()
530 .ok_or(io::Error::new(ErrorKind::InvalidInput, "Can't delete file with no dirent"))?;
531 dst_dir.unlink_file_on_disk(entry.offset_range)?;
532 self.rename(src_path, dst_dir, dst_path)?;
533 self.fs.commit(transaction)?;
534 file.mark_deleted();
535 Ok(())
536 }
537
538 pub fn rename_over_dir(
540 &self,
541 src_path: &str,
542 dst_dir: &Dir<IO, TP, OCC>,
543 dst_path: &str,
544 dir: &mut Dir<IO, TP, OCC>,
545 ) -> io::Result<()> {
546 let transaction = self.fs.begin_transaction().unwrap();
547 if !dir.is_empty()? {
548 return Err(io::Error::new(ErrorKind::Other, FatfsError::DirectoryNotEmpty));
549 }
550 let file;
551 let mut stream = dir.stream.borrow_mut();
552 match &mut *stream {
553 DirRawStream::File(ref mut option) => {
554 file = option.as_mut().ok_or(io::Error::new(ErrorKind::NotFound, "Not found"))?;
555 let entry = file.editor().ok_or(io::Error::new(
556 ErrorKind::InvalidInput,
557 "Can't delete file with no dirent",
558 ))?;
559 dst_dir.unlink_file_on_disk(entry.offset_range)?;
560 if let Some(cluster) = file.first_cluster() {
561 self.fs.free_cluster_chain(cluster)?;
562 }
563 }
564 DirRawStream::Root(_) => {
565 return Err(io::Error::new(
566 ErrorKind::InvalidInput,
567 "Root directory is not child of any directory",
568 ))
569 }
570 };
571 self.rename(src_path, dst_dir, dst_path)?;
572 self.fs.commit(transaction)?;
573 file.mark_deleted_and_purged();
574 Ok(())
575 }
576
577 fn rename_internal(
578 &self,
579 src_name: &str,
580 dst_dir: &Dir<IO, TP, OCC>,
581 dst_name: &str,
582 ) -> io::Result<()> {
583 trace!("rename_internal {} {}", src_name, dst_name);
584 if dst_dir.stream.borrow().is_deleted() {
585 return Err(io::Error::new(
586 ErrorKind::NotFound,
587 "Destination directory has been deleted",
588 ));
589 }
590 let e = self.find_entry(src_name, None, None)?;
592 let r = dst_dir.check_for_existence(dst_name, None)?;
594 let short_name = match r {
595 DirEntryOrShortName::DirEntry(ref dst_e) => {
596 if !e.is_same_entry(dst_e) {
598 return Err(io::Error::new(
599 ErrorKind::AlreadyExists,
600 "Destination file already exists",
601 ));
602 }
603 if src_name == dst_name {
605 return Ok(());
606 }
607 dst_e.raw_short_name().clone()
609 }
610 DirEntryOrShortName::ShortName(short_name) => short_name,
611 };
612 {
614 let mut stream = self.stream.borrow_mut();
615 stream.seek(SeekFrom::Start(e.offset_range.0 as u64))?;
616 let num = (e.offset_range.1 - e.offset_range.0) as usize / DIR_ENTRY_SIZE as usize;
617 for _ in 0..num {
618 let mut data = DirEntryData::deserialize(&mut *stream)?;
619 trace!("removing LFN entry {:?}", data);
620 data.set_deleted();
621 stream.seek(SeekFrom::Current(-(DIR_ENTRY_SIZE as i64)))?;
622 data.serialize(&mut *stream)?;
623 }
624 }
625 let sfn_entry = e.data.renamed(short_name);
627 let dir_entry = dst_dir.write_entry(dst_name, sfn_entry)?;
628 if dir_entry.is_dir()
630 && self.cluster_for_child_dotdot_entry() != dst_dir.cluster_for_child_dotdot_entry()
631 {
632 let dir = dir_entry.to_dir();
633 let mut iter = dir.iter();
634 let error = || io::Error::new(ErrorKind::Other, "Missing expected .. entry");
635 let _ = iter.next().ok_or_else(error)??;
637 let entry = iter.next().ok_or_else(error)??;
638 if entry.short_file_name_as_bytes() != b".." {
639 return Err(error());
640 }
641 let mut editor = entry.editor();
642 editor.set_first_cluster(dst_dir.cluster_for_child_dotdot_entry(), self.fs.fat_type());
643 editor.flush(self.fs)?;
644 }
645 Ok(())
646 }
647
648 fn find_free_entries(
649 &self,
650 num_entries: usize,
651 ) -> io::Result<RefMut<'_, DirRawStream<'fs, IO, TP, OCC>>> {
652 let mut stream = self.stream.borrow_mut();
653 stream.seek(io::SeekFrom::Start(0))?;
654 let mut first_free = 0;
655 let mut num_free = 0;
656 let mut i = 0;
657 loop {
658 let raw_entry = DirEntryData::deserialize(&mut *stream)?;
659 if raw_entry.is_end() {
660 if num_free == 0 {
662 first_free = i;
663 }
664 stream.seek(io::SeekFrom::Start(first_free as u64 * DIR_ENTRY_SIZE))?;
665 return Ok(stream);
666 } else if raw_entry.is_deleted() {
667 if num_free == 0 {
669 first_free = i;
670 }
671 num_free += 1;
672 if num_free == num_entries {
673 stream.seek(io::SeekFrom::Start(first_free as u64 * DIR_ENTRY_SIZE))?;
675 return Ok(stream);
676 }
677 } else {
678 num_free = 0;
680 }
681 i += 1;
682 }
683 }
684
685 fn create_sfn_entry(
686 &self,
687 short_name: [u8; SFN_SIZE],
688 attrs: FileAttributes,
689 first_cluster: Option<u32>,
690 ) -> DirFileEntryData {
691 let mut raw_entry = DirFileEntryData::new(short_name, attrs);
692 raw_entry.set_first_cluster(first_cluster, self.fs.fat_type());
693 let now = self.fs.options.time_provider.get_current_date_time();
694 raw_entry.set_created(now);
695 raw_entry.set_accessed(now.date);
696 raw_entry.set_modified(now);
697 raw_entry
698 }
699
700 #[cfg(feature = "lfn")]
701 fn encode_lfn_utf16(name: &str) -> LfnBuffer {
702 LfnBuffer::from_ucs2_units(name.encode_utf16())
703 }
704 #[cfg(not(feature = "lfn"))]
705 fn encode_lfn_utf16(_name: &str) -> LfnBuffer {
706 LfnBuffer {}
707 }
708
709 fn alloc_and_write_lfn_entries(
710 &self,
711 lfn_utf16: &LfnBuffer,
712 short_name: &[u8; SFN_SIZE],
713 ) -> io::Result<(RefMut<'_, DirRawStream<'fs, IO, TP, OCC>>, u64)> {
714 let lfn_chsum = lfn_checksum(short_name);
716 let lfn_iter = LfnEntriesGenerator::new(lfn_utf16.as_ucs2_units(), lfn_chsum);
718 let num_entries = lfn_iter.len() + 1;
720 let mut stream = self.find_free_entries(num_entries)?;
721 let start_pos = stream.seek(io::SeekFrom::Current(0))?;
722 for lfn_entry in lfn_iter {
724 lfn_entry.serialize(&mut *stream)?;
725 }
726 Ok((stream, start_pos))
727 }
728
729 fn write_entry(
730 &self,
731 name: &str,
732 raw_entry: DirFileEntryData,
733 ) -> io::Result<DirEntry<'fs, IO, TP, OCC>> {
734 trace!("write_entry {}", name);
735 let long_name_required = validate_long_name(name, raw_entry.name())?;
737 let transaction = self.fs.begin_transaction();
740 let (mut stream, start_pos, lfn_utf16) = if long_name_required {
741 let lfn_utf16 = Self::encode_lfn_utf16(name);
743 let (stream, start_pos) =
745 self.alloc_and_write_lfn_entries(&lfn_utf16, raw_entry.name())?;
746 (stream, start_pos, lfn_utf16)
747 } else {
748 let mut stream = self.find_free_entries(1)?;
749 let start_pos = stream.seek(io::SeekFrom::Current(0))?;
750 (stream, start_pos, LfnBuffer::new())
751 };
752 raw_entry.serialize(&mut *stream)?;
754 if let Some(transaction) = transaction {
755 self.fs.commit(transaction)?;
756 }
757 let end_pos = stream.seek(io::SeekFrom::Current(0))?;
758 let abs_pos = stream.abs_pos()?.map(|p| p - DIR_ENTRY_SIZE);
759 let short_name = ShortName::new(raw_entry.name());
761 Ok(DirEntry {
762 data: raw_entry,
763 short_name,
764 #[cfg(feature = "lfn")]
765 lfn_utf16,
766 fs: self.fs,
767 entry_pos: abs_pos.unwrap(), offset_range: (start_pos, end_pos),
769 })
770 }
771
772 fn cluster_for_child_dotdot_entry(&self) -> Option<u32> {
773 if self.is_root {
774 None
775 } else {
776 self.stream.borrow().first_cluster()
777 }
778 }
779
780 pub fn flush_dir_entry(&mut self) -> std::io::Result<()> {
782 match &mut *self.stream.borrow_mut() {
783 DirRawStream::File(Some(file)) => file.flush_dir_entry(),
784 DirRawStream::File(None) => {
785 Err(io::Error::new(ErrorKind::NotFound, "Directory has been deleted"))
786 }
787 DirRawStream::Root(_) => {
788 Err(io::Error::new(ErrorKind::Other, "Cannot flush root directroy entry"))
789 }
790 }
791 }
792}
793
794pub struct DirIter<'a, 'fs, IO: ReadWriteSeek, TP, OCC> {
798 stream: &'a RefCell<DirRawStream<'fs, IO, TP, OCC>>,
799 fs: &'fs FileSystem<IO, TP, OCC>,
800 skip_volume: bool,
801 err: bool,
802}
803
804impl<'a, 'fs, IO: ReadWriteSeek, TP, OCC> DirIter<'a, 'fs, IO, TP, OCC> {
805 fn new(
806 stream: &'a RefCell<DirRawStream<'fs, IO, TP, OCC>>,
807 fs: &'fs FileSystem<IO, TP, OCC>,
808 skip_volume: bool,
809 ) -> Self {
810 stream.borrow_mut().seek(SeekFrom::Start(0)).unwrap();
811 DirIter { stream, fs, skip_volume, err: false }
812 }
813}
814
815impl<'fs, IO: ReadWriteSeek, TP: TimeProvider, OCC> DirIter<'_, 'fs, IO, TP, OCC> {
816 fn should_skip_entry(&self, raw_entry: &DirEntryData) -> bool {
817 if raw_entry.is_deleted() {
818 return true;
819 }
820 match raw_entry {
821 DirEntryData::File(sfn_entry) => self.skip_volume && sfn_entry.is_volume(),
822 _ => false,
823 }
824 }
825
826 fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'fs, IO, TP, OCC>>> {
827 trace!("read_dir_entry");
828 let mut lfn_builder = LongNameBuilder::new();
829 let mut stream = self.stream.borrow_mut();
830 let mut offset = stream.seek(SeekFrom::Current(0))?;
831 let mut begin_offset = offset;
832 loop {
833 let raw_entry = DirEntryData::deserialize(&mut *stream)?;
834 offset += DIR_ENTRY_SIZE;
835 if raw_entry.is_end() {
837 return Ok(None);
838 }
839 if self.should_skip_entry(&raw_entry) {
841 trace!("skip entry");
842 lfn_builder.clear();
843 begin_offset = offset;
844 continue;
845 }
846 match raw_entry {
847 DirEntryData::File(data) => {
848 let abs_pos = stream.abs_pos()?.map(|p| p - DIR_ENTRY_SIZE);
850 lfn_builder.validate_chksum(data.name());
852 let short_name = ShortName::new(data.name());
854 trace!("file entry {:?}", data.name());
855 return Ok(Some(DirEntry {
856 data,
857 short_name,
858 #[cfg(feature = "lfn")]
859 lfn_utf16: lfn_builder.into_buf(),
860 fs: self.fs,
861 entry_pos: abs_pos.unwrap(), offset_range: (begin_offset, offset),
863 }));
864 }
865 DirEntryData::Lfn(data) => {
866 trace!("lfn entry");
868 lfn_builder.process(&data);
869 }
870 }
871 }
872 }
873}
874
875impl<'b, IO: ReadWriteSeek, TP: TimeProvider, OCC> Iterator for DirIter<'_, 'b, IO, TP, OCC> {
876 type Item = io::Result<DirEntry<'b, IO, TP, OCC>>;
877
878 fn next(&mut self) -> Option<Self::Item> {
879 if self.err {
880 return None;
881 }
882 let r = self.read_dir_entry();
883 match r {
884 Ok(Some(e)) => Some(Ok(e)),
885 Ok(None) => None,
886 Err(err) => {
887 self.err = true;
888 Some(Err(err))
889 }
890 }
891 }
892}
893
894fn validate_long_name(name: &str, short_name: &[u8; SFN_SIZE]) -> io::Result<bool> {
895 if name.is_empty() {
897 return Err(io::Error::new(ErrorKind::Other, FatfsError::FileNameEmpty));
898 }
899 if name.len() > 255 {
900 return Err(io::Error::new(ErrorKind::Other, FatfsError::FileNameTooLong));
901 }
902 let mut long_name_required = name.len() > 12;
903 let mut short_name_index: usize = 0;
904 for c in name.chars() {
906 match c {
907 'A'..='Z' | '0'..='9' => {}
908 '$' | '%' | '\'' | '-' | '_' | '@' | '~' | '`' | '!' | '(' | ')' | '{' | '}' | '^'
909 | '#' | '&' | '.' => {}
910 'a'..='z' | '\u{80}'..='\u{10FFFF}' | ' ' | '+' | ',' | ';' | '=' | '[' | ']' => {
911 long_name_required = true
912 }
913 _ => return Err(io::Error::new(ErrorKind::Other, FatfsError::FileNameBadCharacter)),
914 }
915 if !long_name_required {
916 long_name_required = match short_name_index {
917 0..=7 => {
918 if c as u8 == short_name[short_name_index] {
919 short_name_index += 1;
920 false
921 } else if c == '.' {
922 short_name_index = 9;
923 false
924 } else {
925 true
926 }
927 }
928 8 => {
929 short_name_index = 9;
930 c != '.'
931 }
932 9..=11 => {
933 short_name_index += 1;
934 c as u8 != short_name[short_name_index - 2]
935 }
936 _ => true,
937 };
938 }
939 }
940 Ok(long_name_required)
941}
942
943fn lfn_checksum(short_name: &[u8; SFN_SIZE]) -> u8 {
944 let mut chksum = num::Wrapping(0u8);
945 for b in short_name {
946 chksum = (chksum << 7) + (chksum >> 1) + num::Wrapping(*b);
947 }
948 chksum.0
949}
950
951#[cfg(all(feature = "lfn", feature = "alloc"))]
952#[derive(Clone)]
953pub(crate) struct LfnBuffer {
954 ucs2_units: Vec<u16>,
955}
956
957#[cfg(not(feature = "alloc"))]
958const MAX_LFN_LEN: usize = 256;
959
960#[cfg(all(feature = "lfn", not(feature = "alloc")))]
961#[derive(Clone)]
962pub(crate) struct LfnBuffer {
963 ucs2_units: [u16; MAX_LFN_LEN],
964 len: usize,
965}
966
967#[cfg(all(feature = "lfn", feature = "alloc"))]
968impl LfnBuffer {
969 fn new() -> Self {
970 LfnBuffer { ucs2_units: Vec::<u16>::new() }
971 }
972
973 fn from_ucs2_units<I>(usc2_units: I) -> Self
974 where
975 I: Iterator<Item = u16>,
976 {
977 LfnBuffer { ucs2_units: usc2_units.collect() }
978 }
979
980 fn clear(&mut self) {
981 self.ucs2_units.clear();
982 }
983
984 pub(crate) fn len(&self) -> usize {
985 self.ucs2_units.len()
986 }
987
988 fn set_len(&mut self, len: usize) {
989 self.ucs2_units.resize(len, 0u16);
990 }
991
992 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
993 &self.ucs2_units
994 }
995}
996
997#[cfg(all(feature = "lfn", not(feature = "alloc")))]
998impl LfnBuffer {
999 fn new() -> Self {
1000 LfnBuffer { ucs2_units: [0u16; MAX_LFN_LEN], len: 0 }
1001 }
1002
1003 fn from_ucs2_units<I>(usc2_units: I) -> Self
1004 where
1005 I: Iterator<Item = u16>,
1006 {
1007 let mut lfn = LfnBuffer { ucs2_units: [0u16; MAX_LFN_LEN], len: 0 };
1008 for (i, usc2_unit) in usc2_units.enumerate() {
1009 lfn.ucs2_units[i] = usc2_unit;
1010 }
1011 lfn
1012 }
1013
1014 fn clear(&mut self) {
1015 self.ucs2_units = [0u16; MAX_LFN_LEN];
1016 self.len = 0;
1017 }
1018
1019 pub(crate) fn len(&self) -> usize {
1020 self.len
1021 }
1022
1023 fn set_len(&mut self, len: usize) {
1024 self.len = len;
1025 }
1026
1027 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
1028 &self.ucs2_units
1029 }
1030}
1031
1032#[cfg(not(feature = "lfn"))]
1033#[derive(Clone)]
1034pub(crate) struct LfnBuffer {}
1035
1036#[cfg(not(feature = "lfn"))]
1037impl LfnBuffer {
1038 fn new() -> Self {
1039 LfnBuffer {}
1040 }
1041
1042 pub(crate) fn as_ucs2_units(&self) -> &[u16] {
1043 &[]
1044 }
1045}
1046
1047#[cfg(feature = "lfn")]
1048struct LongNameBuilder {
1049 buf: LfnBuffer,
1050 chksum: u8,
1051 index: u8,
1052}
1053
1054#[cfg(feature = "lfn")]
1055impl LongNameBuilder {
1056 fn new() -> Self {
1057 LongNameBuilder { buf: LfnBuffer::new(), chksum: 0, index: 0 }
1058 }
1059
1060 fn clear(&mut self) {
1061 self.buf.clear();
1062 self.index = 0;
1063 }
1064
1065 fn into_buf(mut self) -> LfnBuffer {
1066 if self.index == 1 {
1068 self.truncate();
1069 } else if !self.is_empty() {
1070 warn!("unfinished LFN sequence {}", self.index);
1071 self.clear();
1072 }
1073 self.buf
1074 }
1075
1076 fn truncate(&mut self) {
1077 let mut lfn_len = self.buf.len();
1079 while lfn_len > 0 {
1080 match self.buf.ucs2_units[lfn_len - 1] {
1081 0xFFFF | 0 => lfn_len -= 1,
1082 _ => break,
1083 }
1084 }
1085 self.buf.set_len(lfn_len);
1086 }
1087
1088 fn is_empty(&self) -> bool {
1089 self.index == 0
1092 }
1093
1094 fn process(&mut self, data: &DirLfnEntryData) {
1095 let is_last = (data.order() & LFN_ENTRY_LAST_FLAG) != 0;
1096 let index = data.order() & 0x1F;
1097 if index == 0 {
1098 warn!("corrupted lfn entry! {:x}", data.order());
1100 self.clear();
1101 return;
1102 }
1103 if is_last {
1104 self.index = index;
1106 self.chksum = data.checksum();
1107 self.buf.set_len(index as usize * LFN_PART_LEN);
1108 } else if self.index == 0 || index != self.index - 1 || data.checksum() != self.chksum {
1109 warn!(
1111 "corrupted lfn entry! {:x} {:x} {:x} {:x}",
1112 data.order(),
1113 self.index,
1114 data.checksum(),
1115 self.chksum
1116 );
1117 self.clear();
1118 return;
1119 } else {
1120 self.index -= 1;
1122 }
1123 let pos = LFN_PART_LEN * (index - 1) as usize;
1124 data.copy_name_to_slice(&mut self.buf.ucs2_units[pos..pos + 13]);
1126 }
1127
1128 fn validate_chksum(&mut self, short_name: &[u8; SFN_SIZE]) {
1129 if self.is_empty() {
1130 return;
1132 }
1133 let chksum = lfn_checksum(short_name);
1134 if chksum != self.chksum {
1135 warn!("checksum mismatch {:x} {:x} {:?}", chksum, self.chksum, short_name);
1136 self.clear();
1137 }
1138 }
1139}
1140
1141#[cfg(not(feature = "lfn"))]
1143struct LongNameBuilder {}
1144#[cfg(not(feature = "lfn"))]
1145impl LongNameBuilder {
1146 fn new() -> Self {
1147 LongNameBuilder {}
1148 }
1149 fn clear(&mut self) {}
1150 fn into_vec(self) {}
1151 fn truncate(&mut self) {}
1152 fn process(&mut self, _data: &DirLfnEntryData) {}
1153 fn validate_chksum(&mut self, _short_name: &[u8; SFN_SIZE]) {}
1154}
1155
1156#[cfg(feature = "lfn")]
1157struct LfnEntriesGenerator<'a> {
1158 name_parts_iter: iter::Rev<slice::Chunks<'a, u16>>,
1159 checksum: u8,
1160 index: usize,
1161 num: usize,
1162 ended: bool,
1163}
1164
1165#[cfg(feature = "lfn")]
1166impl<'a> LfnEntriesGenerator<'a> {
1167 fn new(name_utf16: &'a [u16], checksum: u8) -> Self {
1168 let num_entries = (name_utf16.len() + LFN_PART_LEN - 1) / LFN_PART_LEN;
1169 LfnEntriesGenerator {
1171 checksum,
1172 name_parts_iter: name_utf16.chunks(LFN_PART_LEN).rev(),
1173 index: 0,
1174 num: num_entries,
1175 ended: false,
1176 }
1177 }
1178}
1179
1180#[cfg(feature = "lfn")]
1181impl Iterator for LfnEntriesGenerator<'_> {
1182 type Item = DirLfnEntryData;
1183
1184 fn next(&mut self) -> Option<Self::Item> {
1185 if self.ended {
1186 return None;
1187 }
1188
1189 match self.name_parts_iter.next() {
1191 Some(ref name_part) => {
1192 let lfn_index = self.num - self.index;
1193 let mut order = lfn_index as u8;
1194 if self.index == 0 {
1195 order |= LFN_ENTRY_LAST_FLAG;
1197 }
1198 debug_assert!(order > 0);
1199 const LFN_PADDING: u16 = 0xFFFF;
1201 let mut lfn_part = [LFN_PADDING; LFN_PART_LEN];
1202 lfn_part[..name_part.len()].copy_from_slice(&name_part);
1203 if name_part.len() < LFN_PART_LEN {
1204 lfn_part[name_part.len()] = 0;
1206 }
1207 let mut lfn_entry = DirLfnEntryData::new(order, self.checksum);
1209 lfn_entry.copy_name_from_slice(&lfn_part);
1210 self.index += 1;
1211 Some(lfn_entry)
1212 }
1213 None => {
1214 self.ended = true;
1216 None
1217 }
1218 }
1219 }
1220
1221 fn size_hint(&self) -> (usize, Option<usize>) {
1222 self.name_parts_iter.size_hint()
1223 }
1224}
1225
1226#[cfg(feature = "lfn")]
1228impl ExactSizeIterator for LfnEntriesGenerator<'_> {}
1229
1230#[cfg(not(feature = "lfn"))]
1232struct LfnEntriesGenerator {}
1233#[cfg(not(feature = "lfn"))]
1234impl LfnEntriesGenerator {
1235 fn new(_name_utf16: &[u16], _checksum: u8) -> Self {
1236 LfnEntriesGenerator {}
1237 }
1238}
1239#[cfg(not(feature = "lfn"))]
1240impl Iterator for LfnEntriesGenerator {
1241 type Item = DirLfnEntryData;
1242
1243 fn next(&mut self) -> Option<Self::Item> {
1244 None
1245 }
1246
1247 fn size_hint(&self) -> (usize, Option<usize>) {
1248 (0, Some(0))
1249 }
1250}
1251#[cfg(not(feature = "lfn"))]
1252impl ExactSizeIterator for LfnEntriesGenerator {}
1253
1254#[derive(Default, Debug, Clone)]
1255struct ShortNameGenerator {
1256 chksum: u16,
1257 long_prefix_bitmap: u16,
1258 prefix_chksum_bitmap: u16,
1259 name_fits: bool,
1260 lossy_conv: bool,
1261 exact_match: bool,
1262 basename_len: u8,
1263 short_name: [u8; SFN_SIZE],
1264}
1265
1266impl ShortNameGenerator {
1267 fn new(name: &str) -> Self {
1268 let mut short_name = [SFN_PADDING; SFN_SIZE];
1270 let (basename_len, name_fits, lossy_conv) = match name.rfind('.') {
1273 Some(0) | None => {
1274 let (basename_len, basename_fits, basename_lossy) =
1277 Self::copy_short_name_part(&mut short_name[0..8], &name);
1278 (basename_len, basename_fits, basename_lossy)
1279 }
1280 Some(dot_index) => {
1281 let (basename_len, basename_fits, basename_lossy) =
1283 Self::copy_short_name_part(&mut short_name[0..8], &name[..dot_index]);
1284 let (_, ext_fits, ext_lossy) =
1287 Self::copy_short_name_part(&mut short_name[8..11], &name[dot_index + 1..]);
1288 (basename_len, basename_fits && ext_fits, basename_lossy || ext_lossy)
1289 }
1290 };
1291 let chksum = Self::checksum(&name);
1292 Self {
1293 short_name,
1294 chksum,
1295 name_fits,
1296 lossy_conv,
1297 basename_len: basename_len as u8,
1298 ..Default::default()
1299 }
1300 }
1301
1302 fn generate_dot() -> [u8; SFN_SIZE] {
1303 let mut short_name = [SFN_PADDING; SFN_SIZE];
1304 short_name[0] = b'.';
1305 short_name
1306 }
1307
1308 fn generate_dotdot() -> [u8; SFN_SIZE] {
1309 let mut short_name = [SFN_PADDING; SFN_SIZE];
1310 short_name[0] = b'.';
1311 short_name[1] = b'.';
1312 short_name
1313 }
1314
1315 fn copy_short_name_part(dst: &mut [u8], src: &str) -> (usize, bool, bool) {
1316 let mut dst_pos = 0;
1317 let mut lossy_conv = false;
1318 for c in src.chars() {
1319 if dst_pos == dst.len() {
1320 return (dst_pos, false, lossy_conv);
1322 }
1323 let fixed_c = match c {
1325 ' ' | '.' => {
1327 lossy_conv = true;
1328 continue;
1329 }
1330 'A'..='Z' | 'a'..='z' | '0'..='9' => c,
1332 '!' | '#' | '$' | '%' | '&' | '\'' | '(' | ')' | '-' | '@' | '^' | '_' | '`'
1333 | '{' | '}' | '~' => c,
1334 _ => '_',
1336 };
1337 lossy_conv = lossy_conv || (fixed_c != c);
1339 let upper = fixed_c.to_ascii_uppercase();
1341 dst[dst_pos] = upper as u8; dst_pos += 1;
1343 }
1344 (dst_pos, true, lossy_conv)
1345 }
1346
1347 fn add_existing(&mut self, short_name: &[u8; SFN_SIZE]) {
1348 if short_name == &self.short_name {
1350 self.exact_match = true;
1351 }
1352 let long_prefix_len = cmp::min(self.basename_len, 6) as usize;
1354 let num_suffix = if short_name[long_prefix_len] == b'~' {
1355 (short_name[long_prefix_len + 1] as char).to_digit(10)
1356 } else {
1357 None
1358 };
1359 let long_prefix_matches =
1360 short_name[..long_prefix_len] == self.short_name[..long_prefix_len];
1361 let ext_matches = short_name[8..] == self.short_name[8..];
1362 if long_prefix_matches && num_suffix.is_some() && ext_matches {
1363 let num = num_suffix.unwrap(); self.long_prefix_bitmap |= 1 << num;
1365 }
1366
1367 let short_prefix_len = cmp::min(self.basename_len, 2) as usize;
1369 let num_suffix = if short_name[short_prefix_len + 4] == b'~' {
1370 (short_name[short_prefix_len + 4 + 1] as char).to_digit(10)
1371 } else {
1372 None
1373 };
1374 let short_prefix_matches =
1375 short_name[..short_prefix_len] == self.short_name[..short_prefix_len];
1376 if short_prefix_matches && num_suffix.is_some() && ext_matches {
1377 let chksum_res = str::from_utf8(&short_name[short_prefix_len..short_prefix_len + 4])
1378 .map(|s| u16::from_str_radix(s, 16));
1379 if chksum_res == Ok(Ok(self.chksum)) {
1380 let num = num_suffix.unwrap(); self.prefix_chksum_bitmap |= 1 << num;
1382 }
1383 }
1384 }
1385
1386 fn checksum(name: &str) -> u16 {
1387 let mut chksum = num::Wrapping(0u16);
1389 for c in name.chars() {
1390 chksum = (chksum >> 1) + (chksum << 15) + num::Wrapping(c as u16);
1391 }
1392 chksum.0
1393 }
1394
1395 fn generate(&self) -> io::Result<[u8; SFN_SIZE]> {
1396 if !self.lossy_conv && self.name_fits && !self.exact_match {
1397 return Ok(self.short_name);
1400 }
1401 for i in 1..5 {
1403 if self.long_prefix_bitmap & (1 << i) == 0 {
1404 return Ok(self.build_prefixed_name(i, false));
1405 }
1406 }
1407 for i in 1..10 {
1409 if self.prefix_chksum_bitmap & (1 << i) == 0 {
1410 return Ok(self.build_prefixed_name(i, true));
1411 }
1412 }
1413 Err(io::Error::new(ErrorKind::AlreadyExists, "short name already exists"))
1415 }
1416
1417 fn next_iteration(&mut self) {
1418 self.chksum = (num::Wrapping(self.chksum) + num::Wrapping(1)).0;
1420 self.long_prefix_bitmap = 0;
1422 self.prefix_chksum_bitmap = 0;
1423 }
1424
1425 fn build_prefixed_name(&self, num: u32, with_chksum: bool) -> [u8; SFN_SIZE] {
1426 let mut buf = [SFN_PADDING; SFN_SIZE];
1427 let prefix_len = if with_chksum {
1428 let prefix_len = cmp::min(self.basename_len as usize, 2);
1429 buf[..prefix_len].copy_from_slice(&self.short_name[..prefix_len]);
1430 buf[prefix_len..prefix_len + 4].copy_from_slice(&Self::u16_to_u8_array(self.chksum));
1431 prefix_len + 4
1432 } else {
1433 let prefix_len = cmp::min(self.basename_len as usize, 6);
1434 buf[..prefix_len].copy_from_slice(&self.short_name[..prefix_len]);
1435 prefix_len
1436 };
1437 buf[prefix_len] = b'~';
1438 buf[prefix_len + 1] = char::from_digit(num, 10).unwrap() as u8; buf[8..].copy_from_slice(&self.short_name[8..]);
1440 buf
1441 }
1442
1443 fn u16_to_u8_array(x: u16) -> [u8; 4] {
1444 let c1 = char::from_digit((x as u32 >> 12) & 0xF, 16).unwrap().to_ascii_uppercase() as u8;
1445 let c2 = char::from_digit((x as u32 >> 8) & 0xF, 16).unwrap().to_ascii_uppercase() as u8;
1446 let c3 = char::from_digit((x as u32 >> 4) & 0xF, 16).unwrap().to_ascii_uppercase() as u8;
1447 let c4 = char::from_digit((x as u32 >> 0) & 0xF, 16).unwrap().to_ascii_uppercase() as u8;
1448 [c1, c2, c3, c4]
1449 }
1450}
1451
1452#[cfg(test)]
1453mod tests {
1454 use super::*;
1455
1456 #[test]
1457 fn test_split_path() {
1458 assert_eq!(split_path("aaa/bbb/ccc"), Ok(("aaa", Some("bbb/ccc"))));
1459 assert_eq!(split_path("aaa/bbb"), Ok(("aaa", Some("bbb"))));
1460 assert_eq!(split_path("aaa"), Ok(("aaa", None)));
1461 }
1462
1463 #[test]
1464 fn test_generate_short_name() {
1465 assert_eq!(&ShortNameGenerator::new("Foo").generate().unwrap(), b"FOO ");
1466 assert_eq!(&ShortNameGenerator::new("Foo.b").generate().unwrap(), b"FOO B ");
1467 assert_eq!(&ShortNameGenerator::new("Foo.baR").generate().unwrap(), b"FOO BAR");
1468 assert_eq!(&ShortNameGenerator::new("Foo+1.baR").generate().unwrap(), b"FOO_1~1 BAR");
1469 assert_eq!(&ShortNameGenerator::new("ver +1.2.text").generate().unwrap(), b"VER_12~1TEX");
1470 assert_eq!(&ShortNameGenerator::new(".bashrc.swp").generate().unwrap(), b"BASHRC~1SWP");
1471 assert_eq!(&ShortNameGenerator::new(".foo").generate().unwrap(), b"FOO~1 ");
1472 }
1473
1474 #[test]
1475 fn test_short_name_checksum_overflow() {
1476 ShortNameGenerator::checksum("\u{FF5A}\u{FF5A}\u{FF5A}\u{FF5A}");
1477 }
1478
1479 #[test]
1480 fn test_lfn_checksum_overflow() {
1481 lfn_checksum(&[
1482 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8,
1483 ]);
1484 }
1485
1486 #[test]
1487 fn test_generate_short_name_collisions_long() {
1488 let mut buf: [u8; SFN_SIZE];
1489 let mut gen = ShortNameGenerator::new("TextFile.Mine.txt");
1490 buf = gen.generate().unwrap();
1491 assert_eq!(&buf, b"TEXTFI~1TXT");
1492 gen.add_existing(&buf);
1493 buf = gen.generate().unwrap();
1494 assert_eq!(&buf, b"TEXTFI~2TXT");
1495 gen.add_existing(&buf);
1496 buf = gen.generate().unwrap();
1497 assert_eq!(&buf, b"TEXTFI~3TXT");
1498 gen.add_existing(&buf);
1499 buf = gen.generate().unwrap();
1500 assert_eq!(&buf, b"TEXTFI~4TXT");
1501 gen.add_existing(&buf);
1502 buf = gen.generate().unwrap();
1503 assert_eq!(&buf, b"TE527D~1TXT");
1504 gen.add_existing(&buf);
1505 buf = gen.generate().unwrap();
1506 assert_eq!(&buf, b"TE527D~2TXT");
1507 for i in 3..10 {
1508 gen.add_existing(&buf);
1509 buf = gen.generate().unwrap();
1510 assert_eq!(&buf, format!("TE527D~{}TXT", i).as_bytes());
1511 }
1512 gen.add_existing(&buf);
1513 assert!(gen.generate().is_err());
1514 gen.next_iteration();
1515 for _i in 0..4 {
1516 buf = gen.generate().unwrap();
1517 gen.add_existing(&buf);
1518 }
1519 buf = gen.generate().unwrap();
1520 assert_eq!(&buf, b"TE527E~1TXT");
1521 }
1522
1523 #[test]
1524 fn test_generate_short_name_collisions_short() {
1525 let mut buf: [u8; SFN_SIZE];
1526 let mut gen = ShortNameGenerator::new("x.txt");
1527 buf = gen.generate().unwrap();
1528 assert_eq!(&buf, b"X TXT");
1529 gen.add_existing(&buf);
1530 buf = gen.generate().unwrap();
1531 assert_eq!(&buf, b"X~1 TXT");
1532 gen.add_existing(&buf);
1533 buf = gen.generate().unwrap();
1534 assert_eq!(&buf, b"X~2 TXT");
1535 gen.add_existing(&buf);
1536 buf = gen.generate().unwrap();
1537 assert_eq!(&buf, b"X~3 TXT");
1538 gen.add_existing(&buf);
1539 buf = gen.generate().unwrap();
1540 assert_eq!(&buf, b"X~4 TXT");
1541 gen.add_existing(&buf);
1542 buf = gen.generate().unwrap();
1543 assert_eq!(&buf, b"X40DA~1 TXT");
1544 gen.add_existing(&buf);
1545 buf = gen.generate().unwrap();
1546 assert_eq!(&buf, b"X40DA~2 TXT");
1547 }
1548
1549 #[test]
1550 fn test_long_name_requires_long_name() {
1551 assert_eq!(
1552 validate_long_name("01234567.1234", b"01234567123").expect("should be valid"),
1553 true
1554 );
1555 }
1556
1557 #[test]
1558 fn test_dot_and_dotdot_does_not_require_long_name() {
1559 assert_eq!(validate_long_name(".", b". ").expect("should be valid"), false);
1560 assert_eq!(validate_long_name("..", b".. ").expect("should be valid"), false);
1561 }
1562
1563 #[test]
1564 fn test_lowercase_and_special_requires_long_name() {
1565 assert_eq!(validate_long_name("abc", b"abc ").expect("should be valid"), true);
1566 assert_eq!(validate_long_name(",", b", ").expect("should be valid"), true);
1567 assert_eq!(
1568 validate_long_name("FOO\u{100}", b"FOO ").expect("should be valid"),
1569 true
1570 );
1571 }
1572
1573 #[test]
1574 fn test_name_with_extension_does_not_require_long_name() {
1575 assert_eq!(validate_long_name("NAME.EXT", b"NAME EXT").expect("should be valid"), false);
1576 }
1577
1578 #[test]
1579 fn test_name_with_long_extension_does_require_long_name() {
1580 assert_eq!(validate_long_name("NAME.EXT1", b"NAME EXT").expect("should be valid"), true);
1581 }
1582}