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