1use crate::node::Node;
8#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
9use flex_client::fidl::ControlHandle;
10use flex_client::fidl::ServerEnd;
11
12use flex_fuchsia_io as fio;
13use futures::StreamExt as _;
14use std::sync::Arc;
15use zx_status::Status;
16
17pub use vfs_macros::attribute_query;
18
19#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
21const FS_RIGHTS: fio::OpenFlags = fio::OPEN_RIGHTS;
22
23#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
25pub(crate) fn stricter_or_same_rights(parent_flags: fio::OpenFlags, flags: fio::OpenFlags) -> bool {
26 let parent_rights = parent_flags & FS_RIGHTS;
27 let rights = flags & FS_RIGHTS;
28 return !rights.intersects(!parent_rights);
29}
30
31#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
44pub fn send_on_open_with_error(
45 describe: bool,
46 server_end: ServerEnd<fio::NodeMarker>,
47 status: Status,
48) {
49 if status == Status::OK {
50 panic!("send_on_open_with_error() should not be used to respond with Status::OK");
51 }
52
53 if !describe {
54 let _ = server_end.close_with_epitaph(status);
57 return;
58 }
59
60 let (_, control_handle) = server_end.into_stream_and_control_handle();
61 let _ = control_handle.send_on_open_(status.into_raw(), None);
63 control_handle.shutdown_with_epitaph(status);
64}
65
66pub trait IntoAny: std::any::Any {
72 fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static>;
74}
75
76impl<T: 'static + Send + Sync> IntoAny for T {
77 fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static> {
78 self as Arc<dyn std::any::Any + Send + Sync + 'static>
79 }
80}
81
82pub async fn extended_attributes_sender(
83 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
84 attributes: Vec<Vec<u8>>,
85) {
86 let mut stream = iterator.into_stream();
87
88 let mut chunks = attributes.chunks(fio::MAX_LIST_ATTRIBUTES_CHUNK as usize).peekable();
89
90 while let Some(Ok(fio::ExtendedAttributeIteratorRequest::GetNext { responder })) =
91 stream.next().await
92 {
93 let (chunk, last) = match chunks.next() {
94 Some(chunk) => (chunk, chunks.peek().is_none()),
95 None => (&[][..], true),
96 };
97 #[allow(clippy::unnecessary_lazy_evaluations)]
98 responder.send(Ok((chunk, last))).unwrap_or_else(|_error| {
99 #[cfg(any(test, feature = "use_log"))]
100 log::error!(_error:?; "list extended attributes failed to send a chunk");
101 });
102 if last {
103 break;
104 }
105 }
106}
107
108pub fn encode_extended_attribute_value(
109 value: Vec<u8>,
110) -> Result<fio::ExtendedAttributeValue, Status> {
111 let size = value.len() as u64;
112 if size > fio::MAX_INLINE_ATTRIBUTE_VALUE {
113 #[cfg(target_os = "fuchsia")]
114 {
115 let vmo = fidl::Vmo::create(size)?;
116 vmo.write(&value, 0)?;
117 Ok(fio::ExtendedAttributeValue::Buffer(vmo))
118 }
119 #[cfg(not(target_os = "fuchsia"))]
120 Err(Status::NOT_SUPPORTED)
121 } else {
122 Ok(fio::ExtendedAttributeValue::Bytes(value))
123 }
124}
125
126pub fn decode_extended_attribute_value(
127 value: fio::ExtendedAttributeValue,
128) -> Result<Vec<u8>, Status> {
129 match value {
130 fio::ExtendedAttributeValue::Bytes(val) => Ok(val),
131 #[cfg(target_os = "fuchsia")]
132 fio::ExtendedAttributeValue::Buffer(vmo) => {
133 let length = vmo.get_content_size()?;
134 vmo.read_to_vec(0, length)
135 }
136 #[cfg(not(target_os = "fuchsia"))]
137 fio::ExtendedAttributeValue::Buffer(_) => Err(Status::NOT_SUPPORTED),
138 fio::ExtendedAttributeValue::__SourceBreaking { .. } => Err(Status::NOT_SUPPORTED),
139 }
140}
141
142#[macro_export]
154macro_rules! attributes {
155 ($requested:expr,
156 Mutable {$($mut_a:ident: $mut_v:expr),* $(,)?},
157 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
158 {
159 use $crate::common::attribute_query;
160 fio::NodeAttributes2 {
161 mutable_attributes: fio::MutableNodeAttributes {
162 $($mut_a: if $requested.contains(attribute_query!($mut_a)) {
163 Option::from($mut_v)
164 } else {
165 None
166 },)*
167 ..Default::default()
168 },
169 immutable_attributes: fio::ImmutableNodeAttributes {
170 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
171 Option::from($immut_v)
172 } else {
173 None
174 },)*
175 ..Default::default()
176 }
177 }
178 }
179 )
180}
181
182#[macro_export]
193macro_rules! immutable_attributes {
194 ($requested:expr,
195 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
196 {
197 use $crate::common::attribute_query;
198 fio::NodeAttributes2 {
199 mutable_attributes: Default::default(),
200 immutable_attributes: fio::ImmutableNodeAttributes {
201 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
202 Option::from($immut_v)
203 } else {
204 None
205 },)*
206 ..Default::default()
207 },
208 }
209 }
210 )
211}
212
213#[derive(Debug, PartialEq, Eq)]
215pub enum CreationMode {
216 Never,
218 AllowExisting,
220 Always,
222 UnnamedTemporary,
224 UnlinkableUnnamedTemporary,
226}
227
228pub(crate) fn io1_to_io2_attrs(
230 flags: fio::NodeAttributeFlags,
231 attrs: fio::NodeAttributes,
232) -> fio::MutableNodeAttributes {
233 fio::MutableNodeAttributes {
234 creation_time: flags
235 .contains(fio::NodeAttributeFlags::CREATION_TIME)
236 .then_some(attrs.creation_time),
237 modification_time: flags
238 .contains(fio::NodeAttributeFlags::MODIFICATION_TIME)
239 .then_some(attrs.modification_time),
240 ..Default::default()
241 }
242}
243
244const ALL_IO1_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::PROTOCOLS
246 .union(fio::NodeAttributesQuery::ABILITIES)
247 .union(fio::NodeAttributesQuery::ID)
248 .union(fio::NodeAttributesQuery::CONTENT_SIZE)
249 .union(fio::NodeAttributesQuery::STORAGE_SIZE)
250 .union(fio::NodeAttributesQuery::LINK_COUNT)
251 .union(fio::NodeAttributesQuery::CREATION_TIME)
252 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
253
254const DEFAULT_IO1_ATTRIBUTES: fio::NodeAttributes = fio::NodeAttributes {
256 mode: 0,
257 id: fio::INO_UNKNOWN,
258 content_size: 0,
259 storage_size: 0,
260 link_count: 0,
261 creation_time: 0,
262 modification_time: 0,
263};
264
265const DEFAULT_LINK_COUNT: u64 = 1;
266
267const fn approximate_posix_mode(
271 protocols: Option<fio::NodeProtocolKinds>,
272 abilities: fio::Abilities,
273) -> u32 {
274 let Some(protocols) = protocols else {
275 return 0;
276 };
277 match protocols {
278 fio::NodeProtocolKinds::DIRECTORY => {
279 let mut mode = libc::S_IFDIR;
280 if abilities.contains(fio::Abilities::ENUMERATE) {
281 mode |= libc::S_IRUSR;
282 }
283 if abilities.contains(fio::Abilities::MODIFY_DIRECTORY) {
284 mode |= libc::S_IWUSR;
285 }
286 if abilities.contains(fio::Abilities::TRAVERSE) {
287 mode |= libc::S_IXUSR;
288 }
289 mode
290 }
291 fio::NodeProtocolKinds::FILE => {
292 let mut mode = libc::S_IFREG;
293 if abilities.contains(fio::Abilities::READ_BYTES) {
294 mode |= libc::S_IRUSR;
295 }
296 if abilities.contains(fio::Abilities::WRITE_BYTES) {
297 mode |= libc::S_IWUSR;
298 }
299 if abilities.contains(fio::Abilities::EXECUTE) {
300 mode |= libc::S_IXUSR;
301 }
302 mode
303 }
304 fio::NodeProtocolKinds::CONNECTOR => 0,
305 #[cfg(fuchsia_api_level_at_least = "HEAD")]
306 fio::NodeProtocolKinds::SYMLINK => libc::S_IFLNK | libc::S_IRUSR,
307 _ => 0,
308 }
309}
310
311pub async fn io2_to_io1_attrs<T: Node>(
315 node: &T,
316 rights: fio::Rights,
317) -> (Status, fio::NodeAttributes) {
318 if !rights.contains(fio::Rights::GET_ATTRIBUTES) {
319 return (Status::BAD_HANDLE, DEFAULT_IO1_ATTRIBUTES);
320 }
321
322 let attributes = node.get_attributes(ALL_IO1_ATTRIBUTES).await;
323 let Ok(fio::NodeAttributes2 {
324 mutable_attributes: mut_attrs,
325 immutable_attributes: immut_attrs,
326 }) = attributes
327 else {
328 return (attributes.unwrap_err(), DEFAULT_IO1_ATTRIBUTES);
329 };
330
331 (
332 Status::OK,
333 fio::NodeAttributes {
334 mode: mut_attrs.mode.unwrap_or_else(|| {
337 approximate_posix_mode(
338 immut_attrs.protocols,
339 immut_attrs.abilities.unwrap_or_default(),
340 )
341 }),
342 id: immut_attrs.id.unwrap_or(fio::INO_UNKNOWN),
343 content_size: immut_attrs.content_size.unwrap_or_default(),
344 storage_size: immut_attrs.storage_size.unwrap_or_default(),
345 link_count: immut_attrs.link_count.unwrap_or(DEFAULT_LINK_COUNT),
346 creation_time: mut_attrs.creation_time.unwrap_or_default(),
347 modification_time: mut_attrs.modification_time.unwrap_or_default(),
348 },
349 )
350}
351
352pub fn mutable_node_attributes_to_query(
353 attributes: &fio::MutableNodeAttributes,
354) -> fio::NodeAttributesQuery {
355 let mut query = fio::NodeAttributesQuery::empty();
356
357 if attributes.creation_time.is_some() {
358 query |= fio::NodeAttributesQuery::CREATION_TIME;
359 }
360 if attributes.modification_time.is_some() {
361 query |= fio::NodeAttributesQuery::MODIFICATION_TIME;
362 }
363 if attributes.access_time.is_some() {
364 query |= fio::NodeAttributesQuery::ACCESS_TIME;
365 }
366 if attributes.mode.is_some() {
367 query |= fio::NodeAttributesQuery::MODE;
368 }
369 if attributes.uid.is_some() {
370 query |= fio::NodeAttributesQuery::UID;
371 }
372 if attributes.gid.is_some() {
373 query |= fio::NodeAttributesQuery::GID;
374 }
375 if attributes.rdev.is_some() {
376 query |= fio::NodeAttributesQuery::RDEV;
377 }
378 query
379}