1use super::*;
2
3use core::{cmp, num, ops};
4
5#[cfg(not(feature = "std"))]
6use alloc::{borrow::ToOwned, string::String, vec::Vec};
7use time::{Date, PrimitiveDateTime};
8
9use crate::utils::{self, bincode::BINCODE_CONFIG};
10use crate::{FSError, FSResult, InternalFSError};
11
12use embedded_io::*;
13
14#[derive(Debug)]
15pub(crate) struct FileProps {
16 pub(crate) entry: Properties,
17 pub(crate) offset: FileSize,
21 pub(crate) current_cluster: ClusterIndex,
22}
23
24#[derive(Debug)]
30pub struct ROFile<'a, S>
31where
32 S: Read + Seek,
33{
34 pub(crate) fs: &'a FileSystem<S>,
35 pub(crate) props: FileProps,
36}
37
38impl<S> ops::Deref for ROFile<'_, S>
39where
40 S: Read + Seek,
41{
42 type Target = Properties;
43
44 fn deref(&self) -> &Self::Target {
45 &self.props.entry
46 }
47}
48
49impl<S> ops::DerefMut for ROFile<'_, S>
50where
51 S: Read + Seek,
52{
53 fn deref_mut(&mut self) -> &mut Self::Target {
54 &mut self.props.entry
55 }
56}
57
58impl<'a, S> ROFile<'a, S>
60where
61 S: Read + Seek,
62{
63 pub(crate) fn from_props(props: FileProps, fs: &'a FileSystem<S>) -> Self {
64 Self { fs, props }
65 }
66}
67
68impl<S> ROFile<'_, S>
70where
71 S: Read + Seek,
72{
73 #[inline]
74 fn next_cluster(&mut self) -> Result<(), <Self as ErrorType>::Error> {
76 self.props.current_cluster = self.get_next_cluster()?.unwrap();
78
79 Ok(())
80 }
81
82 #[inline]
83 fn get_next_cluster(&mut self) -> Result<Option<ClusterIndex>, <Self as ErrorType>::Error> {
85 self.fs.get_next_cluster(self.props.current_cluster)
86 }
87
88 fn last_cluster_in_chain(&mut self) -> Result<ClusterIndex, <Self as ErrorType>::Error> {
90 let mut current_cluster = self.props.current_cluster;
92
93 loop {
94 match self.fs.read_nth_FAT_entry(current_cluster)? {
95 FATEntry::Allocated(next_cluster) => current_cluster = next_cluster,
96 FATEntry::Eof => break,
97 _ => unreachable!(),
98 }
99 }
100
101 Ok(current_cluster)
102 }
103
104 pub(crate) fn cluster_chain_is_healthy(&mut self) -> Result<bool, S::Error> {
106 let mut current_cluster = self.data_cluster;
107 let mut cluster_count = 0;
108
109 loop {
110 cluster_count += 1;
111
112 if cluster_count * self.fs.cluster_size() >= self.file_size {
113 break;
114 }
115
116 match self.fs.read_nth_FAT_entry(current_cluster)? {
117 FATEntry::Allocated(next_cluster) => current_cluster = next_cluster,
118 _ => return Ok(false),
119 };
120 }
121
122 Ok(true)
123 }
124
125 fn offset_from_seekfrom(&self, seekfrom: SeekFrom) -> u64 {
126 match seekfrom {
127 SeekFrom::Start(offset) => offset,
128 SeekFrom::Current(offset) => {
129 let offset = i64::from(self.props.offset) + offset;
130 offset.try_into().unwrap_or(u64::MIN)
131 }
132 SeekFrom::End(offset) => {
133 let offset = i64::from(self.file_size) + offset;
134 offset.try_into().unwrap_or(u64::MIN)
135 }
136 }
137 }
138}
139
140impl<S> ErrorType for ROFile<'_, S>
141where
142 S: Read + Seek,
143{
144 type Error = S::Error;
145}
146
147impl<S> Read for ROFile<'_, S>
148where
149 S: Read + Seek,
150{
151 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
152 let mut bytes_read = 0;
153 let read_cap = cmp::min(
155 buf.len(),
156 usize::try_from(self.file_size - self.props.offset).unwrap_or(usize::MAX),
158 );
159
160 'outer: loop {
161 let sector_init_offset =
162 self.props.offset % self.fs.cluster_size() / u32::from(self.fs.sector_size());
163 let first_sector_of_cluster = self
164 .fs
165 .data_cluster_to_partition_sector(self.props.current_cluster)
166 + sector_init_offset;
167 let last_sector_of_cluster = first_sector_of_cluster
168 + SectorCount::from(self.fs.sectors_per_cluster())
169 - sector_init_offset
170 - 1;
171 log::debug!(
172 "Reading cluster {} from sectors {} to {}",
173 self.props.current_cluster,
174 first_sector_of_cluster,
175 last_sector_of_cluster
176 );
177
178 for sector in first_sector_of_cluster..=last_sector_of_cluster {
179 self.fs.load_nth_sector(sector)?;
180
181 let start_index = usize::try_from(self.props.offset % u32::from(self.fs.sector_size()))
182 .expect("sector_size's upper limit is 2^16, within Rust's usize (Rust support 16, 32 and 64-bit archs)");
183 let bytes_to_read = cmp::min(
184 read_cap - bytes_read,
185 usize::from(self.fs.sector_size()) - start_index,
186 );
187 log::debug!(
188 "Gonna read {bytes_to_read} bytes from sector {sector} starting at byte {start_index}"
189 );
190
191 buf[bytes_read..bytes_read + bytes_to_read].copy_from_slice(
192 &self.fs.sector_buffer.borrow()[start_index..start_index + bytes_to_read],
193 );
194
195 bytes_read += bytes_to_read;
196 self.props.offset += FileSize::try_from(bytes_to_read).unwrap();
197
198 if bytes_read >= read_cap {
200 if self.props.offset % self.fs.cluster_size() == 0
203 && self.props.offset < self.file_size
204 {
205 self.next_cluster()?;
206 }
207
208 break 'outer;
209 }
210 }
211
212 self.next_cluster()?;
213 }
214
215 Ok(bytes_read)
216 }
217}
218
219impl<S> Seek for ROFile<'_, S>
220where
221 S: Read + Seek,
222{
223 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
224 let mut offset = self.offset_from_seekfrom(pos);
225
226 offset = cmp::min(offset, self.file_size.into());
229
230 let offset = FileSize::try_from(offset)
231 .expect("file_size is u32, so offset must be able to fit in a u32 too");
232
233 log::trace!(
234 "Previous cursor offset is {}, new cursor offset is {}",
235 self.props.offset,
236 offset
237 );
238
239 use cmp::Ordering;
240 match offset.cmp(&self.props.offset) {
241 Ordering::Less => {
242 self.props.offset = 0;
246 self.props.current_cluster = self.data_cluster;
247 self.seek(SeekFrom::Start(offset.into()))?;
248 }
249 Ordering::Equal => (),
250 Ordering::Greater => {
251 for _ in self.props.offset / self.fs.cluster_size()..offset / self.fs.cluster_size()
252 {
253 self.next_cluster()?;
254 }
255 self.props.offset = offset;
256 }
257 }
258
259 Ok(self.props.offset.into())
260 }
261}
262
263#[derive(Debug)]
270pub struct RWFile<'a, S>
271where
272 S: Read + Write + Seek,
273{
274 pub(crate) ro_file: ROFile<'a, S>,
275 pub(crate) entry_modified: bool,
277}
278
279impl<'a, S> From<ROFile<'a, S>> for RWFile<'a, S>
280where
281 S: Read + Write + Seek,
282{
283 fn from(value: ROFile<'a, S>) -> Self {
284 Self {
285 ro_file: value,
286 entry_modified: false,
287 }
288 }
289}
290
291impl<'a, S> ops::Deref for RWFile<'a, S>
292where
293 S: Read + Write + Seek,
294{
295 type Target = ROFile<'a, S>;
296
297 fn deref(&self) -> &Self::Target {
298 &self.ro_file
299 }
300}
301
302impl<S> ops::DerefMut for RWFile<'_, S>
303where
304 S: Read + Write + Seek,
305{
306 fn deref_mut(&mut self) -> &mut Self::Target {
307 &mut self.ro_file
308 }
309}
310
311impl<'a, S> RWFile<'a, S>
313where
314 S: Read + Write + Seek,
315{
316 pub(crate) fn from_props(props: FileProps, fs: &'a FileSystem<S>) -> Self {
317 ROFile::from_props(props, fs).into()
318 }
319}
320
321impl<S> RWFile<'_, S>
323where
324 S: Read + Write + Seek,
325{
326 pub fn set_accessed(&mut self, accessed: Date) {
328 self.accessed = Some(accessed);
329
330 self.entry_modified = true;
331 }
332
333 pub fn set_created(&mut self, created: PrimitiveDateTime) {
335 self.created = Some(created);
336
337 self.entry_modified = true;
338 }
339
340 pub fn set_modified(&mut self, modified: PrimitiveDateTime) {
342 self.modified = modified;
343
344 self.entry_modified = true;
345 }
346
347 pub fn truncate(&mut self) -> Result<(), <Self as ErrorType>::Error> {
349 let size = self.props.offset;
350
351 if size.next_multiple_of(self.fs.props.cluster_size) >= self.file_size {
353 if size < self.file_size {
354 self.file_size = size;
355 }
356
357 return Ok(());
358 }
359
360 let previous_offset = cmp::min(self.props.offset, size);
362
363 self.seek(SeekFrom::Start(size.into()))?;
365
366 let previous_size = self.file_size;
368 self.file_size = size;
369
370 let mut next_cluster_option = self.get_next_cluster()?;
371
372 self.ro_file
374 .fs
375 .write_nth_FAT_entry(self.ro_file.props.current_cluster, FATEntry::Eof)?;
376
377 while let Some(next_cluster) = next_cluster_option {
379 next_cluster_option = self.fs.get_next_cluster(next_cluster)?;
380
381 self.fs.write_nth_FAT_entry(next_cluster, FATEntry::Free)?;
382 }
383
384 self.seek(SeekFrom::Start(previous_offset.into()))?;
386
387 log::debug!(
388 "Successfully truncated file {} from {} to {} bytes",
389 self.path,
390 previous_size,
391 self.file_size
392 );
393
394 self.entry_modified = true;
395
396 Ok(())
397 }
398
399 pub fn remove(mut self) -> Result<(), <Self as ErrorType>::Error> {
401 self.ro_file
403 .fs
404 .remove_entry_chain(&self.ro_file.props.entry.chain)?;
405
406 self.rewind()?;
410
411 let current_cluster = self.ro_file.props.current_cluster;
412 self.ro_file.fs.free_cluster_chain(current_cluster)?;
413
414 self.entry_modified = false;
418
419 Ok(())
420 }
421}
422
423impl<S> RWFile<'_, S>
425where
426 S: Read + Write + Seek,
427{
428 fn sync_entry(&mut self) -> FSResult<(), S::Error> {
429 if self.entry_modified {
430 let direntry = FATDirEntry::from(MinProperties::from(self.props.entry.clone()));
431 let mut bytes = [0; DIRENTRY_SIZE];
432 bincode::encode_into_slice(direntry, &mut bytes, BINCODE_CONFIG)
433 .map_err(utils::bincode::map_err_enc)?;
434
435 let chain_start = self.props.entry.chain.location;
436 let file_name = self
437 .path()
438 .file_name()
439 .expect("This file name should be valid")
440 .to_owned();
441 let direntry_location = match num::NonZero::new(
443 EntryCount::from(calc_entries_needed(file_name, &self.fs.options.codepage)) - 1,
444 ) {
445 Some(nonzero) => {
446 chain_start
447 .nth_entry(self.fs, nonzero)?
448 .ok_or(FSError::InternalFSError(
449 InternalFSError::MalformedEntryChain,
450 ))?
451 }
452 None => chain_start,
453 };
454
455 direntry_location.set_bytes(self.fs, bytes)?;
456
457 self.entry_modified = false;
458 }
459
460 Ok(())
461 }
462
463 #[inline]
464 fn _set_accessed(&mut self) {
465 if self.fs.options.update_file_fields {
466 let now = self.fs.options.clock.now();
467
468 if let Some(accessed) = &mut self.accessed {
469 *accessed = now.date();
470 }
471
472 self.entry_modified = true;
473 }
474 }
475
476 #[inline]
477 fn _set_modified(&mut self) {
478 if self.fs.options.update_file_fields {
479 let now = self.fs.options.clock.now();
480
481 if let Some(accessed) = &mut self.accessed {
482 *accessed = now.date();
483 }
484 self.modified = now;
485
486 self.entry_modified = true;
487 }
488 }
489}
490
491#[derive(Debug)]
492#[non_exhaustive] pub enum RWFileError<I>
495where
496 I: Error,
497{
498 StorageFull,
500 IOError(I),
502}
503
504impl<I> Error for RWFileError<I>
505where
506 I: Error,
507{
508 #[inline]
509 fn kind(&self) -> ErrorKind {
510 match self {
511 Self::StorageFull => ErrorKind::OutOfMemory,
513 Self::IOError(err) => err.kind(),
514 }
515 }
516}
517
518impl<I> From<I> for RWFileError<I>
519where
520 I: Error,
521{
522 #[inline]
523 fn from(value: I) -> Self {
524 Self::IOError(value)
525 }
526}
527
528impl<S> ErrorType for RWFile<'_, S>
529where
530 S: Read + Write + Seek,
531{
532 type Error = RWFileError<S::Error>;
533}
534
535impl<S> Read for RWFile<'_, S>
536where
537 S: Read + Write + Seek,
538{
539 #[inline]
540 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
541 let res = self.ro_file.read(buf);
542
543 if res.is_ok() {
544 self._set_accessed()
545 };
546
547 res.map_err(|e| e.into())
548 }
549
550 #[inline]
551 fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
552 let res = self.ro_file.read_exact(buf);
553
554 if res.is_ok() {
555 self._set_accessed()
556 };
557
558 res.map_err(|e| match e {
559 ReadExactError::UnexpectedEof => ReadExactError::UnexpectedEof,
560 ReadExactError::Other(err) => ReadExactError::Other(err.into()),
561 })
562 }
563}
564
565impl<S> Write for RWFile<'_, S>
566where
567 S: Read + Write + Seek,
568{
569 fn write(&mut self, mut buf: &[u8]) -> Result<usize, Self::Error> {
570 let cur_offset = self.props.offset;
571
572 if u64::try_from(buf.len()).unwrap_or(u64::MAX) > FileSize::MAX.into() {
575 log::warn!("a file can be up to 2^32 bytes long, can't have a file larger than that");
576
577 buf = &buf[..FileSize::MAX as usize];
578 };
579
580 self.seek(SeekFrom::Start(u64::from(cur_offset) + buf.len() as u64))?;
582 self.seek(SeekFrom::Start(cur_offset.into()))?;
584
585 let mut bytes_written = 0;
586
587 'outer: loop {
588 log::trace!(
589 "writing file data to cluster: {}",
590 self.props.current_cluster
591 );
592
593 let sector_init_offset =
594 self.props.offset % self.fs.cluster_size() / u32::from(self.fs.sector_size());
595 let first_sector_of_cluster = self
596 .fs
597 .data_cluster_to_partition_sector(self.props.current_cluster)
598 + sector_init_offset;
599 let last_sector_of_cluster = first_sector_of_cluster
600 + SectorCount::from(self.fs.sectors_per_cluster())
601 - sector_init_offset
602 - 1;
603 for sector in first_sector_of_cluster..=last_sector_of_cluster {
604 self.fs.load_nth_sector(sector)?;
605
606 let start_index = usize::try_from(self.props.offset % u32::from(self.fs.sector_size()))
607 .expect("sector_size's upper limit is 2^16, within Rust's usize (Rust support 16, 32 and 64-bit archs)");
608
609 let bytes_to_write = cmp::min(
610 buf.len() - bytes_written,
611 usize::from(self.fs.sector_size()) - start_index,
612 );
613
614 self.fs.sector_buffer.borrow_mut()[start_index..start_index + bytes_to_write]
615 .copy_from_slice(&buf[bytes_written..bytes_written + bytes_to_write]);
616 self.fs.set_modified();
617
618 bytes_written += bytes_to_write;
619 self.props.offset += FileSize::try_from(bytes_to_write).unwrap();
620
621 if bytes_written >= buf.len() {
623 if self.props.offset % self.fs.cluster_size() == 0 {
626 self.next_cluster()?;
627 }
628
629 break 'outer;
630 }
631 }
632
633 self.next_cluster()?;
634 }
635
636 self._set_modified();
638
639 Ok(bytes_written)
640 }
641
642 fn flush(&mut self) -> Result<(), Self::Error> {
644 Ok(())
645 }
646}
647
648impl<S> Seek for RWFile<'_, S>
649where
650 S: Read + Write + Seek,
651{
652 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
653 let offset = self.offset_from_seekfrom(pos);
654
655 let offset = match FileSize::try_from(offset) {
656 Ok(offset) => offset,
657 Err(_) => {
658 log::warn!(
659 "a file can be up to 2^32 bytes long, can't have a file larger than that"
660 );
661
662 FileSize::MAX
663 }
664 };
665
666 if offset > self.file_size.next_multiple_of(self.fs.cluster_size()) {
668 let bytes_allocated = if self.file_size == 0 {
669 self.fs.props.cluster_size
671 } else {
672 self.file_size.next_multiple_of(self.fs.cluster_size())
673 };
674 let clusters_to_allocate = (offset - bytes_allocated).div_ceil(self.fs.cluster_size());
675 log::debug!("Seeking beyond EOF, allocating {clusters_to_allocate} more clusters");
676
677 let last_cluster_in_chain = self.last_cluster_in_chain()?;
678
679 match self.fs.allocate_clusters(
682 num::NonZero::new(clusters_to_allocate).expect("This is greater than 1"),
683 Some(last_cluster_in_chain),
684 ) {
685 Ok(_) => (),
686 Err(_) => return Err(RWFileError::StorageFull),
687 };
688
689 self.file_size = offset;
690 log::debug!(
691 "New file size after reallocation is {} bytes",
692 self.file_size
693 );
694 }
695
696 self._set_accessed();
697 self.entry_modified = true;
698
699 self.ro_file.seek(pos).map_err(|e| e.into())
700 }
701}
702
703impl<S> Drop for RWFile<'_, S>
704where
705 S: Read + Write + Seek,
706{
707 fn drop(&mut self) {
708 let _ = self.sync_entry();
710 }
711}