Skip to main content

erofs_component/
symlink.rs

1// Copyright 2026 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
5use crate::volume::ErofsVolume;
6use erofs::SymlinkNode;
7use fidl_fuchsia_io as fio;
8use std::sync::Arc;
9use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
10use vfs::node::Node;
11use vfs::symlink::Symlink;
12
13/// An implementation of an EROFS symlink node.
14pub struct ErofsSymlink {
15    volume: Arc<ErofsVolume>,
16    node: SymlinkNode,
17    target: Vec<u8>,
18}
19
20impl ErofsSymlink {
21    pub fn new(volume: Arc<ErofsVolume>, node: SymlinkNode) -> Result<Arc<Self>, zx::Status> {
22        let target = volume.fs().read_symlink(&node).map_err(|e| e.to_status())?;
23        Ok(Arc::new(Self { volume, node, target }))
24    }
25
26    pub fn node(&self) -> &SymlinkNode {
27        &self.node
28    }
29}
30
31impl GetEntryInfo for ErofsSymlink {
32    fn entry_info(&self) -> EntryInfo {
33        EntryInfo::new(self.node.nid(), fio::DirentType::Symlink)
34    }
35}
36
37impl DirectoryEntry for ErofsSymlink {
38    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
39        request.open_symlink(self)
40    }
41}
42
43impl Node for ErofsSymlink {
44    async fn get_attributes(
45        &self,
46        requested_attributes: fio::NodeAttributesQuery,
47    ) -> Result<fio::NodeAttributes2, zx::Status> {
48        storage_trace::duration!(
49            "storage",
50            "ErofsSymlink::get_attributes",
51            "nid" => self.node.nid()
52        );
53        let mtime = self.node.mtime_ns();
54        let content_size = self.target.len() as u64;
55        let selinux_context = self
56            .volume
57            .fs()
58            .get_xattr(&self.node, b"security.selinux")
59            .ok()
60            .flatten()
61            .map(fio::SelinuxContext::Data);
62        Ok(vfs::attributes!(
63            requested_attributes,
64            Mutable {
65                mode: self.node.mode() as u32,
66                uid: self.node.uid(),
67                gid: self.node.gid(),
68                creation_time: mtime,
69                modification_time: mtime,
70                access_time: mtime,
71                selinux_context: selinux_context,
72            },
73            Immutable {
74                protocols: fio::NodeProtocolKinds::SYMLINK,
75                abilities: fio::Operations::GET_ATTRIBUTES,
76                content_size: content_size,
77                storage_size: content_size,
78                id: self.node.nid(),
79                link_count: self.node.link_count() as u64,
80                change_time: mtime,
81            }
82        ))
83    }
84
85    async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
86        self.volume.fs().list_xattrs(&self.node).map_err(|e| e.to_status())
87    }
88
89    async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
90        self.volume
91            .fs()
92            .get_xattr(&self.node, &name)
93            .map_err(|e| e.to_status())?
94            .ok_or(zx::Status::NOT_FOUND)
95    }
96}
97
98impl Symlink for ErofsSymlink {
99    async fn read_target(&self) -> Result<Vec<u8>, zx::Status> {
100        Ok(self.target.clone())
101    }
102}