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