Skip to main content

littlefs_rust/fs/
handles.rs

1use super::*;
2
3impl<'fs, D: BlockDevice + 'static> FileWriter<'fs, D> {
4    pub fn write(&mut self, data: &[u8]) -> Result<usize> {
5        if self.stream.is_some() {
6            self.write_streaming(data)?;
7            return Ok(data.len());
8        }
9
10        let end = self.pos.checked_add(data.len()).ok_or(Error::NoSpace)?;
11        if end > self.fs.info().file_max as usize {
12            return Err(Error::FileTooLarge);
13        }
14        if end > self.fs.fs.inline_threshold() && self.pos == self.data.len() {
15            self.start_streaming_from_buffer()?;
16            self.write_streaming(data)?;
17            return Ok(data.len());
18        }
19        if end > self.data.len() {
20            self.data.resize(end, 0);
21        }
22        self.data[self.pos..end].copy_from_slice(data);
23        self.pos = end;
24        if self.data.len() > self.fs.fs.inline_threshold() && self.pos == self.data.len() {
25            self.start_streaming_from_buffer()?;
26        }
27        Ok(data.len())
28    }
29
30    pub fn write_all(&mut self, data: &[u8]) -> Result<()> {
31        self.write(data)?;
32        Ok(())
33    }
34
35    pub fn seek(&mut self, pos: usize) -> Result<()> {
36        if let Some(stream) = &self.stream {
37            if pos != stream.len {
38                return Err(Error::Unsupported);
39            }
40        }
41        self.pos = pos;
42        Ok(())
43    }
44
45    pub fn close(self) -> Result<()> {
46        match self.stream {
47            Some(stream) => self.fs.finish_streaming_create_file(self.path, stream),
48            None => self.fs.create_file(&self.path, &self.data),
49        }
50    }
51
52    fn start_streaming_from_buffer(&mut self) -> Result<()> {
53        let buffered = core::mem::take(&mut self.data);
54        self.stream = Some(StreamingWrite {
55            allocator: self.fs.allocator.clone(),
56            blocks: Vec::new(),
57            current: None,
58            len: 0,
59            target: StreamingTarget::Create,
60        });
61        self.pos = 0;
62        self.write_streaming(&buffered)
63    }
64
65    fn write_streaming(&mut self, mut data: &[u8]) -> Result<()> {
66        let stream = self.stream.as_mut().ok_or(Error::Corrupt)?;
67        if self.pos != stream.len {
68            return Err(Error::Unsupported);
69        }
70        if stream.len.checked_add(data.len()).ok_or(Error::NoSpace)?
71            > self.fs.info().file_max as usize
72        {
73            return Err(Error::FileTooLarge);
74        }
75
76        while !data.is_empty() {
77            if stream.current.is_none() {
78                let index = stream.blocks.len();
79                let block = stream.allocator.alloc_block()?;
80                let mut bytes = alloc::vec![0xff; self.fs.info().block_size as usize];
81                let data_start = ctz_data_start(index)?;
82                if index > 0 {
83                    let skips = index.trailing_zeros() as usize + 1;
84                    for skip in 0..skips {
85                        let target_index =
86                            index.checked_sub(1usize << skip).ok_or(Error::Corrupt)?;
87                        let target = stream
88                            .blocks
89                            .get(target_index)
90                            .copied()
91                            .ok_or(Error::Corrupt)?;
92                        program_nor_bytes(&mut bytes, skip * 4, &target.to_le_bytes())?;
93                    }
94                }
95                stream.current = Some(StreamingBlock {
96                    block,
97                    bytes,
98                    off: data_start,
99                    mode: StreamingBlockMode::New,
100                });
101            }
102
103            let current = stream.current.as_mut().ok_or(Error::Corrupt)?;
104            let capacity = current.bytes.len().saturating_sub(current.off);
105            let n = core::cmp::min(capacity, data.len());
106            current.bytes[current.off..current.off + n].copy_from_slice(&data[..n]);
107            current.off += n;
108            stream.len = stream.len.checked_add(n).ok_or(Error::NoSpace)?;
109            self.pos = stream.len;
110            data = &data[n..];
111
112            if current.off == current.bytes.len() {
113                let current = stream.current.take().ok_or(Error::Corrupt)?;
114                self.fs.flush_streaming_block(stream, current)?;
115            }
116        }
117
118        Ok(())
119    }
120}
121
122impl FileOptions {
123    pub fn new() -> Self {
124        Self::default()
125    }
126
127    pub fn read(mut self, value: bool) -> Self {
128        self.read = value;
129        self
130    }
131
132    pub fn write(mut self, value: bool) -> Self {
133        self.write = value;
134        self
135    }
136
137    pub fn create(mut self, value: bool) -> Self {
138        self.create = value;
139        self
140    }
141
142    pub fn create_new(mut self, value: bool) -> Self {
143        self.create_new = value;
144        self
145    }
146
147    pub fn truncate(mut self, value: bool) -> Self {
148        self.truncate = value;
149        self
150    }
151
152    pub fn append(mut self, value: bool) -> Self {
153        self.append = value;
154        self
155    }
156}
157
158impl<'fs, D: BlockDevice + 'static> FileHandle<'fs, D> {
159    pub fn read(&mut self, out: &mut [u8]) -> Result<usize> {
160        if !self.readable {
161            return Err(Error::BadFileDescriptor);
162        }
163        if self.stream.is_some() {
164            return Err(Error::Unsupported);
165        }
166        if self.merge.is_some() {
167            return Err(Error::Unsupported);
168        }
169        if self.stream_read {
170            let source = self.stream_source.as_ref().ok_or(Error::Corrupt)?;
171            let n = self.fs.read_file_data_at(source, self.pos, out)?;
172            self.pos += n;
173            return Ok(n);
174        }
175        let available = self.data.len().saturating_sub(self.pos);
176        let n = core::cmp::min(out.len(), available);
177        out[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
178        self.pos += n;
179        Ok(n)
180    }
181
182    pub fn write(&mut self, data: &[u8]) -> Result<usize> {
183        if !self.writable {
184            return Err(Error::BadFileDescriptor);
185        }
186        if self.stream.is_some() {
187            self.write_streaming(data)?;
188            return Ok(data.len());
189        }
190        if self.merge.is_some() {
191            return self.write_merge_patch(data);
192        }
193        let end = self.pos.checked_add(data.len()).ok_or(Error::NoSpace)?;
194        if end > self.fs.info().file_max as usize {
195            return Err(Error::FileTooLarge);
196        }
197        if end > self.fs.fs.inline_threshold() && self.pos == self.data.len() {
198            self.start_streaming_from_buffer()?;
199            self.write_streaming(data)?;
200            return Ok(data.len());
201        }
202        if end > self.data.len() {
203            self.data.resize(end, 0);
204        }
205        self.data[self.pos..end].copy_from_slice(data);
206        self.pos = end;
207        self.len = self.data.len();
208        self.dirty = true;
209        if self.data.len() > self.fs.fs.inline_threshold() && self.pos == self.data.len() {
210            self.start_streaming_from_buffer()?;
211        }
212        Ok(data.len())
213    }
214
215    pub fn write_all(&mut self, data: &[u8]) -> Result<()> {
216        self.write(data)?;
217        Ok(())
218    }
219
220    pub fn seek(&mut self, pos: usize) -> Result<()> {
221        if let Some(stream) = &self.stream {
222            if pos != stream.len {
223                return Err(Error::Unsupported);
224            }
225        }
226        self.pos = pos;
227        Ok(())
228    }
229
230    pub fn truncate(&mut self, len: usize) -> Result<()> {
231        if !self.writable {
232            return Err(Error::BadFileDescriptor);
233        }
234        if len > self.fs.info().file_max as usize {
235            return Err(Error::Unsupported);
236        }
237        if self.stream.is_some() {
238            return Err(Error::Unsupported);
239        }
240        if self.merge.is_some() {
241            self.promote_merge_to_buffer()?;
242        }
243        self.data.resize(len, 0);
244        if self.pos > len {
245            self.pos = len;
246        }
247        self.len = len;
248        self.dirty = true;
249        Ok(())
250    }
251
252    pub fn flush(&mut self) -> Result<()> {
253        if !self.dirty {
254            return Ok(());
255        }
256        // Try writeback with a cloned plan first. A sync fault can happen after
257        // data or metadata reached the device; keeping the live handle state
258        // intact lets callers retry the same logical flush.
259        if let Some(merge) = self.merge.clone() {
260            self.flush_merge_patches(merge)?;
261            self.data = self.fs.read_file(&self.path)?;
262            self.len = self.data.len();
263            self.pos = self.len;
264            self.stream_target = StreamingTarget::Replace;
265            self.merge = None;
266            self.dirty = false;
267            return Ok(());
268        }
269        if let Some(stream) = self.stream.clone() {
270            let len = stream.len;
271            self.fs.finish_streaming_file(self.path.clone(), stream)?;
272            // Streaming handles are enabled only for write-only create,
273            // truncate-replace, and append shapes. Re-reading the just-written
274            // CTZ file here used to make close/sync briefly allocate the full
275            // payload again, which defeated the point of streaming writes.
276            self.data.clear();
277            self.len = len;
278            self.pos = self.len;
279            self.stream_target = StreamingTarget::Replace;
280            self.stream = None;
281            self.dirty = false;
282            return Ok(());
283        }
284        self.fs.write_file(&self.path, &self.data)?;
285        self.len = self.data.len();
286        self.stream_target = StreamingTarget::Replace;
287        self.dirty = false;
288        Ok(())
289    }
290
291    pub fn sync(&mut self) -> Result<()> {
292        self.flush()?;
293        self.fs.sync()
294    }
295
296    pub fn close(mut self) -> Result<()> {
297        if !self.dirty {
298            return Ok(());
299        }
300        if let Some(merge) = self.merge.take() {
301            return self.flush_merge_patches(merge);
302        }
303        if let Some(stream) = self.stream.take() {
304            return self.fs.finish_streaming_file(self.path, stream);
305        }
306        self.fs.write_file(&self.path, &self.data)
307    }
308
309    fn start_streaming_from_buffer(&mut self) -> Result<()> {
310        let buffered = core::mem::take(&mut self.data);
311        self.stream = Some(StreamingWrite {
312            allocator: self.fs.allocator.clone(),
313            blocks: Vec::new(),
314            current: None,
315            len: 0,
316            target: self.stream_target,
317        });
318        self.pos = 0;
319        self.write_streaming(&buffered)
320    }
321
322    fn write_merge_patch(&mut self, data: &[u8]) -> Result<usize> {
323        let end = self.pos.checked_add(data.len()).ok_or(Error::NoSpace)?;
324        if end > self.fs.info().file_max as usize {
325            return Err(Error::FileTooLarge);
326        }
327        let original_len = self.merge.as_ref().ok_or(Error::Corrupt)?.original_len;
328        if end > original_len {
329            // Extending at an arbitrary offset needs sparse-zero semantics and
330            // read-after-write merging. Keep that less common shape correct by
331            // falling back to the existing buffered path.
332            self.promote_merge_to_buffer()?;
333            return self.write(data);
334        }
335
336        let merge = self.merge.as_mut().ok_or(Error::Corrupt)?;
337        merge.patches.push(FilePatch {
338            off: self.pos,
339            data: data.to_vec(),
340        });
341        self.pos = end;
342        self.len = original_len;
343        self.dirty = true;
344        Ok(data.len())
345    }
346
347    fn promote_merge_to_buffer(&mut self) -> Result<()> {
348        let Some(merge) = self.merge.take() else {
349            return Ok(());
350        };
351        let mut data = self.fs.read_file(&self.path)?;
352        for patch in merge.patches {
353            let end = patch
354                .off
355                .checked_add(patch.data.len())
356                .ok_or(Error::NoSpace)?;
357            if end > data.len() {
358                data.resize(end, 0);
359            }
360            data[patch.off..end].copy_from_slice(&patch.data);
361        }
362        self.len = data.len();
363        self.data = data;
364        Ok(())
365    }
366
367    fn flush_merge_patches(&mut self, merge: MergeWrite) -> Result<()> {
368        let mut stream = StreamingWrite {
369            allocator: self.fs.allocator.clone(),
370            blocks: Vec::new(),
371            current: None,
372            len: 0,
373            target: StreamingTarget::Replace,
374        };
375
376        let mut off = 0usize;
377        let mut buf = alloc::vec![0; self.fs.info().block_size as usize];
378        while off < merge.original_len {
379            let n = core::cmp::min(buf.len(), merge.original_len - off);
380            let read = self.fs.read_file_at(&self.path, off, &mut buf[..n])?;
381            if read != n {
382                return Err(Error::Corrupt);
383            }
384            for patch in &merge.patches {
385                let patch_end = patch
386                    .off
387                    .checked_add(patch.data.len())
388                    .ok_or(Error::NoSpace)?;
389                let start = core::cmp::max(off, patch.off);
390                let end = core::cmp::min(off + n, patch_end);
391                if start < end {
392                    let dst = start - off;
393                    let src = start - patch.off;
394                    let len = end - start;
395                    buf[dst..dst + len].copy_from_slice(&patch.data[src..src + len]);
396                }
397            }
398            self.write_streaming_into(&mut stream, &buf[..n])?;
399            off += n;
400        }
401
402        if let Some(current) = stream.current.take() {
403            self.fs.flush_streaming_block(&mut stream, current)?;
404        }
405        self.fs.finish_streaming_file(self.path.clone(), stream)
406    }
407
408    fn write_streaming_into(&mut self, stream: &mut StreamingWrite, mut data: &[u8]) -> Result<()> {
409        while !data.is_empty() {
410            if stream.current.is_none() {
411                let index = stream.blocks.len();
412                let block = stream.allocator.alloc_block()?;
413                let mut bytes = alloc::vec![0xff; self.fs.info().block_size as usize];
414                let data_start = ctz_data_start(index)?;
415                if index > 0 {
416                    let skips = index.trailing_zeros() as usize + 1;
417                    for skip in 0..skips {
418                        let target_index =
419                            index.checked_sub(1usize << skip).ok_or(Error::Corrupt)?;
420                        let target = stream
421                            .blocks
422                            .get(target_index)
423                            .copied()
424                            .ok_or(Error::Corrupt)?;
425                        program_nor_bytes(&mut bytes, skip * 4, &target.to_le_bytes())?;
426                    }
427                }
428                stream.current = Some(StreamingBlock {
429                    block,
430                    bytes,
431                    off: data_start,
432                    mode: StreamingBlockMode::New,
433                });
434            }
435
436            let current = stream.current.as_mut().ok_or(Error::Corrupt)?;
437            let capacity = current.bytes.len().saturating_sub(current.off);
438            let n = core::cmp::min(capacity, data.len());
439            current.bytes[current.off..current.off + n].copy_from_slice(&data[..n]);
440            current.off += n;
441            stream.len = stream.len.checked_add(n).ok_or(Error::NoSpace)?;
442            data = &data[n..];
443
444            if current.off == current.bytes.len() {
445                let current = stream.current.take().ok_or(Error::Corrupt)?;
446                self.fs.flush_streaming_block(stream, current)?;
447            }
448        }
449        Ok(())
450    }
451
452    fn write_streaming(&mut self, mut data: &[u8]) -> Result<()> {
453        let stream = self.stream.as_mut().ok_or(Error::Corrupt)?;
454        if self.pos != stream.len {
455            return Err(Error::Unsupported);
456        }
457        if stream.len.checked_add(data.len()).ok_or(Error::NoSpace)?
458            > self.fs.info().file_max as usize
459        {
460            return Err(Error::FileTooLarge);
461        }
462
463        while !data.is_empty() {
464            if stream.current.is_none() {
465                let index = stream.blocks.len();
466                let block = stream.allocator.alloc_block()?;
467                let mut bytes = alloc::vec![0xff; self.fs.info().block_size as usize];
468                let data_start = ctz_data_start(index)?;
469                if index > 0 {
470                    let skips = index.trailing_zeros() as usize + 1;
471                    for skip in 0..skips {
472                        let target_index =
473                            index.checked_sub(1usize << skip).ok_or(Error::Corrupt)?;
474                        let target = stream
475                            .blocks
476                            .get(target_index)
477                            .copied()
478                            .ok_or(Error::Corrupt)?;
479                        program_nor_bytes(&mut bytes, skip * 4, &target.to_le_bytes())?;
480                    }
481                }
482                stream.current = Some(StreamingBlock {
483                    block,
484                    bytes,
485                    off: data_start,
486                    mode: StreamingBlockMode::New,
487                });
488            }
489
490            let current = stream.current.as_mut().ok_or(Error::Corrupt)?;
491            let capacity = current.bytes.len().saturating_sub(current.off);
492            let n = core::cmp::min(capacity, data.len());
493            current.bytes[current.off..current.off + n].copy_from_slice(&data[..n]);
494            current.off += n;
495            stream.len = stream.len.checked_add(n).ok_or(Error::NoSpace)?;
496            self.pos = stream.len;
497            self.len = stream.len;
498            self.dirty = true;
499            data = &data[n..];
500
501            if current.off == current.bytes.len() {
502                let current = stream.current.take().ok_or(Error::Corrupt)?;
503                self.fs.flush_streaming_block(stream, current)?;
504            }
505        }
506
507        Ok(())
508    }
509}
510
511impl<'fs, 'a> DirHandle<'fs, 'a> {
512    pub fn read(&mut self) -> Result<Option<DirEntry>> {
513        let entry = self.fs.dir_entry_at(self.head, self.pos)?;
514        if entry.is_some() {
515            self.pos += 1;
516        }
517        Ok(entry)
518    }
519
520    pub fn seek(&mut self, pos: usize) -> Result<()> {
521        self.pos = pos;
522        Ok(())
523    }
524
525    pub fn rewind(&mut self) -> Result<()> {
526        self.pos = 0;
527        Ok(())
528    }
529
530    pub fn close(self) -> Result<()> {
531        Ok(())
532    }
533}