cat_dev/fsemul/pcfs/sata_proto/
read_file.rs1use crate::{
7 errors::{CatBridgeError, NetworkParseError},
8 fsemul::{
9 pcfs::sata_proto::{construct_sata_response, MoveToFileLocation, SataPacketHeader},
10 HostFilesystem,
11 },
12};
13use bytes::{Buf, BufMut, Bytes, BytesMut};
14use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
15
16const FS_ERROR: u32 = 0xFFF0_FFE0;
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct SataReadFilePacketBody {
22 block_count: u32,
23 block_size: u32,
24 handle: i32,
25 move_to_pointer: MoveToFileLocation,
26 should_move: bool,
27}
28
29impl SataReadFilePacketBody {
30 #[must_use]
31 pub const fn block_count(&self) -> u32 {
32 self.block_count
33 }
34 #[must_use]
35 pub const fn block_size(&self) -> u32 {
36 self.block_size
37 }
38 #[must_use]
39 pub const fn file_descriptor(&self) -> i32 {
40 self.handle
41 }
42 #[must_use]
43 pub const fn move_to_pointer(&self) -> MoveToFileLocation {
44 self.move_to_pointer
45 }
46 #[must_use]
47 pub const fn should_move(&self) -> bool {
48 self.should_move
49 }
50
51 pub async fn handle(
59 &self,
60 request_header: &SataPacketHeader,
61 host_filesystem: &HostFilesystem,
62 ffio_supported: bool,
63 ) -> Result<Bytes, CatBridgeError> {
64 if self.should_move {
65 match self.move_to_pointer {
66 MoveToFileLocation::Begin => {
67 if host_filesystem.seek_file(self.handle, true).await.is_err() {
68 return Self::construct_error(request_header, FS_ERROR);
69 }
70 }
71 MoveToFileLocation::Current => {
72 }
74 MoveToFileLocation::End => {
75 if host_filesystem.seek_file(self.handle, false).await.is_err() {
76 return Self::construct_error(request_header, FS_ERROR);
77 }
78 }
79 }
80 }
81
82 let Some(file_size) = host_filesystem.file_length(self.handle).await else {
83 return Self::construct_error(request_header, FS_ERROR);
84 };
85 let Ok(Some(read_file)) = host_filesystem
86 .read_file(
87 self.handle,
88 usize::try_from(self.block_size)
89 .map_err(|_| CatBridgeError::UnsupportedBitsPerCore)?
90 * usize::try_from(self.block_count)
91 .map_err(|_| CatBridgeError::UnsupportedBitsPerCore)?,
92 )
93 .await
94 else {
95 return Self::construct_error(request_header, FS_ERROR);
96 };
97
98 if ffio_supported {
99 let mut buff = BytesMut::with_capacity(read_file.len() + 0x24);
100 buff.extend_from_slice(&[0; 0x20]);
105 buff.put_u32(u32::try_from(file_size).unwrap_or(u32::MAX));
106 buff.extend(read_file);
107 Ok(buff.freeze())
108 } else {
109 todo!("Implement non-FFIO support.")
110 }
111 }
112
113 fn construct_error(
114 packet_header: &SataPacketHeader,
115 error_code: u32,
116 ) -> Result<Bytes, CatBridgeError> {
117 let mut buff = BytesMut::with_capacity(8);
118 buff.put_u32(error_code);
119 Ok(construct_sata_response(packet_header, 0, buff.freeze())?)
120 }
121}
122
123impl TryFrom<Bytes> for SataReadFilePacketBody {
124 type Error = NetworkParseError;
125
126 fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
127 if value.len() < 20 {
128 return Err(NetworkParseError::FieldNotLongEnough(
129 "SataReadFile",
130 "Body",
131 20,
132 value.len(),
133 value,
134 ));
135 }
136 if value.len() > 20 {
137 return Err(NetworkParseError::UnexpectedTrailer(
138 "SataReadFile",
139 value.slice(20..),
140 ));
141 }
142
143 let block_count = value.get_u32();
144 let block_length = value.get_u32();
145 let handle = value.get_i32();
146 let move_to_ptr = value.get_u32();
147 let should_move = value.get_u32();
148
149 Ok(Self {
150 block_count,
151 block_size: block_length,
152 handle,
153 move_to_pointer: MoveToFileLocation::try_from(move_to_ptr)?,
154 should_move: (should_move & 1) != 0,
155 })
156 }
157}
158
159const SATA_READ_FILE_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[
160 NamedField::new("block_count"),
161 NamedField::new("block_size"),
162 NamedField::new("handle"),
163 NamedField::new("move_to_pointer"),
164 NamedField::new("should_move"),
165];
166
167impl Structable for SataReadFilePacketBody {
168 fn definition(&self) -> StructDef<'_> {
169 StructDef::new_static(
170 "SataReadFilePacketBody",
171 Fields::Named(SATA_READ_FILE_PACKET_BODY_FIELDS),
172 )
173 }
174}
175
176impl Valuable for SataReadFilePacketBody {
177 fn as_value(&self) -> Value<'_> {
178 Value::Structable(self)
179 }
180
181 fn visit(&self, visitor: &mut dyn Visit) {
182 visitor.visit_named_fields(&NamedValues::new(
183 SATA_READ_FILE_PACKET_BODY_FIELDS,
184 &[
185 Valuable::as_value(&self.block_count),
186 Valuable::as_value(&self.block_size),
187 Valuable::as_value(&self.handle),
188 Valuable::as_value(&self.move_to_pointer),
189 Valuable::as_value(&self.should_move),
190 ],
191 ));
192 }
193}
194
195#[cfg(test)]
196mod unit_tests {
197 use super::*;
198 use crate::fsemul::host_filesystem::test_helpers::{
199 create_temporary_host_filesystem, join_many,
200 };
201 use tokio::fs::OpenOptions;
202
203 #[tokio::test]
204 pub async fn simple_ffio_read_file_request() {
205 let (tempdir, fs) = create_temporary_host_filesystem().await;
206
207 let base_dir = join_many(tempdir.path(), ["data", "slc", "to-query"]);
208 tokio::fs::create_dir(&base_dir)
209 .await
210 .expect("Failed to create temporary directory for test!");
211 tokio::fs::write(join_many(&base_dir, ["file.txt"]), vec![0; 2])
212 .await
213 .expect("Failed to write test file!");
214 let mocked_header = SataPacketHeader {
215 packet_data_len: 0,
216 packet_id: 0,
217 flags: 0,
218 version: 0,
219 timestamp_on_host: 0,
220 pid_on_host: 0,
221 };
222
223 let mut open_options = OpenOptions::new();
224 open_options.read(true).create(false).write(false);
225 let fd = fs
226 .open_file(open_options, &join_many(&base_dir, ["file.txt"]))
227 .await
228 .expect("Failed to open file!");
229
230 let read_request = SataReadFilePacketBody {
231 block_count: 4,
232 block_size: 1,
233 handle: fd,
234 move_to_pointer: MoveToFileLocation::Begin,
235 should_move: false,
236 };
237
238 let response = read_request
239 .handle(&mocked_header, &fs, true)
240 .await
241 .expect("Failed to handle read request!");
242 let mut expected_response = BytesMut::new();
243 expected_response.extend_from_slice(&[0; 0x20]);
245 expected_response.extend_from_slice(&2_u32.to_be_bytes());
247 expected_response.extend_from_slice(&[0x00, 0x00, 0xCD, 0xCD]);
249 assert_eq!(response, expected_response.freeze());
250 }
251}