1use serde_byte_array::ByteArray;
5use trussed_core::{
6 syscall, try_syscall,
7 types::{KeyId, Location, Message, PathBuf, UserAttribute},
8 Error,
9};
10
11use crate::{ChunkedClient, CHACHA8_STREAM_NONCE_LEN};
12
13#[derive(Clone, Copy)]
14pub struct EncryptionData {
15 pub key: KeyId,
16 pub nonce: Option<ByteArray<CHACHA8_STREAM_NONCE_LEN>>,
17}
18
19pub fn write_all(
23 client: &mut impl ChunkedClient,
24 location: Location,
25 path: PathBuf,
26 data: &[u8],
27 user_attribute: Option<UserAttribute>,
28 encryption: Option<EncryptionData>,
29) -> Result<(), Error> {
30 if let (Ok(msg), None) = (Message::from_slice(data), encryption) {
31 try_syscall!(client.write_file(location, path, msg, user_attribute))?;
33 Ok(())
34 } else {
35 write_chunked(client, location, path, data, user_attribute, encryption)
36 }
37}
38
39fn write_chunked(
40 client: &mut impl ChunkedClient,
41 location: Location,
42 path: PathBuf,
43 data: &[u8],
44 user_attribute: Option<UserAttribute>,
45 encryption: Option<EncryptionData>,
46) -> Result<(), Error> {
47 let res = write_chunked_inner(client, location, path, data, user_attribute, encryption);
48 if res.is_err() {
49 syscall!(client.abort_chunked_write());
50 return res;
51 }
52 Ok(())
53}
54
55fn write_chunked_inner(
56 client: &mut impl ChunkedClient,
57 location: Location,
58 path: PathBuf,
59 data: &[u8],
60 user_attribute: Option<UserAttribute>,
61 encryption: Option<EncryptionData>,
62) -> Result<(), Error> {
63 let msg = Message::new();
64 let chunk_size = msg.capacity();
65 let chunks = data.chunks(chunk_size).map(|chunk| {
66 Message::from_slice(chunk).expect("Iteration over chunks yields maximum of chunk_size")
67 });
68 if let Some(encryption_data) = encryption {
69 try_syscall!(client.start_encrypted_chunked_write(
70 location,
71 path,
72 encryption_data.key,
73 encryption_data.nonce,
74 user_attribute,
75 ))?;
76 } else {
77 try_syscall!(client.start_chunked_write(location, path, user_attribute))?;
78 }
79 let mut written = 0;
80 for chunk in chunks {
81 written += chunk.len();
82 try_syscall!(client.write_file_chunk(chunk))?;
83 }
84
85 if { written % chunk_size } == 0 {
86 try_syscall!(client.write_file_chunk(Message::new()))?;
87 }
88 Ok(())
89}