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::ops::{Deref, Drop};
28use core::option::Option::{None, Some};
29use core::result::Result::{self, Err, Ok};
30use core::{cmp, matches, mem};
31
32use rpsp::ignore_error;
33use rpsp::io::{Read, Seek, SeekFrom, Write};
34use rpsp::time::{Time, Weekday};
35
36use crate::fs::volume::LongNamePtr;
37use crate::fs::{Block, BlockCache, BlockDevice, Cluster, DIR_SIZE, DeviceError, DirectoryIndex, Error, FatVersion, LongName, ShortName, Storage, Volume, le_u16, le_u32, to_le_u16, to_le_u32};
38
39const FILE_MAX_SIZE: u32 = 0xFFFFFFFFu32;
40
41pub enum Mode {}
42
43pub struct DirEntry {
44    name:     ShortName,
45    lfn:      u8,
46    size:     u32,
47    attrs:    u8,
48    block:    u32,
49    offset:   u32,
50    created:  Time,
51    cluster:  Cluster,
52    modified: Time,
53}
54pub struct DirEntryFull {
55    lfn:   LongNamePtr,
56    sum:   u8,
57    entry: DirEntry,
58}
59
60pub struct File<'a, B: BlockDevice> {
61    vol:   &'a Volume<'a, B>,
62    pos:   u32,
63    file:  DirEntry,
64    last:  u32,
65    mode:  u8,
66    short: u32,
67}
68pub struct Directory<'a, B: BlockDevice> {
69    vol: &'a Volume<'a, B>,
70    dir: DirEntry,
71}
72
73impl Mode {
74    pub const READ: u8 = 0x0u8;
75    pub const WRITE: u8 = 0x1u8;
76    pub const CREATE: u8 = 0x2u8;
77    pub const APPEND: u8 = 0x4u8;
78    pub const TRUNCATE: u8 = 0x8u8;
79
80    #[inline(always)]
81    pub(super) fn is_create(m: u8) -> bool {
82        m & Mode::CREATE != 0 && (m & Mode::WRITE != 0 || m & Mode::APPEND != 0)
83    }
84    #[inline(always)]
85    pub(super) fn is_mode_valid(m: u8) -> bool {
86        if (m >> 4) > 0 || m & (Mode::TRUNCATE | Mode::APPEND) == 0x12 || m == Mode::CREATE {
87            false
88        } else {
89            true
90        }
91    }
92}
93impl DirEntry {
94    #[inline]
95    pub(super) fn new_root() -> DirEntry {
96        DirEntry {
97            lfn:      0,
98            name:     ShortName::empty(),
99            size:     0u32,
100            block:    0u32,
101            attrs:    0x10u8,
102            offset:   0u32,
103            created:  Time::empty(),
104            cluster:  None,
105            modified: Time::empty(),
106        }
107    }
108    #[inline]
109    pub(super) fn new(attrs: u8, lfn: u8) -> DirEntry {
110        DirEntry {
111            lfn,
112            attrs,
113            name: ShortName::empty(),
114            size: 0u32,
115            block: 0u32,
116            offset: 0u32,
117            created: Time::empty(),
118            cluster: Some(0),
119            modified: Time::empty(),
120        }
121    }
122    #[inline]
123    pub(super) fn new_self(parent: &DirEntry, block: u32) -> DirEntry {
124        DirEntry {
125            block,
126            lfn: 0u8,
127            name: ShortName::SELF,
128            size: 0u32,
129            attrs: 0x10u8,
130            offset: 0u32,
131            created: Time::empty(),
132            cluster: parent.cluster,
133            modified: Time::empty(),
134        }
135    }
136    #[inline]
137    pub(super) fn new_parent(cluster: Cluster, block: u32) -> DirEntry {
138        DirEntry {
139            block,
140            cluster,
141            lfn: 0u8,
142            name: ShortName::PARENT,
143            size: 0u32,
144            attrs: 0x10u8,
145            offset: 0x20u32,
146            created: Time::empty(),
147            modified: Time::empty(),
148        }
149    }
150
151    #[inline(always)]
152    pub fn size(&self) -> u32 {
153        self.size
154    }
155    #[inline(always)]
156    pub fn name(&self) -> &str {
157        self.name.as_str()
158    }
159    #[inline(always)]
160    pub fn offset(&self) -> u32 {
161        self.offset
162    }
163    #[inline(always)]
164    pub fn is_file(&self) -> bool {
165        !self.is_directory()
166    }
167    #[inline(always)]
168    pub fn created(&self) -> &Time {
169        &self.created
170    }
171    #[inline(always)]
172    pub fn attributes(&self) -> u8 {
173        self.attrs
174    }
175    #[inline(always)]
176    pub fn modified(&self) -> &Time {
177        &self.modified
178    }
179    #[inline(always)]
180    pub fn cluster(&self) -> Cluster {
181        self.cluster
182    }
183    #[inline(always)]
184    pub fn is_directory(&self) -> bool {
185        self.attrs & 0x10 == 0x10
186    }
187    #[inline(always)]
188    pub fn filename(&self) -> &ShortName {
189        &self.name
190    }
191    #[inline(always)]
192    pub fn set_created(&mut self, t: Time) {
193        self.created = t
194    }
195    #[inline(always)]
196    pub fn set_modified(&mut self, t: Time) {
197        self.modified = t
198    }
199    #[inline(always)]
200    pub fn set_attributes(&mut self, a: u8) {
201        self.attrs = a
202    }
203    #[inline]
204    pub fn into_dir<'a, B: BlockDevice>(self, vol: &'a Volume<B>) -> Result<Directory<'a, B>, DeviceError> {
205        if !self.is_directory() {
206            return Err(DeviceError::NotADirectory);
207        }
208        Ok(Directory::new(self, vol))
209    }
210    #[inline]
211    pub fn into_file<'a, B: BlockDevice>(self, vol: &'a Volume<B>, mode: u8) -> Result<File<'a, B>, DeviceError> {
212        if !Mode::is_mode_valid(mode) {
213            return Err(DeviceError::InvalidOptions);
214        }
215        if !self.is_file() {
216            return Err(DeviceError::NotAFile);
217        }
218        Ok(File::new(self, mode, vol))
219    }
220
221    #[inline(always)]
222    pub(super) fn fill(&mut self, v: &[u8]) {
223        self.name.fill(v);
224    }
225    #[inline(always)]
226    pub(super) fn cluster_abs(&self) -> u32 {
227        self.cluster.unwrap_or(0xFFFFFFFC)
228    }
229    #[inline]
230    pub(super) fn is_root_or_parent(&self) -> bool {
231        match self.cluster {
232            Some(v) if v == 0xFFFFFFFC => return true,
233            None => return true,
234            _ => (),
235        }
236        self.name.0.is_empty() || self.name.is_self() || self.name.is_parent()
237    }
238    #[inline(always)]
239    pub(super) fn write_prep(&mut self, block: u32, offset: usize) {
240        self.block = block;
241        self.offset = offset as u32;
242    }
243    pub(super) fn write_entry(&self, f: &FatVersion, b: &mut [u8]) {
244        b[0..ShortName::SIZE].copy_from_slice(&self.name.as_bytes());
245        b[11] = self.attrs;
246        b[12] = 0;
247        b[13] = 0;
248        time_write(&self.created, &mut b[14..]);
249        let c = self.cluster_abs();
250        match f {
251            FatVersion::Fat16(_) => (b[20], b[21]) = (0, 0),
252            FatVersion::Fat32(_) => to_le_u16(((c >> 16) & 0xFFFF) as u16, &mut b[20..]),
253        }
254        time_write(&self.modified, &mut b[22..]);
255        to_le_u16((c & 0xFFFF) as u16, &mut b[26..]);
256        to_le_u32(self.size, &mut b[28..])
257    }
258    pub(super) fn write_lfn_entry(&self, lfn: &LongName, pos: u8, s: u8, b: &mut [u8]) {
259        let (n, c) = (lfn.len(), self.name.checksum());
260        b[0..DIR_SIZE].fill(0);
261        b[0] = if pos == 0 { 0x40 } else { 0 } | (s - pos) as u8;
262        b[0xB] = 0xF;
263        b[0xD] = c;
264        let (s, mut p) = ((s - 1 - pos) as usize * 0xD, 0);
265        for v in s..cmp::min(s + 0xD, n) {
266            b[LongName::pos_to_lfn(p)] = lfn.0[v];
267            p += 1;
268        }
269        // Add NUL padding char.
270        if p < 0xC {
271            b[LongName::pos_to_lfn(p + 1)] = 0;
272            p += 1;
273        }
274        // Fill remaining with 0xFF
275        if p < 0xC {
276            for x in LongName::pos_to_lfn(p)..DIR_SIZE {
277                match x {
278                    0 | 0xB | 0xC | 0xD | 0x1A | 0x1B => continue,
279                    _ => (),
280                }
281                b[x] = 0xFF
282            }
283        }
284    }
285    pub(super) fn delete(&self, vol: &Volume<impl BlockDevice>, scratch: &mut Block) -> Result<(), DeviceError> {
286        vol.dev.read_single(scratch, self.block)?;
287        if self.offset as usize > Block::SIZE {
288            return Err(DeviceError::BadData);
289        }
290        scratch[self.offset as usize] = 0xE5;
291        if self.lfn == 0 {
292            return vol.dev.write_single(scratch, self.block);
293        }
294        // NOTE(sf): We try to remove the long filename entries, but we're not
295        //            gonna go back further than a whole block for simplicity.
296        let n = self.lfn as u32 * DIR_SIZE as u32;
297        if n > self.offset {
298            return vol.dev.write_single(scratch, self.block);
299        }
300        let mut i = self.offset.saturating_sub(n) as usize;
301        while i < self.offset as usize {
302            scratch[i..i + DIR_SIZE].fill(0);
303            i += DIR_SIZE;
304        }
305        vol.dev.write_single(scratch, self.block)?;
306        if let Some(v) = self.cluster {
307            vol.man.cluster_truncate(vol.dev, scratch, v)?;
308        }
309        Ok(())
310    }
311    #[inline(always)]
312    pub(super) fn allocate(&mut self, vol: &Volume<impl BlockDevice>, scratch: &mut Block) -> Result<(), DeviceError> {
313        self.cluster = Some(vol.man.cluster_allocate(vol.dev, scratch, None, false)?);
314        Ok(())
315    }
316    #[inline]
317    pub(super) fn sync(&self, dev: &Storage<impl BlockDevice>, scratch: &mut Block, f: &FatVersion) -> Result<(), DeviceError> {
318        dev.read_single(scratch, self.block)?;
319        if self.offset as usize > Block::SIZE {
320            return Err(DeviceError::BadData);
321        }
322        self.write_entry(f, &mut scratch[self.offset as usize..]);
323        dev.write_single(scratch, self.block)
324    }
325}
326impl DirEntryFull {
327    #[inline]
328    pub(super) fn new() -> DirEntryFull {
329        DirEntryFull {
330            lfn:   LongNamePtr::new(),
331            sum:   0u8,
332            entry: DirEntry::new(0, 0),
333        }
334    }
335
336    #[inline]
337    pub fn name(&self) -> &str {
338        if self.entry.lfn == 0 || self.lfn.is_empty() {
339            self.entry.name.as_str()
340        } else {
341            self.lfn.as_str()
342        }
343    }
344    #[inline(always)]
345    pub fn longname(&self) -> &LongName {
346        &self.lfn
347    }
348    #[inline(always)]
349    pub fn is_name(&self, v: &str) -> bool {
350        self.eq(v)
351    }
352
353    #[inline]
354    pub(super) fn reset(&mut self) {
355        self.lfn.reset();
356        self.sum = 0;
357        self.entry.lfn = 0;
358    }
359    #[inline(always)]
360    pub(super) fn fill(&mut self, b: &[u8]) {
361        self.sum = self.lfn.fill_lfn(b)
362    }
363    #[inline]
364    pub(super) fn entry(&mut self) -> DirEntry {
365        let mut n = DirEntry::new(0, 0);
366        mem::swap(&mut self.entry, &mut n);
367        n
368    }
369    pub(super) fn load(&mut self, f: &FatVersion, b: &[u8], block: u32, offset: u32) {
370        let v = if matches!(f, FatVersion::Fat32(_)) {
371            (le_u16(&b[20..]) as u32) << 16 | le_u16(&b[26..]) as u32
372        } else {
373            le_u16(&b[26..]) as u32
374        };
375        self.entry.lfn = self.lfn.lfn_size();
376        self.entry.name.fill_inner(b);
377        if self.sum != self.entry.name.checksum() {
378            self.lfn.reset();
379            self.entry.lfn = 0;
380        }
381        self.entry.block = block;
382        self.entry.offset = offset;
383        self.entry.size = le_u32(&b[28..]);
384        self.entry.attrs = b[11];
385        self.entry.created = time_read(le_u16(&b[16..]), le_u16(&b[14..]));
386        self.entry.cluster = if v == 0 && b[11] & 0x10 == 0x10 { None } else { Some(v) };
387        self.entry.modified = time_read(le_u16(&b[24..]), le_u16(&b[22..]));
388    }
389}
390impl<'a, B: BlockDevice> File<'a, B> {
391    #[inline]
392    pub(super) fn new(file: DirEntry, mode: u8, vol: &'a Volume<'a, B>) -> File<'a, B> {
393        File {
394            last: file.cluster_abs(),
395            vol,
396            file,
397            mode,
398            pos: 0u32,
399            short: 0u32,
400        }
401    }
402
403    #[inline(always)]
404    pub fn cursor(&self) -> usize {
405        self.pos as usize
406    }
407    #[inline(always)]
408    pub fn is_dirty(&self) -> bool {
409        self.mode & 0x80 != 0
410    }
411    #[inline(always)]
412    pub fn available(&self) -> usize {
413        self.file.size.saturating_sub(self.pos) as usize
414    }
415    #[inline(always)]
416    pub fn is_readable(&self) -> bool {
417        self.mode >> 4 == 0 || self.mode & Mode::READ != 0
418    }
419    #[inline(always)]
420    pub fn is_writeable(&self) -> bool {
421        self.mode & Mode::WRITE != 0
422    }
423    #[inline(always)]
424    pub fn is_allocated(&self) -> bool {
425        self.file.cluster.is_some_and(|v| v > 2)
426    }
427    #[inline(always)]
428    pub fn volume(&self) -> &Volume<'a, B> {
429        self.vol
430    }
431    #[inline]
432    pub fn delete(self) -> Result<(), DeviceError> {
433        let mut b = Block::new();
434        self.file.delete(self.vol, &mut b)
435    }
436    #[inline(always)]
437    pub fn close(mut self) -> Result<(), DeviceError> {
438        self.flush()
439    }
440    #[inline]
441    pub fn flush(&mut self) -> Result<(), DeviceError> {
442        if !self.is_dirty() {
443            return Ok(());
444        }
445        let mut b = Block::new();
446        self.vol.man.sync(self.vol.dev, &mut b)?;
447        self.file.sync(self.vol.dev, &mut b, &self.vol.man.ver)
448    }
449    pub fn write(&mut self, b: &[u8]) -> Result<usize, DeviceError> {
450        if !self.is_writeable() {
451            return Err(DeviceError::NotWritable);
452        }
453        if b.is_empty() {
454            return Ok(0);
455        }
456        self.mode |= 0x80;
457        let mut d = Block::new();
458        if !self.is_allocated() {
459            self.file.cluster = Some(self.vol.man.cluster_allocate(self.vol.dev, &mut d, None, false)?);
460            d.clear();
461        }
462        let c = self.file.cluster.ok_or(DeviceError::WriteError)?;
463        if self.last < c {
464            self.last = c;
465            self.short = 0;
466        }
467        let t = cmp::min(b.len(), (FILE_MAX_SIZE - self.pos) as usize);
468        let (mut c, mut p) = (BlockCache::new(), 0);
469        while p < t {
470            let (i, o, a) = match self.data(&mut d, &mut c) {
471                Ok(v) => v,
472                Err(DeviceError::EndOfFile) => {
473                    self.vol
474                        .man
475                        .cluster_allocate(self.vol.dev, &mut d, self.cluster(), false)
476                        .map_err(|_| DeviceError::NoSpace)?;
477                    self.data(&mut d, &mut c).map_err(|_| DeviceError::WriteError)?
478                },
479                Err(e) => return Err(e),
480            };
481            let n = cmp::min(a, t - p);
482            if n == 0 {
483                break;
484            }
485            if o != 0 {
486                self.vol.dev.read_single(&mut d, i)?;
487            }
488            d[o..o + n].copy_from_slice(&b[p..p + n]);
489            self.vol.dev.write_single(&d, i)?;
490            self.file.size = self.file.size.saturating_add(n as u32);
491            self.pos = self.pos.saturating_add(n as u32);
492            p += n;
493        }
494        self.file.attrs |= 0x20;
495        Ok(p)
496    }
497    /// Does not save the file and keeps the current Cluster intact.
498    /// To fully truncate a File entry, it must be opened with 'Mode::TRUNCATE'.
499    pub fn truncate(&mut self, pos: usize) -> Result<(), DeviceError> {
500        let i = pos.try_into().map_err(|_| DeviceError::Overflow)?;
501        if i > self.file.size {
502            return Err(DeviceError::InvalidIndex);
503        }
504        self.file.size = i;
505        if self.file.size > self.pos {
506            self.pos = i;
507        }
508        Ok(())
509    }
510    pub fn read(&mut self, b: &mut [u8]) -> Result<usize, DeviceError> {
511        if !self.is_readable() {
512            return Err(DeviceError::NotReadable);
513        }
514        if b.is_empty() {
515            return Ok(0);
516        }
517        let (mut p, t) = (0, b.len());
518        let (mut d, mut c) = (Block::new(), BlockCache::new());
519        while p < t && self.pos < self.file.size {
520            let (i, o, a) = match self.data(&mut d, &mut c) {
521                Err(DeviceError::EndOfFile) => return Ok(p),
522                Err(e) => return Err(e),
523                Ok(v) => v,
524            };
525            self.vol.dev.read_single(&mut d, i)?;
526            let n = cmp::min(cmp::min(a, t - p), self.available());
527            if n == 0 {
528                break;
529            }
530            b[p..p + n].copy_from_slice(&d[o..o + n]);
531            self.pos = self.pos.saturating_add(n as u32);
532            p += n;
533        }
534        Ok(p)
535    }
536
537    #[inline(always)]
538    pub(super) fn zero(&mut self) {
539        self.file.size = 0
540    }
541    #[inline(always)]
542    pub(super) fn seek_to_end(&mut self) {
543        self.pos = self.file.size
544    }
545
546    fn data(&mut self, scratch: &mut Block, cache: &mut BlockCache) -> Result<(u32, usize, usize), DeviceError> {
547        if self.pos < self.short {
548            (self.short, self.last) = (0, self.cluster_abs());
549        }
550        let c = self.vol.man.blocks.bytes_per_cluster();
551        let n = self.pos.saturating_sub(self.short);
552        cache.clear();
553        for _ in 0..(n / c) {
554            self.last = self
555                .vol
556                .man
557                .cluster_next(self.vol.dev, scratch, cache, self.last)?
558                .ok_or_else(|| DeviceError::EndOfFile)?;
559            self.short += c;
560        }
561        let i = self.vol.man.block_pos_at(self.last) + (self.pos.saturating_sub(self.short) / Block::SIZE as u32);
562        let o = self.pos as usize % Block::SIZE;
563        Ok((i, o, Block::SIZE - o))
564    }
565}
566impl<'a, B: BlockDevice> Directory<'a, B> {
567    #[inline(always)]
568    pub(super) fn new(dir: DirEntry, vol: &'a Volume<'a, B>) -> Directory<'a, B> {
569        Directory { vol, dir }
570    }
571
572    #[inline(always)]
573    pub fn volume(&self) -> &Volume<'a, B> {
574        self.vol
575    }
576    #[inline]
577    pub fn delete(self, force: bool) -> Result<(), DeviceError> {
578        let mut b = Block::new();
579        Directory::delete_inner(self.vol, &self.dir, &mut b, force)
580    }
581    #[inline(always)]
582    pub fn list(&self) -> Result<DirectoryIndex<'a, B>, DeviceError> {
583        // Safe as we're the entry and valid.
584        unsafe { self.vol.list_entry(Some(&self.dir)) }
585    }
586    #[inline]
587    pub fn file(&'a self, name: impl AsRef<str>, mode: u8) -> Result<File<'a, B>, DeviceError> {
588        // Safe as we're the entry and valid.
589        unsafe { self.vol.file_entry(name, Some(&self.dir), mode) }
590    }
591    #[inline]
592    pub fn dir(&'a self, name: impl AsRef<str>, create: bool) -> Result<Directory<'a, B>, DeviceError> {
593        // Safe as we're the entry and valid.
594        unsafe { self.vol.dir_entry(name, Some(&self.dir), create) }
595    }
596
597    #[inline(always)]
598    pub(super) fn entry(&self) -> &DirEntry {
599        &self.dir
600    }
601    #[inline(always)]
602    pub(super) fn cluster(&self) -> Cluster {
603        self.dir.cluster
604    }
605
606    fn delete_inner(vol: &'a Volume<'a, B>, dir: &DirEntry, scratch: &mut Block, force: bool) -> Result<(), DeviceError> {
607        // NOTE(sf): This works at deleting recursively for directories. However,
608        //           it sometimes can be buggy just due to how the FAT FS is
609        //           structured.
610        for e in unsafe { vol.list_entry(Some(dir))? } {
611            if !force {
612                return Err(DeviceError::NonEmptyDirectory);
613            }
614            let v = e?;
615            if v.is_root_or_parent() {
616                // NOTE(sf): Usually this is the start of the Block, so we
617                //           should be good to stop here to prevent deleting
618                //           other things.
619                break;
620            }
621            if v.is_directory() {
622                Directory::delete_inner(vol, &v, scratch, force)?;
623            } else {
624                v.delete(vol, scratch)?;
625            }
626        }
627        dir.delete(vol, scratch)?;
628        vol.man.cluster_truncate(vol.dev, scratch, dir.cluster_abs())
629    }
630}
631
632impl<B: BlockDevice> Drop for File<'_, B> {
633    #[inline(always)]
634    fn drop(&mut self) {
635        ignore_error!(self.flush());
636    }
637}
638impl<B: BlockDevice> Deref for File<'_, B> {
639    type Target = DirEntry;
640
641    #[inline(always)]
642    fn deref(&self) -> &DirEntry {
643        &self.file
644    }
645}
646impl<B: BlockDevice> Seek<DeviceError> for File<'_, B> {
647    fn seek(&mut self, s: SeekFrom) -> Result<u64, Error> {
648        let r = match s {
649            SeekFrom::End(v) => {
650                if v > 0 {
651                    return Err(Error::InvalidIndex);
652                }
653                self.pos
654                    .saturating_sub(v.unsigned_abs().try_into().map_err(|_| DeviceError::Overflow)?)
655            },
656            SeekFrom::Start(v) => v.try_into().map_err(|_| DeviceError::Overflow)?,
657            SeekFrom::Current(v) if v > 0 => self.pos.saturating_add(v.try_into().map_err(|_| DeviceError::Overflow)?),
658            SeekFrom::Current(v) => self
659                .pos
660                .saturating_sub(v.unsigned_abs().try_into().map_err(|_| DeviceError::Overflow)?),
661        };
662        if r > self.file.size {
663            return Err(Error::InvalidIndex);
664        }
665        self.pos = r;
666        Ok(self.pos as u64)
667    }
668}
669impl<B: BlockDevice> Read<DeviceError> for File<'_, B> {
670    #[inline(always)]
671    fn read(&mut self, b: &mut [u8]) -> Result<usize, Error> {
672        Ok(self.read(b)?)
673    }
674}
675impl<B: BlockDevice> Write<DeviceError> for File<'_, B> {
676    #[inline(always)]
677    fn flush(&mut self) -> Result<(), Error> {
678        Ok(self.flush()?)
679    }
680    #[inline(always)]
681    fn write(&mut self, b: &[u8]) -> Result<usize, Error> {
682        Ok(self.write(b)?)
683    }
684}
685
686impl PartialEq<str> for DirEntry {
687    #[inline(always)]
688    fn eq(&self, other: &str) -> bool {
689        self.eq(other.as_bytes())
690    }
691}
692impl PartialEq<[u8]> for DirEntry {
693    #[inline(always)]
694    fn eq(&self, other: &[u8]) -> bool {
695        self.name.eq(other)
696    }
697}
698
699impl Deref for DirEntryFull {
700    type Target = DirEntry;
701
702    #[inline(always)]
703    fn deref(&self) -> &DirEntry {
704        &self.entry
705    }
706}
707impl PartialEq<str> for DirEntryFull {
708    #[inline(always)]
709    fn eq(&self, other: &str) -> bool {
710        self.eq(other.as_bytes())
711    }
712}
713impl PartialEq<[u8]> for DirEntryFull {
714    #[inline]
715    fn eq(&self, other: &[u8]) -> bool {
716        if self.entry.lfn == 0 { self.entry.name.eq(other) } else { self.lfn.eq(other) }
717    }
718}
719
720impl<B: BlockDevice> Deref for Directory<'_, B> {
721    type Target = DirEntry;
722
723    #[inline(always)]
724    fn deref(&self) -> &DirEntry {
725        &self.dir
726    }
727}
728
729#[inline]
730fn time_read(a: u16, b: u16) -> Time {
731    Time {
732        year:    ((a >> 0x9) + 0xA) + 0x7B2u16,
733        month:   (((a >> 0x5) & 0xF) as u8 + 1).into(),
734        day:     (a & 0x1F) as u8,
735        hours:   ((b >> 0xB) & 0x1F) as u8,
736        mins:    ((b >> 0x5) & 0x3F) as u8,
737        secs:    ((b << 0x1) & 0x3F) as u8,
738        weekday: Weekday::None,
739    }
740}
741#[inline]
742fn time_write(t: &Time, b: &mut [u8]) {
743    to_le_u16(
744        (((t.hours as u16) << 11) & 0xF800) | (((t.mins as u16) << 5) & 0x7E0) | (((t.secs as u16) / 2) & 0x1F),
745        b,
746    );
747    to_le_u16(
748        (((t.year.saturating_sub(0x7B2)).saturating_sub(10) << 9) & 0xFE00) | (((t.month as u16 + 1) << 5) & 0x1E0) | ((t.day as u16 + 1) & 0x1F),
749        &mut b[2..],
750    )
751}