Skip to content

Commit ae5e116

Browse files
authored
Merge pull request #112 from rust-embedded-community/enhanced-shell
Enhanced shell example
2 parents e7ef46f + 8f0e37c commit ae5e116

File tree

9 files changed

+546
-167
lines changed

9 files changed

+546
-167
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic
99
* `Volume`, `Directory` and `File` are now smart! They hold references to the thing they were made from, and will clean themselves up when dropped. The trade-off is you can can't open multiple volumes, directories or files at the same time.
1010
* Renamed the old types to `RawVolume`, `RawDirectory` and `RawFile`
1111
* New method `make_dir_in_dir`
12-
* Fixed long-standing bug that caused an integer overflow when a FAT32 directory
13-
was longer than one cluster ([#74])
12+
* Fixed long-standing bug that caused an integer overflow when a FAT32 directory was longer than one cluster ([#74])
13+
* Updated 'shell' example to support `mkdir`, `tree` and relative/absolute paths
14+
* Renamed `Error::FileNotFound` to `Error::NotFound`
15+
* New API `change_dir` which changes a directory to point to some child directory (or the parent) without opening a new directory.
16+
* Empty strings and `"."` convert to `ShortFileName::this_dir()`
17+
* You can now open directories multiple times without error
1418

1519
[#74]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/issues/74
1620

examples/shell.rs

Lines changed: 457 additions & 121 deletions
Large diffs are not rendered by default.

src/fat/volume.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ impl FatVolume {
575575
match_name,
576576
block,
577577
) {
578-
Err(Error::FileNotFound) => continue,
578+
Err(Error::NotFound) => continue,
579579
x => return x,
580580
}
581581
}
@@ -592,7 +592,7 @@ impl FatVolume {
592592
current_cluster = None;
593593
}
594594
}
595-
Err(Error::FileNotFound)
595+
Err(Error::NotFound)
596596
}
597597
FatSpecificInfo::Fat32(fat32_info) => {
598598
let mut current_cluster = match dir.cluster {
@@ -609,7 +609,7 @@ impl FatVolume {
609609
match_name,
610610
block,
611611
) {
612-
Err(Error::FileNotFound) => continue,
612+
Err(Error::NotFound) => continue,
613613
x => return x,
614614
}
615615
}
@@ -619,7 +619,7 @@ impl FatVolume {
619619
_ => None,
620620
}
621621
}
622-
Err(Error::FileNotFound)
622+
Err(Error::NotFound)
623623
}
624624
}
625625
}
@@ -653,7 +653,7 @@ impl FatVolume {
653653
return Ok(dir_entry.get_entry(fat_type, block, start));
654654
}
655655
}
656-
Err(Error::FileNotFound)
656+
Err(Error::NotFound)
657657
}
658658

