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