1use crate::common::{
8 decode_extended_attribute_value, encode_extended_attribute_value, extended_attributes_sender,
9 inherit_rights_for_clone, send_on_open_with_error,
10};
11use crate::execution_scope::ExecutionScope;
12use crate::name::parse_name;
13use crate::node::Node;
14use crate::object_request::{run_synchronous_future_or_spawn, ConnectionCreator, Representation};
15use crate::request_handler::{RequestHandler, RequestListener};
16use crate::{ObjectRequest, ObjectRequestRef, ProtocolsExt, ToObjectRequest};
17use fidl::endpoints::{ControlHandle as _, Responder, ServerEnd};
18use fidl_fuchsia_io as fio;
19use std::future::{ready, Future};
20use std::ops::ControlFlow;
21use std::pin::Pin;
22use std::sync::Arc;
23use zx_status::Status;
24
25pub trait Symlink: Node {
26 fn read_target(&self) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
27
28 fn list_extended_attributes(
30 &self,
31 ) -> impl Future<Output = Result<Vec<Vec<u8>>, Status>> + Send {
32 ready(Err(Status::NOT_SUPPORTED))
33 }
34 fn get_extended_attribute(
35 &self,
36 _name: Vec<u8>,
37 ) -> impl Future<Output = Result<Vec<u8>, Status>> + Send {
38 ready(Err(Status::NOT_SUPPORTED))
39 }
40 fn set_extended_attribute(
41 &self,
42 _name: Vec<u8>,
43 _value: Vec<u8>,
44 _mode: fio::SetExtendedAttributeMode,
45 ) -> impl Future<Output = Result<(), Status>> + Send {
46 ready(Err(Status::NOT_SUPPORTED))
47 }
48 fn remove_extended_attribute(
49 &self,
50 _name: Vec<u8>,
51 ) -> impl Future<Output = Result<(), Status>> + Send {
52 ready(Err(Status::NOT_SUPPORTED))
53 }
54}
55
56pub struct Connection<T> {
57 scope: ExecutionScope,
58 symlink: Arc<T>,
59}
60
61pub struct SymlinkOptions;
62
63impl<T: Symlink> Connection<T> {
64 pub async fn create(
70 scope: ExecutionScope,
71 symlink: Arc<T>,
72 protocols: impl ProtocolsExt,
73 object_request: ObjectRequestRef<'_>,
74 ) -> Result<(), Status> {
75 let _options = protocols.to_symlink_options()?;
76 let connection = Self { scope: scope.clone(), symlink };
77 if let Ok(requests) = object_request.take().into_request_stream(&connection).await {
78 scope.spawn(RequestListener::new(requests, connection));
79 }
80 Ok(())
81 }
82
83 pub fn create_sync(
86 scope: ExecutionScope,
87 symlink: Arc<T>,
88 options: impl ProtocolsExt,
89 object_request: ObjectRequest,
90 ) {
91 run_synchronous_future_or_spawn(
92 scope.clone(),
93 object_request.handle_async(async |object_request| {
94 Self::create(scope, symlink, options, object_request).await
95 }),
96 )
97 }
98
99 async fn handle_request(&mut self, req: fio::SymlinkRequest) -> Result<bool, fidl::Error> {
101 match req {
102 #[cfg(fuchsia_api_level_at_least = "26")]
103 fio::SymlinkRequest::DeprecatedClone { flags, object, control_handle: _ } => {
104 self.handle_deprecated_clone(flags, object).await;
105 }
106 #[cfg(not(fuchsia_api_level_at_least = "26"))]
107 fio::SymlinkRequest::Clone { flags, object, control_handle: _ } => {
108 self.handle_deprecated_clone(flags, object).await;
109 }
110 #[cfg(fuchsia_api_level_at_least = "26")]
111 fio::SymlinkRequest::Clone { request, control_handle: _ } => {
112 self.handle_clone(ServerEnd::new(request.into_channel())).await;
113 }
114 #[cfg(not(fuchsia_api_level_at_least = "26"))]
115 fio::SymlinkRequest::Clone2 { request, control_handle: _ } => {
116 self.handle_clone(ServerEnd::new(request.into_channel())).await;
117 }
118 fio::SymlinkRequest::Close { responder } => {
119 responder.send(Ok(()))?;
120 return Ok(true);
121 }
122 fio::SymlinkRequest::LinkInto { dst_parent_token, dst, responder } => {
123 responder.send(
124 self.handle_link_into(dst_parent_token, dst).await.map_err(|s| s.into_raw()),
125 )?;
126 }
127 fio::SymlinkRequest::GetConnectionInfo { responder } => {
128 let rights = fio::Operations::GET_ATTRIBUTES;
130 responder
131 .send(fio::ConnectionInfo { rights: Some(rights), ..Default::default() })?;
132 }
133 fio::SymlinkRequest::Sync { responder } => {
134 responder.send(Ok(()))?;
135 }
136 fio::SymlinkRequest::GetAttr { responder } => {
137 let (status, attrs) = crate::common::io2_to_io1_attrs(
139 self.symlink.as_ref(),
140 fio::Rights::GET_ATTRIBUTES,
141 )
142 .await;
143 responder.send(status.into_raw(), &attrs)?;
144 }
145 fio::SymlinkRequest::SetAttr { responder, .. } => {
146 responder.send(Status::ACCESS_DENIED.into_raw())?;
147 }
148 fio::SymlinkRequest::GetAttributes { query, responder } => {
149 let attrs = self.symlink.get_attributes(query).await;
151 responder.send(
152 attrs
153 .as_ref()
154 .map(|attrs| (&attrs.mutable_attributes, &attrs.immutable_attributes))
155 .map_err(|status| status.into_raw()),
156 )?;
157 }
158 fio::SymlinkRequest::UpdateAttributes { payload: _, responder } => {
159 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
160 }
161 fio::SymlinkRequest::ListExtendedAttributes { iterator, control_handle: _ } => {
162 self.handle_list_extended_attribute(iterator).await;
163 }
164 fio::SymlinkRequest::GetExtendedAttribute { responder, name } => {
165 let res = self.handle_get_extended_attribute(name).await.map_err(|s| s.into_raw());
166 responder.send(res)?;
167 }
168 fio::SymlinkRequest::SetExtendedAttribute { responder, name, value, mode } => {
169 let res = self
170 .handle_set_extended_attribute(name, value, mode)
171 .await
172 .map_err(|s| s.into_raw());
173 responder.send(res)?;
174 }
175 fio::SymlinkRequest::RemoveExtendedAttribute { responder, name } => {
176 let res =
177 self.handle_remove_extended_attribute(name).await.map_err(|s| s.into_raw());
178 responder.send(res)?;
179 }
180 fio::SymlinkRequest::Describe { responder } => match self.symlink.read_target().await {
181 Ok(target) => responder
182 .send(&fio::SymlinkInfo { target: Some(target), ..Default::default() })?,
183 Err(status) => {
184 responder.control_handle().shutdown_with_epitaph(status);
185 return Ok(true);
186 }
187 },
188 #[cfg(fuchsia_api_level_at_least = "NEXT")]
189 fio::SymlinkRequest::GetFlags { responder } => {
190 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
191 }
192 #[cfg(fuchsia_api_level_at_least = "NEXT")]
193 fio::SymlinkRequest::SetFlags { flags: _, responder } => {
194 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
195 }
196 #[cfg(fuchsia_api_level_at_least = "NEXT")]
197 fio::SymlinkRequest::DeprecatedGetFlags { responder } => {
198 responder.send(Status::NOT_SUPPORTED.into_raw(), fio::OpenFlags::empty())?;
199 }
200 #[cfg(fuchsia_api_level_at_least = "NEXT")]
201 fio::SymlinkRequest::DeprecatedSetFlags { responder, .. } => {
202 responder.send(Status::ACCESS_DENIED.into_raw())?;
203 }
204 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
205 fio::SymlinkRequest::GetFlags { responder } => {
206 responder.send(Status::NOT_SUPPORTED.into_raw(), fio::OpenFlags::empty())?;
207 }
208 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
209 fio::SymlinkRequest::SetFlags { responder, .. } => {
210 responder.send(Status::ACCESS_DENIED.into_raw())?;
211 }
212 fio::SymlinkRequest::Query { responder } => {
213 responder.send(fio::SYMLINK_PROTOCOL_NAME.as_bytes())?;
214 }
215 fio::SymlinkRequest::QueryFilesystem { responder } => {
216 match self.symlink.query_filesystem() {
217 Err(status) => responder.send(status.into_raw(), None)?,
218 Ok(info) => responder.send(0, Some(&info))?,
219 }
220 }
221 fio::SymlinkRequest::_UnknownMethod { ordinal: _ordinal, .. } => {
222 #[cfg(any(test, feature = "use_log"))]
223 log::warn!(_ordinal; "Received unknown method")
224 }
225 }
226 Ok(false)
227 }
228
229 async fn handle_deprecated_clone(
230 &mut self,
231 flags: fio::OpenFlags,
232 server_end: ServerEnd<fio::NodeMarker>,
233 ) {
234 let flags = match inherit_rights_for_clone(fio::OpenFlags::RIGHT_READABLE, flags) {
235 Ok(updated) => updated,
236 Err(status) => {
237 send_on_open_with_error(
238 flags.contains(fio::OpenFlags::DESCRIBE),
239 server_end,
240 status,
241 );
242 return;
243 }
244 };
245 flags
246 .to_object_request(server_end)
247 .handle_async(async |object_request| {
248 Self::create(self.scope.clone(), self.symlink.clone(), flags, object_request).await
249 })
250 .await;
251 }
252
253 async fn handle_clone(&mut self, server_end: ServerEnd<fio::SymlinkMarker>) {
254 let flags = fio::Flags::PROTOCOL_SYMLINK | fio::Flags::PERM_GET_ATTRIBUTES;
255 flags
256 .to_object_request(server_end)
257 .handle_async(async |object_request| {
258 Self::create(self.scope.clone(), self.symlink.clone(), flags, object_request).await
259 })
260 .await;
261 }
262
263 async fn handle_link_into(
264 &mut self,
265 target_parent_token: fidl::Event,
266 target_name: String,
267 ) -> Result<(), Status> {
268 let target_name = parse_name(target_name).map_err(|_| Status::INVALID_ARGS)?;
269
270 let target_parent = self
271 .scope
272 .token_registry()
273 .get_owner(target_parent_token.into())?
274 .ok_or(Err(Status::NOT_FOUND))?;
275
276 self.symlink.clone().link_into(target_parent, target_name).await
277 }
278
279 async fn handle_list_extended_attribute(
280 &self,
281 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
282 ) {
283 let attributes = match self.symlink.list_extended_attributes().await {
284 Ok(attributes) => attributes,
285 Err(status) => {
286 #[cfg(any(test, feature = "use_log"))]
287 log::error!(status:?; "list extended attributes failed");
288 #[allow(clippy::unnecessary_lazy_evaluations)]
289 iterator.close_with_epitaph(status).unwrap_or_else(|_error| {
290 #[cfg(any(test, feature = "use_log"))]
291 log::error!(_error:?; "failed to send epitaph")
292 });
293 return;
294 }
295 };
296 self.scope.spawn(extended_attributes_sender(iterator, attributes));
297 }
298
299 async fn handle_get_extended_attribute(
300 &self,
301 name: Vec<u8>,
302 ) -> Result<fio::ExtendedAttributeValue, Status> {
303 let value = self.symlink.get_extended_attribute(name).await?;
304 encode_extended_attribute_value(value)
305 }
306
307 async fn handle_set_extended_attribute(
308 &self,
309 name: Vec<u8>,
310 value: fio::ExtendedAttributeValue,
311 mode: fio::SetExtendedAttributeMode,
312 ) -> Result<(), Status> {
313 if name.contains(&0) {
314 return Err(Status::INVALID_ARGS);
315 }
316 let val = decode_extended_attribute_value(value)?;
317 self.symlink.set_extended_attribute(name, val, mode).await
318 }
319
320 async fn handle_remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
321 self.symlink.remove_extended_attribute(name).await
322 }
323}
324
325impl<T: Symlink> RequestHandler for Connection<T> {
326 type Request = Result<fio::SymlinkRequest, fidl::Error>;
327
328 async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
329 let this = self.get_mut();
330 let _guard = this.scope.active_guard();
331 match request {
332 Ok(request) => match this.handle_request(request).await {
333 Ok(false) => ControlFlow::Continue(()),
334 Ok(true) | Err(_) => ControlFlow::Break(()),
335 },
336 Err(_) => ControlFlow::Break(()),
337 }
338 }
339}
340
341impl<T: Symlink> Representation for Connection<T> {
342 type Protocol = fio::SymlinkMarker;
343
344 async fn get_representation(
345 &self,
346 requested_attributes: fio::NodeAttributesQuery,
347 ) -> Result<fio::Representation, Status> {
348 Ok(fio::Representation::Symlink(fio::SymlinkInfo {
349 attributes: if requested_attributes.is_empty() {
350 None
351 } else {
352 Some(self.symlink.get_attributes(requested_attributes).await?)
353 },
354 target: Some(self.symlink.read_target().await?),
355 ..Default::default()
356 }))
357 }
358
359 async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
360 Ok(fio::NodeInfoDeprecated::Symlink(fio::SymlinkObject {
361 target: self.symlink.read_target().await?,
362 }))
363 }
364}
365
366impl<T: Symlink> ConnectionCreator<T> for Connection<T> {
367 async fn create<'a>(
368 scope: ExecutionScope,
369 node: Arc<T>,
370 protocols: impl ProtocolsExt,
371 object_request: ObjectRequestRef<'a>,
372 ) -> Result<(), Status> {
373 Self::create(scope, node, protocols, object_request).await
374 }
375}
376
377pub fn serve(
379 link: Arc<impl Symlink>,
380 scope: ExecutionScope,
381 protocols: impl ProtocolsExt,
382 object_request: ObjectRequestRef<'_>,
383) -> Result<(), Status> {
384 if protocols.is_node() {
385 let options = protocols.to_node_options(link.entry_info().type_())?;
386 link.open_as_node(scope, options, object_request)
387 } else {
388 Connection::create_sync(scope, link, protocols, object_request.take());
389 Ok(())
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::{Connection, Symlink};
396 use crate::common::rights_to_posix_mode_bits;
397 use crate::directory::entry::{EntryInfo, GetEntryInfo};
398 use crate::execution_scope::ExecutionScope;
399 use crate::node::Node;
400 use crate::{immutable_attributes, ToObjectRequest};
401 use assert_matches::assert_matches;
402 use fidl::endpoints::{create_proxy, ServerEnd};
403 use fidl_fuchsia_io as fio;
404 use fuchsia_sync::Mutex;
405 use futures::StreamExt;
406 use std::collections::HashMap;
407 use std::sync::Arc;
408 use zx_status::Status;
409
410 const TARGET: &[u8] = b"target";
411
412 struct TestSymlink {
413 xattrs: Mutex<HashMap<Vec<u8>, Vec<u8>>>,
414 }
415
416 impl TestSymlink {
417 fn new() -> Self {
418 TestSymlink { xattrs: Mutex::new(HashMap::new()) }
419 }
420 }
421
422 impl Symlink for TestSymlink {
423 async fn read_target(&self) -> Result<Vec<u8>, Status> {
424 Ok(TARGET.to_vec())
425 }
426 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
427 let map = self.xattrs.lock();
428 Ok(map.values().map(|x| x.clone()).collect())
429 }
430 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
431 let map = self.xattrs.lock();
432 map.get(&name).map(|x| x.clone()).ok_or(Status::NOT_FOUND)
433 }
434 async fn set_extended_attribute(
435 &self,
436 name: Vec<u8>,
437 value: Vec<u8>,
438 _mode: fio::SetExtendedAttributeMode,
439 ) -> Result<(), Status> {
440 let mut map = self.xattrs.lock();
441 map.insert(name, value);
444 Ok(())
445 }
446 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
447 let mut map = self.xattrs.lock();
448 map.remove(&name);
449 Ok(())
450 }
451 }
452
453 impl Node for TestSymlink {
454 async fn get_attributes(
455 &self,
456 requested_attributes: fio::NodeAttributesQuery,
457 ) -> Result<fio::NodeAttributes2, Status> {
458 Ok(immutable_attributes!(
459 requested_attributes,
460 Immutable {
461 content_size: TARGET.len() as u64,
462 storage_size: TARGET.len() as u64,
463 protocols: fio::NodeProtocolKinds::SYMLINK,
464 abilities: fio::Abilities::GET_ATTRIBUTES,
465 }
466 ))
467 }
468 }
469
470 impl GetEntryInfo for TestSymlink {
471 fn entry_info(&self) -> EntryInfo {
472 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Symlink)
473 }
474 }
475
476 async fn serve_test_symlink() -> fio::SymlinkProxy {
477 let (client_end, server_end) = create_proxy::<fio::SymlinkMarker>();
478 let flags = fio::PERM_READABLE | fio::Flags::PROTOCOL_SYMLINK;
479
480 Connection::create_sync(
481 ExecutionScope::new(),
482 Arc::new(TestSymlink::new()),
483 flags,
484 flags.to_object_request(server_end),
485 );
486
487 client_end
488 }
489
490 #[fuchsia::test]
491 async fn test_read_target() {
492 let client_end = serve_test_symlink().await;
493
494 assert_eq!(
495 client_end.describe().await.expect("fidl failed").target.expect("missing target"),
496 b"target"
497 );
498 }
499
500 #[fuchsia::test]
501 async fn test_validate_flags() {
502 let scope = ExecutionScope::new();
503
504 let check = |mut flags: fio::OpenFlags| {
505 let (client_end, server_end) = create_proxy::<fio::SymlinkMarker>();
506 flags |= fio::OpenFlags::DESCRIBE;
507 flags.to_object_request(server_end).create_connection_sync::<Connection<_>, _>(
508 scope.clone(),
509 Arc::new(TestSymlink::new()),
510 flags,
511 );
512
513 async move {
514 Status::from_raw(
515 client_end
516 .take_event_stream()
517 .next()
518 .await
519 .expect("no event")
520 .expect("next failed")
521 .into_on_open_()
522 .expect("expected OnOpen")
523 .0,
524 )
525 }
526 };
527
528 for flags in [
529 fio::OpenFlags::RIGHT_WRITABLE,
530 fio::OpenFlags::RIGHT_EXECUTABLE,
531 fio::OpenFlags::CREATE,
532 fio::OpenFlags::CREATE_IF_ABSENT,
533 fio::OpenFlags::TRUNCATE,
534 fio::OpenFlags::APPEND,
535 fio::OpenFlags::POSIX_WRITABLE,
536 fio::OpenFlags::POSIX_EXECUTABLE,
537 fio::OpenFlags::CLONE_SAME_RIGHTS,
538 fio::OpenFlags::BLOCK_DEVICE,
539 ] {
540 assert_eq!(check(flags).await, Status::INVALID_ARGS, "{flags:?}");
541 }
542
543 assert_eq!(
544 check(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::NOT_DIRECTORY).await,
545 Status::OK
546 );
547 }
548
549 #[fuchsia::test]
550 async fn test_get_attr() {
551 let client_end = serve_test_symlink().await;
552
553 assert_matches!(
554 client_end.get_attr().await.expect("fidl failed"),
555 (
556 0,
557 fio::NodeAttributes {
558 mode,
559 id: fio::INO_UNKNOWN,
560 content_size: 6,
561 storage_size: 6,
562 link_count: 1,
563 creation_time: 0,
564 modification_time: 0,
565 }
566 ) if mode == fio::MODE_TYPE_SYMLINK
567 | rights_to_posix_mode_bits(true, false, false)
568 );
569 }
570
571 #[fuchsia::test]
572 async fn test_clone() {
573 let client_end = serve_test_symlink().await;
574
575 let orig_attrs = client_end
576 .get_attributes(fio::NodeAttributesQuery::all())
577 .await
578 .expect("fidl failed")
579 .unwrap();
580 let (cloned_client, cloned_server) = create_proxy::<fio::SymlinkMarker>();
582 client_end.clone(ServerEnd::new(cloned_server.into_channel())).unwrap();
583 let cloned_attrs = cloned_client
584 .get_attributes(fio::NodeAttributesQuery::all())
585 .await
586 .expect("fidl failed")
587 .unwrap();
588 assert_eq!(orig_attrs, cloned_attrs);
589 }
590
591 #[fuchsia::test]
592 async fn test_describe() {
593 let client_end = serve_test_symlink().await;
594
595 assert_matches!(
596 client_end.describe().await.expect("fidl failed"),
597 fio::SymlinkInfo {
598 target: Some(target),
599 ..
600 } if target == b"target"
601 );
602 }
603
604 #[fuchsia::test]
605 async fn test_xattrs() {
606 let client_end = serve_test_symlink().await;
607
608 client_end
609 .set_extended_attribute(
610 b"foo",
611 fio::ExtendedAttributeValue::Bytes(b"bar".to_vec()),
612 fio::SetExtendedAttributeMode::Set,
613 )
614 .await
615 .unwrap()
616 .unwrap();
617 assert_eq!(
618 client_end.get_extended_attribute(b"foo").await.unwrap().unwrap(),
619 fio::ExtendedAttributeValue::Bytes(b"bar".to_vec()),
620 );
621 let (iterator_client_end, iterator_server_end) =
622 create_proxy::<fio::ExtendedAttributeIteratorMarker>();
623 client_end.list_extended_attributes(iterator_server_end).unwrap();
624 assert_eq!(
625 iterator_client_end.get_next().await.unwrap().unwrap(),
626 (vec![b"bar".to_vec()], true)
627 );
628 client_end.remove_extended_attribute(b"foo").await.unwrap().unwrap();
629 assert_eq!(
630 client_end.get_extended_attribute(b"foo").await.unwrap().unwrap_err(),
631 Status::NOT_FOUND.into_raw(),
632 );
633 }
634}