vfs/
symlink.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Server support for symbolic links.
6
7use 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    // Extended attributes for symlinks.
29    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    /// Creates a new connection to serve the symlink. The symlink will be served from a new async
65    /// `Task`, not from the current `Task`. Errors in constructing the connection are not
66    /// guaranteed to be returned, they may be sent directly to the client end of the connection.
67    /// This method should be called from within an `ObjectRequest` handler to ensure that errors
68    /// are sent to the client end of the connection.
69    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    /// Similar to `create` but optimized for symlinks whose implementation is synchronous and
84    /// creating the connection is being done from a non-async context.
85    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    // Returns true if the connection should terminate.
100    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                // TODO(https://fxbug.dev/293947862): Restrict GET_ATTRIBUTES.
133                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                // TODO(https://fxbug.dev/293947862): Restrict GET_ATTRIBUTES.
143                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                // TODO(https://fxbug.dev/293947862): Restrict GET_ATTRIBUTES.
160                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
390/// Helper to open a symlink or node as required.
391pub 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            // Don't bother replicating the mode behavior, we just care that this method is hooked
454            // up at all.
455            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        // Clone the original connection and query it's attributes, which should match the original.
591        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}