1#![doc = include_str!("../README.md")]
2
3use std::io::{Read, Seek, SeekFrom, Write};
4
5use fatfs::FatType;
6use rusb::{Device, GlobalContext};
7use thiserror::Error;
8use usbh_scsi::storage::{
9 Closed, Opened, UsbMassStorage, UsbMassStorageError, UsbMassStorageReadWriteError,
10};
11
12pub use bootsector;
14pub use fatfs;
16pub use rusb;
18
19#[derive(Debug)]
24pub struct StorageUsb {
25 pub inner: StorageUsbInner,
26 pub usb_device: Device<GlobalContext>,
27}
28
29#[derive(Debug)]
35pub enum StorageUsbInner {
36 Closed(UsbMassStorage<Closed>),
37 Opened(UsbMassStorage<Opened>),
38 ClosedDummy,
39}
40
41#[derive(Error, Debug)]
43pub enum StorageUsbError {
44 #[error("usb mass storage error")]
46 UsbMassStorageError(#[from] UsbMassStorageError),
47
48 #[error("failed to open as block device")]
50 BlockDeviceOpenFail,
51
52 #[error("listing partitions failed")]
54 ListingPartitionFail,
55}
56
57impl StorageUsb {
58 pub fn list_usbs() -> Result<Vec<Self>, StorageUsbError> {
62 let usbs: Vec<_> = UsbMassStorage::list()?
63 .into_iter()
64 .map(|usb| {
65 let device = usb.device.clone();
66
67 Self {
68 inner: StorageUsbInner::Closed(usb),
69 usb_device: device,
70 }
71 })
72 .collect();
73
74 Ok(usbs)
75 }
76
77 pub fn open(&mut self) -> Result<&mut UsbMassStorage<Opened>, StorageUsbError> {
83 let inner = std::mem::replace(&mut self.inner, StorageUsbInner::ClosedDummy);
85 self.inner = match inner {
86 StorageUsbInner::Closed(closed) => StorageUsbInner::Opened(closed.open()?),
87 opened @ StorageUsbInner::Opened(_) => opened,
88 _ => unreachable!(),
89 };
90
91 match &mut self.inner {
92 StorageUsbInner::Opened(opened) => Ok(opened),
93 _ => unreachable!(),
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct FatPartition {
101 pub inner: bootsector::Partition,
103 pub volume_id: u32,
105 pub volume_label: String,
107 pub fat_type: FatType,
109 pub cluster_size: u32,
111 pub first_byte: u64,
113 pub length: u64,
115}
116
117impl FatPartition {
118 pub fn list_partitions(usb: &mut StorageUsb) -> Result<Vec<Self>, StorageUsbError> {
127 let opened = usb.open()?;
128
129 let mut block_device = opened
130 .block_device()
131 .map_err(|_| StorageUsbError::BlockDeviceOpenFail)?;
132
133 let partitions =
134 bootsector::list_partitions(&block_device, &bootsector::Options::default())
135 .map_err(|_| StorageUsbError::ListingPartitionFail)?;
136
137 let mut results = Vec::new();
138
139 for partition in partitions {
140 let first_byte = partition.first_byte;
141 let length = partition.len;
142 let view = PartitionView::new(&mut block_device, first_byte, length).unwrap();
143
144 let fs = match fatfs::FileSystem::new(view, fatfs::FsOptions::new()) {
145 Ok(fs) => fs,
146 Err(err) => {
147 log::debug!("Failed to open as fatfs file system {err:#}");
148 continue;
149 }
150 };
151
152 results.push(Self {
153 inner: partition,
154 volume_id: fs.volume_id(),
155 volume_label: fs.volume_label(),
156 fat_type: fs.fat_type(),
157 cluster_size: fs.cluster_size(),
158 first_byte: first_byte,
159 length: length,
160 })
161 }
162
163 Ok(results)
164 }
165}
166
167#[derive(Debug)]
172pub struct PartitionView<D> {
173 pub inner: D,
175 pub start: u64,
177 pub len: u64,
179}
180
181impl<D: Seek> PartitionView<D> {
182 pub fn new(mut inner: D, start: u64, len: u64) -> Result<Self, FatError> {
186 inner.seek(SeekFrom::Start(start)).map_err(FatError::from)?;
188 Ok(Self { inner, start, len })
189 }
190
191 fn current_rel_pos(&mut self) -> Result<u64, std::io::Error> {
195 let abs = self.inner.seek(SeekFrom::Current(0))?;
196 Ok(abs.saturating_sub(self.start))
197 }
198}
199
200impl<D> PartitionView<D> {
201 fn clamp_rel(&self, rel: i128) -> u64 {
203 let len = self.len as i128;
204 rel.clamp(0, len) as u64
205 }
206}
207
208impl<D: Read + Seek> Read for PartitionView<D> {
209 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
211 if buf.is_empty() {
212 return Ok(0);
213 }
214 let cur_rel = self.current_rel_pos()?;
216 if cur_rel >= self.len {
217 return Ok(0);
218 }
219 let max_here = (self.len - cur_rel) as usize;
220 let want = buf.len().min(max_here);
221
222 let n = self.inner.read(&mut buf[..want])?;
224 Ok(n)
225 }
226}
227
228impl<D: Write + Seek> Write for PartitionView<D> {
229 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
231 if buf.is_empty() {
232 return Ok(0);
233 }
234 let cur_rel = self.current_rel_pos()?;
236 if cur_rel >= self.len {
237 return Ok(0);
238 }
239 let max_here = (self.len - cur_rel) as usize;
240 let want = buf.len().min(max_here);
241
242 let n = self.inner.write(&buf[..want])?;
243 if n == 0 && want != 0 {
244 return Err(std::io::Error::new(std::io::ErrorKind::Other, "WriteZero"));
245 }
246 Ok(n)
247 }
248
249 fn flush(&mut self) -> std::io::Result<()> {
250 self.inner.flush()
251 }
252}
253
254impl<D: Seek> Seek for PartitionView<D> {
255 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
259 let rel_target: u64 = match pos {
261 SeekFrom::Start(o) => self.clamp_rel(o as i128),
262 SeekFrom::End(off) => {
263 let rel = self.len as i128 + off as i128;
264 self.clamp_rel(rel)
265 }
266 SeekFrom::Current(off) => {
267 let cur_rel = self.current_rel_pos()? as i128;
268 self.clamp_rel(cur_rel + off as i128)
269 }
270 };
271
272 let abs = self.start + rel_target;
274 self.inner.seek(SeekFrom::Start(abs))?;
275 Ok(rel_target)
276 }
277}
278
279#[derive(Error, Debug)]
281pub enum FatError {
282 #[error("write interrupted")]
284 Interrupted,
285
286 #[error("unexpected end of file")]
288 UnexpectedEof,
289
290 #[error("zero write")]
292 WriteZero,
293
294 #[error("usb read/write failed: {0}")]
296 UsbIo(#[from] UsbMassStorageReadWriteError),
297
298 #[error("io error: {0}")]
300 StdIo(#[from] std::io::Error),
301}