Skip to content

Commit 1a8cafd

Browse files
committed
Add a little shell example.
You can walk around a disk image and do directory listings. I had to make some more things 'Debug' so I could print the filesystem state. Also our handling of ".." was not right so I fixed that.
1 parent e3f807f commit 1a8cafd

File tree

6 files changed

+177
-6
lines changed

6 files changed

+177
-6
lines changed

examples/linux/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl BlockDevice for LinuxBlockDevice {
7474
}
7575
}
7676

77+
#[derive(Debug)]
7778
pub struct Clock;
7879

7980
impl TimeSource for Clock {

examples/shell.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//! A simple shell demo for embedded-sdmmc
2+
//!
3+
//! Presents a basic command prompt which implements some basic MS-DOS style shell commands.
4+
5+
use std::io::prelude::*;
6+
7+
use embedded_sdmmc::{Directory, Error, Volume, VolumeIdx, VolumeManager};
8+
9+
use crate::linux::{Clock, LinuxBlockDevice};
10+
11+
mod linux;
12+
13+
struct State {
14+
directory: Directory,
15+
volume: Volume,
16+
}
17+
18+
fn main() -> Result<(), Error<std::io::Error>> {
19+
env_logger::init();
20+
let mut args = std::env::args().skip(1);
21+
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
22+
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
23+
println!("Opening '{filename}'...");
24+
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
25+
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
26+
VolumeManager::new_with_limits(lbd, Clock, 100);
27+
let stdin = std::io::stdin();
28+
let mut volumes: [Option<State>; 4] = [None, None, None, None];
29+
30+
let mut current_volume = None;
31+
for volume_no in 0..4 {
32+
match volume_mgr.open_volume(VolumeIdx(volume_no)) {
33+
Ok(volume) => {
34+
println!("Volume # {}: found", volume_no,);
35+
match volume_mgr.open_root_dir(volume) {
36+
Ok(root_dir) => {
37+
volumes[volume_no] = Some(State {
38+
directory: root_dir,
39+
volume,
40+
});
41+
if current_volume.is_none() {
42+
current_volume = Some(volume_no);
43+
}
44+
}
45+
Err(e) => {
46+
println!("Failed to open root directory: {e:?}");
47+
volume_mgr.close_volume(volume).expect("close volume");
48+
}
49+
}
50+
}
51+
Err(e) => {
52+
println!("Failed to open volume {volume_no}: {e:?}");
53+
}
54+
}
55+
}
56+
57+
let Some(mut current_volume) = current_volume else {
58+
println!("No volumes found in file. Sorry.");
59+
return Ok(());
60+
};
61+
62+
loop {
63+
print!("{}:> ", current_volume);
64+
std::io::stdout().flush().unwrap();
65+
let mut line = String::new();
66+
stdin.read_line(&mut line)?;
67+
let line = line.trim();
68+
log::info!("Got command: {line:?}");
69+
if line == "quit" {
70+
break;
71+
} else if line == "help" {
72+
println!("Commands:");
73+
println!("\thelp -> this help text");
74+
println!("\t<volume>: -> change volume/partition");
75+
println!("\tdir -> do a directory listing");
76+
println!("\tquit -> exits the program");
77+
} else if line == "0:" {
78+
current_volume = 0;
79+
} else if line == "1:" {
80+
current_volume = 1;
81+
} else if line == "2:" {
82+
current_volume = 2;
83+
} else if line == "3:" {
84+
current_volume = 3;
85+
} else if line == "stat" {
86+
println!("Status:\n{volume_mgr:#?}");
87+
} else if line == "dir" {
88+
let Some(s) = &volumes[current_volume] else {
89+
println!("That volume isn't available");
90+
continue;
91+
};
92+
let r = volume_mgr.iterate_dir(s.directory, |entry| {
93+
println!(
94+
"{:12} {:9} {} {}",
95+
entry.name,
96+
entry.size,
97+
entry.mtime,
98+
if entry.attributes.is_directory() {
99+
"<DIR>"
100+
} else {
101+
""
102+
}
103+
);
104+
});
105+
handle("iterating directory", r);
106+
} else if let Some(arg) = line.strip_prefix("cd ") {
107+
let Some(s) = &mut volumes[current_volume] else {
108+
println!("This volume isn't available");
109+
continue;
110+
};
111+
match volume_mgr.open_dir(s.directory, arg) {
112+
Ok(d) => {
113+
let r = volume_mgr.close_dir(s.directory);
114+
handle("closing old directory", r);
115+
s.directory = d;
116+
}
117+
Err(e) => {
118+
handle("changing directory", Err(e));
119+
}
120+
}
121+
} else {
122+
println!("Unknown command {line:?} - try 'help' for help");
123+
}
124+
}
125+
126+
for (idx, s) in volumes.into_iter().enumerate() {
127+
if let Some(state) = s {
128+
println!("Unmounting {idx}...");
129+
let r = volume_mgr.close_dir(state.directory);
130+
handle("closing directory", r);
131+
let r = volume_mgr.close_volume(state.volume);
132+
handle("closing volume", r);
133+
println!("Unmounted {idx}!");
134+
}
135+
}
136+
137+
println!("Bye!");
138+
Ok(())
139+
}
140+
141+
fn handle(operation: &str, r: Result<(), Error<std::io::Error>>) {
142+
if let Err(e) = r {
143+
println!("Error {operation}: {e:?}");
144+
}
145+
}
146+
147+
// ****************************************************************************
148+
//
149+
// End Of File
150+
//
151+
// ****************************************************************************

src/fat/ondiskdirentry.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,26 @@ impl<'a> OnDiskDirEntry<'a> {
148148
entry_block: BlockIdx,
149149
entry_offset: u32,
150150
) -> DirEntry {
151+
let attributes = Attributes::create_from_fat(self.raw_attr());
151152
let mut result = DirEntry {
152153
name: ShortFileName {
153154
contents: [0u8; 11],
154155
},
155156
mtime: Timestamp::from_fat(self.write_date(), self.write_time()),
156157
ctime: Timestamp::from_fat(self.create_date(), self.create_time()),
157-
attributes: Attributes::create_from_fat(self.raw_attr()),
158-
cluster: if fat_type == FatType::Fat32 {
159-
self.first_cluster_fat32()
160-
} else {
161-
self.first_cluster_fat16()
158+
attributes,
159+
cluster: {
160+
let cluster = if fat_type == FatType::Fat32 {
161+
self.first_cluster_fat32()
162+
} else {
163+
self.first_cluster_fat16()
164+
};
165+
if cluster == ClusterId::EMPTY && attributes.is_directory() {
166+
// FAT16/FAT32 uses a cluster ID of `0` in the ".." entry to mean 'root directory'
167+
ClusterId::ROOT_DIR
168+
} else {
169+
cluster
170+
}
162171
},
163172
size: self.file_size(),
164173
entry_block,

src/filesystem/filename.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ impl ShortFileName {
8585
let mut sfn = ShortFileName {
8686
contents: [b' '; Self::FILENAME_MAX_LEN],
8787
};
88+
89+
// Special case `..`, which means "parent directory".
90+
if name == ".." {
91+
return Ok(ShortFileName::parent_dir());
92+
}
93+
8894
let mut idx = 0;
8995
let mut seen_dot = false;
9096
for ch in name.bytes() {

src/filesystem/search_id.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct SearchId(pub(crate) u32);
1212
/// Well, it will wrap after `2**32` IDs. But most systems won't open that many
1313
/// files, and if they do, they are unlikely to hold one file open and then
1414
/// open/close `2**32 - 1` others.
15+
#[derive(Debug)]
1516
pub struct SearchIdGenerator {
1617
next_id: Wrapping<u32>,
1718
}

src/volume_mgr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use heapless::Vec;
2020

2121
/// A `VolumeManager` wraps a block device and gives access to the FAT-formatted
2222
/// volumes within it.
23+
#[derive(Debug)]
2324
pub struct VolumeManager<
2425
D,
2526
T,
@@ -242,6 +243,8 @@ where
242243
}
243244
};
244245

246+
debug!("Found dir entry: {:?}", dir_entry);
247+
245248
if !dir_entry.attributes.is_directory() {
246249
return Err(Error::OpenedFileAsDir);
247250
}
@@ -482,7 +485,7 @@ where
482485

483486
// Check if it's open already
484487
if let Some(dir_entry) = &dir_entry {
485-
if self.file_is_open(volume_info.volume_id, &dir_entry) {
488+
if self.file_is_open(volume_info.volume_id, dir_entry) {
486489
return Err(Error::FileAlreadyOpen);
487490
}
488491
}

0 commit comments

Comments
 (0)