1use core::ptr::null_mut;
2
3use super::{Configuration, Directory, convert_result, littlefs};
4use crate::{
5 File,
6 attributes::InternalAttributes,
7 configuration::{self},
8};
9use alloc::{boxed::Box, ffi::CString};
10use file_system::{
11 AttributeFlags, AttributeOperations, Attributes, BaseOperations, Context, DirectBlockDevice,
12 DirectoryOperations, Entry, Error, FileOperations, FileSystemOperations, Flags, Kind,
13 MountOperations, Path, Position, Result, Size, mount::MutexMountWrapper,
14};
15use synchronization::blocking_mutex::raw::CriticalSectionRawMutex;
16
17pub struct FileSystem {
18 file_system: MutexMountWrapper<CriticalSectionRawMutex, littlefs::lfs_t>,
19}
20
21impl FileSystem {
22 pub fn new_format(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<Self> {
23 Self::format(device, cache_size)?;
24
25 Self::new(device, cache_size)
26 }
27
28 pub fn get_or_format(
29 device: &'static dyn DirectBlockDevice,
30 cache_size: usize,
31 ) -> Result<Self> {
32 match Self::new(device, cache_size) {
33 Ok(file_system) => Ok(file_system),
34 Err(_) => {
35 device.set_position(0, &Position::Start(0))?;
36
37 Self::format(device, cache_size)?;
38
39 Self::new(device, cache_size)
40 }
41 }
42 }
43
44 pub fn new(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<Self> {
45 let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
46 let block_count = device.get_block_count().map_err(|_| Error::InputOutput)?;
47
48 let configuration: littlefs::lfs_config = Configuration::new(
49 device,
50 block_size as _,
51 block_count as _,
52 cache_size,
53 cache_size,
54 )
55 .ok_or(Error::InvalidParameter)?
56 .try_into()
57 .map_err(|_| Error::InvalidParameter)?;
58
59 let configuration = Box::new(configuration);
60
61 let mut file_system = littlefs::lfs_t::default();
62
63 unsafe {
64 convert_result(littlefs::lfs_mount(
65 &mut file_system,
66 Box::leak(configuration),
67 ))?;
68
69 let result = convert_result(littlefs::lfs_getattr(
70 &mut file_system,
71 c"/".as_ptr(),
72 InternalAttributes::IDENTIFIER,
73 null_mut(),
74 0,
75 ));
76
77 if let Err(Error::NoAttribute) = result {
78 let mut internal_attributes = InternalAttributes::new_uninitialized().assume_init();
80 internal_attributes.kind = Kind::Directory;
81
82 convert_result(littlefs::lfs_setattr(
83 &mut file_system,
84 c"/".as_ptr(),
85 InternalAttributes::IDENTIFIER,
86 &internal_attributes as *const _ as *const _,
87 size_of::<InternalAttributes>() as u32,
88 ))?;
89 } else {
90 result?;
91 }
92 }
93
94 Ok(Self {
95 file_system: MutexMountWrapper::new_mounted(file_system),
96 })
97 }
98
99 pub fn format(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<()> {
100 let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
101 let block_count = device.get_block_count().map_err(|_| Error::InputOutput)?;
102
103 let configuration: littlefs::lfs_config = Configuration::new(
104 device,
105 block_size as _,
106 block_count as _,
107 cache_size,
108 cache_size,
109 )
110 .ok_or(Error::InvalidParameter)?
111 .try_into()
112 .map_err(|_| Error::InvalidParameter)?;
113
114 let mut file_system = littlefs::lfs_t::default();
115
116 convert_result(unsafe { littlefs::lfs_format(&mut file_system, &configuration) })?;
117
118 Ok(())
119 }
120
121 pub fn operation<T>(
122 &self,
123 operation: impl FnOnce(&mut littlefs::lfs_t) -> Result<T>,
124 ) -> Result<T> {
125 let mut file_system = self.file_system.try_get()?;
126
127 operation(&mut file_system)
128 }
129
130 pub fn operation_with_context<I: 'static, T>(
131 &self,
132 context: &mut Context,
133 operation: impl FnOnce(&mut littlefs::lfs_t, &mut I) -> Result<T>,
134 ) -> Result<T> {
135 let mut file_system = self.file_system.try_get()?;
136
137 let file = context
138 .get_private_data_mutable_of_type::<I>()
139 .ok_or(Error::InvalidParameter)?;
140
141 operation(&mut file_system, file)
142 }
143}
144
145unsafe impl Send for FileSystem {}
146unsafe impl Sync for FileSystem {}
147
148impl MountOperations for FileSystem {
149 fn unmount(&self) -> Result<()> {
150 self.file_system.unmount()
151 }
152}
153
154impl FileSystemOperations for FileSystem {
155 fn rename(&self, source: &Path, destination: &Path) -> Result<()> {
156 self.operation(|file_system| {
157 let source = CString::new(source.as_str()).map_err(|_| Error::InvalidParameter)?;
158
159 let destination =
160 CString::new(destination.as_str()).map_err(|_| Error::InvalidParameter)?;
161
162 convert_result(unsafe {
163 littlefs::lfs_rename(file_system, source.as_ptr(), destination.as_ptr())
164 })?;
165
166 Ok(())
167 })
168 }
169
170 fn remove(&self, path: &Path) -> Result<()> {
171 let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
172
173 self.operation(|file_system| {
174 convert_result(unsafe { littlefs::lfs_remove(file_system, path.as_ptr()) })?;
175
176 Ok(())
177 })
178 }
179
180 fn create_directory(&self, path: &Path) -> Result<()> {
181 self.operation(|file_system| {
182 Directory::create(file_system, path)?;
183
184 Ok(())
185 })
186 }
187
188 fn lookup_directory(&self, context: &mut Context, path: &Path) -> Result<()> {
189 self.operation(|file_system| {
190 let directory = Directory::lookup(file_system, path)?;
191 context.set_private_data(directory);
192 Ok(())
193 })
194 }
195
196 fn lookup_file(&self, context: &mut Context, path: &Path, flags: Flags) -> Result<()> {
197 self.operation(|file_system| {
198 let file = File::lookup(file_system, path, flags)?;
199 context.set_private_data(file);
200 Ok(())
201 })
202 }
203
204 fn create_file(&self, path: &Path) -> Result<()> {
205 self.operation(|file_system| File::create(file_system, path))
206 }
207
208 fn get_attributes(&self, path: &Path, attributes: &mut Attributes) -> Result<()> {
209 self.operation(|file_system| {
210 let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
211
212 let mut internal_attributes =
213 unsafe { InternalAttributes::new_uninitialized().assume_init() };
214
215 convert_result(unsafe {
216 littlefs::lfs_getattr(
217 file_system,
218 path.as_ptr(),
219 InternalAttributes::IDENTIFIER,
220 &mut internal_attributes as *mut _ as *mut _,
221 size_of::<InternalAttributes>() as u32,
222 )
223 })?;
224
225 internal_attributes.update_attributes(attributes)?;
226
227 if let Some(size) = attributes.get_mutable_size() {
228 let mut info = littlefs::lfs_info::default();
229
230 convert_result(unsafe {
231 littlefs::lfs_stat(file_system, path.as_ptr(), &mut info)
232 })?;
233
234 *size = info.size as Size;
235 }
236
237 Ok(())
238 })
239 }
240
241 fn set_attributes(&self, path: &Path, attributes: &Attributes) -> Result<()> {
242 self.operation(|file_system| {
243 let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
244
245 let mut internal_attributes =
246 unsafe { InternalAttributes::new_uninitialized().assume_init() };
247
248 if attributes.get_mask() != AttributeFlags::All {
249 convert_result(unsafe {
250 littlefs::lfs_getattr(
251 file_system,
252 path.as_ptr(),
253 InternalAttributes::IDENTIFIER,
254 &mut internal_attributes as *mut _ as *mut _,
255 size_of::<InternalAttributes>() as u32,
256 )
257 })?;
258 }
259
260 internal_attributes.update_with_attributes(attributes)?;
261
262 convert_result(unsafe {
263 littlefs::lfs_setattr(
264 file_system,
265 path.as_ptr(),
266 InternalAttributes::IDENTIFIER,
267 &internal_attributes as *const _ as *const _,
268 size_of::<InternalAttributes>() as u32,
269 )
270 })?;
271
272 Ok(())
273 })
274 }
275}
276
277impl AttributeOperations for FileSystem {
278 fn get_attributes(&self, context: &mut Context, attributes: &mut Attributes) -> Result<()> {
279 if let Some(file) = context.get_private_data_mutable_of_type::<File>() {
280 file.get_attributes(attributes)
281 } else if let Some(directory) = context.get_private_data_mutable_of_type::<Directory>() {
282 directory.get_attributes(attributes)
283 } else {
284 Err(Error::InvalidParameter)
285 }
286 }
287
288 fn set_attributes(&self, context: &mut Context, attributes: &Attributes) -> Result<()> {
289 if let Some(file) = context.get_private_data_mutable_of_type::<File>() {
290 file.set_attributes(attributes)
291 } else if let Some(directory) = context.get_private_data_mutable_of_type::<Directory>() {
292 directory.set_attributes(attributes)
293 } else {
294 Err(Error::InvalidParameter)
295 }
296 }
297}
298
299impl BaseOperations for FileSystem {
300 fn read(
301 &self,
302 context: &mut Context,
303 buffer: &mut [u8],
304 absolute_position: Size,
305 ) -> Result<usize> {
306 self.operation_with_context(context, |file_system, file: &mut File| {
307 file.read(file_system, buffer, absolute_position)
308 })
309 }
310
311 fn write(
312 &self,
313 context: &mut Context,
314 buffer: &[u8],
315 absolute_position: Size,
316 ) -> Result<usize> {
317 self.operation_with_context(context, |file_system, file: &mut File| {
318 file.write(file_system, buffer, absolute_position)
319 })
320 }
321
322 fn set_position(
323 &self,
324 context: &mut Context,
325 current_position: Size,
326 position: &Position,
327 ) -> Result<Size> {
328 self.operation_with_context(context, |file_system, file: &mut File| {
329 file.set_position(file_system, current_position, position)
330 })
331 }
332
333 fn flush(&self, context: &mut Context) -> Result<()> {
334 self.operation_with_context(context, |file_system, file: &mut File| {
335 file.flush(file_system)
336 })
337 }
338
339 fn clone_context(&self, context: &Context) -> Result<Context> {
340 if let Some(file) = context.get_private_data_of_type::<File>() {
341 Ok(Context::new(Some(file.clone())))
342 } else if let Some(directory) = context.get_private_data_of_type::<Directory>() {
343 Ok(Context::new(Some(directory.clone())))
344 } else {
345 Err(Error::InvalidParameter)
346 }
347 }
348
349 fn close(&self, context: &mut Context) -> Result<()> {
350 self.operation(|file_system| {
351 let mut file = context
352 .take_private_data_of_type::<File>()
353 .ok_or(Error::InvalidParameter)?;
354
355 file.close(file_system)?;
356
357 Ok(())
358 })
359 }
360}
361
362impl FileOperations for FileSystem {}
363
364impl DirectoryOperations for FileSystem {
365 fn read(&self, context: &mut Context) -> Result<Option<Entry>> {
366 self.operation_with_context(context, |file_system, directory: &mut Directory| {
367 directory.read(file_system)
368 })
369 }
370
371 fn get_position(&self, context: &mut file_system::Context) -> Result<Size> {
372 self.operation_with_context(context, |file_system, directory: &mut Directory| {
373 directory.get_position(file_system)
374 })
375 }
376
377 fn set_position(&self, context: &mut Context, position: Size) -> Result<()> {
378 self.operation_with_context(context, |file_system, directory: &mut Directory| {
379 directory.set_position(file_system, position)
380 })
381 }
382
383 fn rewind(&self, context: &mut Context) -> Result<()> {
384 self.operation_with_context(context, |file_system, directory: &mut Directory| {
385 directory.rewind(file_system)
386 })
387 }
388
389 fn close(&self, context: &mut Context) -> Result<()> {
390 self.operation(|file_system| {
391 let mut directory = context
392 .take_private_data_of_type::<Directory>()
393 .ok_or(Error::InvalidParameter)?;
394
395 directory.close(file_system)?;
396
397 Ok(())
398 })
399 }
400}
401
402impl Drop for FileSystem {
403 fn drop(&mut self) {
404 let _ = self.operation(|file_system| {
405 unsafe {
406 littlefs::lfs_unmount(file_system);
407 }
408
409 let mut configuration =
410 unsafe { Box::from_raw(file_system.cfg as *mut littlefs::lfs_config) };
411
412 unsafe {
413 configuration::Context::take_from_configuration(&mut *configuration);
414 }
415
416 Ok(())
417 });
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 extern crate std;
424
425 use drivers_std;
426 use file_system::{MemoryDevice, file_system::tests::implement_file_system_tests};
427
428 use super::*;
429
430 const CACHE_SIZE: usize = 256;
431
432 drivers_std::memory::instantiate_global_allocator!();
433
434 fn initialize() -> FileSystem {
435 if !log::is_initialized() {
436 let _ = log::initialize(&drivers_std::log::Logger);
437 }
438
439 let _ = users::initialize();
440
441 task::initialize();
442
443 let _ = time::initialize(&drivers_std::devices::TimeDevice).unwrap();
444
445 let device = Box::leak(Box::new(MemoryDevice::<512>::new(2048 * 512)));
446
447 FileSystem::format(device, CACHE_SIZE).unwrap();
448
449 FileSystem::new(device, CACHE_SIZE).unwrap()
450 }
451
452 implement_file_system_tests!(initialize());
453}