file_system/operations/base.rs
1//! Device abstraction for storage and I/O operations.
2//!
3//! This module provides the core device trait and types for abstracting various
4//! storage devices, peripherals, and I/O endpoints in the file system.
5
6use shared::AnyByLayout;
7
8use crate::{Context, ControlCommandIdentifier, Error, Position, Result, Size};
9
10/// Core trait for all device implementations in the file system.
11///
12/// A device represents any storage medium or I/O endpoint that can be read from and written to.
13/// This includes physical storage devices (hard drives, SSDs, SD cards), memory devices for testing,
14/// partition devices, and other specialized I/O devices.
15///
16/// ## Thread Safety
17///
18/// All device implementations must be thread-safe (`Send + Sync`) as they may be accessed
19/// by multiple tasks/threads concurrently. Implementations should use appropriate synchronization
20/// primitives like `RwLock` or `Mutex` to handle concurrent access.
21///
22/// ## Non-Blocking Operations
23///
24/// Devices should never block indefinitely. If an operation would block, implementations should
25/// return [`Error::RessourceBusy`] instead. This means implementations should prefer
26/// `try_read()` and `try_write()` variants of synchronization primitives.
27///
28/// ## Position Management
29///
30/// Devices maintain an internal position cursor that affects read and write operations.
31/// The position can be manipulated using [`BaseOperations::set_position`].
32///
33/// # Examples
34///
35/// ```rust
36/// # extern crate alloc;
37/// # use file_system::{Size, MemoryDevice, DirectBaseOperations, Position};
38///
39/// // Create a memory device for testing
40/// let device = MemoryDevice::<512>::new(1024);
41///
42/// // Write data
43/// let data = b"Hello, World!";
44/// let bytes_written = device.write(data, 0).unwrap();
45/// assert_eq!(bytes_written, data.len());
46///
47/// // Reset position and read back
48/// device.set_position(0, &Position::Start(0)).unwrap();
49/// let mut buffer = alloc::vec![0u8; data.len()];
50/// let bytes_read = device.read(&mut buffer, 0).unwrap();
51/// assert_eq!(bytes_read, data.len());
52/// assert_eq!(&buffer, data);
53/// ```
54pub trait BaseOperations: Send + Sync {
55 fn open(&self, _context: &mut Context) -> Result<()> {
56 Ok(())
57 }
58
59 fn close(&self, _context: &mut Context) -> Result<()> {
60 Ok(())
61 }
62
63 /// Read data from the device at the current position.
64 ///
65 /// Reads up to `Buffer.len()` bytes from the device into the provided buffer.
66 /// The actual number of bytes read may be less than requested.
67 ///
68 /// # Arguments
69 ///
70 /// * `context` - File system context
71 /// * `Buffer` - Mutable byte slice to read data into
72 ///
73 /// # Returns
74 ///
75 /// * `Ok(Size)` - Number of bytes successfully read
76 /// * `Err(Error)` - Error if read operation failed
77 ///
78 /// # Errors
79 ///
80 /// * [`Error::InputOutput`] - I/O error during read operation
81 /// * [`Error::RessourceBusy`] - Device is temporarily unavailable
82 /// * [`Error::InvalidParameter`] - Invalid buffer or device state
83 fn read(
84 &self,
85 context: &mut Context,
86 buffer: &mut [u8],
87 absolute_position: Size,
88 ) -> Result<usize>;
89
90 fn read_until(
91 &self,
92 context: &mut Context,
93 buffer: &mut [u8],
94 absolute_position: Size,
95 delimiter: &[u8],
96 ) -> Result<usize> {
97 if delimiter.is_empty() {
98 return Err(Error::InvalidParameter);
99 }
100
101 let mut total_read = 0;
102 let mut match_count = 0;
103
104 while total_read < buffer.len() {
105 let bytes_read = self.read(
106 context,
107 &mut buffer[total_read..total_read + 1],
108 absolute_position + total_read as Size,
109 )?;
110
111 if bytes_read == 0 {
112 break; // End of file or no more data
113 }
114
115 total_read += bytes_read;
116
117 // Check if we're matching the delimiter
118 if buffer[total_read - 1] == delimiter[match_count] {
119 match_count += 1;
120 if match_count == delimiter.len() {
121 break; // Found complete delimiter
122 }
123 } else {
124 // Reset and check if current byte starts a new match
125 match_count = 0;
126 if buffer[total_read - 1] == delimiter[0] {
127 match_count = 1;
128 }
129 }
130 }
131
132 Ok(total_read)
133 }
134
135 /// Write data to the device at the current position.
136 ///
137 /// Writes up to `Buffer.len()` bytes from the buffer to the device.
138 /// The actual number of bytes written may be less than requested.
139 ///
140 /// # Arguments
141 ///
142 /// * `context` - File system context
143 /// * `Buffer` - Byte slice containing data to write
144 ///
145 /// # Returns
146 ///
147 /// * `Ok(Size)` - Number of bytes successfully written
148 /// * `Err(Error)` - Error if write operation failed
149 ///
150 /// # Errors
151 ///
152 /// * [`Error::InputOutput`] - I/O error during write operation
153 /// * [`Error::NoSpaceLeft`] - Device is full
154 /// * [`Error::RessourceBusy`] - Device is temporarily unavailable
155 /// * [`Error::PermissionDenied`] - Device is read-only
156 fn write(&self, context: &mut Context, buffer: &[u8], absolute_position: Size)
157 -> Result<usize>;
158
159 fn write_pattern(
160 &self,
161 context: &mut Context,
162 pattern: &[u8],
163 count: usize,
164 absolute_position: Size,
165 ) -> Result<usize> {
166 let mut total_written = 0;
167
168 for _ in 0..count {
169 let bytes_written =
170 self.write(context, pattern, absolute_position + total_written as Size)?;
171 if bytes_written == 0 {
172 break; // Unable to write more
173 }
174 total_written += bytes_written;
175 }
176
177 Ok(total_written)
178 }
179
180 fn write_vectored(
181 &self,
182 context: &mut Context,
183 buffers: &[&[u8]],
184 absolute_position: Size,
185 ) -> Result<usize> {
186 let mut total_written = 0;
187
188 for buffer in buffers {
189 if buffer.is_empty() {
190 continue; // Skip empty buffers
191 }
192 let bytes_written =
193 self.write(context, buffer, absolute_position + total_written as Size)?;
194 if bytes_written == 0 {
195 break; // Unable to write more
196 }
197 total_written += bytes_written;
198 }
199
200 Ok(total_written)
201 }
202
203 /// Set the current position cursor for read/write operations.
204 ///
205 /// The position affects where subsequent read and write operations will occur.
206 /// Different position types allow for absolute positioning, relative positioning,
207 /// and positioning from the end of the device.
208 ///
209 /// # Arguments
210 ///
211 /// * `context` - File system context
212 /// * `Position` - The new position to set
213 ///
214 /// # Returns
215 ///
216 /// * `Ok(Size)` - The new absolute position after the operation
217 /// * `Err(Error)` - Error if position is invalid
218 ///
219 /// # Errors
220 ///
221 /// * [`Error::InvalidParameter`] - Position is beyond device bounds
222 fn set_position(
223 &self,
224 _context: &mut Context,
225 _current_position: Size,
226 _position: &Position,
227 ) -> Result<Size> {
228 match _position {
229 Position::Start(position) => Ok(*position),
230 Position::Current(offset) => Ok(_current_position.wrapping_add(*offset as Size)),
231 Position::End(_) => Err(Error::UnsupportedOperation),
232 }
233 }
234
235 /// Flush any buffered data to the underlying storage.
236 ///
237 /// Ensures that all pending write operations are committed to the physical device.
238 /// This is important for data integrity, especially on buffered devices.
239 ///
240 /// # Arguments
241 ///
242 /// * `context` - File system context
243 ///
244 /// # Returns
245 ///
246 /// * `Ok(())` - Flush completed successfully
247 /// * `Err(Error)` - Error during flush operation
248 fn flush(&self, _context: &mut Context) -> Result<()> {
249 Ok(())
250 }
251
252 fn control(
253 &self,
254 _context: &mut Context,
255 _command: ControlCommandIdentifier,
256 _input: &AnyByLayout,
257 _output: &mut AnyByLayout,
258 ) -> Result<()> {
259 Err(Error::UnsupportedOperation)
260 }
261
262 fn clone_context(&self, context: &Context) -> Result<Context>;
263}
264
265pub fn open_close_operation<D, R>(device: &D, operation: impl Fn(&D) -> Result<R>) -> Result<R>
266where
267 D: DirectBaseOperations,
268{
269 device.open()?;
270 let result = operation(device);
271 device.close()?;
272 result
273}
274
275pub trait DirectBaseOperations: Send + Sync {
276 fn open(&self) -> Result<()> {
277 Ok(())
278 }
279
280 fn close(&self) -> Result<()> {
281 Ok(())
282 }
283
284 fn read(&self, buffer: &mut [u8], absolute_position: Size) -> Result<usize>;
285
286 fn read_until(
287 &self,
288 buffer: &mut [u8],
289 absolute_position: Size,
290 delimiter: &[u8],
291 ) -> Result<usize> {
292 if delimiter.is_empty() {
293 return Err(Error::InvalidParameter);
294 }
295
296 let mut total_read = 0;
297 let mut match_count = 0;
298
299 while total_read < buffer.len() {
300 let bytes_read = self.read(
301 &mut buffer[total_read..total_read + 1],
302 absolute_position + total_read as Size,
303 )?;
304
305 if bytes_read == 0 {
306 break; // End of file or no more data
307 }
308
309 total_read += bytes_read;
310
311 // Check if we're matching the delimiter
312 if buffer[total_read - 1] == delimiter[match_count] {
313 match_count += 1;
314 if match_count == delimiter.len() {
315 break; // Found complete delimiter
316 }
317 } else {
318 // Reset and check if current byte starts a new match
319 match_count = 0;
320 if buffer[total_read - 1] == delimiter[0] {
321 match_count = 1;
322 }
323 }
324 }
325
326 Ok(total_read)
327 }
328
329 fn write(&self, buffer: &[u8], absolute_position: Size) -> Result<usize>;
330
331 fn write_pattern(
332 &self,
333 pattern: &[u8],
334 count: usize,
335 absolute_position: Size,
336 ) -> Result<usize> {
337 let mut total_written = 0;
338
339 for _ in 0..count {
340 let bytes_written = self.write(pattern, absolute_position + total_written as Size)?;
341 if bytes_written == 0 {
342 break; // Unable to write more
343 }
344 total_written += bytes_written;
345 }
346
347 Ok(total_written)
348 }
349
350 fn write_vectored(&self, buffers: &[&[u8]], absolute_position: Size) -> Result<usize> {
351 let mut total_written = 0;
352
353 for buffer in buffers {
354 if buffer.is_empty() {
355 continue; // Skip empty buffers
356 }
357 let bytes_written = self.write(buffer, absolute_position + total_written as Size)?;
358 if bytes_written == 0 {
359 break; // Unable to write more
360 }
361 total_written += bytes_written;
362 }
363
364 Ok(total_written)
365 }
366
367 fn flush(&self) -> Result<()> {
368 Ok(())
369 }
370
371 fn set_position(&self, _current_position: Size, _position: &Position) -> Result<Size> {
372 match _position {
373 Position::Start(position) => Ok(*position),
374 Position::Current(offset) => Ok(_current_position.wrapping_add(*offset as Size)),
375 Position::End(_) => Err(Error::UnsupportedOperation),
376 }
377 }
378
379 fn control(
380 &self,
381 _command: ControlCommandIdentifier,
382 _input: &AnyByLayout,
383 _output: &mut AnyByLayout,
384 ) -> Result<()> {
385 Err(Error::UnsupportedOperation)
386 }
387}
388
389impl<T> BaseOperations for T
390where
391 T: DirectBaseOperations + Send + Sync,
392{
393 fn open(&self, _: &mut Context) -> Result<()> {
394 self.open()
395 }
396
397 fn read(&self, _: &mut Context, buffer: &mut [u8], absolute_position: Size) -> Result<usize> {
398 self.read(buffer, absolute_position)
399 }
400
401 fn write(&self, _: &mut Context, buffer: &[u8], absolute_position: Size) -> Result<usize> {
402 self.write(buffer, absolute_position)
403 }
404
405 fn write_pattern(
406 &self,
407 _: &mut Context,
408 pattern: &[u8],
409 count: usize,
410 absolute_position: Size,
411 ) -> Result<usize> {
412 self.write_pattern(pattern, count, absolute_position)
413 }
414
415 fn write_vectored(
416 &self,
417 _context: &mut Context,
418 buffers: &[&[u8]],
419 absolute_position: Size,
420 ) -> Result<usize> {
421 self.write_vectored(buffers, absolute_position)
422 }
423
424 fn flush(&self, _: &mut Context) -> Result<()> {
425 self.flush()
426 }
427
428 fn set_position(
429 &self,
430 _context: &mut Context,
431 current_position: Size,
432 position: &Position,
433 ) -> Result<Size> {
434 self.set_position(current_position, position)
435 }
436
437 fn control(
438 &self,
439 _: &mut Context,
440 command: ControlCommandIdentifier,
441 input: &AnyByLayout,
442 output: &mut AnyByLayout,
443 ) -> Result<()> {
444 self.control(command, input, output)
445 }
446
447 fn clone_context(&self, _context: &Context) -> Result<Context> {
448 Ok(Context::new_empty())
449 }
450}