cat_dev/fsemul/pcfs/sata_proto/
write_file.rs

1//! Definitions, and handlers for the `WriteFile` packet type.
2//!
3//! This is what actively handles writing bytes to a file. With either
4//! FFIO, and Combined Send/Recv options being turned on/off.
5
6use crate::{
7	errors::{CatBridgeError, NetworkError, NetworkParseError},
8	fsemul::{
9		pcfs::sata_proto::{
10			construct_sata_response, MoveToFileLocation, SataPacketHeader, SataProtoChunker,
11		},
12		HostFilesystem,
13	},
14};
15use bytes::{Buf, BufMut, Bytes, BytesMut};
16use futures::{stream::SplitStream, StreamExt};
17use std::sync::{
18	atomic::{AtomicUsize, Ordering as AtomicOrdering},
19	Arc,
20};
21use tokio::net::TcpStream;
22use tokio_util::codec::Framed;
23use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
24
25/// A filesystem error occured.
26const FS_ERROR: u32 = 0xFFF0_FFE0;
27
28/// A packet to write to an already open file.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct SataWriteFilePacketBody {
31	block_count: u32,
32	block_size: u32,
33	handle: i32,
34	move_to_pointer: MoveToFileLocation,
35	should_move: bool,
36}
37
38impl SataWriteFilePacketBody {
39	#[must_use]
40	pub const fn block_count(&self) -> u32 {
41		self.block_count
42	}
43	#[must_use]
44	pub const fn block_size(&self) -> u32 {
45		self.block_size
46	}
47	#[must_use]
48	pub const fn file_descriptor(&self) -> i32 {
49		self.handle
50	}
51	#[must_use]
52	pub const fn move_to_pointer(&self) -> MoveToFileLocation {
53		self.move_to_pointer
54	}
55	#[must_use]
56	pub const fn should_move(&self) -> bool {
57		self.should_move
58	}
59
60	/// Handle writing to a file that is already open.
61	///
62	/// ## Errors
63	///
64	/// If we cannot construct a sata response packet because our data to send
65	/// was somehow too large (this should ideally never happen), or if we're
66	/// running on a 16 bit system.
67	pub async fn handle(
68		&self,
69		request_header: &SataPacketHeader,
70		host_filesystem: &HostFilesystem,
71		ffio_supported: bool,
72		socket: &mut SplitStream<Framed<TcpStream, SataProtoChunker>>,
73		override_ptr: &Arc<AtomicUsize>,
74	) -> Result<Bytes, CatBridgeError> {
75		if self.should_move {
76			match self.move_to_pointer {
77				MoveToFileLocation::Begin => {
78					if host_filesystem.seek_file(self.handle, true).await.is_err() {
79						return Self::construct_error(request_header, FS_ERROR);
80					}
81				}
82				MoveToFileLocation::Current => {
83					// Luckily to move to current, we don't need to move at all.
84				}
85				MoveToFileLocation::End => {
86					if host_filesystem.seek_file(self.handle, false).await.is_err() {
87						return Self::construct_error(request_header, FS_ERROR);
88					}
89				}
90			}
91		}
92
93		if ffio_supported {
94			let len_needed = usize::try_from(self.block_count * self.block_size)
95				.map_err(|_| CatBridgeError::UnsupportedBitsPerCore)?;
96			// Bypass header and such checks...
97			override_ptr.store(len_needed, AtomicOrdering::Release);
98			let buff = socket
99				.next()
100				.await
101				.ok_or_else(|| NetworkError::ExpectedData)?
102				.map_err(NetworkError::IO)?
103				.freeze();
104			host_filesystem
105				.write_file(self.file_descriptor(), buff)
106				.await?;
107
108			let mut result = BytesMut::with_capacity(4);
109			result.put_u32(self.block_count * self.block_size);
110			Ok(construct_sata_response(request_header, 0, result.freeze())?)
111		} else {
112			todo!("Implement non-FFIO support.")
113		}
114	}
115
116	fn construct_error(
117		packet_header: &SataPacketHeader,
118		error_code: u32,
119	) -> Result<Bytes, CatBridgeError> {
120		let mut buff = BytesMut::with_capacity(8);
121		buff.put_u32(error_code);
122		Ok(construct_sata_response(packet_header, 0, buff.freeze())?)
123	}
124}
125
126impl TryFrom<Bytes> for SataWriteFilePacketBody {
127	type Error = NetworkParseError;
128
129	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
130		if value.len() < 20 {
131			return Err(NetworkParseError::FieldNotLongEnough(
132				"SataWriteFile",
133				"Body",
134				20,
135				value.len(),
136				value,
137			));
138		}
139		if value.len() > 20 {
140			return Err(NetworkParseError::UnexpectedTrailer(
141				"SataWriteFile",
142				value.slice(20..),
143			));
144		}
145
146		let block_count = value.get_u32();
147		let block_length = value.get_u32();
148		let handle = value.get_i32();
149		let move_to_ptr = value.get_u32();
150		let should_move = value.get_u32();
151
152		Ok(Self {
153			block_count,
154			block_size: block_length,
155			handle,
156			move_to_pointer: MoveToFileLocation::try_from(move_to_ptr)?,
157			should_move: (should_move & 1) != 0,
158		})
159	}
160}
161
162const SATA_WRITE_FILE_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[
163	NamedField::new("block_count"),
164	NamedField::new("block_size"),
165	NamedField::new("handle"),
166	NamedField::new("move_to_pointer"),
167	NamedField::new("should_move"),
168];
169
170impl Structable for SataWriteFilePacketBody {
171	fn definition(&self) -> StructDef<'_> {
172		StructDef::new_static(
173			"SataWriteFilePacketBody",
174			Fields::Named(SATA_WRITE_FILE_PACKET_BODY_FIELDS),
175		)
176	}
177}
178
179impl Valuable for SataWriteFilePacketBody {
180	fn as_value(&self) -> Value<'_> {
181		Value::Structable(self)
182	}
183
184	fn visit(&self, visitor: &mut dyn Visit) {
185		visitor.visit_named_fields(&NamedValues::new(
186			SATA_WRITE_FILE_PACKET_BODY_FIELDS,
187			&[
188				Valuable::as_value(&self.block_count),
189				Valuable::as_value(&self.block_size),
190				Valuable::as_value(&self.handle),
191				Valuable::as_value(&self.move_to_pointer),
192				Valuable::as_value(&self.should_move),
193			],
194		));
195	}
196}