Merge pull request #13 from trussed-dev/chunked

chunked: Add AppendFile and PartialReadFile syscalls
This commit is contained in:
Robin Krahl
2023-11-28 10:43:31 +01:00
committed by GitHub
3 changed files with 210 additions and 6 deletions
+1
View File
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased][]
- Add `ManageExtension`: Factory reset the entire device or the state of a given client ([#11][])
- `ChunkedExtension`: Add `AppendFile` and `PartialReadFile` syscalls.
[#11]: https://github.com/trussed-dev/trussed-staging/pull/11
+150
View File
@@ -4,6 +4,7 @@
mod store;
use store::OpenSeekFrom;
#[cfg(feature = "encrypted-chunked")]
pub mod utils;
#[cfg(feature = "encrypted-chunked")]
@@ -85,6 +86,8 @@ pub enum ChunkedRequest {
ReadChunk(request::ReadChunk),
WriteChunk(request::WriteChunk),
AbortChunkedWrite(request::AbortChunkedWrite),
PartialReadFile(request::PartialReadFile),
AppendFile(request::AppendFile),
}
#[derive(Debug, Deserialize, Serialize)]
@@ -99,6 +102,8 @@ pub enum ChunkedReply {
StartEncryptedChunkedRead(reply::StartEncryptedChunkedRead),
WriteChunk(reply::WriteChunk),
AbortChunkedWrite(reply::AbortChunkedWrite),
PartialReadFile(reply::PartialReadFile),
AppendFile(reply::AppendFile),
}
mod request {
@@ -265,6 +270,53 @@ mod request {
Self::AbortChunkedWrite(request)
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct PartialReadFile {
pub location: Location,
pub path: PathBuf,
pub offset: usize,
pub length: usize,
}
impl TryFrom<ChunkedRequest> for PartialReadFile {
type Error = Error;
fn try_from(request: ChunkedRequest) -> Result<Self, Self::Error> {
match request {
ChunkedRequest::PartialReadFile(request) => Ok(request),
_ => Err(Error::InternalError),
}
}
}
impl From<PartialReadFile> for ChunkedRequest {
fn from(request: PartialReadFile) -> Self {
Self::PartialReadFile(request)
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct AppendFile {
pub location: Location,
pub path: PathBuf,
pub data: Message,
}
impl TryFrom<ChunkedRequest> for AppendFile {
type Error = Error;
fn try_from(request: ChunkedRequest) -> Result<Self, Self::Error> {
match request {
ChunkedRequest::AppendFile(request) => Ok(request),
_ => Err(Error::InternalError),
}
}
}
impl From<AppendFile> for ChunkedRequest {
fn from(request: AppendFile) -> Self {
Self::AppendFile(request)
}
}
}
mod reply {
@@ -419,6 +471,49 @@ mod reply {
Self::AbortChunkedWrite(reply)
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct PartialReadFile {
pub data: Message,
pub file_length: usize,
}
impl TryFrom<ChunkedReply> for PartialReadFile {
type Error = Error;
fn try_from(reply: ChunkedReply) -> Result<Self, Self::Error> {
match reply {
ChunkedReply::PartialReadFile(reply) => Ok(reply),
_ => Err(Error::InternalError),
}
}
}
impl From<PartialReadFile> for ChunkedReply {
fn from(reply: PartialReadFile) -> Self {
Self::PartialReadFile(reply)
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct AppendFile {
pub file_length: usize,
}
impl TryFrom<ChunkedReply> for AppendFile {
type Error = Error;
fn try_from(reply: ChunkedReply) -> Result<Self, Self::Error> {
match reply {
ChunkedReply::AppendFile(reply) => Ok(reply),
_ => Err(Error::InternalError),
}
}
}
impl From<AppendFile> for ChunkedReply {
fn from(reply: AppendFile) -> Self {
Self::AppendFile(reply)
}
}
}
impl ExtensionImpl<ChunkedExtension> for super::StagingBackend {
@@ -502,6 +597,27 @@ impl ExtensionImpl<ChunkedExtension> for super::StagingBackend {
store::start_chunked_write(store, client_id, &request.path, request.location, &[])?;
Ok(reply::StartChunkedWrite {}.into())
}
ChunkedRequest::PartialReadFile(request) => {
let (data, file_length) = store::partial_read_file(
store,
client_id,
&request.path,
request.location,
request.offset,
request.length,
)?;
Ok(reply::PartialReadFile { data, file_length }.into())
}
ChunkedRequest::AppendFile(request) => {
let file_length = store::append_file(
store,
client_id,
&request.path,
request.location,
&request.data,
)?;
Ok(reply::AppendFile { file_length }.into())
}
#[cfg(feature = "encrypted-chunked")]
ChunkedRequest::StartEncryptedChunkedWrite(request) => {
clear_chunked_state(store, client_id, backend_ctx)?;
@@ -834,6 +950,40 @@ pub trait ChunkedClient: ExtensionClient<ChunkedExtension> + FilesystemClient {
fn read_file_chunk(&mut self) -> ChunkedResult<'_, reply::ReadChunk, Self> {
self.extension(request::ReadChunk {})
}
/// Partially read a file from a given offset, returning a chunk of the given length and the
/// total file size.
///
/// If the length is greater than [`trussed::config::MAX_MESSAGE_LENGTH`][] or if the offset is
/// greater than the file size, an error is returned.
fn partial_read_file(
&mut self,
location: Location,
path: PathBuf,
offset: usize,
length: usize,
) -> ChunkedResult<'_, reply::PartialReadFile, Self> {
self.extension(request::PartialReadFile {
location,
path,
offset,
length,
})
}
/// Append data to an existing file and return the size of the file after the write.
fn append_file(
&mut self,
location: Location,
path: PathBuf,
data: Message,
) -> ChunkedResult<'_, reply::AppendFile, Self> {
self.extension(request::AppendFile {
location,
path,
data,
})
}
}
impl<C: ExtensionClient<ChunkedExtension> + FilesystemClient> ChunkedClient for C {}
+59 -6
View File
@@ -2,11 +2,11 @@
// SPDX-License-Identifier: Apache-2.0 or MIT
use littlefs2::driver::Storage as LfsStorage;
use littlefs2::fs::{File, Filesystem};
use littlefs2::fs::{File, Filesystem, OpenOptions};
use littlefs2::io::{SeekFrom, Write};
use trussed::store::{create_directories, Store};
use trussed::types::{Bytes, Location, Path, PathBuf};
use trussed::types::{Bytes, Location, Message, Path, PathBuf};
use trussed::Error;
use serde::{Deserialize, Serialize};
@@ -33,9 +33,13 @@ pub fn fs_read_chunk<Storage: LfsStorage, const N: usize>(
fs: &Filesystem<Storage>,
path: &Path,
pos: OpenSeekFrom,
length: usize,
) -> Result<(Bytes<N>, usize), Error> {
let mut contents = Bytes::default();
contents.resize_default(contents.capacity()).unwrap();
if length > contents.capacity() {
return Err(Error::FilesystemReadFailure);
}
contents.resize_default(length).unwrap();
let file_len = File::open_and_then(fs, path, |file| {
file.seek(pos.into())?;
let read_n = file.read(&mut contents)?;
@@ -45,6 +49,7 @@ pub fn fs_read_chunk<Storage: LfsStorage, const N: usize>(
.map_err(|_| Error::FilesystemReadFailure)?;
Ok((contents, file_len))
}
/// Reads contents from path in location of store.
#[inline(never)]
pub fn read_chunk<const N: usize>(
@@ -55,9 +60,9 @@ pub fn read_chunk<const N: usize>(
) -> Result<(Bytes<N>, usize), Error> {
debug_now!("reading chunk {},{:?}", &path, pos);
match location {
Location::Internal => fs_read_chunk(store.ifs(), path, pos),
Location::External => fs_read_chunk(store.efs(), path, pos),
Location::Volatile => fs_read_chunk(store.vfs(), path, pos),
Location::Internal => fs_read_chunk(store.ifs(), path, pos, N),
Location::External => fs_read_chunk(store.efs(), path, pos, N),
Location::Volatile => fs_read_chunk(store.vfs(), path, pos, N),
}
}
@@ -290,3 +295,51 @@ pub fn flush_chunks(
&client_path,
)
}
pub fn partial_read_file(
store: impl Store,
client_id: &Path,
path: &PathBuf,
location: Location,
offset: usize,
length: usize,
) -> Result<(Message, usize), Error> {
let path = actual_path(client_id, path)?;
let offset = u32::try_from(offset).map_err(|_| Error::FilesystemReadFailure)?;
let pos = OpenSeekFrom::Start(offset);
match location {
Location::Internal => fs_read_chunk(store.ifs(), &path, pos, length),
Location::External => fs_read_chunk(store.efs(), &path, pos, length),
Location::Volatile => fs_read_chunk(store.vfs(), &path, pos, length),
}
}
fn fs_append_file<Storage: LfsStorage>(
fs: &Filesystem<Storage>,
path: &Path,
data: &[u8],
) -> Result<usize, Error> {
OpenOptions::new()
.write(true)
.append(true)
.open_and_then(fs, path, |file| {
file.write_all(data)?;
file.len()
})
.map_err(|_| Error::FilesystemWriteFailure)
}
pub fn append_file(
store: impl Store,
client_id: &Path,
path: &PathBuf,
location: Location,
data: &[u8],
) -> Result<usize, Error> {
let path = actual_path(client_id, path)?;
match location {
Location::Internal => fs_append_file(store.ifs(), &path, data),
Location::External => fs_append_file(store.efs(), &path, data),
Location::Volatile => fs_append_file(store.vfs(), &path, data),
}
}