inky_frame/fs/volume/
file.rs

1// Permission is hereby granted, free of charge, to any person obtaining a copy
2// of this software and associated documentation files (the "Software"), to deal
3// in the Software without restriction, including without limitation the rights
4// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5// copies of the Software, and to permit persons to whom the Software is
6// furnished to do so, subject to the following conditions:
7//
8// The above copyright notice and this permission notice shall be included in
9// all copies or substantial portions of the Software.
10//
11// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17// SOFTWARE.
18//
19
20#![no_implicit_prelude]
21
22extern crate core;
23extern crate rpsp;
24
25use core::cmp::PartialEq;
26use core::convert::{AsRef, Into, TryInto};
27use core::marker::PhantomData;
28use core::mem::{drop, transmute};
29use core::ops::{Deref, Drop};
30use core::option::Option::{None, Some};
31use core::result::Result::{self, Err, Ok};
32use core::{cmp, matches, mem};
33
34use rpsp::ignore_error;
35use rpsp::io::{Read, Seek, SeekFrom, Write};
36use rpsp::time::{Time, Weekday};
37
38use crate::fs::state::{Safe, Unsafe};
39use crate::fs::{Block, BlockCache, BlockDevice, BlockPtr, Cache, Cluster, DIR_SIZE, DeviceError, DirectoryIndex, Error, FatVersion, LongName, LongNamePtr, ShortName, Storage, Volume, le_u16, le_u32, to_le_u16, to_le_u32};
40
41const FILE_MAX_SIZE: u32 = 0xFFFFFFFFu32;
42
43pub enum Mode {}
44
45pub struct DirEntry {
46    name:     ShortName,
47    lfn:      u8,
48    size:     u32,
49    attrs:    u8,
50    block:    u32,
51    offset:   u32,
52    created:  Time,
53    cluster:  Cluster,
54    modified: Time,
55}
56pub struct DirEntryFull {
57    lfn:   LongNamePtr,
58    sum:   u8,
59    entry: DirEntry,
60}
61pub struct Reader<'a, B: BlockDevice> {
62    f:   File<'a, B, Unsafe>,
63    bp:  u32,
64    buf: BlockPtr,
65}
66pub struct Directory<'a, B: BlockDevice> {
67    vol: &'a Volume<'a, B>,
68    dir: DirEntry,
69}
70pub struct File<'a, B: BlockDevice, S: FileSync = Safe> {
71    vol:   &'a Volume<'a, B>,
72    pos:   u32,
73    file:  DirEntry,
74    last:  u32,
75    mode:  u8,
76    short: u32,
77    _p:    PhantomData<*const S>,
78}
79
80pub trait FileSync {
81    fn cache() -> BlockPtr;
82}
83
84impl Mode {
85    pub const READ: u8 = 0x0u8;
86    pub const WRITE: u8 = 0x1u8;
87    pub const CREATE: u8 = 0x2u8;
88    pub const APPEND: u8 = 0x4u8;
89    pub const TRUNCATE: u8 = 0x8u8;
90
91    #[inline(always)]
92    pub(super) fn is_create(m: u8) -> bool {
93        m & Mode::CREATE != 0 && (m & Mode::WRITE != 0 || m & Mode::APPEND != 0)
94    }
95    #[inline(always)]
96    pub(super) fn is_mode_valid(m: u8) -> bool {
97        if (m >> 4) > 0 || m & (Mode::TRUNCATE | Mode::APPEND) == 0x12 || m == Mode::CREATE {
98            false
99        } else {
100            true
101        }
102    }
103}
104impl DirEntry {
105    #[inline]
106    pub(super) fn new_root() -> DirEntry {
107        DirEntry {
108            lfn:      0,
109            name:     ShortName::empty(),
110            size:     0u32,
111            block:    0u32,
112            attrs:    0x10u8,
113            offset:   0u32,
114            created:  Time::empty(),
115            cluster:  None,
116            modified: Time::empty(),
117        }
118    }
119    #[inline]
120    pub(super) fn new(attrs: u8, lfn: u8) -> DirEntry {
121        DirEntry {
122            lfn,
123            attrs,
124            name: ShortName::empty(),
125            size: 0u32,
126            block: 0u32,
127            offset: 0u32,
128            created: Time::empty(),
129            cluster: Some(0),
130            modified: Time::empty(),
131        }
132    }
133    #[inline]
134    pub(super) fn new_self(parent: &DirEntry, block: u32) -> DirEntry {
135        DirEntry {
136            block,
137            lfn: 0u8,
138            name: ShortName::SELF,
139            size: 0u32,
140            attrs: 0x10u8,
141            offset: 0u32,
142            created: Time::empty(),
143            cluster: parent.cluster,
144            modified: Time::empty(),
145        }
146    }
147    #[inline]
148    pub(super) fn new_parent(cluster: Cluster, block: u32) -> DirEntry {
149        DirEntry {
150            block,
151            cluster,
152            lfn: 0u8,
153            name: ShortName::PARENT,
154            size: 0u32,
155            attrs: 0x10u8,
156            offset: 0x20u32,
157            created: Time::empty(),
158            modified: Time::empty(),
159        }
160    }
161
162    #[inline(always)]
163    pub fn size(&self) -> u32 {
164        self.size
165    }
166    #[inline(always)]
167    pub fn name(&self) -> &str {
168        self.name.as_str()
169    }
170    #[inline(always)]
171    pub fn offset(&self) -> u32 {
172        self.offset
173    }
174    #[inline(always)]
175    pub fn is_file(&self) -> bool {
176        !self.is_directory()
177    }
178    #[inline(always)]
179    pub fn created(&self) -> &Time {
180        &self.created
181    }
182    #[inline(always)]
183    pub fn attributes(&self) -> u8 {
184        self.attrs
185    }
186    #[inline(always)]
187    pub fn modified(&self) -> &Time {
188        &self.modified
189    }
190    #[inline(always)]
191    pub fn cluster(&self) -> Cluster {
192        self.cluster
193    }
194    #[inline(always)]
195    pub fn is_directory(&self) -> bool {
196        self.attrs & 0x10 == 0x10
197    }
198    #[inline(always)]
199    pub fn filename(&self) -> &ShortName {
200        &self.name
201    }
202    #[inline(always)]
203    pub fn set_created(&mut self, t: Time) {
204        self.created = t
205    }
206    #[inline(always)]
207    pub fn set_modified(&mut self, t: Time) {
208        self.modified = t
209    }
210    #[inline(always)]
211    pub fn set_attributes(&mut self, a: u8) {
212        self.attrs = a
213    }
214    #[inline]
215    pub fn into_dir<'a, B: BlockDevice>(self, vol: &'a Volume<B>) -> Result<Directory<'a, B>, DeviceError> {
216        if !self.is_directory() {
217            return Err(DeviceError::NotADirectory);
218        }
219        Ok(Directory::new(self, vol))
220    }
221    #[inline]
222    pub fn into_file<'a, B: BlockDevice>(self, vol: &'a Volume<B>, mode: u8) -> Result<File<'a, B>, DeviceError> {
223        if !Mode::is_mode_valid(mode) {
224            return Err(DeviceError::InvalidOptions);
225        }
226        if !self.is_file() {
227            return Err(DeviceError::NotAFile);
228        }
229        Ok(File::new(self, mode, vol))
230    }
231
232    #[inline(always)]
233    pub(super) fn fill(&mut self, v: &[u8]) {
234        self.name.fill(v);
235    }
236    #[inline(always)]
237    pub(super) fn cluster_abs(&self) -> u32 {
238        self.cluster.unwrap_or(0xFFFFFFFC)
239    }
240    #[inline]
241    pub(super) fn is_root_or_parent(&self) -> bool {
242        match self.cluster {
243            Some(v) if v == 0xFFFFFFFC => return true,
244            None => return true,
245            _ => (),
246        }
247        self.name.0.is_empty() || self.name.is_self() || self.name.is_parent()
248    }
249    #[inline(always)]
250    pub(super) fn write_prep(&mut self, block: u32, offset: usize) {
251        self.block = block;
252        self.offset = offset as u32;
253    }
254    pub(super) fn write_entry(&self, f: &FatVersion, b: &mut [u8]) {
255        b[0..ShortName::SIZE].copy_from_slice(&self.name.as_bytes());
256        b[11] = self.attrs;
257        b[12] = 0;
258        b[13] = 0;
259        time_write(&self.created, &mut b[14..]);
260        let c = self.cluster_abs();
261        match f {
262            FatVersion::Fat16(_) => (b[20], b[21]) = (0, 0),
263            FatVersion::Fat32(_) => to_le_u16(((c >> 16) & 0xFFFF) as u16, &mut b[20..]),
264        }
265        time_write(&self.modified, &mut b[22..]);
266        to_le_u16((c & 0xFFFF) as u16, &mut b[26..]);
267        to_le_u32(self.size, &mut b[28..])
268    }
269    pub(super) fn write_lfn_entry(&self, lfn: &LongName, pos: u8, s: u8, b: &mut [u8]) {
270        let (n, c) = (lfn.len(), self.name.checksum());
271        b[0..DIR_SIZE].fill(0);
272        b[0] = if pos == 0 { 0x40 } else { 0 } | (s - pos) as u8;
273        b[0xB] = 0xF;
274        b[0xD] = c;
275        let (s, mut p) = ((s - 1 - pos) as usize * 0xD, 0);
276        for v in s..cmp::min(s + 0xD, n) {
277            b[LongName::pos_to_lfn(p)] = lfn.0[v];
278            p += 1;
279        }
280        // Add NUL padding char.
281        if p < 0xC {
282            b[LongName::pos_to_lfn(p + 1)] = 0;
283            p += 1;
284        }
285        // Fill remaining with 0xFF
286        if p < 0xC {
287            for x in LongName::pos_to_lfn(p)..DIR_SIZE {
288                match x {
289                    0 | 0xB | 0xC | 0xD | 0x1A | 0x1B => continue,
290                    _ => (),
291                }
292                b[x] = 0xFF
293            }
294        }
295    }
296    pub(super) fn delete(&self, vol: &Volume<impl BlockDevice>, scratch: &mut Block) -> Result<(), DeviceError> {
297        vol.dev.read_single(scratch, self.block)?;
298        if self.offset as usize > Block::SIZE {
299            return Err(DeviceError::BadData);
300        }
301        scratch[self.offset as usize] = 0xE5;
302        if self.lfn == 0 {
303            return vol.dev.write_single(scratch, self.block);
304        }
305        // NOTE(sf): We try to remove the long filename entries, but we're not
306        //            gonna go back further than a whole block for simplicity.
307        let n = self.lfn as u32 * DIR_SIZE as u32;
308        if n > self.offset {
309            return vol.dev.write_single(scratch, self.block);
310        }
311        let mut i = self.offset.saturating_sub(n) as usize;
312        while i < self.offset as usize {
313            scratch[i..i + DIR_SIZE].fill(0);
314            i += DIR_SIZE;
315        }
316        vol.dev.write_single(scratch, self.block)?;
317        if let Some(v) = self.cluster {
318            vol.man.cluster_truncate(vol.dev, scratch, v)?;
319        }
320        Ok(())
321    }
322    #[inline(always)]
323    pub(super) fn allocate(&mut self, vol: &Volume<impl BlockDevice>, scratch: &mut Block) -> Result<(), DeviceError> {
324        self.cluster = Some(vol.man.cluster_allocate(vol.dev, scratch, None, false)?);
325        Ok(())
326    }
327    #[inline]
328    pub(super) fn sync(&self, dev: &Storage<impl BlockDevice>, scratch: &mut Block, f: &FatVersion) -> Result<(), DeviceError> {
329        dev.read_single(scratch, self.block)?;
330        if self.offset as usize > Block::SIZE {
331            return Err(DeviceError::BadData);
332        }
333        self.write_entry(f, &mut scratch[self.offset as usize..]);
334        dev.write_single(scratch, self.block)
335    }
336}
337impl DirEntryFull {
338    #[inline]
339    pub(super) fn new() -> DirEntryFull {
340        DirEntryFull {
341            lfn:   Cache::lfn(),
342            sum:   0u8,
343            entry: DirEntry::new(0, 0),
344        }
345    }
346
347    #[inline]
348    pub fn name(&self) -> &str {
349        if self.entry.lfn == 0 || self.lfn.is_empty() {
350            self.entry.name.as_str()
351        } else {
352            self.lfn.as_str()
353        }
354    }
355    #[inline(always)]
356    pub fn longname(&self) -> &LongName {
357        &self.lfn
358    }
359    #[inline(always)]
360    pub fn is_name(&self, v: &str) -> bool {
361        self.eq(v)
362    }
363
364    #[inline]
365    pub(super) fn reset(&mut self) {
366        self.lfn.reset();
367        self.sum = 0;
368        self.entry.lfn = 0;
369    }
370    #[inline(always)]
371    pub(super) fn fill(&mut self, b: &[u8]) {
372        self.sum = self.lfn.fill_lfn(b)
373    }
374    #[inline]
375    pub(super) fn entry(&mut self) -> DirEntry {
376        let mut n = DirEntry::new(0, 0);
377        mem::swap(&mut self.entry, &mut n);
378        n
379    }
380    pub(super) fn load(&mut self, f: &FatVersion, b: &[u8], block: u32, offset: u32) {
381        let v = if matches!(f, FatVersion::Fat32(_)) {
382            (le_u16(&b[20..]) as u32) << 16 | le_u16(&b[26..]) as u32
383        } else {
384            le_u16(&b[26..]) as u32
385        };
386        self.entry.lfn = self.lfn.lfn_size();
387        self.entry.name.fill_inner(b);
388        if self.sum != self.entry.name.checksum() {
389            self.lfn.reset();
390            self.entry.lfn = 0;
391        }
392        self.entry.block = block;
393        self.entry.offset = offset;
394        self.entry.size = le_u32(&b[28..]);
395        self.entry.attrs = b[11];
396        self.entry.created = time_read(le_u16(&b[16..]), le_u16(&b[14..]));
397        self.entry.cluster = if v == 0 && b[11] & 0x10 == 0x10 { None } else { Some(v) };
398        self.entry.modified = time_read(le_u16(&b[24..]), le_u16(&b[22..]));
399    }
400}
401impl<'a, B: BlockDevice> File<'a, B> {
402    #[inline(always)]
403    pub(super) fn new(file: DirEntry, mode: u8, vol: &'a Volume<'a, B>) -> File<'a, B, Safe> {
404        File {
405            last: file.cluster_abs(),
406            vol,
407            file,
408            mode,
409            pos: 0u32,
410            short: 0u32,
411            _p: PhantomData,
412        }
413    }
414}
415impl<'a, B: BlockDevice> Reader<'a, B> {
416    #[inline(always)]
417    pub fn cursor(&self) -> usize {
418        self.f.pos as usize
419    }
420    #[inline(always)]
421    pub fn available(&self) -> usize {
422        self.f.file.size.saturating_sub(self.f.pos) as usize
423    }
424    #[inline(always)]
425    pub fn volume(&self) -> &Volume<'a, B> {
426        self.f.vol
427    }
428    #[inline(always)]
429    pub fn into_file(self) -> File<'a, B, Unsafe> {
430        drop(self.buf);
431        self.f
432    }
433    /// Similar to 'File.read' but does NOT re-read nearby chunks inside the
434    /// same Block.
435    ///
436    /// This will hold the Cache lock until dropped.
437    pub fn read(&mut self, b: &mut [u8]) -> Result<usize, DeviceError> {
438        if b.is_empty() {
439            return Ok(0);
440        }
441        let (mut p, t) = (0, b.len());
442        let (d, mut c) = (&mut *self.buf, BlockCache::new());
443        while p < t && self.f.pos < self.f.file.size {
444            let (i, o, a) = match self.f.data(d, &mut c) {
445                Err(DeviceError::EndOfFile) => return Ok(p),
446                Err(e) => return Err(e),
447                Ok(v) => v,
448            };
449            if self.bp == 0 || self.bp != i {
450                // Only read if the block changed, to prevent double reads.
451                // Speedup is 200%!
452                self.f.vol.dev.read_single(d, i)?;
453                self.bp = i;
454            }
455            let n = cmp::min(cmp::min(a, t - p), self.f.available());
456            if n == 0 {
457                break;
458            }
459            b[p..p + n].copy_from_slice(&d[o..o + n]);
460            self.f.pos = self.f.pos.saturating_add(n as u32);
461            p = p.saturating_add(n);
462        }
463        Ok(p)
464    }
465}
466impl<'a, B: BlockDevice> Directory<'a, B> {
467    #[inline(always)]
468    pub(super) fn new(dir: DirEntry, vol: &'a Volume<'a, B>) -> Directory<'a, B> {
469        Directory { vol, dir }
470    }
471
472    #[inline(always)]
473    pub fn volume(&self) -> &Volume<'a, B> {
474        self.vol
475    }
476    #[inline]
477    pub fn delete(self, force: bool) -> Result<(), DeviceError> {
478        let mut b = Cache::block_a();
479        Directory::delete_inner(self.vol, &self.dir, &mut b, force)
480    }
481    #[inline(always)]
482    pub fn list(&self) -> Result<DirectoryIndex<'a, B>, DeviceError> {
483        // Safe as we're the entry and valid.
484        unsafe { self.vol.list_entry(Some(&self.dir)) }
485    }
486    #[inline]
487    pub fn file(&'a self, name: impl AsRef<str>, mode: u8) -> Result<File<'a, B>, DeviceError> {
488        // Safe as we're the entry and valid.
489        unsafe { self.vol.file_entry(name, Some(&self.dir), mode) }
490    }
491    #[inline]
492    pub fn dir(&'a self, name: impl AsRef<str>, create: bool) -> Result<Directory<'a, B>, DeviceError> {
493        // Safe as we're the entry and valid.
494        unsafe { self.vol.dir_entry(name, Some(&self.dir), create) }
495    }
496
497    #[inline(always)]
498    pub(super) fn entry(&self) -> &DirEntry {
499        &self.dir
500    }
501    #[inline(always)]
502    pub(super) fn cluster(&self) -> Cluster {
503        self.dir.cluster
504    }
505
506    fn delete_inner(vol: &'a Volume<'a, B>, dir: &DirEntry, scratch: &mut Block, force: bool) -> Result<(), DeviceError> {
507        // NOTE(sf): This works at deleting recursively for directories. However,
508        //           it sometimes can be buggy just due to how the FAT FS is
509        //           structured.
510        for e in unsafe { vol.list_entry(Some(dir))? } {
511            if !force {
512                return Err(DeviceError::NonEmptyDirectory);
513            }
514            let v = e?;
515            if v.is_root_or_parent() {
516                // NOTE(sf): Usually this is the start of the Block, so we
517                //           should be good to stop here to prevent deleting
518                //           other things.
519                break;
520            }
521            if v.is_directory() {
522                Directory::delete_inner(vol, &v, scratch, force)?;
523            } else {
524                v.delete(vol, scratch)?;
525            }
526        }
527        dir.delete(vol, scratch)?;
528        vol.man.cluster_truncate(vol.dev, scratch, dir.cluster_abs())
529    }
530}
531impl<'a, B: BlockDevice> File<'a, B, Safe> {
532    #[inline(always)]
533    /// Remove the locking requirement for file Read/Writes.
534    ///
535    /// Use only if sure that reads/writes will not happen on the same or
536    /// multiple files by different cores at the same.
537    pub unsafe fn into_unsafe(self) -> File<'a, B, Unsafe> {
538        unsafe { transmute(self) }
539    }
540    #[inline(always)]
541    /// Transform the File into a high-speed Reader with better caching
542    /// and locking mechanisms.
543    pub unsafe fn into_reader(self) -> Result<Reader<'a, B>, DeviceError> {
544        if !self.is_readable() {
545            return Err(DeviceError::NotReadable);
546        }
547        Ok(Reader {
548            f:   unsafe { self.into_unsafe() },
549            bp:  0u32,
550            buf: Cache::block_a(),
551        })
552    }
553}
554impl<'a, B: BlockDevice> File<'a, B, Unsafe> {
555    #[inline(always)]
556    pub fn into_safe(self) -> File<'a, B, Safe> {
557        unsafe { transmute(self) }
558    }
559}
560impl<'a, B: BlockDevice, S: FileSync> File<'a, B, S> {
561    #[inline(always)]
562    pub fn cursor(&self) -> usize {
563        self.pos as usize
564    }
565    #[inline(always)]
566    pub fn is_dirty(&self) -> bool {
567        self.mode & 0x80 != 0
568    }
569    #[inline(always)]
570    pub fn available(&self) -> usize {
571        self.file.size.saturating_sub(self.pos) as usize
572    }
573    #[inline(always)]
574    pub fn is_readable(&self) -> bool {
575        self.mode >> 4 == 0 || self.mode & Mode::READ != 0
576    }
577    #[inline(always)]
578    pub fn is_writeable(&self) -> bool {
579        self.mode & Mode::WRITE != 0
580    }
581    #[inline(always)]
582    pub fn is_allocated(&self) -> bool {
583        self.file.cluster.is_some_and(|v| v > 2)
584    }
585    #[inline(always)]
586    pub fn volume(&self) -> &Volume<'a, B> {
587        self.vol
588    }
589    #[inline]
590    pub fn delete(self) -> Result<(), DeviceError> {
591        let mut b = S::cache();
592        self.file.delete(self.vol, &mut b)
593    }
594    #[inline(always)]
595    pub fn close(mut self) -> Result<(), DeviceError> {
596        self.flush()
597    }
598    #[inline]
599    pub fn flush(&mut self) -> Result<(), DeviceError> {
600        if !self.is_dirty() {
601            return Ok(());
602        }
603        let mut b = S::cache();
604        self.vol.man.sync(self.vol.dev, &mut b)?;
605        self.file.sync(self.vol.dev, &mut b, &self.vol.man.ver)
606    }
607    pub fn write(&mut self, b: &[u8]) -> Result<usize, DeviceError> {
608        if !self.is_writeable() {
609            return Err(DeviceError::NotWritable);
610        }
611        if b.is_empty() {
612            return Ok(0);
613        }
614        self.mode |= 0x80;
615        let mut d = S::cache();
616        if !self.is_allocated() {
617            self.file.cluster = Some(self.vol.man.cluster_allocate(self.vol.dev, &mut d, None, false)?);
618            d.clear();
619        }
620        let c = self.file.cluster.ok_or(DeviceError::Write)?;
621        if self.last < c {
622            (self.last, self.short) = (c, 0);
623        }
624        let t = cmp::min(b.len(), (FILE_MAX_SIZE - self.pos) as usize);
625        let (mut c, mut p) = (BlockCache::new(), 0);
626        while p < t {
627            let (i, o, a) = match self.data(&mut d, &mut c) {
628                Ok(v) => v,
629                Err(DeviceError::EndOfFile) => {
630                    self.vol
631                        .man
632                        .cluster_allocate(self.vol.dev, &mut d, self.cluster(), false)
633                        .map_err(|_| DeviceError::NoSpace)?;
634                    self.data(&mut d, &mut c).map_err(|_| DeviceError::Write)?
635                },
636                Err(e) => return Err(e),
637            };
638            let n = cmp::min(a, t.saturating_sub(p));
639            if n == 0 {
640                break;
641            }
642            if o != 0 {
643                self.vol.dev.read_single(&mut d, i)?;
644            }
645            d[o..o + n].copy_from_slice(&b[p..p + n]);
646            self.vol.dev.write_single(&d, i)?;
647            self.file.size = self.file.size.saturating_add(n as u32);
648            self.pos = self.pos.saturating_add(n as u32);
649            p = p.saturating_add(n);
650        }
651        self.file.attrs |= 0x20;
652        Ok(p)
653    }
654    /// Does not save the file and keeps the current Cluster intact.
655    /// To fully truncate a File entry, it must be opened with 'Mode::TRUNCATE'.
656    pub fn truncate(&mut self, pos: usize) -> Result<(), DeviceError> {
657        let i = pos.try_into().map_err(|_| DeviceError::Overflow)?;
658        if i > self.file.size {
659            return Err(DeviceError::InvalidIndex);
660        }
661        self.file.size = i;
662        if self.file.size > self.pos {
663            self.pos = i;
664        }
665        Ok(())
666    }
667    pub fn read(&mut self, b: &mut [u8]) -> Result<usize, DeviceError> {
668        if !self.is_readable() {
669            return Err(DeviceError::NotReadable);
670        }
671        if b.is_empty() {
672            return Ok(0);
673        }
674        let (mut p, t) = (0, b.len());
675        let (mut d, mut c) = (S::cache(), BlockCache::new());
676        while p < t && self.pos < self.file.size {
677            let (i, o, a) = match self.data(&mut d, &mut c) {
678                Err(DeviceError::EndOfFile) => return Ok(p),
679                Err(e) => return Err(e),
680                Ok(v) => v,
681            };
682            self.vol.dev.read_single(&mut d, i)?;
683            let n = cmp::min(cmp::min(a, t - p), self.available());
684            if n == 0 {
685                break;
686            }
687            b[p..p + n].copy_from_slice(&d[o..o + n]);
688            self.pos = self.pos.saturating_add(n as u32);
689            p = p.saturating_add(n);
690        }
691        Ok(p)
692    }
693
694    #[inline(always)]
695    pub(super) fn zero(&mut self) {
696        self.file.size = 0
697    }
698    #[inline(always)]
699    pub(super) fn seek_to_end(&mut self) {
700        self.pos = self.file.size
701    }
702
703    fn data(&mut self, scratch: &mut Block, cache: &mut BlockCache) -> Result<(u32, usize, usize), DeviceError> {
704        if self.pos < self.short {
705            (self.short, self.last) = (0, self.cluster_abs());
706        }
707        let c = self.vol.man.blocks.bytes_per_cluster();
708        let n = self.pos.saturating_sub(self.short);
709        cache.clear();
710        for _ in 0..(n / c) {
711            self.last = self
712                .vol
713                .man
714                .cluster_next(self.vol.dev, scratch, cache, self.last)?
715                .ok_or_else(|| DeviceError::EndOfFile)?;
716            self.short += c;
717        }
718        let i = self.vol.man.block_pos_at(self.last) + (self.pos.saturating_sub(self.short) / Block::SIZE as u32);
719        let o = self.pos as usize % Block::SIZE;
720        Ok((i, o, Block::SIZE - o))
721    }
722}
723
724impl<B: BlockDevice, S: FileSync> Drop for File<'_, B, S> {
725    #[inline(always)]
726    fn drop(&mut self) {
727        ignore_error!(self.flush());
728    }
729}
730impl<B: BlockDevice, S: FileSync> Deref for File<'_, B, S> {
731    type Target = DirEntry;
732
733    #[inline(always)]
734    fn deref(&self) -> &DirEntry {
735        &self.file
736    }
737}
738impl<B: BlockDevice, S: FileSync> Seek<DeviceError> for File<'_, B, S> {
739    fn seek(&mut self, s: SeekFrom) -> Result<u64, Error> {
740        let r = match s {
741            SeekFrom::End(v) => {
742                if v > 0 {
743                    return Err(Error::InvalidIndex);
744                }
745                self.pos
746                    .saturating_sub(v.unsigned_abs().try_into().map_err(|_| DeviceError::Overflow)?)
747            },
748            SeekFrom::Start(v) => v.try_into().map_err(|_| DeviceError::Overflow)?,
749            SeekFrom::Current(v) if v > 0 => self.pos.saturating_add(v.try_into().map_err(|_| DeviceError::Overflow)?),
750            SeekFrom::Current(v) => self
751                .pos
752                .saturating_sub(v.unsigned_abs().try_into().map_err(|_| DeviceError::Overflow)?),
753        };
754        if r > self.file.size {
755            return Err(Error::InvalidIndex);
756        }
757        self.pos = r;
758        Ok(self.pos as u64)
759    }
760}
761impl<B: BlockDevice, S: FileSync> Read<DeviceError> for File<'_, B, S> {
762    #[inline(always)]
763    fn read(&mut self, b: &mut [u8]) -> Result<usize, Error> {
764        Ok(self.read(b)?)
765    }
766}
767impl<B: BlockDevice, S: FileSync> Write<DeviceError> for File<'_, B, S> {
768    #[inline(always)]
769    fn flush(&mut self) -> Result<(), Error> {
770        Ok(self.flush()?)
771    }
772    #[inline(always)]
773    fn write(&mut self, b: &[u8]) -> Result<usize, Error> {
774        Ok(self.write(b)?)
775    }
776}
777
778impl<B: BlockDevice> Deref for Reader<'_, B> {
779    type Target = DirEntry;
780
781    #[inline(always)]
782    fn deref(&self) -> &DirEntry {
783        &self.f.file
784    }
785}
786impl<B: BlockDevice> Seek<DeviceError> for Reader<'_, B> {
787    fn seek(&mut self, s: SeekFrom) -> Result<u64, Error> {
788        self.f.seek(s)
789    }
790}
791impl<B: BlockDevice> Read<DeviceError> for Reader<'_, B> {
792    #[inline(always)]
793    fn read(&mut self, b: &mut [u8]) -> Result<usize, Error> {
794        Ok(self.read(b)?)
795    }
796}
797
798impl PartialEq<str> for DirEntry {
799    #[inline(always)]
800    fn eq(&self, other: &str) -> bool {
801        self.eq(other.as_bytes())
802    }
803}
804impl PartialEq<[u8]> for DirEntry {
805    #[inline(always)]
806    fn eq(&self, other: &[u8]) -> bool {
807        self.name.eq(other)
808    }
809}
810
811impl Deref for DirEntryFull {
812    type Target = DirEntry;
813
814    #[inline(always)]
815    fn deref(&self) -> &DirEntry {
816        &self.entry
817    }
818}
819impl PartialEq<str> for DirEntryFull {
820    #[inline(always)]
821    fn eq(&self, other: &str) -> bool {
822        self.eq(other.as_bytes())
823    }
824}
825impl PartialEq<[u8]> for DirEntryFull {
826    #[inline]
827    fn eq(&self, other: &[u8]) -> bool {
828        if self.entry.lfn == 0 { self.entry.name.eq(other) } else { self.lfn.eq(other) }
829    }
830}
831
832impl FileSync for Safe {
833    #[inline(always)]
834    fn cache() -> BlockPtr {
835        Cache::block_a()
836    }
837}
838impl FileSync for Unsafe {
839    #[inline(always)]
840    fn cache() -> BlockPtr {
841        unsafe { Cache::block_a_nolock() }
842    }
843}
844
845impl<B: BlockDevice> Deref for Directory<'_, B> {
846    type Target = DirEntry;
847
848    #[inline(always)]
849    fn deref(&self) -> &DirEntry {
850        &self.dir
851    }
852}
853
854#[inline]
855fn time_read(a: u16, b: u16) -> Time {
856    Time {
857        year:    ((a >> 0x9) + 0xA) + 0x7B2u16,
858        month:   (((a >> 0x5) & 0xF) as u8 + 1).into(),
859        day:     (a & 0x1F) as u8,
860        hours:   ((b >> 0xB) & 0x1F) as u8,
861        mins:    ((b >> 0x5) & 0x3F) as u8,
862        secs:    ((b << 0x1) & 0x3F) as u8,
863        weekday: Weekday::None,
864    }
865}
866#[inline]
867fn time_write(t: &Time, b: &mut [u8]) {
868    to_le_u16(
869        (((t.hours as u16) << 11) & 0xF800) | (((t.mins as u16) << 5) & 0x7E0) | (((t.secs as u16) / 2) & 0x1F),
870        b,
871    );
872    to_le_u16(
873        (((t.year.saturating_sub(0x7B2)).saturating_sub(10) << 9) & 0xFE00) | (((t.month as u16 + 1) << 5) & 0x1E0) | ((t.day as u16 + 1) & 0x1F),
874        &mut b[2..],
875    )
876}
877
878pub mod state {
879    pub struct Safe;
880    pub struct Unsafe;
881}