659659
/// Delete an entry from the given directory
@@ -691,7 +691,7 @@ impl FatVolume {
691691
// Scan the cluster / root dir a block at a time
692692
for block in first_dir_block_num.range(dir_size) {
693693
match self.delete_entry_in_block(block_device, match_name, block) {
694-
Err(Error::FileNotFound) => {
694+
Err(Error::NotFound) => {
695695
// Carry on
696696
}
697697
x => {
@@ -731,7 +731,7 @@ impl FatVolume {
731731
let block_idx = self.cluster_to_block(cluster);
732732
for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
733733
match self.delete_entry_in_block(block_device, match_name, block) {
734-
Err(Error::FileNotFound) => {
734+
Err(Error::NotFound) => {
735735
// Carry on
736736
continue;
737737
}
@@ -755,7 +755,7 @@ impl FatVolume {
755755
}
756756
// If we get here we never found the right entry in any of the
757757
// blocks that made up the directory
758-
Err(Error::FileNotFound)
758+
Err(Error::NotFound)
759759
}
760760

761761
/// Deletes a directory entry from a block of directory entries.
@@ -790,7 +790,7 @@ impl FatVolume {
790790
.map_err(Error::DeviceError);
791791
}
792792
}
793-
Err(Error::FileNotFound)
793+
Err(Error::NotFound)
794794
}
795795

796796
/// Finds the next free cluster after the start_cluster and before end_cluster

src/filesystem/directory.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,19 @@ where
121121
Ok(d.to_directory(self.volume_mgr))
122122
}
123123

124+
/// Change to a directory, mutating this object.
125+
///
126+
/// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`.
127+
pub fn change_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
128+
where
129+
N: ToShortFileName,
130+
{
131+
let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
132+
self.volume_mgr.close_dir(self.raw_directory).unwrap();
133+
self.raw_directory = d;
134+
Ok(())
135+
}
136+
124137
/// Look in a directory for a named file.
125138
pub fn find_directory_entry<N>(&mut self, name: N) -> Result<DirEntry, Error<D::Error>>
126139
where
@@ -161,6 +174,14 @@ where
161174
self.volume_mgr.delete_file_in_dir(self.raw_directory, name)
162175
}
163176

177+
/// Make a directory inside this directory
178+
pub fn make_dir_in_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
179+
where
180+
N: ToShortFileName,
181+
{
182+
self.volume_mgr.make_dir_in_dir(self.raw_directory, name)
183+
}
184+
164185
/// Convert back to a raw directory
165186
pub fn to_raw_directory(self) -> RawDirectory {
166187
let d = self.raw_directory;

src/filesystem/filename.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ impl ShortFileName {
9191
return Ok(ShortFileName::parent_dir());
9292
}
9393

94+
// Special case `.` (or blank), which means "this directory".
95+
if name.is_empty() || name == "." {
96+
return Ok(ShortFileName::this_dir());
97+
}
98+
9499
let mut idx = 0;
95100
let mut seen_dot = false;
96101
for ch in name.bytes() {
@@ -318,9 +323,16 @@ mod test {
318323
assert_eq!(sfn, ShortFileName::create_from_str("1.C").unwrap());
319324
}
320325

326+
#[test]
327+
fn filename_empty() {
328+
assert_eq!(
329+
ShortFileName::create_from_str("").unwrap(),
330+
ShortFileName::this_dir()
331+
);
332+
}
333+
321334
#[test]
322335
fn filename_bad() {
323-
assert!(ShortFileName::create_from_str("").is_err());
324336
assert!(ShortFileName::create_from_str(" ").is_err());
325337
assert!(ShortFileName::create_from_str("123456789").is_err());
326338
assert!(ShortFileName::create_from_str("12345678.ABCD").is_err());

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ where
159159
TooManyOpenFiles,
160160
/// Bad handle given
161161
BadHandle,
162-
/// That file doesn't exist
163-
FileNotFound,
162+
/// That file or directory doesn't exist
163+
NotFound,
164164
/// You can't open a file twice or delete an open file
165165
FileAlreadyOpen,
166166
/// You can't open a directory twice

src/volume_mgr.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use crate::filesystem::{
1212
SearchIdGenerator, TimeSource, ToShortFileName, MAX_FILE_SIZE,
1313
};
1414
use crate::{
15-
debug, Block, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, Volume, VolumeIdx,
16-
VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA, PARTITION_ID_FAT32_CHS_LBA,
17-
PARTITION_ID_FAT32_LBA,
15+
debug, Block, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName, Volume,
16+
VolumeIdx, VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA,
17+
PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA,
1818
};
1919
use heapless::Vec;
2020

@@ -206,11 +206,7 @@ where
206206
/// You can then read the directory entries with `iterate_dir`, or you can
207207
/// use `open_file_in_dir`.
208208
pub fn open_root_dir(&mut self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
209-
for dir in self.open_dirs.iter() {
210-
if dir.cluster == ClusterId::ROOT_DIR && dir.volume_id == volume {
211-
return Err(Error::DirAlreadyOpen);
212-
}
213-
}
209+
// Opening a root directory twice is OK
214210

215211
let directory_id = RawDirectory(self.id_generator.get());
216212
let dir_info = DirectoryInfo {
@@ -229,6 +225,8 @@ where
229225
/// Open a directory.
230226
///
231227
/// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`.
228+
///
229+
/// Passing "." as the name results in opening the `parent_dir` a second time.
232230
pub fn open_dir<N>(
233231
&mut self,
234232
parent_dir: RawDirectory,
@@ -245,9 +243,25 @@ where
245243
let parent_dir_idx = self.get_dir_by_id(parent_dir)?;
246244
let volume_idx = self.get_volume_by_id(self.open_dirs[parent_dir_idx].volume_id)?;
247245
let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
246+
let parent_dir_info = &self.open_dirs[parent_dir_idx];
248247

249248
// Open the directory
250-
let parent_dir_info = &self.open_dirs[parent_dir_idx];
249+
if short_file_name == ShortFileName::this_dir() {
250+
// short-cut (root dir doesn't have ".")
251+
let directory_id = RawDirectory(self.id_generator.get());
252+
let dir_info = DirectoryInfo {
253+
directory_id,
254+
volume_id: self.open_volumes[volume_idx].volume_id,
255+
cluster: parent_dir_info.cluster,
256+
};
257+
258+
self.open_dirs
259+
.push(dir_info)
260+
.map_err(|_| Error::TooManyOpenDirs)?;
261+
262+
return Ok(directory_id);
263+
}
264+
251265
let dir_entry = match &self.open_volumes[volume_idx].volume_type {
252266
VolumeType::Fat(fat) => {
253267
fat.find_directory_entry(&self.block_device, parent_dir_info, &short_file_name)?
@@ -260,14 +274,8 @@ where
260274
return Err(Error::OpenedFileAsDir);
261275
}
262276

263-
// Check it's not already open
264-
for d in self.open_dirs.iter() {
265-
if d.volume_id == self.open_volumes[volume_idx].volume_id
266-
&& d.cluster == dir_entry.cluster
267-
{
268-
return Err(Error::DirAlreadyOpen);
269-
}
270-
}
277+
// We don't check if the directory is already open - directories hold
278+
// no cached state and so opening a directory twice is allowable.
271279

272280
// Remember this open directory.
273281
let directory_id = RawDirectory(self.id_generator.get());
@@ -493,7 +501,7 @@ where
493501
}
494502
_ => {
495503
// We are opening a non-existant file, and that's not OK.
496-
return Err(Error::FileNotFound);
504+
return Err(Error::NotFound);
497505
}
498506
};
499507

@@ -905,7 +913,7 @@ where
905913
Ok(_entry) => {
906914
return Err(Error::FileAlreadyExists);
907915
}
908-
Err(Error::FileNotFound) => {
916+
Err(Error::NotFound) => {
909917
// perfect, let's make it
910918
}
911919
Err(e) => {

tests/directories.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,9 @@ fn open_dir_twice() {
237237
.open_root_dir(fat32_volume)
238238
.expect("open root dir");
239239

240-
assert!(matches!(
241-
volume_mgr.open_root_dir(fat32_volume),
242-
Err(embedded_sdmmc::Error::DirAlreadyOpen)
243-
));
240+
let root_dir2 = volume_mgr
241+
.open_root_dir(fat32_volume)
242+
.expect("open it again");
244243

245244
assert!(matches!(
246245
volume_mgr.open_dir(root_dir, "README.TXT"),
@@ -251,13 +250,12 @@ fn open_dir_twice() {
251250
.open_dir(root_dir, "TEST")
252251
.expect("open test dir");
253252

254-
assert!(matches!(
255-
volume_mgr.open_dir(root_dir, "TEST"),
256-
Err(embedded_sdmmc::Error::DirAlreadyOpen)
257-
));
253+
let test_dir2 = volume_mgr.open_dir(root_dir, "TEST").unwrap();
258254

259255
volume_mgr.close_dir(root_dir).expect("close root dir");
260256
volume_mgr.close_dir(test_dir).expect("close test dir");
257+
volume_mgr.close_dir(test_dir2).expect("close test dir");
258+
volume_mgr.close_dir(root_dir2).expect("close test dir");
261259

262260
assert!(matches!(
263261
volume_mgr.close_dir(test_dir),
@@ -316,7 +314,7 @@ fn find_dir_entry() {
316314

317315
assert!(matches!(
318316
volume_mgr.find_directory_entry(root_dir, "README.TXS"),
319-
Err(embedded_sdmmc::Error::FileNotFound)
317+
Err(embedded_sdmmc::Error::NotFound)
320318
));
321319
}
322320

@@ -345,7 +343,7 @@ fn delete_file() {
345343

346344
assert!(matches!(
347345
volume_mgr.delete_file_in_dir(root_dir, "README2.TXT"),
348-
Err(embedded_sdmmc::Error::FileNotFound)
346+
Err(embedded_sdmmc::Error::NotFound)
349347
));
350348

351349
volume_mgr.close_file(file).unwrap();
@@ -356,12 +354,12 @@ fn delete_file() {
356354

357355
assert!(matches!(
358356
volume_mgr.delete_file_in_dir(root_dir, "README.TXT"),
359-
Err(embedded_sdmmc::Error::FileNotFound)
357+
Err(embedded_sdmmc::Error::NotFound)
360358
));
361359

362360
assert!(matches!(
363361
volume_mgr.open_file_in_dir(root_dir, "README.TXT", Mode::ReadOnly),
364-
Err(embedded_sdmmc::Error::FileNotFound)
362+
Err(embedded_sdmmc::Error::NotFound)
365363
));
366364
}
367365

tests/open_files.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ fn open_files() {
6767

6868
assert!(matches!(
6969
volume_mgr.open_file_in_dir(root_dir, "README.TXS", Mode::ReadOnly),
70-
Err(Error::FileNotFound)
70+
Err(Error::NotFound)
7171
));
7272

7373
// Create a new file

0 commit comments

Comments
 (0)