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