Skip to content

Commit a087f64

Browse files
committed
Use unique IDs for files and directories
1 parent 1cfdab5 commit a087f64

File tree

6 files changed

+104
-48
lines changed

6 files changed

+104
-48
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ version = "0.5.0"
1414
byteorder = {version = "1", default-features = false}
1515
defmt = {version = "0.3", optional = true}
1616
embedded-hal = "0.2.3"
17+
heapless = "0.7"
1718
log = {version = "0.4", default-features = false, optional = true}
1819

1920
[dev-dependencies]

src/filesystem/directory.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use core::convert::TryFrom;
22

33
use crate::blockdevice::BlockIdx;
44
use crate::fat::{FatType, OnDiskDirEntry};
5-
use crate::filesystem::{Attributes, Cluster, ShortFileName, Timestamp};
5+
use crate::filesystem::{Attributes, Cluster, SearchId, ShortFileName, Timestamp};
66

77
/// Represents a directory entry, which tells you about
88
/// other files and directories.
@@ -33,6 +33,8 @@ pub struct DirEntry {
3333
pub struct Directory {
3434
/// The starting point of the directory listing.
3535
pub(crate) cluster: Cluster,
36+
/// Search ID for this directory.
37+
pub(crate) search_id: SearchId,
3638
}
3739

3840
impl DirEntry {

src/filesystem/files.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::filesystem::{Cluster, DirEntry};
1+
use crate::filesystem::{Cluster, DirEntry, SearchId};
22

33
/// Represents an open file on disk.
44
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
@@ -16,6 +16,8 @@ pub struct File {
1616
pub(crate) mode: Mode,
1717
/// DirEntry of this file
1818
pub(crate) entry: DirEntry,
19+
/// Search ID for this file
20+
pub(crate) search_id: SearchId,
1921
}
2022

2123
/// Errors related to file operations

src/filesystem/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ mod cluster;
1111
mod directory;
1212
mod filename;
1313
mod files;
14+
mod search_id;
1415
mod timestamp;
1516

1617
pub use self::attributes::Attributes;
1718
pub use self::cluster::Cluster;
1819
pub use self::directory::{DirEntry, Directory};
1920
pub use self::filename::{FilenameError, ShortFileName};
2021
pub use self::files::{File, FileError, Mode};
22+
pub use self::search_id::{IdGenerator, SearchId};
2123
pub use self::timestamp::{TimeSource, Timestamp};

src/filesystem/search_id.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#[derive(Clone, Copy, Debug)]
2+
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
3+
/// Unique ID used to search for files and directories in the open File/Directory lists
4+
pub struct SearchId(pub(crate) u32);
5+
6+
impl PartialEq for SearchId {
7+
fn eq(&self, other: &Self) -> bool {
8+
self.0 == other.0
9+
}
10+
}
11+
12+
/// ID generator intented to be used in a static context.
13+
///
14+
/// This object will always return a different ID.
15+
pub struct IdGenerator {
16+
next_id: core::sync::atomic::AtomicU32,
17+
}
18+
19+
impl IdGenerator {
20+
/// Create a new [`IdGenerator`].
21+
pub const fn new() -> Self {
22+
Self {
23+
next_id: core::sync::atomic::AtomicU32::new(0),
24+
}
25+
}
26+
27+
/// Generate a new, unique [`SearchId`].
28+
pub fn next(&self) -> SearchId {
29+
use core::sync::atomic::Ordering;
30+
let id = self.next_id.load(Ordering::Acquire);
31+
self.next_id.store(id + 1, Ordering::Release);
32+
SearchId(id)
33+
}
34+
}

src/volume_mgr.rs

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ use core::convert::TryFrom;
55

66
use crate::fat::{self, RESERVED_ENTRIES};
77
use crate::filesystem::{
8-
Attributes, Cluster, DirEntry, Directory, File, Mode, ShortFileName, TimeSource, MAX_FILE_SIZE,
8+
Attributes, Cluster, DirEntry, Directory, File, IdGenerator, Mode, SearchId, ShortFileName,
9+
TimeSource, MAX_FILE_SIZE,
910
};
1011
use crate::{
1112
debug, Block, BlockCount, BlockDevice, BlockIdx, Error, Volume, VolumeIdx, VolumeType,
1213
PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA, PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA,
1314
};
15+
use heapless::Vec;
16+
17+
static ID_GENERATOR: IdGenerator = IdGenerator::new();
1418

1519
/// A `VolumeManager` wraps a block device and gives access to the volumes within it.
1620
pub struct VolumeManager<D, T, const MAX_DIRS: usize = 4, const MAX_FILES: usize = 4>
@@ -21,8 +25,8 @@ where
2125
{
2226
pub(crate) block_device: D,
2327
pub(crate) timesource: T,
24-
open_dirs: [(VolumeIdx, Cluster); MAX_DIRS],
25-
open_files: [(VolumeIdx, Cluster); MAX_FILES],
28+
open_dirs: Vec<(VolumeIdx, Cluster, SearchId), MAX_DIRS>,
29+
open_files: Vec<(VolumeIdx, Cluster, SearchId), MAX_FILES>,
2630
}
2731

2832
impl<D, T> VolumeManager<D, T, 4, 4>
@@ -60,8 +64,8 @@ where
6064
VolumeManager {
6165
block_device,
6266
timesource,
63-
open_dirs: [(VolumeIdx(0), Cluster::INVALID); MAX_DIRS],
64-
open_files: [(VolumeIdx(0), Cluster::INVALID); MAX_FILES],
67+
open_dirs: Vec::new(),
68+
open_files: Vec::new(),
6569
}
6670
}
6771

@@ -157,21 +161,20 @@ where
157161
// Find a free directory entry, and check the root dir isn't open. As
158162
// we already know the root dir's magic cluster number, we can do both
159163
// checks in one loop.
160-
let mut open_dirs_row = None;
161-
for (i, d) in self.open_dirs.iter().enumerate() {
162-
if *d == (volume.idx, Cluster::ROOT_DIR) {
164+
for (v, c, _) in self.open_dirs.iter() {
165+
if *v == volume.idx && *c == Cluster::ROOT_DIR {
163166
return Err(Error::DirAlreadyOpen);
164167
}
165-
if d.1 == Cluster::INVALID {
166-
open_dirs_row = Some(i);
167-
break;
168-
}
169168
}
170-
let open_dirs_row = open_dirs_row.ok_or(Error::TooManyOpenDirs)?;
169+
let search_id = ID_GENERATOR.next();
171170
// Remember this open directory
172-
self.open_dirs[open_dirs_row] = (volume.idx, Cluster::ROOT_DIR);
171+
self.open_dirs
172+
.push((volume.idx, Cluster::ROOT_DIR, search_id))
173+
.map_err(|_| Error::TooManyOpenDirs)?;
174+
173175
Ok(Directory {
174176
cluster: Cluster::ROOT_DIR,
177+
search_id,
175178
})
176179
}
177180

@@ -188,14 +191,9 @@ where
188191
parent_dir: &Directory,
189192
name: &str,
190193
) -> Result<Directory, Error<D::Error>> {
191-
// Find a free open directory table row
192-
let mut open_dirs_row = None;
193-
for (i, d) in self.open_dirs.iter().enumerate() {
194-
if d.1 == Cluster::INVALID {
195-
open_dirs_row = Some(i);
196-
}
194+
if self.open_dirs.is_full() {
195+
return Err(Error::TooManyOpenDirs);
197196
}
198-
let open_dirs_row = open_dirs_row.ok_or(Error::TooManyOpenDirs)?;
199197

200198
// Open the directory
201199
let dir_entry = match &volume.volume_type {
@@ -207,25 +205,29 @@ where
207205
}
208206

209207
// Check it's not already open
210-
for (_i, dir_table_row) in self.open_dirs.iter().enumerate() {
211-
if *dir_table_row == (volume.idx, dir_entry.cluster) {
208+
for dir_table_row in self.open_dirs.iter() {
209+
if dir_table_row.0 == volume.idx && dir_table_row.1 == dir_entry.cluster {
212210
return Err(Error::DirAlreadyOpen);
213211
}
214212
}
215-
// Remember this open directory
216-
self.open_dirs[open_dirs_row] = (volume.idx, dir_entry.cluster);
213+
// Remember this open directory.
214+
let search_id = ID_GENERATOR.next();
215+
self.open_dirs
216+
.push((volume.idx, dir_entry.cluster, search_id))
217+
.map_err(|_| Error::TooManyOpenDirs)?;
218+
217219
Ok(Directory {
218220
cluster: dir_entry.cluster,
221+
search_id,
219222
})
220223
}
221224

222225
/// Close a directory. You cannot perform operations on an open directory
223226
/// and so must close it if you want to do something with it.
224227
pub fn close_dir(&mut self, volume: &Volume, dir: Directory) {
225-
let target = (volume.idx, dir.cluster);
226-
for d in self.open_dirs.iter_mut() {
227-
if *d == target {
228-
d.1 = Cluster::INVALID;
228+
for (i, d) in self.open_dirs.iter().enumerate() {
229+
if d.2 == dir.search_id {
230+
self.open_dirs.swap_remove(i);
229231
break;
230232
}
231233
}
@@ -265,10 +267,12 @@ where
265267
dir_entry: DirEntry,
266268
mode: Mode,
267269
) -> Result<File, Error<D::Error>> {
268-
let open_files_row = self.get_open_files_row()?;
270+
if self.open_files.is_full() {
271+
return Err(Error::TooManyOpenFiles);
272+
}
269273
// Check it's not already open
270274
for dir_table_row in self.open_files.iter() {
271-
if *dir_table_row == (volume.idx, dir_entry.cluster) {
275+
if dir_table_row.0 == volume.idx && dir_table_row.1 == dir_entry.cluster {
272276
return Err(Error::DirAlreadyOpen);
273277
}
274278
}
@@ -280,6 +284,8 @@ where
280284
}
281285

282286
let mode = solve_mode_variant(mode, true);
287+
let search_id = ID_GENERATOR.next();
288+
283289
let file = match mode {
284290
Mode::ReadOnly => File {
285291
starting_cluster: dir_entry.cluster,
@@ -288,6 +294,7 @@ where
288294
length: dir_entry.size,
289295
mode,
290296
entry: dir_entry,
297+
search_id,
291298
},
292299
Mode::ReadWriteAppend => {
293300
let mut file = File {
@@ -297,6 +304,7 @@ where
297304
length: dir_entry.size,
298305
mode,
299306
entry: dir_entry,
307+
search_id,
300308
};
301309
// seek_from_end with 0 can't fail
302310
file.seek_from_end(0).ok();
@@ -310,6 +318,7 @@ where
310318
length: dir_entry.size,
311319
mode,
312320
entry: dir_entry,
321+
search_id,
313322
};
314323
match &mut volume.volume_type {
315324
VolumeType::Fat(fat) => {
@@ -330,7 +339,10 @@ where
330339
_ => return Err(Error::Unsupported),
331340
};
332341
// Remember this open file
333-
self.open_files[open_files_row] = (volume.idx, file.starting_cluster);
342+
self.open_files
343+
.push((volume.idx, file.starting_cluster, search_id))
344+
.map_err(|_| Error::TooManyOpenDirs)?;
345+
334346
Ok(file)
335347
}
336348

@@ -346,7 +358,10 @@ where
346358
VolumeType::Fat(fat) => fat.find_directory_entry(self, dir, name),
347359
};
348360

349-
let open_files_row = self.get_open_files_row()?;
361+
if self.open_files.is_full() {
362+
return Err(Error::TooManyOpenFiles);
363+
}
364+
350365
let dir_entry = match dir_entry {
351366
Ok(entry) => Some(entry),
352367
Err(_)
@@ -375,16 +390,23 @@ where
375390
}
376391
};
377392

393+
let search_id = ID_GENERATOR.next();
394+
378395
let file = File {
379396
starting_cluster: entry.cluster,
380397
current_cluster: (0, entry.cluster),
381398
current_offset: 0,
382399
length: entry.size,
383400
mode,
384401
entry,
402+
search_id,
385403
};
404+
386405
// Remember this open file
387-
self.open_files[open_files_row] = (volume.idx, file.starting_cluster);
406+
self.open_files
407+
.push((volume.idx, file.starting_cluster, search_id))
408+
.map_err(|_| Error::TooManyOpenFiles)?;
409+
388410
Ok(file)
389411
}
390412
_ => {
@@ -428,9 +450,8 @@ where
428450
return Err(Error::DeleteDirAsFile);
429451
}
430452

431-
let target = (volume.idx, dir_entry.cluster);
432453
for d in self.open_files.iter_mut() {
433-
if *d == target {
454+
if d.0 == volume.idx && d.1 == dir_entry.cluster {
434455
return Err(Error::FileIsOpen);
435456
}
436457
}
@@ -498,11 +519,6 @@ where
498519
file.starting_cluster = match &mut volume.volume_type {
499520
VolumeType::Fat(fat) => fat.alloc_cluster(self, None, false)?,
500521
};
501-
for f in self.open_files.iter_mut() {
502-
if f.1 == Cluster(0) {
503-
*f = (f.0, file.starting_cluster)
504-
}
505-
}
506522

507523
file.entry.cluster = file.starting_cluster;
508524
debug!("Alloc first cluster {:?}", file.starting_cluster);
@@ -593,10 +609,9 @@ where
593609

594610
/// Close a file with the given full path.
595611
pub fn close_file(&mut self, volume: &Volume, file: File) -> Result<(), Error<D::Error>> {
596-
let target = (volume.idx, file.starting_cluster);
597-
for d in self.open_files.iter_mut() {
598-
if *d == target {
599-
d.1 = Cluster::INVALID;
612+
for (i, f) in self.open_files.iter().enumerate() {
613+
if f.2 == file.search_id {
614+
self.open_files.swap_remove(i);
600615
break;
601616
}
602617
}
@@ -609,7 +624,7 @@ where
609624
.open_dirs
610625
.iter()
611626
.chain(self.open_files.iter())
612-
.all(|(_, c)| c == &Cluster::INVALID)
627+
.all(|(_, c, _)| c == &Cluster::INVALID)
613628
}
614629

615630
/// Consume self and return BlockDevice and TimeSource

0 commit comments

Comments
 (0)