cat_dev/fsemul/pcfs/sata_proto/
write_file.rs1use 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
25const FS_ERROR: u32 = 0xFFF0_FFE0;
27
28#[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 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 }
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 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}