cat_dev/fsemul/pcfs/sata_proto/
close_folder.rs1use crate::{
6 errors::NetworkParseError,
7 fsemul::{
8 host_filesystem::HostFilesystem,
9 pcfs::{
10 errors::PCFSApiError,
11 sata_proto::{construct_sata_response, SataPacketHeader},
12 },
13 },
14};
15use bytes::{Buf, Bytes, BytesMut};
16use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct SataCloseFolderPacketBody {
21 file_descriptor: i32,
22}
23
24impl SataCloseFolderPacketBody {
25 #[must_use]
26 pub const fn file_descriptor(&self) -> i32 {
27 self.file_descriptor
28 }
29
30 pub async fn handle(
36 &self,
37 request_header: &SataPacketHeader,
38 host_filesystem: &HostFilesystem,
39 ) -> Result<Bytes, PCFSApiError> {
40 host_filesystem.close_folder(self.file_descriptor).await;
41
42 construct_sata_response(request_header, 0, BytesMut::zeroed(4).freeze())
43 }
44}
45
46impl TryFrom<Bytes> for SataCloseFolderPacketBody {
47 type Error = NetworkParseError;
48
49 fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
50 if value.len() < 0x4 {
51 return Err(NetworkParseError::FieldNotLongEnough(
52 "SataCloseFolder",
53 "Body",
54 0x4,
55 value.len(),
56 value,
57 ));
58 }
59 if value.len() > 0x4 {
60 return Err(NetworkParseError::UnexpectedTrailer(
61 "SataCloseFolder",
62 value.slice(0x4..),
63 ));
64 }
65
66 let fd = value.get_i32();
67
68 Ok(Self {
69 file_descriptor: fd,
70 })
71 }
72}
73
74const SATA_CLOSE_FOLDER_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("fd")];
75
76impl Structable for SataCloseFolderPacketBody {
77 fn definition(&self) -> StructDef<'_> {
78 StructDef::new_static(
79 "SataCloseFolderPacketBody",
80 Fields::Named(SATA_CLOSE_FOLDER_PACKET_BODY_FIELDS),
81 )
82 }
83}
84
85impl Valuable for SataCloseFolderPacketBody {
86 fn as_value(&self) -> Value<'_> {
87 Value::Structable(self)
88 }
89
90 fn visit(&self, visitor: &mut dyn Visit) {
91 visitor.visit_named_fields(&NamedValues::new(
92 SATA_CLOSE_FOLDER_PACKET_BODY_FIELDS,
93 &[Valuable::as_value(&self.file_descriptor)],
94 ));
95 }
96}
97
98#[cfg(test)]
99mod unit_tests {
100 use super::*;
101 use crate::fsemul::host_filesystem::test_helpers::{
102 create_temporary_host_filesystem, join_many,
103 };
104 use tokio::fs::OpenOptions;
105
106 #[tokio::test]
107 pub async fn simple_ffio_read_file_request() {
108 let (tempdir, fs) = create_temporary_host_filesystem().await;
109
110 let base_dir = join_many(tempdir.path(), ["data", "slc", "to-query"]);
111 tokio::fs::create_dir(&base_dir)
112 .await
113 .expect("Failed to create temporary directory for test!");
114 tokio::fs::write(join_many(&base_dir, ["file.txt"]), vec![0; 2])
115 .await
116 .expect("Failed to write test file!");
117 let mocked_header = SataPacketHeader {
118 packet_data_len: 0,
119 packet_id: 0,
120 flags: 0,
121 version: 0,
122 timestamp_on_host: 0,
123 pid_on_host: 0,
124 };
125
126 let mut open_options = OpenOptions::new();
127 open_options.read(true).create(false).write(false);
128 let fd = fs
129 .open_file(open_options, &join_many(&base_dir, ["file.txt"]))
130 .await
131 .expect("Failed to open file!");
132
133 let close_request = SataCloseFolderPacketBody {
134 file_descriptor: fd,
135 };
136
137 let response = close_request
138 .handle(&mocked_header, &fs)
139 .await
140 .expect("Failed to handle read request!");
141 assert_eq!(&response[0x20..], &[0; 4]);
142 }
143}