1use crate::volume::ErofsVolume;
6use erofs::FileNode;
7use fidl_fuchsia_io as fio;
8use fuchsia_async as fasync;
9use std::sync::Arc;
10use storage_trace::TraceFutureExt as _;
11use vfs::ObjectRequestRef;
12use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
13use vfs::execution_scope::ExecutionScope;
14use vfs::file::connection::GetVmo;
15use vfs::file::{File, FileLike, FileOptions, SyncMode};
16use vfs::node::Node;
17
18pub struct ErofsFile {
20 volume: Arc<ErofsVolume>,
21 node: FileNode,
22 vmo: zx::Vmo,
23 registration_key: u64,
24}
25
26impl Drop for ErofsFile {
27 fn drop(&mut self) {
28 self.volume.pager().unregister(self.registration_key);
29 }
30}
31
32impl ErofsFile {
33 pub fn new(volume: Arc<ErofsVolume>, node: FileNode) -> Result<Arc<Self>, zx::Status> {
37 let file = Arc::new_cyclic(|weak| {
38 let (vmo, registration_key) = volume
39 .pager()
40 .create_vmo(weak.clone(), node.size())
41 .expect("Failed to create pager VMO");
42 Self { volume, node, vmo, registration_key }
43 });
44 Ok(file)
45 }
46
47 pub fn fs(&self) -> &erofs::ErofsFilesystem {
48 self.volume.fs()
49 }
50
51 pub fn node(&self) -> &FileNode {
52 &self.node
53 }
54
55 pub fn vmo(&self) -> &zx::Vmo {
56 &self.vmo
57 }
58
59 pub(crate) fn register_zero_children_wait(&self) -> Result<(), zx::Status> {
60 self.vmo.wait_async(
61 fasync::EHandle::local().port(),
62 self.registration_key,
63 zx::Signals::VMO_ZERO_CHILDREN,
64 zx::WaitAsyncOpts::empty(),
65 )
66 }
67
68 pub fn watch_for_zero_children(&self) -> Result<bool, zx::Status> {
75 self.volume.pager().watch_for_zero_children(self.registration_key, &self.vmo)
76 }
77}
78
79impl DirectoryEntry for ErofsFile {
80 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
81 request.open_file(self)
82 }
83}
84
85impl GetEntryInfo for ErofsFile {
86 fn entry_info(&self) -> EntryInfo {
87 EntryInfo::new(self.node.nid(), fio::DirentType::File)
88 }
89}
90
91impl Node for ErofsFile {
92 async fn get_attributes(
93 &self,
94 requested_attributes: fio::NodeAttributesQuery,
95 ) -> Result<fio::NodeAttributes2, zx::Status> {
96 storage_trace::duration!("storage", "ErofsFile::get_attributes", "nid" => self.node.nid());
97 let mtime = self.node.mtime_ns();
98 let content_size = self.node.size();
99 let storage_size = self.node.storage_size(self.volume.fs().block_size());
100 let selinux_context = self
101 .volume
102 .fs()
103 .get_xattr(&self.node, fio::SELINUX_CONTEXT_NAME.as_bytes())
104 .ok()
105 .flatten()
106 .map(|val| {
107 if val.len() <= fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize {
108 fio::SelinuxContext::Data(val)
109 } else {
110 fio::SelinuxContext::UseExtendedAttributes(fio::EmptyStruct {})
111 }
112 });
113 Ok(vfs::attributes!(
114 requested_attributes,
115 Mutable {
116 mode: self.node.mode() as u32,
117 uid: self.node.uid(),
118 gid: self.node.gid(),
119 creation_time: mtime,
120 modification_time: mtime,
121 access_time: mtime,
122 selinux_context: selinux_context,
123 },
124 Immutable {
125 protocols: fio::NodeProtocolKinds::FILE,
126 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::READ_BYTES,
127 content_size: content_size,
128 storage_size: storage_size,
129 id: self.node.nid(),
130 link_count: self.node.link_count() as u64,
131 change_time: mtime,
132 }
133 ))
134 }
135
136 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
137 self.volume.fs().list_xattrs(&self.node).map_err(|e| e.to_status())
138 }
139
140 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
141 self.volume
142 .fs()
143 .get_xattr(&self.node, &name)
144 .map_err(|e| e.to_status())?
145 .ok_or(zx::Status::NOT_FOUND)
146 }
147}
148
149impl GetVmo for ErofsFile {
150 const PAGER_ON_FIDL_EXECUTOR: bool = true;
151
152 fn get_vmo(&self) -> &zx::Vmo {
153 &self.vmo
154 }
155}
156
157impl File for ErofsFile {
158 fn readable(&self) -> bool {
159 true
160 }
161
162 fn writable(&self) -> bool {
163 false
164 }
165
166 fn executable(&self) -> bool {
167 false
168 }
169
170 async fn open_file(&self, _options: &FileOptions) -> Result<(), zx::Status> {
171 Ok(())
172 }
173
174 async fn truncate(&self, _length: u64) -> Result<(), zx::Status> {
175 Err(zx::Status::NOT_SUPPORTED)
176 }
177
178 async fn get_size(&self) -> Result<u64, zx::Status> {
179 Ok(self.node.size())
180 }
181
182 async fn update_attributes(
183 &self,
184 _attributes: fio::MutableNodeAttributes,
185 ) -> Result<(), zx::Status> {
186 Err(zx::Status::NOT_SUPPORTED)
187 }
188
189 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, zx::Status> {
190 storage_trace::duration!(
191 "storage",
192 "ErofsFile::get_backing_memory",
193 "nid" => self.node.nid()
194 );
195 let mut vmo_rights = vmo_flags_to_rights(flags)
196 | zx::Rights::BASIC
197 | zx::Rights::MAP
198 | zx::Rights::GET_PROPERTY;
199
200 let child_vmo = if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
201 vmo_rights |= zx::Rights::SET_PROPERTY;
202 let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
203 if flags.contains(fio::VmoFlags::WRITE) {
204 child_options |= zx::VmoChildOptions::RESIZABLE;
205 vmo_rights |= zx::Rights::RESIZE;
206 }
207 self.vmo.create_child(child_options, 0, self.node.size())?
208 } else {
209 self.vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0)?
210 };
211
212 let child_vmo = child_vmo.replace_handle(vmo_rights)?;
213
214 let _ = self.watch_for_zero_children()?;
215
216 Ok(child_vmo)
217 }
218
219 async fn sync(&self, _mode: SyncMode) -> Result<(), zx::Status> {
220 Ok(())
221 }
222}
223
224impl FileLike for ErofsFile {
225 fn open(
226 self: Arc<Self>,
227 scope: ExecutionScope,
228 options: FileOptions,
229 object_request: ObjectRequestRef<'_>,
230 ) -> Result<(), zx::Status> {
231 let request = object_request.take();
232 let scope_clone = scope.clone();
233 let nid = self.node.nid();
234 scope.spawn(
235 request
236 .handle_async(async move |object_request_ref| {
237 vfs::file::StreamIoConnection::create(
238 scope_clone,
239 self,
240 options,
241 object_request_ref,
242 )
243 .await
244 })
245 .trace(storage_trace::trace_future_args!(
246 "storage",
247 "ErofsFile::open",
248 "nid" => nid
249 )),
250 );
251 Ok(())
252 }
253}
254
255fn vmo_flags_to_rights(vmo_flags: fio::VmoFlags) -> zx::Rights {
257 let mut rights = zx::Rights::NONE;
258 if vmo_flags.contains(fio::VmoFlags::READ) {
259 rights |= zx::Rights::READ;
260 }
261 if vmo_flags.contains(fio::VmoFlags::WRITE) {
262 rights |= zx::Rights::WRITE;
263 }
264 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
265 rights |= zx::Rights::EXECUTE;
266 }
267 rights
268}