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 fn rights_to_posix_mode_bits(readable: bool, writable: bool, executable: bool) -> u32 {
114 return (if readable { libc::S_IRUSR } else { 0 }
115 | if writable { libc::S_IWUSR } else { 0 }
116 | if executable { libc::S_IXUSR } else { 0 })
117 .into();
118}
119
120pub async fn extended_attributes_sender(
121 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
122 attributes: Vec<Vec<u8>>,
123) {
124 let mut stream = iterator.into_stream();
125
126 let mut chunks = attributes.chunks(fio::MAX_LIST_ATTRIBUTES_CHUNK as usize).peekable();
127
128 while let Some(Ok(fio::ExtendedAttributeIteratorRequest::GetNext { responder })) =
129 stream.next().await
130 {
131 let (chunk, last) = match chunks.next() {
132 Some(chunk) => (chunk, chunks.peek().is_none()),
133 None => (&[][..], true),
134 };
135 #[allow(clippy::unnecessary_lazy_evaluations)]
136 responder.send(Ok((chunk, last))).unwrap_or_else(|_error| {
137 #[cfg(any(test, feature = "use_log"))]
138 log::error!(_error:?; "list extended attributes failed to send a chunk");
139 });
140 if last {
141 break;
142 }
143 }
144}
145
146pub fn encode_extended_attribute_value(
147 value: Vec<u8>,
148) -> Result<fio::ExtendedAttributeValue, Status> {
149 let size = value.len() as u64;
150 if size > fio::MAX_INLINE_ATTRIBUTE_VALUE {
151 #[cfg(target_os = "fuchsia")]
152 {
153 let vmo = fidl::Vmo::create(size)?;
154 vmo.write(&value, 0)?;
155 Ok(fio::ExtendedAttributeValue::Buffer(vmo))
156 }
157 #[cfg(not(target_os = "fuchsia"))]
158 Err(Status::NOT_SUPPORTED)
159 } else {
160 Ok(fio::ExtendedAttributeValue::Bytes(value))
161 }
162}
163
164pub fn decode_extended_attribute_value(
165 value: fio::ExtendedAttributeValue,
166) -> Result<Vec<u8>, Status> {
167 match value {
168 fio::ExtendedAttributeValue::Bytes(val) => Ok(val),
169 #[cfg(target_os = "fuchsia")]
170 fio::ExtendedAttributeValue::Buffer(vmo) => {
171 let length = vmo.get_content_size()?;
172 vmo.read_to_vec(0, length)
173 }
174 #[cfg(not(target_os = "fuchsia"))]
175 fio::ExtendedAttributeValue::Buffer(_) => Err(Status::NOT_SUPPORTED),
176 fio::ExtendedAttributeValue::__SourceBreaking { .. } => Err(Status::NOT_SUPPORTED),
177 }
178}
179
180#[macro_export]
192macro_rules! attributes {
193 ($requested:expr,
194 Mutable {$($mut_a:ident: $mut_v:expr),* $(,)?},
195 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
196 {
197 use $crate::common::attribute_query;
198 fio::NodeAttributes2 {
199 mutable_attributes: fio::MutableNodeAttributes {
200 $($mut_a: if $requested.contains(attribute_query!($mut_a)) {
201 Option::from($mut_v)
202 } else {
203 None
204 }),*,
205 ..Default::default()
206 },
207 immutable_attributes: fio::ImmutableNodeAttributes {
208 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
209 Option::from($immut_v)
210 } else {
211 None
212 }),*,
213 ..Default::default()
214 }
215 }
216 }
217 )
218}
219
220#[macro_export]
231macro_rules! immutable_attributes {
232 ($requested:expr,
233 Immutable {$($immut_a:ident: $immut_v:expr),* $(,)?}) => (
234 {
235 use $crate::common::attribute_query;
236 fio::NodeAttributes2 {
237 mutable_attributes: Default::default(),
238 immutable_attributes: fio::ImmutableNodeAttributes {
239 $($immut_a: if $requested.contains(attribute_query!($immut_a)) {
240 Option::from($immut_v)
241 } else {
242 None
243 }),*,
244 ..Default::default()
245 },
246 }
247 }
248 )
249}
250
251#[derive(PartialEq, Eq)]
253pub enum CreationMode {
254 Never,
256 AllowExisting,
258 Always,
260 UnnamedTemporary,
262 UnlinkableUnnamedTemporary,
264}
265
266pub(crate) fn io1_to_io2_attrs(
268 flags: fio::NodeAttributeFlags,
269 attrs: fio::NodeAttributes,
270) -> fio::MutableNodeAttributes {
271 fio::MutableNodeAttributes {
272 creation_time: flags
273 .contains(fio::NodeAttributeFlags::CREATION_TIME)
274 .then_some(attrs.creation_time),
275 modification_time: flags
276 .contains(fio::NodeAttributeFlags::MODIFICATION_TIME)
277 .then_some(attrs.modification_time),
278 ..Default::default()
279 }
280}
281
282const ALL_IO1_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::PROTOCOLS
284 .union(fio::NodeAttributesQuery::ABILITIES)
285 .union(fio::NodeAttributesQuery::ID)
286 .union(fio::NodeAttributesQuery::CONTENT_SIZE)
287 .union(fio::NodeAttributesQuery::STORAGE_SIZE)
288 .union(fio::NodeAttributesQuery::LINK_COUNT)
289 .union(fio::NodeAttributesQuery::CREATION_TIME)
290 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
291
292const DEFAULT_IO1_ATTRIBUTES: fio::NodeAttributes = fio::NodeAttributes {
294 mode: 0,
295 id: fio::INO_UNKNOWN,
296 content_size: 0,
297 storage_size: 0,
298 link_count: 0,
299 creation_time: 0,
300 modification_time: 0,
301};
302
303const DEFAULT_LINK_COUNT: u64 = 1;
304
305const fn approximate_posix_mode(
309 protocols: Option<fio::NodeProtocolKinds>,
310 abilities: fio::Abilities,
311) -> u32 {
312 let Some(protocols) = protocols else {
313 return 0;
314 };
315 match protocols {
316 fio::NodeProtocolKinds::DIRECTORY => {
317 let mut mode = libc::S_IFDIR;
318 if abilities.contains(fio::Abilities::ENUMERATE) {
319 mode |= libc::S_IRUSR;
320 }
321 if abilities.contains(fio::Abilities::MODIFY_DIRECTORY) {
322 mode |= libc::S_IWUSR;
323 }
324 if abilities.contains(fio::Abilities::TRAVERSE) {
325 mode |= libc::S_IXUSR;
326 }
327 mode
328 }
329 fio::NodeProtocolKinds::FILE => {
330 let mut mode = libc::S_IFREG;
331 if abilities.contains(fio::Abilities::READ_BYTES) {
332 mode |= libc::S_IRUSR;
333 }
334 if abilities.contains(fio::Abilities::WRITE_BYTES) {
335 mode |= libc::S_IWUSR;
336 }
337 if abilities.contains(fio::Abilities::EXECUTE) {
338 mode |= libc::S_IXUSR;
339 }
340 mode
341 }
342 fio::NodeProtocolKinds::CONNECTOR => fio::MODE_TYPE_SERVICE | libc::S_IRUSR | libc::S_IWUSR,
343 #[cfg(fuchsia_api_level_at_least = "HEAD")]
344 fio::NodeProtocolKinds::SYMLINK => libc::S_IFLNK | libc::S_IRUSR,
345 _ => 0,
346 }
347}
348
349pub async fn io2_to_io1_attrs<T: Node>(
353 node: &T,
354 rights: fio::Rights,
355) -> (Status, fio::NodeAttributes) {
356 if !rights.contains(fio::Rights::GET_ATTRIBUTES) {
357 return (Status::BAD_HANDLE, DEFAULT_IO1_ATTRIBUTES);
358 }
359
360 let attributes = node.get_attributes(ALL_IO1_ATTRIBUTES).await;
361 let Ok(fio::NodeAttributes2 {
362 mutable_attributes: mut_attrs,
363 immutable_attributes: immut_attrs,
364 }) = attributes
365 else {
366 return (attributes.unwrap_err(), DEFAULT_IO1_ATTRIBUTES);
367 };
368
369 (
370 Status::OK,
371 fio::NodeAttributes {
372 mode: mut_attrs.mode.unwrap_or_else(|| {
375 approximate_posix_mode(
376 immut_attrs.protocols,
377 immut_attrs.abilities.unwrap_or_default(),
378 )
379 }),
380 id: immut_attrs.id.unwrap_or(fio::INO_UNKNOWN),
381 content_size: immut_attrs.content_size.unwrap_or_default(),
382 storage_size: immut_attrs.storage_size.unwrap_or_default(),
383 link_count: immut_attrs.link_count.unwrap_or(DEFAULT_LINK_COUNT),
384 creation_time: mut_attrs.creation_time.unwrap_or_default(),
385 modification_time: mut_attrs.modification_time.unwrap_or_default(),
386 },
387 )
388}
389
390pub fn mutable_node_attributes_to_query(
391 attributes: &fio::MutableNodeAttributes,
392) -> fio::NodeAttributesQuery {
393 let mut query = fio::NodeAttributesQuery::empty();
394
395 if attributes.creation_time.is_some() {
396 query |= fio::NodeAttributesQuery::CREATION_TIME;
397 }
398 if attributes.modification_time.is_some() {
399 query |= fio::NodeAttributesQuery::MODIFICATION_TIME;
400 }
401 if attributes.access_time.is_some() {
402 query |= fio::NodeAttributesQuery::ACCESS_TIME;
403 }
404 if attributes.mode.is_some() {
405 query |= fio::NodeAttributesQuery::MODE;
406 }
407 if attributes.uid.is_some() {
408 query |= fio::NodeAttributesQuery::UID;
409 }
410 if attributes.gid.is_some() {
411 query |= fio::NodeAttributesQuery::GID;
412 }
413 if attributes.rdev.is_some() {
414 query |= fio::NodeAttributesQuery::RDEV;
415 }
416 query
417}
418
419#[cfg(test)]
420mod tests {
421 use super::inherit_rights_for_clone;
422
423 use fidl_fuchsia_io as fio;
424
425 macro_rules! irfc_ok {
429 ($parent_flags:expr, $flags:expr, $expected_new_flags:expr $(,)*) => {{
430 let res = inherit_rights_for_clone($parent_flags, $flags);
431 match res {
432 Ok(new_flags) => assert_eq!(
433 $expected_new_flags, new_flags,
434 "`inherit_rights_for_clone` returned unexpected set of flags.\n\
435 Expected: {:X}\n\
436 Actual: {:X}",
437 $expected_new_flags, new_flags
438 ),
439 Err(status) => panic!("`inherit_rights_for_clone` failed. Status: {status}"),
440 }
441 }};
442 }
443
444 #[test]
445 fn node_reference_is_inherited() {
446 irfc_ok!(
447 fio::OpenFlags::NODE_REFERENCE,
448 fio::OpenFlags::empty(),
449 fio::OpenFlags::NODE_REFERENCE
450 );
451 }
452}