mod change_mode;
mod change_owner;
mod close_file;
mod close_folder;
mod create_folder;
mod get_info_by_query;
mod open_file;
mod open_folder;
mod ping;
mod read_file;
mod read_folder;
mod remove;
mod rename;
mod rewind_folder;
mod set_file_position;
mod stat_file;
mod write_file;
use crate::{
errors::{CatBridgeError, FSError, NetworkError, NetworkParseError},
fsemul::{
HostFilesystem,
pcfs::errors::{PcfsApiError, SataProtocolError},
},
net::models::{IntoResponse, Response},
};
use bytes::{BufMut, Bytes, BytesMut};
use std::{
fmt::{Debug, Display, Formatter, Result as FmtResult},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
pub use crate::fsemul::pcfs::sata::proto::{
change_mode::*, change_owner::*, close_file::*, close_folder::*, create_folder::*,
get_info_by_query::*, open_file::*, open_folder::*, ping::*, read_file::*, read_folder::*,
remove::*, rename::*, rewind_folder::*, set_file_position::*, stat_file::*, write_file::*,
};
pub const DEFAULT_PCFS_VERSION: u32 = 0x0200_0600;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SataPacketHeader {
packet_data_len: u32,
packet_id: u32,
flags: u32,
version: u32,
timestamp_on_host: u32,
pid_on_host: u32,
}
impl SataPacketHeader {
#[must_use]
pub const fn new(packet_id: u32) -> Self {
Self {
packet_data_len: 0,
packet_id,
flags: 0,
version: DEFAULT_PCFS_VERSION,
timestamp_on_host: 0,
pid_on_host: 0,
}
}
#[must_use]
pub const fn data_len(&self) -> u32 {
self.packet_data_len
}
pub const fn set_data_len(&mut self, len: u32) {
self.packet_data_len = len;
}
#[must_use]
pub const fn id(&self) -> u32 {
self.packet_id
}
pub const fn set_id(&mut self, new_id: u32) {
self.packet_id = new_id;
}
#[must_use]
pub const fn flags(&self) -> u32 {
self.flags
}
pub const fn set_flags(&mut self, new_flags: u32) {
self.flags = new_flags;
}
#[must_use]
pub const fn version(&self) -> u32 {
self.version
}
#[must_use]
pub const fn raw_timestamp_on_host(&self) -> u32 {
self.timestamp_on_host
}
#[must_use]
pub fn host_timestamp(&self) -> SystemTime {
UNIX_EPOCH
.checked_add(Duration::from_secs(u64::from(self.timestamp_on_host)))
.unwrap_or_else(SystemTime::now)
}
#[must_use]
pub const fn host_pid(&self) -> u32 {
self.pid_on_host
}
pub fn ensure_not_from_host(&self) -> Result<(), SataProtocolError> {
if self.pid_on_host != 0 || self.timestamp_on_host != 0 {
return Err(SataProtocolError::NonHostSetHostOnlyHeaderFields(
self.timestamp_on_host,
self.pid_on_host,
));
}
Ok(())
}
}
impl From<&SataPacketHeader> for Bytes {
fn from(value: &SataPacketHeader) -> Self {
let mut buff = BytesMut::with_capacity(0x20);
buff.put_u32(value.packet_data_len);
buff.put_u32(value.packet_id);
buff.put_u32(value.flags);
buff.put_u32(value.version);
buff.put_u32(value.timestamp_on_host);
buff.put_u32(value.pid_on_host);
buff.extend([0; 8]);
buff.freeze()
}
}
impl From<SataPacketHeader> for Bytes {
fn from(value: SataPacketHeader) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SataPacketHeader {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x20 {
return Err(NetworkParseError::FieldNotLongEnough(
"SataPacket",
"Header",
0x20,
value.len(),
value,
));
}
if value.len() > 0x20 {
return Err(NetworkParseError::UnexpectedTrailer(
"SataPacketHeader",
value.slice(0x20..),
));
}
let packet_data_len = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
let packet_id = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
let flags = u32::from_be_bytes([value[8], value[9], value[10], value[11]]);
let version = u32::from_be_bytes([value[12], value[13], value[14], value[15]]);
let timestamp_on_host = u32::from_be_bytes([value[16], value[17], value[18], value[19]]);
let pid_on_host = u32::from_be_bytes([value[20], value[21], value[22], value[23]]);
let should_be_padding = [
value[24], value[25], value[26], value[27], value[28], value[29], value[30], value[31],
];
if should_be_padding != [0x0; 8] {
return Err(SataProtocolError::HeaderBadPadding(should_be_padding).into());
}
Ok(Self {
packet_data_len,
packet_id,
flags,
version,
timestamp_on_host,
pid_on_host,
})
}
}
const SATA_PACKET_HEADER_FIELDS: &[NamedField<'static>] = &[
NamedField::new("data_len"),
NamedField::new("id"),
NamedField::new("flags"),
NamedField::new("version"),
NamedField::new("host_timestamp"),
NamedField::new("host_pid"),
];
impl Structable for SataPacketHeader {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SataPacketHeader", Fields::Named(SATA_PACKET_HEADER_FIELDS))
}
}
impl Valuable for SataPacketHeader {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SATA_PACKET_HEADER_FIELDS,
&[
Valuable::as_value(&self.packet_data_len),
Valuable::as_value(&self.packet_id),
Valuable::as_value(&self.flags),
Valuable::as_value(&self.version),
Valuable::as_value(&self.timestamp_on_host),
Valuable::as_value(&self.pid_on_host),
],
));
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SataCommandInfo {
user: (u32, u32),
capabilities: (u32, u32),
command: u32,
}
impl SataCommandInfo {
#[must_use]
pub const fn new(user: (u32, u32), capabilities: (u32, u32), command: u32) -> Self {
Self {
user,
capabilities,
command,
}
}
#[must_use]
pub const fn user(&self) -> (u32, u32) {
self.user
}
pub const fn set_user(&mut self, new: (u32, u32)) {
self.user = new;
}
#[must_use]
pub const fn capabilities(&self) -> (u32, u32) {
self.capabilities
}
pub const fn set_capabilities(&mut self, new: (u32, u32)) {
self.capabilities = new;
}
#[must_use]
pub const fn command(&self) -> u32 {
self.command
}
pub const fn set_command(&mut self, new: u32) {
self.command = new;
}
}
impl From<&SataCommandInfo> for Bytes {
fn from(value: &SataCommandInfo) -> Self {
let mut buff = BytesMut::with_capacity(0x14);
buff.put_u32(value.user.0);
buff.put_u32(value.user.1);
buff.put_u32(value.capabilities.0);
buff.put_u32(value.capabilities.1);
buff.put_u32(value.command);
buff.freeze()
}
}
impl From<SataCommandInfo> for Bytes {
fn from(value: SataCommandInfo) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SataCommandInfo {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x14 {
return Err(NetworkParseError::FieldNotLongEnough(
"SataPacket",
"CommandInfo",
0x14,
value.len(),
value,
));
}
if value.len() > 0x14 {
return Err(NetworkParseError::UnexpectedTrailer(
"SataPacketCommandInfo",
value.slice(0x14..),
));
}
let user0 = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
let user1 = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
let cap0 = u32::from_le_bytes([value[8], value[9], value[10], value[11]]);
let cap1 = u32::from_le_bytes([value[12], value[13], value[14], value[15]]);
let cmd = u32::from_be_bytes([value[16], value[17], value[18], value[19]]);
Ok(Self {
user: (user0, user1),
capabilities: (cap0, cap1),
command: cmd,
})
}
}
const SATA_COMMAND_INFO_FIELDS: &[NamedField<'static>] = &[
NamedField::new("user.0"),
NamedField::new("user.1"),
NamedField::new("cap.0"),
NamedField::new("cap.1"),
NamedField::new("cap.2"),
NamedField::new("cap.3"),
NamedField::new("cap.4"),
NamedField::new("command"),
];
impl Structable for SataCommandInfo {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SataCommandInfo", Fields::Named(SATA_COMMAND_INFO_FIELDS))
}
}
impl Valuable for SataCommandInfo {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SATA_COMMAND_INFO_FIELDS,
&[
Valuable::as_value(&self.user.0),
Valuable::as_value(&self.user.1),
Valuable::as_value(&(self.capabilities.0 & 0xFF)),
Valuable::as_value(&((self.capabilities.0 >> 8) & 0xFF)),
Valuable::as_value(&((self.capabilities.1) & 0xFF)),
Valuable::as_value(&((self.capabilities.0 >> 16) & 0xFF)),
Valuable::as_value(&((self.capabilities.0 >> 24) & 0xFF)),
Valuable::as_value(&self.command),
],
));
}
}
impl Display for SataCommandInfo {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
write!(
fmt,
"Cmd: {}, user: {} {}, cap: {} {} {} {} {}",
self.command,
self.user.0,
self.user.1,
self.capabilities.0 & 0xFF,
(self.capabilities.0 >> 8) & 0xFF,
self.capabilities.1 & 0xFF,
(self.capabilities.0 >> 16) & 0xFF,
(self.capabilities.0 >> 24) & 0xFF,
)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Valuable)]
pub enum MoveToFileLocation {
Begin,
Current,
End,
}
impl MoveToFileLocation {
pub async fn do_move(
&self,
host_fs: &HostFilesystem,
handle: i32,
stream_id: Option<u64>,
) -> Result<(), FSError> {
match *self {
MoveToFileLocation::Begin => {
host_fs.seek_file(handle, true, stream_id).await?;
}
MoveToFileLocation::Current => {
}
MoveToFileLocation::End => {
host_fs.seek_file(handle, false, stream_id).await?;
}
}
Ok(())
}
}
impl From<&MoveToFileLocation> for u32 {
fn from(value: &MoveToFileLocation) -> u32 {
match *value {
MoveToFileLocation::Begin => 0,
MoveToFileLocation::Current => 1,
MoveToFileLocation::End => 2,
}
}
}
impl From<MoveToFileLocation> for u32 {
fn from(value: MoveToFileLocation) -> u32 {
Self::from(&value)
}
}
impl TryFrom<u32> for MoveToFileLocation {
type Error = SataProtocolError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Begin),
1 => Ok(Self::Current),
2 => Ok(Self::End),
val => Err(SataProtocolError::UnknownFileLocation(val)),
}
}
}
impl Display for MoveToFileLocation {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Begin => write!(fmt, "Begin"),
Self::Current => write!(fmt, "Nowhere"),
Self::End => write!(fmt, "End"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Valuable)]
#[repr(transparent)]
pub struct SataCapabilitiesFlags(pub u32);
bitflags::bitflags! {
impl SataCapabilitiesFlags: u32 {
const FAST_FILE_IO_SUPPORTED = 0b0000_0010;
const COMBINED_SEND_RECV_SUPPORTED = 0b0000_0100;
}
}
#[derive(Clone, Debug)]
pub struct SataRequest<InnerTy: Debug> {
header: SataPacketHeader,
command_info: SataCommandInfo,
body: InnerTy,
}
impl<InnerTy: Debug> SataRequest<InnerTy> {
#[must_use]
pub const fn new(
header: SataPacketHeader,
command_info: SataCommandInfo,
body: InnerTy,
) -> Self {
Self {
header,
command_info,
body,
}
}
#[must_use]
pub const fn header(&self) -> &SataPacketHeader {
&self.header
}
#[must_use]
pub const fn header_mut(&mut self) -> &mut SataPacketHeader {
&mut self.header
}
#[must_use]
pub const fn command_info(&self) -> &SataCommandInfo {
&self.command_info
}
#[must_use]
pub const fn command_info_mut(&mut self) -> &mut SataCommandInfo {
&mut self.command_info
}
#[must_use]
pub const fn body(&self) -> &InnerTy {
&self.body
}
#[must_use]
pub const fn body_mut(&mut self) -> &mut InnerTy {
&mut self.body
}
#[must_use]
pub fn into_parts(self) -> (SataPacketHeader, SataCommandInfo, InnerTy) {
(self.header, self.command_info, self.body)
}
pub fn parse_opaque(mut body: Bytes) -> Result<SataRequest<Bytes>, NetworkParseError> {
if body.len() < 0x34 {
return Err(NetworkParseError::NotEnoughData(
"SataRequest",
0x34,
body.len(),
body,
));
}
let header = SataPacketHeader::try_from(body.split_to(0x20))?;
let ci = SataCommandInfo::try_from(body.split_to(0x14))?;
Ok(SataRequest {
header,
command_info: ci,
body,
})
}
}
impl<ErrorTy, InnerTy: Debug> TryFrom<Bytes> for SataRequest<InnerTy>
where
InnerTy: TryFrom<Bytes, Error = ErrorTy>,
ErrorTy: Into<NetworkParseError>,
{
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
let (header, ci, bytes) = Self::parse_opaque(value)?.into_parts();
let body = InnerTy::try_from(bytes).map_err(Into::into)?;
Ok(Self {
header,
command_info: ci,
body,
})
}
}
impl<InnerTy: Debug> From<SataRequest<InnerTy>> for Bytes
where
InnerTy: Into<Bytes>,
{
fn from(value: SataRequest<InnerTy>) -> Self {
let body = value.body.into();
let mut packet = BytesMut::with_capacity(0x34 + body.len());
packet.extend(Bytes::from(&value.header));
packet.extend(Bytes::from(&value.command_info));
packet.extend(body);
packet.freeze()
}
}
const SATA_REQUEST_FIELDS: &[NamedField<'static>] = &[
NamedField::new("header"),
NamedField::new("command_info"),
NamedField::new("body"),
];
impl<InnerTy: Debug> Structable for SataRequest<InnerTy> {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SataRequest", Fields::Named(SATA_REQUEST_FIELDS))
}
}
impl<InnerTy: Debug> Valuable for SataRequest<InnerTy> {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SATA_REQUEST_FIELDS,
&[
Valuable::as_value(&self.header),
Valuable::as_value(&self.command_info),
Valuable::as_value(&format!("{:?}", self.body)),
],
));
}
}
#[derive(Debug)]
pub struct SataResponse<InnerTy: Debug> {
header: SataPacketHeader,
flags: u32,
pid: u32,
force_zero_version: bool,
body: InnerTy,
}
impl<InnerTy: Debug> SataResponse<InnerTy> {
#[must_use]
pub const fn new(pid: u32, header: SataPacketHeader, body: InnerTy) -> Self {
Self {
header,
flags: 0,
pid,
body,
force_zero_version: false,
}
}
#[must_use]
pub const fn new_force_zero_version(pid: u32, header: SataPacketHeader, body: InnerTy) -> Self {
Self {
header,
flags: 0,
pid,
body,
force_zero_version: true,
}
}
#[must_use]
pub const fn new_with_flags(
pid: u32,
header: SataPacketHeader,
flags: u32,
body: InnerTy,
) -> Self {
Self {
header,
flags,
pid,
body,
force_zero_version: false,
}
}
#[must_use]
pub const fn from_existing(header: SataPacketHeader, body: InnerTy) -> Self {
let flags = header.flags();
let host_pid = header.host_pid();
Self {
header,
flags,
pid: host_pid,
body,
force_zero_version: false,
}
}
#[must_use]
pub const fn header(&self) -> &SataPacketHeader {
&self.header
}
#[must_use]
pub const fn body(&self) -> &InnerTy {
&self.body
}
#[must_use]
pub fn take_body(self) -> InnerTy {
self.body
}
#[must_use]
pub fn to_parts(self) -> (SataPacketHeader, InnerTy) {
(self.header, self.body)
}
pub fn parse_opaque(mut body: Bytes) -> Result<SataResponse<Bytes>, NetworkParseError> {
if body.len() < 0x20 {
return Err(NetworkParseError::NotEnoughData(
"SataResponse",
0x20,
body.len(),
body,
));
}
let header = SataPacketHeader::try_from(body.split_to(0x20))?;
Ok(SataResponse::from_existing(header, body))
}
}
impl<ErrorTy: Into<NetworkError>, InnerTy: Debug> TryFrom<Bytes> for SataResponse<InnerTy>
where
InnerTy: TryFrom<Bytes, Error = ErrorTy>,
{
type Error = NetworkError;
fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x20 {
return Err(
NetworkParseError::NotEnoughData("SataResponse", 0x20, value.len(), value).into(),
);
}
let real_body = value.split_off(0x20_usize);
let header = SataPacketHeader::try_from(value)?;
let flags = header.flags();
let pid = header.host_pid();
let body = InnerTy::try_from(real_body).map_err(Into::into)?;
Ok(Self {
header,
flags,
pid,
body,
force_zero_version: false,
})
}
}
impl<InnerTy: Debug> TryFrom<SataResponse<InnerTy>> for Bytes
where
InnerTy: Into<Bytes>,
{
type Error = PcfsApiError;
fn try_from(value: SataResponse<InnerTy>) -> Result<Self, Self::Error> {
let body_as_bytes = value.body.into();
let mut new_buff = BytesMut::with_capacity(0x20 + body_as_bytes.len());
new_buff.put_u32(
u32::try_from(body_as_bytes.len())
.map_err(|_| PcfsApiError::PacketTooLargeForSata(body_as_bytes.len()))?,
);
new_buff.put_u32(value.header.id());
new_buff.put_u32(value.flags);
if value.force_zero_version {
new_buff.put_u32(0);
} else {
new_buff.put_u32(if value.header.version() != 0 {
value.header.version()
} else {
DEFAULT_PCFS_VERSION
});
}
new_buff.put_u32(
u32::try_from(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs()
.rem_euclid(u64::from(u32::MAX)),
)
.unwrap_or(u32::MAX),
);
new_buff.put_u32(value.pid);
new_buff.extend([0; 8]);
new_buff.extend(body_as_bytes);
Ok(new_buff.freeze())
}
}
impl<InnerTy: Debug> IntoResponse for SataResponse<InnerTy>
where
InnerTy: Into<Bytes>,
{
fn to_response(self) -> Result<Response, CatBridgeError> {
Ok(Response::new_with_body(self.try_into()?))
}
}
const SATA_RESPONSE_FIELDS: &[NamedField<'static>] = &[
NamedField::new("header"),
NamedField::new("pid"),
NamedField::new("flags"),
NamedField::new("body"),
];
impl<InnerTy: Debug> Structable for SataResponse<InnerTy> {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SataResponse", Fields::Named(SATA_RESPONSE_FIELDS))
}
}
impl<InnerTy: Debug> Valuable for SataResponse<InnerTy> {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SATA_RESPONSE_FIELDS,
&[
Valuable::as_value(&self.header),
Valuable::as_value(&self.pid),
Valuable::as_value(&self.flags),
Valuable::as_value(&format!("{:?}", self.body)),
],
));
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Valuable)]
#[repr(transparent)]
pub struct SataResultCode(pub u32);
impl SataResultCode {
#[must_use]
pub const fn success() -> Self {
Self(0)
}
#[must_use]
pub const fn error(code: u32) -> Self {
Self(code)
}
}
impl From<&SataResultCode> for Bytes {
fn from(value: &SataResultCode) -> Self {
let mut buff = BytesMut::with_capacity(4);
buff.put_u32(value.0);
buff.freeze()
}
}
impl From<SataResultCode> for Bytes {
fn from(value: SataResultCode) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SataResultCode {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x4 {
return Err(NetworkParseError::FieldNotLongEnough(
"SataPacket",
"ResultCode",
0x4,
value.len(),
value,
));
}
if value.len() > 0x4 {
return Err(NetworkParseError::UnexpectedTrailer(
"SataResultCode",
value.slice(0x4..),
));
}
let rc = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
Ok(Self(rc))
}
}
impl Display for SataResultCode {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
if self.0 == 0 {
write!(fmt, "Success")
} else {
write!(fmt, "Failure ({:02x})", self.0)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
pub struct SataFileDescriptorResult {
file_descriptor: Result<i32, u32>,
}
impl SataFileDescriptorResult {
#[must_use]
pub const fn success(fd: i32) -> Self {
Self {
file_descriptor: Ok(fd),
}
}
#[must_use]
pub const fn error(error_code: u32) -> Self {
Self {
file_descriptor: Err(error_code),
}
}
pub const fn result(&self) -> Result<i32, u32> {
self.file_descriptor
}
}
impl From<&SataFileDescriptorResult> for Bytes {
fn from(value: &SataFileDescriptorResult) -> Self {
let mut response = BytesMut::with_capacity(8);
match value.file_descriptor {
Ok(fd) => {
response.put_u32(0);
response.put_i32(fd);
}
Err(code) => {
response.put_u32(code);
response.put_i32(i32::from_be_bytes([0xFF, 0xFF, 0xFF, 0xFF]));
}
}
response.freeze()
}
}
impl From<SataFileDescriptorResult> for Bytes {
fn from(value: SataFileDescriptorResult) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SataFileDescriptorResult {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x8 {
return Err(NetworkParseError::FieldNotLongEnough(
"SataPacket",
"FileDescriptorResult",
0x8,
value.len(),
value,
));
}
if value.len() > 0x8 {
return Err(NetworkParseError::UnexpectedTrailer(
"SataFileDescriptorResult",
value.slice(0x8..),
));
}
let rc = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
let fd = i32::from_be_bytes([value[4], value[5], value[6], value[7]]);
if rc == 0 {
Ok(Self::success(fd))
} else {
Ok(Self::error(rc))
}
}
}
impl Display for SataFileDescriptorResult {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
match self.file_descriptor {
Ok(fd) => write!(fmt, "Success ({fd})"),
Err(rc) => write!(fmt, "Failure ({rc:02x})"),
}
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
pub fn move_to_file_location_conversions() {
for mtfl in vec![
MoveToFileLocation::Begin,
MoveToFileLocation::Current,
MoveToFileLocation::End,
] {
assert_eq!(
mtfl,
MoveToFileLocation::try_from(u32::from(mtfl))
.expect("MTFL turned into u32 could not be parsed"),
"MoveToFileLocation wasn't the same after being converted back n forth!",
);
}
}
fn parse_sata_request<
ErrorTy: Into<NetworkParseError>,
BodyTy: Debug + TryFrom<Bytes, Error = ErrorTy>,
>(
body: Bytes,
) -> (SataPacketHeader, SataCommandInfo, BodyTy) {
SataRequest::try_from(body)
.expect("Failed to parse sata request!")
.into_parts()
}
#[test]
pub fn decode_real_ping_packet() {
let (header, command_info, _body) =
parse_sata_request::<_, SataPingPacketBody>(Bytes::from(vec![
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
0x51, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
]));
assert_eq!(header.data_len(), 0x14);
assert_eq!(header.id(), 0);
assert_eq!(
header.flags(),
(SataCapabilitiesFlags::FAST_FILE_IO_SUPPORTED
| SataCapabilitiesFlags::COMBINED_SEND_RECV_SUPPORTED)
.0
);
assert_eq!(header.host_pid(), 0);
assert_eq!(header.raw_timestamp_on_host(), 0);
assert_eq!(header.version(), 0);
assert_eq!(command_info.command(), 0x14);
}
#[test]
pub fn decode_real_query_info_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataGetInfoByQueryPacketBody>(Bytes::from(vec![
0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2f, 0x25, 0x53, 0x4c,
0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x73, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
]));
assert_eq!(command_info.command(), 0x10);
assert_eq!(body.query_type(), SataQueryType::FileDetails);
assert_eq!(body.path(), "/%SLC_EMU_DIR/sys");
}
#[test]
pub fn decode_real_change_mode_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataChangeModePacketBody>(Bytes::from(vec![
0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x2f, 0x25, 0x53, 0x4c,
0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x73, 0x2f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]));
assert_eq!(command_info.command(), 0x13);
assert_eq!(body.path(), "/%SLC_EMU_DIR/sys/config");
assert!(!body.will_set_write_mode());
}
#[test]
pub fn decode_open_file_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataOpenFilePacketBody>(Bytes::from(vec![
0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x72, 0x00, 0x53, 0x4c,
0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x2f, 0x25,
0x53, 0x4c, 0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79,
0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]));
assert_eq!(command_info.command(), 0x5);
assert_eq!(body.mode(), "r");
assert_eq!(body.path(), "/%SLC_EMU_DIR/sys/config/system.xml");
}
#[test]
pub fn decode_read_file_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataReadFilePacketBody>(Bytes::from(vec![
0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x05, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
]));
assert_eq!(command_info.command(), 0x6);
assert_eq!(body.block_count(), 1);
assert_eq!(body.block_size(), 0x51B);
assert_eq!(body.file_descriptor(), 2);
assert_eq!(body.move_to_pointer(), MoveToFileLocation::Begin);
assert_eq!(body.should_move(), false);
}
#[test]
pub fn decode_close_file_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataCloseFilePacketBody>(Bytes::from(vec![
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02,
]));
assert_eq!(command_info.command(), 0xD);
assert_eq!(body.file_descriptor(), 2);
}
#[test]
pub fn decode_open_folder_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataOpenFolderPacketBody>(Bytes::from(vec![
0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x25, 0x4d, 0x4c,
0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x75, 0x73, 0x72, 0x2f,
0x74, 0x6d, 0x70, 0x00, 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x52, 0x2f, 0x73, 0x79,
0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
]));
assert_eq!(command_info.command(), 0x1);
assert_eq!(body.path(), "/%MLC_EMU_DIR/usr/tmp");
}
#[test]
pub fn decode_read_folder_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataReadFolderPacketBody>(Bytes::from(vec![
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
]));
assert_eq!(command_info.command(), 0x2);
assert_eq!(body.file_descriptor(), 1);
}
#[test]
pub fn decode_close_folder_packet() {
let (_header, command_info, body) =
parse_sata_request::<_, SataCloseFolderPacketBody>(Bytes::from(vec![
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
]));
assert_eq!(command_info.command(), 0x04);
assert_eq!(body.file_descriptor(), 1);
}
}