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