Skip to content

Commit b173f7b

Browse files
thejpsterjonathanpallant
authored andcommitted
Tools for relative path handling.
* Converting "" to a ShortFileName, gives you ShortFileName::this_dir() or ".<10 spaces>", which is the special entry at the top of every directory. * You can open the same directory multiple times. This is required if you want to walk a path, but also hold open a directory in that path. * Lets you open "." and get a duplicate handle to a directory * Lets you mutate a Directory object to point at a child, without having to make a new Directory object and drop the old one.
1 parent d8bcdf7 commit b173f7b

File tree

4 files changed

+65
-26
lines changed

4 files changed

+65
-26
lines changed

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/volume_mgr.rs

Lines changed: 25 additions & 17 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());

tests/directories.rs

Lines changed: 6 additions & 8 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),

0 commit comments

Comments
 (0)