Skip to content

Commit 93bb5e5

Browse files
committed
Support directory deletion
1 parent c629f38 commit 93bb5e5

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
matrix:
2020
# Always run MSRV too!
21-
rust: ["stable", "1.86"]
21+
rust: ["stable", "1.87"]
2222
features: ['log', 'defmt-log', '""']
2323
steps:
2424
- uses: actions/checkout@v4

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ repository = "https://github.com/rust-embedded-community/embedded-sdmmc-rs"
1111
version = "0.9.0"
1212

1313
# Make sure to update the CI too!
14-
rust-version = "1.86"
14+
rust-version = "1.87"
1515

1616
[dependencies]
1717
byteorder = {version = "1", default-features = false}

src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ where
191191
OpenedDirAsFile,
192192
/// You can't open a file as a directory
193193
OpenedFileAsDir,
194-
/// You can't delete a directory as a file
194+
/// You can't delete a directory as a file [no longer being emitted]
195195
DeleteDirAsFile,
196+
/// You can't delete a non-empty directory
197+
DeleteNonEmptyDir,
196198
/// You can't close a volume with open files or directories
197199
VolumeStillInUse,
198200
/// You can't open a volume twice
@@ -255,6 +257,7 @@ impl<E: Debug> embedded_io::Error for Error<E> {
255257
Error::OpenedDirAsFile
256258
| Error::OpenedFileAsDir
257259
| Error::DeleteDirAsFile
260+
| Error::DeleteNonEmptyDir
258261
| Error::BadCluster
259262
| Error::ConversionError
260263
| Error::UnterminatedFatChain => ErrorKind::InvalidData,
@@ -294,6 +297,7 @@ where
294297
Error::OpenedDirAsFile => write!(f, "cannot open directory as file"),
295298
Error::OpenedFileAsDir => write!(f, "cannot open file as directory"),
296299
Error::DeleteDirAsFile => write!(f, "cannot delete directory as file"),
300+
Error::DeleteNonEmptyDir => write!(f, "cannot delete a non-empty directory"),
297301
Error::VolumeStillInUse => write!(f, "volume is still in use"),
298302
Error::VolumeAlreadyOpen => write!(f, "cannot open volume twice"),
299303
Error::Unsupported => write!(f, "unsupported operation"),

src/volume_mgr.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -665,26 +665,62 @@ where
665665
let data = data.deref_mut();
666666

667667
let dir_idx = data.get_dir_by_id(directory)?;
668-
let dir_info = &data.open_dirs[dir_idx];
669-
let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?;
668+
let parent_dir_info = &data.open_dirs[dir_idx];
669+
let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
670670
let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
671671

672672
let dir_entry = match &data.open_volumes[volume_idx].volume_type {
673-
VolumeType::Fat(fat) => fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn),
673+
VolumeType::Fat(fat) => {
674+
fat.find_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)
675+
}
674676
}?;
675677

676678
if dir_entry.attributes.is_directory() {
677-
return Err(Error::DeleteDirAsFile);
678-
}
679-
680-
if data.file_is_open(dir_info.raw_volume, &dir_entry) {
679+
// Find the directory to be deleted, so that we can check its contents.
680+
let dir_info = if data
681+
.open_dirs
682+
.iter()
683+
.find(|dir_info| dir_info.cluster == dir_entry.cluster)
684+
.is_some()
685+
{
686+
// Subdirectory is already open.
687+
return Err(Error::DirAlreadyOpen);
688+
} else {
689+
// The subdirectory isn't yet open. Open it in order to be able to list it.
690+
let raw_directory = RawDirectory(data.id_generator.generate());
691+
DirectoryInfo {
692+
raw_directory,
693+
raw_volume: data.open_volumes[volume_idx].raw_volume,
694+
cluster: dir_entry.cluster,
695+
}
696+
};
697+
// Can only delete directories that are already empty.
698+
let mut count = 0;
699+
// Equivalent to `self.iterate_dir(raw_dir, |_| count += 1)?;`, without locking again.
700+
match &data.open_volumes[volume_idx].volume_type {
701+
VolumeType::Fat(fat) => {
702+
fat.iterate_dir(&mut data.block_cache, &dir_info, |de| {
703+
// Hide all the LFN directory entries
704+
if !de.attributes.is_lfn()
705+
&& de.name != ShortFileName::this_dir()
706+
&& de.name != ShortFileName::parent_dir()
707+
{
708+
count += 1;
709+
}
710+
})?;
711+
}
712+
}
713+
if count != 0 {
714+
return Err(Error::DeleteNonEmptyDir);
715+
}
716+
} else if data.file_is_open(parent_dir_info.raw_volume, &dir_entry) {
681717
return Err(Error::FileAlreadyOpen);
682718
}
683719

684-
let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?;
720+
let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
685721
match &data.open_volumes[volume_idx].volume_type {
686722
VolumeType::Fat(fat) => {
687-
fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn)?
723+
fat.delete_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)?
688724
}
689725
}
690726

tests/directories.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,49 @@ fn make_directory() {
592592
volume_mgr.close_file(new_file).expect("close file");
593593
}
594594

595+
#[test]
596+
fn delete_directory() {
597+
let time_source = utils::make_time_source();
598+
let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap();
599+
let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source);
600+
601+
let fat32_volume = volume_mgr
602+
.open_raw_volume(embedded_sdmmc::VolumeIdx(1))
603+
.expect("open volume 1");
604+
605+
let root_dir = volume_mgr
606+
.open_root_dir(fat32_volume)
607+
.expect("open root dir");
608+
609+
volume_mgr.make_dir_in_dir(root_dir, "FOOBAR").unwrap();
610+
611+
let dir = volume_mgr.open_dir(root_dir, "FOOBAR").unwrap();
612+
613+
assert!(matches!(
614+
volume_mgr.delete_file_in_dir(root_dir, "FOOBAR"),
615+
Err(embedded_sdmmc::Error::DirAlreadyOpen)
616+
));
617+
618+
assert!(matches!(
619+
volume_mgr.delete_file_in_dir(root_dir, "FOO"),
620+
Err(embedded_sdmmc::Error::NotFound)
621+
));
622+
623+
volume_mgr.close_dir(dir).unwrap();
624+
625+
volume_mgr.delete_file_in_dir(root_dir, "FOOBAR").unwrap();
626+
627+
assert!(matches!(
628+
volume_mgr.delete_file_in_dir(root_dir, "FOOBAR"),
629+
Err(embedded_sdmmc::Error::NotFound)
630+
));
631+
632+
assert!(matches!(
633+
volume_mgr.open_dir(root_dir, "FOOBAR"),
634+
Err(embedded_sdmmc::Error::NotFound)
635+
));
636+
}
637+
595638
// ****************************************************************************
596639
//
597640
// End Of File

0 commit comments

Comments
 (0)