cat_dev/fsemul/pcfs/sata/proto/
get_info_by_query.rs1use crate::{
9 errors::NetworkParseError,
10 fsemul::{
11 HostFilesystem,
12 pcfs::errors::{PcfsApiError, SataProtocolError},
13 },
14};
15use bytes::{Buf, BufMut, Bytes, BytesMut};
16use std::{
17 ffi::CStr,
18 fs::Metadata,
19 path::PathBuf,
20 sync::LazyLock,
21 time::{Duration, SystemTime},
22};
23use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
24
25static FAT_TIMESTAMP_START: LazyLock<SystemTime> = LazyLock::new(|| {
27 SystemTime::UNIX_EPOCH
28 .checked_add(Duration::from_secs(315_540_000))
29 .expect("Failed to get timestamp for 1980! required!")
30});
31
32#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct SataGetInfoByQueryPacketBody {
38 path: String,
49 typ: SataQueryType,
51}
52
53impl SataGetInfoByQueryPacketBody {
54 pub fn new(path: String, query_type: SataQueryType) -> Result<Self, PcfsApiError> {
65 if path.len() > 511 {
66 return Err(PcfsApiError::PathTooLong(path));
67 }
68
69 Ok(Self {
70 path,
71 typ: query_type,
72 })
73 }
74
75 #[must_use]
76 pub const fn query_type(&self) -> SataQueryType {
77 self.typ
78 }
79
80 pub const fn set_query_type(&mut self, new_type: SataQueryType) {
81 self.typ = new_type;
82 }
83
84 #[must_use]
85 pub fn path(&self) -> &str {
86 self.path.as_str()
87 }
88
89 pub fn set_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
100 if new_path.len() > 511 {
101 return Err(PcfsApiError::PathTooLong(new_path));
102 }
103
104 self.path = new_path;
105 Ok(())
106 }
107}
108
109impl From<&SataGetInfoByQueryPacketBody> for Bytes {
110 fn from(value: &SataGetInfoByQueryPacketBody) -> Self {
111 let mut result = BytesMut::with_capacity(0x204);
112 result.extend_from_slice(value.path.as_bytes());
113 result.extend(BytesMut::zeroed(0x200 - result.len()));
116 result.put_u32(u32::from(value.typ));
117 result.freeze()
118 }
119}
120
121impl From<SataGetInfoByQueryPacketBody> for Bytes {
122 fn from(value: SataGetInfoByQueryPacketBody) -> Self {
123 Self::from(&value)
124 }
125}
126
127impl TryFrom<Bytes> for SataGetInfoByQueryPacketBody {
128 type Error = NetworkParseError;
129
130 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
131 if value.len() < 0x204 {
132 return Err(NetworkParseError::FieldNotLongEnough(
133 "SataGetInfoByQuery",
134 "Body",
135 0x204,
136 value.len(),
137 value,
138 ));
139 }
140 if value.len() > 0x204 {
141 return Err(NetworkParseError::UnexpectedTrailer(
142 "SataGetInfoByQueryBody",
143 value.slice(0x204..),
144 ));
145 }
146
147 let (path_bytes, num) = value.split_at(0x200);
148 let path_c_str =
149 CStr::from_bytes_until_nul(path_bytes).map_err(NetworkParseError::BadCString)?;
150 let query_type = u32::from_be_bytes([num[0], num[1], num[2], num[3]]);
151 let final_path = path_c_str.to_str()?.to_owned();
152
153 Ok(Self {
154 path: final_path,
155 typ: SataQueryType::try_from(query_type)?,
156 })
157 }
158}
159
160const SATA_GET_INFO_BY_QUERY_PACKET_BODY_FIELDS: &[NamedField<'static>] =
161 &[NamedField::new("path"), NamedField::new("type")];
162
163impl Structable for SataGetInfoByQueryPacketBody {
164 fn definition(&self) -> StructDef<'_> {
165 StructDef::new_static(
166 "SataGetInfoByQueryPacketBody",
167 Fields::Named(SATA_GET_INFO_BY_QUERY_PACKET_BODY_FIELDS),
168 )
169 }
170}
171
172impl Valuable for SataGetInfoByQueryPacketBody {
173 fn as_value(&self) -> Value<'_> {
174 Value::Structable(self)
175 }
176
177 fn visit(&self, visitor: &mut dyn Visit) {
178 visitor.visit_named_fields(&NamedValues::new(
179 SATA_GET_INFO_BY_QUERY_PACKET_BODY_FIELDS,
180 &[
181 Valuable::as_value(&self.path),
182 Valuable::as_value(&self.typ),
183 ],
184 ));
185 }
186}
187
188#[derive(Copy, Clone, Debug, PartialEq, Eq, Valuable)]
190pub enum SataQueryType {
191 FreeDiskSpace,
193 SizeOfFolder,
195 FileCount,
197 FileDetails,
199}
200
201impl From<SataQueryType> for u32 {
202 fn from(value: SataQueryType) -> Self {
203 match value {
204 SataQueryType::FreeDiskSpace => 0,
205 SataQueryType::SizeOfFolder => 1,
206 SataQueryType::FileCount => 2,
207 SataQueryType::FileDetails => 5,
208 }
209 }
210}
211
212impl TryFrom<u32> for SataQueryType {
213 type Error = SataProtocolError;
214
215 fn try_from(value: u32) -> Result<Self, Self::Error> {
216 match value {
217 0 => Ok(Self::FreeDiskSpace),
218 1 => Ok(Self::SizeOfFolder),
219 2 => Ok(Self::FileCount),
220 5 => Ok(Self::FileDetails),
221 val => Err(SataProtocolError::UnknownGetInfoQueryType(val)),
222 }
223 }
224}
225
226#[derive(Clone, Debug, Valuable, PartialEq, Eq)]
233pub enum SataQueryResponse {
234 ErrorCode(u32),
236 SmallSize(u32),
242 LargeSize(u64),
249 FDInfo(SataFDInfo),
255}
256
257impl SataQueryResponse {
258 pub fn try_from_small(mut value: Bytes) -> Result<Self, NetworkParseError> {
266 let rc = value.get_u32();
267 if rc != 0 {
268 return Err(NetworkParseError::ErrorCode(rc));
269 }
270
271 let smol = value.get_u32();
272
273 Ok(Self::SmallSize(smol))
274 }
275
276 pub fn try_from_large(mut value: Bytes) -> Result<Self, NetworkParseError> {
284 let rc = value.get_u32();
285 if rc != 0 {
286 return Err(NetworkParseError::ErrorCode(rc));
287 }
288
289 let larg = value.get_u64();
290
291 Ok(Self::LargeSize(larg))
292 }
293
294 pub fn try_from_fd_info(mut value: Bytes) -> Result<Self, NetworkParseError> {
302 let rc = value.get_u32();
303 if rc != 0 {
304 return Err(NetworkParseError::ErrorCode(rc));
305 }
306
307 let fd_info = SataFDInfo::try_from(value)?;
308
309 Ok(Self::FDInfo(fd_info))
310 }
311}
312
313impl From<&SataQueryResponse> for Bytes {
314 fn from(value: &SataQueryResponse) -> Self {
315 match value {
316 SataQueryResponse::FDInfo(fd_info) => {
317 let mut buff = BytesMut::with_capacity(88);
318 buff.put_u32(0);
319 buff.extend(Bytes::from(fd_info));
320 buff.freeze()
321 }
322 SataQueryResponse::LargeSize(lorg) => {
323 let mut buff = BytesMut::with_capacity(88);
324 buff.put_u32(0);
325 buff.put_u64(*lorg);
326 buff.extend([0; 76]);
327 buff.freeze()
328 }
329 SataQueryResponse::SmallSize(smol) => {
330 let mut buff = BytesMut::with_capacity(88);
331 buff.put_u32(0);
332 buff.put_u32(*smol);
333 buff.extend([0; 80]);
334 buff.freeze()
335 }
336 SataQueryResponse::ErrorCode(ec) => {
337 let mut buff = BytesMut::with_capacity(88);
338 buff.put_u32(*ec);
339 buff.extend_from_slice(&[0; 84]);
340 buff.freeze()
341 }
342 }
343 }
344}
345
346impl From<SataQueryResponse> for Bytes {
347 fn from(value: SataQueryResponse) -> Self {
348 Self::from(&value)
349 }
350}
351
352#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
353pub struct SataFDInfo {
355 file_or_folder_flags: u32,
357 perms: u32,
359 file_length: u32,
362 created_timestamp: u64,
364 last_updated_timestamp: u64,
366}
367
368impl SataFDInfo {
369 #[must_use]
371 pub async fn get_info(
372 host_filesystem: &HostFilesystem,
373 metadata: &Metadata,
374 path: &PathBuf,
375 ) -> Self {
376 let is_read_only = if metadata.is_dir() {
377 host_filesystem.folder_is_read_only(path).await
378 } else {
379 metadata.permissions().readonly()
380 };
381
382 let file_or_folder_flags = if metadata.is_file() {
383 0x2C00_0000
384 } else {
385 0xAC00_0000
386 };
387 let perms = if is_read_only { 0x444 } else { 0x666 };
388 let file_length = if metadata.is_dir() {
389 0
390 } else {
391 u32::try_from(metadata.len()).unwrap_or(u32::MAX)
392 };
393 let created_timestamp = u64::try_from(
394 metadata
395 .created()
396 .unwrap_or(SystemTime::now())
397 .duration_since(*FAT_TIMESTAMP_START)
398 .unwrap_or(Duration::from_secs(0))
399 .as_millis(),
400 )
401 .unwrap_or(u64::MAX);
402 let updated_timestamp = u64::try_from(
403 metadata
404 .modified()
405 .unwrap_or(SystemTime::now())
406 .duration_since(*FAT_TIMESTAMP_START)
407 .unwrap_or(Duration::from_secs(0))
408 .as_millis(),
409 )
410 .unwrap_or(u64::MAX);
411
412 Self {
413 file_or_folder_flags,
414 perms,
415 file_length,
416 created_timestamp,
417 last_updated_timestamp: updated_timestamp,
418 }
419 }
420
421 #[must_use]
423 pub fn create_fake_info(
424 file_or_folder_flags: u32,
425 perms: u32,
426 file_length: u32,
427 created_timestamp: u64,
428 updated_timestamp: u64,
429 ) -> Self {
430 Self {
431 file_or_folder_flags,
432 perms,
433 file_length,
434 created_timestamp,
435 last_updated_timestamp: updated_timestamp,
436 }
437 }
438
439 #[must_use]
441 pub const fn flags(&self) -> u32 {
442 self.file_or_folder_flags
443 }
444
445 #[must_use]
447 pub const fn exists(&self) -> bool {
448 (self.file_or_folder_flags & 0x2000_0000) != 0
449 }
450
451 #[must_use]
453 pub const fn is_file(&self) -> bool {
454 (self.file_or_folder_flags & 0x8000_0000) == 0
455 }
456
457 #[must_use]
459 pub const fn is_directory(&self) -> bool {
460 !self.is_file()
461 }
462
463 #[must_use]
469 pub const fn permissions(&self) -> u32 {
470 self.perms
471 }
472
473 #[must_use]
475 pub const fn file_size(&self) -> Option<u32> {
476 if self.is_file() {
477 Some(self.file_length)
478 } else {
479 None
480 }
481 }
482
483 #[must_use]
487 pub const fn raw_created_timestamp(&self) -> u64 {
488 self.created_timestamp
489 }
490
491 #[must_use]
495 pub const fn raw_last_updated_timestamp(&self) -> u64 {
496 self.last_updated_timestamp
497 }
498}
499
500impl From<&SataFDInfo> for Bytes {
501 fn from(value: &SataFDInfo) -> Self {
502 let mut buff = BytesMut::with_capacity(84);
503 buff.put_u32(value.file_or_folder_flags);
504 buff.put_u32(value.perms);
505 buff.put_u32(1);
506 buff.put_u32(1);
507 buff.put_u32(value.file_length);
508 buff.put_u32(0);
509 buff.put_u32(0xE8);
510 buff.put_u32(0xDA6F_F000);
511 buff.put_u32(0);
512 buff.put_u64(value.created_timestamp);
513 buff.put_u64(value.last_updated_timestamp);
514 buff.extend_from_slice(&[0; 32]);
515 buff.freeze()
516 }
517}
518
519impl From<SataFDInfo> for Bytes {
520 fn from(value: SataFDInfo) -> Self {
521 Self::from(&value)
522 }
523}
524
525impl TryFrom<Bytes> for SataFDInfo {
526 type Error = NetworkParseError;
527
528 fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
529 if value.len() < 84 {
530 return Err(NetworkParseError::FieldNotLongEnough(
531 "SataFDInfo",
532 "Body",
533 84,
534 value.len(),
535 value,
536 ));
537 }
538 if value.len() > 84 {
539 return Err(NetworkParseError::UnexpectedTrailer(
540 "SataFDInfoBody",
541 value.slice(84..),
542 ));
543 }
544
545 let fd_flags = value.get_u32();
546 let unix_perms = value.get_u32();
547 _ = value.get_u32();
549 _ = value.get_u32();
550 let file_size = value.get_u32();
551 _ = value.get_u32();
553 _ = value.get_u32();
554 _ = value.get_u32();
555 _ = value.get_u32();
556 let created_ts = value.get_u64();
558 let updated_ts = value.get_u64();
559 Ok(Self {
562 file_or_folder_flags: fd_flags,
563 perms: unix_perms,
564 file_length: file_size,
565 created_timestamp: created_ts,
566 last_updated_timestamp: updated_ts,
567 })
568 }
569}
570
571#[cfg(test)]
572mod unit_tests {
573 use super::*;
574
575 #[test]
576 pub fn query_types_to_and_fro() {
577 for qt in vec![
578 SataQueryType::FreeDiskSpace,
579 SataQueryType::SizeOfFolder,
580 SataQueryType::FileCount,
581 SataQueryType::FileDetails,
582 ] {
583 assert_eq!(Ok(qt), SataQueryType::try_from(u32::from(qt)));
584 }
585 }
586}