fatfs_embedded/
lib.rs

1//! FatFs is a generic FAT/exFAT filesystem module designed for small embedded systems.
2//! `fatfs-embedded` is a Rust wrapper of the popular FatFs library from
3//! [elm-chan.org](http://elm-chan.org/fsw/ff/00index_e.html). 
4//! It is based on the R0.15 release.
5//! 
6//! # Goals
7//! * Embedded use - This library is `no_std` by default, but is `std` compatible for 
8//! testing purposes when targeting an OS.
9//! * Thread safe - The choice was made to have a dependency on the Embassy
10//! framework for concurrency support which is suitable for embedded systems. A global
11//! file system mutex is implemented in favor of the `FF_FS_REENTRANT` option, which is
12//! more suitable to a Rust implementation.
13//! * Portable - Implement the `FatFsDriver` trait to add support for any block device.
14//! To support this implementation, `alloc` support is unfortunately required due to the 
15//! structure of FatFs. A simulated block storage driver implementation is included for 
16//! test purposes that may be used for reference.
17//! 
18//! ## Drop
19//! The decision was made not to implement the `Drop` trait for files or directories. 
20//! This is because doing so would require acquiring a lock on the file system object,
21//! which can easily cause a lockup condition.
22//! Files and directories must be manually closed. (The file system object itself is 
23//! implemented as a static singleton and thus is never dropped.)
24//! 
25//! # FatFs Configuration
26//! Most features of FatFs are enabled with a few exceptions:
27//! * `FF_USE_FORWARD` is disabled to avoid using additional `unsafe` code.
28//! * `FF_CODE_PAGE` is set to 0 and thus must be set via a call to `setcp()`.
29//! * `FF_VOLUMES` is currently set to 1 limiting the number of volumes supported to 1.
30//! * `FF_MULTI_PARTITION` is not currently supported.
31//! * `FF_FS_LOCK` is configured to support 10 simultaneous open files.
32//! * An implementation of the `f_printf()` function is not provided.
33//! 
34//! # Features
35//! * `chrono` (default) - Enables time support in the library. Access to an RTC may be 
36//! provided via an implementation of the `FatFsDriver` trait.
37//! 
38//! # Examples
39//! A brief example that formats and mounts a simulated drive, writes a string to a file, 
40//! then reads the data back:
41//! ```
42//! #[path = "../tests/simulated_driver.rs"]
43//! mod simulated_driver;
44//! 
45//! use fatfs_embedded::fatfs::{self, File, FileOptions, FormatOptions};
46//! use embassy_futures::block_on;
47//! 
48//! const TEST_STRING: &[u8] = b"Hello world!";
49//! 
50//! //Install a block device driver that implements `FatFsDriver`
51//! let driver = simulated_driver::RamBlockStorage::new();
52//! block_on(fatfs::diskio::install(driver));
53//! 
54//! //Acquire a lock on the file system.
55//! let mut locked_fs = block_on(fatfs::FS.lock());
56//! 
57//! //Format the drive.
58//! locked_fs.mkfs("", FormatOptions::FAT32, 0, 0, 0, 0);
59//! 
60//! //Mount the drive.
61//! locked_fs.mount();
62//! 
63//! //Create a new file.
64//! let mut test_file: File = locked_fs.open("test.txt", 
65//!     FileOptions::CreateAlways | 
66//!     FileOptions::Read | 
67//!     FileOptions::Write).unwrap();
68//! 
69//! //Write a string to the file.
70//! locked_fs.write(&mut test_file, TEST_STRING);
71//! 
72//! //Seek back to the beginning of the file.
73//! locked_fs.seek(&mut test_file, 0);
74//! 
75//! //Read the string back from the file.
76//! let mut read_back: [u8; TEST_STRING.len()] = [0; TEST_STRING.len()];
77//! locked_fs.read(&mut test_file, &mut read_back);
78//! assert_eq!(TEST_STRING, read_back);
79//! 
80//! //Close the file when done.
81//! locked_fs.close(&mut test_file);
82//! ```
83
84#![no_std]
85
86pub mod fatfs {
87
88    /// Block storage I/O objects are located here.
89    pub mod diskio;
90    mod inc_bindings;
91
92    extern crate alloc;
93
94    use core::ptr;
95    use alloc::string::String;
96    use bitflags::bitflags;
97    use embassy_sync::{mutex::Mutex, blocking_mutex::raw::ThreadModeRawMutex};
98    use crate::fatfs::inc_bindings::*;
99    
100    #[cfg(feature = "chrono")]
101    use chrono::{NaiveDateTime, Timelike, Datelike};
102
103    #[derive(Debug)]
104    #[derive(PartialEq)]
105    pub enum Error {
106        DiskError = FRESULT_FR_DISK_ERR as isize,
107        IntError = FRESULT_FR_INT_ERR as isize,
108        NotReady = FRESULT_FR_NOT_READY as isize,
109        NoFile = FRESULT_FR_NO_FILE as isize,
110        NoPath = FRESULT_FR_NO_PATH as isize,
111        InvalidName = FRESULT_FR_INVALID_NAME as isize,
112        Denied = FRESULT_FR_DENIED as isize,
113        Exists = FRESULT_FR_EXIST as isize,
114        InvalidObject = FRESULT_FR_INVALID_OBJECT as isize,
115        WriteProtected = FRESULT_FR_WRITE_PROTECTED as isize,
116        InvalidDrive = FRESULT_FR_INVALID_DRIVE as isize,
117        NotEnabled = FRESULT_FR_NOT_ENABLED as isize,
118        NoFileSystem = FRESULT_FR_NO_FILESYSTEM as isize,
119        MkfsAborted = FRESULT_FR_MKFS_ABORTED as isize,
120        Timeout = FRESULT_FR_TIMEOUT as isize,
121        Locked = FRESULT_FR_LOCKED as isize,
122        NotEnoughCore = FRESULT_FR_NOT_ENOUGH_CORE as isize,
123        TooManyOpenFiles = FRESULT_FR_TOO_MANY_OPEN_FILES as isize,
124        InvalidParameter = FRESULT_FR_INVALID_PARAMETER as isize
125    }
126
127    impl TryFrom<u32> for Error {
128        type Error = ();
129
130        fn try_from(v: u32) -> Result<Self, Self::Error> {
131            match v {
132                x if x == Error::DiskError as u32 => Ok(Error::DiskError),
133                x if x == Error::IntError as u32 => Ok(Error::IntError),
134                x if x == Error::NotReady as u32 => Ok(Error::NotReady),
135                x if x == Error::NoFile as u32 => Ok(Error::NoFile),
136                x if x == Error::NoPath as u32 => Ok(Error::NoPath),
137                x if x == Error::InvalidName as u32 => Ok(Error::InvalidName),
138                x if x == Error::Denied as u32 => Ok(Error::Denied),
139                x if x == Error::Exists as u32 => Ok(Error::Exists),
140                x if x == Error::InvalidObject as u32 => Ok(Error::InvalidObject),
141                x if x == Error::WriteProtected as u32 => Ok(Error::WriteProtected),
142                x if x == Error::InvalidDrive as u32 => Ok(Error::InvalidDrive),
143                x if x == Error::NotEnabled as u32 => Ok(Error::NotEnabled),
144                x if x == Error::NoFileSystem as u32 => Ok(Error::NoFileSystem),
145                x if x == Error::MkfsAborted as u32 => Ok(Error::MkfsAborted),
146                x if x == Error::Timeout as u32 => Ok(Error::Timeout),
147                x if x == Error::Locked as u32 => Ok(Error::Locked),
148                x if x == Error::NotEnoughCore as u32 => Ok(Error::NotEnoughCore),
149                x if x == Error::TooManyOpenFiles as u32 => Ok(Error::TooManyOpenFiles),
150                x if x == Error::InvalidParameter as u32 => Ok(Error::InvalidParameter),
151                _ => Err(()),
152            }
153        }
154    }
155
156    impl Default for FATFS {
157        fn default() -> FATFS {
158            FATFS {
159                fs_type: Default::default(), 
160                pdrv: Default::default(), 
161                ldrv: Default::default(), 
162                n_fats: Default::default(), 
163                wflag: Default::default(), 
164                fsi_flag: Default::default(), 
165                id: Default::default(), 
166                n_rootdir: Default::default(), 
167                csize: Default::default(), 
168                last_clst: Default::default(), 
169                free_clst: Default::default(), 
170                n_fatent: Default::default(), 
171                fsize: Default::default(), 
172                volbase: Default::default(), 
173                fatbase: Default::default(), 
174                dirbase: Default::default(), 
175                database: Default::default(), 
176                winsect: Default::default(), 
177                win: [0; 512],
178                lfnbuf: ptr::null_mut(),
179                cdir: Default::default(),
180            }
181        }
182    }
183
184    impl Default for FFOBJID {
185        fn default() -> Self {
186            Self {
187                fs: ptr::null_mut(),
188                id: Default::default(),
189                attr: Default::default(),
190                stat: Default::default(),
191                sclust: Default::default(),
192                objsize: Default::default(),
193                lockid: Default::default(),
194            }
195        }
196    }
197
198    impl Default for FIL {
199        fn default() -> Self {
200            Self { 
201                obj: Default::default(), 
202                flag: Default::default(), 
203                err: Default::default(), 
204                fptr: Default::default(), 
205                clust: Default::default(), 
206                sect: Default::default(), 
207                dir_sect: Default::default(), 
208                dir_ptr: ptr::null_mut(), 
209                buf: [0; 512],
210                cltbl: ptr::null_mut() 
211            }
212        }
213    }
214
215    impl Default for DIR {
216        fn default() -> Self {
217            Self {
218                obj: Default::default(),
219                dptr: Default::default(),
220                clust: Default::default(),
221                sect: Default::default(),
222                dir: ptr::null_mut(),
223                fn_: Default::default(),
224                blk_ofs: Default::default(),
225                pat: ptr::null_mut(),
226            }
227        }
228    }
229
230    impl Default for FILINFO {
231        fn default() -> Self {
232            Self {
233                fsize: Default::default(),
234                fdate: Default::default(),
235                ftime: Default::default(),
236                fattrib: Default::default(),
237                fname: [0; 256],
238                altname: Default::default(),
239            }
240        }
241    }
242
243    bitflags! {
244        pub struct FileOptions: u8 {
245            const Read = FA_READ as u8;
246            const Write = FA_WRITE as u8;
247            const OpenExisting = FA_OPEN_EXISTING as u8;
248            const CreateNew = FA_CREATE_NEW as u8;
249            const CreateAlways = FA_CREATE_ALWAYS as u8;
250            const OpenAlways = FA_OPEN_ALWAYS as u8;
251            const OpenAppend = FA_OPEN_APPEND as u8;
252        }
253    }
254
255    bitflags! {
256        pub struct FileAttributes: u8 {
257            const ReadOnly = AM_RDO as u8;
258            const Hidden = AM_HID as u8;
259            const System = AM_SYS as u8;
260            const Directory = AM_DIR as u8;
261            const Archive = AM_ARC as u8;
262        }
263    }
264
265    bitflags! {
266        pub struct FormatOptions: u8 {
267            const FAT = FM_FAT as u8;
268            const FAT32 = FM_FAT32 as u8;
269            const EXFAT = FM_EXFAT as u8;
270            const Any = FM_ANY as u8;
271        }
272    }
273
274    impl FileOptions {
275        pub fn as_u8(&self) -> u8 {
276            self.bits() as u8
277        }
278    }
279
280    impl FileAttributes {
281        pub fn as_u8(&self) -> u8 {
282            self.bits() as u8
283        }
284    }
285
286    impl FormatOptions {
287        pub fn as_u8(&self) -> u8 {
288            self.bits() as u8
289        }
290    }
291
292    pub type FileSystem = Mutex<ThreadModeRawMutex, RawFileSystem>;
293    pub type File = FIL;
294    pub type Directory = DIR;
295    pub type FileInfo = FILINFO;
296
297    /// This is the file system singleton object. Access the file system
298    /// API by acquiring a lock on this object.
299    pub static FS: FileSystem = Mutex::new(
300        RawFileSystem { fs:
301            FATFS {
302                fs_type: 0, 
303                pdrv: 0, 
304                ldrv: 0, 
305                n_fats: 0, 
306                wflag: 0, 
307                fsi_flag: 0, 
308                id: 0, 
309                n_rootdir: 0, 
310                csize: 0, 
311                last_clst: 0, 
312                free_clst: 0, 
313                n_fatent: 0, 
314                fsize: 0, 
315                volbase: 0, 
316                fatbase: 0, 
317                dirbase: 0, 
318                database: 0, 
319                winsect: 0, 
320                win: [0; 512],
321                lfnbuf: ptr::null_mut(),
322                cdir: 0,
323            }
324    });
325
326    /// The file system API is located here.
327    pub struct RawFileSystem {
328        fs: FATFS
329    }
330
331    unsafe impl Send for RawFileSystem {}
332
333    impl RawFileSystem {
334        /// Opens the file at the given path in the given mode. FileOption flags may be OR'd together.
335        pub fn open(&self, path: &str, mode: FileOptions) -> Result<File, Error> {
336            let result;
337            let mut file = Default::default(); 
338            unsafe { result = f_open(ptr::addr_of_mut!(file), path.as_ptr().cast(), mode.as_u8());}
339            if result == FRESULT_FR_OK {
340                return Ok(file)
341            } else {
342                return Err(Error::try_from(result).unwrap())
343            }
344        }
345
346        /// Closes the given file.
347        pub fn close(&self, file: &mut File) -> Result<(), Error> {
348            let result;
349            unsafe { result = f_close(ptr::addr_of_mut!(*file)); }
350            if result == FRESULT_FR_OK {
351                return Ok(())
352            } else {
353                return Err(Error::try_from(result).unwrap())
354            }
355        }
356
357        /// Read data from the given file. The length of the provided buffer determines the length of data read.
358        pub fn read(&self, file: &mut File, buffer: &mut [u8]) -> Result<u32, Error> {
359            let result;
360            let mut bytes_read: UINT = 0;
361            unsafe { result = f_read(ptr::addr_of_mut!(*file), buffer.as_mut_ptr().cast(), buffer.len() as u32, ptr::addr_of_mut!(bytes_read)); }
362            if result == FRESULT_FR_OK {
363                return Ok(bytes_read)
364            } else {
365                return Err(Error::try_from(result).unwrap())
366            }
367        }
368
369        /// Write data to the given file. The length of the provided buffer determines the length of data written.
370        pub fn write(&self, file: &mut File, buffer: &[u8]) -> Result<u32, Error> {
371            let result;
372            let mut bytes_written: UINT = 0;
373            unsafe { result = f_write(ptr::addr_of_mut!(*file), buffer.as_ptr().cast(), buffer.len() as u32, ptr::addr_of_mut!(bytes_written)); }
374            if result == FRESULT_FR_OK {
375                return Ok(bytes_written)
376            } else {
377                return Err(Error::try_from(result).unwrap())
378            }
379        }
380
381        /// Move to an offset in the given file. This represents the location within the file for where data is read or written.
382        pub fn seek(&self, file: &mut File, offset: u32) -> Result<(), Error> {
383            let result;
384            unsafe { result = f_lseek(ptr::addr_of_mut!(*file), offset); }
385            if result == FRESULT_FR_OK {
386                return Ok(())
387            } else {
388                return Err(Error::try_from(result).unwrap())
389            }
390        }
391
392        /// Truncates the given file.
393        pub fn truncate(&self, file: &mut File) -> Result<(), Error> {
394            let result;
395            unsafe { result = f_truncate(ptr::addr_of_mut!(*file)); }
396            if result == FRESULT_FR_OK {
397                return Ok(())
398            } else {
399                return Err(Error::try_from(result).unwrap())
400            }
401        }
402
403        /// Forces a write of all data to storage. Whether this has any effect depends on the driver implementation.
404        pub fn sync(&self, file: &mut File) -> Result<(), Error> {
405            let result;
406            unsafe { result = f_sync(ptr::addr_of_mut!(*file)); }
407            if result == FRESULT_FR_OK {
408                return Ok(())
409            } else {
410                return Err(Error::try_from(result).unwrap())
411            }
412        }
413
414        /// Opens a directory. On success, the Directory object is returned.
415        pub fn opendir(&self, path: &str) -> Result<Directory, Error> {
416            let result;
417            let mut dir: Directory = Default::default();
418            unsafe { result = f_opendir(ptr::addr_of_mut!(dir), path.as_ptr().cast()); }
419            if result == FRESULT_FR_OK {
420                return Ok(dir)
421            } else {
422                return Err(Error::try_from(result).unwrap())
423            }
424        }
425
426        /// Closes the given directory.
427        pub fn closedir(&self, dir: &mut Directory) -> Result<(), Error> {
428            let result;
429            unsafe { result = f_closedir(ptr::addr_of_mut!(*dir)); }
430            if result == FRESULT_FR_OK {
431                return Ok(())
432            } else {
433                return Err(Error::try_from(result).unwrap())
434            }
435        }
436
437        /// Gets information about items within the given directory.
438        /// Each call to this function returns the next item in sequence, until a null string is returned.
439        pub fn readdir(&self, dir:  &mut Directory) -> Result<FileInfo, Error> {
440            let result;
441            let mut info: FileInfo = Default::default();
442            unsafe { result = f_readdir(ptr::addr_of_mut!(*dir), ptr::addr_of_mut!(info)); }
443            if result == FRESULT_FR_OK {
444                return Ok(info)
445            } else {
446                return Err(Error::try_from(result).unwrap())
447            }
448        }
449
450        /// Find the first item that matches the given pattern.
451        /// On success a tuple is returned containing file information and the enclosing directory.
452        pub fn findfirst(&self, path: &str, pattern: &str) -> Result<(Directory, FileInfo), Error> {
453            let result;
454            let mut info: FileInfo = Default::default();
455            let mut dir: Directory = Default::default();
456            unsafe { result = f_findfirst(ptr::addr_of_mut!(dir), ptr::addr_of_mut!(info), path.as_ptr().cast(), pattern.as_ptr().cast()); }
457            if result == FRESULT_FR_OK {
458                return Ok((dir, info))
459            } else {
460                return Err(Error::try_from(result).unwrap())
461            }
462        }
463
464        /// Returns the next item that matches a pattern following a call to `findfirst()`.
465        pub fn findnext(&self, dir: &mut Directory) -> Result<FileInfo, Error> {
466            let result;
467            let mut info: FileInfo = Default::default();
468            unsafe { result = f_findnext(ptr::addr_of_mut!(*dir), ptr::addr_of_mut!(info)); }
469            if result == FRESULT_FR_OK {
470                return Ok(info)
471            } else {
472                return Err(Error::try_from(result).unwrap())
473            }
474        }
475
476        /// Create a directory at the specified path.
477        pub fn mkdir(&self, path: &str) -> Result<(), Error> {
478            let result;
479            unsafe { result = f_mkdir(path.as_ptr().cast()); }
480            if result == FRESULT_FR_OK {
481                return Ok(())
482            } else {
483                return Err(Error::try_from(result).unwrap())
484            }
485        }
486
487        /// Deletes a file at the specified path.
488        pub fn unlink(&self, path: &str) -> Result<(), Error> {
489            let result;
490            unsafe { result = f_unlink(path.as_ptr().cast()); }
491            if result == FRESULT_FR_OK {
492                return Ok(())
493            } else {
494                return Err(Error::try_from(result).unwrap())
495            }
496        }
497
498        /// Renames a file at the old path to the new path.
499        pub fn rename(&self, old_path: &str, new_path: &str) -> Result<(), Error> {
500            let result;
501            unsafe { result = f_rename(old_path.as_ptr().cast(), new_path.as_ptr().cast()); }
502            if result == FRESULT_FR_OK {
503                return Ok(())
504            } else {
505                return Err(Error::try_from(result).unwrap())
506            }
507        }
508
509        /// Returns information about a file at the given path.
510        pub fn stat(&self, path: &str) -> Result<FileInfo, Error> {
511            let result;
512            let mut info: FileInfo = Default::default();
513            unsafe { result = f_stat(path.as_ptr().cast(), ptr::addr_of_mut!(info)); }
514            if result == FRESULT_FR_OK {
515                return Ok(info)
516            } else {
517                return Err(Error::try_from(result).unwrap())
518            }
519        }
520
521        /// Applies the given attributes to the file according to the supplied mask.
522        pub fn chmod(&self, path: &str, attr: FileAttributes, mask: FileAttributes) -> Result<(), Error> {
523            let result;
524            unsafe { result = f_chmod(path.as_ptr().cast(), attr.as_u8(), mask.as_u8()); }
525            if result == FRESULT_FR_OK {
526                return Ok(())
527            } else {
528                return Err(Error::try_from(result).unwrap())
529            }
530        }
531
532        /// Applies a timestamp to the given file.
533        #[cfg(feature = "chrono")]
534        pub fn utime(&self, path: &str, timestamp: NaiveDateTime) -> Result<(), Error> {
535            let result;
536            let year = timestamp.year() as u32;
537            let month = timestamp.month();
538            let day = timestamp.day();
539            let hour = timestamp.hour();
540            let minute = timestamp.minute();
541            let second = timestamp.second();
542            let mut info = FileInfo::default();
543            info.fdate = (((year - 1980) * 512) | month * 32 | day) as u16;
544            info.ftime = (hour * 2048 | minute * 32 | second / 2) as u16;
545            unsafe { result = f_utime(path.as_ptr().cast(), ptr::addr_of_mut!(info)); }
546            if result == FRESULT_FR_OK {
547                return Ok(())
548            } else {
549                return Err(Error::try_from(result).unwrap())
550            }
551        }
552
553        /// Change the current directory to the given path.
554        pub fn chdir(&self, path: &str) -> Result<(), Error> {
555            let result;
556            unsafe { result = f_chdir(path.as_ptr().cast()); }
557            if result == FRESULT_FR_OK {
558                return Ok(())
559            } else {
560                return Err(Error::try_from(result).unwrap())
561            }
562        }
563
564        /// Change the current drive.
565        pub fn chdrive(&self, path: &str) -> Result<(), Error> {
566            let result;
567            unsafe { result = f_chdrive(path.as_ptr().cast()); }
568            if result == FRESULT_FR_OK {
569                return Ok(())
570            } else {
571                return Err(Error::try_from(result).unwrap())
572            }
573        }
574
575        /// Retrieves full path name of the current directory of the current drive.
576        /// The supplied String buffer must have sufficient capacity to read the entire path.
577        pub fn getcwd(&self, buffer: &mut String) -> Result<(), Error> {
578            let result;
579            unsafe { result = f_getcwd(buffer.as_mut_ptr().cast(), buffer.capacity() as u32); }
580            if result == FRESULT_FR_OK {
581                return Ok(())
582            } else {
583                return Err(Error::try_from(result).unwrap())
584            }
585        }
586
587        /// Get number of free clusters on the drive.
588        pub fn getfree(&self, path: &str) -> Result<u32, Error> {
589            let result;
590            let mut num_clusters = 0;
591            let mut fs_ptr: *mut FATFS = ptr::null_mut();
592            unsafe { result = f_getfree(path.as_ptr().cast(), ptr::addr_of_mut!(num_clusters), ptr::addr_of_mut!(fs_ptr)); }
593            if result == FRESULT_FR_OK {
594                return Ok(num_clusters)
595            } else {
596                return Err(Error::try_from(result).unwrap())
597            }
598        }
599
600        /// Get the volume label.
601        /// The supplied String buffer must have sufficient capacity to read the entire label.
602        pub fn getlabel(&self, path: &str, label: &mut String) -> Result<u32, Error> {
603            let result;
604            let mut vsn = 0;
605            if label.capacity() < 34 { //From FATFS documentation, this is the max length required for this parameter.
606                return Err(Error::InvalidParameter)
607            }
608            unsafe { result = f_getlabel(path.as_ptr().cast(), label.as_mut_ptr().cast(), ptr::addr_of_mut!(vsn)); }
609            if result == FRESULT_FR_OK {
610                return Ok(vsn)
611            } else {
612                return Err(Error::try_from(result).unwrap())
613            }
614        }
615
616        /// Set the volume label.
617        pub fn setlabel(&self, label: &str) -> Result<(), Error> {
618            let result;
619            unsafe { result = f_setlabel(label.as_ptr().cast()); }
620            if result == FRESULT_FR_OK {
621                return Ok(())
622            } else {
623                return Err(Error::try_from(result).unwrap())
624            }
625        }
626        
627        /// Allocate a contiguous block to the given file.
628        pub fn expand(&self, file: &mut File, size: u32) ->Result<(), Error> {
629            let result;
630            unsafe { result = f_expand(ptr::addr_of_mut!(*file), size, 1); }
631            if result == FRESULT_FR_OK {
632                return Ok(())
633            } else {
634                return Err(Error::try_from(result).unwrap())
635            }
636        }
637
638        /// Mount the drive.
639        pub fn mount(&mut self) -> Result<(), Error> {
640            self.fs = FATFS::default();
641            let file_path = "";
642            let result;
643            unsafe { result = f_mount(ptr::addr_of_mut!(self.fs), file_path.as_ptr().cast(), 1); }
644            if result == FRESULT_FR_OK {
645                return Ok(())
646            } else {
647                return Err(Error::try_from(result).unwrap())
648            }
649        }
650
651        /// Format the drive according to the supplied options.
652        pub fn mkfs(&self, path: &str, format: FormatOptions, copies: u8, alignment: u32, au_size: u32, root_entries: u32) -> Result<(), Error> {
653            let result;
654            let mut work: [u8; FF_MAX_SS as usize] = [0; FF_MAX_SS as usize];
655            let parameters = MKFS_PARM {
656                fmt: format.as_u8(),
657                n_fat: copies,
658                align: alignment,
659                n_root: root_entries,
660                au_size: au_size,
661            };
662            unsafe { result = f_mkfs(path.as_ptr().cast(), ptr::addr_of!(parameters), work.as_mut_ptr().cast(), work.len() as u32); }
663            if result == FRESULT_FR_OK {
664                return Ok(())
665            } else {
666                return Err(Error::try_from(result).unwrap())
667            }
668        }
669
670        /// Set the code page.
671        pub fn setcp(&self, code_page: u16) -> Result<(), Error> {
672            let result;
673            unsafe { result = f_setcp(code_page); }
674            if result == FRESULT_FR_OK {
675                return Ok(())
676            } else {
677                return Err(Error::try_from(result).unwrap())
678            }
679        }
680
681        /// Write a character to the file.
682        pub fn putc(&self, file: &mut File, char: u8) -> Result<i32, Error> {
683            let result;
684            unsafe { result = f_putc(char as TCHAR, ptr::addr_of_mut!(*file)); }
685            if result >= 0 {
686                return Ok(result)
687            } else {
688                return Err(Error::Denied)
689            }
690        }
691
692        /// Write a string to the file.
693        pub fn puts(&self, file: &mut File, string: &str) -> Result<i32, Error> {
694            let result;
695            unsafe { result = f_puts(string.as_ptr().cast(), ptr::addr_of_mut!(*file)); }
696            if result >= 0 {
697                return Ok(result)
698            } else {
699                return Err(Error::Denied)
700            }
701        }
702
703        /// Get a string from the file.
704        /// The capacity of the supplied String buffer determines the maximum length of data read.
705        pub fn gets(&self, file: &mut File, buffer: &mut String) -> Result<(), Error> {
706            let result;
707            unsafe { result = f_gets(buffer.as_mut_ptr().cast(), buffer.capacity() as i32, ptr::addr_of_mut!(*file)); }
708            if result != ptr::null_mut() {
709                return Ok(())
710            } else {
711                return Err(Error::Denied)
712            }
713        }
714
715        /// Unmount the drive at the supplied path.
716        pub fn unmount(&self, path: &str) -> Result<(), Error> {
717            let result;
718            unsafe { result = f_mount(ptr::null_mut(), path.as_ptr().cast(), 0); }
719            if result == FRESULT_FR_OK {
720                return Ok(())
721            } else {
722                return Err(Error::try_from(result).unwrap())
723            }
724        }
725    }
726
727}
728