Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit b7933bf

Browse files
Add chunked read/write API
1 parent c2cae35 commit b7933bf

7 files changed

Lines changed: 231 additions & 2 deletions

File tree

src/api.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ generate_enums! {
4343
ReadDirFilesFirst: 13
4444
ReadDirFilesNext: 14
4545
ReadFile: 15
46+
ReadChunk: 65
4647
Metadata: 26
4748
// ReadCounter: 7
4849
RandomBytes: 16
4950
SerializeKey: 17
5051
Sign: 18
5152
WriteFile: 19
53+
WriteChunk: 66
5254
UnsafeInjectKey: 20
5355
UnsafeInjectSharedKey: 21
5456
UnwrapKey: 22
@@ -241,6 +243,10 @@ pub mod request {
241243
ReadFile:
242244
- location: Location
243245
- path: PathBuf
246+
ReadChunk:
247+
- location: Location
248+
- pos: OpenSeekFrom
249+
- path: PathBuf
244250

245251
Metadata:
246252
- location: Location
@@ -282,6 +288,12 @@ pub mod request {
282288
- data: Message
283289
- user_attribute: Option<UserAttribute>
284290

291+
WriteChunk:
292+
- location: Location
293+
- pos: OpenSeekFrom
294+
- path: PathBuf
295+
- data: Message
296+
285297
UnsafeInjectKey:
286298
- mechanism: Mechanism // -> implies key type
287299
- raw_key: SerializedKey
@@ -430,6 +442,9 @@ pub mod reply {
430442

431443
ReadFile:
432444
- data: Message
445+
ReadChunk:
446+
- data: Message
447+
- len: usize
433448

434449
Metadata:
435450
- metadata: Option<crate::types::Metadata>
@@ -454,6 +469,7 @@ pub mod reply {
454469
- signature: Signature
455470

456471
WriteFile:
472+
WriteChunk:
457473

458474
Verify:
459475
- valid: bool

src/client.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,19 @@ pub trait FilesystemClient: PollClient {
622622
self.request(request::ReadFile { location, path })
623623
}
624624

625+
fn read_file_chunk(
626+
&mut self,
627+
location: Location,
628+
path: PathBuf,
629+
pos: OpenSeekFrom,
630+
) -> ClientResult<'_, reply::ReadChunk, Self> {
631+
self.request(request::ReadChunk {
632+
location,
633+
path,
634+
pos,
635+
})
636+
}
637+
625638
/// Fetch the Metadata for a file or directory
626639
///
627640
/// If the file doesn't exists, return None
@@ -660,6 +673,21 @@ pub trait FilesystemClient: PollClient {
660673
user_attribute,
661674
})
662675
}
676+
677+
fn write_file_chunk(
678+
&mut self,
679+
location: Location,
680+
path: PathBuf,
681+
data: Message,
682+
pos: OpenSeekFrom,
683+
) -> ClientResult<'_, reply::WriteChunk, Self> {
684+
self.request(request::WriteChunk {
685+
location,
686+
path,
687+
data,
688+
pos,
689+
})
690+
}
663691
}
664692

665693
/// All the other methods that are fit to expose.

src/service.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,14 @@ impl<P: Platform> ServiceResources<P> {
444444
}))
445445
}
446446

