fatfs/
dir.rs

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
116/// Ensures the filename has no trailing spaces or dots.
117pub fn validate_filename<'a>(name: &'a str) -> io::Result<()> {
118    if name == "." || name == ".." {
119        return Ok(());
120    }
121
122    // Disallow trailing dots or spaces.
123    // Trailing dots are ignored by Windows and Linux, and trailing spaces are ignored by Windows.
124    // We error so that opening a file with a given name will always mean that a file with that
125    // name exists.
126    // We also disallow empty filenames.
127    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    // remove trailing slash and split into 2 components - top-most parent and rest
135    let mut path_split = path.trim_matches('/').splitn(2, '/');
136    let comp = path_split.next().unwrap(); // SAFE: splitn always returns at least one element
137    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
147/// A FAT filesystem directory.
148///
149/// This struct is created by the `open_dir` or `create_dir` methods on `Dir`.
150/// The root directory is returned by the `root_dir` method on `FileSystem`.
151pub 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    /// Creates directory entries iterator.
167    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            // compare name ignoring case
182            if e.eq_name(name) {
183                // check if file or directory is expected
184                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            // update short name generator state
192            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                // other error
220                Err(err) => return Err(err),
221                // directory already exists - return it
222                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    /// Set modification datetime for this directory.
232    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    /// Set access date for this directory.
240    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    /// Set modification datetime for this directory.
248    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    /// Get the access time of this directory.
256    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    /// Get the creation time of this directory.
264    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    /// Get the modification time of this directory.
272    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    /// Opens existing subdirectory.
280    ///
281    /// `path` is a '/' separated directory path relative to self directory.
282    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    /// Opens existing file.
293    ///
294    /// `path` is a '/' separated file path relative to self directory.
295    pub fn open_file(&self, path: &str) -> io::Result<File<'fs, IO, TP, OCC>> {
296        trace!("open_file {}", path);
297        // traverse path
298        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        // convert entry to a file
304        let e = self.find_entry(name, Some(false), None)?;
305        Ok(e.to_file())
306    }
307
308    /// Creates new or opens existing file=.
309    ///
310    /// `path` is a '/' separated file path relative to self directory.
311    /// File is never truncated when opening. It can be achieved by calling `File::truncate` method after opening.
312    pub fn create_file(&self, path: &str) -> io::Result<File<'fs, IO, TP, OCC>> {
313        trace!("create_file {}", path);
314        // traverse path
315        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        // this is final filename in the path
323        let r = self.check_for_existence(name, Some(false))?;
324        match r {
325            // file does not exist - create it
326            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            // file already exists - return it
332            DirEntryOrShortName::DirEntry(e) => Ok(e.to_file()),
333        }
334    }
335
336    /// Creates new directory or opens existing.
337    ///
338    /// `path` is a '/' separated path relative to self directory.
339    pub fn create_dir(&self, path: &str) -> io::Result<Self> {
340        trace!("create_dir {}", path);
341        // traverse path
342        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        // this is final filename in the path
350        let r = self.check_for_existence(name, Some(true))?;
351        match r {
352            // directory does not exist - create it
353            DirEntryOrShortName::ShortName(short_name) => {
354                let transaction = self.fs.begin_transaction().unwrap();
355                // alloc cluster for directory data
356                let cluster = self.fs.alloc_cluster(None, true)?;
357                // create entry in parent directory
358                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                // create special entries "." and ".."
363                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            // directory already exists - return it
381            DirEntryOrShortName::DirEntry(e) => Ok(e.to_dir()),
382        }
383    }
384
385    pub fn is_empty(&self) -> io::Result<bool> {
386        trace!("is_empty");
387        // check if directory contains no files
388        for r in self.iter() {
389            let e = r?;
390            let name = e.short_file_name_as_bytes();
391            // ignore special entries "." and ".."
392            if name != b"." && name != b".." {
393                return Ok(false);
394            }
395        }
396        Ok(true)
397    }
398
399    /// Removes existing file or directory.
400    ///
401    /// `path` is a '/' separated file path relative to self directory.
402    /// Make sure there is no reference to this file (no File instance) or filesystem corruption
403    /// can happen.
404    pub fn remove(&self, path: &str) -> io::Result<()> {
405        trace!("remove {}", path);
406        // traverse path
407        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        // in case of directory check if it is empty
413        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        // free data
418        if let Some(n) = e.first_cluster() {
419            self.fs.free_cluster_chain(n)?;
420        }
421        // free long and short name entries
422        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    /// Unlink the given File from this directory.
450    ///
451    /// Will cause filesystem corruption if the file is not actually in this directory,
452    /// but continuing to use the file is legal. It will be completely removed from the filesystem
453    /// once it is dropped.
454    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    /// Unlink the given Dir from this directory. Returns an error if the Dir is not empty.
464    ///
465    /// Will cause filesystem corruption if the file is not actually in this directory.
466    /// The directory's clusters will be removed from the disk.
467    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                    // Directory has already been deleted.
478                    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    /// Renames or moves existing file or directory.
489    ///
490    /// `src_path` is a '/' separated source file path relative to self directory.
491    /// `dst_path` is a '/' separated destination file path relative to `dst_dir`.
492    /// `dst_dir` can be set to self directory if rename operation without moving is needed.
493    /// Make sure there is no reference to this file (no File instance) or filesystem corruption
494    /// can happen.
495    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        // traverse source path
503        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        // traverse destination path
509        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        // move/rename file
516        self.rename_internal(src_path, dst_dir, dst_path)
517    }
518
519    /// Same as rename, but supports renaming over an existing file.
520    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    /// Same as rename but supports renaming over an existing directory (which must be empty).
539    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        // find existing file
591        let e = self.find_entry(src_name, None, None)?;
592        // check if destionation filename is unused
593        let r = dst_dir.check_for_existence(dst_name, None)?;
594        let short_name = match r {
595            DirEntryOrShortName::DirEntry(ref dst_e) => {
596                // check if source and destination entry is the same
597                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 names are exactly the same, we don't need to do anything.
604                if src_name == dst_name {
605                    return Ok(());
606                }
607                // otherwise, we'll rewrite the LFN below.
608                dst_e.raw_short_name().clone()
609            }
610            DirEntryOrShortName::ShortName(short_name) => short_name,
611        };
612        // free long and short name entries
613        {
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        // save new directory entry
626        let sfn_entry = e.data.renamed(short_name);
627        let dir_entry = dst_dir.write_entry(dst_name, sfn_entry)?;
628        // if renaming a directory, then we need to update the '..' entry
629        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            // The dotdot entry should be the second entry.
636            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                // first unused entry - all remaining space can be used
661                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                // free entry - calculate number of free entries in a row
668                if num_free == 0 {
669                    first_free = i;
670                }
671                num_free += 1;
672                if num_free == num_entries {
673                    // enough space for new file
674                    stream.seek(io::SeekFrom::Start(first_free as u64 * DIR_ENTRY_SIZE))?;
675                    return Ok(stream);
676                }
677            } else {
678                // used entry - start counting from 0
679                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        // get short name checksum
715        let lfn_chsum = lfn_checksum(short_name);
716        // create LFN entries generator
717        let lfn_iter = LfnEntriesGenerator::new(lfn_utf16.as_ucs2_units(), lfn_chsum);
718        // find space for new entries (multiple LFN entries and 1 SFN entry)
719        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        // write LFN entries before SFN entry
723        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        // check if name doesn't contain unsupported characters
736        let long_name_required = validate_long_name(name, raw_entry.name())?;
737        // start a transaction so that if things go wrong (e.g. we run out of space), we can easily
738        // revert and leave the file system in a good state.
739        let transaction = self.fs.begin_transaction();
740        let (mut stream, start_pos, lfn_utf16) = if long_name_required {
741            // convert long name to UTF-16
742            let lfn_utf16 = Self::encode_lfn_utf16(name);
743            // write LFN entries
744            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        // write short name entry
753        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        // return new logical entry descriptor
760        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(), // SAFE: abs_pos is absent only for empty file
768            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    /// Flushes the directory entry in the parent if not the root.
781    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
794/// An iterator over the directory entries.
795///
796/// This struct is created by the `iter` method on `Dir`.
797pub 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            // Check if this is end of dir
836            if raw_entry.is_end() {
837                return Ok(None);
838            }
839            // Check if this is deleted or volume ID entry
840            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                    // Get entry position on volume
849                    let abs_pos = stream.abs_pos()?.map(|p| p - DIR_ENTRY_SIZE);
850                    // Check if LFN checksum is valid
851                    lfn_builder.validate_chksum(data.name());
852                    // Return directory entry
853                    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(), // SAFE: abs_pos is empty only for empty file
862                        offset_range: (begin_offset, offset),
863                    }));
864                }
865                DirEntryData::Lfn(data) => {
866                    // Append to LFN buffer
867                    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    // check if length is valid
896    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    // check if there are only valid characters
905    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        // Check if last processed entry had index 1
1067        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        // Truncate 0 and 0xFFFF characters from LFN buffer
1078        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        // Check if any LFN entry has been processed
1090        // Note: index 0 is not a valid index in LFN and can be seen only after struct initialization
1091        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            // Corrupted entry
1099            warn!("corrupted lfn entry! {:x}", data.order());
1100            self.clear();
1101            return;
1102        }
1103        if is_last {
1104            // last entry is actually first entry in stream
1105            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            // Corrupted entry
1110            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            // Decrement LFN index only for non-last entries
1121            self.index -= 1;
1122        }
1123        let pos = LFN_PART_LEN * (index - 1) as usize;
1124        // copy name parts into LFN buffer
1125        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            // Nothing to validate - no LFN entries has been processed
1131            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// Dummy implementation for non-alloc build
1142#[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        // create generator using reverse iterator over chunks - first chunk can be shorter
1170        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        // get next part from reverse iterator
1190        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                    // this is last name part (written as first)
1196                    order |= LFN_ENTRY_LAST_FLAG;
1197                }
1198                debug_assert!(order > 0);
1199                // name is padded with ' '
1200                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                    // name is only zero-terminated if its length is not multiplicity of LFN_PART_LEN
1205                    lfn_part[name_part.len()] = 0;
1206                }
1207                // create and return new LFN entry
1208                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                // end of name
1215                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// name_parts_iter is ExactSizeIterator so size_hint returns one limit
1227#[cfg(feature = "lfn")]
1228impl ExactSizeIterator for LfnEntriesGenerator<'_> {}
1229
1230// Dummy implementation for non-alloc build
1231#[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        // padded by ' '
1269        let mut short_name = [SFN_PADDING; SFN_SIZE];
1270        // find extension after last dot
1271        // Note: short file name cannot start with the extension
1272        let (basename_len, name_fits, lossy_conv) = match name.rfind('.') {
1273            Some(0) | None => {
1274                // file starts with a '.', or it has no extension - copy name and leave extension
1275                // empty.
1276                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                // extension found - copy parts before and after dot
1282                let (basename_len, basename_fits, basename_lossy) =
1283                    Self::copy_short_name_part(&mut short_name[0..8], &name[..dot_index]);
1284                // This is safe, because '.' is always exactly one byte - we don't risk
1285                // accidentally indexing mid-character.
1286                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                // result buffer is full
1321                return (dst_pos, false, lossy_conv);
1322            }
1323            // Make sure character is allowed in 8.3 name
1324            let fixed_c = match c {
1325                // strip spaces and dots
1326                ' ' | '.' => {
1327                    lossy_conv = true;
1328                    continue;
1329                }
1330                // copy allowed characters
1331                'A'..='Z' | 'a'..='z' | '0'..='9' => c,
1332                '!' | '#' | '$' | '%' | '&' | '\'' | '(' | ')' | '-' | '@' | '^' | '_' | '`'
1333                | '{' | '}' | '~' => c,
1334                // replace disallowed characters by underscore
1335                _ => '_',
1336            };
1337            // Update 'lossy conversion' flag
1338            lossy_conv = lossy_conv || (fixed_c != c);
1339            // short name is always uppercase
1340            let upper = fixed_c.to_ascii_uppercase();
1341            dst[dst_pos] = upper as u8; // SAFE: upper is in range 0x20-0x7F
1342            dst_pos += 1;
1343        }
1344        (dst_pos, true, lossy_conv)
1345    }
1346
1347    fn add_existing(&mut self, short_name: &[u8; SFN_SIZE]) {
1348        // check for exact match collision
1349        if short_name == &self.short_name {
1350            self.exact_match = true;
1351        }
1352        // check for long prefix form collision (TEXTFI~1.TXT)
1353        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(); // SAFE: checked in if condition
1364            self.long_prefix_bitmap |= 1 << num;
1365        }
1366
1367        // check for short prefix + checksum form collision (TE021F~1.TXT)
1368        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(); // SAFE: checked in if condition
1381                self.prefix_chksum_bitmap |= 1 << num;
1382            }
1383        }
1384    }
1385
1386    fn checksum(name: &str) -> u16 {
1387        // BSD checksum algorithm
1388        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            // If there was no lossy conversion and name fits into
1398            // 8.3 convention and there is no collision return it as is
1399            return Ok(self.short_name);
1400        }
1401        // Try using long 6-characters prefix
1402        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        // Try prefix with checksum
1408        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        // Too many collisions - fail
1414        Err(io::Error::new(ErrorKind::AlreadyExists, "short name already exists"))
1415    }
1416
1417    fn next_iteration(&mut self) {
1418        // Try different checksum in next iteration
1419        self.chksum = (num::Wrapping(self.chksum) + num::Wrapping(1)).0;
1420        // Zero bitmaps
1421        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; // SAFE
1439        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}