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 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 inherit_rights_for_clone(
30 parent_flags: fio::OpenFlags,
31 mut flags: fio::OpenFlags,
32) -> Result<fio::OpenFlags, Status> {
33 if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) && flags.intersects(FS_RIGHTS) {
34 return Err(Status::INVALID_ARGS);
35 }
36
37 flags |= parent_flags & (fio::OpenFlags::APPEND | fio::OpenFlags::NODE_REFERENCE);
41
42 if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) {
46 flags &= !fio::OpenFlags::CLONE_SAME_RIGHTS;
47 flags |= parent_flags & FS_RIGHTS;
48 }
49
50 if !stricter_or_same_rights(parent_flags, flags) {
51 return Err(Status::ACCESS_DENIED);
52 }
53
54 flags &= !(fio::OpenFlags::POSIX_WRITABLE | fio::OpenFlags::POSIX_EXECUTABLE);
56
57 Ok(flags)
58}
59
60pub fn send_on_open_with_error(
73 describe: bool,
74 server_end: ServerEnd<fio::NodeMarker>,
75 status: Status,
76) {
77 if status == Status::OK {
78 panic!("send_on_open_with_error() should not be used to respond with Status::OK");
79 }
80
81 if !describe {
82 let _ = server_end.close_with_epitaph(status);
85 return;
86 }
87
88 let (_, control_handle) = server_end.into_stream_and_control_handle();
89 let _ = control_handle.send_on_open_(status.into_raw(), None);
91 control_handle.shutdown_with_epitaph(status);
92}
93
94pub trait IntoAny: std::any::Any {
100 fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static>;
102}
103
104impl<T: 'static + Send + Sync> IntoAny for T {
105 fn into_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static> {
106 self as Arc<dyn std::any::Any + Send + Sync + 'static>
107 }
108}
109
110pub async fn extended_attributes_sender(
111 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
112 attributes: Vec<Vec<u8>>,
113) {
114 let mut stream = iterator.into_stream();
115
116 let mut chunks = attributes.chunks(fio::MAX_LIST_ATTRIBUTES_CHUNK as usize).peekable();
117
118 while let Some(Ok(fio::ExtendedAttributeIteratorRequest::GetNext { responder })) =
119 stream.next().await
120 {
121 let (chunk, last) = match chunks.next() {
122 Some(chunk) => (chunk, chunks.peek().is_none()),
123 None => (&[][..], true),
124 };
125 #[allow(clippy::unnecessary_lazy_evaluations)]
126 responder.send(Ok((chunk, last))).unwrap_or_else(|_error| {
127 #[cfg(any(test, feature = "use_log"))]
128 log::error!(_error:?; "list extended attributes failed to send a chunk");
129 });
130 if last {
131 break;
132 }
133 }
134}
135
136pub fn encode_extended_attribute_value(
137 value: Vec<u8>,
138) -> Result<fio::ExtendedAttributeValue, Status> {
139 let size = value.len() as u64;
140 if size > fio::MAX_INLINE_ATTRIBUTE_VALUE {
141 #[cfg(target_os = "fuchsia")]
142 {
143 let vmo = fidl::Vmo::create(size)?;
144 vmo.write(&value, 0)?;
145 Ok(fio::ExtendedAttributeValue::Buffer(vmo))
146 }
147 #[cfg(not(target_os = "fuchsia"))]
148 Err(Status::NOT_SUPPORTED)
149 } else {
150 Ok(fio::ExtendedAttributeValue::Bytes(value))
151 }
152}
153
154pub fn decode_extended_attribute_value(
155 value: fio::ExtendedAttributeValue,
156) -> Result<Vec<u8>, Status> {
157 match value {
158 fio::ExtendedAttributeValue::Bytes(val) => Ok(val),
159 #[cfg(target_os = "fuchsia")]
160 fio::ExtendedAttributeValue::Buffer(vmo) => {
161 let length = vmo.get_content_size()?;
162 vmo.read_to_vec(0, length)
163 }
164 #[cfg(not(target_os = "fuchsia"))]
165 fio::ExtendedAttributeValue::Buffer(_) => Err(Status::NOT_SUPPORTED),
166 fio::ExtendedAttributeValue::__SourceBreaking { .. } => Err(Status::NOT_SUPPORTED),
167 }
168}
169
170#[macro_export]
182macro_rules! attributes {
183 ($requested:expr,
184 Mutable {$($mut_a:ident: $mut_v:expr),* $(,)?},
185 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
186 {
187 use $crate::common::attribute_query;
188 fio::NodeAttributes2 {
189 mutable_attributes: fio::MutableNodeAttributes {
190 $($mut_a: if $requested.contains(attribute_query!($mut_a)) {
191 Option::from($mut_v)
192 } else {
193 None
194 }),*,
195 ..Default::default()
196 },
197 immutable_attributes: fio::ImmutableNodeAttributes {
198 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
199 Option::from($immut_v)
200 } else {
201 None
202 }),*,
203 ..Default::default()
204 }
205 }
206 }
207 )
208}
209
210#[macro_export]
221macro_rules! immutable_attributes {
222 ($requested:expr,
223 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
224 {
225 use $crate::common::attribute_query;
226 fio::NodeAttributes2 {
227 mutable_attributes: Default::default(),
228 immutable_attributes: fio::ImmutableNodeAttributes {
229 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
230 Option::from($immut_v)
231 } else {
232 None
233 }),*,
234 ..Default::default()
235 },
236 }
237 }
238 )
239}
240
241#[derive(Debug, PartialEq, Eq)]
243pub enum CreationMode {
244 Never,
246 AllowExisting,
248 Always,
250 UnnamedTemporary,
252 UnlinkableUnnamedTemporary,
254}
255
256pub(crate) fn io1_to_io2_attrs(
258 flags: fio::NodeAttributeFlags,
259 attrs: fio::NodeAttributes,
260) -> fio::MutableNodeAttributes {
261 fio::MutableNodeAttributes {
262 creation_time: flags
263 .contains(fio::NodeAttributeFlags::CREATION_TIME)
264 .then_some(attrs.creation_time),
265 modification_time: flags
266 .contains(fio::NodeAttributeFlags::MODIFICATION_TIME)
267 .then_some(attrs.modification_time),
268 ..Default::default()
269 }
270}
271
272const ALL_IO1_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::PROTOCOLS
274 .union(fio::NodeAttributesQuery::ABILITIES)
275 .union(fio::NodeAttributesQuery::ID)
276 .union(fio::NodeAttributesQuery::CONTENT_SIZE)
277 .union(fio::NodeAttributesQuery::STORAGE_SIZE)
278 .union(fio::NodeAttributesQuery::LINK_COUNT)
279 .union(fio::NodeAttributesQuery::CREATION_TIME)
280 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
281
282const DEFAULT_IO1_ATTRIBUTES: fio::NodeAttributes = fio::NodeAttributes {
284 mode: 0,
285 id: fio::INO_UNKNOWN,
286 content_size: 0,
287 storage_size: 0,
288 link_count: 0,
289 creation_time: 0,
290 modification_time: 0,
291};
292
293const DEFAULT_LINK_COUNT: u64 = 1;
294
295const fn approximate_posix_mode(
299 protocols: Option<fio::NodeProtocolKinds>,
300 abilities: fio::Abilities,
301) -> u32 {
302 let Some(protocols) = protocols else {
303 return 0;
304 };
305 match protocols {
306 fio::NodeProtocolKinds::DIRECTORY => {
307 let mut mode = libc::S_IFDIR;
308 if abilities.contains(fio::Abilities::ENUMERATE) {
309 mode |= libc::S_IRUSR;
310 }
311 if abilities.contains(fio::Abilities::MODIFY_DIRECTORY) {
312 mode |= libc::S_IWUSR;
313 }
314 if abilities.contains(fio::Abilities::TRAVERSE) {
315 mode |= libc::S_IXUSR;
316 }
317 mode
318 }
319 fio::NodeProtocolKinds::FILE => {
320 let mut mode = libc::S_IFREG;
321 if abilities.contains(fio::Abilities::READ_BYTES) {
322 mode |= libc::S_IRUSR;
323 }
324 if abilities.contains(fio::Abilities::WRITE_BYTES) {
325 mode |= libc::S_IWUSR;
326 }
327 if abilities.contains(fio::Abilities::EXECUTE) {
328 mode |= libc::S_IXUSR;
329 }
330 mode
331 }
332 fio::NodeProtocolKinds::CONNECTOR => fio::MODE_TYPE_SERVICE | libc::S_IRUSR | libc::S_IWUSR,
333 #[cfg(fuchsia_api_level_at_least = "HEAD")]
334 fio::NodeProtocolKinds::SYMLINK => libc::S_IFLNK | libc::S_IRUSR,
335 _ => 0,
336 }
337}
338
339pub async fn io2_to_io1_attrs<T: Node>(
343 node: &T,
344 rights: fio::Rights,
345) -> (Status, fio::NodeAttributes) {
346 if !rights.contains(fio::Rights::GET_ATTRIBUTES) {
347 return (Status::BAD_HANDLE, DEFAULT_IO1_ATTRIBUTES);
348 }
349
350 let attributes = node.get_attributes(ALL_IO1_ATTRIBUTES).await;
351 let Ok(fio::NodeAttributes2 {
352 mutable_attributes: mut_attrs,
353 immutable_attributes: immut_attrs,
354 }) = attributes
355 else {
356 return (attributes.unwrap_err(), DEFAULT_IO1_ATTRIBUTES);
357 };
358
359 (
360 Status::OK,
361 fio::NodeAttributes {
362 mode: mut_attrs.mode.unwrap_or_else(|| {
365 approximate_posix_mode(
366 immut_attrs.protocols,
367 immut_attrs.abilities.unwrap_or_default(),
368 )
369 }),
370 id: immut_attrs.id.unwrap_or(fio::INO_UNKNOWN),
371 content_size: immut_attrs.content_size.unwrap_or_default(),
372 storage_size: immut_attrs.storage_size.unwrap_or_default(),
373 link_count: immut_attrs.link_count.unwrap_or(DEFAULT_LINK_COUNT),
374 creation_time: mut_attrs.creation_time.unwrap_or_default(),
375 modification_time: mut_attrs.modification_time.unwrap_or_default(),
376 },
377 )
378}
379
380pub fn mutable_node_attributes_to_query(
381 attributes: &fio::MutableNodeAttributes,
382) -> fio::NodeAttributesQuery {
383 let mut query = fio::NodeAttributesQuery::empty();
384
385 if attributes.creation_time.is_some() {
386 query |= fio::NodeAttributesQuery::CREATION_TIME;
387 }
388 if attributes.modification_time.is_some() {
389 query |= fio::NodeAttributesQuery::MODIFICATION_TIME;
390 }
391 if attributes.access_time.is_some() {
392 query |= fio::NodeAttributesQuery::ACCESS_TIME;
393 }
394 if attributes.mode.is_some() {
395 query |= fio::NodeAttributesQuery::MODE;
396 }
397 if attributes.uid.is_some() {
398 query |= fio::NodeAttributesQuery::UID;
399 }
400 if attributes.gid.is_some() {
401 query |= fio::NodeAttributesQuery::GID;
402 }
403 if attributes.rdev.is_some() {
404 query |= fio::NodeAttributesQuery::RDEV;
405 }
406 query
407}
408
409#[cfg(test)]
410mod tests {
411 use super::inherit_rights_for_clone;
412
413 use fidl_fuchsia_io as fio;
414
415 macro_rules! irfc_ok {
419 ($parent_flags:expr, $flags:expr, $expected_new_flags:expr $(,)*) => {{
420 let res = inherit_rights_for_clone($parent_flags, $flags);
421 match res {
422 Ok(new_flags) => assert_eq!(
423 $expected_new_flags, new_flags,
424 "`inherit_rights_for_clone` returned unexpected set of flags.\n\
425 Expected: {:X}\n\
426 Actual: {:X}",
427 $expected_new_flags, new_flags
428 ),
429 Err(status) => panic!("`inherit_rights_for_clone` failed. Status: {status}"),
430 }
431 }};
432 }
433
434 #[test]
435 fn node_reference_is_inherited() {
436 irfc_ok!(
437 fio::OpenFlags::NODE_REFERENCE,
438 fio::OpenFlags::empty(),
439 fio::OpenFlags::NODE_REFERENCE
440 );
441 }
442}