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::Sync { responder } => {
128 responder.send(Ok(()))?;
129 }
130 #[cfg(fuchsia_api_level_at_least = "NEXT")]
131 fio::SymlinkRequest::DeprecatedGetAttr { responder } => {
132 let (status, attrs) = crate::common::io2_to_io1_attrs(
134 self.symlink.as_ref(),
135 fio::Rights::GET_ATTRIBUTES,
136 )
137 .await;
138 responder.send(status.into_raw(), &attrs)?;
139 }
140 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
141 fio::SymlinkRequest::GetAttr { responder } => {
142 let (status, attrs) = crate::common::io2_to_io1_attrs(
144 self.symlink.as_ref(),
145 fio::Rights::GET_ATTRIBUTES,
146 )
147 .await;
148 responder.send(status.into_raw(), &attrs)?;
149 }
150 #[cfg(fuchsia_api_level_at_least = "NEXT")]
151 fio::SymlinkRequest::DeprecatedSetAttr { responder, .. } => {
152 responder.send(Status::ACCESS_DENIED.into_raw())?;
153 }
154 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
155 fio::SymlinkRequest::SetAttr { responder, .. } => {
156 responder.send(Status::ACCESS_DENIED.into_raw())?;
157 }
158 fio::SymlinkRequest::GetAttributes { query, responder } => {
159 let attrs = self.symlink.get_attributes(query).await;
161 responder.send(
162 attrs
163 .as_ref()
164 .map(|attrs| (&attrs.mutable_attributes, &attrs.immutable_attributes))
165 .map_err(|status| status.into_raw()),
166 )?;
167 }
168 fio::SymlinkRequest::UpdateAttributes { payload: _, responder } => {
169 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
170 }
171 fio::SymlinkRequest::ListExtendedAttributes { iterator, control_handle: _ } => {
172 self.handle_list_extended_attribute(iterator).await;
173 }
174 fio::SymlinkRequest::GetExtendedAttribute { responder, name } => {
175 let res = self.handle_get_extended_attribute(name).await.map_err(|s| s.into_raw());
176 responder.send(res)?;
177 }
178 fio::SymlinkRequest::SetExtendedAttribute { responder, name, value, mode } => {
179 let res = self
180 .handle_set_extended_attribute(name, value, mode)
181 .await
182 .map_err(|s| s.into_raw());
183 responder.send(res)?;
184 }
185 fio::SymlinkRequest::RemoveExtendedAttribute { responder, name } => {
186 let res =
187 self.handle_remove_extended_attribute(name).await.map_err(|s| s.into_raw());
188 responder.send(res)?;
189 }
190 fio::SymlinkRequest::Describe { responder } => match self.symlink.read_target().await {
191 Ok(target) => responder
192 .send(&fio::SymlinkInfo { target: Some(target), ..Default::default() })?,
193 Err(status) => {
194 responder.control_handle().shutdown_with_epitaph(status);
195 return Ok(true);
196 }
197 },
198 #[cfg(fuchsia_api_level_at_least = "27")]
199 fio::SymlinkRequest::GetFlags { responder } => {
200 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
201 }
202 #[cfg(fuchsia_api_level_at_least = "27")]
203 fio::SymlinkRequest::SetFlags { flags: _, responder } => {
204 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
205 }
206 #[cfg(fuchsia_api_level_at_least = "27")]
207 fio::SymlinkRequest::DeprecatedGetFlags { responder } => {
208 responder.send(Status::NOT_SUPPORTED.into_raw(), fio::OpenFlags::empty())?;
209 }
210 #[cfg(fuchsia_api_level_at_least = "27")]
211 fio::SymlinkRequest::DeprecatedSetFlags { responder, .. } => {
212 responder.send(Status::ACCESS_DENIED.into_raw())?;
213 }
214 #[cfg(not(fuchsia_api_level_at_least = "27"))]
215 fio::SymlinkRequest::GetFlags { responder } => {
216 responder.send(Status::NOT_SUPPORTED.into_raw(), fio::OpenFlags::empty())?;
217 }
218 #[cfg(not(fuchsia_api_level_at_least = "27"))]
219 fio::SymlinkRequest::SetFlags { responder, .. } => {
220 responder.send(Status::ACCESS_DENIED.into_raw())?;
221 }
222 fio::SymlinkRequest::Query { responder } => {
223 responder.send(fio::SYMLINK_PROTOCOL_NAME.as_bytes())?;
224 }
225 fio::SymlinkRequest::QueryFilesystem { responder } => {
226 match self.symlink.query_filesystem() {
227 Err(status) => responder.send(status.into_raw(), None)?,
228 Ok(info) => responder.send(0, Some(&info))?,
229 }
230 }
231 fio::SymlinkRequest::_UnknownMethod { ordinal: _ordinal, .. } => {
232 #[cfg(any(test, feature = "use_log"))]
233 log::warn!(_ordinal; "Received unknown method")
234 }
235 }
236 Ok(false)
237 }
238
239 async fn handle_deprecated_clone(
240 &mut self,
241 flags: fio::OpenFlags,
242 server_end: ServerEnd<fio::NodeMarker>,
243 ) {
244 let flags = match inherit_rights_for_clone(fio::OpenFlags::RIGHT_READABLE, flags) {
245 Ok(updated) => updated,
246 Err(status) => {
247 send_on_open_with_error(
248 flags.contains(fio::OpenFlags::DESCRIBE),
249 server_end,
250 status,
251 );
252 return;
253 }
254 };
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_clone(&mut self, server_end: ServerEnd<fio::SymlinkMarker>) {
264 let flags = fio::Flags::PROTOCOL_SYMLINK | fio::Flags::PERM_GET_ATTRIBUTES;
265 flags
266 .to_object_request(server_end)
267 .handle_async(async |object_request| {
268 Self::create(self.scope.clone(), self.symlink.clone(), flags, object_request).await
269 })
270 .await;
271 }
272
273 async fn handle_link_into(
274 &mut self,
275 target_parent_token: fidl::Event,
276 target_name: String,
277 ) -> Result<(), Status> {
278 let target_name = parse_name(target_name).map_err(|_| Status::INVALID_ARGS)?;
279
280 let target_parent = self
281 .scope
282 .token_registry()
283 .get_owner(target_parent_token.into())?
284 .ok_or(Err(Status::NOT_FOUND))?;
285
286 self.symlink.clone().link_into(target_parent, target_name).await
287 }
288
289 async fn handle_list_extended_attribute(
290 &self,
291 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
292 ) {
293 let attributes = match self.symlink.list_extended_attributes().await {
294 Ok(attributes) => attributes,
295 Err(status) => {
296 #[cfg(any(test, feature = "use_log"))]
297 log::error!(status:?; "list extended attributes failed");
298 #[allow(clippy::unnecessary_lazy_evaluations)]
299 iterator.close_with_epitaph(status).unwrap_or_else(|_error| {
300 #[cfg(any(test, feature = "use_log"))]
301 log::error!(_error:?; "failed to send epitaph")
302 });
303 return;
304 }
305 };
306 self.scope.spawn(extended_attributes_sender(iterator, attributes));
307 }
308
309 async fn handle_get_extended_attribute(
310 &self,
311 name: Vec<u8>,
312 ) -> Result<fio::ExtendedAttributeValue, Status> {
313 let value = self.symlink.get_extended_attribute(name).await?;
314 encode_extended_attribute_value(value)
315 }
316
317 async fn handle_set_extended_attribute(
318 &self,
319 name: Vec<u8>,
320 value: fio::ExtendedAttributeValue,
321 mode: fio::SetExtendedAttributeMode,
322 ) -> Result<(), Status> {
323 if name.contains(&0) {
324 return Err(Status::INVALID_ARGS);
325 }
326 let val = decode_extended_attribute_value(value)?;
327 self.symlink.set_extended_attribute(name, val, mode).await
328 }
329
330 async fn handle_remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
331 self.symlink.remove_extended_attribute(name).await
332 }
333}
334
335impl<T: Symlink> RequestHandler for Connection<T> {
336 type Request = Result<fio::SymlinkRequest, fidl::Error>;
337
338 async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
339 let this = self.get_mut();
340 if let Some(_guard) = this.scope.try_active_guard() {
341 match request {
342 Ok(request) => match this.handle_request(request).await {
343 Ok(false) => ControlFlow::Continue(()),
344 Ok(true) | Err(_) => ControlFlow::Break(()),
345 },
346 Err(_) => ControlFlow::Break(()),
347 }
348 } else {
349 ControlFlow::Break(())
350 }
351 }
352}
353
354impl<T: Symlink> Representation for Connection<T> {
355 type Protocol = fio::SymlinkMarker;
356
357 async fn get_representation(
358 &self,
359 requested_attributes: fio::NodeAttributesQuery,
360 ) -> Result<fio::Representation, Status> {
361 Ok(fio::Representation::Symlink(fio::SymlinkInfo {
362 attributes: if requested_attributes.is_empty() {
363 None
364 } else {
365 Some(self.symlink.get_attributes(requested_attributes).await?)
366 },
367 target: Some(self.symlink.read_target().await?),
368 ..Default::default()
369 }))
370 }
371
372 async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
373 Ok(fio::NodeInfoDeprecated::Symlink(fio::SymlinkObject {
374 target: self.symlink.read_target().await?,
375 }))
376 }
377}
378
379impl<T: Symlink> ConnectionCreator<T> for Connection<T> {
380 async fn create<'a>(
381 scope: ExecutionScope,
382 node: Arc<T>,
383 protocols: impl ProtocolsExt,
384 object_request: ObjectRequestRef<'a>,
385 ) -> Result<(), Status> {
386 Self::create(scope, node, protocols, object_request).await
387 }
388}
389
390pub fn serve(
392 link: Arc<impl Symlink>,
393 scope: ExecutionScope,
394 protocols: impl ProtocolsExt,
395 object_request: ObjectRequestRef<'_>,
396) -> Result<(), Status> {
397 if protocols.is_node() {
398 let options = protocols.to_node_options(link.entry_info().type_())?;
399 link.open_as_node(scope, options, object_request)
400 } else {
401 Connection::create_sync(scope, link, protocols, object_request.take());
402 Ok(())
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::{Connection, Symlink};
409 use crate::directory::entry::{EntryInfo, GetEntryInfo};
410 use crate::execution_scope::ExecutionScope;
411 use crate::node::Node;
412 use crate::{immutable_attributes, ToObjectRequest};
413 use assert_matches::assert_matches;
414 use fidl::endpoints::{create_proxy, ServerEnd};
415 use fidl_fuchsia_io as fio;
416 use fuchsia_sync::Mutex;
417 use futures::StreamExt;
418 use std::collections::HashMap;
419 use std::sync::Arc;
420 use zx_status::Status;
421
422 const TARGET: &[u8] = b"target";
423
424 struct TestSymlink {
425 xattrs: Mutex<HashMap<Vec<u8>, Vec<u8>>>,
426 }
427
428 impl TestSymlink {
429 fn new() -> Self {
430 TestSymlink { xattrs: Mutex::new(HashMap::new()) }
431 }
432 }
433
434 impl Symlink for TestSymlink {
435 async fn read_target(&self) -> Result<Vec<u8>, Status> {
436 Ok(TARGET.to_vec())
437 }
438 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
439 let map = self.xattrs.lock();
440 Ok(map.values().map(|x| x.clone()).collect())
441 }
442 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
443 let map = self.xattrs.lock();
444 map.get(&name).map(|x| x.clone()).ok_or(Status::NOT_FOUND)
445 }
446 async fn set_extended_attribute(
447 &self,
448 name: Vec<u8>,
449 value: Vec<u8>,
450 _mode: fio::SetExtendedAttributeMode,
451 ) -> Result<(), Status> {
452 let mut map = self.xattrs.lock();
453 map.insert(name, value);
456 Ok(())
457 }
458 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
459 let mut map = self.xattrs.lock();
460 map.remove(&name);
461 Ok(())
462 }
463 }
464
465 impl Node for TestSymlink {
466 async fn get_attributes(
467 &self,
468 requested_attributes: fio::NodeAttributesQuery,
469 ) -> Result<fio::NodeAttributes2, Status> {
470 Ok(immutable_attributes!(
471 requested_attributes,
472 Immutable {
473 content_size: TARGET.len() as u64,
474 storage_size: TARGET.len() as u64,
475 protocols: fio::NodeProtocolKinds::SYMLINK,
476 abilities: fio::Abilities::GET_ATTRIBUTES,
477 }
478 ))
479 }
480 }
481
482 impl GetEntryInfo for TestSymlink {
483 fn entry_info(&self) -> EntryInfo {
484 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Symlink)
485 }
486 }
487
488 async fn serve_test_symlink() -> fio::SymlinkProxy {
489 let (client_end, server_end) = create_proxy::<fio::SymlinkMarker>();
490 let flags = fio::PERM_READABLE | fio::Flags::PROTOCOL_SYMLINK;
491
492 Connection::create_sync(
493 ExecutionScope::new(),
494 Arc::new(TestSymlink::new()),
495 flags,
496 flags.to_object_request(server_end),
497 );
498
499 client_end
500 }
501
502 #[fuchsia::test]
503 async fn test_read_target() {
504 let client_end = serve_test_symlink().await;
505
506 assert_eq!(
507 client_end.describe().await.expect("fidl failed").target.expect("missing target"),
508 b"target"
509 );
510 }
511
512 #[fuchsia::test]
513 async fn test_validate_flags() {
514 let scope = ExecutionScope::new();
515
516 let check = |mut flags: fio::Flags| {
517 let (client_end, server_end) = create_proxy::<fio::SymlinkMarker>();
518 flags |= fio::Flags::FLAG_SEND_REPRESENTATION;
519 flags.to_object_request(server_end).create_connection_sync::<Connection<_>, _>(
520 scope.clone(),
521 Arc::new(TestSymlink::new()),
522 flags,
523 );
524
525 async move { client_end.take_event_stream().next().await.expect("no event") }
526 };
527
528 for flags in [
529 fio::Flags::PROTOCOL_DIRECTORY,
530 fio::Flags::PROTOCOL_FILE,
531 fio::Flags::PROTOCOL_SERVICE,
532 ] {
533 assert_matches!(
534 check(fio::PERM_READABLE | flags).await,
535 Err(fidl::Error::ClientChannelClosed { status: Status::WRONG_TYPE, .. }),
536 "{flags:?}"
537 );
538 }
539
540 assert_matches!(
541 check(fio::PERM_READABLE | fio::Flags::PROTOCOL_SYMLINK)
542 .await
543 .expect("error from next")
544 .into_on_representation()
545 .expect("expected on representation"),
546 fio::Representation::Symlink(fio::SymlinkInfo { .. })
547 );
548 assert_matches!(
549 check(fio::PERM_READABLE)
550 .await
551 .expect("error from next")
552 .into_on_representation()
553 .expect("expected on representation"),
554 fio::Representation::Symlink(fio::SymlinkInfo { .. })
555 );
556 }
557
558 #[fuchsia::test]
559 async fn test_get_attr() {
560 let client_end = serve_test_symlink().await;
561
562 let (mutable_attrs, immutable_attrs) = client_end
563 .get_attributes(fio::NodeAttributesQuery::all())
564 .await
565 .expect("fidl failed")
566 .expect("GetAttributes failed");
567
568 assert_eq!(mutable_attrs, Default::default());
569 assert_eq!(
570 immutable_attrs,
571 fio::ImmutableNodeAttributes {
572 content_size: Some(TARGET.len() as u64),
573 storage_size: Some(TARGET.len() as u64),
574 protocols: Some(fio::NodeProtocolKinds::SYMLINK),
575 abilities: Some(fio::Abilities::GET_ATTRIBUTES),
576 ..Default::default()
577 }
578 );
579 }
580
581 #[fuchsia::test]
582 async fn test_clone() {
583 let client_end = serve_test_symlink().await;
584
585 let orig_attrs = client_end
586 .get_attributes(fio::NodeAttributesQuery::all())
587 .await
588 .expect("fidl failed")
589 .unwrap();
590 let (cloned_client, cloned_server) = create_proxy::<fio::SymlinkMarker>();
592 client_end.clone(ServerEnd::new(cloned_server.into_channel())).unwrap();
593 let cloned_attrs = cloned_client
594 .get_attributes(fio::NodeAttributesQuery::all())
595 .await
596 .expect("fidl failed")
597 .unwrap();
598 assert_eq!(orig_attrs, cloned_attrs);
599 }
600
601 #[fuchsia::test]
602 async fn test_describe() {
603 let client_end = serve_test_symlink().await;
604
605 assert_matches!(
606 client_end.describe().await.expect("fidl failed"),
607 fio::SymlinkInfo {
608 target: Some(target),
609 ..
610 } if target == b"target"
611 );
612 }
613
614 #[fuchsia::test]
615 async fn test_xattrs() {
616 let client_end = serve_test_symlink().await;
617
618 client_end
619 .set_extended_attribute(
620 b"foo",
621 fio::ExtendedAttributeValue::Bytes(b"bar".to_vec()),
622 fio::SetExtendedAttributeMode::Set,
623 )
624 .await
625 .unwrap()
626 .unwrap();
627 assert_eq!(
628 client_end.get_extended_attribute(b"foo").await.unwrap().unwrap(),
629 fio::ExtendedAttributeValue::Bytes(b"bar".to_vec()),
630 );
631 let (iterator_client_end, iterator_server_end) =
632 create_proxy::<fio::ExtendedAttributeIteratorMarker>();
633 client_end.list_extended_attributes(iterator_server_end).unwrap();
634 assert_eq!(
635 iterator_client_end.get_next().await.unwrap().unwrap(),
636 (vec![b"bar".to_vec()], true)
637 );
638 client_end.remove_extended_attribute(b"foo").await.unwrap().unwrap();
639 assert_eq!(
640 client_end.get_extended_attribute(b"foo").await.unwrap().unwrap_err(),
641 Status::NOT_FOUND.into_raw(),
642 );
643 }
644}