447+
Request::ReadChunk(request) => {
448+
let (data,len) = filestore.read_chunk(&request.path,request.location,request.pos)?;
449+
Ok(Reply::ReadChunk(reply::ReadChunk {
450+
data,
451+
len,
452+
}))
453+
}
454+
447455
Request::Metadata(request) => {
448456
Ok(Reply::Metadata(reply::Metadata{
449457
metadata: filestore.metadata(&request.path, request.location)?
@@ -497,6 +505,10 @@ impl<P: Platform> ServiceResources<P> {
497505
filestore.write(&request.path, request.location, &request.data)?;
498506
Ok(Reply::WriteFile(reply::WriteFile {} ))
499507
}
508+
Request::WriteChunk(request) => {
509+
filestore.write_chunk(&request.path, request.location, &request.data,request.pos)?;
510+
Ok(Reply::WriteChunk(reply::WriteChunk {} ))
511+
}
500512

501513
Request::UnwrapKey(request) => {
502514
match request.mechanism {

src/store.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ use crate::types::*;
7676
#[allow(unused_imports)]
7777
#[cfg(feature = "semihosting")]
7878
use cortex_m_semihosting::hprintln;
79+
use littlefs2::fs::File;
7980
use littlefs2::path::Path;
8081

8182
pub mod certstore;
@@ -523,6 +524,38 @@ pub fn read<const N: usize>(
523524
.map_err(|_| Error::FilesystemReadFailure)
524525
}
525526

527+
pub fn fs_read_chunk<Storage: LfsStorage, const N: usize>(
528+
fs: &Filesystem<Storage>,
529+
path: &Path,
530+
pos: OpenSeekFrom,
531+
) -> Result<(Bytes<N>, usize), Error> {
532+
let mut contents = Bytes::default();
533+
contents.resize_default(contents.capacity()).unwrap();
534+
let file_len = File::open_and_then(fs, path, |file| {
535+
file.seek(pos.into())?;
536+
let read_n = file.read(&mut contents)?;
537+
contents.truncate(read_n);
538+
file.len()
539+
})
540+
.map_err(|_| Error::FilesystemReadFailure)?;
541+
Ok((contents, file_len))
542+
}
543+
/// Reads contents from path in location of store.
544+
#[inline(never)]
545+
pub fn read_chunk<const N: usize>(
546+
store: impl Store,
547+
location: Location,
548+
path: &Path,
549+
pos: OpenSeekFrom,
550+
) -> Result<(Bytes<N>, usize), Error> {
551+
debug_now!("reading chunk {},{:?}", &path, pos);
552+
match location {
553+
Location::Internal => fs_read_chunk(store.ifs(), path, pos),
554+
Location::External => fs_read_chunk(store.efs(), path, pos),
555+
Location::Volatile => fs_read_chunk(store.vfs(), path, pos),
556+
}
557+
}
558+
526559
/// Writes contents to path in location of store.
527560
#[inline(never)]
528561
pub fn write(
@@ -540,6 +573,39 @@ pub fn write(
540573
.map_err(|_| Error::FilesystemWriteFailure)
541574
}
542575

576+
pub fn fs_write_chunk<Storage: LfsStorage>(
577+
fs: &Filesystem<Storage>,
578+
path: &Path,
579+
contents: &[u8],
580+
pos: OpenSeekFrom,
581+
) -> Result<(), Error> {
582+
File::open_and_then(fs, path, |file| {
583+
use littlefs2::io::Write;
584+
file.seek(pos.into())?;
585+
file.write_all(contents)
586+
})
587+
.map_err(|_| Error::FilesystemReadFailure)?;
588+
Ok(())
589+
}
590+
591+
/// Writes contents to path in location of store.
592+
#[inline(never)]
593+
pub fn write_chunk(
594+
store: impl Store,
595+
location: Location,
596+
path: &Path,
597+
contents: &[u8],
598+
pos: OpenSeekFrom,
599+
) -> Result<(), Error> {
600+
debug_now!("writing {}", &path);
601+
match location {
602+
Location::Internal => fs_write_chunk(store.ifs(), path, contents, pos),
603+
Location::External => fs_write_chunk(store.efs(), path, contents, pos),
604+
Location::Volatile => fs_write_chunk(store.vfs(), path, contents, pos),
605+
}
606+
.map_err(|_| Error::FilesystemWriteFailure)
607+
}
608+
543609
/// Creates parent directory if necessary, then writes.
544610
#[inline(never)]
545611
pub fn store(

src/store/filestore.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
error::{Error, Result},
33
// service::ReadDirState,
44
store::{self, Store},
5-
types::{LfsStorage, Location, Message, UserAttribute},
5+
types::{LfsStorage, Location, Message, OpenSeekFrom, UserAttribute},
66
Bytes,
77
};
88

@@ -76,7 +76,20 @@ impl<S: Store> ClientFilestore<S> {
7676

7777
pub trait Filestore {
7878
fn read<const N: usize>(&mut self, path: &PathBuf, location: Location) -> Result<Bytes<N>>;
79+
fn read_chunk<const N: usize>(
80+
&mut self,
81+
path: &PathBuf,
82+
location: Location,
83+
pos: OpenSeekFrom,
84+
) -> Result<(Bytes<N>, usize)>;
7985
fn write(&mut self, path: &PathBuf, location: Location, data: &[u8]) -> Result<()>;
86+
fn write_chunk(
87+
&mut self,
88+
path: &PathBuf,
89+
location: Location,
90+
data: &[u8],
91+
pos: OpenSeekFrom,
92+
) -> Result<()>;
8093
fn exists(&mut self, path: &PathBuf, location: Location) -> bool;
8194
fn metadata(&mut self, path: &PathBuf, location: Location) -> Result<Option<Metadata>>;
8295
fn remove_file(&mut self, path: &PathBuf, location: Location) -> Result<()>;
@@ -351,12 +364,33 @@ impl<S: Store> Filestore for ClientFilestore<S> {
351364

352365
store::read(self.store, location, &path)
353366
}
367+
fn read_chunk<const N: usize>(
368+
&mut self,
369+
path: &PathBuf,
370+
location: Location,
371+
pos: OpenSeekFrom,
372+
) -> Result<(Bytes<N>, usize)> {
373+
let path = self.actual_path(path)?;
374+
375+
store::read_chunk(self.store, location, &path, pos)
376+
}
354377

355378
fn write(&mut self, path: &PathBuf, location: Location, data: &[u8]) -> Result<()> {
356379
let path = self.actual_path(path)?;
357380
store::store(self.store, location, &path, data)
358381
}
359382

383+
fn write_chunk(
384+
&mut self,
385+
path: &PathBuf,
386+
location: Location,
387+
data: &[u8],
388+
pos: OpenSeekFrom,
389+
) -> Result<()> {
390+
let path = self.actual_path(path)?;
391+
store::write_chunk(self.store, location, &path, data, pos)
392+
}
393+
360394
fn exists(&mut self, path: &PathBuf, location: Location) -> bool {
361395
if let Ok(path) = self.actual_path(path) {
362396
store::exists(self.store, location, &path)

src/tests.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,8 @@ fn filesystem() {
637637
.metadata
638638
.is_none(),);
639639

640-
let data = Bytes::from_slice(&[0; 20]).unwrap();
640+
let data = Bytes::from_slice(b"test data").unwrap();
641+
let more_data = Bytes::from_slice(b"there's more").unwrap();
641642
block!(client
642643
.write_file(
643644
Location::Internal,
@@ -655,6 +656,59 @@ fn filesystem() {
655656
.data;
656657
assert_eq!(data, recv_data);
657658

659+
let partial_data = block!(client
660+
.read_file_chunk(
661+
Location::Internal,
662+
PathBuf::from("test_file"),
663+
OpenSeekFrom::Start(data.len() as u32)
664+
)
665+
.unwrap())
666+
.unwrap();
667+
assert_eq!(&partial_data.data, &[]);
668+
assert_eq!(partial_data.len, data.len());
669+
670+
let partial_data = block!(client
671+
.read_file_chunk(
672+
Location::Internal,
673+
PathBuf::from("test_file"),
674+
OpenSeekFrom::Start(3)
675+
)
676+
.unwrap())
677+
.unwrap();
678+
assert_eq!(&partial_data.data, &data[3..]);
679+
assert_eq!(partial_data.len, data.len());
680+
681+
let partial_data = block!(client
682+
.read_file_chunk(
683+
Location::Internal,
684+
PathBuf::from("test_file"),
685+
OpenSeekFrom::Start(data.len() as u32)
686+
)
687+
.unwrap())
688+
.unwrap();
689+
assert_eq!(&partial_data.data, &[]);
690+
assert_eq!(partial_data.len, data.len());
691+
692+
block!(client
693+
.write_file_chunk(
694+
Location::Internal,
695+
PathBuf::from("test_file"),
696+
more_data.clone(),
697+
OpenSeekFrom::Start(data.len() as u32)
698+
)
699+
.unwrap())
700+
.unwrap();
701+
let partial_data = block!(client
702+
.read_file_chunk(
703+
Location::Internal,
704+
PathBuf::from("test_file"),
705+
OpenSeekFrom::Start(data.len() as u32)
706+
)
707+
.unwrap())
708+
.unwrap();
709+
assert_eq!(&partial_data.data, &more_data);
710+
assert_eq!(partial_data.len, data.len() + more_data.len());
711+
658712
let metadata = block!(client
659713
.entry_metadata(Location::Internal, PathBuf::from("test_file"))
660714
.expect("no client error"))

src/types.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use core::ops::Deref;
44
pub use generic_array::GenericArray;
55

66
pub use heapless::{String, Vec};
7+
use littlefs2::io::SeekFrom;
78

89
pub use crate::Bytes;
910

@@ -641,3 +642,21 @@ pub struct RsaCrtImportFormat<'d> {
641642
pub dp: &'d [u8],
642643
pub dq: &'d [u8],
643644
}
645+
646+
/// Enumeration of possible methods to seek within an file that was just opened
647+
/// Used in the [`read_chunk`](crate::store::read_chunk) and [`write_chunk`](crate::store::write_chunk) calls,
648+
/// Where [`SeekFrom::Current`](littlefs2::io::SeekFrom::Current) would not make sense.
649+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
650+
pub enum OpenSeekFrom {
651+
Start(u32),
652+
End(i32),
653+
}
654+
655+
impl From<OpenSeekFrom> for SeekFrom {
656+
fn from(value: OpenSeekFrom) -> Self {
657+
match value {
658+
OpenSeekFrom::Start(o) => Self::Start(o),
659+
OpenSeekFrom::End(o) => Self::End(o),
660+
}
661+
}
662+
}

0 commit comments

Comments
 (0)