cat_dev/fsemul/pcfs/sata/proto/
change_owner.rs

1//! Definitions for the `ChangeOwner` packet type, and it's response types.
2//!
3//! For some reason this always responds with an error. Rather than setting an
4//! actual `uid`/`gid`. This is because windows doesn't have the concept of a
5//! uid/gid.
6
7use crate::{errors::NetworkParseError, fsemul::pcfs::errors::PcfsApiError};
8use bytes::{BufMut, Bytes, BytesMut};
9use std::ffi::CStr;
10use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
11
12/// A packet to change the owner of a file.
13///
14/// This will always, literally always fail.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct SataChangeOwnerPacketBody {
17	/// The path to query, note that this is not the 'resolved' path which is
18	/// the path to actual read from.
19	///
20	/// Interpolation has a few known ways of being replaced:
21	///
22	/// - `%MLC_EMU_DIR`: `<cafe_sdk>/data/mlc/`
23	/// - `%SLC_EMU_DIR`: `<cafe_sdk>/data/slc/`
24	/// - `%DISC_EMU_DIR`: `<cafe_sdk>/data/disc/`
25	/// - `%SAVE_EMU_DIR`: `<cafe_sdk>/data/save/`
26	/// - `%NETWORK`: <mounted network share path>
27	path: String,
28	/// The UID to set the owner of this file as.
29	uid: u32,
30	/// The GID to set the owner of this file as.
31	gid: u32,
32}
33
34impl SataChangeOwnerPacketBody {
35	/// Attempt to construct a new change owner packet.
36	///
37	/// ## Errors
38	///
39	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
40	/// but because we need to encode our data as a C-String with a NUL
41	/// terminator we cannot be longer than 511 bytes.
42	///
43	/// Consider using relative/mapped paths if possible when dealing with long
44	/// paths.
45	pub fn new(path: String, uid: u32, gid: u32) -> Result<Self, PcfsApiError> {
46		if path.len() > 511 {
47			return Err(PcfsApiError::PathTooLong(path));
48		}
49
50		Ok(Self { path, uid, gid })
51	}
52
53	#[must_use]
54	pub fn path(&self) -> &str {
55		self.path.as_str()
56	}
57
58	/// Update the path to send in this particular change owner packet.
59	///
60	/// ## Errors
61	///
62	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
63	/// but because we need to encode our data as a C-String with a NUL
64	/// terminator we cannot be longer than 511 bytes.
65	///
66	/// Consider using relative/mapped paths if possible when dealing with long
67	/// paths.
68	pub fn set_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
69		if new_path.len() > 511 {
70			return Err(PcfsApiError::PathTooLong(new_path));
71		}
72
73		self.path = new_path;
74		Ok(())
75	}
76
77	#[must_use]
78	pub const fn uid(&self) -> u32 {
79		self.uid
80	}
81
82	/// Set the user id to send for this packet.
83	pub const fn set_uid(&mut self, new_uid: u32) {
84		self.uid = new_uid;
85	}
86
87	#[must_use]
88	pub const fn gid(&self) -> u32 {
89		self.gid
90	}
91
92	/// Set the group id to send for this packet.
93	pub const fn set_gid(&mut self, new_gid: u32) {
94		self.gid = new_gid;
95	}
96}
97
98impl TryFrom<Bytes> for SataChangeOwnerPacketBody {
99	type Error = NetworkParseError;
100
101	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
102		if value.len() < 0x208 {
103			return Err(NetworkParseError::FieldNotLongEnough(
104				"SataChangeMode",
105				"Body",
106				0x208,
107				value.len(),
108				value,
109			));
110		}
111		if value.len() > 0x208 {
112			return Err(NetworkParseError::UnexpectedTrailer(
113				"SataChangeMode",
114				value.slice(0x208..),
115			));
116		}
117
118		let (path_bytes, num) = value.split_at(0x200);
119		let path_c_str =
120			CStr::from_bytes_until_nul(path_bytes).map_err(NetworkParseError::BadCString)?;
121		let uid = u32::from_be_bytes([num[0], num[1], num[2], num[3]]);
122		let gid = u32::from_be_bytes([num[4], num[5], num[6], num[7]]);
123		let final_path = path_c_str.to_str()?.to_owned();
124
125		Ok(Self {
126			path: final_path,
127			uid,
128			gid,
129		})
130	}
131}
132
133impl From<&SataChangeOwnerPacketBody> for Bytes {
134	fn from(value: &SataChangeOwnerPacketBody) -> Self {
135		let mut result = BytesMut::with_capacity(0x208);
136		result.extend_from_slice(value.path.as_bytes());
137		// These are C Strings so we need a NUL terminator.
138		// Pad with `0`, til we get a full path with a nul terminator.
139		result.extend(BytesMut::zeroed(0x200 - result.len()));
140		result.put_u32(value.uid);
141		result.put_u32(value.gid);
142		result.freeze()
143	}
144}
145
146impl From<SataChangeOwnerPacketBody> for Bytes {
147	fn from(value: SataChangeOwnerPacketBody) -> Self {
148		Self::from(&value)
149	}
150}
151
152const SATA_CHANGE_OWNER_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[
153	NamedField::new("path"),
154	NamedField::new("uid"),
155	NamedField::new("gid"),
156];
157
158impl Structable for SataChangeOwnerPacketBody {
159	fn definition(&self) -> StructDef<'_> {
160		StructDef::new_static(
161			"SataChangeOwnerPacketBody",
162			Fields::Named(SATA_CHANGE_OWNER_PACKET_BODY_FIELDS),
163		)
164	}
165}
166
167impl Valuable for SataChangeOwnerPacketBody {
168	fn as_value(&self) -> Value<'_> {
169		Value::Structable(self)
170	}
171
172	fn visit(&self, visitor: &mut dyn Visit) {
173		visitor.visit_named_fields(&NamedValues::new(
174			SATA_CHANGE_OWNER_PACKET_BODY_FIELDS,
175			&[
176				Valuable::as_value(&self.path),
177				Valuable::as_value(&self.uid),
178				Valuable::as_value(&self.gid),
179			],
180		));
181	}
182}