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