mirror of
https://github.com/trussed-dev/trussed-staging.git
synced 2026-06-20 04:16:23 -07:00
Merge pull request #13 from trussed-dev/chunked
chunked: Add AppendFile and PartialReadFile syscalls
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user