1use crate::pager::ErofsPacketReceiver;
6use crate::volume::ErofsVolume;
7use erofs::FileNode;
8use fidl_fuchsia_io as fio;
9use fuchsia_async as fasync;
10use std::sync::Arc;
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: fasync::ReceiverRegistration<ErofsPacketReceiver>,
24}
25
26impl ErofsFile {
27 pub fn new(volume: Arc<ErofsVolume>, node: FileNode) -> Result<Arc<Self>, zx::Status> {
31 let file = Arc::new_cyclic(|weak| {
32 let (vmo, registration) = volume
33 .pager()
34 .create_vmo(weak.clone(), node.size())
35 .expect("Failed to create pager VMO");
36 Self { volume, node, vmo, registration }
37 });
38 Ok(file)
39 }
40
41 pub fn fs(&self) -> &erofs::ErofsFilesystem {
42 self.volume.fs()
43 }
44
45 pub fn node(&self) -> &FileNode {
46 &self.node
47 }
48
49 pub fn vmo(&self) -> &zx::Vmo {
50 &self.vmo
51 }
52
53 pub(crate) fn register_zero_children_wait(&self) -> Result<(), zx::Status> {
54 self.vmo.wait_async(
55 fasync::EHandle::local().port(),
56 self.registration.key(),
57 zx::Signals::VMO_ZERO_CHILDREN,
58 zx::WaitAsyncOpts::empty(),
59 )
60 }
61
62 pub fn watch_for_zero_children(&self) -> Result<bool, zx::Status> {
69 let mut file_holder = self.registration.receiver().file.lock().unwrap();
70 match &*file_holder {
71 crate::pager::FileHolder::Weak(weak) => {
72 let strong = weak.upgrade().ok_or(zx::Status::BAD_STATE)?;
73
74 self.register_zero_children_wait()?;
76
77 *file_holder = crate::pager::FileHolder::Strong(strong);
78 Ok(true)
79 }
80 crate::pager::FileHolder::Strong(_) => Ok(false),
81 }
82 }
83}
84
85impl DirectoryEntry for ErofsFile {
86 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
87 request.open_file(self)
88 }
89}
90
91impl GetEntryInfo for ErofsFile {
92 fn entry_info(&self) -> EntryInfo {
93 EntryInfo::new(self.node.nid(), fio::DirentType::File)
94 }
95}
96
97impl Node for ErofsFile {
98 async fn get_attributes(
99 &self,
100 requested_attributes: fio::NodeAttributesQuery,
101 ) -> Result<fio::NodeAttributes2, zx::Status> {
102 let mtime = self.node.mtime_ns();
103 let content_size = self.node.size();
104 let storage_size = self.node.storage_size(self.volume.fs().block_size());
105 let selinux_context = self
106 .volume
107 .fs()
108 .get_xattr(&self.node, fio::SELINUX_CONTEXT_NAME.as_bytes())
109 .ok()
110 .flatten()
111 .map(|val| {
112 if val.len() <= fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize {
113 fio::SelinuxContext::Data(val)
114 } else {
115 fio::SelinuxContext::UseExtendedAttributes(fio::EmptyStruct {})
116 }
117 });
118 Ok(vfs::attributes!(
119 requested_attributes,
120 Mutable {
121 mode: self.node.mode() as u32,
122 uid: self.node.uid(),
123 gid: self.node.gid(),
124 creation_time: mtime,
125 modification_time: mtime,
126 access_time: mtime,
127 selinux_context: selinux_context,
128 },
129 Immutable {
130 protocols: fio::NodeProtocolKinds::FILE,
131 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::READ_BYTES,
132 content_size: content_size,
133 storage_size: storage_size,
134 id: self.node.nid(),
135 link_count: self.node.link_count() as u64,
136 change_time: mtime,
137 }
138 ))
139 }
140
141 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
142 self.volume.fs().list_xattrs(&self.node).map_err(|e| e.to_status())
143 }
144
145 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
146 self.volume
147 .fs()
148 .get_xattr(&self.node, &name)
149 .map_err(|e| e.to_status())?
150 .ok_or(zx::Status::NOT_FOUND)
151 }
152}
153
154impl GetVmo for ErofsFile {
155 const PAGER_ON_FIDL_EXECUTOR: bool = true;
156
157 fn get_vmo(&self) -> &zx::Vmo {
158 &self.vmo
159 }
160}
161
162impl File for ErofsFile {
163 fn readable(&self) -> bool {
164 true
165 }
166
167 fn writable(&self) -> bool {
168 false
169 }
170
171 fn executable(&self) -> bool {
172 false
173 }
174
175 async fn open_file(&self, _options: &FileOptions) -> Result<(), zx::Status> {
176 Ok(())
177 }
178
179 async fn truncate(&self, _length: u64) -> Result<(), zx::Status> {
180 Err(zx::Status::NOT_SUPPORTED)
181 }
182
183 async fn get_size(&self) -> Result<u64, zx::Status> {
184 Ok(self.node.size())
185 }
186
187 async fn update_attributes(
188 &self,
189 _attributes: fio::MutableNodeAttributes,
190 ) -> Result<(), zx::Status> {
191 Err(zx::Status::NOT_SUPPORTED)
192 }
193
194 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, zx::Status> {
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 scope.spawn(request.handle_async(async move |object_request_ref| {
234 vfs::file::StreamIoConnection::create(scope_clone, self, options, object_request_ref)
235 .await
236 }));
237 Ok(())
238 }
239}
240
241fn vmo_flags_to_rights(vmo_flags: fio::VmoFlags) -> zx::Rights {
243 let mut rights = zx::Rights::NONE;
244 if vmo_flags.contains(fio::VmoFlags::READ) {
245 rights |= zx::Rights::READ;
246 }
247 if vmo_flags.contains(fio::VmoFlags::WRITE) {
248 rights |= zx::Rights::WRITE;
249 }
250 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
251 rights |= zx::Rights::EXECUTE;
252 }
253 rights
254}