cat_dev/fsemul/pcfs/sata_proto/
rewind_directory.rs1use crate::{
7 errors::{CatBridgeError, NetworkParseError},
8 fsemul::{
9 host_filesystem::HostFilesystem,
10 pcfs::sata_proto::{construct_sata_response, SataPacketHeader},
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 SataRewindDirPacketBody {
22 file_descriptor: i32,
23}
24
25impl SataRewindDirPacketBody {
26 #[must_use]
27 pub const fn file_descriptor(&self) -> i32 {
28 self.file_descriptor
29 }
30
31 pub async fn handle(
37 &self,
38 request_header: &SataPacketHeader,
39 host_filesystem: &HostFilesystem,
40 ) -> Result<Bytes, CatBridgeError> {
41 if host_filesystem
42 .reverse_directory(self.file_descriptor)
43 .await
44 .is_err()
45 {
46 return Self::construct_error_repsonse(request_header, FS_ERROR);
47 }
48
49 Ok(construct_sata_response(
50 request_header,
51 0,
52 BytesMut::zeroed(4).freeze(),
53 )?)
54 }
55
56 fn construct_error_repsonse(
57 request_header: &SataPacketHeader,
58 error_code: u32,
59 ) -> Result<Bytes, CatBridgeError> {
60 let mut buff = BytesMut::with_capacity(4);
61 buff.put_u32(error_code);
62 Ok(construct_sata_response(request_header, 0, buff.freeze())?)
63 }
64}
65
66impl TryFrom<Bytes> for SataRewindDirPacketBody {
67 type Error = NetworkParseError;
68
69 fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
70 if value.len() < 0x4 {
71 return Err(NetworkParseError::FieldNotLongEnough(
72 "SataRewindDir",
73 "Body",
74 0x4,
75 value.len(),
76 value,
77 ));
78 }
79 if value.len() > 0x4 {
80 return Err(NetworkParseError::UnexpectedTrailer(
81 "SataRewindDir",
82 value.slice(0x4..),
83 ));
84 }
85
86 let fd = value.get_i32();
87
88 Ok(Self {
89 file_descriptor: fd,
90 })
91 }
92}
93
94const SATA_REWIND_DIRECTORY_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("fd")];
95
96impl Structable for SataRewindDirPacketBody {
97 fn definition(&self) -> StructDef<'_> {
98 StructDef::new_static(
99 "SataRewindDirPacketBody",
100 Fields::Named(SATA_REWIND_DIRECTORY_PACKET_BODY_FIELDS),
101 )
102 }
103}
104
105impl Valuable for SataRewindDirPacketBody {
106 fn as_value(&self) -> Value<'_> {
107 Value::Structable(self)
108 }
109
110 fn visit(&self, visitor: &mut dyn Visit) {
111 visitor.visit_named_fields(&NamedValues::new(
112 SATA_REWIND_DIRECTORY_PACKET_BODY_FIELDS,
113 &[Valuable::as_value(&self.file_descriptor)],
114 ));
115 }
116}
117
118#[cfg(test)]
119mod unit_tests {
120 use super::*;
121 use crate::fsemul::host_filesystem::test_helpers::{
122 create_temporary_host_filesystem, join_many,
123 };
124
125 #[tokio::test]
126 pub async fn can_handle_rewind_directory() {
127 let (tempdir, fs) = create_temporary_host_filesystem().await;
128 let mocked_header = SataPacketHeader {
129 packet_data_len: 0,
130 packet_id: 0,
131 flags: 0,
132 version: 0,
133 timestamp_on_host: 0,
134 pid_on_host: 0,
135 };
136
137 let base_dir = join_many(tempdir.path(), ["data", "slc", "to-query"]);
138 tokio::fs::create_dir(&base_dir)
139 .await
140 .expect("Failed to create temporary directory for test!");
141 _ = tokio::fs::File::create(join_many(&base_dir, ["cafe.bat"]))
143 .await
144 .expect("Failed to create file to use!");
145
146 let dfd = fs
147 .open_folder(&base_dir)
148 .await
149 .expect("Failed to open existing directory!");
150 let request = SataRewindDirPacketBody {
151 file_descriptor: dfd,
152 };
153
154 let actual_response = request
156 .handle(&mocked_header, &fs)
157 .await
158 .expect("Failed to get file information back from directory that was opened!");
159 assert_eq!(&actual_response[0x20..], &[0x00, 0x00, 0x00, 0x00]);
160 fs.close_folder(dfd).await;
161 }
162}